/*
* 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 javax.annotation.PreDestroy;
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.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<Object> services;
private ClientConnectionInfo connectionInfo;
private RemoteConnectorServer remoteConnectorServer;
/** Spring application context if the process server was instantiated by the Spring framework */
private ApplicationContext applicationContext;
private volatile boolean initialized;
private volatile boolean initializing;
/**
* 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()
{
}
/**
* 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)
{
synchronized(this)
{
if (! initialized && ! initializing)
{
try
{
// To prevent endless recursion by calling initialize() from within some getters below
initializing = true;
determineRootDir();
if (propertiesResourceName != null)
{
Application.registerPropertyResource(propertiesResourceName, 80, false);
}
// Perform the core initialization
super.initialize();
// Reads all models
initModels();
// Initialize the remote services
initRemoting();
// Initialize the engine
tokenContextService.initialize();
if (debugger != null)
{
debugger.initialize();
}
if (getEngineRunner() != null)
{
getEngineRunner().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);
}
}
/**
* Performs shutdown of the process server using a 60 second timeout.
*
* Calls shutdownModel for each loaded model.
*/
@PreDestroy
public boolean shutdown()
{
return shutdown(60000L);
}
/**
* 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).
* @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 ret = true;
if (initialized)
{
synchronized (this)
{
if (initialized)
{
LogUtil.info(getClass(), "Shutting down the OpenBP engine...");
try
{
if (getEngineRunner() != null)
{
ret = getEngineRunner().waitForStop(timeoutMS);
getEngineRunner().shutdown();
}
if (debugger != null)
{
debugger.shutdown();
}
tokenContextService.shutdown();
if (remoteConnectorServer != null)
{
try
{
remoteConnectorServer.unbindFromRegistry();
}
catch (Exception e)
{
// Ignore errors when unbinding, 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
{
model.shutdownModel();
}
catch (Exception e)
{
LogUtil.error(getClass(), "Error during shutdown of model $0", model.getQualifier(), e);
ret = false;
}
}
super.shutdown();
}
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;
}
/**
* 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;
}
/**
* 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;
}
}