| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.openjpa.datacache; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| import org.apache.openjpa.conf.OpenJPAConfiguration; |
| import org.apache.openjpa.enhance.PCDataGenerator; |
| import org.apache.openjpa.kernel.AbstractPCData; |
| import org.apache.openjpa.kernel.OpenJPAStateManager; |
| import org.apache.openjpa.kernel.StoreContext; |
| import org.apache.openjpa.meta.ClassMetaData; |
| import org.apache.openjpa.meta.FieldMetaData; |
| import org.apache.openjpa.meta.JavaTypes; |
| import org.apache.openjpa.meta.ValueMetaData; |
| |
| import serp.bytecode.BCClass; |
| import serp.bytecode.BCField; |
| import serp.bytecode.BCMethod; |
| import serp.bytecode.Code; |
| import serp.bytecode.Instruction; |
| import serp.bytecode.JumpInstruction; |
| |
| /** |
| * A {@link PCDataGenerator} instance which generates properly |
| * synchronized instances suitable for use in the cache. In addition, |
| * proper timed behavior is added. |
| * |
| * @author Steve Kim |
| * @since 0.3.3.0 |
| */ |
| public class DataCachePCDataGenerator extends PCDataGenerator { |
| |
| public static final String POSTFIX = "datacache"; |
| |
| private static final Set _synchs = new HashSet(Arrays.asList |
| (new String []{ "getData", "setData", "clearData", "getImplData", |
| "setImplData", "setIntermediate", "getIntermediate", |
| "isLoaded", "setLoaded", "setVersion", "getVersion", "store" |
| })); |
| |
| public DataCachePCDataGenerator(OpenJPAConfiguration conf) { |
| super(conf); |
| } |
| |
| @Override |
| protected String getUniqueName(Class type) { |
| return super.getUniqueName(type) + POSTFIX; |
| } |
| |
| @Override |
| protected void finish(DynamicPCData data, ClassMetaData meta) { |
| int timeout = meta.getDataCacheTimeout(); |
| if (timeout > 0) |
| ((Timed) data).setTimeout(timeout + System.currentTimeMillis()); |
| else |
| ((Timed) data).setTimeout(-1); |
| } |
| |
| @Override |
| protected void decorate(BCClass bc, ClassMetaData meta) { |
| enhanceToData(bc); |
| enhanceToNestedData(bc); |
| replaceNewEmbeddedPCData(bc); |
| addSynchronization(bc); |
| addTimeout(bc); |
| } |
| |
| private void enhanceToData(BCClass bc) { |
| BCMethod meth = bc.declareMethod("toData", Object.class, |
| new Class []{ FieldMetaData.class, Object.class, |
| StoreContext.class }); |
| Code code = meth.getCode(true); |
| // if (fmd.isLRS ())) |
| // return NULL; |
| code.aload().setParam(0); |
| code.invokevirtual().setMethod(FieldMetaData.class, "isLRS", |
| boolean.class, null); |
| JumpInstruction ifins = code.ifeq(); |
| code.getstatic().setField(AbstractPCData.class, "NULL", Object.class); |
| code.areturn(); |
| // super.toData (fmd, val, ctx); |
| ifins.setTarget(code.aload().setThis()); |
| code.aload().setParam(0); |
| code.aload().setParam(1); |
| code.aload().setParam(2); |
| code.invokespecial().setMethod(AbstractPCData.class, "toData", |
| Object.class, new Class[]{ FieldMetaData.class, Object.class, |
| StoreContext.class }); |
| code.areturn(); |
| code.calculateMaxStack(); |
| code.calculateMaxLocals(); |
| } |
| |
| private void enhanceToNestedData(BCClass bc) { |
| BCMethod meth = bc.declareMethod("toNestedData", Object.class, |
| new Class []{ ValueMetaData.class, Object.class, |
| StoreContext.class }); |
| Code code = meth.getCode(true); |
| |
| // if (val == null) |
| // return null; |
| code.aload().setParam(1); |
| JumpInstruction ifins = code.ifnonnull(); |
| code.constant().setNull(); |
| code.areturn(); |
| |
| // int type = vmd.getDeclaredTypeCode (); |
| ifins.setTarget(code.aload().setParam(0)); |
| code.invokeinterface().setMethod(ValueMetaData.class, |
| "getDeclaredTypeCode", int.class, null); |
| int local = code.getNextLocalsIndex(); |
| code.istore().setLocal(local); |
| |
| // if (type != JavaTypes.COLLECTION && |
| // type != JavaTypes.MAP && |
| // type != JavaTypes.ARRAY) |
| // return super.toNestedData(type, val, ctx); |
| // else |
| // return NULL; |
| Collection jumps = new ArrayList(3); |
| code.iload().setLocal(local); |
| code.constant().setValue(JavaTypes.COLLECTION); |
| jumps.add(code.ificmpeq()); |
| code.iload().setLocal(local); |
| code.constant().setValue(JavaTypes.MAP); |
| jumps.add(code.ificmpeq()); |
| code.iload().setLocal(local); |
| code.constant().setValue(JavaTypes.ARRAY); |
| jumps.add(code.ificmpeq()); |
| code.aload().setThis(); |
| code.aload().setParam(0); |
| code.aload().setParam(1); |
| code.aload().setParam(2); |
| code.invokespecial().setMethod(AbstractPCData.class, "toNestedData", |
| Object.class, new Class[]{ ValueMetaData.class, Object.class, |
| StoreContext.class }); |
| code.areturn(); |
| setTarget(code.getstatic().setField |
| (AbstractPCData.class, "NULL", Object.class), jumps); |
| code.areturn(); |
| code.calculateMaxStack(); |
| code.calculateMaxLocals(); |
| } |
| |
| private void replaceNewEmbeddedPCData(BCClass bc) { |
| BCMethod meth = bc.declareMethod("newEmbeddedPCData", |
| AbstractPCData.class, new Class[]{ OpenJPAStateManager.class }); |
| Code code = meth.getCode(true); |
| |
| // return new DataCachePCDataImpl(sm.getObjectId(), sm.getMetaData()); |
| code.anew().setType(DataCachePCDataImpl.class); |
| code.dup(); |
| code.aload().setParam(0); |
| code.invokeinterface().setMethod(OpenJPAStateManager.class, "getId", |
| Object.class, null); |
| code.aload().setParam(0); |
| code.invokeinterface().setMethod(OpenJPAStateManager.class, |
| "getMetaData", ClassMetaData.class, null); |
| code.invokespecial().setMethod(DataCachePCDataImpl.class, "<init>", |
| void.class, new Class[] { Object.class, ClassMetaData.class }); |
| code.areturn(); |
| |
| code.calculateMaxLocals(); |
| code.calculateMaxStack(); |
| } |
| |
| private void addTimeout(BCClass bc) { |
| bc.declareInterface(DataCachePCData.class); |
| bc.declareInterface(Timed.class); |
| |
| // public boolean isTimedOut (); |
| BCField field = addBeanField(bc, "timeout", long.class); |
| BCMethod meth = bc.declareMethod("isTimedOut", boolean.class, null); |
| Code code = meth.getCode(true); |
| |
| // if (timeout == -1) ... |
| code.aload().setThis(); |
| code.getfield().setField(field); |
| code.constant().setValue(-1L); |
| code.lcmp(); |
| JumpInstruction ifneg = code.ifeq(); |
| |
| // if (timeout >= System.currentTimeMillis ()) |
| code.aload().setThis(); |
| code.getfield().setField(field); |
| code.invokestatic().setMethod(System.class, "currentTimeMillis", |
| long.class, null); |
| code.lcmp(); |
| JumpInstruction ifnexp = code.ifge(); |
| |
| // return true; |
| code.constant().setValue(1); |
| |
| // ... else return false; |
| JumpInstruction go2 = code.go2(); |
| Instruction flse = code.constant().setValue(0); |
| ifneg.setTarget(flse); |
| ifnexp.setTarget(flse); |
| go2.setTarget(code.ireturn()); |
| |
| code.calculateMaxStack(); |
| code.calculateMaxLocals(); |
| } |
| |
| private void addSynchronization(BCClass bc) { |
| BCMethod[] methods = bc.getDeclaredMethods(); |
| for (BCMethod bcMethod : methods) { |
| if (bcMethod.isPublic() |
| && _synchs.contains(bcMethod.getName())) |
| bcMethod.setSynchronized(true); |
| } |
| |
| // add synchronized isLoaded call. |
| // public synchronized boolean isLoaded (int field) |
| // { |
| // return super.isLoaded (field); |
| // } |
| BCMethod method = bc.declareMethod("isLoaded", boolean.class, |
| new Class[]{ int.class }); |
| method.setSynchronized(true); |
| Code code = method.getCode(true); |
| code.aload().setThis(); |
| code.iload().setParam(0); |
| code.invokespecial().setMethod(AbstractPCData.class, "isLoaded", |
| boolean.class, new Class[]{ int.class }); |
| code.calculateMaxLocals(); |
| code.calculateMaxStack(); |
| code.ireturn(); |
| } |
| |
| /** |
| * Simple interface to give access to expiration time. |
| */ |
| public interface Timed { |
| |
| void setTimeout(long time); |
| } |
| } |