/** * 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()); } }
/** 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); } }
/** * 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} * * <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; } }
/** * 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; }
/** {@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); } }
/** * 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); }
/** {@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); }
/** {@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 {@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; } }
/** * 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 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; } }
/** * 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); } }