/** * {@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(); } }
/** Processes (at least) the first event in the queue. */ void serviceEvent() { checkState(); ClientSessionServiceImpl sessionService = ClientSessionServiceImpl.getInstance(); ManagedQueue<SessionEvent> eventQueue = getQueue(); DataService dataService = ClientSessionServiceImpl.getDataService(); for (int i = 0; i < sessionService.eventsPerTxn; i++) { SessionEvent event = eventQueue.poll(); if (event == null) { // no more events break; } logger.log(Level.FINEST, "processing event:{0}", event); int cost = event.getCost(); if (cost > 0) { dataService.markForUpdate(this); writeBufferAvailable += cost; if (logger.isLoggable(Level.FINEST)) { logger.log( Level.FINEST, "{0} cleared reservation of " + "{1,number,#} bytes, leaving {2,number,#}", this, cost, writeBufferAvailable); } } event.serviceEvent(this); } }
/** * 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; }
/** * Attempts to enqueue the specified {@code event}, and returns {@code true} if successful, and * {@code false} otherwise. * * @param event the event * @return {@code true} if successful, and {@code false} otherwise * @throws MessageRejectedException if the cost of the event exceeds the available buffer space * in the queue */ boolean offer(SessionEvent event) { int cost = event.getCost(); if (cost > writeBufferAvailable) { ClientSessionServiceImpl sessionService = ClientSessionServiceImpl.getInstance(); if (sessionService.receiveMessageRejectedOp != null) { sessionService.receiveMessageRejectedOp.report(); } throw new MessageRejectedException( "Not enough queue space: " + writeBufferAvailable + " bytes available, " + cost + " requested"); } boolean success = getQueue().offer(event); if (success && cost > 0) { ClientSessionServiceImpl.getDataService().markForUpdate(this); writeBufferAvailable -= cost; if (logger.isLoggable(Level.FINEST)) { logger.log( Level.FINEST, "{0} reserved {1,number,#} leaving {2,number,#}", this, cost, writeBufferAvailable); } } return success; }
/** {@inheritDoc} */ public void doReady() { channelService = txnProxy.getService(ChannelServiceImpl.class); acceptFuture = acceptor.accept(new AcceptorListener()); try { if (logger.isLoggable(Level.CONFIG)) { logger.log(Level.CONFIG, "listening on {0}", acceptor.getLocalAddress()); } } catch (IOException ioe) { throw new RuntimeException(ioe.getMessage(), ioe); } }
/** {@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 } }
/** * If the session is connected, enqueues a disconnect event to this client session's event queue, * and marks this session as disconnected. */ void disconnect() { if (isConnected()) { addEvent(new DisconnectEvent()); sessionService.getDataService().markForUpdate(this); connected = false; } logger.log(Level.FINEST, "disconnect returns"); }
/** {@inheritDoc} */ public Channel getChannel(String name) { try { return ChannelImpl.getInstance(name); } catch (RuntimeException e) { logger.logThrow(Level.FINEST, e, "getChannel:{0} throws"); throw e; } }
/** * Creates an instance of <code>FIFOSchedulerQueue</code>. * * @param properties the available system properties */ public FIFOSchedulerQueue(Properties properties) { logger.log(Level.CONFIG, "Creating a FIFO Scheduler Queue"); if (properties == null) { throw new NullPointerException("Properties cannot be null"); } queue = new LinkedBlockingQueue<ScheduledTask>(); timedTaskHandler = new TimedTaskHandler(this); }
/** {@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; } }
/** * 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()); } }
/** Trigger a one-time presence update. */ public void updatePresence() { if (!service.isValidConfiguration()) { logger.log( Level.WARNING, "Tried to update presence information, but XMPP configuration was not valid. Make sure to set an account name and password."); return; } service.doUpdateStatusMessage(); }
/** Notifies the scheduler that a thread has been interrupted and is finishing its work. */ private void notifyThreadLeaving() { profileCollectorHandle.notifyThreadRemoved(); // NOTE: we're not yet trying to adapt the number of threads being // used, so we assume that threads are only lost when the system // wants to shutdown...in practice, this should look at some // threshold and see if another consumer needs to be created if (threadCount.decrementAndGet() == 0) { logger.log(Level.CONFIG, "No more threads are consuming tasks"); shutdown(); } }
/** * 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} */ 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); }
/** * 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 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} */ public void sendProtocolMessageNonTransactional( BigInteger sessionRefId, ByteBuffer message, Delivery delivery) { ClientSessionHandler handler = handlers.get(sessionRefId); /* * If a local handler exists, forward message to local handler * to send to client session. */ if (handler != null) { byte[] bytes = new byte[message.remaining()]; message.get(bytes); handler.sendProtocolMessage(bytes, delivery); } else { logger.log(Level.FINE, "Discarding messages for unknown session:{0}", sessionRefId); return; } }
/** {@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; } }
/** * Constructs an instance of this class with the specified {@code sessionService}, {@code * identity}, and the local node ID, and stores this instance with the following bindings: * * <p> * * <pre> * com.sun.sgs.impl.service.session.impl.<idBytes> * com.sun.sgs.impl.service.session.node.<nodeId>.impl.<idBytes> * </pre> * * This method should only be called within a transaction. * * @param sessionService a client session service * @param identity the session's identity * @throws TransactionException if there is a problem with the current transaction */ ClientSessionImpl(ClientSessionServiceImpl sessionService, Identity identity) { if (sessionService == null) { throw new NullPointerException("null sessionService"); } if (identity == null) { throw new IllegalStateException("session's identity is not set"); } this.sessionService = sessionService; this.identity = identity; this.nodeId = sessionService.getLocalNodeId(); writeBufferCapacity = sessionService.getWriteBufferSize(); DataService dataService = sessionService.getDataService(); ManagedReference<ClientSessionImpl> sessionRef = dataService.createReference(this); id = sessionRef.getId(); this.wrappedSessionRef = dataService.createReference(new ClientSessionWrapper(sessionRef)); idBytes = id.toByteArray(); dataService.setServiceBinding(getSessionKey(), this); dataService.setServiceBinding(getSessionNodeKey(), this); dataService.setServiceBinding(getEventQueueKey(), new EventQueue(this)); logger.log(Level.FINEST, "Stored session, identity:{0} id:{1}", identity, id); }
/** * 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; } } }
/** * Start updating the presence information. Tells the PresenceManager to plug into the * UserListener system so it can be notified of user join/leave events, which it takes as signals * to update the presence information. */ public void startPresenceUpdating() { if (!service.isValidConfiguration()) { logger.log( Level.WARNING, "Tried to start presence updating, but XMPP configuration was not valid. Make sure to set an account name and password."); return; } // Start a recurring task that updates the status messages. // lets try using the UserManager notification mechanism for deciding when a good time to update // would be. UserManager manager = UserManager.getUserManager(); listener = new PresenceUserListener(); // Using this inner class as an indirection to avoid serialization problems. manager.addUserListener(listener); // Kick off a task that periodically cleans out expired XMPP connections // (ie connections from which we haven't seen an event in a while) // no need to run this that often; being off by a few seconds is no big deal. AppContext.getTaskManager().schedulePeriodicTask(new ExpireConnectionsTask(), 0, 3000); }
/** {@inheritDoc} */ public ProfileConsumer registerProfileProducer(ProfileProducer producer) { if (logger.isLoggable(Level.CONFIG)) logger.log(Level.CONFIG, "Registering profile producer {0}", producer); return new ProfileConsumerImpl(producer, profileCollector); }
/** * 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); } } } }
/** * 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); }
/** * 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 } }
/** {@inheritDoc} */ public void report(ProfileReport profileReport) { if (profileReport.wasTaskSuccessful()) { commitCount++; } else { abortCount++; } totalRunningTime += profileReport.getRunningTime(); for (String op : profileReport.getReportedOperations()) { Long i = opCounts.get(op); opCounts.put(op, Long.valueOf(i == null ? 1 : i + 1)); } Map<String, Long> counterMap = profileReport.getUpdatedTaskCounters(); if (counterMap != null) { for (Entry<String, Long> entry : counterMap.entrySet()) { String key = entry.getKey(); long value = 0; if (localCounters.containsKey(key)) { value = localCounters.get(key); } localCounters.put(key, entry.getValue() + value); } } if ((commitCount + abortCount) >= logOps) { if (logger.isLoggable(Level.FINE)) { long now = System.currentTimeMillis(); Formatter opCountTally = new Formatter(); boolean first = true; for (String op : opCounts.keySet()) { if (!first) { opCountTally.format("%n"); } first = false; Long count = opCounts.get(op); opCountTally.format(" %s: %d", op, (count == null) ? 0 : count.longValue()); opCounts.put(op, 0L); } Formatter counterTally = new Formatter(); if (!localCounters.isEmpty()) { counterTally.format("[task counters]%n"); for (Entry<String, Long> entry : localCounters.entrySet()) { counterTally.format(" %s: %d%n", entry.getKey(), entry.getValue()); } } logger.log( Level.FINE, "Operations [logOps=" + logOps + "]:\n" + " succeeded: " + commitCount + " failed: " + abortCount + "\n" + " elapsed time: " + (now - lastReport) + " ms\n" + " running time: " + totalRunningTime + " ms " + "[threads=" + threadCount + "]\n" + opCountTally.toString() + "\n" + counterTally.toString()); } else { for (String op : opCounts.keySet()) { opCounts.put(op, 0L); } } commitCount = 0; abortCount = 0; totalRunningTime = 0; localCounters.clear(); lastReport = System.currentTimeMillis(); } }
/** * 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; } }
/** * 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; } }