001: /*
002: * Copyright (c) 1998 - 2005 Versant Corporation
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * Versant Corporation - initial API and implementation
010: */
011: package com.versant.core.jdo.query.mem;
012:
013: import com.versant.lib.bcel.Constants;
014: import com.versant.lib.bcel.classfile.JavaClass;
015: import com.versant.lib.bcel.generic.ClassGen;
016: import com.versant.lib.bcel.generic.InstructionFactory;
017:
018: import com.versant.core.metadata.ClassMetaData;
019: import com.versant.core.metadata.ModelMetaData;
020: import com.versant.core.jdo.*;
021: import com.versant.core.jdo.query.ParamNode;
022: import com.versant.core.jdo.query.ParseException;
023:
024: import java.util.HashMap;
025: import java.util.Map;
026: import java.lang.reflect.Method;
027: import java.lang.reflect.InvocationTargetException;
028: import java.io.File;
029: import java.io.FileOutputStream;
030: import java.io.IOException;
031:
032: import com.versant.core.common.BindingSupportImpl;
033:
034: /**
035: * Compiler for in memory queries. This maintains a cache of precompiled
036: * queries.
037: */
038: public class MemQueryCompiler {
039:
040: private final ModelMetaData jmd;
041: private final ClassLoader loader;
042: private Map compiledQueryMap = new HashMap();
043: private long clsCounter;
044: private Method defineClass;
045:
046: public MemQueryCompiler(ModelMetaData jmd, ClassLoader loader) {
047: this .jmd = jmd;
048: this .loader = loader;
049:
050: try {
051: defineClass = ClassLoader.class.getDeclaredMethod(
052: "defineClass", new Class[] { String.class,
053: byte[].class, Integer.TYPE, Integer.TYPE });
054: } catch (NoSuchMethodException e) {
055: // not possible really
056: throw BindingSupportImpl.getInstance().internal(
057: e.toString(), e);
058: }
059: defineClass.setAccessible(true);
060:
061: }
062:
063: public synchronized BCodeQuery compile(QueryDetails queryParams,
064: Object[] params) {
065: BCodeQuery bCodeQuery = (BCodeQuery) compiledQueryMap
066: .get(queryParams);
067: if (bCodeQuery != null)
068: return bCodeQuery;
069:
070: boolean toFilter = true;
071: if (queryParams.getFilter() == null
072: || queryParams.getFilter().equals("true")) {
073: toFilter = false;
074: } else {
075: toFilter = true;
076: }
077:
078: ClassMetaData classMetaData = jmd.getClassMetaData(queryParams
079: .getCandidateClass());
080: String name = getQClsName();
081: ClassGen classGen = new ClassGen(name, BCodeQuery.class
082: .getName(), "<generated>", Constants.ACC_PUBLIC
083: | Constants.ACC_SUPER, null);
084: InstructionFactory factory = new InstructionFactory(classGen);
085: classGen.addEmptyConstructor(Constants.ACC_PUBLIC);
086:
087: CompiledMemQuery compiledMemQuery = new CompiledMemQuery(jmd);
088: try {
089: compiledMemQuery.compile(queryParams);
090: } catch (ParseException e) {
091: throw BindingSupportImpl.getInstance().exception(
092: e.getMessage());
093: }
094:
095: if (toFilter) {
096: ByteCodeQVisitor byteCodeQVisitor = new ByteCodeQVisitor(
097: classGen, factory, name, classMetaData,
098: compiledMemQuery);
099: byteCodeQVisitor.setParamMap(createParamMap(params,
100: compiledMemQuery));
101: compiledMemQuery.filter.visit(byteCodeQVisitor, null);
102: byteCodeQVisitor.finish();
103: }
104:
105: if (queryParams.getOrdering() != null) {
106: ByteCodeQCompareVisitor byteCodeQCompareVisitor = new ByteCodeQCompareVisitor(
107: classGen, factory, name, classMetaData);
108: for (int i = 0; i < compiledMemQuery.orders.length; i++) {
109: compiledMemQuery.orders[i].visit(
110: byteCodeQCompareVisitor, null);
111: }
112: byteCodeQCompareVisitor.finish();
113: }
114:
115: JavaClass javaClass = classGen.getJavaClass();
116: try {
117: bCodeQuery = (BCodeQuery) defineClass(javaClass.getBytes(),
118: null).newInstance();
119: compiledQueryMap.put(queryParams, bCodeQuery);
120: } catch (Exception e) {
121: if (BindingSupportImpl.getInstance().isOwnException(e)) {
122: throw (RuntimeException) e;
123: }
124: throw BindingSupportImpl.getInstance().internal(
125: e.getMessage(), e);
126: }
127:
128: return bCodeQuery;
129: }
130:
131: private Map createParamMap(Object[] params,
132: CompiledMemQuery compiledMemQuery) {
133: Map m = new HashMap();
134: if (compiledMemQuery.params == null) {
135: return m;
136: }
137: for (int i = 0; i < compiledMemQuery.params.length; i++) {
138: ParamNode param = compiledMemQuery.params[i];
139: m.put(param.getIdentifier(), params[i]);
140: }
141: return m;
142: }
143:
144: private Class defineClass(byte[] bytecode, File dir) {
145: try {
146: Class cls = (Class) defineClass.invoke(loader,
147: new Object[] { null, bytecode, new Integer(0),
148: new Integer(bytecode.length) });
149: if (dir != null) {
150: File f = new File(dir, cls.getName() + ".class");
151: try {
152: FileOutputStream o = new FileOutputStream(f);
153: o.write(bytecode, 0, bytecode.length);
154: o.close();
155: } catch (IOException x) {
156: throw BindingSupportImpl.getInstance().runtime(
157: "Error writing to " + f + ": " + x, x);
158: }
159: }
160: return cls;
161: } catch (InvocationTargetException e) {
162: Throwable t = e.getTargetException();
163: throw BindingSupportImpl.getInstance().internal(
164: t.toString(), t);
165: } catch (Exception x) {
166: throw BindingSupportImpl.getInstance().internal(
167: x.toString(), x);
168: }
169: }
170:
171: private synchronized String getQClsName() {
172: return "VOA_QUERY_" + clsCounter++;
173: }
174:
175: }
|