/** * 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; }
/** @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; } } } }
/** * 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."); } } }
/** * 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; } }
/** * 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; } }
/** @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.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); } }
/** * @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); } } }