/* * 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.Reference; /** * 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(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(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(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 element * @param name * @return * @throws IntrospectionException */ private org.apache.tuscany.sca.assembly.Reference createReference(JavaElementImpl element, String name) throws IntrospectionException { org.apache.tuscany.sca.assembly.Reference reference = assemblyFactory.createReference(); JavaInterfaceContract interfaceContract = javaInterfaceFactory.createJavaInterfaceContract(); reference.setInterfaceContract(interfaceContract); // 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); } try { JavaInterface callInterface = javaInterfaceFactory.createJavaInterface(baseType); reference.getInterfaceContract().setInterface(callInterface); if (callInterface.getCallbackClass() != null) { JavaInterface callbackInterface = javaInterfaceFactory.createJavaInterface(callInterface.getCallbackClass()); 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; } }