1 /* 2 * Copyright (c) 1997, 2008, Oracle and/or its affiliates. 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 27 /* 28 * The Original Code is HAT. The Initial Developer of the 29 * Original Code is Bill Foote, with contributions from others 30 * at JavaSoft/Sun. 31 */ 32 33 package com.sun.tools.hat.internal.oql; 34 35 import com.sun.tools.hat.internal.model.*; 36 import java.io.*; 37 import java.lang.reflect.*; 38 import java.util.*; 39 40 /** 41 * This is Object Query Language Interpreter 42 * 43 */ 44 public class OQLEngine { 45 static { 46 try { 47 // Do we have javax.script support? 48 // create ScriptEngineManager 49 Class<?> managerClass = Class.forName("javax.script.ScriptEngineManager"); 50 Object manager = managerClass.newInstance(); 51 52 // create JavaScript engine 53 Method getEngineMethod = managerClass.getMethod("getEngineByName", 54 new Class<?>[] { String.class }); 55 Object jse = getEngineMethod.invoke(manager, new Object[] {"js"}); 56 oqlSupported = (jse != null); 57 } catch (Exception exp) { 58 oqlSupported = false; 59 } 60 } 61 62 // check OQL is supported or not before creating OQLEngine 63 public static boolean isOQLSupported() { 64 return oqlSupported; 65 } 66 67 public OQLEngine(Snapshot snapshot) { 68 if (!isOQLSupported()) { 69 throw new UnsupportedOperationException("OQL not supported"); 70 } 71 init(snapshot); 72 } 73 74 /** 75 Query is of the form 76 77 select <java script code to select> 78 [ from [instanceof] <class name> [<identifier>] 79 [ where <java script boolean expression> ] 80 ] 81 */ 82 public synchronized void executeQuery(String query, ObjectVisitor visitor) 83 throws OQLException { 84 debugPrint("query : " + query); 85 StringTokenizer st = new StringTokenizer(query); 86 if (st.hasMoreTokens()) { 87 String first = st.nextToken(); 88 if (! first.equals("select") ) { 89 // Query does not start with 'select' keyword. 90 // Just treat it as plain JavaScript and eval it. 91 try { 92 Object res = evalScript(query); 93 visitor.visit(res); 94 } catch (Exception e) { 95 throw new OQLException(e); 96 } 97 return; 98 } 99 } else { 100 throw new OQLException("query syntax error: no 'select' clause"); 101 } 102 103 String selectExpr = ""; 104 boolean seenFrom = false; 105 while (st.hasMoreTokens()) { 106 String tok = st.nextToken(); 107 if (tok.equals("from")) { 108 seenFrom = true; 109 break; 110 } 111 selectExpr += " " + tok; 112 } 113 114 if (selectExpr.equals("")) { 115 throw new OQLException("query syntax error: 'select' expression can not be empty"); 116 } 117 118 String className = null; 119 boolean isInstanceOf = false; 120 String whereExpr = null; 121 String identifier = null; 122 123 if (seenFrom) { 124 if (st.hasMoreTokens()) { 125 String tmp = st.nextToken(); 126 if (tmp.equals("instanceof")) { 127 isInstanceOf = true; 128 if (! st.hasMoreTokens()) { 129 throw new OQLException("no class name after 'instanceof'"); 130 } 131 className = st.nextToken(); 132 } else { 133 className = tmp; 134 } 135 } else { 136 throw new OQLException("query syntax error: class name must follow 'from'"); 137 } 138 139 if (st.hasMoreTokens()) { 140 identifier = st.nextToken(); 141 if (identifier.equals("where")) { 142 throw new OQLException("query syntax error: identifier should follow class name"); 143 } 144 if (st.hasMoreTokens()) { 145 String tmp = st.nextToken(); 146 if (! tmp.equals("where")) { 147 throw new OQLException("query syntax error: 'where' clause expected after 'from' clause"); 148 } 149 150 whereExpr = ""; 151 while (st.hasMoreTokens()) { 152 whereExpr += " " + st.nextToken(); 153 } 154 if (whereExpr.equals("")) { 155 throw new OQLException("query syntax error: 'where' clause cannot have empty expression"); 156 } 157 } 158 } else { 159 throw new OQLException("query syntax error: identifier should follow class name"); 160 } 161 } 162 163 executeQuery(new OQLQuery(selectExpr, isInstanceOf, className, 164 identifier, whereExpr), visitor); 165 } 166 167 private void executeQuery(OQLQuery q, ObjectVisitor visitor) 168 throws OQLException { 169 JavaClass clazz = null; 170 if (q.className != null) { 171 clazz = snapshot.findClass(q.className); 172 if (clazz == null) { 173 throw new OQLException(q.className + " is not found!"); 174 } 175 } 176 177 StringBuffer buf = new StringBuffer(); 178 buf.append("function __select__("); 179 if (q.identifier != null) { 180 buf.append(q.identifier); 181 } 182 buf.append(") { return "); 183 buf.append(q.selectExpr.replace('\n', ' ')); 184 buf.append("; }"); 185 186 String selectCode = buf.toString(); 187 debugPrint(selectCode); 188 String whereCode = null; 189 if (q.whereExpr != null) { 190 buf = new StringBuffer(); 191 buf.append("function __where__("); 192 buf.append(q.identifier); 193 buf.append(") { return "); 194 buf.append(q.whereExpr.replace('\n', ' ')); 195 buf.append("; }"); 196 whereCode = buf.toString(); 197 } 198 debugPrint(whereCode); 199 200 // compile select expression and where condition 201 try { 202 evalMethod.invoke(engine, new Object[] { selectCode }); 203 if (whereCode != null) { 204 evalMethod.invoke(engine, new Object[] { whereCode }); 205 } 206 207 if (q.className != null) { 208 Enumeration<JavaHeapObject> objects = clazz.getInstances(q.isInstanceOf); 209 while (objects.hasMoreElements()) { 210 JavaHeapObject obj = objects.nextElement(); 211 Object[] args = new Object[] { wrapJavaObject(obj) }; 212 boolean b = (whereCode == null); 213 if (!b) { 214 Object res = call("__where__", args); 215 if (res instanceof Boolean) { 216 b = ((Boolean)res).booleanValue(); 217 } else if (res instanceof Number) { 218 b = ((Number)res).intValue() != 0; 219 } else { 220 b = (res != null); 221 } 222 } 223 224 if (b) { 225 Object select = call("__select__", args); 226 if (visitor.visit(select)) return; 227 } 228 } 229 } else { 230 // simple "select <expr>" query 231 Object select = call("__select__", new Object[] {}); 232 visitor.visit(select); 233 } 234 } catch (Exception e) { 235 throw new OQLException(e); 236 } 237 } 238 239 public Object evalScript(String script) throws Exception { 240 return evalMethod.invoke(engine, new Object[] { script }); 241 } 242 243 public Object wrapJavaObject(JavaHeapObject obj) throws Exception { 244 return call("wrapJavaObject", new Object[] { obj }); 245 } 246 247 public Object toHtml(Object obj) throws Exception { 248 return call("toHtml", new Object[] { obj }); 249 } 250 251 public Object call(String func, Object[] args) throws Exception { 252 return invokeMethod.invoke(engine, new Object[] { func, args }); 253 } 254 255 private static void debugPrint(String msg) { 256 if (debug) System.out.println(msg); 257 } 258 259 private void init(Snapshot snapshot) throws RuntimeException { 260 this.snapshot = snapshot; 261 try { 262 // create ScriptEngineManager 263 Class<?> managerClass = Class.forName("javax.script.ScriptEngineManager"); 264 Object manager = managerClass.newInstance(); 265 266 // create JavaScript engine 267 Method getEngineMethod = managerClass.getMethod("getEngineByName", 268 new Class<?>[] { String.class }); 269 engine = getEngineMethod.invoke(manager, new Object[] {"js"}); 270 271 // initialize engine with init file (hat.js) 272 InputStream strm = getInitStream(); 273 Class<?> engineClass = Class.forName("javax.script.ScriptEngine"); 274 evalMethod = engineClass.getMethod("eval", 275 new Class<?>[] { Reader.class }); 276 evalMethod.invoke(engine, new Object[] {new InputStreamReader(strm)}); 277 278 // initialize ScriptEngine.eval(String) and 279 // Invocable.invokeFunction(String, Object[]) methods. 280 Class<?> invocableClass = Class.forName("javax.script.Invocable"); 281 282 evalMethod = engineClass.getMethod("eval", 283 new Class<?>[] { String.class }); 284 invokeMethod = invocableClass.getMethod("invokeFunction", 285 new Class<?>[] { String.class, Object[].class }); 286 287 // initialize ScriptEngine.put(String, Object) method 288 Method putMethod = engineClass.getMethod("put", 289 new Class<?>[] { String.class, Object.class }); 290 291 // call ScriptEngine.put to initialize built-in heap object 292 putMethod.invoke(engine, new Object[] { 293 "heap", call("wrapHeapSnapshot", new Object[] { snapshot }) 294 }); 295 } catch (Exception e) { 296 if (debug) e.printStackTrace(); 297 throw new RuntimeException(e); 298 } 299 } 300 301 private InputStream getInitStream() { 302 return getClass().getResourceAsStream("/com/sun/tools/hat/resources/hat.js"); 303 } 304 305 private Object engine; 306 private Method evalMethod; 307 private Method invokeMethod; 308 private Snapshot snapshot; 309 private static boolean debug = false; 310 private static boolean oqlSupported; 311 }