/** * Obtain the sleep duration. Note that the sleep duration can be negative if the session remains * offline for a period longer than the last execution/process time plus the timeout. * * @param timeMillis A <code>long</code>. * @param latestProcessTimeMillis A <code>long</code>. * @return A <code>long</code>. */ private long getSleepMillis(final long timeMillis, final long latestProcessTimeMillis) { final long latest = Math.max(latestExecutionTime, latestProcessTimeMillis); logger.logVariable("latest", format(latest)); final long sleep = latest + timeout - timeMillis; logger.logVariable("sleep", formatDuration(sleep)); return sleep; }
/** * Complete a transaction. If the transaction belongs to the given transaction context a * rollback/commit will be attempted. * * @param transaction A <code>Transaction</code>. * @param transactionContext A <code>TransactionContext</code>. * @throws HeuristicMixedException * @throws HeuristicRollbackException * @throws RollbackException */ private void completeXA(final Transaction transaction, final TransactionContext context) { if (isSetXAContext() && getXAContext().equals(context)) { unsetXAContext(); if (transaction.isRollbackOnly()) { XA_LOGGER.logInfo("Rolling transaction-{0}.", context); transaction.rollback(); } else { XA_LOGGER.logInfo("Committing transaction-{0}.", context); transaction.commit(); } } else { switch (context.getType()) { case REQUIRES_NEW: case REQUIRED: case SUPPORTED: break; case NEVER: XA_LOGGER.logInfo("No transaction participation-{0}.", context); break; default: LOGGER.logFatal("Unknown transaction type."); XA_LOGGER.logFatal("Unknown transaction type."); Assert.assertUnreachable("Unknown transaction type."); } } }
/** @see com.thinkparity.network.NetworkConnection#connect() */ @Override public void connect() throws NetworkException { logger.logTraceId(); logger.logInfo("{0} - Connect.", getId()); Exception lastX = null; connected = false; for (final NetworkProxy proxy : proxies) { this.proxy = proxy; try { connectViaProxy(); setSocketOptions(); setSocketStreams(); logger.logInfo("{0} - Connected.", getId()); logger.logDebug( "{0} - Local: {1}:{2}", getId(), socket.getLocalAddress().getHostAddress(), socket.getLocalPort()); final InetSocketAddress remoteSocketAddress = (InetSocketAddress) socket.getRemoteSocketAddress(); logger.logDebug( "{0} - Remote: {1}:{2}", getId(), remoteSocketAddress.getAddress().getHostAddress(), remoteSocketAddress.getPort()); connected = true; break; } catch (final SocketException sx) { lastX = sx; } catch (final IOException iox) { lastX = iox; } } if (false == connected) { throw new NetworkException(lastX); } }
/** Stop the stream service. */ public void stop() { logger.logTraceId(); logger.logInfo("Stopping stream service."); synchronized (this) { stopImpl(); } logger.logInfo("Stream service stopped."); }
/** Start the stream service. */ public void start() { logger.logTraceId(); logger.logInfo("Starting stream service."); synchronized (this) { startImpl(); } logger.logInfo("Stream service started."); }
/** Unset the current transaction context. */ private void unsetXAContext() { final TransactionContext currentContext = getXAContext(); if (null == currentContext) { LOGGER.logFatal("Context not set."); XA_LOGGER.logFatal("Context not set."); } else { XA_CONTEXT.remove(getXAThread()); } }
/** @see com.thinkparity.network.NetworkConnection#write(byte[]) */ @Override public void write(final byte[] buffer) throws NetworkException { logger.logTraceId(); logger.logVariable("buffer", buffer); ensureConnected(); try { write(buffer, true); } catch (final IOException iox) { throw new NetworkException(iox); } }
/** @see com.thinkparity.ophelia.model.util.daemon.DaemonJob#invoke() */ @Override public void run() { interruptCount = 0; while (true) { final long now = System.currentTimeMillis(); logger.logVariable("sessionModel.isOnline()", sessionModel.isOnline()); final long latestProcessTime = queueModel.getLatestProcessTimeMillis(); logger.logVariable("latestProcessTime", format(latestProcessTime)); logger.logVariable("latestExecutionTime", format(latestExecutionTime)); logger.logVariable("timeout", formatDuration(timeout)); if (isQueueTimeout(now, latestProcessTime) && isExecutionTimeout(now) && sessionModel.isOnline()) { logger.logInfo("The session has expired and will be reclaimed."); try { queueModel.stopNotificationClient(); } finally { try { sessionModel.logout(); } finally { latestExecutionTime = System.currentTimeMillis(); } } } else { logger.logInfo("The session has not expired."); } try { final long sleepMillis = getSleepMillis(now, latestProcessTime); if (0 > sleepMillis) { /* we break out of the reaper because we have been "offline" * longer than we'd like */ logger.logInfo("Terminating session reaper."); break; } else { logger.logInfo("Session reaper sleeping."); Thread.sleep(getSleepMillis(now, latestProcessTime)); } } catch (final InterruptedException ix) { interruptCount++; logger.logError( ix, "Session reaper interrupted: {0}/{1}", interruptCount, DEFAULT_INTERRUPT_THRESHOLD); if (interruptCount + 1 > interruptThreshold) { interruptCount = 0; /* we break out of the reaper because we have encountered * too many interrupts (unlikely) */ logger.logInfo("Terminating session reaper."); break; } } } }
/** @see com.thinkparity.network.NetworkConnection#read(byte[]) */ @Override public int read(final byte[] buffer) throws NetworkException { logger.logTraceId(); logger.logVariable("buffer", buffer); ensureConnected(); try { return input.read(buffer); } catch (final IOException iox) { throw new NetworkException(iox); } }
/** Stop the backup service. */ private void stopImpl() { logger.logInfo("Stopping backup service ."); try { eventHandler.stop(); final WorkspaceModel workspaceModel = WorkspaceModel.getInstance(); workspaceModel.close(workspace); logger.logInfo("Backup service stopped."); } catch (final Throwable t) { throw new BackupException(t, "Failed to stop backup service."); } }
/** * Set the transaction context. * * @param context A <code>TransactionContext</code>. */ private void setXAContext(final TransactionContext context) { final TransactionContext currentContext = getXAContext(); if (null == currentContext) { XA_CONTEXT.put(getXAThread(), context); } else { LOGGER.logFatal( "Context already set.{0} current:{1}{0} context:{2}", Separator.SystemNewLine, currentContext, context); XA_LOGGER.logFatal( "Context already set.{0} current:{1}{0} context:{2}", Separator.SystemNewLine, currentContext, context); } }
/** * @see * com.thinkparity.ophelia.model.util.configuration.ReconfigureListener#reconfigure(com.thinkparity.ophelia.model.util.configuration.ReconfigureEvent) */ @Override public void reconfigure(final ReconfigureEvent<Configuration> event) { if (event.isReconfigured(CFG_NAME_INTERRUPT_THRESHOLD)) { logger.logInfo("Reconfiguring session reaper's interrupt threshold."); setInterruptThreshold(event.getCurrent()); } else if (event.isReconfigured(CFG_NAME_TIMEOUT)) { logger.logInfo("Reconfiguring session reaper's timeout."); setTimeout(event.getCurrent()); } else if (event.isReconfigured(CFG_NAME_TIMEOUT_MARGIN)) { logger.logInfo("Reconfiguring session reaper's timeout margin."); setTimeoutMargin(event.getCurrent()); } }
/** * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, * java.lang.Object[]) */ public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { LOGGER.logTrace("Invoking method {0} on {1} in {2}.", method, model, workspace); if (null != args && 0 < args.length && LOGGER.isDebugEnabled()) { for (int i = 0; i < args.length; i++) LOGGER.logDebug("args[{0}]:{1}", i, args[i]); } final Object lock; switch (extractLock(method)) { case NONE: lock = new Object(); break; case EXCLUSIVE: // deliberate fall-through case LOCAL_READ: // deliberate fall-through default: lock = workspace; } synchronized (lock) { final Transaction transaction = workspace.getTransaction(); final TransactionContext transactionContext = newXAContext(method); beginXA(transaction, transactionContext); try { /* if the method is annotated as online, ensure that we are * online */ if (isOnline(method)) model.ensureOnline(); model.setInvocationContext( new ModelInvocationContext() { public Object[] getArguments() { return null == args ? NO_ARGS : args; } public Method getMethod() { return method; } }); final Object metricsContext = newMetricsContext(lock, method); ModelInvocationMetrics.begin(metricsContext); final Object result = method.invoke(model, args); ModelInvocationMetrics.end(metricsContext); model.notifyListeners(); return LOGGER.logVariable("result", result); } catch (final InvocationTargetException itx) { rollbackXA(transaction, transactionContext); throw itx.getTargetException(); } catch (final Throwable t) { rollbackXA(transaction, transactionContext); throw t; } finally { completeXA(transaction, transactionContext); } } }
/** * Set socket options pulled from the network configuration. * * @throws SocketException */ private void setSocketOptions() throws SocketException { final NetworkConfiguration configuration = networkImpl.getConfiguration(); final Integer soLinger = configuration.getSoLinger(protocol, address, proxy, this); final boolean linger = -1 < soLinger ? true : false; logger.logDebug("{0} - linger:{1}", getId(), linger); logger.logDebug("{0} - soLinger:{1}", getId(), soLinger); final Integer soTimeout = configuration.getSoTimeout(protocol, address, proxy, this); logger.logDebug("{0} - soTimeout:{1}", getId(), soTimeout); final Boolean tcpNoDelay = configuration.getTcpNoDelay(protocol, address, proxy, this); logger.logDebug("{0} - tcpNoDelay:{1}", getId(), tcpNoDelay); socket.setSoLinger(linger, linger ? soLinger : -1); socket.setSoTimeout(soTimeout); socket.setTcpNoDelay(tcpNoDelay); }
/** * Determine whether or not the last execution was before the time in millis. * * @param timeMillis A <code>long</code>. * @return True if the time minus the last execution time is greater than the timeout. */ private boolean isExecutionTimeout(final long timeMillis) { final long executionDuration = timeMillis - latestExecutionTime; logger.logVariable("executionDuration", formatDuration(executionDuration)); if (0 > executionDuration) { return false; } else { return (timeout - timeoutMargin) < executionDuration; } }
/** * Determine whether or not the last read of the queue was before the time in millis. * * @param timeMillis A <code>long</code>. * @return True if the time minus the last queue process time is greater than the timeout. */ private boolean isQueueTimeout(final long timeMillis, final long latestProcessTimeMillis) { final long queueDuration = timeMillis - latestProcessTimeMillis; logger.logVariable("queueDuration", formatDuration(queueDuration)); if (0 > queueDuration) { return false; } else { return (timeout - timeoutMargin) < queueDuration; } }
/** Start the backup service. This involves opening the backup workspace. */ private void startImpl() { logger.logInfo("Starting backup service."); try { environment = readBackupEnvironment(); workspace = readBackupWorkspace(); Assert.assertNotNull(environment, "Backup environment not yet initialized."); Assert.assertNotNull(workspace, "Backup workspace not yet initialized."); final WorkspaceModel workspaceModel = WorkspaceModel.getInstance(); if (!workspaceModel.isInitialized(workspace)) { workspaceModel.initialize( new ProcessAdapter() { @Override public void beginProcess() {} @Override public void beginStep(final Step step, final Object data) {} @Override public void determineSteps(final Integer steps) {} @Override public void endProcess() {} @Override public void endStep(final Step step) {} }, new InitializeMediator() { public Boolean confirmRestorePremium() { return null; } public Boolean confirmRestoreStandard() { return null; } }, workspace, null); } eventHandler.start(getModelFactory()); logger.logInfo("Backup service started."); } catch (final Throwable t) { throw new BackupException(t, "Failed to start backup service."); } }
/** Stop stream service implementation. */ private void stopImpl() { logger.logTraceId(); try { server.stop(Boolean.TRUE); } catch (final Throwable t) { throw new StreamException(t); } finally { server = null; } }
/** * Obtain the annotated transaction type for the method. If the transaction type is not defined on * the method; attempt to retreive it from the class. If no transaction annotation is found * assert. * * @param method A <code>Method</code>. * @return A <code>TransactionType</code>. */ private TransactionType extractXAType(final Method method) { ThinkParityTransaction transaction = method.getAnnotation(ThinkParityTransaction.class); if (null == transaction) { transaction = method.getDeclaringClass().getAnnotation(ThinkParityTransaction.class); } if (null == transaction) { LOGGER.logFatal( "Method {0} of class {1} does not define transactional behaviour.", method.getName(), method.getDeclaringClass().toString()); XA_LOGGER.logFatal( "Method {0} of class {1} does not define transactional behaviour.", method.getName(), method.getDeclaringClass().toString()); } Assert.assertNotNull( transaction, "Method {0} of class {1} does not define transactional behaviour.", method.getName(), method.getDeclaringClass().toString()); return transaction.value(); }
/** Start stream service implementation. */ private void startImpl() { logger.logTraceId(); try { server = new StreamServer( new File(properties.getProperty(JivePropertyNames.THINKPARITY_STREAM_ROOT)), environment); server.start(); } catch (final Throwable t) { server = null; throw new StreamException(t); } }
/** @see com.thinkparity.network.NetworkConnection#disconnect() */ @Override public void disconnect() { logger.logTraceId(); logger.logInfo("{0} - Disconnect", getId()); if (isConnected()) { try { if (Boolean.FALSE == protocol.isSecure()) { socket.shutdownInput(); } } catch (final IOException iox) { logger.logWarning(iox, "{0} - Error disconnecting.", getId()); } finally { try { if (Boolean.FALSE == protocol.isSecure()) { socket.shutdownOutput(); } } catch (final IOException iox) { logger.logWarning(iox, "{0} - Error disconnecting.", getId()); } finally { try { socket.close(); } catch (final IOException iox) { logger.logWarning(iox, "{0} - Error disconnecting.", getId()); } finally { socket = null; input = null; output = null; logger.logInfo("{0} - Disconnected", getId()); connected = false; } } } } else { logger.logWarning("{0} - Is not connected.", getId()); } }
/** * Rollback a transaction. If the transaction belongs to the context; a rollback will be * attempted. If it does not; depending on the context type; the transaction's rollback only flag * will be set. * * @param transaction A <code>Transaction</code>. * @param transactionContext A <code>TransactionContext</code>. */ private void rollbackXA(final Transaction transaction, final TransactionContext context) { switch (context.getType()) { case REQUIRES_NEW: case REQUIRED: XA_LOGGER.logInfo("Set rollback only-{0}.", context); transaction.setRollbackOnly(); break; case NEVER: XA_LOGGER.logInfo("No transaction participation-{0}.", context); break; case SUPPORTED: if (transaction.isActive()) { XA_LOGGER.logInfo("Set rollback only-{0}.", context); transaction.setRollbackOnly(); } else { XA_LOGGER.logTrace("Transaction for {0} is not active.", context); } break; default: LOGGER.logFatal("Unknown transaction type."); XA_LOGGER.logFatal("Unknown transaction type."); Assert.assertUnreachable("Unknown transaction type."); } }
/** @see com.thinkparity.network.NetworkConnection#getId() */ @Override public String getId() { logger.logTraceId(); return id; }
/** * Obtain the proxy. * * @return A <code>NetworkProxy</code>. */ NetworkProxy getProxy() { logger.logTraceId(); return proxy; }
/** * Obtain the protocol. * * @return A <code>NetworkProtocol</code>. */ NetworkProtocol getProtocol() { logger.logTraceId(); return protocol; }
/** @see com.thinkparity.network.NetworkConnection#getAddress() */ public NetworkAddress getAddress() { logger.logTraceId(); return address; }
/** @see com.thinkparity.network.NetworkConnection#isConnected() */ @Override public Boolean isConnected() { logger.logTraceId(); return connected && socket.isConnected(); }
/** @see com.thinkparity.network.NetworkConnection#getSocket() */ @Override public Socket getSocket() { logger.logTraceId(); return socket; }
/** * Begin a transaction within a context if required. The context will define a transaction type * and if the type requires a transaction one will be begun. * * @param transaction A <code>Transaction</code>. * @param context A <code>TransactionContext</code>. * @throws NamingException * @throws NotSupportedException */ private void beginXA(final Transaction transaction, final TransactionContext context) { XA_LOGGER.logVariable("transaction", transaction); XA_LOGGER.logVariable("context", context); /* when the transaction context is set, nothing is done * * when the transaction context is null, no transaction boundary is * currently set, so we need to check whether nor not to begin the * transaction based upon the type */ if (isSetXAContext()) { switch (context.getType()) { case REQUIRED: XA_LOGGER.logInfo("{0}Join {1} with {2}.", "\t\t", context, getXAContext()); break; case REQUIRES_NEW: LOGGER.logFatal("New transaction required-{0}", context); XA_LOGGER.logFatal("New transaction required-{0}", context); Assert.assertUnreachable("New transaction required-{0}", context); break; case NEVER: XA_LOGGER.logInfo("{0}No transaction participation-{1}.", "\t\t", context); break; case SUPPORTED: break; default: LOGGER.logFatal("Unknown transaction type."); XA_LOGGER.logFatal("Unknown transaction type."); Assert.assertUnreachable("Unknown transaction type."); } } else { switch (context.getType()) { case REQUIRES_NEW: case REQUIRED: setXAContext(context); transaction.begin(); XA_LOGGER.logInfo("Begin transaction-{0}.", context); break; case NEVER: XA_LOGGER.logInfo("{0}No transaction participation-{1}.", "\t\t", context); break; case SUPPORTED: break; default: LOGGER.logFatal("Unknown transaction type."); XA_LOGGER.logFatal("Unknown transaction type."); Assert.assertUnreachable("Unknown transaction type."); } } }