All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openbp.server.ProcessServer Maven / Gradle / Ivy

There is a newer version: 0.9.11
Show newest version
/*
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
package org.openbp.server;

import java.util.Iterator;
import java.util.List;

import javax.annotation.PostConstruct;

import org.openbp.common.ExceptionUtil;
import org.openbp.common.application.Application;
import org.openbp.common.generic.msgcontainer.MsgItem;
import org.openbp.common.generic.msgcontainer.StandardMsgContainer;
import org.openbp.common.logger.LogUtil;
import org.openbp.core.CoreModule;
import org.openbp.core.OpenBPException;
import org.openbp.core.engine.EngineException;
import org.openbp.core.model.Model;
import org.openbp.core.remote.ClientConnectionInfo;
import org.openbp.server.context.TokenContextService;
import org.openbp.server.engine.Engine;
import org.openbp.server.engine.EngineRunner;
import org.openbp.server.engine.debugger.Debugger;
import org.openbp.server.engine.debugger.DebuggerImpl;
import org.openbp.server.remote.RemoteConnectorServer;
import org.openbp.server.scheduler.ProcessScheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;

/**
 * This class is the main anchor point of an OpenBP server.
 * It performs all server startup and shutdown tasks.
 * Note that you must call the {@link #initialize} method before you can request any services from the server.
 *
 * @author Heiko Erhardt
 */
