/** * Private method that determines whether a given task should be re-tried based on the given * {@code Throwable} that caused failure. If this returns {@code true} then the task should be * re-tried. Otherwise, the task should be dropped. */ private boolean shouldRetry(ScheduledTaskImpl task, Throwable t) { // NOTE: as a first-pass implementation this simply instructs the // caller to try again if retry is requested, but other strategies // (like the number of times re-tried) might be considered later if ((t instanceof ExceptionRetryStatus) && (((ExceptionRetryStatus) t).shouldRetry())) { return true; } // we're not re-trying the task, so log that it's being dropped if (logger.isLoggable(Level.WARNING)) { if (task.isRecurring()) { logger.logThrow( Level.WARNING, t, "skipping a recurrence of " + "a task that failed with a non-retryable " + "exception: {0}", task); } else { logger.logThrow( Level.WARNING, t, "dropping a task that " + "failed with a non-retryable exception: {0}", task); } } return false; }
/** * Creates an instance of {@code TransactionSchedulerImpl}. * * @param properties the {@code Properties} for the system * @param transactionCoordinator the {@code TransactionCoordinator} used by the system to manage * transactions * @param profileCollectorHandle the {@code ProfileCollectorHandler} used to manage collection of * per-task profiling data * @param accessCoordinator the {@code AccessCoordinator} used by the system to managed shared * data * @throws InvocationTargetException if there is a failure initializing the {@code SchedulerQueue} * @throws Exception if there is any failure creating the scheduler */ TransactionSchedulerImpl( Properties properties, TransactionCoordinator transactionCoordinator, ProfileCollectorHandle profileCollectorHandle, AccessCoordinatorHandle accessCoordinator) throws Exception { logger.log(Level.CONFIG, "Creating a Transaction Scheduler"); if (properties == null) { throw new NullPointerException("Properties cannot be null"); } if (transactionCoordinator == null) { throw new NullPointerException("Coordinator cannot be null"); } if (profileCollectorHandle == null) { throw new NullPointerException("Collector handle cannot be null"); } if (accessCoordinator == null) { throw new NullPointerException("AccessCoordinator cannot be null"); } this.transactionCoordinator = transactionCoordinator; this.profileCollectorHandle = profileCollectorHandle; this.accessCoordinator = accessCoordinator; String queueName = properties.getProperty(SCHEDULER_QUEUE_PROPERTY, DEFAULT_SCHEDULER_QUEUE); try { Class<?> queueClass = Class.forName(queueName); Constructor<?> queueCtor = queueClass.getConstructor(Properties.class); this.backingQueue = (SchedulerQueue) (queueCtor.newInstance(properties)); } catch (InvocationTargetException e) { if (logger.isLoggable(Level.CONFIG)) { logger.logThrow( Level.CONFIG, e.getCause(), "Queue {0} " + "failed to initialize", queueName); } throw e; } catch (Exception e) { if (logger.isLoggable(Level.CONFIG)) { logger.logThrow(Level.CONFIG, e, "Queue {0} unavailable", queueName); } throw e; } // startup the requested number of consumer threads // NOTE: this is a simple implmentation to replicate the previous // behvavior, with the assumption that it will change if the // scheduler starts trying to add or drop consumers adaptively int requestedThreads = Integer.parseInt( properties.getProperty(CONSUMER_THREADS_PROPERTY, DEFAULT_CONSUMER_THREADS)); if (logger.isLoggable(Level.CONFIG)) { logger.log(Level.CONFIG, "Using {0} transaction consumer threads", requestedThreads); } this.executor = Executors.newCachedThreadPool(); for (int i = 0; i < requestedThreads; i++) { executor.submit(new TaskConsumer()); } }
/** {@inheritDoc} */ public void doShutdown() { final IoFuture<?, ?> future = acceptFuture; acceptFuture = null; if (future != null) { future.cancel(true); } if (acceptor != null) { try { acceptor.close(); } catch (IOException e) { logger.logThrow(Level.FINEST, e, "closing acceptor throws"); // swallow exception } } if (asyncChannelGroup != null) { asyncChannelGroup.shutdown(); boolean groupShutdownCompleted = false; try { groupShutdownCompleted = asyncChannelGroup.awaitTermination(1, TimeUnit.SECONDS); } catch (InterruptedException e) { logger.logThrow(Level.FINEST, e, "shutdown acceptor interrupted"); Thread.currentThread().interrupt(); } if (!groupShutdownCompleted) { logger.log(Level.WARNING, "forcing async group shutdown"); try { asyncChannelGroup.shutdownNow(); } catch (IOException e) { logger.logThrow(Level.FINEST, e, "shutdown acceptor throws"); // swallow exception } } } logger.log(Level.FINEST, "acceptor shutdown"); if (exporter != null) { try { exporter.unexport(); logger.log(Level.FINEST, "client session server unexported"); } catch (RuntimeException e) { logger.logThrow(Level.FINEST, e, "unexport server throws"); // swallow exception } } for (ClientSessionHandler handler : handlers.values()) { handler.shutdown(); } handlers.clear(); flushContextsThread.interrupt(); }
/** * {@inheritDoc} * * <p>Enqueues a send event to this client session's event queue for servicing. */ public ClientSession send(ByteBuffer message) { try { if (message.remaining() > SimpleSgsProtocol.MAX_PAYLOAD_LENGTH) { throw new IllegalArgumentException( "message too long: " + message.remaining() + " > " + SimpleSgsProtocol.MAX_PAYLOAD_LENGTH); } else if (!isConnected()) { throw new IllegalStateException("client session not connected"); } /* * TODO: Possible optimization: if we have passed our own special * buffer to the app, we can detect that here and possibly avoid a * copy. Our special buffer could be one we passed to the * receivedMessage callback, or we could add a special API to * pre-allocate buffers. -JM */ ByteBuffer buf = ByteBuffer.wrap(new byte[1 + message.remaining()]); buf.put(SimpleSgsProtocol.SESSION_MESSAGE).put(message).flip(); addEvent(new SendEvent(buf.array())); if (sessionService.sendMessageOp != null) { sessionService.sendMessageOp.report(); } return this; } catch (RuntimeException e) { if (logger.isLoggable(Level.FINEST)) { logger.logThrow( Level.FINEST, e, "send message:{0} throws", HexDumper.format(message, 0x50)); } throw e; } }
/** {@inheritDoc} */ public void doShutdown() { if (protocolAcceptor != null) { try { protocolAcceptor.close(); } catch (IOException ignore) { } } for (ClientSessionHandler handler : handlers.values()) { handler.shutdown(); } handlers.clear(); if (exporter != null) { try { exporter.unexport(); logger.log(Level.FINEST, "client session server unexported"); } catch (RuntimeException e) { logger.logThrow(Level.FINEST, e, "unexport server throws"); // swallow exception } } synchronized (flushContextsLock) { flushContextsLock.notifyAll(); } }
/** {@inheritDoc} */ public Channel getChannel(String name) { try { return ChannelImpl.getInstance(name); } catch (RuntimeException e) { logger.logThrow(Level.FINEST, e, "getChannel:{0} throws"); throw e; } }
/** {@inheritDoc} */ public Channel createChannel(String name, ChannelListener listener, Delivery delivery) { try { Channel channel = ChannelImpl.newInstance(name, listener, delivery, writeBufferSize); return channel; } catch (RuntimeException e) { logger.logThrow(Level.FINEST, e, "createChannel:{0} throws"); throw e; } }
/** * Returns a set of protocol descriptors for the specified {@code nodeId}, or {@code null} if * there are no descriptors for the node. This method must be run outside a transaction. */ Set<ProtocolDescriptor> getProtocolDescriptors(long nodeId) { checkNonTransactionalContext(); GetProtocolDescriptorsTask protocolDescriptorsTask = new GetProtocolDescriptorsTask(nodeId); try { transactionScheduler.runTask(protocolDescriptorsTask, taskOwner); return protocolDescriptorsTask.descriptors; } catch (Exception e) { logger.logThrow(Level.WARNING, e, "GetProtocolDescriptorsTask for node:{0} throws", nodeId); return null; } }
/** {@inheritDoc} */ protected void doShutdown() { logger.log(Level.FINEST, "shutdown"); try { if (exporter != null) { exporter.unexport(); } } catch (RuntimeException e) { logger.logThrow(Level.FINEST, e, "unexport server throws"); // swallow exception } }
/** {@inheritDoc} */ public void run() { String channelServerKey = getChannelServerKey(nodeId); try { ManagedSerializable wrappedProxy = (ManagedSerializable) getDataService().getServiceBinding(channelServerKey); channelServer = (ChannelServer) wrappedProxy.get(); } catch (NameNotBoundException e) { channelServer = null; } catch (ObjectNotFoundException e) { logger.logThrow( Level.SEVERE, e, "ChannelServer binding:{0} exists, but object removed", channelServerKey); channelServer = null; } }
/** * Runs a transactional task to query the status of the node with the specified {@code nodeId} and * returns {@code true} if the node is alive and {@code false} otherwise. * * <p>This method must be called from outside a transaction or {@code IllegalStateException} will * be thrown. */ boolean isAlive(long nodeId) { // Make sure that we're not in a transactional context. try { getTransaction(); throw new IllegalStateException("isAlive called from a transactional context"); } catch (TransactionNotActiveException e) { } try { CheckNodeStatusTask nodeStatus = new CheckNodeStatusTask(nodeId); transactionScheduler.runTask(nodeStatus, taskOwner); return nodeStatus.isAlive; } catch (Exception e) { if (logger.isLoggable(Level.WARNING)) { logger.logThrow(Level.WARNING, e, "running CheckNodeStatusTask throws"); } // TBD: is this the correct value to return? return false; } }
/** {@inheritDoc} */ public void doReady() throws Exception { channelService = txnProxy.getService(ChannelServiceImpl.class); try { protocolAcceptor.accept(protocolListener); } catch (IOException e) { if (logger.isLoggable(Level.CONFIG)) { logger.logThrow(Level.CONFIG, e, "Failed to start accepting connections"); } throw e; } transactionScheduler.runTask( new AbstractKernelRunnable("AddProtocolDescriptorMapping") { public void run() { getProtocolDescriptorsMap() .put(localNodeId, Collections.singleton(protocolAcceptor.getDescriptor())); } }, taskOwner); }
/** * Returns the proxy for the client session server on the specified {@code nodeId}, or {@code * null} if no server exists. * * @param nodeId a node ID * @return the proxy for the client session server on the specified {@code nodeId}, or {@code * null} */ ClientSessionServer getClientSessionServer(long nodeId) { if (nodeId == localNodeId) { return serverImpl; } else { String sessionServerKey = getClientSessionServerKey(nodeId); try { ManagedSerializable wrappedProxy = (ManagedSerializable) dataService.getServiceBinding(sessionServerKey); return (ClientSessionServer) wrappedProxy.get(); } catch (NameNotBoundException e) { return null; } catch (ObjectNotFoundException e) { logger.logThrow( Level.SEVERE, e, "ClientSessionServer binding:{0} exists, " + "but object removed", sessionServerKey); throw e; } } }
/** * Creates each of the <code>Service</code>s in order, in preparation for starting up an * application. At completion, this schedules an <code>AppStartupRunner</code> to finish * application startup. */ public void run() { if (logger.isLoggable(Level.CONFIG)) logger.log(Level.CONFIG, "{0}: starting services", appName); // create an empty context and register with the scheduler ComponentRegistryImpl services = new ComponentRegistryImpl(); ComponentRegistryImpl managers = new ComponentRegistryImpl(); AppKernelAppContext ctx = new AppKernelAppContext(appName, services, managers); MasterTaskScheduler scheduler = systemRegistry.getComponent(MasterTaskScheduler.class); try { scheduler.registerApplication(ctx, appProperties); } catch (Exception e) { if (logger.isLoggable(Level.SEVERE)) logger.logThrow(Level.SEVERE, e, "{0}: failed app scheduler " + "setup", appName); return; } // create the application's identity, and set as the current owner IdentityImpl id = new IdentityImpl("app:" + appName); TaskOwnerImpl owner = new TaskOwnerImpl(id, ctx); ThreadState.setCurrentOwner(owner); // get the managers and services that we're using HashSet<Object> managerSet = new HashSet<Object>(); try { fetchServices(services, managerSet); } catch (Exception e) { if (logger.isLoggable(Level.SEVERE)) logger.logThrow(Level.SEVERE, e, "{0}: failed to create " + "services", appName); ctx.shutdownServices(); return; } // register any profiling managers and fill in the manager registry for (Object manager : managerSet) { if (profileRegistrar != null) { if (manager instanceof ProfileProducer) ((ProfileProducer) manager).setProfileRegistrar(profileRegistrar); } managers.addComponent(manager); } // with the managers created, setup the final context and owner ctx = new AppKernelAppContext(appName, services, managers); owner = new TaskOwnerImpl(id, ctx); ThreadState.setCurrentOwner(owner); // notify all of the services that the application state is ready try { for (Object s : services) ((Service) s).ready(); } catch (Exception e) { logger.logThrow( Level.SEVERE, e, "{0}: failed when notifying services that application is " + "ready", appName); // shutdown all of the services ctx.shutdownServices(); return; } // At this point the services are now created, so the final step // is to try booting the application by running a special // KernelRunnable in an unbounded transaction. Note that if we're // running as a "server" then we don't actually start an app if (!appProperties .getProperty(StandardProperties.APP_LISTENER) .equals(StandardProperties.APP_LISTENER_NONE)) { AppStartupRunner startupRunner = new AppStartupRunner(ctx, appProperties); UnboundedTransactionRunner unboundedTransactionRunner = new UnboundedTransactionRunner(startupRunner); try { if (logger.isLoggable(Level.CONFIG)) logger.log(Level.CONFIG, "{0}: starting application", appName); // run the startup task, notifying the kernel on success scheduler.runTask(unboundedTransactionRunner, owner, true); kernel.contextReady(owner, true); } catch (Exception e) { if (logger.isLoggable(Level.CONFIG)) logger.logThrow(Level.CONFIG, e, "{0}: failed to " + "start application", appName); return; } } else { // we're running without an application, so we're finished kernel.contextReady(owner, false); } if (logger.isLoggable(Level.CONFIG)) logger.log(Level.CONFIG, "{0}: finished service config runner", appName); }
/** * Private helper that creates the services and their associated managers, taking care to call out * the standard services first, because we need to get the ordering constant and make sure that * they're all present. */ private void fetchServices(ComponentRegistryImpl services, HashSet<Object> managerSet) throws Exception { // before we start, figure out if we're running with only a sub-set // of services, in which case there should be no external services String finalService = appProperties.getProperty(StandardProperties.FINAL_SERVICE); StandardService finalStandardService = null; String externalServices = appProperties.getProperty(StandardProperties.SERVICES); String externalManagers = appProperties.getProperty(StandardProperties.MANAGERS); if (finalService != null) { if ((externalServices != null) || (externalManagers != null)) throw new IllegalArgumentException( "Cannot specify external " + "services and a final " + "service"); // validate the final service try { finalStandardService = Enum.valueOf(StandardService.class, finalService); } catch (IllegalArgumentException iae) { if (logger.isLoggable(Level.SEVERE)) logger.logThrow(Level.SEVERE, iae, "Invalid final " + "service name: {0}", finalService); throw iae; } // make sure we're not running with an application if (!appProperties .getProperty(StandardProperties.APP_LISTENER) .equals(StandardProperties.APP_LISTENER_NONE)) throw new IllegalArgumentException( "Cannot specify an app " + "listener and a final " + "service"); } else { finalStandardService = StandardService.LAST_SERVICE; } // load the data service String dataServiceClass = appProperties.getProperty(StandardProperties.DATA_SERVICE, DEFAULT_DATA_SERVICE); String dataManagerClass = appProperties.getProperty(StandardProperties.DATA_MANAGER, DEFAULT_DATA_MANAGER); services.addComponent(setupService(dataServiceClass, dataManagerClass, managerSet)); // load the watch-dog service, which has no associated manager if (StandardService.WatchdogService.ordinal() > finalStandardService.ordinal()) return; String watchdogServiceClass = appProperties.getProperty(StandardProperties.WATCHDOG_SERVICE, DEFAULT_WATCHDOG_SERVICE); Service watchdogService = createService(Class.forName(watchdogServiceClass)); services.addComponent(watchdogService); if (watchdogService instanceof ProfileProducer) { if (profileRegistrar != null) ((ProfileProducer) watchdogService).setProfileRegistrar(profileRegistrar); } // load the node mapping service, which has no associated manager if (StandardService.NodeMappingService.ordinal() > finalStandardService.ordinal()) return; String nodemapServiceClass = appProperties.getProperty( StandardProperties.NODE_MAPPING_SERVICE, DEFAULT_NODE_MAPPING_SERVICE); Service nodemapService = createService(Class.forName(nodemapServiceClass)); services.addComponent(nodemapService); if (nodemapService instanceof ProfileProducer) { if (profileRegistrar != null) ((ProfileProducer) nodemapService).setProfileRegistrar(profileRegistrar); } // load the task service if (StandardService.TaskService.ordinal() > finalStandardService.ordinal()) return; String taskServiceClass = appProperties.getProperty(StandardProperties.TASK_SERVICE, DEFAULT_TASK_SERVICE); String taskManagerClass = appProperties.getProperty(StandardProperties.TASK_MANAGER, DEFAULT_TASK_MANAGER); services.addComponent(setupService(taskServiceClass, taskManagerClass, managerSet)); // load the client session service, which has no associated manager if (StandardService.ClientSessionService.ordinal() > finalStandardService.ordinal()) return; String clientSessionServiceClass = appProperties.getProperty( StandardProperties.CLIENT_SESSION_SERVICE, DEFAULT_CLIENT_SESSION_SERVICE); Service clientSessionService = createService(Class.forName(clientSessionServiceClass)); services.addComponent(clientSessionService); if (clientSessionService instanceof ProfileProducer) { if (profileRegistrar != null) ((ProfileProducer) clientSessionService).setProfileRegistrar(profileRegistrar); } // load the channel service if (StandardService.ChannelService.ordinal() > finalStandardService.ordinal()) return; String channelServiceClass = appProperties.getProperty(StandardProperties.CHANNEL_SERVICE, DEFAULT_CHANNEL_SERVICE); String channelManagerClass = appProperties.getProperty(StandardProperties.CHANNEL_MANAGER, DEFAULT_CHANNEL_MANAGER); services.addComponent(setupService(channelServiceClass, channelManagerClass, managerSet)); // finally, load any external services and their associated managers if ((externalServices != null) && (externalManagers != null)) { String[] serviceClassNames = externalServices.split(":", -1); String[] managerClassNames = externalManagers.split(":", -1); if (serviceClassNames.length != managerClassNames.length) { if (logger.isLoggable(Level.SEVERE)) logger.log( Level.SEVERE, "External service count " + "({0}) does not match manager count ({1}).", serviceClassNames.length, managerClassNames.length); throw new IllegalArgumentException("Mis-matched service " + "and manager count"); } for (int i = 0; i < serviceClassNames.length; i++) { if (!managerClassNames[i].equals("")) { services.addComponent( setupService(serviceClassNames[i], managerClassNames[i], managerSet)); } else { Class<?> serviceClass = Class.forName(serviceClassNames[i]); Service service = createService(serviceClass); services.addComponent(service); if ((profileRegistrar != null) && (service instanceof ProfileProducer)) ((ProfileProducer) service).setProfileRegistrar(profileRegistrar); } } } }
/** * Invokes the {@code disconnected} callback on this session's {@code ClientSessionListener} (if * present and {@code notify} is {@code true}), removes the listener and its binding (if present), * and then removes this session and its bindings from the specified {@code dataService}. If the * bindings have already been removed from the {@code dataService} this method takes no action. * This method should only be called within a transaction. * * @param dataService a data service * @param graceful {@code true} if disconnection is graceful, and {@code false} otherwise * @param notify {@code true} if the {@code disconnected} callback should be invoked * @throws TransactionException if there is a problem with the current transaction */ void notifyListenerAndRemoveSession( final DataService dataService, final boolean graceful, boolean notify) { String sessionKey = getSessionKey(); String sessionNodeKey = getSessionNodeKey(); String listenerKey = getListenerKey(); String eventQueueKey = getEventQueueKey(); /* * Get ClientSessionListener, and remove its binding and * wrapper if applicable. The listener may not be bound * in the data service if: the AppListener.loggedIn callback * either threw a non-retryable exception or returned a * null listener, or the application removed the * ClientSessionListener object from the data service. */ ClientSessionListener listener = null; try { ManagedObject obj = dataService.getServiceBinding(listenerKey); dataService.removeServiceBinding(listenerKey); if (obj instanceof ListenerWrapper) { dataService.removeObject(obj); listener = ((ListenerWrapper) obj).get(); } else { listener = (ClientSessionListener) obj; } } catch (NameNotBoundException e) { logger.logThrow(Level.FINE, e, "removing ClientSessionListener for session:{0} throws", this); } /* * Remove event queue and associated binding. */ try { ManagedObject eventQueue = dataService.getServiceBinding(eventQueueKey); dataService.removeServiceBinding(eventQueueKey); dataService.removeObject(eventQueue); } catch (NameNotBoundException e) { logger.logThrow(Level.FINE, e, "removing EventQueue for session:{0} throws", this); } /* * Invoke listener's 'disconnected' callback if 'notify' * is true and a listener exists for this client session. If the * 'disconnected' callback throws a non-retryable exception, * schedule a task to remove this session and its associated * bindings without invoking the listener, and rethrow the * exception so that the currently executing transaction aborts. */ if (notify && listener != null) { try { listener.disconnected(graceful); } catch (RuntimeException e) { if (!isRetryableException(e)) { logger.logThrow( Level.WARNING, e, "invoking disconnected callback on listener:{0} " + " for session:{1} throws", listener, this); sessionService.scheduleTask( new AbstractKernelRunnable() { public void run() { ClientSessionImpl sessionImpl = ClientSessionImpl.getSession(dataService, id); sessionImpl.notifyListenerAndRemoveSession(dataService, graceful, false); } }, identity); } throw e; } } /* * Remove this session's state and bindings. */ try { dataService.removeServiceBinding(sessionKey); dataService.removeServiceBinding(sessionNodeKey); dataService.removeObject(this); } catch (NameNotBoundException e) { logger.logThrow(Level.WARNING, e, "session binding already removed:{0}", sessionKey); } /* * Remove this session's wrapper object, if it still exists. */ try { dataService.removeObject(wrappedSessionRef.get()); } catch (ObjectNotFoundException e) { // already removed } }
/** * Constructs an instance of this class with the specified properties. * * @param properties service properties * @param systemRegistry system registry * @param txnProxy transaction proxy * @throws Exception if a problem occurs when creating the service */ public ClientSessionServiceImpl( Properties properties, ComponentRegistry systemRegistry, TransactionProxy txnProxy) throws Exception { super(properties, systemRegistry, txnProxy, logger); logger.log(Level.CONFIG, "Creating ClientSessionServiceImpl properties:{0}", properties); PropertiesWrapper wrappedProps = new PropertiesWrapper(properties); try { appPort = wrappedProps.getRequiredIntProperty(StandardProperties.APP_PORT, 1, 65535); /* * Get the property for controlling session event processing. */ eventsPerTxn = wrappedProps.getIntProperty( EVENTS_PER_TXN_PROPERTY, DEFAULT_EVENTS_PER_TXN, 1, Integer.MAX_VALUE); readBufferSize = wrappedProps.getIntProperty( READ_BUFFER_SIZE_PROPERTY, DEFAULT_READ_BUFFER_SIZE, 8192, Integer.MAX_VALUE); writeBufferSize = wrappedProps.getIntProperty( WRITE_BUFFER_SIZE_PROPERTY, DEFAULT_WRITE_BUFFER_SIZE, 8192, Integer.MAX_VALUE); /* * Export the ClientSessionServer. */ int serverPort = wrappedProps.getIntProperty(SERVER_PORT_PROPERTY, DEFAULT_SERVER_PORT, 0, 65535); serverImpl = new SessionServerImpl(); exporter = new Exporter<ClientSessionServer>(ClientSessionServer.class); try { int port = exporter.export(serverImpl, serverPort); serverProxy = exporter.getProxy(); if (logger.isLoggable(Level.CONFIG)) { logger.log(Level.CONFIG, "export successful. port:{0,number,#}", port); } } catch (Exception e) { try { exporter.unexport(); } catch (RuntimeException re) { } throw e; } /* * Get services and initialize service-related and other * instance fields. */ identityManager = systemRegistry.getComponent(IdentityCoordinator.class); flushContextsThread.start(); contextFactory = new ContextFactory(txnProxy); watchdogService = txnProxy.getService(WatchdogService.class); nodeMapService = txnProxy.getService(NodeMappingService.class); taskService = txnProxy.getService(TaskService.class); localNodeId = watchdogService.getLocalNodeId(); watchdogService.addRecoveryListener(new ClientSessionServiceRecoveryListener()); int acceptorBacklog = wrappedProps.getIntProperty(ACCEPTOR_BACKLOG_PROPERTY, DEFAULT_ACCEPTOR_BACKLOG); /* * Check service version. */ transactionScheduler.runTask( new AbstractKernelRunnable() { public void run() { checkServiceVersion(VERSION_KEY, MAJOR_VERSION, MINOR_VERSION); } }, taskOwner); /* * Store the ClientSessionServer proxy in the data store. */ transactionScheduler.runTask( new AbstractKernelRunnable() { public void run() { dataService.setServiceBinding( getClientSessionServerKey(localNodeId), new ManagedSerializable<ClientSessionServer>(serverProxy)); } }, taskOwner); /* * Listen for incoming client connections. */ InetSocketAddress listenAddress = new InetSocketAddress(appPort); AsynchronousChannelProvider provider = // TODO fetch from config AsynchronousChannelProvider.provider(); asyncChannelGroup = // TODO fetch from config provider.openAsynchronousChannelGroup(Executors.newCachedThreadPool()); acceptor = provider.openAsynchronousServerSocketChannel(asyncChannelGroup); try { acceptor.bind(listenAddress, acceptorBacklog); if (logger.isLoggable(Level.CONFIG)) { logger.log(Level.CONFIG, "bound to port:{0,number,#}", getListenPort()); } } catch (Exception e) { logger.logThrow(Level.WARNING, e, "acceptor failed to listen on {0}", listenAddress); try { acceptor.close(); } catch (IOException ioe) { logger.logThrow(Level.WARNING, ioe, "problem closing acceptor"); } throw e; } // TBD: listen for UNRELIABLE connections as well? } catch (Exception e) { if (logger.isLoggable(Level.CONFIG)) { logger.logThrow(Level.CONFIG, e, "Failed to create ClientSessionServiceImpl"); } doShutdown(); throw e; } }
/** * Private method that executes a single task, creating the transaction state and handling re-try * as appropriate. If the thread calling this method is interrupted before the task can complete * then this method attempts to re-schedule the task to run in another thread if {@code * retryOnInterruption} is {@code true} and always re-throws the associated {@code * InterruptedException}. Providing {@code true} for the {@code unbounded} parameter results in a * transaction with timeout value as specified by the value of the {@code * TransactionCoordinator.TXN_UNBOUNDED_TIMEOUT_PROPERTY} property. * * <p>This method returns {@code true} if the task was completed or failed permanently, and {@code * false} otherwise. If {@code false} is returned then the task is scheduled to be re-tried at * some point in the future, possibly by another thread, by this method. The caller may query the * status of the task and wait for the task to complete or fail permanently through the {@code * ScheduledTaskImpl} interface. */ private boolean executeTask( ScheduledTaskImpl task, boolean unbounded, boolean retryOnInterruption) throws InterruptedException { logger.log(Level.FINEST, "starting a new transactional task"); // store the current owner, and then push the new thread detail Identity parent = ContextResolver.getCurrentOwner(); ContextResolver.setTaskState(kernelContext, task.getOwner()); try { // keep trying to run the task until we succeed, tracking how // many tries it actually took while (true) { if (!task.setRunning(true)) { // this task is already finished return true; } // NOTE: We could report the two queue sizes separately, // so we should figure out how we want to represent these int waitSize = backingQueue.getReadyCount() + dependencyCount.get(); profileCollectorHandle.startTask( task.getTask(), task.getOwner(), task.getStartTime(), waitSize); task.incrementTryCount(); Transaction transaction = null; try { // setup the transaction state TransactionHandle handle = transactionCoordinator.createTransaction(unbounded); transaction = handle.getTransaction(); ContextResolver.setCurrentTransaction(transaction); try { // notify the profiler and access coordinator profileCollectorHandle.noteTransactional(transaction.getId()); accessCoordinator.notifyNewTransaction( transaction, task.getStartTime(), task.getTryCount()); // run the task in the new transactional context task.getTask().run(); } finally { // regardless of the outcome, always clear the current // transaction state before proceeding... ContextResolver.clearCurrentTransaction(transaction); } // try to commit the transaction...note that there's the // chance that the application code masked the orginal // cause of a failure, so we'll check for that first, // re-throwing the root cause in that case if (transaction.isAborted()) { throw transaction.getAbortCause(); } handle.commit(); // the task completed successfully, so we're done profileCollectorHandle.finishTask(task.getTryCount()); task.setDone(null); return true; } catch (InterruptedException ie) { // make sure the transaction was aborted if (!transaction.isAborted()) { transaction.abort(ie); } profileCollectorHandle.finishTask(task.getTryCount(), ie); // if the task didn't finish because of the interruption // then we want to note that and possibly re-queue the // task to run in a usable thread if (task.setInterrupted() && retryOnInterruption) { if (!handoffRetry(task, ie)) { // if the task couldn't be re-queued, then there's // nothing left to do but drop it task.setDone(ie); if (logger.isLoggable(Level.WARNING)) { logger.logThrow(Level.WARNING, ie, "dropping " + "an interrupted task: {0}" + task); } } } // always re-throw the interruption throw ie; } catch (Throwable t) { // make sure the transaction was aborted if ((transaction != null) && (!transaction.isAborted())) { transaction.abort(t); } profileCollectorHandle.finishTask(task.getTryCount(), t); // some error occurred, so see if we should re-try if (!shouldRetry(task, t)) { // the task is not being re-tried task.setDone(t); return true; } else { // see if the re-try should be handed-off task.setRunning(false); if (handoffRetry(task, t)) { return false; } } } } } finally { // always restore the previous owner before leaving... ContextResolver.setTaskState(kernelContext, parent); } }
/** * Constructs an instance of this class with the specified properties. * * @param properties service properties * @param systemRegistry system registry * @param txnProxy transaction proxy * @throws Exception if a problem occurs when creating the service */ public ClientSessionServiceImpl( Properties properties, ComponentRegistry systemRegistry, TransactionProxy txnProxy) throws Exception { super(properties, systemRegistry, txnProxy, logger); logger.log(Level.CONFIG, "Creating ClientSessionServiceImpl properties:{0}", properties); PropertiesWrapper wrappedProps = new PropertiesWrapper(properties); try { /* * Get the property for controlling session event processing * and connection disconnection. */ writeBufferSize = wrappedProps.getIntProperty( WRITE_BUFFER_SIZE_PROPERTY, DEFAULT_WRITE_BUFFER_SIZE, 8192, Integer.MAX_VALUE); eventsPerTxn = wrappedProps.getIntProperty( EVENTS_PER_TXN_PROPERTY, DEFAULT_EVENTS_PER_TXN, 1, Integer.MAX_VALUE); allowNewLogin = wrappedProps.getBooleanProperty(ALLOW_NEW_LOGIN_PROPERTY, false); /* Export the ClientSessionServer. */ int serverPort = wrappedProps.getIntProperty(SERVER_PORT_PROPERTY, DEFAULT_SERVER_PORT, 0, 65535); serverImpl = new SessionServerImpl(); exporter = new Exporter<ClientSessionServer>(ClientSessionServer.class); try { int port = exporter.export(serverImpl, serverPort); serverProxy = exporter.getProxy(); if (logger.isLoggable(Level.CONFIG)) { logger.log(Level.CONFIG, "export successful. port:{0,number,#}", port); } } catch (Exception e) { try { exporter.unexport(); } catch (RuntimeException re) { } throw e; } /* Get services and check service version. */ flushContextsThread.start(); contextFactory = new ContextFactory(txnProxy); watchdogService = txnProxy.getService(WatchdogService.class); nodeMapService = txnProxy.getService(NodeMappingService.class); taskService = txnProxy.getService(TaskService.class); localNodeId = watchdogService.getLocalNodeId(); watchdogService.addRecoveryListener(new ClientSessionServiceRecoveryListener()); transactionScheduler.runTask( new AbstractKernelRunnable("CheckServiceVersion") { public void run() { checkServiceVersion(VERSION_KEY, MAJOR_VERSION, MINOR_VERSION); } }, taskOwner); /* Store the ClientSessionServer proxy in the data store. */ transactionScheduler.runTask( new AbstractKernelRunnable("StoreClientSessionServiceProxy") { public void run() { dataService.setServiceBinding( getClientSessionServerKey(localNodeId), new ManagedSerializable<ClientSessionServer>(serverProxy)); } }, taskOwner); /* * Create the protocol listener and acceptor. */ protocolListener = new ProtocolListenerImpl(); protocolAcceptor = wrappedProps.getClassInstanceProperty( PROTOCOL_ACCEPTOR_PROPERTY, DEFAULT_PROTOCOL_ACCEPTOR, ProtocolAcceptor.class, new Class[] {Properties.class, ComponentRegistry.class, TransactionProxy.class}, properties, systemRegistry, txnProxy); assert protocolAcceptor != null; /* Create our service profiling info and register our MBean */ ProfileCollector collector = systemRegistry.getComponent(ProfileCollector.class); serviceStats = new ClientSessionServiceStats(collector); try { collector.registerMBean(serviceStats, ClientSessionServiceStats.MXBEAN_NAME); } catch (JMException e) { logger.logThrow(Level.CONFIG, e, "Could not register MBean"); } /* Set the protocol descriptor in the ConfigMXBean. */ ConfigManager config = (ConfigManager) collector.getRegisteredMBean(ConfigManager.MXBEAN_NAME); if (config == null) { logger.log(Level.CONFIG, "Could not find ConfigMXBean"); } else { config.setProtocolDescriptor(protocolAcceptor.getDescriptor().toString()); } } catch (Exception e) { if (logger.isLoggable(Level.CONFIG)) { logger.logThrow(Level.CONFIG, e, "Failed to create ClientSessionServiceImpl"); } doShutdown(); throw e; } }
/** * Constructs an instance of this class with the specified {@code properties}, {@code * systemRegistry}, and {@code txnProxy}. * * @param properties service properties * @param systemRegistry system registry * @param txnProxy transaction proxy * @throws Exception if a problem occurs when creating the service */ public ChannelServiceImpl( Properties properties, ComponentRegistry systemRegistry, TransactionProxy txnProxy) throws Exception { super(properties, systemRegistry, txnProxy, logger); logger.log(Level.CONFIG, "Creating ChannelServiceImpl properties:{0}", properties); PropertiesWrapper wrappedProps = new PropertiesWrapper(properties); try { synchronized (ChannelServiceImpl.class) { if (contextMap == null) { contextMap = new TransactionContextMap<Context>(txnProxy); } } contextFactory = new ContextFactory(contextMap); WatchdogService watchdogService = txnProxy.getService(WatchdogService.class); sessionService = txnProxy.getService(ClientSessionService.class); localNodeId = watchdogService.getLocalNodeId(); writeBufferSize = wrappedProps.getIntProperty( WRITE_BUFFER_SIZE_PROPERTY, DEFAULT_WRITE_BUFFER_SIZE, 8192, Integer.MAX_VALUE); /* * Get the property for controlling channel event processing. */ eventsPerTxn = wrappedProps.getIntProperty( EVENTS_PER_TXN_PROPERTY, DEFAULT_EVENTS_PER_TXN, 1, Integer.MAX_VALUE); /* * Export the ChannelServer. */ int serverPort = wrappedProps.getIntProperty(SERVER_PORT_PROPERTY, DEFAULT_SERVER_PORT, 0, 65535); serverImpl = new ChannelServerImpl(); exporter = new Exporter<ChannelServer>(ChannelServer.class); try { int port = exporter.export(serverImpl, serverPort); serverProxy = exporter.getProxy(); logger.log(Level.CONFIG, "ChannelServer export successful. port:{0,number,#}", port); } catch (Exception e) { try { exporter.unexport(); } catch (RuntimeException re) { } throw e; } /* * Check service version. */ transactionScheduler.runTask( new AbstractKernelRunnable() { public void run() { checkServiceVersion(VERSION_KEY, MAJOR_VERSION, MINOR_VERSION); } }, taskOwner); /* * Store the ChannelServer proxy in the data store. */ transactionScheduler.runTask( new AbstractKernelRunnable() { public void run() { dataService.setServiceBinding( getChannelServerKey(localNodeId), new ManagedSerializable<ChannelServer>(serverProxy)); } }, taskOwner); /* * Add listeners for handling recovery and for receiving * notification of client session disconnection. */ watchdogService.addRecoveryListener(new ChannelServiceRecoveryListener()); watchdogService.addNodeListener(new ChannelServiceNodeListener()); sessionService.registerSessionDisconnectListener(new ChannelSessionDisconnectListener()); } catch (Exception e) { if (logger.isLoggable(Level.CONFIG)) { logger.logThrow(Level.CONFIG, e, "Failed to create ChannelServiceImpl"); } doShutdown(); throw e; } }