/*
Copyright (C) 2007 Mobixess Inc. http://www.java-objects-database.com
This file is part of the JODB (Java Objects Database) open source project.
JODB is free software; you can redistribute it and/or modify it under
the terms of version 2 of the GNU General Public License as published
by the Free Software Foundation.
JODB is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package com.mobixess.jodb.core.io.rmi;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Field;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.rmi.Naming;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import com.mobixess.jodb.core.IDatabaseStatistics;
import com.mobixess.jodb.core.IPersistentObjectStatistics;
import com.mobixess.jodb.core.IllegalClassTypeException;
import com.mobixess.jodb.core.JODBConfig;
import com.mobixess.jodb.core.JODBServer;
import com.mobixess.jodb.core.JODBSessionContainer;
import com.mobixess.jodb.core.JodbIOException;
import com.mobixess.jodb.core.JodbNetConstants;
import com.mobixess.jodb.core.index.IndexDataIterator;
import com.mobixess.jodb.core.index.JODBIndexingRootAgent;
import com.mobixess.jodb.core.io.IOBase;
import com.mobixess.jodb.core.io.IOTicket;
import com.mobixess.jodb.core.io.IRandomAccessDataBuffer;
import com.mobixess.jodb.core.io.JODBOperationContext;
import com.mobixess.jodb.core.io.rmi.IRemoteServer.IServerQueryResult;
import com.mobixess.jodb.core.query.JODBQueryList;
import com.mobixess.jodb.core.query.NQExecutor;
import com.mobixess.jodb.core.query.QueryNode;
import com.mobixess.jodb.core.query.SimpleArrayQueryList;
import com.mobixess.jodb.core.transaction.JODBSession;
import com.mobixess.jodb.core.transaction.TransactionAssembler;
import com.mobixess.jodb.core.transaction.TransactionContainer;
import com.mobixess.jodb.query.api.ObjectSet;
import com.mobixess.jodb.query.api.Predicate;
public class JODBIOBaseProxy implements IOBase {
private IRemoteServer _server;
private URI _serverId;
private boolean _closed;
private ReentrantReadWriteLock _transactionLock = new ReentrantReadWriteLock();
private DatabaseStatisticsProxy _databaseStatisticsProxy = new DatabaseStatisticsProxy();
private HashMap<IOTicket, Object> _remoteTicketsCollector = new HashMap<IOTicket, Object>();
public JODBIOBaseProxy(URI serverURI) throws JodbIOException {
connect(serverURI);
}
private void connect(URI serverURI) throws JodbIOException{
_serverId = serverURI;
try {
_server = (IRemoteServer) Naming.lookup (_serverId.toString());
} catch (Exception e) {
throw new JodbIOException(e);
}
}
public static URI composeURI(String host, String serverName) throws JodbIOException {
String id = host+JODBServer.REMOTE_OBJECT_NAME;
if(serverName!=null && serverName.length() > 0){
id+="_"+serverName;
}
try {
return new URI(id).normalize();
} catch (URISyntaxException e) {
throw new JodbIOException(e);
}
}
public void applyTransaction(TransactionContainer transactionContainer,
JODBSession session, IOTicket writeTicket,
JODBIndexingRootAgent indexingRootAgent, JODBSessionContainer sessionContainer) throws IOException
{
transactionContainer.lockTransaction();
transactionContainer.runResolve(sessionContainer);
IRemoteTransactionContainer remoteTransactionContainer = _server.getRemoteTransactionContainer();
long transactionOffset = remoteTransactionContainer.initTransaction();
long indexVersion = remoteTransactionContainer.getIndexingAgentVersion();
session.ensureIndexVersion(indexVersion);
IOTicket ticket = getIOTicket(true,false);
try {
applyTransaction(transactionContainer, ticket, session, transactionOffset, indexingRootAgent);
dispatchTranslatedData(transactionContainer, remoteTransactionContainer);
remoteTransactionContainer.checkTransactionComplete();
transactionContainer.resetTranslatedObjects(session, transactionOffset);
} catch (Exception e) {
throw new JodbIOException(e);
}finally{
transactionContainer.reset();
ticket.close();
remoteTransactionContainer.disposeRemoteContainer();
}
}
private void applyTransaction(TransactionContainer transactionContainer,IOTicket ticket,
JODBSession session, long transactionOffset,JODBIndexingRootAgent indexingRootAgent) throws Exception
{
JODBOperationContext context = new JODBOperationContext(session,ticket,null,transactionContainer, indexingRootAgent);
context.setTransactionOffset(transactionOffset);
TransactionAssembler.assembleTransactionData(context, transactionContainer);
transactionContainer.processTranslatedObjectsIndex(context, transactionOffset);
transactionContainer.resetTransactionBufferToEnd();
try {
transactionContainer.enableAgentMode();
TransactionAssembler.assembleTransactionData(context, transactionContainer);
} finally {
transactionContainer.disableAgentMode();
}
}
private void dispatchTranslatedData(TransactionContainer transactionContainer, IRemoteTransactionContainer remoteTransactionContainer) throws IOException{
//Socket socket = new Socket(_serverId.getHost(),JODBConstants.DEFAULT_DATA_STREAM_PORT);
transactionContainer.resetTransactionBufferToStart();
remoteTransactionContainer.setTransactionDataSizes(transactionContainer.getTransactionNewDataFile().length(),
transactionContainer.getTransactionReplacementsDataFile().length(),
transactionContainer.getRollbackDataFile().length());
dispatchTranslatedData( transactionContainer.getTransactionNewDataFile(), remoteTransactionContainer.getNewDataBufferId());
dispatchTranslatedData( transactionContainer.getTransactionReplacementsDataFile(), remoteTransactionContainer.getReplacementsBufferId());
dispatchTranslatedData( transactionContainer.getRollbackDataFile(), remoteTransactionContainer.getRollbackBufferId());
}
private SocketChannel openDataTransferChane() throws IOException{
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress(_serverId.getHost(),JodbNetConstants.DEFAULT_DATA_STREAM_PORT));
socketChannel.configureBlocking(true);
return socketChannel;
}
private void dispatchTranslatedData(IRandomAccessDataBuffer randomAccessDataBuffer, int dataId) throws IOException{
SocketChannel socketChannel = openDataTransferChane();
Socket socket = socketChannel.socket();
try {
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeInt(dataId);
long len = randomAccessDataBuffer.length();
if(randomAccessDataBuffer.transferTo(0, len , socketChannel)!=len){
throw new IOException("unable to transfer all data");
}
} finally {
socketChannel.close();
socket.close();
}
}
public synchronized void close() throws IOException {
_closed = true;//TODO notify server?
synchronized (_remoteTicketsCollector) {
Iterator<IOTicket> iterator = _remoteTicketsCollector.keySet().iterator();
while (iterator.hasNext()) {
IOTicket ticket = iterator.next();
ticket.close();
}
}
}
public String getClassTypeForID(int id) throws JodbIOException {
try {
return _server.getClassTypeForID(id);
} catch (IOException e) {
throw new JodbIOException(e);
}
}
public int getClassTypeSubstitutionID(String classType) throws JodbIOException {
try {
return _server.getClassTypeSubstitutionID(classType);
} catch (IOException e) {
throw new JodbIOException(e);
}
}
public IDatabaseStatistics getDatabaseStatistics(boolean remote) throws IOException {
_databaseStatisticsProxy.setDatabaseStatisticsRemote(_server.getDatabaseStatistics());
return _databaseStatisticsProxy;
}
public URI getDbIdentificator() {
return _serverId;
}
public int getFieldSubstitutionID(Field field) {
try {
int declaringClassID = getOrSetClassTypeSubstitutionID(field.getDeclaringClass().getName());
int fieldTypeID = getOrSetClassTypeSubstitutionID(field.getType().getName());
return _server.getFieldSubstitutionID(declaringClassID,fieldTypeID, field.getName());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public long getFirstObjectOffset() throws IOException {
return _server.getFirstObjectOffset();
}
public long[] getForAllObjects(IOTicket ioTicket) throws IOException {
return _server.getForAllObjects(ioTicket.getTicketIdentity());
}
public String getFullFieldNameForID(int id) throws IOException {
return _server.getFullFieldNameForID(id);
}
public IOTicket getIOTicket(boolean read, boolean write) throws IOException
{
if(isClosed()){
throw new JodbIOException("Container closed");
}
try {
IOTicketProxy result = new IOTicketProxy(_server.getIOTicket(read, write));
_remoteTicketsCollector.put(result, null);
return result;
} finally {
if(JODBConfig.DEBUG){
//RegistryManager.getInstance().printRegistryList();
}
}
}
public int getOrSetClassTypeSubstitutionID(Class clazz) throws IOException {
return getOrSetClassTypeSubstitutionID(clazz.getName());
}
public int getOrSetClassTypeSubstitutionID(String classType) throws IOException {
return _server.getOrSetClassTypeSubstitutionID(classType);
}
public int getOrSetFieldSubstitutionID(Field field) {
try {
int declaringClassID = getOrSetClassTypeSubstitutionID(field.getDeclaringClass().getName());
int fieldTypeID = getOrSetClassTypeSubstitutionID(field.getType().getName());
return _server.getOrSetFieldSubstitutionID(declaringClassID,fieldTypeID, field.getName());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public IPersistentObjectStatistics getPersistenceStatistics(long offset,
JODBSession session) throws IOException
{
return _server.getPersistenceStatistics(offset);
}
public String getPrefixForID(int id) throws IOException {
return _server.getPrefixForID(id);
}
public String getSimpleFieldNameForID(int id) throws IOException {
return _server.getSimpleFieldNameForID(id);
}
public synchronized boolean isClosed() throws IOException {
return _closed;
}
public boolean isNewDatabase() {
// TODO Auto-generated method stub
throw new RuntimeException("Not Implemented");
//return false;
}
public ReentrantReadWriteLock getTransactionLock() {
return _transactionLock;
}
public void printFileMap(JODBSession session, PrintStream printStream)
throws IOException
{
}
public JODBQueryList executeQuery(QueryNode query) throws IOException, IllegalClassTypeException {
JODBQueryList result = null;
IServerQueryResult queryResult = _server.runQuery(query, null);
if(query!=null){
long[] resultingOffsets = queryResult.getSearchResult();
if(resultingOffsets!=null){
result = new SimpleArrayQueryList(resultingOffsets, query.getSession());
}
}
return result;
}
public ObjectIdentity checkObjectChanged(long objectId, int hash, byte versionCounter) throws IOException {
return _server.checkObjectChanged(objectId, hash, versionCounter);
}
public <TargetType> boolean isOptimizedQuery(JODBSession session,
Predicate<TargetType> predicate, Comparator<TargetType> comparator)
throws IOException
{
return NQExecutor.getInstance().isOptimizedQuery(session, predicate, comparator);
}
public <TargetType> ObjectSet<TargetType> query(JODBSession session,
Predicate<TargetType> predicate, Comparator<TargetType> comparator)
throws IOException
{
// QueryBytecode queryBytecode = NQExecutor.getInstance().getTransformationForQuery(session, predicate, comparator);
// if(queryBytecode==null){
// throw new JodbIOException("cannot run unoptimized query in server mode");
// }
JODBQueryList result = null;
IServerQueryResult queryResult = _server.query(predicate.getClass().getName(), comparator==null?null:comparator.getClass().getName());
long[] resultingOffsets = queryResult.getSearchResult();
if(resultingOffsets!=null){
result = new SimpleArrayQueryList(resultingOffsets, session);
}
return result;
}
public boolean isRemote() {
return true;
}
private static class UnprocessedObjectsIterator implements IndexDataIterator{
long[] _ids1;
long[] _ids2;
int _total;
int _index = 0;
/**
* @param ids1
* @param ids2
*/
public UnprocessedObjectsIterator(long[] ids1, long[] ids2) {
super();
_ids1 = ids1;
_ids2 = ids2;
if(_ids1!=null){
_total+=_ids1.length;
}
if(_ids2 != null){
_total+=_ids2.length;
}
}
public long next(ByteBuffer result) {
return next();
}
public boolean hasNext() {
return _index<_total;
}
public int length() {
return _total;
}
public long next() {
int offset = _index++;
if(offset < _ids1.length){
return _ids1[offset];
}else{
return _ids2[offset-_ids1.length];
}
}
}
private class IOTicketProxy implements IOTicket{
IOTicketRemoteInterface _remoteTicket;
RandomAccessDataBufferProxy _randomAccessDataBufferProxy;
boolean _closed;
/**
* @param remoteTicket
* @throws IOException
*/
public IOTicketProxy(IOTicketRemoteInterface remoteTicket) throws IOException {
super();
_remoteTicket = remoteTicket;
_randomAccessDataBufferProxy = new RandomAccessDataBufferProxy(_remoteTicket);
}
public void close() throws IOException {
if(_closed){
return;
}
_closed = true;
_randomAccessDataBufferProxy.delete();
_remoteTicket.close();
_randomAccessDataBufferProxy = null;
_remoteTicket = null;
}
public boolean isClosed() {
return _closed;
}
public IOBase getBase() {
return JODBIOBaseProxy.this;
}
public IRandomAccessDataBuffer getRandomAccessBuffer() {
return _randomAccessDataBufferProxy;
}
public void lock(boolean write) throws IOException {
// TODO Auto-generated method stub
//throw new RuntimeException("Not Implemented");
//
}
public void lock(boolean write, long offset) throws IOException {
// TODO Auto-generated method stub
//throw new RuntimeException("Not Implemented");
//
}
public void unlock() {
// TODO Auto-generated method stub
//throw new RuntimeException("Not Implemented");
//
}
public int getTicketIdentity() throws IOException {
return _remoteTicket.getTicketIdentity();
}
}
private static class DatabaseStatisticsProxy implements IDatabaseStatistics{
IDatabaseStatisticsRemote _databaseStatisticsRemote;
public long literalSubstFreeSize(int tableIndex) throws IOException {
return _databaseStatisticsRemote.literalSubstFreeSize(tableIndex);
}
public void setDatabaseStatisticsRemote(IDatabaseStatisticsRemote databaseStatisticsRemote) {
_databaseStatisticsRemote = databaseStatisticsRemote;
}
public long literalSubstMaxSize(int tableIndex) throws IOException {
return _databaseStatisticsRemote.literalSubstMaxSize(tableIndex);
}
public int totalDataEntries() throws IOException {
return _databaseStatisticsRemote.totalDataEntries();
}
}
}