package org.springframework.roo.addon.ws.addon;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Logger;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.springframework.roo.addon.ws.annotations.RooSei;
import org.springframework.roo.classpath.PhysicalTypeIdentifier;
import org.springframework.roo.classpath.PhysicalTypeIdentifierNamingUtils;
import org.springframework.roo.classpath.PhysicalTypeMetadata;
import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails;
import org.springframework.roo.classpath.details.MethodMetadata;
import org.springframework.roo.classpath.details.MethodMetadataBuilder;
import org.springframework.roo.classpath.details.annotations.AnnotatedJavaType;
import org.springframework.roo.classpath.details.annotations.AnnotationAttributeValue;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadata;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadataBuilder;
import org.springframework.roo.classpath.details.annotations.ArrayAttributeValue;
import org.springframework.roo.classpath.details.annotations.NestedAnnotationAttributeValue;
import org.springframework.roo.classpath.itd.AbstractItdTypeDetailsProvidingMetadataItem;
import org.springframework.roo.metadata.MetadataIdentificationUtils;
import org.springframework.roo.model.JavaPackage;
import org.springframework.roo.model.JavaSymbolName;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.model.SpringJavaType;
import org.springframework.roo.model.SpringletsJavaType;
import org.springframework.roo.project.LogicalPath;
import org.springframework.roo.support.logging.HandlerUtils;
/**
* Metadata for {@link RooSei}.
*
* @author Juan Carlos GarcĂa
* @since 2.0
*/
public class SeiMetadata extends AbstractItdTypeDetailsProvidingMetadataItem {
protected final static Logger LOGGER = HandlerUtils.getLogger(SeiMetadata.class);
private static final String PROVIDES_TYPE_STRING = SeiMetadata.class.getName();
private static final String PROVIDES_TYPE = MetadataIdentificationUtils
.create(PROVIDES_TYPE_STRING);
private final JavaPackage projectTopLevelPackage;
private final ClassOrInterfaceTypeDetails sei;
private final JavaType service;
private final List<MethodMetadata> serviceMethods;
private Map<MethodMetadata, MethodMetadata> seiMethods;
private Map<MethodMetadata, MethodMetadata> seiMethodsFromServiceMethods;
private JavaType wsdlDocumentationType = new JavaType(
"org.apache.cxf.annotations.WSDLDocumentation");
public static String createIdentifier(final JavaType javaType, final LogicalPath path) {
return PhysicalTypeIdentifierNamingUtils.createIdentifier(PROVIDES_TYPE_STRING, javaType, path);
}
public static String createIdentifier(ClassOrInterfaceTypeDetails details) {
final LogicalPath logicalPath =
PhysicalTypeIdentifier.getPath(details.getDeclaredByMetadataId());
return createIdentifier(details.getType(), logicalPath);
}
public static JavaType getJavaType(final String metadataIdentificationString) {
return PhysicalTypeIdentifierNamingUtils.getJavaType(PROVIDES_TYPE_STRING,
metadataIdentificationString);
}
public static String getMetadataIdentiferType() {
return PROVIDES_TYPE;
}
public static LogicalPath getPath(final String metadataIdentificationString) {
return PhysicalTypeIdentifierNamingUtils.getPath(PROVIDES_TYPE_STRING,
metadataIdentificationString);
}
public static boolean isValid(final String metadataIdentificationString) {
return PhysicalTypeIdentifierNamingUtils.isValid(PROVIDES_TYPE_STRING,
metadataIdentificationString);
}
/**
* Constructor
*
* @param identifier
* the identifier for this item of metadata (required)
* @param aspectName
* the Java type of the ITD (required)
* @param governorPhysicalTypeMetadata
* the governor, which is expected to contain a
* {@link ClassOrInterfaceTypeDetails} (required)
* @param projectTopLevelPackage the base package of the project that will be
* used to create the targetNamespace
* @param sei the annotated sei
* @param service the service related with this SEI
* @param serviceMethods
* the methods registered in based service
*/
public SeiMetadata(final String identifier, final JavaType aspectName,
final PhysicalTypeMetadata governorPhysicalTypeMetadata, JavaPackage projectTopLevelPackage,
ClassOrInterfaceTypeDetails sei, JavaType service, List<MethodMetadata> serviceMethods) {
super(identifier, aspectName, governorPhysicalTypeMetadata);
this.projectTopLevelPackage = projectTopLevelPackage;
this.sei = sei;
this.service = service;
this.serviceMethods = serviceMethods;
// Initialize collections
seiMethodsFromServiceMethods = new HashMap<MethodMetadata, MethodMetadata>();
seiMethods = new TreeMap<MethodMetadata, MethodMetadata>();
// Include @WebService annotation
AnnotationMetadataBuilder webServiceAnnotation =
new AnnotationMetadataBuilder(JavaType.WEB_SERVICE);
webServiceAnnotation.addStringAttribute("name", sei.getType().getSimpleTypeName());
webServiceAnnotation.addStringAttribute(
"targetNamespace",
String.format("http://ws.%s/", StringUtils.reverseDelimited(
projectTopLevelPackage.getFullyQualifiedPackageName(), '.')));
ensureGovernorIsAnnotated(webServiceAnnotation);
// Include @WSDLDocumentation annotation
AnnotationMetadataBuilder documentationAnnotation =
new AnnotationMetadataBuilder(wsdlDocumentationType);
documentationAnnotation.addStringAttribute("value", String.format(
"TODO Auto-generated documentation for %s", sei.getType().getSimpleTypeName()));
documentationAnnotation.addEnumAttribute("placement", wsdlDocumentationType,
new JavaSymbolName("Placement.TOP"));
ensureGovernorIsAnnotated(documentationAnnotation);
// Include the same methods as the provided service
for (MethodMetadata serviceMethod : serviceMethods) {
ensureGovernorHasMethod(new MethodMetadataBuilder(
getSEIMethodFromServiceMethod(serviceMethod)));
}
// Build the ITD
itdTypeDetails = builder.build();
}
/**
* This method obtains a SEI method from a provided service method.
*
* This method caches the generated methods
*
* @param serviceMethod defined in a service interface
*
* @return MethodMetadataBuilder that contains all the information about the new SEI method.
*/
private MethodMetadata getSEIMethodFromServiceMethod(MethodMetadata serviceMethod) {
// Check if already exists the method
if (seiMethodsFromServiceMethods.get(serviceMethod) != null) {
return seiMethodsFromServiceMethods.get(serviceMethod);
}
// If not exists, generate it and cache it.
// Obtain the necessary elements from service method
JavaSymbolName methodName = serviceMethod.getMethodName();
JavaType returnType = serviceMethod.getReturnType();
List<AnnotatedJavaType> parameterTypes = serviceMethod.getParameterTypes();
List<JavaSymbolName> parameterNames = serviceMethod.getParameterNames();
// Obtain parameterList
// Is necessary to change the method name to prevent errors
String paramList = "";
for (AnnotatedJavaType param : parameterTypes) {
paramList =
paramList.concat(StringUtils.capitalize(param.getJavaType().getSimpleTypeName())).concat(
"And");
}
if (StringUtils.isNotBlank(paramList)) {
// Before to update, check if is a finder
if (methodName.toString().startsWith("findBy")) {
methodName = new JavaSymbolName("find");
} else if (methodName.toString().startsWith("countBy")) {
methodName = new JavaSymbolName("count");
}
paramList = paramList.substring(0, paramList.length() - "And".length());
methodName = new JavaSymbolName(methodName.toString().concat("By").concat(paramList));
}
// Annotate parameter types with @WebParam and @XmlJavaTypeAdapter if needed
List<AnnotatedJavaType> annotatedParameterTypes = new ArrayList<AnnotatedJavaType>();
for (int i = 0; i < parameterTypes.size(); i++) {
List<AnnotationMetadata> annotations = new ArrayList<AnnotationMetadata>();
// Getting parameter type and parameter name
AnnotatedJavaType paramType = parameterTypes.get(i);
JavaSymbolName paramName = parameterNames.get(i);
// Creating @WebParam annotation
AnnotationMetadataBuilder webParamAnnotation =
new AnnotationMetadataBuilder(JavaType.WEB_PARAM);
webParamAnnotation.addStringAttribute("name", paramName.toString());
webParamAnnotation.addStringAttribute("targetNamespace", "");
annotations.add(webParamAnnotation.build());
// Creating @XmlJavaTypeAdapter annotation
AnnotationMetadataBuilder javaTypeAdapter =
new AnnotationMetadataBuilder(JavaType.XML_JAVATYPE_ADAPTER);
if (paramType.getJavaType().getFullyQualifiedTypeName()
.equals(JavaType.ITERABLE.getFullyQualifiedTypeName())) {
javaTypeAdapter.addClassAttribute("value", SpringletsJavaType.SPRINGLETS_ITERABLE_ADAPTER);
annotations.add(javaTypeAdapter.build());
} else if (paramType.getJavaType().getFullyQualifiedTypeName()
.equals(SpringJavaType.PAGE.getFullyQualifiedTypeName())) {
javaTypeAdapter.addClassAttribute("value", SpringletsJavaType.SPRINGLETS_PAGE_ADAPTER);
annotations.add(javaTypeAdapter.build());
} else if (paramType.getJavaType().equals(SpringletsJavaType.SPRINGLETS_GLOBAL_SEARCH)) {
javaTypeAdapter.addClassAttribute("value",
SpringletsJavaType.SPRINGLETS_GLOBAL_SEARCH_ADAPTER);
annotations.add(javaTypeAdapter.build());
} else if (paramType.getJavaType().equals(SpringJavaType.PAGEABLE)) {
javaTypeAdapter.addClassAttribute("value", SpringletsJavaType.SPRINGLETS_PAGEABLE_ADAPTER);
annotations.add(javaTypeAdapter.build());
}
// Creating new parameter type annotated with @WebParam
AnnotatedJavaType annotatedParam =
new AnnotatedJavaType(paramType.getJavaType(), annotations);
annotatedParameterTypes.add(annotatedParam);
}
MethodMetadataBuilder seiMethod =
new MethodMetadataBuilder(getId(), Modifier.PUBLIC + Modifier.ABSTRACT, methodName,
returnType, annotatedParameterTypes, parameterNames, null);
// Include @XmlJavaTypeAdapter annotation if needed
AnnotationMetadataBuilder javaTypeAdapter =
new AnnotationMetadataBuilder(JavaType.XML_JAVATYPE_ADAPTER);
if (returnType.getFullyQualifiedTypeName()
.equals(JavaType.ITERABLE.getFullyQualifiedTypeName())) {
javaTypeAdapter.addClassAttribute("value", SpringletsJavaType.SPRINGLETS_ITERABLE_ADAPTER);
seiMethod.addAnnotation(javaTypeAdapter);
} else if (returnType.getFullyQualifiedTypeName().equals(
SpringJavaType.PAGE.getFullyQualifiedTypeName())) {
javaTypeAdapter.addClassAttribute("value", SpringletsJavaType.SPRINGLETS_PAGE_ADAPTER);
seiMethod.addAnnotation(javaTypeAdapter);
} else if (returnType.equals(SpringletsJavaType.SPRINGLETS_GLOBAL_SEARCH)) {
javaTypeAdapter.addClassAttribute("value",
SpringletsJavaType.SPRINGLETS_GLOBAL_SEARCH_ADAPTER);
seiMethod.addAnnotation(javaTypeAdapter);
} else if (returnType.equals(SpringJavaType.PAGEABLE)) {
javaTypeAdapter.addClassAttribute("value", SpringletsJavaType.SPRINGLETS_PAGEABLE_ADAPTER);
seiMethod.addAnnotation(javaTypeAdapter);
}
// Include @RequestWrapper annotation
AnnotationMetadataBuilder requestWrapperAnnotation =
new AnnotationMetadataBuilder(JavaType.REQUEST_WRAPPER);
requestWrapperAnnotation.addStringAttribute("className", String.format("%s.%sRequest", sei
.getType().getPackage(), seiMethod.getMethodName().getSymbolNameCapitalisedFirstLetter()));
requestWrapperAnnotation
.addStringAttribute("localName", String.format("%sRequest", seiMethod.getMethodName()
.getSymbolNameCapitalisedFirstLetter()));
requestWrapperAnnotation.addStringAttribute(
"targetNamespace",
String.format("http://ws.%s/", StringUtils.reverseDelimited(
projectTopLevelPackage.getFullyQualifiedPackageName(), '.')));
seiMethod.addAnnotation(requestWrapperAnnotation);
// Include @ResponseWrapper annotation
AnnotationMetadataBuilder responseWrapperAnnotation =
new AnnotationMetadataBuilder(JavaType.RESPONSE_WRAPPER);
responseWrapperAnnotation.addStringAttribute("className", String.format("%s.%sResponse", sei
.getType().getPackage(), seiMethod.getMethodName().getSymbolNameCapitalisedFirstLetter()));
responseWrapperAnnotation.addStringAttribute("localName", String.format("%sResponse", seiMethod
.getMethodName().getSymbolNameCapitalisedFirstLetter()));
responseWrapperAnnotation.addStringAttribute(
"targetNamespace",
String.format("http://ws.%s/", StringUtils.reverseDelimited(
projectTopLevelPackage.getFullyQualifiedPackageName(), '.')));
seiMethod.addAnnotation(responseWrapperAnnotation);
// Include @WebMethod annotation
AnnotationMetadataBuilder webMethodAnnotation =
new AnnotationMetadataBuilder(JavaType.WEB_METHOD);
webMethodAnnotation.addStringAttribute("action",
String.format("urn:%s", seiMethod.getMethodName().getSymbolNameCapitalisedFirstLetter()));
seiMethod.addAnnotation(webMethodAnnotation);
// Include @WebResult annotation
AnnotationMetadataBuilder webResultAnnotation =
new AnnotationMetadataBuilder(JavaType.WEB_RESULT);
webResultAnnotation.addStringAttribute("name", returnType.getBaseType().getSimpleTypeName()
.toLowerCase());
webResultAnnotation.addStringAttribute("targetNamespace", "");
seiMethod.addAnnotation(webResultAnnotation);
// Include @WSDLDocumentationCollection annotation
AnnotationMetadataBuilder wsdlDocumentationCollectionAnnotation =
new AnnotationMetadataBuilder(new JavaType(
"org.apache.cxf.annotations.WSDLDocumentationCollection"));
// Create @WSDLDocumentation annotation
List<AnnotationAttributeValue<?>> documentations = new ArrayList<AnnotationAttributeValue<?>>();
AnnotationMetadataBuilder documentationAnnotation1 =
new AnnotationMetadataBuilder(wsdlDocumentationType);
documentationAnnotation1.addStringAttribute("value", String.format(
"TODO Auto-generated documentation for %s", sei.getType().getSimpleTypeName()));
documentationAnnotation1.addEnumAttribute("placement", wsdlDocumentationType,
new JavaSymbolName("Placement.DEFAULT"));
NestedAnnotationAttributeValue newDocumentation1 =
new NestedAnnotationAttributeValue(new JavaSymbolName("value"),
documentationAnnotation1.build());
documentations.add(newDocumentation1);
AnnotationMetadataBuilder documentationAnnotation2 =
new AnnotationMetadataBuilder(wsdlDocumentationType);
documentationAnnotation2.addStringAttribute("value", String.format(
"TODO Auto-generated documentation for %s", sei.getType().getSimpleTypeName()));
documentationAnnotation2.addEnumAttribute("placement", wsdlDocumentationType,
new JavaSymbolName("Placement.PORT_TYPE_OPERATION_OUTPUT"));
NestedAnnotationAttributeValue newDocumentation2 =
new NestedAnnotationAttributeValue(new JavaSymbolName("value"),
documentationAnnotation2.build());
documentations.add(newDocumentation2);
ArrayAttributeValue<AnnotationAttributeValue<?>> newDocumentations =
new ArrayAttributeValue<AnnotationAttributeValue<?>>(new JavaSymbolName("value"),
documentations);
wsdlDocumentationCollectionAnnotation.addAttribute(newDocumentations);
seiMethod.addAnnotation(wsdlDocumentationCollectionAnnotation);
seiMethodsFromServiceMethods.put(serviceMethod, seiMethod.build());
seiMethods.put(seiMethod.build(), serviceMethod);
return seiMethod.build();
}
@Override
public String toString() {
final ToStringBuilder builder = new ToStringBuilder(this);
builder.append("identifier", getId());
builder.append("valid", valid);
builder.append("aspectName", aspectName);
builder.append("destinationType", destination);
builder.append("governor", governorPhysicalTypeMetadata.getId());
builder.append("itdTypeDetails", itdTypeDetails);
return builder.toString();
}
public JavaType getService() {
return this.service;
}
public List<MethodMetadata> getServiceMethods() {
return this.serviceMethods;
}
public Map<MethodMetadata, MethodMetadata> getSeiMethods() {
return this.seiMethods;
}
}