diff options
Diffstat (limited to 'sca-java-2.x/tags/2.0-Beta4-RC1/modules/implementation-java/src/main/java/org/apache/tuscany/sca/implementation/java/introspect/impl/ReferenceProcessor.java')
-rw-r--r-- | sca-java-2.x/tags/2.0-Beta4-RC1/modules/implementation-java/src/main/java/org/apache/tuscany/sca/implementation/java/introspect/impl/ReferenceProcessor.java | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/sca-java-2.x/tags/2.0-Beta4-RC1/modules/implementation-java/src/main/java/org/apache/tuscany/sca/implementation/java/introspect/impl/ReferenceProcessor.java b/sca-java-2.x/tags/2.0-Beta4-RC1/modules/implementation-java/src/main/java/org/apache/tuscany/sca/implementation/java/introspect/impl/ReferenceProcessor.java new file mode 100644 index 0000000000..cb612bd224 --- /dev/null +++ b/sca-java-2.x/tags/2.0-Beta4-RC1/modules/implementation-java/src/main/java/org/apache/tuscany/sca/implementation/java/introspect/impl/ReferenceProcessor.java @@ -0,0 +1,300 @@ +/* + * 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.tuscany.sca.implementation.java.introspect.impl; + +import static org.apache.tuscany.sca.implementation.java.introspect.JavaIntrospectionHelper.getBaseType; + +import java.lang.annotation.Annotation; +import java.lang.annotation.ElementType; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.util.Collection; +import java.util.List; + +import org.apache.tuscany.sca.assembly.AssemblyFactory; +import org.apache.tuscany.sca.assembly.Multiplicity; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.implementation.java.IntrospectionException; +import org.apache.tuscany.sca.implementation.java.JavaElementImpl; +import org.apache.tuscany.sca.implementation.java.JavaImplementation; +import org.apache.tuscany.sca.implementation.java.JavaParameterImpl; +import org.apache.tuscany.sca.implementation.java.introspect.BaseJavaClassVisitor; +import org.apache.tuscany.sca.implementation.java.introspect.JavaIntrospectionHelper; +import org.apache.tuscany.sca.interfacedef.InvalidInterfaceException; +import org.apache.tuscany.sca.interfacedef.java.JavaInterface; +import org.apache.tuscany.sca.interfacedef.java.JavaInterfaceContract; +import org.apache.tuscany.sca.interfacedef.java.JavaInterfaceFactory; +import org.oasisopen.sca.ServiceReference; +import org.oasisopen.sca.annotation.AllowsPassByReference; +import org.oasisopen.sca.annotation.Reference; +import org.oasisopen.sca.annotation.Remotable; + +/** + * Processes an {@link @Reference} annotation, updating the component type with + * corresponding {@link + * org.apache.tuscany.spi.implementation.java.JavaMappedReference} + * + * @version $Rev$ $Date$ + */ +public class ReferenceProcessor extends BaseJavaClassVisitor { + + public ReferenceProcessor(AssemblyFactory assemblyFactory, JavaInterfaceFactory javaFactory) { + super(assemblyFactory); + this.javaInterfaceFactory = javaFactory; + } + + public ReferenceProcessor(ExtensionPointRegistry registry) { + super(registry); + } + + @Override + public void visitMethod(Method method, JavaImplementation type) throws IntrospectionException { + Reference annotation = method.getAnnotation(Reference.class); + if (annotation != null) { + + if (!JavaIntrospectionHelper.isSetter(method)) { + throw new IllegalReferenceException("Annotated method is not a setter: " + method, method); + } + + if(Modifier.isStatic(method.getModifiers())) { + throw new IllegalPropertyException("Static method " + method.getName() +" in class " + method.getDeclaringClass().getName() + " can not be annotated as a Reference"); + } + + String name = annotation.name(); + if ("".equals(name)) { + name = JavaIntrospectionHelper.toPropertyName(method.getName()); + } + JavaElementImpl ref = type.getReferenceMembers().get(name); + // Setter override field + if (ref != null && ref.getElementType() != ElementType.FIELD) { + throw new DuplicateReferenceException(name); + } + removeReference(ref, type); + + JavaElementImpl element = new JavaElementImpl(method, 0); + org.apache.tuscany.sca.assembly.Reference reference = createReference(type, element, name); + type.getReferences().add(reference); + type.getReferenceMembers().put(name, element); + } + + // enforce the constraint that an ordinary method's argument can not be a reference + Annotation paramsAnnotations[][] = method.getParameterAnnotations(); + for (int i = 0; i < paramsAnnotations.length; i++) { + Annotation argAnnotations[] = paramsAnnotations[i]; + for (int j = 0; j < argAnnotations.length; j++) { + if(argAnnotations[j].annotationType() == Reference.class) { + throw new IllegalReferenceException("Argument " + (i+1) + " of method " + method.getName() + " in class " + method.getDeclaringClass() + " can not be a Reference"); + } + } + } + } + + + @Override + public void visitField(Field field, JavaImplementation type) throws IntrospectionException { + Reference annotation = field.getAnnotation(Reference.class); + if (annotation == null) { + return; + } + + if(Modifier.isStatic(field.getModifiers())) { + throw new IllegalReferenceException("Static field " + field.getName() +" in class " + field.getDeclaringClass().getName() + " can not be annotated as Reference"); + } + String name = annotation.name(); + if ("".equals(name)) { + name = field.getName(); + } + JavaElementImpl ref = type.getReferenceMembers().get(name); + if (ref != null && ref.getElementType() == ElementType.FIELD) { + throw new DuplicateReferenceException(name); + } + + // Setter method override field + if (ref == null) { + JavaElementImpl element = new JavaElementImpl(field); + org.apache.tuscany.sca.assembly.Reference reference = createReference(type, element, name); + type.getReferences().add(reference); + type.getReferenceMembers().put(name, element); + } + } + + @Override + public void visitConstructorParameter(JavaParameterImpl parameter, JavaImplementation type) + throws IntrospectionException { + Reference refAnnotation = parameter.getAnnotation(Reference.class); + if (refAnnotation == null) { + return; + } + + if (!refAnnotation.required()) { + throw new InvalidReferenceException("[JCA90016] Constructor has @Reference with required=false: " + type.getName()); + } + + if (refAnnotation.name() == null || refAnnotation.name().length() < 1) { + throw new InvalidReferenceException("[JCA90018] @Reference in a Constructor must have a name attribute" + type.getName()); + } + + String paramName = parameter.getName(); + String name = getReferenceName(paramName, parameter.getIndex(), refAnnotation.name()); + JavaElementImpl ref = type.getReferenceMembers().get(name); + + // Setter override field + if (ref != null && ref.getElementType() != ElementType.FIELD) { + throw new DuplicateReferenceException(name); + } + + removeReference(ref, type); + org.apache.tuscany.sca.assembly.Reference reference = createReference(type, parameter, name); + type.getReferences().add(reference); + type.getReferenceMembers().put(name, parameter); + parameter.setClassifer(Reference.class); + parameter.setName(name); + } + + /** + * Create a SCA reference for a java Element + * @param implementation TODO + * @param element + * @param name + * @return + * @throws IntrospectionException + */ + private org.apache.tuscany.sca.assembly.Reference createReference(JavaImplementation implementation, JavaElementImpl element, String name) + throws IntrospectionException { + org.apache.tuscany.sca.assembly.Reference reference = assemblyFactory.createReference(); + JavaInterfaceContract interfaceContract = javaInterfaceFactory.createJavaInterfaceContract(); + reference.setInterfaceContract(interfaceContract); + + AllowsPassByReference pbr = element.getAnnotation(AllowsPassByReference.class); + if (pbr != null) { + reference.setAllowsPassByReference(true); + } else { + reference.setAllowsPassByReference(implementation.isAllowsPassByReference()); + } + + // reference.setMember((Member)element.getAnchor()); + boolean required = true; + Reference ref = element.getAnnotation(Reference.class); + if (ref != null) { + required = ref.required(); + } + // reference.setRequired(required); + reference.setName(name); + Class<?> rawType = element.getType(); + if (rawType.isArray() || Collection.class.isAssignableFrom(rawType)) { + if (required) { + reference.setMultiplicity(Multiplicity.ONE_N); + } else { + reference.setMultiplicity(Multiplicity.ZERO_N); + } + } else { + if (required) { + reference.setMultiplicity(Multiplicity.ONE_ONE); + } else { + reference.setMultiplicity(Multiplicity.ZERO_ONE); + } + } + Type genericType = element.getGenericType(); + Class<?> baseType = getBaseType(rawType, genericType); + if (ServiceReference.class.isAssignableFrom(baseType)) { + if (Collection.class.isAssignableFrom(rawType)) { + genericType = JavaIntrospectionHelper.getParameterType(genericType); + } + baseType = JavaIntrospectionHelper.getBusinessInterface(baseType, genericType); + } + // The reference can have a Remotable annotation. This forces the interface to be + // remotable even if the interface doesn't have a Remotable annotation. + boolean forceRemotable = element.getAnnotation(Remotable.class) != null; + // If the reference element is a setter method, element.getAnnotation() looks at + // the method-level annotations only. Compliance test POJO_8017 puts the + // Remotable annotation on the setter method's argument, so we need some special + // logic to look at the argument. + if (!forceRemotable && element.getElementType() == ElementType.PARAMETER && (element.getAnchor() instanceof Method)) { + Annotation argAnnotations[] = ((Method)element.getAnchor()).getParameterAnnotations()[0]; + for (int j = 0; j < argAnnotations.length; j++) { + if (argAnnotations[j].annotationType() == Remotable.class) { + forceRemotable = true; + break; + } + } + } + try { + JavaInterface callInterface = javaInterfaceFactory.createJavaInterface(baseType, forceRemotable); + reference.getInterfaceContract().setInterface(callInterface); + if (callInterface.getCallbackClass() != null) { + JavaInterface callbackInterface = javaInterfaceFactory.createJavaInterface(callInterface.getCallbackClass(), forceRemotable); + reference.getInterfaceContract().setCallbackInterface(callbackInterface); + } + } catch (InvalidInterfaceException e) { + throw new IntrospectionException(e); + } + return reference; + } + + + /** + * Utility methods + */ + + /** + * + * @param paramName + * @param pos + * @param name + * @return + * @throws InvalidConstructorException + */ + private static String getReferenceName(String paramName, int pos, String name) throws InvalidConstructorException { + if ("".equals(name)) { + name = paramName; + } + if ("".equals(name)) { + return "_ref" + pos; + } + if (!"".equals(paramName) && !name.equals(paramName)) { + throw new InvalidConstructorException("Mismatching names specified for reference parameter " + pos); + } else { + return name; + } + } + + /** + * + * @param ref + * @param type + * @return + */ + private static boolean removeReference(JavaElementImpl ref, JavaImplementation type) { + if (ref == null) { + return false; + } + List<org.apache.tuscany.sca.assembly.Reference> refs = type.getReferences(); + for (int i = 0; i < refs.size(); i++) { + if (refs.get(i).getName().equals(ref.getName())) { + refs.remove(i); + return true; + } + } + return false; + } + + +} |