package jadex.bdi.runtime.impl.flyweights; import jadex.bdi.runtime.IElement; import jadex.bdi.runtime.interpreter.BDIInterpreter; import jadex.bdi.runtime.interpreter.OAVBDIRuntimeModel; import jadex.bridge.ComponentTerminatedException; import jadex.commons.SReflect; import jadex.commons.SUtil; import jadex.rules.state.IOAVState; import java.util.Collection; /** * Flyweight for all runtime elements. */ public abstract class ElementFlyweight implements IElement { //-------- attributes -------- /** The state. */ private IOAVState state; /** The object handle for the element. */ private Object handle; /** The object handle for the element's scope. */ // Todo: remove??? private Object scope; /** The interpreter. */ private BDIInterpreter interpreter; /** Flag to indicate if the flyweight was already cleaned up. */ private boolean cleanedup; //-------- constructors -------- /** * Create a new element flyweight. * @param state The state. * @param scope The scope handle. * @param handle The element handle. * @param rplan The calling plan (if called from plan) */ public ElementFlyweight(IOAVState state, Object scope, Object handle) { assert !(scope instanceof ElementFlyweight); // if(handle==null && !(this instanceof ParameterFlyweight || this instanceof ParameterSetFlyweight)) // Thread.dumpStack(); assert handle!=null || this instanceof ParameterFlyweight || this instanceof ParameterSetFlyweight || this instanceof ProcessableElementFlyweight: this; this.state = state; this.scope = scope; if(scope!=null) state.addExternalObjectUsage(scope, this); this.interpreter = BDIInterpreter.getInterpreter(state); if(interpreter==null) throw new ComponentTerminatedException(null); setHandle(handle); } //-------- element methods --------- /** * Get the name. * @return The name. * / public String getName() { return (String)state.getAttributeValue(handle, OAVBDIRuntimeModel.element_has_name); }*/ /** * The hash code. */ public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (!hasHandle() ? 0 : getHandle().hashCode()); result = prime * result + ((getScope() == null) ? 0 : getScope().hashCode()); result = prime * result + ((getState() == null) ? 0 : getState().hashCode()); return result; } /** * Test equality. */ public boolean equals(Object obj) { boolean ret = false; if(obj instanceof ElementFlyweight) { ElementFlyweight other = (ElementFlyweight)obj; ret = SUtil.equals(getHandle(), other.getHandle()) // && SUtil.equals(getScope(), other.getScope()) && SUtil.equals(getState(), other.getState()); } return ret; } /** * Get the state. * @return The state. */ public IOAVState getState() { return state; } /** * Test, if the handle has already been set. */ public boolean hasHandle() { return handle!=null; } /** * Get the handle. * @return The handle. */ public Object getHandle() { if(handle==null) throw new UnsupportedOperationException("Cannot get handle before it is set: "+this.getClass()); return handle; } /** * Set the handle. * @param handle The handle to set. */ protected void setHandle(Object handle) { assert this.handle==null; this.handle = handle; // Only called from synchronized code -> no agent invocation necessary if(handle!=null) state.addExternalObjectUsage(handle, this); } /** * Get the scope. * @return The scope. */ public Object getScope() { return scope; } /** * Get the interpreter. * @return The interpreter. */ public BDIInterpreter getInterpreter() { return interpreter; } /** * Remove the external usage preventing * the state object from being garbage * collected. * / // Hack!!! finalize() is required to call state.removeExternalObjectUsage(...) // unless ContentIDState is used, which ignores external object usages. protected void finalize() throws Throwable { // Must be done on agent thread, not // on gc thread. if(!cleanedup) { // String id = ""+handle; // System.err.println("cleaning up: "+id+", "+cnt[0]); try { interpreter.invokeSynchronized(new Runnable() { String element = ""+handle; public void run() { cleanup(); } public String toString() { return super.toString()+", "+element; } }); } catch(Throwable e) { // Exception can occur if agent is already terminated. // So nothing to do in that case. // System.out.println("Agent already terminated: "+id); // e.printStackTrace(); } // System.err.println("cleaned up: "+id+", "+cnt[0]); } super.finalize(); }*/ /** * Cleanup the flyweight. * Must be called on agent thread. */ public final void cleanup() { if(!cleanedup) doCleanup(); cleanedup = true; } /** * Actual cleanup code. * When overriding this method, super.doCleanup() has to be called. */ protected void doCleanup() { if(handle!=null) { state.removeExternalObjectUsage(handle, this); handle = null; } if(scope!=null) { state.removeExternalObjectUsage(scope, this); scope = null; } } /** * Get a string representation of this element. */ public String toString() { String string = SReflect.getUnqualifiedClassName(this.getClass()); if(string.endsWith("Flyweight")) string = string.substring(0, string.length()-9); return string; // if(getInterpreter().isExternalThread()) // { // AgentInvocation invoc = new AgentInvocation() // { // public void run() // { // string = SReflect.getUnqualifiedClassName(this.getClass()); // if(string.endsWith("Flyweight")) // string = string.substring(0, string.length()-9); // string += "("+getTypeName()+"-"+getHandle()+")"; // } // }; // return invoc.string; // } // else // { // String ret = SReflect.getUnqualifiedClassName(this.getClass()); // if(ret.endsWith("Flyweight")) // ret = ret.substring(0, ret.length()-9); // return ret+"("+getTypeName()+"-"+getHandle()+")"; // } } /** * Add an event listener. * @param listener The listener. * @param handle The handle. */ protected void addEventListener(Object listener, Object handle) { assert handle!=null; IOAVState state = getState(); Object scope = getScope(); addEventListener(listener, handle, state, scope); // System.out.println("addLis: "+getScope()+" "+listener+" "+handle+" "+getState().getAttributeValues(getScope(), OAVBDIRuntimeModel.capability_has_listeners)); } /** * Add an event listener to the agent. */ public static void addEventListener(Object listener, Object handle, IOAVState state, Object scope) { Object le = state.getAttributeValue(scope, OAVBDIRuntimeModel.capability_has_listeners, listener); if(le==null) { le = state.createObject(OAVBDIRuntimeModel.listenerentry_type); state.setAttributeValue(le, OAVBDIRuntimeModel.listenerentry_has_listener, listener); state.setAttributeValue(le, OAVBDIRuntimeModel.listenerentry_has_scope, scope); state.addAttributeValue(scope, OAVBDIRuntimeModel.capability_has_listeners, le); } state.addAttributeValue(le, OAVBDIRuntimeModel.listenerentry_has_relevants, handle); BDIInterpreter.getInterpreter(state).getEventReificator().addObservedElement(handle); } /** * Remove an event listener. * @param listener The listener. * @param handle The handle. * @param failsafe Don't throw exception, when listener could not be removed (e.g. for finished goals). */ protected void removeEventListener(Object listener, Object handle, boolean failsafe) { assert handle!=null; // System.out.println("remLis: "+getScope()+" "+listener+" "+getState().getAttributeValues(getScope(), OAVBDIRuntimeModel.capability_has_listeners)); IOAVState state = getState(); Object scope = getScope(); removeEventListener(listener, handle, failsafe, state, scope); } /** * Remove an event listener. */ public static void removeEventListener(Object listener, Object handle, boolean failsafe, IOAVState state, Object scope) { Object le = state.getAttributeValue(scope, OAVBDIRuntimeModel.capability_has_listeners, listener); if(le==null && !failsafe) { throw new RuntimeException("Listener not found: "+listener); } else if(le!=null) { Collection coll = state.getAttributeValues(le, OAVBDIRuntimeModel.listenerentry_has_relevants); if(!coll.contains(handle) && !failsafe) { throw new RuntimeException("Listener could not be removed properly: "+listener); } else if(coll.contains(handle)) { state.removeAttributeValue(le, OAVBDIRuntimeModel.listenerentry_has_relevants, handle); coll = state.getAttributeValues(le, OAVBDIRuntimeModel.listenerentry_has_relevants); if(coll==null || coll.isEmpty()) state.removeAttributeValue(scope, OAVBDIRuntimeModel.capability_has_listeners, listener); BDIInterpreter.getInterpreter(state).getEventReificator().removeObservedElement(handle); } } } //-------- inner classes -------- /** * An action to be executed on the agent thread. * Provides predefined variables to store results. * Directly invokes agenda in construcor. */ public abstract class AgentInvocation implements Runnable { //-------- attributes -------- /** Argument. */ public Object arg; /** Arguments. */ public Object[] args; //-------- out parameters -------- /** The object result variable. */ public Object object; /** The string result variable. */ public String string; /** The int result variable. */ public int integer; /** The long result variable. */ public long longint; /** The boolean result variable. */ public boolean bool; /** The object array result variable. */ public Object[] oarray; /** The string result variable. */ public String[] sarray; /** The class result variable. */ public Class clazz; /** The exception. */ public Exception exception; //-------- constructors -------- /** * Create an action to be executed in sync with the agent thread. */ public AgentInvocation() { this(null); } /** * Create an action to be executed in sync with the agent thread. */ public AgentInvocation(Object arg) { this.arg = arg; getInterpreter().invokeSynchronized(this); } /** * Create an action to be executed in sync with the agent thread. */ public AgentInvocation(Object[] args) { this.args = args; getInterpreter().invokeSynchronized(this); } } }