/* * 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.openejb.config; import org.apache.openejb.OpenEJBException; import org.apache.openejb.loader.IO; import org.apache.xbean.asm5.AnnotationVisitor; import org.apache.xbean.asm5.ClassReader; import org.apache.xbean.asm5.FieldVisitor; import org.apache.xbean.asm5.MethodVisitor; import org.apache.xbean.asm5.Opcodes; import org.apache.xbean.asm5.shade.commons.EmptyVisitor; import javax.persistence.PersistenceContext; import javax.persistence.PersistenceProperty; import javax.persistence.SynchronizationType; import java.io.IOException; import java.net.URL; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; public class PersistenceContextAnnFactory { @SuppressWarnings("FieldMayBeFinal") // This gets set by reflection private static boolean useAsm; static { boolean isPersistenceContextAnnotationValid = false; try { // Tomcat persistence context class is missing the properties method final Class<?> persistenceContextClass = Class.forName("javax.persistence.PersistenceContext"); persistenceContextClass.getMethod("properties", (Class[]) null); isPersistenceContextAnnotationValid = true; } catch (final Exception e) { // no-op } useAsm = !isPersistenceContextAnnotationValid; } public Map<String, AsmPersistenceContext> contexts = new HashMap<String, AsmPersistenceContext>(); private final Set<String> processed = new HashSet<String>(); public void addAnnotations(final Class c) throws OpenEJBException { if (!useAsm) { return; } if (processed.contains(c.getName())) { return; } try { final URL u = c.getResource("/" + c.getName().replace('.', '/') + ".class"); final ClassReader r = new ClassReader(IO.read(u)); r.accept(new PersistenceContextReader(contexts), ClassReader.SKIP_DEBUG); } catch (final IOException e) { throw new OpenEJBException("Unable to read class " + c.getName()); } processed.add(c.getName()); } public PersistenceContextAnn create(final PersistenceContext persistenceContext, final AnnotationDeployer.Member member) throws OpenEJBException { if (useAsm) { if (member != null) { addAnnotations(member.getDeclaringClass()); } String name = persistenceContext.name(); if (name == null || name.equals("")) { name = member == null ? null : member.getDeclaringClass().getName() + "/" + member.getName(); } final AsmPersistenceContext asmPersistenceContext = contexts.get(name); if (asmPersistenceContext == null) { throw new NullPointerException("PersistenceContext " + name + " not found"); } return asmPersistenceContext; } else { return new DirectPersistenceContext(persistenceContext); } } private static class DirectPersistenceContext implements PersistenceContextAnn { private final PersistenceContext persistenceContext; public DirectPersistenceContext(final PersistenceContext persistenceContext) { if (persistenceContext == null) { throw new NullPointerException("persistenceContext is null"); } this.persistenceContext = persistenceContext; } public String name() { return persistenceContext.name(); } @Override public String synchronization() { return persistenceContext.synchronization().name(); } public String unitName() { return persistenceContext.unitName(); } public String type() { if (persistenceContext.type() == null) { return null; } return persistenceContext.type().toString(); } public Map<String, String> properties() { final Map<String, String> properties = new LinkedHashMap<String, String>(); for (final PersistenceProperty property : persistenceContext.properties()) { properties.put(property.name(), property.value()); } return properties; } public String toString() { return persistenceContext.toString(); } } private static class AsmPersistenceContext implements PersistenceContextAnn { public String name; public String unitName; public String type; public String synchronization = SynchronizationType.SYNCHRONIZED.name(); // default public final Map<String, String> properties = new LinkedHashMap<String, String>(); public String name() { return name; } @Override public String synchronization() { return synchronization; } public String unitName() { return unitName; } public String type() { return type; } public Map<String, String> properties() { return properties; } public String toString() { return "@PersistenceContext(name = \"" + name + "\", unitName = \"" + unitName + "\", type = PersistenceContextType." + type + ", " + properties + ")"; } } private static final class PersistenceContextReader extends EmptyVisitor { private String className; private String currentName; private final Map<String, AsmPersistenceContext> contexts; private PersistenceContextReader(final Map<String, AsmPersistenceContext> contexts) { this.contexts = contexts; } public void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) { className = name.replace("/", "."); super.visit(version, access, name, signature, superName, interfaces); } public FieldVisitor visitField(final int access, final String name, final String desc, final String signature, final Object value) { currentName = name; return super.visitField(access, name, desc, signature, value); } public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) { currentName = name; // setFoo -> foo if (currentName.startsWith("set")) { currentName = currentName.substring(3); } if (currentName.length() > 0) { currentName = Character.toLowerCase(currentName.charAt(0)) + currentName.substring(1); } return super.visitMethod(access, name, desc, signature, exceptions); } public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) { return visitAnnotation(null, desc); } @Override public AnnotationVisitor visitAnnotation(final String name, final String desc) { if ("Ljavax/persistence/PersistenceContext;".equals(desc)) { return new PersistenceContextVisitor(className, currentName, contexts); } else if ("Ljavax/persistence/PersistenceContexts;".equals(desc)) { return super.visitAnnotation(name, desc); } return new EmptyVisitor().annotationVisitor(); } @Override public AnnotationVisitor visitArray(final String string) { return annotationVisitor(); } @Override public AnnotationVisitor visitMethodParameterAnnotation(final int i, final String string, final boolean b) { return new EmptyVisitor().annotationVisitor(); } @Override public AnnotationVisitor visitParameterAnnotation(final int i, final String string, final boolean b) { return new EmptyVisitor().annotationVisitor(); } @Override public AnnotationVisitor visitAnnotationDefault() { return new EmptyVisitor().annotationVisitor(); } } private static class PersistenceContextVisitor extends AnnotationVisitor { private final Map<String, AsmPersistenceContext> contexts; private final AsmPersistenceContext persistenceContext = new AsmPersistenceContext(); public PersistenceContextVisitor(final String className, final String memberName, final Map<String, AsmPersistenceContext> contexts) { super(Opcodes.ASM5); this.contexts = contexts; persistenceContext.name = className + "/" + memberName; } public void visit(final String name, final Object value) { setValue(name, value == null ? null : value.toString()); } public void visitEnum(final String name, final String type, final String value) { setValue(name, value == null ? null : value.toString()); } public AnnotationVisitor visitAnnotation(final String name, final String desc) { setValue(name, desc); return this; } private void setValue(final String name, final String value) { if ("name".equals(name)) { persistenceContext.name = value; } else if ("unitName".equals(name)) { persistenceContext.unitName = value; } else if ("type".equals(name)) { persistenceContext.type = value; } else if ("synchronization".equals(name)) { persistenceContext.synchronization = value; } } public AnnotationVisitor visitArray(final String string) { return new EmptyVisitor() { private String name; private String value; public void visit(final String n, final Object v) { if ("name".equals(n)) { name = v == null ? null : v.toString(); } else if ("value".equals(n)) { value = v == null ? null : v.toString(); } } public void visitEnd() { persistenceContext.properties.put(name, value); } }.annotationVisitor(); } public void visitEnd() { contexts.put(persistenceContext.name, persistenceContext); } } }