blob: 3d98369e859a70606be14b01e168c2f4c37bd1c0 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26
27/*
28 * The contents of this file are subject to the Sun Public License
29 * Version 1.0 (the "License"); you may not use this file except in
30 * compliance with the License. A copy of the License is available at
31 * http://www.sun.com/, and in the file LICENSE.html in the
32 * doc directory.
33 *
34 * The Original Code is HAT. The Initial Developer of the
35 * Original Code is Bill Foote, with contributions from others
36 * at JavaSoft/Sun. Portions created by Bill Foote and others
37 * at Javasoft/Sun are Copyright (C) 1997-2004. All Rights Reserved.
38 *
39 * In addition to the formal license, I ask that you don't
40 * change the history or donations files without permission.
41 *
42 */
43
44package com.sun.tools.hat.internal.oql;
45
46import com.sun.tools.hat.internal.model.*;
47import java.io.*;
48import java.lang.reflect.*;
49import java.util.*;
50
51/**
52 * This is Object Query Language Interpreter
53 *
54 */
55public class OQLEngine {
56 static {
57 try {
58 // Do we have javax.script support?
59 // create ScriptEngineManager
60 Class<?> managerClass = Class.forName("javax.script.ScriptEngineManager");
61 Object manager = managerClass.newInstance();
62
63 // create JavaScript engine
64 Method getEngineMethod = managerClass.getMethod("getEngineByName",
65 new Class[] { String.class });
66 Object jse = getEngineMethod.invoke(manager, new Object[] {"js"});
67 oqlSupported = (jse != null);
68 } catch (Exception exp) {
69 oqlSupported = false;
70 }
71 }
72
73 // check OQL is supported or not before creating OQLEngine
74 public static boolean isOQLSupported() {
75 return oqlSupported;
76 }
77
78 public OQLEngine(Snapshot snapshot) {
79 if (!isOQLSupported()) {
80 throw new UnsupportedOperationException("OQL not supported");
81 }
82 init(snapshot);
83 }
84
85 /**
86 Query is of the form
87
88 select &lt;java script code to select&gt;
89 [ from [instanceof] &lt;class name&gt; [&lt;identifier&gt;]
90 [ where &lt;java script boolean expression&gt; ]
91 ]
92 */
93 public synchronized void executeQuery(String query, ObjectVisitor visitor)
94 throws OQLException {
95 debugPrint("query : " + query);
96 StringTokenizer st = new StringTokenizer(query);
97 if (st.hasMoreTokens()) {
98 String first = st.nextToken();
99 if (! first.equals("select") ) {
100 // Query does not start with 'select' keyword.
101 // Just treat it as plain JavaScript and eval it.
102 try {
103 Object res = evalScript(query);
104 visitor.visit(res);
105 } catch (Exception e) {
106 throw new OQLException(e);
107 }
108 return;
109 }
110 } else {
111 throw new OQLException("query syntax error: no 'select' clause");
112 }
113
114 String selectExpr = "";
115 boolean seenFrom = false;
116 while (st.hasMoreTokens()) {
117 String tok = st.nextToken();
118 if (tok.equals("from")) {
119 seenFrom = true;
120 break;
121 }
122 selectExpr += " " + tok;
123 }
124
125 if (selectExpr.equals("")) {
126 throw new OQLException("query syntax error: 'select' expression can not be empty");
127 }
128
129 String className = null;
130 boolean isInstanceOf = false;
131 String whereExpr = null;
132 String identifier = null;
133
134 if (seenFrom) {
135 if (st.hasMoreTokens()) {
136 String tmp = st.nextToken();
137 if (tmp.equals("instanceof")) {
138 isInstanceOf = true;
139 if (! st.hasMoreTokens()) {
140 throw new OQLException("no class name after 'instanceof'");
141 }
142 className = st.nextToken();
143 } else {
144 className = tmp;
145 }
146 } else {
147 throw new OQLException("query syntax error: class name must follow 'from'");
148 }
149
150 if (st.hasMoreTokens()) {
151 identifier = st.nextToken();
152 if (identifier.equals("where")) {
153 throw new OQLException("query syntax error: identifier should follow class name");
154 }
155 if (st.hasMoreTokens()) {
156 String tmp = st.nextToken();
157 if (! tmp.equals("where")) {
158 throw new OQLException("query syntax error: 'where' clause expected after 'from' clause");
159 }
160
161 whereExpr = "";
162 while (st.hasMoreTokens()) {
163 whereExpr += " " + st.nextToken();
164 }
165 if (whereExpr.equals("")) {
166 throw new OQLException("query syntax error: 'where' clause cannot have empty expression");
167 }
168 }
169 } else {
170 throw new OQLException("query syntax error: identifier should follow class name");
171 }
172 }
173
174 executeQuery(new OQLQuery(selectExpr, isInstanceOf, className,
175 identifier, whereExpr), visitor);
176 }
177
178 private void executeQuery(OQLQuery q, ObjectVisitor visitor)
179 throws OQLException {
180 JavaClass clazz = null;
181 if (q.className != null) {
182 clazz = snapshot.findClass(q.className);
183 if (clazz == null) {
184 throw new OQLException(q.className + " is not found!");
185 }
186 }
187
188 StringBuffer buf = new StringBuffer();
189 buf.append("function __select__(");
190 if (q.identifier != null) {
191 buf.append(q.identifier);
192 }
193 buf.append(") { return ");
194 buf.append(q.selectExpr.replace('\n', ' '));
195 buf.append("; }");
196
197 String selectCode = buf.toString();
198 debugPrint(selectCode);
199 String whereCode = null;
200 if (q.whereExpr != null) {
201 buf = new StringBuffer();
202 buf.append("function __where__(");
203 buf.append(q.identifier);
204 buf.append(") { return ");
205 buf.append(q.whereExpr.replace('\n', ' '));
206 buf.append("; }");
207 whereCode = buf.toString();
208 }
209 debugPrint(whereCode);
210
211 // compile select expression and where condition
212 try {
213 evalMethod.invoke(engine, new Object[] { selectCode });
214 if (whereCode != null) {
215 evalMethod.invoke(engine, new Object[] { whereCode });
216 }
217
218 if (q.className != null) {
219 Enumeration objects = clazz.getInstances(q.isInstanceOf);
220 while (objects.hasMoreElements()) {
221 JavaHeapObject obj = (JavaHeapObject) objects.nextElement();
222 Object[] args = new Object[] { wrapJavaObject(obj) };
223 boolean b = (whereCode == null);
224 if (!b) {
225 Object res = call("__where__", args);
226 if (res instanceof Boolean) {
227 b = ((Boolean)res).booleanValue();
228 } else if (res instanceof Number) {
229 b = ((Number)res).intValue() != 0;
230 } else {
231 b = (res != null);
232 }
233 }
234
235 if (b) {
236 Object select = call("__select__", args);
237 if (visitor.visit(select)) return;
238 }
239 }
240 } else {
241 // simple "select <expr>" query
242 Object select = call("__select__", new Object[] {});
243 visitor.visit(select);
244 }
245 } catch (Exception e) {
246 throw new OQLException(e);
247 }
248 }
249
250 public Object evalScript(String script) throws Exception {
251 return evalMethod.invoke(engine, new Object[] { script });
252 }
253
254 public Object wrapJavaObject(JavaHeapObject obj) throws Exception {
255 return call("wrapJavaObject", new Object[] { obj });
256 }
257
258 public Object toHtml(Object obj) throws Exception {
259 return call("toHtml", new Object[] { obj });
260 }
261
262 public Object call(String func, Object[] args) throws Exception {
263 return invokeMethod.invoke(engine, new Object[] { func, args });
264 }
265
266 private static void debugPrint(String msg) {
267 if (debug) System.out.println(msg);
268 }
269
270 private void init(Snapshot snapshot) throws RuntimeException {
271 this.snapshot = snapshot;
272 try {
273 // create ScriptEngineManager
274 Class<?> managerClass = Class.forName("javax.script.ScriptEngineManager");
275 Object manager = managerClass.newInstance();
276
277 // create JavaScript engine
278 Method getEngineMethod = managerClass.getMethod("getEngineByName",
279 new Class[] { String.class });
280 engine = getEngineMethod.invoke(manager, new Object[] {"js"});
281
282 // initialize engine with init file (hat.js)
283 InputStream strm = getInitStream();
284 Class<?> engineClass = Class.forName("javax.script.ScriptEngine");
285 evalMethod = engineClass.getMethod("eval",
286 new Class[] { Reader.class });
287 evalMethod.invoke(engine, new Object[] {new InputStreamReader(strm)});
288
289 // initialize ScriptEngine.eval(String) and
290 // Invocable.invokeFunction(String, Object[]) methods.
291 Class<?> invocableClass = Class.forName("javax.script.Invocable");
292
293 evalMethod = engineClass.getMethod("eval",
294 new Class[] { String.class });
295 invokeMethod = invocableClass.getMethod("invokeFunction",
296 new Class[] { String.class, Object[].class });
297
298 // initialize ScriptEngine.put(String, Object) method
299 Method putMethod = engineClass.getMethod("put",
300 new Class[] { String.class, Object.class });
301
302 // call ScriptEngine.put to initialize built-in heap object
303 putMethod.invoke(engine, new Object[] {
304 "heap", call("wrapHeapSnapshot", new Object[] { snapshot })
305 });
306 } catch (Exception e) {
307 if (debug) e.printStackTrace();
308 throw new RuntimeException(e);
309 }
310 }
311
312 private InputStream getInitStream() {
313 return getClass().getResourceAsStream("/com/sun/tools/hat/resources/hat.js");
314 }
315
316 private Object engine;
317 private Method evalMethod;
318 private Method invokeMethod;
319 private Snapshot snapshot;
320 private static boolean debug = false;
321 private static boolean oqlSupported;
322}