001: ///////////////////////////////////////////////////////////////////////////////
002: //
003: // Copyright (C) 2003-@year@ by Thomas M. Hazel, MyOODB (www.myoodb.org)
004: //
005: // All Rights Reserved
006: //
007: // This program is free software; you can redistribute it and/or modify
008: // it under the terms of the GNU General Public License and GNU Library
009: // General Public License as published by the Free Software Foundation;
010: // either version 2, or (at your option) any later version.
011: //
012: // This program is distributed in the hope that it will be useful,
013: // but WITHOUT ANY WARRANTY; without even the implied warranty of
014: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: // GNU General Public License and GNU Library General Public License
016: // for more details.
017: //
018: // You should have received a copy of the GNU General Public License
019: // and GNU Library General Public License along with this program; if
020: // not, write to the Free Software Foundation, 675 Mass Ave, Cambridge,
021: // MA 02139, USA.
022: //
023: ///////////////////////////////////////////////////////////////////////////////
024: package org.myoodb.core;
025:
026: import java.io.*;
027: import java.lang.reflect.*;
028:
029: import javax.servlet.*;
030: import javax.servlet.http.*;
031:
032: import org.myoodb.core.command.*;
033:
034: public class MyOodbTunnelAjax extends BaseServlet {
035: private static final org.myoodb.util.Logger LOGGER = org.myoodb.util.Logger
036: .getLogger(MyOodbTunnelAjax.class);
037:
038: public final static String PARAMETER_DELIMITER = "@tunnelParamDelimiter@";
039:
040: private XMLStream m_xmlStream;
041: private JSONStream m_jsonStream;
042:
043: public MyOodbTunnelAjax() {
044: m_xmlStream = new XMLStream();
045: m_jsonStream = new JSONStream();
046: }
047:
048: private String toXML(Object object) {
049: synchronized (m_xmlStream) {
050: return m_xmlStream.toXML(object);
051: }
052: }
053:
054: private Object fromXML(String stream) {
055: synchronized (m_xmlStream) {
056: return m_xmlStream.fromXML(stream);
057: }
058: }
059:
060: private String toJSON(Object object) {
061: synchronized (m_jsonStream) {
062: return m_jsonStream.toXML(object);
063: }
064: }
065:
066: private Object fromJSON(String stream) {
067: synchronized (m_jsonStream) {
068: return m_jsonStream.fromXML(stream);
069: }
070: }
071:
072: private Class getCommandClass(int commandType) {
073: Class classType = null;
074:
075: if (commandType == 1) {
076: classType = LoginCommand.class;
077: } else if (commandType == 2) {
078: classType = LogoutCommand.class;
079: } else if (commandType == 3) {
080: classType = CreateCommand.class;
081: } else if (commandType == 4) {
082: classType = DeleteCommand.class;
083: } else if (commandType == 5) {
084: classType = GetRootCommand.class;
085: } else if (commandType == 6) {
086: classType = GetObjectCommand.class;
087: } else if (commandType == 7) {
088: classType = InvokeMethodCommand.class;
089: } else if (commandType == 8) {
090: classType = SetXMLCommand.class;
091: } else if (commandType == 9) {
092: classType = GetXMLCommand.class;
093: } else if (commandType == 10) {
094: classType = SetXMLCommand.class;
095: } else if (commandType == 11) {
096: classType = GetXMLCommand.class;
097: } else if (commandType == 12) {
098: classType = SetBeanCommand.class;
099: } else if (commandType == 13) {
100: classType = GetBeanCommand.class;
101: } else if (commandType == 14) {
102: classType = SetBeanCommand.class;
103: } else if (commandType == 15) {
104: classType = GetBeanCommand.class;
105: } else if (commandType == 16) {
106: classType = TransactionCommand.class;
107: }
108:
109: return classType;
110: }
111:
112: private String[] getTokens(int commandType, String parameterString) {
113: String[] tokens = null;
114:
115: if ((commandType == 8) || (commandType == 10)
116: || (commandType == 12) || (commandType == 14)) {
117: int index1 = parameterString.indexOf(PARAMETER_DELIMITER);
118: String param1 = parameterString.substring(0, index1 - 1);
119: int index2 = PARAMETER_DELIMITER.length();
120: String param2 = parameterString.substring(index1 + index2
121: + 1, parameterString.length());
122:
123: if ((commandType == 10) || (commandType == 14)) {
124: tokens = new String[] { param1, toXML(fromJSON(param2)) };
125: } else {
126: tokens = new String[] { param1, param2 };
127: }
128: } else {
129: tokens = parameterString.split(",");
130: }
131:
132: for (int i = 0; i < tokens.length; i++) {
133: if ("undefined".equals(tokens[i]) == true) {
134: tokens[i] = null;
135: }
136: }
137:
138: return tokens;
139: }
140:
141: private String getCommandSignature(Class classType) {
142: String signature = "";
143:
144: if (LoginCommand.class.equals(classType) == true) {
145: signature = "java.lang.String,java.lang.String,boolean";
146: } else if (LogoutCommand.class.equals(classType) == true) {
147: signature = "";
148: } else if (CreateCommand.class.equals(classType) == true) {
149: signature = "java.lang.String,java.lang.String,java.lang.String,[Ljava.lang.Object;";
150: } else if (DeleteCommand.class.equals(classType) == true) {
151: signature = "org.myoodb.core.Identifier";
152: } else if (GetRootCommand.class.equals(classType) == true) {
153: signature = "java.lang.String";
154: } else if (GetObjectCommand.class.equals(classType) == true) {
155: signature = "org.myoodb.core.Identifier";
156: } else if (InvokeMethodCommand.class.equals(classType) == true) {
157: signature = "org.myoodb.core.Identifier,int,[Ljava.lang.Object;,int";
158: } else if (SetXMLCommand.class.equals(classType) == true) {
159: signature = "org.myoodb.core.Identifier,java.lang.String";
160: } else if (GetXMLCommand.class.equals(classType) == true) {
161: signature = "org.myoodb.core.Identifier";
162: } else if (SetBeanCommand.class.equals(classType) == true) {
163: signature = "org.myoodb.core.Identifier,org.myoodb.MyOodbBean";
164: } else if (GetBeanCommand.class.equals(classType) == true) {
165: signature = "org.myoodb.core.Identifier";
166: } else if (TransactionCommand.class.equals(classType) == true) {
167: signature = "int";
168: }
169:
170: return signature;
171: }
172:
173: private Object[] createCommandArguments(Class classType,
174: String[] tokens) {
175: Object[] args = null;
176:
177: if (LoginCommand.class.equals(classType) == true) {
178: args = new Object[] { tokens[0], tokens[1], false };
179: } else if (LogoutCommand.class.equals(classType) == true) {
180: args = new Object[] {};
181: } else if (CreateCommand.class.equals(classType) == true) {
182: args = new Object[] { tokens[0], tokens[1], tokens[2],
183: tokens[3] };
184: } else if (DeleteCommand.class.equals(classType) == true) {
185: args = new Object[] { fromXML(tokens[0]) };
186: } else if (GetRootCommand.class.equals(classType) == true) {
187: args = new Object[] { tokens[0] };
188: } else if (GetObjectCommand.class.equals(classType) == true) {
189: args = new Object[] { fromXML(tokens[0]) };
190: } else if (InvokeMethodCommand.class.equals(classType) == true) {
191: Object identifier = fromXML(tokens[0]);
192:
193: Object[] objects = null;
194: if ((tokens[2] != null) && (tokens[2].length() != 0)) {
195: String[] params = tokens[2].split(PARAMETER_DELIMITER);
196: objects = new Object[params.length];
197: for (int i = 0; i < params.length; i++) {
198: Object object = fromXML(params[i]);
199: if (object instanceof org.myoodb.core.Identifier) {
200: object = m_db
201: .getObject((org.myoodb.core.Identifier) object);
202: }
203:
204: objects[i] = object;
205: }
206: } else if (tokens[2] == null) {
207: objects = new Object[] { null };
208: }
209:
210: int index = Integer.parseInt(tokens[1]);
211: int access = Integer.parseInt(tokens[3]);
212:
213: args = new Object[] { identifier, index, objects, access };
214: } else if (SetXMLCommand.class.equals(classType) == true) {
215: args = new Object[] { fromXML(tokens[0]), tokens[1] };
216: } else if (GetXMLCommand.class.equals(classType) == true) {
217: args = new Object[] { fromXML(tokens[0]) };
218: } else if (SetBeanCommand.class.equals(classType) == true) {
219: args = new Object[] { fromXML(tokens[0]),
220: fromXML(tokens[1]) };
221: } else if (GetBeanCommand.class.equals(classType) == true) {
222: args = new Object[] { fromXML(tokens[0]) };
223: } else if (TransactionCommand.class.equals(classType) == true) {
224: args = new Object[] { Integer.parseInt(tokens[0]) };
225: }
226:
227: return args;
228: }
229:
230: private String createResponse(int code, String result) {
231: return "c=" + code + "&r=" + result;
232: }
233:
234: private Object prepareResponse(Object result)
235: throws java.io.IOException {
236: //
237: // XXX: since this is outside the context of the database we need to check for concurrency
238: //
239: java.util.ConcurrentModificationException globalError = null;
240:
241: for (int i = 0; i < org.myoodb.core.storage.ClusterStore.CONCURRENCY_RETRY; i++) {
242: try {
243: result = toXML(result);
244: globalError = null;
245: break;
246: } catch (java.util.ConcurrentModificationException e) {
247: synchronized (m_xmlStream) {
248: m_xmlStream = new XMLStream();
249: }
250:
251: try {
252: Thread.sleep(100);
253: } catch (java.lang.InterruptedException ee) {
254: // nothing to do
255: }
256:
257: globalError = e;
258: }
259: }
260:
261: if (globalError != null) {
262: result = new org.myoodb.exception.InternalException(
263: "Concurrency error:", globalError);
264: result = toXML(result);
265: }
266:
267: return result;
268: }
269:
270: private void stringifyResult(Object result, StringBuilder string)
271: throws IOException {
272: String type = result.getClass().getName();
273: if (org.myoodb.tools.generator.Helper.isBase(type) == true) {
274: string.append(result + "");
275: } else if (type.equals("java.lang.String") == true) {
276: string.append("\"" + result + "\"");
277: } else if (result instanceof org.myoodb.MyOodbProxy) {
278: String identifier = toXML(((org.myoodb.MyOodbProxy) result)
279: .getDatabaseHandle());
280: identifier = identifier.replaceAll("\n", "");
281: identifier = identifier.replaceAll(" ", "");
282:
283: String constructor = ((org.myoodb.MyOodbProxy) result)
284: .getClass().getName();
285: constructor = constructor.substring(0, constructor.length()
286: - AbstractObjectContainer.PROXY_NAME_SUFFIX
287: .length());
288: constructor = constructor.replaceAll("\\.", "_");
289:
290: string.append("new " + constructor + "(\"" + identifier
291: + "\",db)");
292: } else if (result instanceof java.util.ArrayList) {
293: string.append("new Array(");
294:
295: for (int i = 0; i < ((java.util.ArrayList) result).size(); i++) {
296: string.append(i > 0 ? "," : "");
297: stringifyResult(((java.util.ArrayList) result).get(i),
298: string);
299: }
300:
301: string.append(")");
302: } else {
303: string.append("\"" + prepareResponse(result) + "\"");
304: }
305: }
306:
307: public void doPost(HttpServletRequest request,
308: HttpServletResponse response) throws IOException,
309: ServletException {
310: int gRequestVal = -1;
311:
312: try {
313: gRequestVal = Integer.parseInt(request.getParameter("t"));
314: long sessionIdentifier = Long.parseLong(request
315: .getParameter("s"));
316:
317: Object[] connectionTuple = null;
318: synchronized (TunnelManager.CONNECTIONS) {
319: connectionTuple = (Object[]) TunnelManager.CONNECTIONS
320: .get(sessionIdentifier);
321: }
322:
323: if (gRequestVal >= TunnelManager.Protocol.USER_DATA) {
324: int commandType = Integer.parseInt(request
325: .getParameter("c"));
326: Class classType = getCommandClass(commandType);
327: if (classType == null) {
328: throw new org.myoodb.exception.PermissionException(
329: "Permission denied");
330: }
331:
332: String parameterString = request.getParameter("p");
333: String[] tokens = getTokens(commandType,
334: parameterString);
335:
336: String signature = getCommandSignature(classType);
337: Object[] arguments = createCommandArguments(classType,
338: tokens);
339:
340: Constructor constructor = MethodHelper.getConstructor(
341: classType, signature);
342: AbstractCommand command = (AbstractCommand) constructor
343: .newInstance(arguments);
344:
345: org.myoodb.core.DatabaseConnection connection = null;
346: if (connectionTuple == null) {
347: if (sessionIdentifier != -1) {
348: throw new org.myoodb.exception.TimeoutException(
349: "Connection timed out");
350: }
351:
352: if ((command instanceof LoginCommand) == false) {
353: throw new org.myoodb.exception.PermissionException(
354: "Permission denied");
355: }
356:
357: connection = new org.myoodb.core.DatabaseConnection(
358: m_db, null, m_db.getHost(), m_db.getPort(),
359: -1, false, null);
360:
361: sessionIdentifier = MyOodbManager.getTheManager()
362: .getIdentifierManager()
363: .getNextConnectionId();
364:
365: connectionTuple = new Object[3];
366: connectionTuple[0] = connection;
367: connectionTuple[1] = request.getRemoteAddr();
368: connectionTuple[2] = System.currentTimeMillis();
369:
370: synchronized (TunnelManager.CONNECTIONS) {
371: TunnelManager.CONNECTIONS.put(
372: sessionIdentifier, connectionTuple);
373: }
374: } else {
375: connection = (org.myoodb.core.DatabaseConnection) connectionTuple[0];
376: connectionTuple[2] = System.currentTimeMillis();
377: }
378:
379: Object result = null;
380: synchronized (connection) {
381: boolean tunnel = (gRequestVal == TunnelManager.Protocol.USER_DATA_TUNNEL);
382: if ((tunnel == false)
383: && (command instanceof InvokeMethodCommand)) {
384: tunnel = (((InvokeMethodCommand) command)
385: .getAccessLevel() == org.myoodb.MyOodbAccess.TUNNEL);
386: }
387:
388: if (tunnel == true) {
389: ((AbstractCommand) command)
390: .setTunnelIdentifier(sessionIdentifier);
391:
392: synchronized (TunnelManager.INVOKE_COMMANDS) {
393: TunnelManager.INVOKE_COMMANDS.put(
394: sessionIdentifier, command);
395: }
396: }
397:
398: try {
399: connection.send(command);
400: result = connection.receive(-1);
401: } finally {
402: if (tunnel == true) {
403: synchronized (TunnelManager.INVOKE_COMMANDS) {
404: result = TunnelManager.INVOKE_COMMANDS
405: .remove(sessionIdentifier);
406: }
407: }
408: }
409: }
410:
411: int responseType = (result instanceof java.lang.Throwable) ? 0
412: : 1;
413: if (responseType != 0) {
414: if (command instanceof LoginCommand) {
415: result = "\"" + sessionIdentifier + "\"";
416: } else if (result != null) {
417: if (command instanceof InvokeMethodCommand) {
418: StringBuilder string = new StringBuilder();
419: stringifyResult(result, string);
420: result = string.toString();
421: } else if (command instanceof GetXMLCommand) {
422: if (commandType == 9) {
423: responseType = 2;
424:
425: // XXX: remove extra whitespace from stream
426: Object object = fromXML((String) result);
427: result = toXML(object);
428: } else if (commandType == 11) {
429: Object object = fromXML((String) result);
430: result = "(" + toJSON(object) + ")";
431: }
432: } else if (command instanceof GetBeanCommand) {
433: if (commandType == 13) {
434: responseType = 2;
435:
436: result = toXML(result);
437: } else if (commandType == 15) {
438: result = "(" + toJSON(result) + ")";
439: }
440: } else {
441: StringBuilder string = new StringBuilder();
442: stringifyResult(result, string);
443: result = string.toString();
444: }
445: } else {
446: result = "null";
447: }
448: } else {
449: result = prepareResponse(result);
450: }
451:
452: result = createResponse(responseType, (String) result);
453:
454: response.setContentType("application/octet-stream");
455: response.setContentLength(((String) result).length());
456: response.getWriter().write((String) result);
457: //response.getOutputStream().flush();
458: } else if (connectionTuple != null) {
459: connectionTuple[2] = System.currentTimeMillis();
460: }
461: } catch (Exception e) {
462: if (LOGGER.isDebugEnabled() == true) {
463: LOGGER.debug(null, e);
464: }
465:
466: if (gRequestVal >= TunnelManager.Protocol.USER_DATA) {
467: synchronized (m_xmlStream) {
468: m_xmlStream = new XMLStream();
469: }
470:
471: response.setContentType("application/octet-stream");
472: if ((e instanceof org.myoodb.exception.RemoteException) == false) {
473: e = new org.myoodb.exception.InternalException(e);
474: }
475:
476: response.getWriter().write(
477: (String) createResponse(0, toXML(e)));
478: //response.getOutputStream().flush();
479: }
480: }
481: }
482:
483: class CompactWriter extends
484: com.thoughtworks.xstream.io.xml.PrettyPrintWriter {
485: public CompactWriter(Writer writer) {
486: super (writer);
487: }
488:
489: protected void endOfLine() {
490: // XXX: remove extra whitespace from stream
491: }
492: }
493:
494: class XppDriver extends com.thoughtworks.xstream.io.xml.XppDriver {
495: public com.thoughtworks.xstream.io.HierarchicalStreamWriter createWriter(
496: Writer out) {
497: return new CompactWriter(out);
498: }
499: }
500:
501: class XMLStream extends com.thoughtworks.xstream.XStream {
502: public XMLStream() {
503: super (new XppDriver());
504: }
505:
506: public ObjectOutputStream createObjectOutputStream(Writer writer)
507: throws IOException {
508: return createObjectOutputStream(new CompactWriter(writer),
509: "object-stream");
510: }
511:
512: public ObjectOutputStream createObjectOutputStream(
513: Writer writer, String rootNodeName) throws IOException {
514: return createObjectOutputStream(new CompactWriter(writer),
515: rootNodeName);
516: }
517: }
518:
519: class JSONStream extends com.thoughtworks.xstream.XStream {
520: public JSONStream() {
521: super (
522: new com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver());
523: }
524: }
525: }
|