001: /*
002: * Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: /*
027: * The contents of this file are subject to the Sun Public License
028: * Version 1.0 (the "License"); you may not use this file except in
029: * compliance with the License. A copy of the License is available at
030: * http://www.sun.com/, and in the file LICENSE.html in the
031: * doc directory.
032: *
033: * The Original Code is HAT. The Initial Developer of the
034: * Original Code is Bill Foote, with contributions from others
035: * at JavaSoft/Sun. Portions created by Bill Foote and others
036: * at Javasoft/Sun are Copyright (C) 1997-2004. All Rights Reserved.
037: *
038: * In addition to the formal license, I ask that you don't
039: * change the history or donations files without permission.
040: *
041: */
042:
043: package com.sun.tools.hat.internal.oql;
044:
045: import com.sun.tools.hat.internal.model.*;
046: import java.io.*;
047: import java.lang.reflect.*;
048: import java.util.*;
049:
050: /**
051: * This is Object Query Language Interpreter
052: *
053: * @author A. Sundararajan [jhat @(#)OQLEngine.java 1.15 07/05/09]
054: */
055: public class OQLEngine {
056: static {
057: try {
058: // Do we have javax.script support?
059: // create ScriptEngineManager
060: Class<?> managerClass = Class
061: .forName("javax.script.ScriptEngineManager");
062: Object manager = managerClass.newInstance();
063:
064: // create JavaScript engine
065: Method getEngineMethod = managerClass.getMethod(
066: "getEngineByName", new Class[] { String.class });
067: Object jse = getEngineMethod.invoke(manager,
068: new Object[] { "js" });
069: oqlSupported = (jse != null);
070: } catch (Exception exp) {
071: oqlSupported = false;
072: }
073: }
074:
075: // check OQL is supported or not before creating OQLEngine
076: public static boolean isOQLSupported() {
077: return oqlSupported;
078: }
079:
080: public OQLEngine(Snapshot snapshot) {
081: if (!isOQLSupported()) {
082: throw new UnsupportedOperationException("OQL not supported");
083: }
084: init(snapshot);
085: }
086:
087: /**
088: Query is of the form
089:
090: select <java script code to select>
091: [ from [instanceof] <class name> [<identifier>]
092: [ where <java script boolean expression> ]
093: ]
094: */
095: public synchronized void executeQuery(String query,
096: ObjectVisitor visitor) throws OQLException {
097: debugPrint("query : " + query);
098: StringTokenizer st = new StringTokenizer(query);
099: if (st.hasMoreTokens()) {
100: String first = st.nextToken();
101: if (!first.equals("select")) {
102: // Query does not start with 'select' keyword.
103: // Just treat it as plain JavaScript and eval it.
104: try {
105: Object res = evalScript(query);
106: visitor.visit(res);
107: } catch (Exception e) {
108: throw new OQLException(e);
109: }
110: return;
111: }
112: } else {
113: throw new OQLException(
114: "query syntax error: no 'select' clause");
115: }
116:
117: String selectExpr = "";
118: boolean seenFrom = false;
119: while (st.hasMoreTokens()) {
120: String tok = st.nextToken();
121: if (tok.equals("from")) {
122: seenFrom = true;
123: break;
124: }
125: selectExpr += " " + tok;
126: }
127:
128: if (selectExpr.equals("")) {
129: throw new OQLException(
130: "query syntax error: 'select' expression can not be empty");
131: }
132:
133: String className = null;
134: boolean isInstanceOf = false;
135: String whereExpr = null;
136: String identifier = null;
137:
138: if (seenFrom) {
139: if (st.hasMoreTokens()) {
140: String tmp = st.nextToken();
141: if (tmp.equals("instanceof")) {
142: isInstanceOf = true;
143: if (!st.hasMoreTokens()) {
144: throw new OQLException(
145: "no class name after 'instanceof'");
146: }
147: className = st.nextToken();
148: } else {
149: className = tmp;
150: }
151: } else {
152: throw new OQLException(
153: "query syntax error: class name must follow 'from'");
154: }
155:
156: if (st.hasMoreTokens()) {
157: identifier = st.nextToken();
158: if (identifier.equals("where")) {
159: throw new OQLException(
160: "query syntax error: identifier should follow class name");
161: }
162: if (st.hasMoreTokens()) {
163: String tmp = st.nextToken();
164: if (!tmp.equals("where")) {
165: throw new OQLException(
166: "query syntax error: 'where' clause expected after 'from' clause");
167: }
168:
169: whereExpr = "";
170: while (st.hasMoreTokens()) {
171: whereExpr += " " + st.nextToken();
172: }
173: if (whereExpr.equals("")) {
174: throw new OQLException(
175: "query syntax error: 'where' clause cannot have empty expression");
176: }
177: }
178: } else {
179: throw new OQLException(
180: "query syntax error: identifier should follow class name");
181: }
182: }
183:
184: executeQuery(new OQLQuery(selectExpr, isInstanceOf, className,
185: identifier, whereExpr), visitor);
186: }
187:
188: private void executeQuery(OQLQuery q, ObjectVisitor visitor)
189: throws OQLException {
190: JavaClass clazz = null;
191: if (q.className != null) {
192: clazz = snapshot.findClass(q.className);
193: if (clazz == null) {
194: throw new OQLException(q.className + " is not found!");
195: }
196: }
197:
198: StringBuffer buf = new StringBuffer();
199: buf.append("function __select__(");
200: if (q.identifier != null) {
201: buf.append(q.identifier);
202: }
203: buf.append(") { return ");
204: buf.append(q.selectExpr.replace('\n', ' '));
205: buf.append("; }");
206:
207: String selectCode = buf.toString();
208: debugPrint(selectCode);
209: String whereCode = null;
210: if (q.whereExpr != null) {
211: buf = new StringBuffer();
212: buf.append("function __where__(");
213: buf.append(q.identifier);
214: buf.append(") { return ");
215: buf.append(q.whereExpr.replace('\n', ' '));
216: buf.append("; }");
217: whereCode = buf.toString();
218: }
219: debugPrint(whereCode);
220:
221: // compile select expression and where condition
222: try {
223: evalMethod.invoke(engine, new Object[] { selectCode });
224: if (whereCode != null) {
225: evalMethod.invoke(engine, new Object[] { whereCode });
226: }
227:
228: if (q.className != null) {
229: Enumeration objects = clazz
230: .getInstances(q.isInstanceOf);
231: while (objects.hasMoreElements()) {
232: JavaHeapObject obj = (JavaHeapObject) objects
233: .nextElement();
234: Object[] args = new Object[] { wrapJavaObject(obj) };
235: boolean b = (whereCode == null);
236: if (!b) {
237: Object res = call("__where__", args);
238: if (res instanceof Boolean) {
239: b = ((Boolean) res).booleanValue();
240: } else if (res instanceof Number) {
241: b = ((Number) res).intValue() != 0;
242: } else {
243: b = (res != null);
244: }
245: }
246:
247: if (b) {
248: Object select = call("__select__", args);
249: if (visitor.visit(select))
250: return;
251: }
252: }
253: } else {
254: // simple "select <expr>" query
255: Object select = call("__select__", new Object[] {});
256: visitor.visit(select);
257: }
258: } catch (Exception e) {
259: throw new OQLException(e);
260: }
261: }
262:
263: public Object evalScript(String script) throws Exception {
264: return evalMethod.invoke(engine, new Object[] { script });
265: }
266:
267: public Object wrapJavaObject(JavaHeapObject obj) throws Exception {
268: return call("wrapJavaObject", new Object[] { obj });
269: }
270:
271: public Object toHtml(Object obj) throws Exception {
272: return call("toHtml", new Object[] { obj });
273: }
274:
275: public Object call(String func, Object[] args) throws Exception {
276: return invokeMethod.invoke(engine, new Object[] { func, args });
277: }
278:
279: private static void debugPrint(String msg) {
280: if (debug)
281: System.out.println(msg);
282: }
283:
284: private void init(Snapshot snapshot) throws RuntimeException {
285: this .snapshot = snapshot;
286: try {
287: // create ScriptEngineManager
288: Class<?> managerClass = Class
289: .forName("javax.script.ScriptEngineManager");
290: Object manager = managerClass.newInstance();
291:
292: // create JavaScript engine
293: Method getEngineMethod = managerClass.getMethod(
294: "getEngineByName", new Class[] { String.class });
295: engine = getEngineMethod.invoke(manager,
296: new Object[] { "js" });
297:
298: // initialize engine with init file (hat.js)
299: InputStream strm = getInitStream();
300: Class<?> engineClass = Class
301: .forName("javax.script.ScriptEngine");
302: evalMethod = engineClass.getMethod("eval",
303: new Class[] { Reader.class });
304: evalMethod.invoke(engine,
305: new Object[] { new InputStreamReader(strm) });
306:
307: // initialize ScriptEngine.eval(String) and
308: // Invocable.invokeFunction(String, Object[]) methods.
309: Class<?> invocableClass = Class
310: .forName("javax.script.Invocable");
311:
312: evalMethod = engineClass.getMethod("eval",
313: new Class[] { String.class });
314: invokeMethod = invocableClass.getMethod("invokeFunction",
315: new Class[] { String.class, Object[].class });
316:
317: // initialize ScriptEngine.put(String, Object) method
318: Method putMethod = engineClass.getMethod("put",
319: new Class[] { String.class, Object.class });
320:
321: // call ScriptEngine.put to initialize built-in heap object
322: putMethod.invoke(engine,
323: new Object[] {
324: "heap",
325: call("wrapHeapSnapshot",
326: new Object[] { snapshot }) });
327: } catch (Exception e) {
328: if (debug)
329: e.printStackTrace();
330: throw new RuntimeException(e);
331: }
332: }
333:
334: private InputStream getInitStream() {
335: return getClass().getResourceAsStream(
336: "/com/sun/tools/hat/resources/hat.js");
337: }
338:
339: private Object engine;
340: private Method evalMethod;
341: private Method invokeMethod;
342: private Snapshot snapshot;
343: private static boolean debug = false;
344: private static boolean oqlSupported;
345: }
|