package org.springframework.roo.addon.ws.addon; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; 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.RooWsEndpoints; 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.FieldMetadata; import org.springframework.roo.classpath.details.FieldMetadataBuilder; import org.springframework.roo.classpath.details.MethodMetadata; import org.springframework.roo.classpath.details.MethodMetadataBuilder; import org.springframework.roo.classpath.details.annotations.AnnotationMetadataBuilder; import org.springframework.roo.classpath.itd.AbstractItdTypeDetailsProvidingMetadataItem; import org.springframework.roo.classpath.itd.InvocableMemberBodyBuilder; import org.springframework.roo.metadata.MetadataIdentificationUtils; import org.springframework.roo.model.JavaSymbolName; import org.springframework.roo.model.JavaType; import org.springframework.roo.model.SpringJavaType; import org.springframework.roo.project.LogicalPath; import org.springframework.roo.support.logging.HandlerUtils; /** * Metadata for {@link RooWsEndpoints}. * * @author Juan Carlos GarcĂ­a * @since 2.0 */ public class WsEndpointsMetadata extends AbstractItdTypeDetailsProvidingMetadataItem { protected final static Logger LOGGER = HandlerUtils.getLogger(WsEndpointsMetadata.class); private static final String PROVIDES_TYPE_STRING = WsEndpointsMetadata.class.getName(); private static final String PROVIDES_TYPE = MetadataIdentificationUtils .create(PROVIDES_TYPE_STRING); private JavaType governor; private FieldMetadata loggerField; private final Map<JavaType, JavaType> endpointsAndSeis; private final Map<JavaType, JavaType> endpointsAndServices; private final String profile; private FieldMetadata busField; private FieldMetadata servletField; private MethodMetadata openEntityManagerInViewFilterMethod; private Map<JavaType, FieldMetadata> serviceFields; private Map<JavaType, MethodMetadata> endpointMethods; 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 endpointsAndSeis map that includes information about the endpoint * and its associated SEIs. * @param endpointsAndServices map that includes information about the endpoint * and its associated service * @param profile the provided profile */ public WsEndpointsMetadata(final String identifier, final JavaType aspectName, final PhysicalTypeMetadata governorPhysicalTypeMetadata, Map<JavaType, JavaType> endpointsAndSeis, Map<JavaType, JavaType> endpointsAndServices, String profile) { super(identifier, aspectName, governorPhysicalTypeMetadata); this.governor = governorPhysicalTypeMetadata.getType(); this.endpointsAndSeis = endpointsAndSeis; this.endpointsAndServices = endpointsAndServices; this.profile = profile; // Initializing collections serviceFields = new HashMap<JavaType, FieldMetadata>(); endpointMethods = new HashMap<JavaType, MethodMetadata>(); // Ensure that the annotated class is annotated with @Configuration ensureGovernorIsAnnotated(new AnnotationMetadataBuilder(SpringJavaType.CONFIGURATION)); // Ensure that the annotated class is annotated with @ConditionalOnWebApplication ensureGovernorIsAnnotated(new AnnotationMetadataBuilder(new JavaType( "org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication"))); // Ensure that the annotated class has LOGGER message ensureGovernorHasField(new FieldMetadataBuilder(getLoggerField())); // If developer has specify some profile, annotate class with @Profile if (StringUtils.isNotEmpty(profile)) { AnnotationMetadataBuilder profileAnnotation = new AnnotationMetadataBuilder(SpringJavaType.PROFILE); profileAnnotation.addStringAttribute("value", profile); ensureGovernorIsAnnotated(profileAnnotation); } // Include Bus field ensureGovernorHasField(new FieldMetadataBuilder(getBusField())); // Include cxfServletPath field ensureGovernorHasField(new FieldMetadataBuilder(getServletField())); // Include service @Autowire field for (Entry<JavaType, JavaType> endpointAndService : endpointsAndServices.entrySet()) { ensureGovernorHasField(new FieldMetadataBuilder( getServiceField(endpointAndService.getValue()))); ensureGovernorHasMethod(new MethodMetadataBuilder( getEndpointMethod(endpointAndService.getKey()))); } // Include openEntityManagerInViewFilter method ensureGovernorHasMethod(new MethodMetadataBuilder(getOpenEntityManagerInViewFilterMethod())); // Build the ITD itdTypeDetails = builder.build(); } /** * This method obtains the method that register the openEntityManagerInView * filter * * @return MethodMetadata with the information about the new method */ private MethodMetadata getOpenEntityManagerInViewFilterMethod() { // Check if already exists if (openEntityManagerInViewFilterMethod != null) { return openEntityManagerInViewFilterMethod; } JavaType filterRegistrationBeanType = new JavaType("org.springframework.boot.context.embedded.FilterRegistrationBean"); // Generating method body InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); // FilterRegistrationBean filterRegBean = new FilterRegistrationBean(); bodyBuilder.appendFormalLine("%s filterRegBean = new FilterRegistrationBean();", getNameOfJavaType(filterRegistrationBeanType)); // filterRegBean.setFilter(new OpenEntityManagerInViewFilter()); bodyBuilder.appendFormalLine("filterRegBean.setFilter(new %s());", getNameOfJavaType(new JavaType( "org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter"))); // List<String> urlPatterns = new ArrayList<String>(); bodyBuilder.appendFormalLine("%s<String> urlPatterns = new %s<String>();", getNameOfJavaType(JavaType.LIST), getNameOfJavaType(JavaType.ARRAY_LIST)); // urlPatterns.add(this.cxfServletPath + "/*"); bodyBuilder.appendFormalLine("urlPatterns.add(%s() + \"/*\");", getAccessorMethod(getServletField()).getMethodName()); // filterRegBean.setUrlPatterns(urlPatterns); bodyBuilder.appendFormalLine("filterRegBean.setUrlPatterns(urlPatterns);"); // if (LOG.isDebugEnabled()) { bodyBuilder.appendFormalLine("if (%s().isDebugEnabled()) {", getAccessorMethod(getLoggerField()).getMethodName()); // LOG.debug("Registering the 'OpenEntityManagerInViewFilter' filter for the '" bodyBuilder.indent(); bodyBuilder.appendFormalLine( "%s().debug(\"Registering the 'OpenEntityManagerInViewFilter' filter for the '\"", getAccessorMethod(getLoggerField()).getMethodName()); // .concat(this.cxfServletPath + "/*").concat("' URL.")); bodyBuilder.indent(); bodyBuilder.appendFormalLine(".concat(%s() + \"/*\").concat(\"' URL.\"));", getAccessorMethod(getServletField()).getMethodName()); bodyBuilder.indentRemove(); bodyBuilder.indentRemove(); // } bodyBuilder.appendFormalLine("}"); // return filterRegBean; bodyBuilder.appendFormalLine("return filterRegBean;"); MethodMetadataBuilder method = new MethodMetadataBuilder(getId(), Modifier.PUBLIC, new JavaSymbolName( "openEntityManagerInViewFilter"), filterRegistrationBeanType, bodyBuilder); method.addAnnotation(new AnnotationMetadataBuilder(SpringJavaType.BEAN)); openEntityManagerInViewFilterMethod = method.build(); return openEntityManagerInViewFilterMethod; } /** * This method obtains the method that will be used to registered the endpoint * * @param endpoint JavaType with the information about the related endpoint to * be registered * * @return MethodMetadata with the necessary information about the new method */ private MethodMetadata getEndpointMethod(JavaType endpoint) { // Check if already exists if (endpointMethods.get(endpoint) != null) { return endpointMethods.get(endpoint); } // Generating method body InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); // EndpointImpl endpoint = new EndpointImpl(this.bus, new ENDPOINT(this.SERVICENAME)); bodyBuilder.appendFormalLine("%s endpoint = new EndpointImpl(%s(), new %s(%s()));", getNameOfJavaType(new JavaType("org.apache.cxf.jaxws.EndpointImpl")), getAccessorMethod(getBusField()).getMethodName(), getNameOfJavaType(endpoint), getAccessorMethod(getServiceField(getServiceFromEndpoint(endpoint))).getMethodName()); // endpoint.setFeatures(Arrays.asList(new TraceeCxfFeature(), new LoggingFeature())); bodyBuilder.appendFormalLine("endpoint.setFeatures(%s.asList(new %s(), new %s()));", getNameOfJavaType(JavaType.ARRAYS), getNameOfJavaType(new JavaType( "io.tracee.binding.cxf.TraceeCxfFeature")), getNameOfJavaType(new JavaType( "org.apache.cxf.feature.LoggingFeature"))); // endpoint.publish("/SEI"); bodyBuilder.appendFormalLine("endpoint.publish(\"/%s\");", getSeiFromEndpoint(endpoint) .getSimpleTypeName()); // return endpoint; bodyBuilder.appendFormalLine("return endpoint;"); MethodMetadataBuilder endpointMethod = new MethodMetadataBuilder(getId(), Modifier.PUBLIC, new JavaSymbolName( StringUtils.uncapitalize(endpoint.getSimpleTypeName())), JavaType.ENDPOINT, bodyBuilder); endpointMethod.addAnnotation(new AnnotationMetadataBuilder(SpringJavaType.BEAN)); endpointMethods.put(endpoint, endpointMethod.build()); return endpointMethod.build(); } /** * This method obtains the associated service of the provided endpoint * * @param endpoint * @return */ private JavaType getServiceFromEndpoint(JavaType endpoint) { return endpointsAndServices.get(endpoint); } /** * This method obtains the associated SEI of the provided endpoint * * @param endpoint * @return */ private JavaType getSeiFromEndpoint(JavaType endpoint) { return endpointsAndSeis.get(endpoint); } /** * This method obtains the service field that will be used in some different * methods. * * @param service JavaType with the information about the service related with the new Service field * * @return FieldMetadata with the necessary information about the service */ private FieldMetadata getServiceField(JavaType service) { // Check if already exists if (serviceFields.get(service) != null) { return serviceFields.get(service); } // Create the field FieldMetadataBuilder serviceField = new FieldMetadataBuilder(getId(), Modifier.PRIVATE, new JavaSymbolName( StringUtils.uncapitalize(service.getSimpleTypeName())), service, null); serviceField.addAnnotation(new AnnotationMetadataBuilder(SpringJavaType.AUTOWIRED)); serviceFields.put(service, serviceField.build()); return serviceField.build(); } /** * This method obtains the servlet field that will be used in some different methods * * @return FieldMetadata that contains all the necessary information * about the servlet field */ private FieldMetadata getServletField() { // Check if already exists if (this.servletField != null) { return this.servletField; } // Create the field FieldMetadataBuilder servlet = new FieldMetadataBuilder(getId(), Modifier.PRIVATE, new JavaSymbolName("cxfServletPath"), JavaType.STRING, null); AnnotationMetadataBuilder valueAnnotation = new AnnotationMetadataBuilder(SpringJavaType.VALUE); valueAnnotation.addStringAttribute("value", "${cxf.path}"); servlet.addAnnotation(valueAnnotation); servletField = servlet.build(); return servletField; } /** * This method obtains the bus field that will be used in some different methods * * @return FieldMetadata that contains all the necessary information * about the bus field */ private FieldMetadata getBusField() { // Check if already exists if (this.busField != null) { return this.busField; } // Create the field FieldMetadataBuilder bus = new FieldMetadataBuilder(getId(), Modifier.PRIVATE, new JavaSymbolName("bus"), new JavaType("org.apache.cxf.Bus"), null); bus.addAnnotation(new AnnotationMetadataBuilder(SpringJavaType.AUTOWIRED)); busField = bus.build(); return busField; } /** * This method obtains the LOGGER field * * @return FieldMetadataBuilder that contians information about * the LOGGER field */ public FieldMetadata getLoggerField() { if (loggerField != null) { return loggerField; } // Create the field FieldMetadataBuilder loggger = new FieldMetadataBuilder(getId(), Modifier.PRIVATE + Modifier.STATIC + Modifier.FINAL, new JavaSymbolName("LOGGER"), new JavaType("org.slf4j.Logger"), String.format( "%s.getLogger(%s.class)", getNameOfJavaType(new JavaType("org.slf4j.LoggerFactory")), getNameOfJavaType(this.governor))); this.loggerField = loggger.build(); return loggerField; } @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 String getProfile() { return profile; } public Map<JavaType, JavaType> getEndpointsAndServices() { return endpointsAndServices; } public Map<JavaType, JavaType> getEndpointsAndSeis() { return endpointsAndSeis; } }