/* This file is part of the db4o object database http://www.db4o.com Copyright (C) 2004 - 2011 Versant Corporation http://www.versant.com db4o is free software; you can redistribute it and/or modify it under the terms of version 3 of the GNU General Public License as published by the Free Software Foundation. db4o 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, see http://www.gnu.org/licenses/. */ package com.db4o.cs.internal; import java.io.*; import java.util.*; import com.db4o.*; import com.db4o.config.*; import com.db4o.cs.caching.*; import com.db4o.cs.config.*; import com.db4o.cs.internal.caching.*; import com.db4o.cs.internal.config.*; import com.db4o.cs.internal.events.*; import com.db4o.cs.internal.messages.*; import com.db4o.cs.internal.objectexchange.*; import com.db4o.events.*; import com.db4o.ext.*; import com.db4o.foundation.*; import com.db4o.internal.*; import com.db4o.internal.activation.*; import com.db4o.internal.convert.*; import com.db4o.internal.encoding.*; import com.db4o.internal.events.*; import com.db4o.internal.qlin.*; import com.db4o.internal.query.processor.*; import com.db4o.internal.query.result.*; import com.db4o.internal.references.*; import com.db4o.internal.slots.*; import com.db4o.io.*; import com.db4o.qlin.*; import com.db4o.reflect.*; /** * @exclude */ public class ClientObjectContainer extends ExternalObjectContainer implements ExtClient, BlobTransport, ClientMessageDispatcher { final Object _blobLock = new Object(); private BlobProcessor _blobTask; private Socket4Adapter _socket; private BlockingQueue _synchronousMessageQueue = new BlockingQueue(); private BlockingQueue _asynchronousMessageQueue = new BlockingQueue(); private final String _password; // null denotes password not necessary int[] _prefetchedIDs; ClientMessageDispatcher _messageDispatcher; ClientAsynchronousMessageProcessor _asynchronousMessageProcessor; int remainingIDs; private String switchedToFile; private boolean _singleThreaded; private final String _userName; private Db4oDatabase i_db; protected boolean _doFinalize=true; private int _blockSize = 1; private Collection4 _batchedMessages = new Collection4(); // initial value of _batchedQueueLength is // used for to write the number of messages. private int _batchedQueueLength = Const4.INT_LENGTH; private boolean _login; private final ClientHeartbeat _heartbeat; private final ClassInfoHelper _classInfoHelper; private ClientSlotCache _clientSlotCache; private int _serverSideID = 0; private MessageListener _messageListener = new MessageListener() { public void onMessage(Msg msg) { // do nothing } }; private boolean _bypassSlotCache = false; public interface MessageListener { public void onMessage(Msg msg); } static{ // Db4o.registerClientConstructor(new ClientConstructor()); } public ClientObjectContainer(ClientConfiguration config, Socket4Adapter socket, String user, String password, boolean login) { this((ClientConfigurationImpl)config, socket, user, password, login); } public ClientObjectContainer(ClientConfigurationImpl config, Socket4Adapter socket, String user, String password, boolean login) { super(Db4oClientServerLegacyConfigurationBridge.asLegacy(config)); _userName = user; _password = password; _login = login; _heartbeat = new ClientHeartbeat(this); _classInfoHelper = new ClassInfoHelper(Db4oClientServerLegacyConfigurationBridge.asLegacy(config)); setAndConfigSocket(socket); open(); config.applyConfigurationItems(this); } private void setAndConfigSocket(Socket4Adapter socket) { _socket = socket; _socket.setSoTimeout(_config.timeoutClientSocket()); } protected final void openImpl() { initializeClassMetadataRepository(); initalizeWeakReferenceSupport(); initalizeClientSlotCache(); _singleThreaded = configImpl().singleThreadedClient(); // TODO: Experiment with packet size and noDelay // socket.setSendBufferSize(100); // socket.setTcpNoDelay(true); // System.out.println(socket.getSendBufferSize()); if (_login) { loginToServer(_socket); } if (!_singleThreaded) { startDispatcherThread(_socket, _userName); } logMsg(36, toString()); startHeartBeat(); readThis(); } private final void initalizeClientSlotCache(){ configImpl().prefetchSettingsChanged().addListener(new EventListener4<EventArgs>(){ public void onEvent(Event4<EventArgs> e, EventArgs args) { initalizeClientSlotCache(); } }); if(configImpl().prefetchSlotCacheSize() > 0){ _clientSlotCache = new ClientSlotCacheImpl(this); return; } _clientSlotCache = new NullClientSlotCache(); } private void startHeartBeat(){ _heartbeat.start(); } private void startDispatcherThread(Socket4Adapter socket, String user) { if(! _singleThreaded){ startAsynchronousMessageProcessor(); } final ClientMessageDispatcherImpl dispatcherImpl = new ClientMessageDispatcherImpl(this, socket, _synchronousMessageQueue, _asynchronousMessageQueue); String dispatcherName = "db4o client side message dispatcher for " + user; _messageDispatcher = dispatcherImpl; threadPool().start(dispatcherName, dispatcherImpl); } private void startAsynchronousMessageProcessor() { _asynchronousMessageProcessor = new ClientAsynchronousMessageProcessor(_asynchronousMessageQueue); threadPool().start("Client Asynchronous Message Processor Thread for: " + toString(), _asynchronousMessageProcessor); } public void backup(Storage targetStorage, String path) throws NotSupportedException { throw new NotSupportedException(); } public void closeTransaction(Transaction transaction, boolean isSystemTransaction, boolean rollbackOnClose) { if(isSystemTransaction){ return; } _transaction.close(rollbackOnClose); } public void reserve(int byteCount) { throw new NotSupportedException(); } public byte blockSize() { return (byte)_blockSize; } protected void close2() { if ((!_singleThreaded) && (_messageDispatcher == null || !_messageDispatcher.isMessageDispatcherAlive())) { stopHeartBeat(); shutdownObjectContainer(); return; } try { commit1(_transaction); } catch (Exception e) { Exceptions4.catchAllExceptDb4oException(e); } try { write(Msg.CLOSE); } catch (Exception e) { Exceptions4.catchAllExceptDb4oException(e); } shutDownCommunicationRessources(); try { _socket.close(); } catch (Exception e) { Exceptions4.catchAllExceptDb4oException(e); } shutdownObjectContainer(); } private void stopHeartBeat(){ _heartbeat.stop(); } private void closeMessageDispatcher(){ try { if (!_singleThreaded) { _messageDispatcher.close(); } } catch (Exception e) { Exceptions4.catchAllExceptDb4oException(e); } try { if (!_singleThreaded) { _asynchronousMessageProcessor.stopProcessing(); } } catch (Exception e) { Exceptions4.catchAllExceptDb4oException(e); } } public final void commit1(Transaction trans) { trans.commit(); } public int converterVersion() { return Converter.VERSION; } Socket4Adapter createParallelSocket() throws IOException { write(Msg.GET_THREAD_ID); int serverThreadID = expectedBufferResponse(Msg.ID_LIST).readInt(); Socket4Adapter sock = _socket.openParalellSocket(); loginToServer(sock); if (switchedToFile != null) { MsgD message = Msg.SWITCH_TO_FILE.getWriterForString(systemTransaction(), switchedToFile); message.write(sock); if (!(Msg.OK.equals(Msg.readMessage(this, systemTransaction(), sock)))) { throw new IOException(Messages.get(42)); } } Msg.USE_TRANSACTION.getWriterForInt(_transaction, serverThreadID).write( sock); return sock; } public AbstractQueryResult newQueryResult(Transaction trans, QueryEvaluationMode mode) { throw new IllegalStateException(); } final public Transaction newTransaction(Transaction parentTransaction, ReferenceSystem referenceSystem, boolean isSystemTransaction) { return new ClientTransaction(this, parentTransaction, referenceSystem); } public boolean createClassMetadata(ClassMetadata clazz, ReflectClass claxx, ClassMetadata superClazz) { write(Msg.CREATE_CLASS.getWriterForString(systemTransaction(), config().resolveAliasRuntimeName(claxx.getName()))); Msg resp = getResponse(); if (resp == null) { return false; } if (resp.equals(Msg.FAILED)) { // if the class can not be created on the server, send class meta to the server. sendClassMeta(claxx); resp = getResponse(); } if (resp.equals(Msg.FAILED)) { if (configImpl().exceptionsOnNotStorable()) { throw new ObjectNotStorableException(claxx); } return false; } if (!resp.equals(Msg.OBJECT_TO_CLIENT)) { return false; } MsgObject message = (MsgObject) resp; StatefulBuffer bytes = message.unmarshall(); if (bytes == null) { return false; } bytes.setTransaction(systemTransaction()); if (!super.createClassMetadata(clazz, claxx, superClazz)) { return false; } clazz.setID(message.getId()); clazz.readName1(systemTransaction(), bytes); classCollection().addClassMetadata(clazz); classCollection().readClassMetadata(clazz, claxx); return true; } private void sendClassMeta(ReflectClass reflectClass) { ClassInfo classMeta = _classInfoHelper.getClassMeta(reflectClass); write(Msg.CLASS_META.getWriter(Serializer.marshall(systemTransaction(),classMeta))); } public long currentVersion() { write(Msg.CURRENT_VERSION); return ((MsgD) expectedResponse(Msg.ID_LIST)).readLong(); } public final boolean delete4(Transaction ta, ObjectReference yo, Object obj, int a_cascade, boolean userCall) { MsgD msg = Msg.DELETE.getWriterForInts(_transaction, new int[] { yo.getID(), userCall ? 1 : 0 }); writeBatchedMessage(msg); return true; } public boolean detectSchemaChanges() { return false; } protected boolean doFinalize() { return _doFinalize; } final ByteArrayBuffer expectedBufferResponse(Msg expectedMessage) { Msg msg = expectedResponse(expectedMessage); if (msg == null) { // TODO: throw Exception to allow // smooth shutdown return null; } return msg.getByteLoad(); } public final Msg expectedResponse(Msg expectedMessage) { Msg message = getResponse(); if (expectedMessage.equals(message)) { return message; } checkExceptionMessage(message); throw new IllegalStateException("Unexpected Message:" + message + " Expected:" + expectedMessage); } private void checkExceptionMessage(Msg msg) { if(msg instanceof MRuntimeException) { ((MRuntimeException)msg).throwPayload(); } } public AbstractQueryResult queryAllObjects(Transaction trans) { int mode = config().evaluationMode().asInt(); MsgD msg = Msg.GET_ALL.getWriterForInts(trans, mode, prefetchDepth(), prefetchCount()); write(msg); return readQueryResult(trans); } public final HardObjectReference getHardReferenceBySignature(Transaction trans, long uuid, byte[] signature) { int messageLength = Const4.LONG_LENGTH + Const4.INT_LENGTH + signature.length; MsgD message = Msg.OBJECT_BY_UUID.getWriterForLength(trans, messageLength); message.writeLong(uuid); message.writeInt(signature.length); message.writeBytes(signature); write(message); message = (MsgD)expectedResponse(Msg.OBJECT_BY_UUID); int id = message.readInt(); if(id > 0){ return getHardObjectReferenceById(trans, id); } return HardObjectReference.INVALID; } /** * may return null, if no message is returned. Error handling is weak and * should ideally be able to trigger some sort of state listener (connection * dead) on the client. */ public Msg getResponse() { while(true){ Msg msg = _singleThreaded ? getResponseSingleThreaded(): getResponseMultiThreaded(); if(isClientSideMessage(msg)){ if(((ClientSideMessage)msg).processAtClient()){ continue; } } return msg; } } private Msg getResponseSingleThreaded() { while (isMessageDispatcherAlive()) { try { final Msg message = Msg.readMessage(this, _transaction, _socket); if(isClientSideMessage(message)) { if(((ClientSideMessage)message).processAtClient()){ continue; } } return message; } catch (Db4oIOException exc) { onMsgError(); } } return null; } private Msg getResponseMultiThreaded() { Msg msg; try { msg = (Msg)_synchronousMessageQueue.next(); } catch (BlockingQueueStoppedException e) { if(DTrace.enabled){ DTrace.BLOCKING_QUEUE_STOPPED_EXCEPTION.log(e.toString()); } msg = Msg.ERROR; } if(msg instanceof MError) { onMsgError(); } return msg; } private boolean isClientSideMessage(Msg message) { return message instanceof ClientSideMessage; } private void onMsgError() { close(); throw new DatabaseClosedException(); } public boolean isMessageDispatcherAlive() { return _socket != null; } public ClassMetadata classMetadataForID(int clazzId) { if(clazzId == 0) { return null; } ClassMetadata yc = super.classMetadataForID(clazzId); if (yc != null) { return yc; } MsgD msg = Msg.CLASS_NAME_FOR_ID.getWriterForInt(systemTransaction(), clazzId); write(msg); MsgD message = (MsgD) expectedResponse(Msg.CLASS_NAME_FOR_ID); String className = config().resolveAliasStoredName(message.readString()); if (className != null && className.length() > 0) { ReflectClass claxx = reflector().forName(className); if (claxx != null) { return produceClassMetadata(claxx); } // TODO inform client class not present } return null; } public boolean needsLockFileThread() { return false; } protected boolean hasShutDownHook() { return false; } public Db4oDatabase identity() { if (i_db == null) { write(Msg.IDENTITY); ByteArrayBuffer reader = expectedBufferResponse(Msg.ID_LIST); showInternalClasses(true); try { i_db = (Db4oDatabase) getByID(systemTransaction(), reader.readInt()); activate(systemTransaction(), i_db, new FixedActivationDepth(3)); } finally { showInternalClasses(false); } } return i_db; } public boolean isClient() { return true; } private void loginToServer(Socket4Adapter iSocket) throws InvalidPasswordException { UnicodeStringIO stringWriter = new UnicodeStringIO(); int length = stringWriter.length(_userName) + stringWriter.length(_password); MsgD message = Msg.LOGIN .getWriterForLength(systemTransaction(), length); message.writeString(_userName); message.writeString(_password); message.write(iSocket); Msg msg = readLoginMessage(iSocket); ByteArrayBuffer payLoad = msg.payLoad(); blockSize(payLoad.readInt()); int doEncrypt = payLoad.readInt(); if (doEncrypt == 0) { _handlers.oldEncryptionOff(); } if(payLoad.remainingByteCount() > 0) { _serverSideID = payLoad.readInt(); } } private Msg readLoginMessage(Socket4Adapter iSocket){ Msg msg = Msg.readMessage(this, systemTransaction(), iSocket); while(Msg.PONG.equals(msg)){ msg = Msg.readMessage(this, systemTransaction(), iSocket); } if (!Msg.LOGIN_OK.equals(msg)) { throw new InvalidPasswordException(); } return msg; } public boolean maintainsIndices() { return false; } public final int idForNewUserObject(Transaction trans) { int prefetchIDCount = config().prefetchIDCount(); ensureIDCacheAllocated(prefetchIDCount); ByteArrayBuffer reader = null; if (remainingIDs < 1) { MsgD msg = Msg.PREFETCH_IDS.getWriterForInt(_transaction, prefetchIDCount); write(msg); reader = expectedBufferResponse(Msg.ID_LIST); for (int i = prefetchIDCount - 1; i >= 0; i--) { _prefetchedIDs[i] = reader.readInt(); } remainingIDs = prefetchIDCount; } remainingIDs--; return _prefetchedIDs[remainingIDs]; } void processBlobMessage(MsgBlob msg) { synchronized (_blobLock) { boolean needStart = _blobTask == null || _blobTask.isTerminated(); if (needStart) { _blobTask = new BlobProcessor(this); } _blobTask.add(msg); if (needStart) { threadPool().startLowPriority("Blob processor task", _blobTask); } } } public void raiseCommitTimestamp(long a_minimumVersion) { synchronized(lock()){ write(Msg.RAISE_COMMIT_TIMESTAMP.getWriterForLong(_transaction, a_minimumVersion)); } } public void readBytes(byte[] bytes, int address, int addressOffset, int length) { throw Exceptions4.virtualException(); } public void readBytes(byte[] a_bytes, int a_address, int a_length) { MsgD msg = Msg.READ_SLOT.getWriterForInts(_transaction, new int[] { a_address, a_length }); write(msg); ByteArrayBuffer reader = expectedBufferResponse(Msg.READ_SLOT); System.arraycopy(reader._buffer, 0, a_bytes, 0, a_length); } protected boolean applyRenames(Config4Impl config) { logMsg(58, null); return false; } public final StatefulBuffer readStatefulBufferById(Transaction a_ta, int a_id) { return readStatefulBufferById(a_ta, a_id, false); } public final StatefulBuffer readStatefulBufferById(Transaction a_ta, int a_id, boolean lastCommitted) { MsgD msg = Msg.READ_OBJECT.getWriterForInts(a_ta, new int[]{a_id, lastCommitted?1:0}); write(msg); StatefulBuffer bytes = ((MsgObject) expectedResponse(Msg.OBJECT_TO_CLIENT)).unmarshall(); if(bytes != null){ bytes.setTransaction(a_ta); } return bytes; } @Override public Object peekPersisted(Transaction trans, Object obj, ActivationDepth depth, boolean committed) throws DatabaseClosedException { _bypassSlotCache = true; try { return super.peekPersisted(trans, obj, depth, committed); } finally { _bypassSlotCache = false; } } @Override protected void refreshInternal(final Transaction trans, final Object obj, final int depth) { _bypassSlotCache = true; try { super.refreshInternal(trans, obj, depth); } finally { _bypassSlotCache = false; } } @Override public final ByteArrayBuffer[] readSlotBuffers(final Transaction transaction, final int[] ids) { return readSlotBuffers(transaction, ids, 1); } public final ByteArrayBuffer[] readObjectSlots(final Transaction transaction, final int[] ids) { final int prefetchDepth = config().prefetchDepth(); return readSlotBuffers(transaction, ids, prefetchDepth); } private ByteArrayBuffer[] readSlotBuffers(final Transaction transaction, final int[] ids, final int prefetchDepth) { final Map<Integer, ByteArrayBuffer> buffers = new HashMap(ids.length); final ArrayList<Integer> cacheMisses = populateSlotBuffersFromCache(transaction, ids, buffers); fetchMissingSlotBuffers(transaction, cacheMisses, buffers, prefetchDepth); return packSlotBuffers(ids, buffers); } public final ByteArrayBuffer readBufferById(final Transaction transaction, final int id, final boolean lastCommitted) { if (lastCommitted || _bypassSlotCache) { return fetchSlotBuffer(transaction, id, lastCommitted); } final ByteArrayBuffer cached = _clientSlotCache.get(transaction, id); if (cached != null) { return cached; } final ByteArrayBuffer slot = fetchSlotBuffer(transaction, id, lastCommitted); _clientSlotCache.add(transaction, id, slot); return slot; } public final ByteArrayBuffer readBufferById(Transaction a_ta, int a_id) { return readBufferById(a_ta, a_id, false); } private AbstractQueryResult readQueryResult(final Transaction trans) { final ByRef<AbstractQueryResult> result = ByRef.newInstance(); withEnvironment(new Runnable() { public void run() { ByteArrayBuffer reader = expectedBufferResponse(Msg.QUERY_RESULT); int queryResultID = reader.readInt(); AbstractQueryResult queryResult = queryResultFor(trans, queryResultID); queryResult.loadFromIdReader(idIteratorFor(trans, reader)); result.value = queryResult; }}); return result.value; } public FixedSizeIntIterator4 idIteratorFor(Transaction trans, ByteArrayBuffer reader) { return idIteratorFor(objectExchangeStrategy(), trans, reader); } private FixedSizeIntIterator4 idIteratorFor(final ObjectExchangeStrategy strategy, Transaction trans, ByteArrayBuffer reader) { return strategy.unmarshall((ClientTransaction)trans, _clientSlotCache, reader); } private ObjectExchangeStrategy objectExchangeStrategy() { return ObjectExchangeStrategyFactory.forConfig(defaultObjectExchangeConfiguration()); } private ObjectExchangeConfiguration defaultObjectExchangeConfiguration() { return new ObjectExchangeConfiguration(prefetchDepth(), prefetchCount()); } void readThis() { write(Msg.GET_CLASSES.getWriter(systemTransaction())); ByteArrayBuffer bytes = expectedBufferResponse(Msg.GET_CLASSES); classCollection().setID(bytes.readInt()); final byte stringEncoding = bytes.readByte(); createStringIO(stringEncoding); classCollection().read(systemTransaction()); } public void releaseSemaphore(Transaction trans, final String name){ synchronized (_lock) { checkClosed(); if (name == null) { throw new NullPointerException(); } trans = checkTransaction(trans); write(Msg.RELEASE_SEMAPHORE.getWriterForString(trans, name)); } } public void releaseSemaphore(String name) { releaseSemaphore(_transaction, name); } public void releaseSemaphores(Transaction ta) { // do nothing } public final void rollback1(Transaction trans) { if (_config.batchMessages()) { clearBatchedObjects(); } write(Msg.ROLLBACK); trans.rollback(); } public void send(Object obj) { synchronized (_lock) { if (obj != null) { final MUserMessage message = Msg.USER_MESSAGE; write(message.marshallUserMessage(_transaction, obj)); } } } public final void setDirtyInSystemTransaction(PersistentBase a_object) { // do nothing } public boolean setSemaphore(Transaction trans, final String name, final int timeout){ synchronized (_lock) { checkClosed(); trans = checkTransaction(trans); if (name == null) { throw new NullPointerException(); } MsgD msg = Msg.SET_SEMAPHORE.getWriterForIntString(trans,timeout, name); write(msg); Msg message = getResponse(); return (message.equals(Msg.SUCCESS)); } } public boolean setSemaphore(String name, int timeout) { return setSemaphore(_transaction, name, timeout); } protected String defaultToString() { return "Client connection " + _userName + "(" + _socket + ")"; } public void shutdown() { // do nothing } public final void writeDirtyClassMetadata() { // do nothing } public final boolean write(Msg msg) { writeMsg(msg, true); return true; } public final void writeBatchedMessage(Msg msg) { writeMsg(msg, false); } public final void writeMsg(Msg msg, boolean flush) { if(_config.batchMessages()) { if(flush && _batchedMessages.isEmpty()) { // if there's nothing batched, just send this message directly writeMessageToSocket(msg); } else { addToBatch(msg); if(flush || _batchedQueueLength > _config.maxBatchQueueSize()) { writeBatchedMessages(); } } } else { if (!_batchedMessages.isEmpty()) { addToBatch(msg); writeBatchedMessages(); } else { writeMessageToSocket(msg); } } } public boolean writeMessageToSocket(Msg msg) { if(_messageListener != null){ _messageListener.onMessage(msg); } return msg.write(_socket); } public final void writeNew(Transaction trans, Pointer4 pointer, ClassMetadata classMetadata, ByteArrayBuffer buffer) { MsgD msg = Msg.WRITE_NEW.getWriter(trans, pointer, classMetadata, buffer); writeBatchedMessage(msg); } public final void writeUpdate(Transaction trans, Pointer4 pointer, ClassMetadata classMetadata, ArrayType arrayType, ByteArrayBuffer buffer) { MsgD msg = Msg.WRITE_UPDATE.getWriter(trans, pointer, classMetadata, arrayType.value(), buffer); writeBatchedMessage(msg); } public boolean isAlive() { try { synchronized (lock()) { if(isClosed()) { return false; } write(Msg.IS_ALIVE); return expectedResponse(Msg.IS_ALIVE) != null; } } catch (Db4oException exc) { return false; } } public Socket4Adapter socket() { return _socket; } private void ensureIDCacheAllocated(int prefetchIDCount) { if(_prefetchedIDs==null) { _prefetchedIDs = new int[prefetchIDCount]; return; } if(prefetchIDCount>_prefetchedIDs.length) { int[] newPrefetchedIDs=new int[prefetchIDCount]; System.arraycopy(_prefetchedIDs, 0, newPrefetchedIDs, 0, _prefetchedIDs.length); _prefetchedIDs=newPrefetchedIDs; } } public SystemInfo systemInfo() { throw new NotImplementedException("Functionality not availble on clients."); } public void writeBlobTo(Transaction trans, BlobImpl blob) throws IOException { MsgBlob msg = (MsgBlob) Msg.READ_BLOB.getWriterForInt(trans, (int) getID(blob)); msg._blob = blob; processBlobMessage(msg); } public void readBlobFrom(Transaction trans, BlobImpl blob) throws IOException { MsgBlob msg = null; synchronized (lock()) { store(blob); int id = (int) getID(blob); msg = (MsgBlob) Msg.WRITE_BLOB.getWriterForInt(trans, id); msg._blob = blob; blob.setStatus(Status.QUEUED); } processBlobMessage(msg); } public void deleteBlobFile(Transaction trans, BlobImpl blob){ MDeleteBlobFile msg = (MDeleteBlobFile) Msg.DELETE_BLOB_FILE.getWriterForInt(trans, (int) getID(blob)); writeMsg(msg, false); } public long[] getIDsForClass(final Transaction trans, ClassMetadata clazz){ boolean triggerQueryEvents = false; return getIDsForClass(trans, clazz, triggerQueryEvents); } private long[] getIDsForClass(final Transaction trans, ClassMetadata clazz, boolean triggerQueryEvents) { MsgD msg = Msg.GET_INTERNAL_IDS.getWriterForInts(trans, clazz.getID(), prefetchDepth(), prefetchCount(), triggerQueryEvents ? 1 : 0); write(msg); final ByRef<long[]> result = ByRef.newInstance(); withEnvironment(new Runnable() { public void run() { ByteArrayBuffer reader = expectedBufferResponse(Msg.ID_LIST); FixedSizeIntIterator4 idIterator = idIteratorFor(trans, reader); result.value = toLongArray(idIterator); }}); return result.value; } @Override public QueryResult classOnlyQuery(QQueryBase query, ClassMetadata clazz){ final Transaction trans = query.transaction(); long[] ids = getIDsForClass(trans, clazz, true); ClientQueryResult resClient = new ClientQueryResult(trans, ids.length); for (int i = 0; i < ids.length; i++) { resClient.add((int)ids[i]); } return resClient; } private long[] toLongArray(FixedSizeIntIterator4 idIterator) { final long[] ids = new long[idIterator.size()]; int i = 0; while (idIterator.moveNext()) { ids[i++] = (Integer)idIterator.current(); } return ids; } int prefetchDepth() { return _config.prefetchDepth(); } int prefetchCount() { return _config.prefetchObjectCount(); } public QueryResult executeQuery(QQuery query){ Transaction trans = query.transaction(); query.captureQueryResultConfig(); query.marshall(); MsgD msg = Msg.QUERY_EXECUTE.getWriter(Serializer.marshall(trans,query)); write(msg); return readQueryResult(trans); } public final void writeBatchedMessages() { synchronized(lock()) { if (_batchedMessages.isEmpty()) { return; } Msg msg; MsgD multibytes = Msg.WRITE_BATCHED_MESSAGES.getWriterForLength( transaction(), _batchedQueueLength); multibytes.writeInt(_batchedMessages.size()); Iterator4 iter = _batchedMessages.iterator(); while(iter.moveNext()) { msg = (Msg) iter.current(); if (msg == null) { multibytes.writeInt(0); } else { multibytes.writeInt(msg.payLoad().length()); multibytes.payLoad().append(msg.payLoad()._buffer); } } writeMessageToSocket(multibytes); clearBatchedObjects(); } } public final void addToBatch(Msg msg) { synchronized(lock()) { _batchedMessages.add(msg); // the first INT_LENGTH is for buffer.length, and then buffer content. _batchedQueueLength += Const4.INT_LENGTH + msg.payLoad().length(); } } private final void clearBatchedObjects() { _batchedMessages.clear(); // initial value of _batchedQueueLength is Const4.INT_LENGTH, which is // used for to write the number of messages. _batchedQueueLength = Const4.INT_LENGTH; } int timeout() { return configImpl().timeoutClientSocket(); } protected void shutdownDataStorage() { shutDownCommunicationRessources(); } private void shutDownCommunicationRessources() { stopHeartBeat(); closeMessageDispatcher(); _synchronousMessageQueue.stop(); _asynchronousMessageQueue.stop(); } public void setDispatcherName(String name) { // do nothing here } public ClientMessageDispatcher messageDispatcher() { return _singleThreaded ? this : _messageDispatcher; } public void onCommittedListenerAdded() { if(_singleThreaded) { return; } write(Msg.COMMITTED_CALLBACK_REGISTER); expectedResponse(Msg.OK); } @Override public ClassMetadata classMetadataForReflectClass(ReflectClass claxx) { ClassMetadata classMetadata = super.classMetadataForReflectClass(claxx); if(classMetadata != null){ return classMetadata; } String className = config().resolveAliasRuntimeName(claxx.getName()); if( classMetadataIdForName(className) == 0){ return null; } return produceClassMetadata(claxx); } public int classMetadataIdForName(String name) { MsgD msg = Msg.CLASS_METADATA_ID_FOR_NAME.getWriterForString(systemTransaction(), name); msg.write(_socket); MsgD response = (MsgD) expectedResponse(Msg.CLASS_ID); return response.readInt(); } public int instanceCount(ClassMetadata clazz, Transaction trans) { MsgD msg = Msg.INSTANCE_COUNT.getWriterForInt(trans, clazz.getID()); write(msg); MsgD response = (MsgD) expectedResponse(Msg.INSTANCE_COUNT); return response.readInt(); } public void messageListener(MessageListener listener){ _messageListener = listener; } @Override public void storeAll(final Transaction transaction, final Iterator4 objects) { boolean configuredBatchMessages = _config.batchMessages(); _config.batchMessages(true); try{ super.storeAll(transaction, objects); } finally{ _config.batchMessages(configuredBatchMessages); } } private void sendReadMultipleObjectsMessage(MReadMultipleObjects message, final Transaction transaction, final int prefetchDepth, final List<Integer> idsToRead) { MsgD msg = message.getWriterForLength(transaction, Const4.INT_LENGTH + Const4.INT_LENGTH + Const4.ID_LENGTH * idsToRead.size()); msg.writeInt(prefetchDepth); msg.writeInt(idsToRead.size()); for (int id : idsToRead) { msg.writeInt(id); } write(msg); } private AbstractQueryResult queryResultFor(final Transaction trans, int queryResultID) { if (queryResultID > 0) { return new LazyClientQueryResult(trans, ClientObjectContainer.this, queryResultID); } return new ClientQueryResult(trans); } private void fetchMissingSlotBuffers(final Transaction transaction, final ArrayList<Integer> missing, final Map<Integer, ByteArrayBuffer> buffers, int prefetchDepth) { if (missing.size() == 0) { return; } final int safePrefetchDepth = Math.max(1, prefetchDepth); sendReadMultipleObjectsMessage(Msg.READ_MULTIPLE_OBJECTS, transaction, safePrefetchDepth, missing); final MsgD response = (MsgD) expectedResponse(Msg.READ_MULTIPLE_OBJECTS); Iterator4<Pair<Integer, ByteArrayBuffer>> slots = new CacheContributingObjectReader((ClientTransaction) transaction, _clientSlotCache, response.payLoad()).buffers(); while (slots.moveNext()) { final Pair<Integer, ByteArrayBuffer> pair = slots.current(); buffers.put(pair.first, pair.second); } } private ByteArrayBuffer[] packSlotBuffers(final int[] ids, final Map<Integer, ByteArrayBuffer> buffers) { final ByteArrayBuffer[] returnValue = new ByteArrayBuffer[buffers.size()]; for (int i=0; i<ids.length; ++i) { returnValue[i] = buffers.get(ids[i]); } return returnValue; } private ArrayList<Integer> populateSlotBuffersFromCache(final Transaction transaction, final int[] ids, final Map<Integer, ByteArrayBuffer> buffers) { final ArrayList<Integer> missing = new ArrayList(); for (int id: ids) { final ByteArrayBuffer slot = _clientSlotCache.get(transaction, id); if (null == slot) { missing.add(id); } else { buffers.put(id, slot); } } return missing; } private ByteArrayBuffer fetchSlotBuffer(final Transaction transaction, final int id, final boolean lastCommitted) { MsgD msg = Msg.READ_READER_BY_ID.getWriterForInts(transaction, new int[]{id, lastCommitted?1:0}); write(msg); final ByteArrayBuffer buffer = ((MReadBytes) expectedResponse(Msg.READ_BYTES)).unmarshall(); return buffer; } @Override protected void fatalStorageShutdown() { shutdownDataStorage(); } /** * @sharpen.property */ public String userName() { return _userName; } @Override public boolean isDeleted(Transaction trans, int id){ // This one really is a hack. // It only helps to get information about the current // transaction. // We need a better strategy for C/S concurrency behaviour. MsgD msg = Msg.TA_IS_DELETED.getWriterForInt(trans, id); write(msg); int res = expectedBufferResponse(Msg.TA_IS_DELETED).readInt(); return res == 1; } public void blockSize(int size){ createBlockConverter(size); _blockSize = size; } @Override protected void closeIdSystem() { // do nothing } public ObjectContainer openSession(){ synchronized(lock()){ return new ObjectContainerSession(this); } } public int serverSideID() { return _serverSideID; } public EventRegistryImpl newEventRegistry(){ return new ClientEventRegistryImpl(this); } public <T> QLin<T> from(Class<T> clazz) { return new QLinRoot<T>(query(), clazz); } public void commitReplication(long replicationRecordId, long timestamp){ synchronized (_lock) { checkReadOnly(); ClientTransaction clientTransaction = (ClientTransaction) transaction(); clientTransaction.preCommit(); write(Msg.COMMIT_REPLICATION.getWriterForLongs(clientTransaction, replicationRecordId, timestamp)); expectedResponse(Msg.OK); clientTransaction.postCommit(); } } }