/* * © Copyright IBM Corp. 2012-2013 * * 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 com.ibm.commons.util; /** * Helper class to permit the synchronous call of a method hosted in a secondary * thread, useful for library that make mandatory the use of a specific thread * class in order to call their functions.</P> * This monitor class is the link between the calling and the called thread. * <PRE> * Calling thread * | * | call() Called Thread * +------>-----------+ * | * +------<-----------+ * | * V * </PRE> * <B>Monitor used :</B><BR> * The monitor class has the same methods than the callee object but use the * intermediate BaseMethod object and in fact run the <CODE>call()</CODE> method * of the monitor. The thread synchronization is ensured by the monitor's methods. * <PRE> * class TCalledThread extends BaseThreadMonitor { * public int myMethod(TCallee callee, int param) { * TMyMethod method = new TMyMethod(callee,param); * call( method ); * return method.result; * } * } * </PRE> * <B>Calling thread use :</B><BR> * The call a method must be done within a inherited BaseMethod class, in its * <CODE>execute()</CODE> method. The BaseMethod object is constructed with all * the method parameters, whose need to be stored in order to be used when the * method is called.<BR> * <PRE> * class TMyMethod implements BaseThreadMonitor.BaseMethod { * TMyMethod(TCallee callee, int param) { * this.callee = callee; * this.param = param; * } * public void execute() { * result = callee.myMethod(param); * } * TCallee callee; * int result; * int param; * } * </PRE> * Call from the calling thread : * <PRE> * int myResult = myMonitor.myMethod(myParam); * </PRE> * <B>Called thread use :</B><BR> * The called thread must wait for an incomming event and when this event occurs * it gets the current BaseMethod object and call its <CODE>execute()</CODE> method. * This is done by calling the <CODE>run()</CODE> method of the ThreadMonitor. * The result in then stored in an internal temporary field which can be read by * the calling thread. * <PRE> * class TCalledThread extends Thread { * public TCalledThread(TMyMonitor monitor) { * this.monitor = monitor; * } * public void run() { * // Run some initializations * .... * // Run an infinite loop * while(true) { * monitor.run(); * } * } * TMyMonitor monitor; * } * </PRE> * @ibm-not-published */ public class BaseThreadMonitor { /** * Method call interface. */ public static abstract class BaseMethod { /** * Execute the method. */ public abstract void execute() throws Exception; /** * Filled if an exception is thrown. */ public Exception exception; } /** * Thread monitor constructor. */ public BaseThreadMonitor() { } /** * Call a method located in the called thread. * @param method the method to call. It must own its parameters as well as * its result. * @return the method called (=parameter) */ public synchronized BaseMethod call( BaseMethod method ) throws Exception { synchronized (methodLock) { // Set the method to call this.method = method; this.method.exception = null; //System.out.println("requesting to execute "+method); methodLock.notify(); //in case the monitor was waiting for the request while (this.method != null) { try { methodLock.wait(); } catch( InterruptedException e ) {} } } //System.out.println("got the result for "+method); if (method.exception != null) { throw method.exception; } return method; } protected Object beforeCallingMethod( BaseMethod method ) { return null; } protected void afterCallingMethod( BaseMethod method, Object param ) { } int i = 0; /** * Run a synchronous method. * This method is called by the called thread, in order to wait for a new * request and call the desired method. */ public void run() { // Wait for a method call waitTillMethodRequested(); //System.out.println("executing "+method); Object param = beforeCallingMethod(method); try { // And call it method.execute(); } catch (Exception e) { method.exception = e; } finally { afterCallingMethod(method,param); } synchronized (methodLock) { //System.out.println("done executing "+method); method = null; methodLock.notify(); } } private void waitTillMethodRequested() { synchronized(methodLock) { while (method == null) { //System.out.println("waiting for a new request"); try { methodLock.wait(); } catch( InterruptedException e ) {} } } } /* * */ private BaseMethod method; Object methodLock = new Object (); }