001: package com.xoetrope.service;
002:
003: import java.util.Date;
004: import java.util.Enumeration;
005: import java.util.Hashtable;
006: import java.util.Vector;
007: import net.xoetrope.xui.XProjectManager;
008: import net.xoetrope.optional.service.ServiceProxy;
009: import net.xoetrope.optional.service.ServiceProxyException;
010: import net.xoetrope.optional.service.XRouteManager;
011: import net.xoetrope.optional.service.XServiceProxyNotFoundException;
012: import com.xoetrope.carousel.build.BuildProperties;
013: import net.xoetrope.optional.service.ServiceContext;
014: import net.xoetrope.optional.service.ServiceProxyArgs;
015:
016: /**
017: * Calls the server via several routes, for example when connectivity may
018: * change over the life of an session or application. The routes may be
019: * specified as follows:
020: * <PRE>
021: * <route name="redundantRoute">
022: * <layer class="com.xoetrope.service.MultiRouteService" sleep="300" timeOut="3000">
023: * <path name="calcRoute"/>
024: * <path name="calcRoute64"/>
025: * <path name="calcRouteError"/>
026: * </layer>
027: * </route>
028: * </PRE>
029: * The timeout value is used to specify how long (in milliseconds) this proxy
030: * will wait for a response and the sleep time is the time allowed to elapse
031: * before a second or subsequent route is tried.
032: *
033: * <p> Copyright (c) Xoetrope Ltd., 2001-2006, This software is licensed under
034: * the GNU Public License (GPL), please see license.txt for more details. If
035: * you make commercial use of this software you must purchase a commercial
036: * license from Xoetrope.</p>
037: * <p> $Revision: 1.8 $</p>
038: */
039: public class MultiRouteService extends ServiceProxy {
040: protected Vector alternateRoutes;
041: protected Vector threads;
042: protected int sleepTime = 300;
043: protected int checkInterval = 100;
044: protected int timeOut = 3000;
045:
046: private static int GROW_SIZE = 2;
047: private long endTime;
048:
049: private XRouteManager routeManager;
050:
051: public MultiRouteService() {
052: alternateRoutes = new Vector(3);
053: threads = new Vector();
054:
055: routeManager = XRouteManager.setupRouteManager(XProjectManager
056: .getCurrentProject());
057: }
058:
059: /**
060: * Calls one or more ServiceProxies to provide redundant routing to a server.
061: * Each route is called with its own thread and this proxy attempts to wait
062: * for a suitable response.
063: * @param method the method name to be invoked
064: * @param context the context object of the call
065: * @return the response or result of the call
066: * @throws ServiceProxyException
067: */
068: public Object call(String method, ServiceContext context)
069: throws ServiceProxyException {
070: try {
071: ServiceProxy route;
072: Object result = null;
073:
074: // Calculate the time at which to stop working
075: endTime = new Date().getTime() + timeOut;
076:
077: int numRoutes = alternateRoutes.capacity();
078: for (int i = 0; i < numRoutes; i++) {
079: Object obj = alternateRoutes.elementAt(i);
080: if (obj == null)
081: continue;
082: route = (ServiceProxy) obj;
083:
084: ServiceProxyThread nextThread = new ServiceProxyThread(
085: this , route, method, context);
086: threads.addElement(nextThread);
087:
088: // Start the call
089: if (!(route instanceof FailoverServiceProxy))
090: nextThread.start();
091:
092: long sleepTimeout = new Date().getTime() + sleepTime;
093: do {
094: // If a successful call has been made break.
095: if (result != null)
096: return result;
097: Thread.currentThread().sleep(checkInterval);
098: } while (((result = checkThreads()) == null)
099: && (sleepTimeout > new Date().getTime()));
100: }
101:
102: // Wait till a result has been obtained or the timeout time has been reached
103: do {
104: try {
105: Thread.currentThread().sleep(sleepTime);
106: } catch (InterruptedException ex) {
107: }
108: } while (((result = checkThreads()) == null)
109: && (endTime > new Date().getTime()));
110:
111: // Now check for a failover service implementation
112: startFailoverService();
113:
114: cleanup();
115: return result;
116: } catch (Exception ioex) {
117: status = FAILED;
118: if (BuildProperties.DEBUG)
119: ioex.printStackTrace();
120: }
121:
122: return null;
123: }
124:
125: /**
126: * Receives a signal from the child calling thread that the call has completed.
127: * @param proxy
128: */
129: public synchronized void setChildComplete(ServiceProxy proxy) {
130: int status = proxy.getStatus();
131: try {
132: Thread.currentThread().interrupt();
133: } catch (Exception ex) {
134: }
135: }
136:
137: /**
138: * Checks all the child threads for call completion
139: * @return null if no response has been obtained or returns the response
140: */
141: protected synchronized Object checkThreads() {
142: int numThreads = threads.size();
143: for (int i = 0; i < numThreads; i++) {
144: if (((ServiceProxyThread) threads.elementAt(i)).getStatus() == ServiceProxy.COMPLETE)
145: return ((ServiceProxyThread) threads.elementAt(i))
146: .getResponse();
147: }
148: return null;
149: }
150:
151: /**
152: * Starts the failover service and waits for the timeout period for a response
153: * @return null if no response has been obtained or returns the response
154: */
155: protected synchronized Object startFailoverService() {
156: int numThreads = threads.size();
157: for (int i = 0; i < numThreads; i++) {
158: // Find the failover service
159: if (((ServiceProxyThread) threads.elementAt(i)).proxy instanceof FailoverServiceProxy) {
160: ServiceProxyThread spt = (ServiceProxyThread) threads
161: .elementAt(i);
162:
163: // Initiate the call
164: spt.start();
165: try {
166: // Wait for a completion
167: while ((spt.getStatus() != ServiceProxy.COMPLETE)
168: && (endTime > new Date().getTime())) {
169: Thread.currentThread().sleep(sleepTime);
170: }
171: } catch (InterruptedException ex) {
172: }
173:
174: // Return the response
175: return spt.getResponse();
176: }
177: }
178: return null;
179: }
180:
181: /**
182: * Frees the thread resources.
183: */
184: protected synchronized void cleanup() {
185: int numThreads = threads.size();
186: for (int i = 0; i < numThreads; i++) {
187: ServiceProxyThread spt = (ServiceProxyThread) threads
188: .elementAt(i);
189: if (spt.isAlive())
190: spt.interrupt();
191: }
192: }
193:
194: /**
195: * Sets the attributes of this service proxy. As a redundant routing layer the
196: * hashtable of attributes should contain a hashtable holding the names or the
197: * alternative routes.
198: * @param attributeTable the hash table of attributes.
199: */
200: public void setAttributes(Hashtable attributeTable) {
201: attributeTable.keys();
202: Enumeration enumeration = attributeTable.keys();
203: while (enumeration.hasMoreElements()) {
204: String key = (String) enumeration.nextElement();
205: Object attribute = attributeTable.get(key);
206: if (attribute instanceof Vector) {
207: try {
208: ServiceProxy route = routeManager
209: .getRoute((String) ((Vector) attribute)
210: .elementAt(0));
211: int idx = new Integer(key).intValue();
212: alternateRoutes.setSize(Math.max(alternateRoutes
213: .capacity(), idx + GROW_SIZE));
214: alternateRoutes.setElementAt(route, idx);
215: } catch (XServiceProxyNotFoundException ex) {
216: if (BuildProperties.DEBUG)
217: ex.printStackTrace();
218: }
219: }
220: }
221:
222: Object o = attributeTable.get("sleep");
223: if (o != null)
224: sleepTime = new Integer((String) o).intValue();
225:
226: o = attributeTable.get("timeout");
227: if (o != null)
228: timeOut = new Integer((String) o).intValue();
229: }
230: }
231:
232: /**
233: * A class used to initiate a redundant request
234: * <p>Copyright: Copyright (c) Xoetrope Ltd., 1998-2003</p>
235: * @version 1.0
236: */
237: class ServiceProxyThread extends Thread {
238: MultiRouteService owner;
239: ServiceProxy proxy;
240: String method;
241: ServiceContext spContext;
242: Object response;
243:
244: public ServiceProxyThread(MultiRouteService mrs, ServiceProxy sp,
245: String spMethod, ServiceContext context) {
246: owner = mrs;
247: proxy = sp;
248: method = spMethod;
249: //spArgs = args;
250: spContext = context;
251: response = null;
252: }
253:
254: public void run() {
255: try {
256: response = proxy.call(method, spContext);
257:
258: // Signal the owner that the request has completed.
259: owner.setChildComplete(proxy);
260: } catch (ServiceProxyException ex) {
261: if (BuildProperties.DEBUG)
262: ex.printStackTrace();
263: }
264: }
265:
266: public synchronized int getStatus() {
267: return proxy.getStatus();
268: }
269:
270: public synchronized Object getResponse() {
271: return response;
272: }
273: }
|