// BEGIN LICENSE BLOCK // Version: CMPL 1.1 // // The contents of this file are subject to the Cisco-style Mozilla Public // License Version 1.1 (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License // at www.eclipse-clp.org/license. // // Software distributed under the License is distributed on an "AS IS" // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See // the License for the specific language governing rights and limitations // under the License. // // The Original Code is The ECLiPSe Constraint Logic Programming System. // The Initial Developer of the Original Code is Cisco Systems, Inc. // Portions created by the Initial Developer are // Copyright (C) 2006 Cisco Systems, Inc. All Rights Reserved. // // Contributor(s): Andrew Sadler, IC-Parc // // END LICENSE BLOCK package com.parctechnologies.eclipse; import java.io.*; import java.util.*; /** * Wrapper class which implements the EclipseMultitaskConnection * interface so as to make it seem as though the peer has control for * the entire duration of ECLiPSe multitasking phases. * *
Objects of this class can only be constructed by calling the
* registerMultitask method of EclipseConnections. This class works by
* delegating most of the work to the EclipseConnection that created it.
*
*
* @see EclipseMultitaskConnection
* */
class EclipseMultitaskConnectionImpl implements EclipseMultitaskConnection {
static final String STATE_EXCEPTION_MESSAGE = "Attempt to confirm/terminate multitasking phase whilst not in multitasking phase.";
protected static final Atom peerConfirmMultitaskGoal = new Atom("peer_multitask_confirm");
protected static final Atom peerTerminateMultitaskGoal = new Atom("peer_multitask_terminate");
static final String START_MULTITASK_MESSAGE = "start_multitask";
static final String END_MULTITASK_MESSAGE = "end_multitask";
static final String INTERACT_MULTITASK_MESSAGE = "interact";
static final int MULTITASK_RPC_TIMEOUT = 10000;
/** underlying eclipse connection */
EclipseConnectionImpl eclipse;
/** queue on which multitask messages are recieved */
FromEclipseQueue multitaskQueue;
/** Are we in a multitask phase, and if so which type. null=not in
multitask phase. */
String multitaskPhase;
/** Holds the thread on which the multitask phase messages are begin
delivered. Any RPCs performed during a multitask phase fromthis
thread must be begin called from inside queue callbacks and so
they should NOT be delayed, but rather should be performed
immediately (so as to avoid deadlock). */
Thread multitaskThread;
/** Holds the delayed RPCs */
MultitaskGoalQueue multitaskGoalQueue;
/** Holds the MultitaskListeners */
List multitaskListenerList ;
EclipseMultitaskConnectionImpl(EclipseConnectionImpl eclipse,
FromEclipseQueue multitaskQueue) throws IOException {
this.eclipse = eclipse;
this.multitaskQueue = multitaskQueue;
this.multitaskGoalQueue = new MultitaskGoalQueue();
this.multitaskPhase = null;
this.multitaskListenerList = new LinkedList();
this.multitaskThread = null;
// register the queue listener on the multitask queue
multitaskQueue.setListener(new MultitaskQL());
}
public CompoundTerm rpc(String goal) throws EclipseException, IOException {
if ((multitaskPhase == null) ||
(Thread.currentThread() == multitaskThread)) {
return eclipse.rpc(goal);
}
eclipse.testTerminated();
return multitaskGoalQueue.execute(goal);
}
public CompoundTerm rpc(CompoundTerm goal) throws EclipseException, IOException {
if ((multitaskPhase == null) ||
(Thread.currentThread() == multitaskThread)) {
return eclipse.rpc(goal);
}
eclipse.testTerminated();
return multitaskGoalQueue.execute(goal);
}
public FromEclipseQueue getFromEclipseQueue(String name) throws EclipseException, IOException {
return eclipse.getFromEclipseQueue(name);
}
public ToEclipseQueue getToEclipseQueue(String name) throws EclipseException, IOException {
return eclipse.getToEclipseQueue(name);
}
public AsyncEclipseQueue getAsyncEclipseQueue(String name) throws EclipseException, IOException {
return eclipse.getAsyncEclipseQueue(name);
}
public void compile(File f) throws EclipseException, IOException {
rpc(new CompoundTermImpl("compile" , getPath(f)));
}
public String getPath(File f) throws EclipseException, IOException {
CompoundTerm call = new CompoundTermImpl("os_file_name" , null , f.getAbsolutePath() );
return (String) rpc(call).arg(1);
}
public CompoundTerm rpc(String functor, Object arg1) throws EclipseException, IOException {
return rpc(new CompoundTermImpl(functor,arg1));
}
public CompoundTerm rpc(String functor, Object arg1,
Object arg2) throws EclipseException, IOException {
return rpc(new CompoundTermImpl(functor, arg1, arg2));
}
public CompoundTerm rpc(String functor, Object arg1,
Object arg2, Object arg3) throws EclipseException, IOException {
return rpc(new CompoundTermImpl(functor, arg1, arg2, arg3));
}
public CompoundTerm rpc(String functor, Object arg1,
Object arg2, Object arg3, Object arg4) throws EclipseException, IOException {
return rpc(new CompoundTermImpl(functor, arg1, arg2, arg3, arg4));
}
public CompoundTerm rpc(String functor, Object arg1,
Object arg2, Object arg3, Object arg4,
Object arg5) throws EclipseException, IOException {
return rpc(new CompoundTermImpl(functor, arg1, arg2, arg3, arg4, arg5));
}
public CompoundTerm rpc(String functor, Object[] args) throws EclipseException, IOException {
return rpc(new CompoundTermImpl(functor, args));
}
public CompoundTerm rpc(Object[] goalTerm) throws EclipseException, IOException {
return rpc(new CompoundTermImpl(goalTerm));
}
public Atom getPeerName() {
return eclipse.getPeerName();
}
/**
* Add the given listener to this EclipseMultitaskConnection.
* @return this
. Since this object is already
* registered for multitask phases. */
public EclipseMultitaskConnection registerMultitask(MultitaskListener multitaskListener) throws EclipseException,IOException {
eclipse.testTerminated();
if (multitaskListener != null) {
multitaskListenerList.add(multitaskListener);
}
return this;
}
/**
* Set the property that a multitasking phase is in progress
*/
void setMultitaskPhase(String type) {
multitaskPhase = type ;
}
/**
* Perform any pending RPC's
*/
void processMultitaskTimeSlice() {
// execute any delayed RPC's using the underlying EclipseConnection
multitaskGoalQueue.process(eclipse);
}
private static class MultitaskGoalQueue {
/** Stores the MultitaskGoals yet to be processed by the
multitask timeslice handler */
private List pendingGoals = new LinkedList();
/** Stores any exception which occurs as a result of errors in the
multitask protocol */
Exception multitaskProtocolException;
synchronized void setProtocolException(Exception ioe) {
if (multitaskProtocolException != null) {
multitaskProtocolException = ioe;
}
}
synchronized void testProtocolException() throws EclipseException,IOException {
if (multitaskProtocolException != null) {
if (multitaskProtocolException instanceof EclipseException) {
throw (EclipseException)multitaskProtocolException;
}
if (multitaskProtocolException instanceof IOException) {
throw (IOException)multitaskProtocolException;
}
throw new IOException("Multitask protocol error:"+
multitaskProtocolException.getMessage());
}
}
/** Puts the Object (String or CompoundTerm) on the list of goals
to be executed and then wait until it has been. */
public CompoundTerm execute(Object goal) throws IOException, EclipseException {
MultitaskGoal mg = new MultitaskGoal(goal);
synchronized(this) {
pendingGoals.add(mg);
}
synchronized(mg) {
while((mg.result == null) && (mg.exception == null)) {
try {
mg.wait(MULTITASK_RPC_TIMEOUT);
}
catch(InterruptedException ie){}
testProtocolException();
}
}
// if an exception occured during the RPC, then re-throw it in
// the caller thread
if (mg.exception != null) {
if (mg.exception instanceof IOException) {
throw (IOException)(mg.exception).fillInStackTrace();
} else if (mg.exception instanceof EclipseException) {
throw (EclipseException)(mg.exception).fillInStackTrace();
} else {
throw new EclipseException(mg.exception.getMessage());
}
}
return mg.result;
}
/** Process the pending goals. This should only be called from
within the handler for the multitask message queue. */
public void process(EclipseConnection eclipse) {
Iterator it;
synchronized(this) {
if (pendingGoals.isEmpty()) {
return;
}
// get the iterator for the current pending goals
it = pendingGoals.iterator();
// clear the pending goals before performing any RPC to avoid
// infinite recusion
pendingGoals = new LinkedList();
}
while(it.hasNext()) {
MultitaskGoal mg = (MultitaskGoal)(it.next());
try {
if (mg.goal instanceof CompoundTerm) {
mg.result = eclipse.rpc((CompoundTerm)mg.goal);
} else if (mg.goal instanceof String) {
mg.result = eclipse.rpc((String)mg.goal);
} else {
throw new EclipseException("Unknown object type ("+mg.goal+") for rpc.");
}
} catch(IOException ioe) {
mg.exception = ioe;
} catch(EclipseException ee) {
mg.exception = ee;
}
synchronized(mg) {
/* wake up the threads which were waiting for the results */
mg.notifyAll();
}
}
}
}
/** Class to hold the goal to be executed by the Multitask.timeslice
* thread */
private static class MultitaskGoal
{
private Object goal;
private CompoundTerm result;
private Exception exception;
private MultitaskGoal(Object goal)
{
this.goal = goal;
}
}
/** Queue listener attached to the "Multitask" stream. Behaviour is
to set the state to indicate being in multi-tasking
mode */
private class MultitaskQL implements QueueListener
{
protected CompoundTerm eventTerm;
EXDRInputStream eis = null;
public void dataAvailable(Object source)
{
if(eis == null) {
FromEclipseQueue feq = (FromEclipseQueue) source;
eis = new EXDRInputStream(feq);
}
try {
eventTerm = (CompoundTerm) eis.readTerm();
processEvent();
} catch(EclipseException ee) {
// record the exception so it can be thrown by the next thread
// to attempt multitasking RPC
multitaskGoalQueue.setProtocolException(ee);
} catch(IOException ioe) {
// record the exception so it can be thrown by the next thread
// to attempt multitasking RPC
multitaskGoalQueue.setProtocolException(ioe);
}
}
void processEvent() throws EclipseException,IOException {
String functor = eventTerm.functor();
if (START_MULTITASK_MESSAGE.equals(functor)) {
Object typeTerm = eventTerm.arg(1);
if (typeTerm instanceof String) {
setMultitaskPhase((String)typeTerm);
} else if (typeTerm instanceof CompoundTerm) {
setMultitaskPhase(((CompoundTerm)typeTerm).functor());
} else {
throw new IOException("Multitask protocol error. Unexpected multitasking phase type:"+typeTerm);
}
/* Record the thread on which this messages was delivered */
multitaskThread = Thread.currentThread();
/* notify all listeners */
for(Iterator it = multitaskListenerList.iterator(); it.hasNext(); ) {
MultitaskListener ml = (MultitaskListener)it.next();
ml.starting(EclipseMultitaskConnectionImpl.this,getMultitaskPhase());
}
/* perform any pending rpc*/
processMultitaskTimeSlice();
} else if (END_MULTITASK_MESSAGE.equals(functor)) {
/* notify all listeners */
for(Iterator it = multitaskListenerList.iterator(); it.hasNext(); ) {
MultitaskListener ml = (MultitaskListener)it.next();
ml.ending(EclipseMultitaskConnectionImpl.this,getMultitaskPhase());
}
/* perform any pending rpc*/
processMultitaskTimeSlice();
/* Clear the thread on which this messages was delivered */
multitaskThread = null;
/* leave the multitask phase */
setMultitaskPhase(null);
} else if (INTERACT_MULTITASK_MESSAGE.equals(functor)) {
/* perform any pending rpc*/
processMultitaskTimeSlice();
}
}
public void dataRequest(Object source){}
}
/**
* Gets the non-"multitasking aware" EclipseConnection from which
* this "multitasking aware" connection was created..
*
*
This method is provided incase the caller needs access to the * type of the underlying connection, or if the caller requires the * blocking RPC semantics of the underlying connection. */ public EclipseConnection getEclipseConnection() { return eclipse; } protected String getMultitaskPhase() throws EclipseException, IOException { eclipse.testTerminated(); multitaskGoalQueue.testProtocolException(); return multitaskPhase; } public void multitaskConfirm() throws EclipseException, IOException, IllegalStateException { synchronized(this) { if (multitaskPhase == null) { throw new IllegalStateException(STATE_EXCEPTION_MESSAGE); } } rpc(peerConfirmMultitaskGoal); } public void multitaskTerminate() throws EclipseException, IOException, IllegalStateException { synchronized(this) { if (multitaskPhase == null) { throw new IllegalStateException(STATE_EXCEPTION_MESSAGE); } } rpc(peerTerminateMultitaskGoal); } }