@Service("processServer")
public class ProcessServer extends CoreModule
    implements ApplicationContextAware
{
	// Components configured by Spring

	/** Classpath name of the server's properties resource or null */
	private String propertiesResourceName = "OpenBP-Server.properties";

	@Autowired
	private ProcessFacade processFacade;

	@Autowired
	private Engine engine;

	@Autowired
	private EngineRunner engineRunner;

	@Autowired
	private TokenContextService tokenContextService;

	@Autowired
	private Debugger debugger;

	@Autowired(required=false)
	private ProcessScheduler processScheduler;

	@Autowired @Qualifier("exportedService")
	private List services;

	private ClientConnectionInfo connectionInfo;

	private boolean initialized;
	private boolean initializing;
	private ServerShutdownHook serverShutdownHook;
	private RemoteConnectorServer remoteConnectorServer;

	/** Spring application context if the process server was instantiated by the Spring framework */
	private ApplicationContext applicationContext;

	/**
	 * Constructor to be used by the Spring framework only, class needs to be autowired.
	 * You might also use the {@link ProcessServerFactory} for construction.
	 */
	public ProcessServer()
	{
	}

	//////////////////////////////////////////////////
	// @@ Initialization
	//////////////////////////////////////////////////

	/**
     * Implementation of the ApplicationContextAware interface.
     * See org.springframework.context.ApplicationContextAware.
     * Called by the Spring framework after bean construction.
	 *
	 * @param applicationContext Spring application context the process server was instantiated by the Spring framework
	 */
	public void setApplicationContext(ApplicationContext applicationContext)
	{
		this.applicationContext = applicationContext;
	}

	/**
	 * Gets the spring application context if the process server was instantiated by the Spring framework.
	 */
	public ApplicationContext getApplicationContext()
	{
		return applicationContext;
	}

	/**
	 * Sets the classpath name of the server's properties resource.
	 * @param propertiesResourceName Resource name or null (default: "OpenBP-Server.properties")
	 */
	public void setPropertiesResourceName(String propertiesResourceName)
	{
		this.propertiesResourceName = propertiesResourceName;
	}

	/**
	 * Initializes the server.
	 * Call this method before requesting any services from the core.
     * This method will also be called from the IoC container that supports the JSR 250 @PostConstruct annotation.
	 *
	 * @throws OpenBPException On severe error
	 */
	@PostConstruct
	public synchronized void initialize()
	{
		if (initialized || initializing)
			return;

		try
		{
			// Prevent endless recursion
			initializing = true;

			determineRootDir();
			if (propertiesResourceName != null)
			{
				Application.registerPropertyResource(propertiesResourceName, 80, false);
			}

			// Note: It is important to keep the order of these statements!

			// Perform the core initialization
			super.initialize();

			// Reads all models
			initModels();

			// Register a shutdown hook that allows correct database shutdown
			registerShutdownHook();

			// Initialize the remote services
			initRemoting();

			// TODO FIX 5 The DebuggerImpl.initialize method should be called using @PostConstruct - Need to convert SettingUtil to service to do this
			if (debugger instanceof DebuggerImpl)
			{
				((DebuggerImpl) debugger).initialize();
			}
			initialized = true;
		}
		finally
		{
			initializing = false;
		}
	}

	/**
	 * Initializes the model manager.
	 *
	 * @throws OpenBPException On error
	 */
	private void initModels()
	{
		LogUtil.info(getClass(), "Loading models...");

		// Read all models
		getModelMgr().readModels();

		// Initialize the models
		getModelMgr().initializeModels();

		// Print any errors to stderr
		StandardMsgContainer msgContainer = getModelMgr().getMsgContainer();
		for (Iterator it = msgContainer.getMsgs(); it.hasNext();)
		{
			MsgItem msgItem = (MsgItem) it.next();
			LogUtil.error(getClass(), msgItem.getFormattedMsgWithSource());
		}
		msgContainer.clearMsgs();

		LogUtil.info(getClass(), "Model load complete.");
	}

	/**
	 * Registers the services and start up the RMI registry.
	 *
	 * @throws OpenBPException On error
	 */
	private void initRemoting()
	{
		if (services == null)
		{
			// No services to publish
			return;
		}

		try
		{
			// Determine remote connection properties
			if (connectionInfo == null)
			{
				// Load connection info from properties file if not specified in the Spring config file
				connectionInfo = new ClientConnectionInfo();
				connectionInfo.loadFromProperties();
			}

			// Make the remote connector known to the RMI registry
			if (connectionInfo.isEnabled())
			{
				remoteConnectorServer = new RemoteConnectorServer();

				for (Iterator it = services.iterator(); it.hasNext();)
				{
					remoteConnectorServer.registerService(it.next());
				}

				remoteConnectorServer.setConnectionInfo(connectionInfo);
				remoteConnectorServer.bindToRegistry();
			}
		}
		catch (Exception e)
		{
			throw new EngineException("Initialization", "Error initializing services.", e);
		}
	}

	//////////////////////////////////////////////////
	// @@ Shutdown
	//////////////////////////////////////////////////

	/**
	 * Performs shutdown of the process server using a 60 second timeout.
	 *
	 * @param unregisterHook 
	 *		true	Unregister the VM's shutdown hook. Note that this may not be done during VM shutdown! (when invoked due to servlet reaload etc.)
* false Do not unregister the hook (when invoked due to VM termination) * Calls shutdownModel for each loaded model. */ public boolean shutdown(boolean unregisterHook) { return shutdown(60000L, unregisterHook); } /** * Performs shutdown of the process server. * Before the server shutdown, the engine runner (if present) will be shut down (see {@link EngineRunner#waitForStop}). * Also calls {@link Model#shutdownModel} for each loaded model. * The method will ignore any exception, however, they will be logged. * * @param timeoutMS Timeout in milliseconds for the engine runner shutdown. * If this value is 0, the method will just check if everything has completed, but will not wait for any processes. * If this value is -1, no timeout will apply (i. e. the method will definately wait * until all context executions have finished). * @param unregisterHook * true Unregister the VM's shutdown hook. Note that this may not be done during VM shutdown! * (when invoked due to servlet reaload etc.)
* false Do not unregister the hook (when invoked due to VM termination) * @return true if no context is currently executing and there were no exceptions during shutdown, * false if either the engine runner shutdown timeout has elapsed or there have been exceptions during the shutdown. */ public boolean shutdown(long timeoutMS, boolean unregisterHook) { boolean ret = true; synchronized (this) { if (initialized) { LogUtil.info(getClass(), "Shutting down the OpenBP engine..."); try { if (getEngineRunner() != null) { ret = getEngineRunner().waitForStop(timeoutMS); } if (remoteConnectorServer != null) { try { remoteConnectorServer.unbindFromRegistry(); } catch (Exception e) { // Ignore errors when unbing, we are shutting down anyway // We also don't log this one. ret = false; } remoteConnectorServer = null; } // Shutdown all models for (Iterator it = getModelMgr().getModels().iterator(); it.hasNext();) { Model model = (Model) it.next(); try { shutdownModel(model); } catch (Exception e) { LogUtil.error(getClass(), "Error during shutdown of model.", model.getQualifier(), e); ret = false; } } super.shutdown(unregisterHook); // TODONOW Shutdown triggered by container instead if (unregisterHook) { unregisterShutdownHook(); } } catch (Exception e) { LogUtil.error(getClass(), "Error during OpenBP engine shutdown.", e); ret = false; } finally { LogUtil.info(getClass(), "OpenBP engine shutdown complete."); initialized = false; } } } return ret; } /** * Shuts down the given model and its sub models. * * @param model Model to shut down or null for all models */ private void shutdownModel(Model model) { // Shut down the model; catch any exception // TODO Feature 5 Trigger the appropriate model event try { model.shutdownModel(); } catch (Exception e) { LogUtil.error(getClass(), "Error while shutting down model $0", model.getQualifier(), e); } } /** * Registers a shutdown hook that allows correct database shutdown. */ private void registerShutdownHook() { // TODO FIX 3 Maybe we should register with the app context shutdown hook instead the JDK runtime... serverShutdownHook = new ServerShutdownHook(); Runtime.getRuntime().addShutdownHook(serverShutdownHook); } /** * Registers a shutdown hook that allows correct database shutdown. */ private void unregisterShutdownHook() { if (serverShutdownHook != null) { Runtime.getRuntime().removeShutdownHook(serverShutdownHook); serverShutdownHook = null; } } /** * Server shutdown hook. */ private class ServerShutdownHook extends Thread { /** * Constructor. */ public ServerShutdownHook() { // This thread must not prevent VM shutdown. setDaemon(true); } /** * Called when the virtual machine is about to terminate. * This can be because the exit method has been called or CTRL+C has been pressed. */ public void run() { ProcessServer.this.shutdown(false); } } ////////////////////////////////////////////////// // @@ Components setters/getters ////////////////////////////////////////////////// /** * Gets the process facade. */ public ProcessFacade getProcessFacade() { initialize(); return processFacade; } /** * Sets the process facade. */ public void setProcessFacade(ProcessFacade processFacade) { this.processFacade = processFacade; } /** * Gets the engine. */ public Engine getEngine() { initialize(); return engine; } /** * Sets the engine. */ public void setEngine(Engine engine) { this.engine = engine; } /** * Gets the engine runner. */ public EngineRunner getEngineRunner() { initialize(); return engineRunner; } /** * Sets the engine runner. */ public void setEngineRunner(EngineRunner engineRunner) { this.engineRunner = engineRunner; } /** * Gets the token context service. */ public TokenContextService getTokenContextService() { initialize(); return tokenContextService; } /** * Sets the token context service. */ public void setTokenContextService(TokenContextService tokenContextService) { this.tokenContextService = tokenContextService; } /** * Gets the process scheduler. */ public ProcessScheduler getProcessScheduler() { initialize(); return processScheduler; } /** * Sets the process scheduler. */ public void setProcessScheduler(ProcessScheduler processScheduler) { this.processScheduler = processScheduler; } /** * Sets the list of service implementations. */ public void setServices(List services) { this.services = services; } /** * Sets the connection info. */ public void setConnectionInfo(ClientConnectionInfo connectionInfo) { this.connectionInfo = connectionInfo; } ////////////////////////////////////////////////// // @@ Main class for simple test server mode ////////////////////////////////////////////////// /** * Main method for simple server test mode. * * @param arguments Command line argument array */ public static void main(String[] arguments) { long time = System.currentTimeMillis(); try { // Initialize the server Application.setArguments(arguments); // Start up the server ProcessServer processServer = new ProcessServerFactory().createProcessServer(); // Compute startup time and print startup message time = System.currentTimeMillis() - time; String startedUpMessage = "OpenBP test server startup complete [" + time + " ms]."; LogUtil.info(ProcessServer.class, startedUpMessage); processServer.shutdown(true); } catch (Exception e) { ExceptionUtil.printTrace("Error initializing OpenBP server", e); System.exit(1); } } }