package org.marketcetera.photon.strategy.engine; import java.io.File; import java.net.URL; import java.util.*; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import org.apache.commons.lang.StringUtils; import org.eclipse.core.runtime.FileLocator; import org.eclipse.emf.common.util.EList; import org.marketcetera.core.Util; import org.marketcetera.module.ModuleURN; import org.marketcetera.photon.commons.ExceptionUtils; import org.marketcetera.photon.commons.Validate; import org.marketcetera.photon.strategy.engine.model.core.*; import org.marketcetera.photon.strategy.engine.model.core.impl.StrategyEngineConnectionImpl; import org.marketcetera.util.except.I18NException; import org.marketcetera.util.log.SLF4JLoggerProxy; import org.marketcetera.util.misc.ClassVersion; import com.google.common.collect.Maps; import com.google.common.collect.Sets; /* $License$ */ /** * A base class with common {@link StrategyEngineConnection} functionality. This * class handles the model updates and delegates to abstract methods to * implement the interactions with the underlying module framework * implmentation. * * @author <a href="mailto:will@marketcetera.com">Will Horn</a> * @version $Id: AbstractStrategyEngineConnection.java 16161 2012-07-16 20:27:32Z colin $ * @since 2.0.0 */ @ClassVersion("$Id: AbstractStrategyEngineConnection.java 16161 2012-07-16 20:27:32Z colin $") public abstract class AbstractStrategyEngineConnection extends StrategyEngineConnectionImpl { private final ExecutorService mGUIExecutor; /** * Constructor. * * @param guiExecutor * the GUI executor service * @throws IllegalArgumentException * if guiExecutor is null */ protected AbstractStrategyEngineConnection(ExecutorService guiExecutor) { Validate.notNull(guiExecutor, "guiExecutor"); mGUIExecutor = guiExecutor; } /** * Returns the executor service to be used for model updates. * * @return the GUI executor service */ protected ExecutorService getGUIExecutor() { return mGUIExecutor; } @Override public DeployedStrategy deploy(Strategy strategy) throws Exception { Validate.notNull(strategy, "strategy"); //$NON-NLS-1$ String scriptPath = strategy.getScriptPath(); if (StringUtils.isBlank(scriptPath)) { throw new I18NException( Messages.ABSTRACT_STRATEGY_ENGINE_CONNECTION_MISSING_SCRIPT_PATH); } if (StringUtils.isBlank(strategy.getClassName())) { throw new I18NException( Messages.ABSTRACT_STRATEGY_ENGINE_CONNECTION_MISSING_CLASS_NAME); } if (StringUtils.isBlank(strategy.getLanguage())) { throw new I18NException( Messages.ABSTRACT_STRATEGY_ENGINE_CONNECTION_MISSING_LANGUAGE); } if (StringUtils.isBlank(strategy.getInstanceName())) { throw new I18NException( Messages.ABSTRACT_STRATEGY_ENGINE_CONNECTION_MISSING_INSTANCE_NAME); } File script = new File(scriptPath); if (!script.isFile()) { /* * The script path can also be a URL, as long as the platform can * resolve it to a File URL. */ File resolved = null; try { URL fileURL = FileLocator.toFileURL(new URL(scriptPath)); if (fileURL.getProtocol().equals("file")) { //$NON-NLS-1$ resolved = new File(fileURL.getPath()); if (resolved.isFile()) { SLF4JLoggerProxy .debug( this, "Resolved strategy scriptPath '{}' as a URL to file '{}'.", //$NON-NLS-1$ scriptPath, resolved); script = resolved; } } } catch (Exception e) { // ignore, send strategy the original path } } ModuleURN urn = doDeploy(strategy, script); return internalDeploy(scriptPath, urn); } /** * Return strategy properties as a {@link Properties} object. * * @param strategy * the strategy * @return the properties */ protected static Properties getProperties(Strategy strategy) { Properties properties = new Properties(); properties.putAll(strategy.getParameters().map()); return properties; } /** * Return strategy properties as a String. * * @param strategy * the strategy * @return the properties */ protected static String getPropertiesString(Strategy strategy) { return Util.propertiesToString(getProperties(strategy)); } /** * Return string based properties as a map. * * @param properties * the string-encoded properties * @return a map of properties */ protected static Map<String, String> getPropertiesMap(String properties) { Map<String, String> map = Maps.newHashMap(); Properties props = Util.propertiesFromString(properties); if (props != null) { for (String key : props.stringPropertyNames()) { map.put(key, props.getProperty(key)); } } return map; } @Override public void start(final DeployedStrategy deployedStrategy) throws Exception { Validate.notNull(deployedStrategy, "deployedStrategy"); //$NON-NLS-1$ ModuleURN urn = deployedStrategy.getUrn(); doStart(urn); ExceptionUtils.launderedGet(getGUIExecutor().submit(new Runnable() { @Override public void run() { deployedStrategy.setState(StrategyState.RUNNING); } })); } @Override public void stop(final DeployedStrategy deployedStrategy) throws Exception { Validate.notNull(deployedStrategy, "deployedStrategy"); //$NON-NLS-1$ ModuleURN urn = deployedStrategy.getUrn(); doStop(urn); ExceptionUtils.launderedGet(getGUIExecutor().submit(new Runnable() { @Override public void run() { deployedStrategy.setState(StrategyState.STOPPED); } })); } @Override public void refresh(final DeployedStrategy deployedStrategy) throws Exception { Validate.notNull(deployedStrategy, "deployedStrategy"); //$NON-NLS-1$ ModuleURN urn = deployedStrategy.getUrn(); if (getDeployed().contains(urn)) { internalRefresh(deployedStrategy); } else { ExceptionUtils.launderedGet(getGUIExecutor().submit(new Runnable() { @Override public void run() { deployedStrategy.setEngine(null); } })); } } @Override public void refresh() throws Exception { Set<ModuleURN> deployedModules = Sets.newHashSet(getDeployed()); final Set<DeployedStrategy> removedStrategies = Collections .synchronizedSet(Sets.<DeployedStrategy> newHashSet()); /* * Iterate visible strategies, refresh ones that should be there and * mark for removal those that should not. */ for (DeployedStrategy deployedStrategy : getEngine() .getDeployedStrategies()) { if (deployedModules.contains(deployedStrategy.getUrn())) { /* * This may theoretically fail if the strategy was deleted since * the call to getDeployed() above. The user would just have to * refresh again to recover. */ internalRefresh(deployedStrategy); deployedModules.remove(deployedStrategy.getUrn()); } else { removedStrategies.add(deployedStrategy); } } /* * Remove the ones that should not be there. */ ExceptionUtils.launderedGet(getGUIExecutor().submit(new Runnable() { @Override public void run() { /* * Cannot call * getDeployedStrategies().removeAll(removedStrategies) due to * Eclipse bug. See note on * StrategyEngine#getDeployedStrategies(). */ EList<DeployedStrategy> deployedStrategies = getEngine().getDeployedStrategies(); for (DeployedStrategy toRemove : removedStrategies) { deployedStrategies.remove(toRemove); } } })); /* * Now add new ones. */ for (ModuleURN missing : deployedModules) { internalDeploy(null, missing); } } @Override public void update(final DeployedStrategy deployedStrategy, Strategy newConfiguration) throws Exception { Validate.notNull(deployedStrategy, "deployedStrategy", //$NON-NLS-1$ newConfiguration, "newConfiguration"); //$NON-NLS-1$ ModuleURN urn = deployedStrategy.getUrn(); try { doUpdate(urn, newConfiguration); } finally { internalRefresh(deployedStrategy); } } @Override public void undeploy(final DeployedStrategy deployedStrategy) throws Exception { Validate.notNull(deployedStrategy, "deployedStrategy"); //$NON-NLS-1$ ModuleURN urn = deployedStrategy.getUrn(); if (isRunning(urn)) { doStop(urn); } doUndeploy(urn); ExceptionUtils.launderedGet(getGUIExecutor().submit(new Runnable() { @Override public void run() { deployedStrategy.setEngine(null); } })); } private DeployedStrategy internalDeploy(final String scriptPath, final ModuleURN urn) throws Exception { final DeployedStrategy deployed = ExceptionUtils .launderedGet(getGUIExecutor().submit( new Callable<DeployedStrategy>() { @Override public DeployedStrategy call() { DeployedStrategy deployed = StrategyEngineCoreFactory.eINSTANCE .createDeployedStrategy(); deployed.setUrn(urn); deployed.setScriptPath(scriptPath); return deployed; } })); internalRefresh(deployed); ExceptionUtils.launderedGet(getGUIExecutor().submit(new Runnable() { @Override public void run() { getEngine().getDeployedStrategies().add( deployed); } })); return deployed; } private void internalRefresh(final DeployedStrategy deployed) throws Exception { final DeployedStrategy refreshed = StrategyEngineCoreFactory.eINSTANCE .createDeployedStrategy(); doRefresh(deployed.getUrn(), refreshed); final boolean isRunning = isRunning(deployed.getUrn()); ExceptionUtils.launderedGet(getGUIExecutor().submit(new Runnable() { @Override public void run() { deployed.setInstanceName(refreshed.getInstanceName()); deployed.setLanguage(refreshed.getLanguage()); deployed.setClassName(refreshed.getClassName()); deployed.setRouteOrdersToServer(refreshed .isRouteOrdersToServer()); deployed.getParameters().clear(); deployed.getParameters().addAll(refreshed.getParameters()); if (isRunning) { deployed.setState(StrategyState.RUNNING); } else { deployed.setState(StrategyState.STOPPED); } } })); } /** * Deploys the strategy and provide the URN. * * @param strategy * the strategy * @param script * the script file (computed from the script path) * @return the URN of the new strategy * @throws Exception * if something goes wrong */ protected abstract ModuleURN doDeploy(Strategy strategy, File script) throws Exception; /** * Refreshes the state of the strategy with the given urn. The state must be * stored in the given strategy and can be written directly (i.e. without * using the GUI executor). * * @param urn * the strategy urn * @param strategy * @throws Exception * if something goes wrong */ protected abstract void doRefresh(ModuleURN urn, DeployedStrategy strategy) throws Exception; /** * Returns whether the strategy with the given urn is running. * * @param urn * the strategy urn * @return true if the strategy is running * @throws Exception * if something goes wrong */ protected abstract boolean isRunning(ModuleURN urn) throws Exception; /** * Starts the strategy with the given urn. * * @param urn * the strategy urn * @throws Exception * if something goes wrong */ protected abstract void doStart(ModuleURN urn) throws Exception; /** * Stops the strategy with the given urn. * * @param urn * the strategy urn * @throws Exception * if something goes wrong */ protected abstract void doStop(ModuleURN urn) throws Exception; /** * Updates the strategy with the given urn. * * @param urn * the strategy urn * @param newConfiguration * the new configuration to apply to the strategy * @throws Exception * if something goes wrong */ protected abstract void doUpdate(ModuleURN urn, Strategy newConfiguration) throws Exception; /** * Undeploys the strategy with the given urn. * * @param urn * the strategy urn * @throws Exception * if something goes wrong */ protected abstract void doUndeploy(ModuleURN urn) throws Exception; /** * Returns a list of deployed strategy urns. * * @return the strategy urn * @throws Exception * if something goes wrong */ protected abstract List<ModuleURN> getDeployed() throws Exception; }