/*************************************************************************** * Copyright (C) 2006-09-10 by Fabrizio Montesi <famontesi@gmail.com> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * * * For details about the authors of this software, see the AUTHORS file. * ***************************************************************************/ package jolie; import java.io.*; import java.lang.ref.WeakReference; import java.net.URI; import java.net.URISyntaxException; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Logger; import jolie.lang.Constants; import jolie.lang.parse.Scanner; import jolie.lang.parse.*; import jolie.lang.parse.ast.Program; import jolie.monitoring.MonitoringEvent; import jolie.monitoring.events.MonitorAttachedEvent; import jolie.monitoring.events.OperationStartedEvent; import jolie.monitoring.events.SessionEndedEvent; import jolie.monitoring.events.SessionStartedEvent; import jolie.net.CommChannel; import jolie.net.CommCore; import jolie.net.CommMessage; import jolie.net.SessionMessage; import jolie.net.ports.OutputPort; import jolie.process.DefinitionProcess; import jolie.process.InputOperationProcess; import jolie.process.SequentialProcess; import jolie.process.TransformationReason; import jolie.runtime.*; import jolie.runtime.correlation.CorrelationEngine; import jolie.runtime.correlation.CorrelationError; import jolie.runtime.correlation.CorrelationSet; import jolie.runtime.embedding.EmbeddedServiceLoader; /** * The Jolie interpreter engine. * Multiple Interpreter instances can be run in the same JavaVM; * this is used, e.g., for service embedding. * @author Fabrizio Montesi */ public class Interpreter { private class InitSessionThread extends SessionThread { public InitSessionThread( Interpreter interpreter, jolie.process.Process process ) { super( interpreter, process ); } @Override public boolean isInitialisingThread() { return true; } @Override public void run() { super.run(); if ( executionMode == Constants.ExecutionMode.SINGLE ) { try { mainSession = new SessionThread( getDefinition( "main" ), initExecutionThread ); correlationEngine.onSingleExecutionSessionStart( mainSession ); mainSession.addSessionListener( correlationEngine ); } catch( InvalidIdException e ) { assert false; } } /* * We need to relay the messages we did not consume during the init procedure. * We do this asynchronously, because calling correlationEngine.onMessageReceive * will trigger a join() on this thread, leading to a deadlock if we * were to call that directly from here. */ execute( new Runnable() { private void pushMessages( Deque< SessionMessage > queue ) { for( SessionMessage message : queue ) { try { correlationEngine.onMessageReceive( message.message(), message.channel() ); } catch( CorrelationError e ) { logWarning( e ); try { message.channel().send( CommMessage.createFaultResponse( message.message(), new FaultException( "CorrelationError", "The message you sent can not be correlated with any session and can not be used to start a new session." ) ) ); } catch( IOException ioe ) { logSevere( ioe ); } } } } public void run() { for( Deque< SessionMessage > queue : messageQueues.values() ) { pushMessages( queue ); } pushMessages( uncorrelatedMessageQueue ); } }); } } private static class JolieExecutionThreadFactory implements ThreadFactory { private final Interpreter interpreter; private static class JolieExecutionThread extends JolieThread { public JolieExecutionThread( Interpreter interpreter, Runnable r ) { super( interpreter, r ); } } public JolieExecutionThreadFactory( Interpreter interpreter ) { this.interpreter = interpreter; } public Thread newThread( Runnable r ) { return new JolieExecutionThread( interpreter, r ); } } public static class SessionStarter { private final InputOperationProcess guard; private final jolie.process.Process body; private CorrelationSet correlationInitializer = null; public SessionStarter( InputOperationProcess guard, jolie.process.Process body ) { this.guard = guard; this.body = body; } public InputOperationProcess guard() { return guard; } public jolie.process.Process body() { return body; } public void setCorrelationInitializer( CorrelationSet cset ) { correlationInitializer = cset; } public CorrelationSet correlationInitializer() { return correlationInitializer; } } private CommCore commCore; private CommandLineParser cmdParser; private Map< String, SessionStarter > sessionStarters = new HashMap< String, SessionStarter >(); private boolean exiting = false; private final Lock exitingLock; private final Condition exitingCondition; private final CorrelationEngine correlationEngine; private final List< CorrelationSet > correlationSets = new ArrayList< CorrelationSet > (); private final Map< String, CorrelationSet > operationCorrelationSetMap = new HashMap< String, CorrelationSet >(); private Constants.ExecutionMode executionMode = Constants.ExecutionMode.SINGLE; private final Value globalValue = Value.createRootValue(); private final String[] arguments; private final Collection< EmbeddedServiceLoader > embeddedServiceLoaders = new LinkedList< EmbeddedServiceLoader >(); private final Logger logger = Logger.getLogger( "JOLIE" ); private final Map< String, DefinitionProcess > definitions = new HashMap< String, DefinitionProcess >(); private final Map< String, OutputPort > outputPorts = new HashMap< String, OutputPort >(); private final Map< String, InputOperation > inputOperations = new HashMap< String, InputOperation>(); private final HashMap< String, Object > locksMap = new HashMap< String, Object >(); private final ClassLoader parentClassLoader; private final String[] includePaths; private final String[] optionArgs; private final String logPrefix; private final boolean verbose; private final Timer timer; // private long inputMessageTimeout = 24 * 60 * 60 * 1000; // 1 day private long persistentConnectionTimeout = 24 * 60 * 60 * 1000 * 10; // 10 days // private long persistentConnectionTimeout = 2 * 60 * 1000; // 4 minutes // private long persistentConnectionTimeout = 1; // 11 is the default initial capacity for PriorityQueue private final Queue< WeakReference< TimeoutHandler > > timeoutHandlerQueue = new PriorityQueue< WeakReference< TimeoutHandler > >( 11, new TimeoutHandler.Comparator() ); private final ExecutorService timeoutHandlerExecutor = Executors.newSingleThreadExecutor(); private final String programFilename; private final File programDirectory; private OutputPort monitor = null; public void setMonitor( OutputPort monitor ) { this.monitor = monitor; fireMonitorEvent( new MonitorAttachedEvent() ); } public boolean isMonitoring() { return monitor != null; } /*public long inputMessageTimeout() { return inputMessageTimeout; }*/ public void fireMonitorEvent( MonitoringEvent event ) { if ( monitor != null ) { CommMessage m = CommMessage.createRequest( "pushEvent", "/", MonitoringEvent.toValue( event ) ); CommChannel channel = null; try { channel = monitor.getCommChannel(); channel.send( m ); CommMessage response = null; do { response = channel.recvResponseFor( m ); } while( response == null ); } catch( URISyntaxException e ) { logWarning( e ); } catch( IOException e ) { logWarning( e ); } finally { if ( channel != null ) { try { channel.release(); } catch( IOException e ) { logWarning( e ); } } } } } public long persistentConnectionTimeout() { return persistentConnectionTimeout; } public CorrelationEngine correlationEngine() { return correlationEngine; } public void schedule( TimerTask task, long delay ) { if ( exiting == false ) { timer.schedule( task, delay ); } } public void addTimeoutHandler( TimeoutHandler handler ) { synchronized( timeoutHandlerQueue ) { timeoutHandlerQueue.add( new WeakReference< TimeoutHandler >( handler ) ); if ( timeoutHandlerQueue.size() == 1 ) { schedule( new TimerTask() { public void run() { synchronized( timeoutHandlerQueue ) { checkForExpiredTimeoutHandlers(); } } }, handler.time() - System.currentTimeMillis() + 1 ); } else { checkForExpiredTimeoutHandlers(); } } } /*public void removeTimeoutHandler( TimeoutHandler handler ) { synchronized( timeoutHandlerQueue ) { timeoutHandlerQueue.remove( handler ); checkForExpiredTimeoutHandlers(); } }*/ private void checkForExpiredTimeoutHandlers() { long currentTime = System.currentTimeMillis(); WeakReference< TimeoutHandler > whandler = timeoutHandlerQueue.peek(); boolean keepRun = true; TimeoutHandler handler; while( whandler != null && keepRun ) { handler = whandler.get(); if ( handler == null ) { timeoutHandlerQueue.remove(); whandler = timeoutHandlerQueue.peek(); } else if ( handler.time() < currentTime ) { final TimeoutHandler h = handler; timeoutHandlerExecutor.execute( new Runnable() { public void run() { h.onTimeout(); } } ); timeoutHandlerQueue.remove(); whandler = timeoutHandlerQueue.peek(); } else { keepRun = false; } } } /** * Returns the option arguments passed to this Interpreter. * @return the option arguments passed to this Interpreter */ public String[] optionArgs() { return optionArgs; } /** * Returns the include paths this Interpreter is considering. * @return the include paths this Interpreter is considering */ public String[] includePaths() { return includePaths; } /** * Registers a session starter on this <code>Interpreter</code>. * @param guard the input guard for this session starter * @param process the body of this session starter */ public void registerSessionStarter( InputOperationProcess guard, jolie.process.Process body ) { sessionStarters.put( guard.inputOperation().id(), new SessionStarter( guard, body ) ); } private JolieClassLoader classLoader; /** * Returns the output ports of this Interpreter. * @return the output ports of this Interpreter */ public Collection< OutputPort > outputPorts() { return outputPorts.values(); } /** * Returns the InputOperation identified by key. * @param key the name of the InputOperation to return * @return the InputOperation identified by key * @throws jolie.runtime.InvalidIdException if this Interpreter does not own the requested InputOperation * @see InputOperation */ public InputOperation getInputOperation( String key ) throws InvalidIdException { InputOperation ret; if ( (ret=inputOperations.get( key )) == null ) throw new InvalidIdException( key ); return ret; } /** * As {@link #getInputOperation(String) getInputOperation}, with the additional constraint that key must identify a OneWayOperation. * @param key the name of the OneWayOperation to return * @return the OneWayOperation identified by key * @throws jolie.runtime.InvalidIdException if this Interpreter does not own the requested OneWayOperation * @see OneWayOperation */ public OneWayOperation getOneWayOperation( String key ) throws InvalidIdException { InputOperation ret; if ( (ret=inputOperations.get( key )) == null || !(ret instanceof OneWayOperation) ) throw new InvalidIdException( key ); return (OneWayOperation)ret; } /** * As {@link #getInputOperation(String) getInputOperation}, with the additional constraint that key must identify a RequestResponseOperation. * @param key the name of the RequestResponseOperation to return * @return the RequestResponseOperation identified by key * @throws jolie.runtime.InvalidIdException if this Interpreter does not own the requested RequestResponseOperation * @see RequestResponseOperation */ public RequestResponseOperation getRequestResponseOperation( String key ) throws InvalidIdException { InputOperation ret; if ( (ret=inputOperations.get( key )) == null || !(ret instanceof RequestResponseOperation) ) throw new InvalidIdException( key ); return (RequestResponseOperation)ret; } /** * Returns the OutputPort identified by key. * @param key the name of the OutputPort to return * @return the OutputPort identified by key * @throws jolie.runtime.InvalidIdException if this Interpreter does not own the requested OutputPort */ public synchronized OutputPort getOutputPort( String key ) throws InvalidIdException { OutputPort ret; if ( (ret=outputPorts.get( key )) == null ) throw new InvalidIdException( key ); return (OutputPort)ret; } /** * Removes a registered OutputPort. * @param key the name of the OutputPort to remove */ public synchronized void removeOutputPort( String key ) { outputPorts.remove( key ); } /** * Returns the Definition identified by key. * @param key the name of the Definition to return * @return the Definition identified by key * @throws jolie.runtime.InvalidIdException if this Interpreter does not own the requested Definition */ public DefinitionProcess getDefinition( String key ) throws InvalidIdException { DefinitionProcess ret; if ( (ret=definitions.get( key )) == null ) throw new InvalidIdException( key ); return ret; } /** * Registers an <code>OutputPort</code> on this interpreter. * @param key the name of the <code>OutputPort</code> to register * @param value the <code>OutputPort</code> to register */ public void register( String key, OutputPort value ) { outputPorts.put( key, value ); } /** * Registers a defined sub-routine on this interpreter. * @param key the name of the defined sub-routine to register * @param value the defined sub-routine to register */ public void register( String key, DefinitionProcess value ) { definitions.put( key, value ); } /** * Registers an <code>InputOperation</code> on this interpreter. * @param key the name of the <code>InputOperation</code> to register * @param value the <code>InputOperation</code> to register */ public void register( String key, InputOperation value ) { inputOperations.put( key, value ); } /** * Registers an <code>EmbeddedServiceLoader</code> on this interpreter. * @param key the name of the <code>EmbeddedServiceLoader</code> to register * @param value the <code>EmbeddedServiceLoader</code> to register */ public void addEmbeddedServiceLoader( EmbeddedServiceLoader n ) { embeddedServiceLoaders.add( n ); } /** * Returns the <code>EmbeddedServiceLoader</code> instances registered on this interpreter. * @return the <code>EmbeddedServiceLoader</code> instances registered on this interpreter */ public Collection< EmbeddedServiceLoader > embeddedServiceLoaders() { return embeddedServiceLoaders; } /** * Makes this <code>Interpreter</code> entering in exiting mode. * When in exiting mode, an interpreter waits for each session to finish * its execution and then terminates gracefully the execution of the entire program. * An interpreter in exiting mode cannot receive any more messages. * * Multiple calls of this method are redundant. * * The fact that the interpreter cannot receive any more messages after * entering exiting mode can cause deadlocks if a session is waiting for a * message to finish its execution. Use this method with caution. */ public void exit() { synchronized( this ) { if ( exiting ) { return; } else { exiting = true; } } exitingLock.lock(); try { exitingCondition.signalAll(); } finally { exitingLock.unlock(); } executorService.shutdown(); commCore.shutdown(); timer.cancel(); } /** * Returns <code>true</code> if this interpreter is in exiting mode, <code>false</code> otherwise. * @return <code>true</code> if this interpreter is in exiting mode, <code>false</code> otherwise * @see #exit() */ public boolean exiting() { return exiting; } /** * Logs an unhandled fault using the logger of this interpreter. * This method is used by sessions that had to terminate due to a fault * which could not be handled, due to a missing fault handler. * @param fault the <code>FaultException</code> that could not be handled */ public void logUnhandledFault( FaultException fault ) { logger.info( logPrefix + "Thrown unhandled fault: " + fault.faultName() ); } /** * Logs an information message using the logger of this interpreter. * @param message the message to log */ public void logInfo( String message ) { logger.info( logPrefix + message ); } /** * Logs a severe error message using the logger of this interpreter. * @param message the message to log */ public void logSevere( String message ) { logger.severe( logPrefix + message ); } /** * Logs a warning message using the logger of this interpreter. * @param message the message to log */ public void logWarning( String message ) { logger.warning( logPrefix + message ); } /** * Logs a severe error message, created by reading the stack trace of the passed * <code>Throwable</code>, using the logger of this interpreter. * @param t the <code>Throwable</code> object whose stack trace has to be logged */ public void logSevere( Throwable t ) { ByteArrayOutputStream bs = new ByteArrayOutputStream(); t.printStackTrace( new PrintStream( bs ) ); logger.severe( logPrefix + bs.toString() ); } /** * Logs a warning message, created by reading the stack trace of the passed * <code>Throwable</code>, using the logger of this interpreter. * @param t the <code>Throwable</code> object whose stack trace has to be logged */ public void logWarning( Throwable t ) { ByteArrayOutputStream bs = new ByteArrayOutputStream(); t.printStackTrace( new PrintStream( bs ) ); logger.warning( logPrefix + bs.toString() ); } /** * Returns the execution mode of this Interpreter. * @return the execution mode of this Interpreter * @see Constants.ExecutionMode */ public Constants.ExecutionMode executionMode() { return executionMode; } /** * Sets the execution mode of this Interpreter. * @param mode the execution mode to set * @see Constants.ExecutionMode */ public void setExecutionMode( Constants.ExecutionMode mode ) { executionMode = mode; } /** * Adds a correlation set to this interpreter. * @param set the correlation set to add */ public void addCorrelationSet( CorrelationSet set ) { correlationSets.add( set ); for( String operation : set.correlatingOperations() ) { operationCorrelationSetMap.put( operation, set ); } } public CorrelationSet getCorrelationSetForOperation( String operationName ) { return operationCorrelationSetMap.get( operationName ); } /** * Returns the correlation sets of this Interpreter. * @return the correlation sets of this Interpreter */ public List< CorrelationSet > correlationSets() { return correlationSets; } /** * Returns the Interpreter the current thread is referring to. * @return the Interpreter the current thread is referring to */ public static Interpreter getInstance() { return ((JolieThread)Thread.currentThread()).interpreter(); } /** * Returns the JolieClassLoader this Interpreter is using. * @return the JolieClassLoader this Interpreter is using */ public JolieClassLoader getClassLoader() { return classLoader; } /** * Returns <code>true</code> if this interpreter is in verbose mode. * @return <code>true</code> if this interpreter is in verbose mode */ public boolean verbose() { return verbose; } /** Constructor. * * @param args The command line arguments. * @param parentClassLoader the parent ClassLoader to fall back when not finding resources. * @throws CommandLineException if the command line is not valid or asks for simple information. (like --help and --version) * @throws FileNotFoundException if one of the passed input files is not found. * @throws IOException if a Scanner constructor signals an error. */ public Interpreter( String[] args, ClassLoader parentClassLoader ) throws CommandLineException, FileNotFoundException, IOException { this( args, parentClassLoader, null ); } /** Constructor. * * @param args The command line arguments. * @param parentClassLoader the parent ClassLoader to fall back when not finding resources. * @param programDirectory the program directory of this Interpreter, necessary if it is run inside a JAP file. * @throws CommandLineException if the command line is not valid or asks for simple information. (like --help and --version) * @throws FileNotFoundException if one of the passed input files is not found. * @throws IOException if a Scanner constructor signals an error. */ public Interpreter( String[] args, ClassLoader parentClassLoader, File programDirectory ) throws CommandLineException, FileNotFoundException, IOException { this.parentClassLoader = parentClassLoader; cmdParser = new CommandLineParser( args, parentClassLoader ); classLoader = cmdParser.jolieClassLoader(); optionArgs = cmdParser.optionArgs(); programFilename = new File( cmdParser.programFilepath() ).getName(); arguments = cmdParser.arguments(); this.correlationEngine = cmdParser.correlationAlgorithmType().createInstance( this ); commCore = new CommCore( this, cmdParser.connectionsLimit() /*, cmdParser.connectionsCache() */ ); includePaths = cmdParser.includePaths(); StringBuilder builder = new StringBuilder(); builder.append( '[' ); builder.append( programFilename ); builder.append( "] " ); logPrefix = builder.toString(); verbose = cmdParser.verbose(); timer = new Timer( programFilename + "-Timer" ); exitingLock = new ReentrantLock(); exitingCondition = exitingLock.newCondition(); if ( cmdParser.programDirectory() == null ) { this.programDirectory = programDirectory; } else { this.programDirectory = cmdParser.programDirectory(); } if ( this.programDirectory == null ) { throw new IOException( "Could not localize the service execution directory. This is probably a bug in the JOLIE interpreter, please report it to jolie-devel@lists.sf.net" ); } } /** * Returns the parent directory of the program executed by this Interpreter. * @return the parent directory of the program executed by this Interpreter. */ public File programDirectory() { return programDirectory; } /** * Returns the program filename this interpreter was launched with. * @return the program filename this interpreter was launched with */ public String programFilename() { return programFilename; } /** * Returns the parent class loader passed to the constructor of this interpreter. * @return the parent class loader passed to the constructor of this interpreter */ public ClassLoader parentClassLoader() { return parentClassLoader; } /** * Returns the global lock registered on this interpreter with the passed identifier. * If a global lock with such identifier is not registered, a new one is * automatically created, registered and returned. * @param id the global lock identifier * @return the global lock registered on this interpreter with the specified identifier */ public Object getLock( String id ) { Object l = locksMap.get( id ); if ( l == null ) { l = new Object(); locksMap.put( id, l ); } return l; } public SessionStarter getSessionStarter( String operationName ) { return sessionStarters.get( operationName ); } /** * Returns the {@code global} value of this Interpreter. * @return the {@code global} value of this Interpreter */ public Value globalValue() { return globalValue; } private InitSessionThread initExecutionThread; private SessionThread mainSession = null; /** * Returns the {@link SessionThread} of the Interpreter that started the program execution. * @return the {@link SessionThread} of the Interpreter that started the program execution */ public SessionThread initThread() { return initExecutionThread; } private static class InterpreterStartFuture implements Future< Exception > { private final Lock lock; private final Condition initCompleted; private Exception result; private boolean isDone = false; public InterpreterStartFuture() { lock = new ReentrantLock(); initCompleted = lock.newCondition(); } public boolean cancel( boolean mayInterruptIfRunning ) { return false; } public Exception get( long timeout, TimeUnit unit ) throws InterruptedException, TimeoutException { try { lock.lock(); if ( !isDone ) { if ( !initCompleted.await( timeout, unit ) ) { throw new TimeoutException(); } } } finally { lock.unlock(); } return result; } public Exception get() throws InterruptedException { try { lock.lock(); if ( !isDone ) { initCompleted.await(); } } finally { lock.unlock(); } return result; } public boolean isCancelled() { return false; } public boolean isDone() { return isDone; } private void setResult( Exception e ) { lock.lock(); try { result = e; isDone = true; initCompleted.signalAll(); } finally { lock.unlock(); } } } /** * Starts this interpreter, returning a <code>Future</code> which can * be interrogated to know when the interpreter start procedure has been * completed and the interpreter is ready to receive messages. * @return a <code>Future</code> which can * be interrogated to know when the interpreter start procedure has been * completed and the interpreter is ready to receive messages. */ public Future< Exception > start() { InterpreterStartFuture f = new InterpreterStartFuture(); (new StarterThread( f )).start(); return f; } private void init() throws InterpreterException, IOException { /** * Order is important. * 1 - CommCore needs the OOIT to be initialized. * 2 - initExec must be instantiated before we can receive communications. */ if ( buildOOIT() == false ) { throw new InterpreterException( "Error: the interpretation environment couldn't have been initialized" ); } sessionStarters = Collections.unmodifiableMap( sessionStarters ); try { initExecutionThread = new InitSessionThread( this, getDefinition( "init" ) ); commCore.init(); // Initialize program arguments in the args variabile. ValueVector jArgs = ValueVector.create(); for( String s : arguments ) { jArgs.add( Value.create( s ) ); } initExecutionThread.state().root().getChildren( "args" ).deepCopy( jArgs ); initExecutionThread.addSessionListener( new SessionListener() { public void onSessionExecuted( SessionThread session ) {} public void onSessionError( SessionThread session, FaultException fault ) { exit(); } }); correlationEngine.onSingleExecutionSessionStart( initExecutionThread ); initExecutionThread.addSessionListener( correlationEngine ); initExecutionThread.start(); } catch( InvalidIdException e ) { assert false; } } private void runCode() { try { initExecutionThread.join(); } catch( InterruptedException e ) { logSevere( e ); } if ( executionMode == Constants.ExecutionMode.SINGLE ) { try { mainSession.start(); mainSession.join(); } catch( InterruptedException e ) { logSevere( e ); } } else { exitingLock.lock(); try { exitingCondition.await(); } catch( InterruptedException e ) { logSevere( e ); } finally { exitingLock.unlock(); } } } /** * Runs the interpreter behaviour specified by command line. * The default behaviour is to execute the input code. * * Note that you must shutdown the CommCore of this Interpreter * manually after calling this method. * @throws IOException if a Parser propagates a Scanner exception * @throws InterpreterException if the interpretation tree could not be built */ public void run() throws InterpreterException, IOException { init(); runCode(); } private final ExecutorService executorService = Executors.newCachedThreadPool( new JolieExecutionThreadFactory( this ) ); /** * Runs an asynchronous task in this Interpreter internal thread pool. * @param r the Runnable object to execute */ public void execute( Runnable r ) { executorService.execute( r ); } private static final AtomicInteger starterThreadCounter = new AtomicInteger(); private static String createStarterThreadName( String programFilename ) { return programFilename + "-StarterThread-" + starterThreadCounter.incrementAndGet(); } private class StarterThread extends Thread { private final InterpreterStartFuture future; public StarterThread( InterpreterStartFuture future ) { super( createStarterThreadName( programFilename ) ); this.future = future; setContextClassLoader( classLoader ); } @Override public void run() { try { init(); future.setResult( null ); } catch( Exception e ) { future.setResult( e ); } runCode(); //commCore.shutdown(); exit(); free(); } private void free() { /* We help the Java(tm) Garbage Collector. * Looks like it needs this or the Interpreter * does not get collected. */ definitions.clear(); inputOperations.clear(); locksMap.clear(); initExecutionThread = null; sessionStarters = new HashMap< String, SessionStarter >(); outputPorts.clear(); correlationSets.clear(); globalValue.erase(); embeddedServiceLoaders.clear(); classLoader = null; commCore = null; //System.gc(); } } /** * Returns the CommCore of this Interpreter. * @return the CommCore of this Interpreter */ public CommCore commCore() { return commCore; } private boolean buildOOIT() throws InterpreterException { try { Program program = null; if ( cmdParser.isProgramCompiled() ) { ObjectInputStream istream = new ObjectInputStream( cmdParser.programStream() ); Object o = istream.readObject(); if ( o instanceof Program ) { program = (Program)o; } else { throw new InterpreterException( "Input compiled program is not a JOLIE program" ); } } else { OLParser olParser = new OLParser( new Scanner( cmdParser.programStream(), new URI( "file:" + cmdParser.programFilepath() ) ), includePaths, classLoader ); olParser.putConstants( cmdParser.definedConstants() ); program = olParser.parse(); OLParseTreeOptimizer optimizer = new OLParseTreeOptimizer( program ); program = optimizer.optimize(); } SemanticVerifier semanticVerifier = new SemanticVerifier( program ); if ( !semanticVerifier.validate() ) { throw new InterpreterException( "Exiting" ); } if ( cmdParser.typeCheck() ) { TypeChecker typeChecker = new TypeChecker( program, semanticVerifier.executionMode(), semanticVerifier.correlationFunctionInfo() ); if ( !typeChecker.check() ) { throw new InterpreterException( "Exiting" ); } } return (new OOITBuilder( this, program, semanticVerifier.isConstantMap(), semanticVerifier.correlationFunctionInfo() )).build(); } catch( IOException e ) { throw new InterpreterException( e ); } catch( URISyntaxException e ) { throw new InterpreterException( e ); } catch( ParserException e ) { throw new InterpreterException( e ); } catch( ClassNotFoundException e ) { throw new InterpreterException( e ); } finally { cmdParser = null; // Free memory } } /** * Starts a service session. * @param message the message triggering the session start * @param channel the channel of the message triggering the session start * @return {@code true} if the service session is started, {@code false} otherwise */ public boolean startServiceSession( final CommMessage message, CommChannel channel ) { if ( executionMode == Constants.ExecutionMode.SINGLE ) { return false; } SessionStarter starter = sessionStarters.get( message.operationName() ); if ( starter == null ) { return false; } try { initExecutionThread.join(); } catch( InterruptedException e ) { return false; } final SessionThread spawnedSession; if ( executionMode == Constants.ExecutionMode.CONCURRENT ) { State state = initExecutionThread.state().clone(); jolie.process.Process sequence = new SequentialProcess( new jolie.process.Process[] { starter.guard.receiveMessage( new SessionMessage( message, channel ), state ), starter.body } ); spawnedSession = new SessionThread( sequence, state, initExecutionThread ); correlationEngine.onSessionStart( spawnedSession, starter, message ); spawnedSession.addSessionListener( correlationEngine ); logSessionStart( message.operationName(), spawnedSession.getName() ); spawnedSession.addSessionListener( new SessionListener() { public void onSessionExecuted( SessionThread session ) { logSessionEnd( message.operationName(), session.getSessionId() ); } public void onSessionError( SessionThread session, FaultException fault ) { logSessionEnd( message.operationName(), session.getSessionId() ); } } ); spawnedSession.start(); } else if ( executionMode == Constants.ExecutionMode.SEQUENTIAL ) { /* * We use mainSession as a reference to the latest arrived * sequential session spawn request. */ final SessionThread currentMainSession = mainSession; State state = initExecutionThread.state().clone(); jolie.process.Process sequence = new SequentialProcess( new jolie.process.Process[] { new jolie.process.Process() { public void run() throws FaultException, ExitingException { if ( currentMainSession != null ) { try { currentMainSession.join(); } catch( InterruptedException e ) { logSevere( e ); } } } public jolie.process.Process clone( TransformationReason reason ) { return this; } public boolean isKillable() { return false; } }, starter.guard.receiveMessage( new SessionMessage( message, channel ), state ), starter.body } ); spawnedSession = new SessionThread( sequence, state, initExecutionThread ); correlationEngine.onSessionStart( spawnedSession, starter, message ); spawnedSession.addSessionListener( correlationEngine ); spawnedSession.addSessionListener( new SessionListener() { public void onSessionExecuted( SessionThread session ) { logSessionEnd( message.operationName(), session.getSessionId() ); } public void onSessionError( SessionThread session, FaultException fault ) { logSessionEnd( message.operationName(), session.getSessionId() ); } } ); mainSession = spawnedSession; mainSession.start(); } return true; } private void logSessionStart( String operationName, String sessionId ) { if ( isMonitoring() ) { fireMonitorEvent( new SessionStartedEvent( operationName, sessionId ) ); fireMonitorEvent( new OperationStartedEvent( operationName, sessionId ) ); } } private void logSessionEnd( String operationName, String sessionId ) { if ( isMonitoring() ) { fireMonitorEvent( new SessionEndedEvent( operationName, sessionId ) ); } } }