001: package com.xoetrope.service;
002:
003: import java.io.File;
004: import java.io.FileReader;
005: import java.io.FileWriter;
006: import java.util.Date;
007: import java.util.Vector;
008:
009: import net.n3.nanoxml.IXMLElement;
010: import net.n3.nanoxml.XMLElement;
011: import net.n3.nanoxml.XMLWriter;
012: import net.xoetrope.optional.service.ServiceProxy;
013: import net.xoetrope.optional.service.ServiceProxyException;
014: import net.xoetrope.optional.service.XServiceModelNode;
015: import com.xoetrope.carousel.build.BuildProperties;
016: import java.util.Enumeration;
017: import java.util.Hashtable;
018: import net.xoetrope.optional.service.ServiceContext;
019: import net.xoetrope.optional.service.ServiceProxyArgs;
020: import net.xoetrope.xml.XmlElement;
021: import net.xoetrope.xml.XmlSource;
022: import net.xoetrope.xml.nanoxml.NanoXmlElement;
023: import net.xoetrope.xui.XProject;
024: import net.xoetrope.xui.XProjectManager;
025: import net.xoetrope.xui.data.XModel;
026:
027: /**
028: * Uses the following parameters in the startup.properties file:
029: * CacheSleepTime the time to sleep between retries of failed calls.
030: * CachePath the path to which failed calls are saved.
031: *
032: * <p> Copyright (c) Xoetrope Ltd., 2001-2006, This software is licensed under
033: * the GNU Public License (GPL), please see license.txt for more details. If
034: * you make commercial use of this software you must purchase a commercial
035: * license from Xoetrope.</p>
036: * <p> $Revision: 1.9 $</p>
037: */
038: public class XPersistantService extends ServiceProxy {
039: protected static Vector storedCalls = null;
040: protected static PersitantProxyThread persitantLoaderThread = null;
041: protected XServiceCallback callbackObj = null;
042:
043: /**
044: * The owner project and the context in which this object operates.
045: */
046: protected XProject currentProject = XProjectManager
047: .getCurrentProject();
048:
049: /**
050: * Public constructor
051: */
052: public XPersistantService() {
053: loadStoredCalls();
054: }
055:
056: public Object call() throws ServiceProxyException {
057: try {
058: status = STARTED;
059: Object res = nextProxy.call();
060: status = COMPLETE;
061: return res;
062: } catch (ServiceProxyException e) {
063: // storeCall();
064: status = FAILED;
065: throw (e);
066: }
067: }
068:
069: /**
070: * Call this proxy with the specified arguments
071: * @return the result of the call
072: * @param context The ServiceContext contain pass and return parameters
073: * @param method the name of the service being called
074: * @throws net.xoetrope.optional.service.ServiceProxyException Throw an exception if there is a problem with the call
075: */
076: public Object call(String method, ServiceContext context)
077: throws ServiceProxyException {
078: return call(method, context, true);
079: }
080:
081: public Object call(String method, ServiceContext context,
082: boolean store) throws ServiceProxyException {
083: try {
084: status = STARTED;
085: Object res = nextProxy.call(method, context);
086: status = COMPLETE;
087: return res;
088: } catch (ServiceProxyException ex) {
089: if (store) {
090: CallbackToken token = new CallbackToken();
091: storeCall(token.getToken(), method, context);
092: status = PENDING;
093: return token;
094: }
095: }
096: status = FAILED;
097: return null;
098: }
099:
100: public void storeCall(int token, String method,
101: ServiceContext context) {
102: ServiceProxyArgs args = context.getArgs();
103: try {
104: String callTime = new Long(new Date().getTime()).toString();
105: String cachePath = currentProject
106: .getStartupParam("CachePath");
107: File file = new File(cachePath, callTime + ".xml");
108: if (BuildProperties.DEBUG)
109: System.out.println("creating file: " + callTime
110: + ".xml");
111: file.createNewFile();
112: FileWriter fw = new FileWriter(file);
113:
114: XMLElement xml = new XMLElement("Calls");
115: XmlElement nanoXml = new NanoXmlElement(xml);
116:
117: xml.setAttribute("method", method);
118: xml.setAttribute("token", new Integer(token).toString());
119: xml.setAttribute("name", method);
120: xml.setAttribute("time", callTime);
121: xml.setAttribute("route", routeName);
122: xml.setAttribute("service", serviceName);
123:
124: // Use this flag to control persistance accross sessions.
125: xml.setAttribute("status", "persist");
126: Hashtable passArgs = context.getPassArgs();
127: Enumeration keys = passArgs.keys();
128: while (keys.hasMoreElements()) {
129: String name = (String) keys.nextElement();
130: String value = (String) passArgs.get(name);
131: IXMLElement attribXml = xml.createElement("Arg");
132: attribXml.setAttribute("name", name);
133: attribXml.setAttribute("value", value);
134: xml.addChild(attribXml);
135: }
136: /*for ( int i = 0; i < args.getNumArgs(); i++ ) {
137: if ( args.getParam( i ) != null ) {
138: IXMLElement attribXml = xml.createElement( "Arg" );
139: attribXml.setAttribute( "name", args.getArgName( i ) );
140: attribXml.setAttribute( "value", args.getParam( i ).toString() );
141: xml.addChild( attribXml );
142: }
143: }*/
144:
145: XMLWriter xmlWriter = new XMLWriter(fw);
146: xmlWriter.write(xml, true, 4);
147: fw.flush();
148: fw.close();
149:
150: storedCalls.addElement(nanoXml);
151:
152: file = new File(cachePath, "tokenCounter.dat");
153: fw = new FileWriter(file);
154: fw.write(token);
155: fw.flush();
156: fw.close();
157: fw = null;
158: } catch (Exception ex) {
159: ex.printStackTrace();
160: }
161: }
162:
163: /**
164: * Load any stored instances of calls from the cache.
165: */
166: protected void loadStoredCalls() {
167: if (storedCalls != null)
168: return;
169:
170: persitantLoaderThread = new PersitantProxyThread(this );
171: try {
172: storedCalls = new Vector();
173: File[] files = new File(currentProject
174: .getStartupParam("CachePath"), "").listFiles();
175: if (files != null) {
176:
177: int numFiles = files.length;
178: for (int i = 0; i < numFiles; i++) {
179: if (files[i].getName().indexOf(".xml") > 0) {
180: FileReader reader = new FileReader(files[i]);
181: XmlElement callXml = XmlSource.read(reader);
182: if (callXml.getAttribute("status").compareTo(
183: "persist") == 0)
184: storedCalls.addElement(callXml);
185: }
186: }
187: }
188: } catch (Exception ex) {
189: if (BuildProperties.DEBUG)
190: ex.printStackTrace();
191: }
192:
193: try {
194: File tokenFile = new File(currentProject
195: .getStartupParam("CachePath"), "tokenCounter.dat");
196: FileReader reader = new FileReader(tokenFile);
197: int nextToken = reader.read();
198: CallbackToken
199: .setNextToken(storedCalls.size() > 0 ? nextToken
200: : 0);
201: } catch (Exception ex1) {
202: CallbackToken.setNextToken(0);
203: }
204:
205: persitantLoaderThread.start();
206: }
207:
208: /**
209: * Get the number of stored calls.
210: * @return the number of calls awaiting completion
211: */
212: public int getNumStoredCalls() {
213: return storedCalls.size();
214: }
215:
216: public void setCallbackObject(XServiceCallback cb) {
217: callbackObj = cb;
218: }
219:
220: /**
221: * Rebuild the call
222: * @param xml
223: */
224: protected int callProxy(XmlElement xml) {
225: try {
226: int callStatus = STARTED;
227: String service = xml.getAttribute("service");
228: Object obj = currentProject.getModel().get(service);
229: XServiceModelNode node = (XServiceModelNode) obj;
230:
231: Vector attribs = xml.getChildren();
232: int numArgs = attribs.size();
233: ServiceContext context = new ServiceContext();
234: ServiceProxyArgs args = context.getArgs();
235: for (int i = 0; i < numArgs; i++) {
236: XmlElement childNode = (XmlElement) attribs
237: .elementAt(i);
238: args.setPassParam(childNode.getAttribute("name"),
239: childNode.getAttribute("value"));
240: node.setAttribValue(i, childNode.getAttribute("value"));
241: }
242:
243: // The call was stored at the persistence level so find it in the service stack.
244: ServiceProxy aNextProxy = node.getServiceProxy(getClass()
245: .getName());
246: Object res = ((XPersistantService) aNextProxy).call(xml
247: .getAttribute("method"), context, false);
248: callStatus = COMPLETE;
249: int token = new Integer(xml.getAttribute("token"))
250: .intValue();
251: callbackObj.callCompleted(token, res, xml);
252: return callStatus;
253: } catch (ServiceProxyException ex) {
254: return PENDING;
255: }
256: }
257: }
258:
259: /**
260: * A class used to initiate a redundant request
261: * <p>Copyright: Copyright (c) Xoetrope Ltd., 1998-2003</p>
262: * @version 1.0
263: */
264: class PersitantProxyThread extends Thread {
265: XPersistantService owner;
266: int status = ServiceProxy.FAILED;
267: long sleepTime;
268:
269: public PersitantProxyThread(XPersistantService mrs) {
270: owner = mrs;
271: try {
272: sleepTime = new Long(owner.currentProject
273: .getStartupParam("CacheSleepTime")).longValue();
274: } catch (Exception ex) {
275: sleepTime = 60000;
276: }
277: }
278:
279: public void run() {
280: // Sleep first to allow normal setup to commence.
281: try {
282: sleep(100);
283: } catch (InterruptedException ex1) {
284: }
285:
286: do {
287: try {
288: int numCalls = owner.storedCalls.size();
289: for (int i = 0; i < numCalls; i++) {
290: XmlElement element = (XmlElement) owner.storedCalls
291: .elementAt(i);
292: int callStatus = owner.callProxy(element);
293: if ((callStatus == ServiceProxy.OK)
294: || (callStatus == ServiceProxy.COMPLETE)) {
295: // Remove the element from the local file.
296: owner.storedCalls.removeElementAt(i);
297: numCalls--;
298: i--;
299:
300: // Remove the cache elements
301: removeDeadFile(element);
302: }
303: }
304: sleep(sleepTime);
305: } catch (Exception ex) {
306: if (BuildProperties.DEBUG)
307: ex.printStackTrace();
308: }
309: } while (true);
310: }
311:
312: /**
313: * Remove an unneeded cache file
314: * @param element
315: */
316: private void removeDeadFile(XmlElement element) {
317: try {
318: if (BuildProperties.DEBUG)
319: System.out.println("deleting:"
320: + element.getAttribute("time") + ".xml");
321: File file = new File(owner.currentProject
322: .getStartupParam("CachePath"), element
323: .getAttribute("time")
324: + ".xml");
325: try {
326: if (!file.delete())
327: file.deleteOnExit();
328: } catch (Exception ex1) {
329: if (BuildProperties.DEBUG)
330: ex1.printStackTrace();
331: file.deleteOnExit();
332: }
333: } catch (Exception ex) {
334: if (BuildProperties.DEBUG)
335: ex.printStackTrace();
336: }
337: }
338: }
|