blob: 1ab0dcc58e0928094c16e3f91c3692e47a36bc1d [file] [log] [blame]
/*
* 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.asm7.AnnotationVisitor;
import org.apache.xbean.asm7.ClassReader;
import org.apache.xbean.asm7.FieldVisitor;
import org.apache.xbean.asm7.MethodVisitor;
import org.apache.xbean.asm7.Opcodes;
import org.apache.xbean.asm7.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<>();
private final Set<String> processed = new HashSet<>();
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.isEmpty()) {
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<>();
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<>();
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.ASM7);
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);
}
}
}