/*******************************************************************************
* Copyright (c) 2006-2010 eBay Inc. All Rights Reserved.
* Licensed 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
*******************************************************************************/
package org.ebayopensource.turmeric.plugins.maven;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.InputStream;
import java.util.Properties;
import org.apache.commons.io.IOUtils;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.StringUtils;
import org.ebayopensource.turmeric.plugins.maven.resources.ResourceLocator;
import org.ebayopensource.turmeric.plugins.maven.resources.ResourceLocator.Location;
import org.ebayopensource.turmeric.plugins.maven.utils.CodegenCommands;
import org.ebayopensource.turmeric.plugins.maven.utils.LegacyProperties;
import org.ebayopensource.turmeric.tools.codegen.InputOptions;
import org.ebayopensource.turmeric.tools.codegen.util.CodeGenConstants;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
/**
* Perform servicegen on an implementation.
*
* @goal gen-implementation
* @phase generate-sources
* @requiresDependencyResolution compile
* @requiresProject true
*/
public class GenImplMojo extends AbstractTurmericCodegenMojo {
private static final String LEGACY_PROP_REF = "${project.basedir}/service_impl_project.properties";
/**
* Service Name
*
* @parameter expression="${codegen.serviceName}" default-value="${project.artifactId}"
* @required
*/
protected String serviceName = "${project.artifactId}";
/**
* WSDL Path Reference.
* <p>
* Use syntax similar to how {@link ClassLoader#getResource(String)} expects.
* <p>
* Actual WSDL File location is looked up via {@link ResourceLocator#findResource(String)}
*
* @parameter expression="${codegen.wsdl.path}"
* default-value="META-INF/soa/services/wsdl/$${mojo.serviceName}/$${mojo.serviceName}.wsdl"
* @optional
*/
protected String wsdlPathRef = "META-INF/soa/services/wsdl/${mojo.serviceName}/${mojo.serviceName}.wsdl";
/**
* CodeGen Type.
*
* @parameter expression="${codegen.generator.type}" default-value="DispatcherForMaven"
* @required
*/
protected String genType = "DispatcherForMaven";
/**
* Service Operations Path Reference.
* <p>
* Use syntax similar to how {@link ClassLoader#getResource(String)} expects.
* <p>
* Actual Service Operations File location is looked up via {@link ResourceLocator#findResource(String)}
*
* @parameter expression="${codegen.service.operations.path}" default-value=
* "META-INF/soa/services/config/$${mojo.serviceName}/service_operations.properties"
* @required
*/
private String serviceOperationsPathRef = "META-INF/soa/services/config/${mojo.serviceName}/service_operations.properties";
/**
* Service Metadata Path Reference.
* <p>
* Use syntax similar to how {@link ClassLoader#getResource(String)} expects.
* <p>
* Actual Service Metadata File location is looked up via {@link ResourceLocator#findResource(String)}
*
* @parameter expression="${codegen.service.metadata.path}" default-value=
* "META-INF/soa/common/config/$${mojo.serviceName}/service_metadata.properties"
* @required
*/
private String serviceMetadataPathRef = "META-INF/soa/common/config/${mojo.serviceName}/service_metadata.properties";
/**
* Service Config Path Reference.
* <p>
* Use syntax similar to how {@link ClassLoader#getResource(String)} expects.
* <p>
* Actual Service Operations File location is looked up via {@link ResourceLocator#findResource(String)}
*
* @parameter expression="${codegen.service.config.path}"
* default-value="META-INF/soa/services/config/$${mojo.serviceName}/ServiceConfig.xml"
* @required
*/
private String serviceConfigPathRef = "META-INF/soa/services/config/${mojo.serviceName}/ServiceConfig.xml";
/**
* Service Group ID
*
* @parameter expression="${codegen.service.groupId}" default-value="${project.groupId}"
* @required
*/
private String serviceGroupId = "${project.groupId}";
/**
* Use the service-impl-class-name from the {@link #serviceConfigPathRef} as the name of the generated classname.
*
* @parameter expression="${codegen.classname.from.config}" default-value="true"
* @optional
*/
private boolean useClassnameFromConfig = true;
/**
* A {@link Location} for the wsdl file referenced in {@link #wsdlPathRef}
*/
private Location wsdlLocation;
/**
* A {@link Location} for the service operations file referenced in {@link #serviceOperationsPathRef}
*/
private Location serviceOperationsLocation;
/**
* A {@link Location} for the service metadata file referenced in {@link #serviceMetadataPathRef}
*/
private Location serviceMetadataLocation;
/**
* A {@link Location} for the service config file referenced in {@link #serviceConfigPathRef}
*/
private Location serviceConfigLocation;
private ServiceConfigDetails serviceConfig;
static class ServiceConfigDetails {
String serviceImplClassName;
String serviceInterfaceClassName;
}
@Override
protected String getGoalName() {
return "gen-implementation";
}
@Override
protected void addCodegenCommands(CodegenCommands commands) throws MojoExecutionException, MojoFailureException {
getLog().info("Processing Impl Project: " + getProject().getId());
commands.setServiceName(serviceName);
commands.add(InputOptions.OPT_ADMIN_NAME, serviceName);
commands.add(InputOptions.OPT_INTERFACE, serviceConfig.serviceInterfaceClassName);
if (isLegacyMode()) {
commands.add(InputOptions.OPT_PROJECT_ROOT, getProject().getBasedir().getAbsolutePath());
if (serviceConfig.serviceImplClassName == null) {
throw new MojoExecutionException("Can not read implementation classname from: "
+ serviceConfigLocation);
}
commands.add(InputOptions.OPT_SVC_IMPL_CLASS_NAME, serviceConfig.serviceImplClassName.trim());
}
else {
// From here down is standard mode
if (useClassnameFromConfig) {
if (serviceConfig.serviceImplClassName == null) {
throw new MojoExecutionException("Can not read implementation classname from: "
+ serviceConfigLocation);
}
commands.add(InputOptions.OPT_SVC_IMPL_CLASS_NAME, serviceConfig.serviceImplClassName.trim());
}
commands.add(InputOptions.OPT_GEN_INTERFACE_PACKAGE, serviceGroupId);
if ((serviceMetadataLocation != null) && (serviceMetadataLocation.isLocalToProject())) {
commands.addSingle(InputOptions.OPT_USE_INTERFACE_JAR);
}
}
}
@Override
public String getGenType() {
return genType;
}
public String getServiceName() {
return serviceName;
}
public String getServiceGroupId() {
return serviceGroupId;
}
@Override
public boolean needsGeneration() throws MojoExecutionException {
if (super.needsGeneration()) {
return true;
}
if (isNewerThanLastTimestamp(wsdlLocation, serviceOperationsLocation, serviceMetadataLocation)) {
getLog().info("Must Generate: source files modified recently");
return true;
}
getLog().info("Service WSDL has not been updated recently.");
return false;
}
@Override
protected void onValidateParameters() throws MojoExecutionException, MojoFailureException {
super.onValidateParameters();
// Valid service name
if (StringUtils.isBlank(serviceName)) {
throw new MojoExecutionException("serviceName not specified");
}
genType = expandParameter(genType);
if (isLegacyMode()) {
LegacyProperties props = getLegacyProperties(LEGACY_PROP_REF);
serviceName = props.getProperty("serviceName", serviceName);
serviceGroupId = props.getProperty("serviceGroupID", serviceGroupId);
}
else {
serviceName = expandParameter(serviceName);
serviceGroupId = expandParameter(serviceGroupId);
}
serviceConfigPathRef = expandParameter(serviceConfigPathRef);
serviceOperationsPathRef = expandParameter(serviceOperationsPathRef);
serviceMetadataPathRef = expandParameter(serviceMetadataPathRef);
wsdlPathRef = expandParameter(wsdlPathRef);
ResourceLocator locator = new ResourceLocator(getLog(), getProject());
wsdlLocation = locator.findResource(wsdlPathRef);
serviceConfigLocation = locator.findResource(serviceConfigPathRef);
serviceOperationsLocation = locator.findResource(serviceOperationsPathRef);
serviceMetadataLocation = locator.findResource(serviceMetadataPathRef);
getLog().debug("wsdlLocation: " + wsdlLocation);
getLog().debug("serviceConfigLocation: " + serviceConfigLocation);
getLog().debug("serviceOperationsLocation: " + serviceOperationsLocation);
getLog().debug("serviceMetadataLocation: " + serviceMetadataLocation);
// Validate Required Files
if (wsdlLocation == null) {
throw new MojoExecutionException("No service <wsdlPathRef/> file"
+ " found among project resources and dependencies: " + wsdlPathRef);
}
if (serviceMetadataLocation == null) {
throw new MojoExecutionException("No <serviceMetadataPathRef/>"
+ " file found among project resources and dependencies: " + serviceMetadataPathRef);
}
if (serviceConfigLocation == null) {
throw new MojoExecutionException("No <serviceConfigPathRef/>"
+ " file found among project resources and dependencies: " + serviceConfigPathRef);
}
if (serviceOperationsLocation == null) {
getLog().warn("No <serviceOperationsPathRef/>" + " file found among project resources and dependencies: "
+ serviceOperationsPathRef);
}
serviceConfig = readServiceConfig(serviceConfigLocation);
// Sanity Check: verify information from Interface project
Properties svcMetaProps = loadProperties(serviceMetadataLocation);
String serviceInterfaceClassName = svcMetaProps.getProperty(CodeGenConstants.SERVICE_INTF_CLASS_NAME);
if (StringUtils.isBlank(serviceInterfaceClassName)) {
throw new MojoExecutionException("Unable to find service interface class name ("
+ CodeGenConstants.SERVICE_INTF_CLASS_NAME + " in <serviceMetadataPathRef/> file: "
+ serviceMetadataLocation.getUri().toASCIIString());
}
if (!StringUtils.equals(serviceConfig.serviceInterfaceClassName, serviceInterfaceClassName)) {
StringBuilder err = new StringBuilder();
err.append("ERROR: service-interface-class-name mismatch:");
err.append("\n The <service-interface-class-name> from ");
err.append(serviceConfigLocation.getUri().toASCIIString());
err.append("\n * ").append(serviceConfig.serviceInterfaceClassName);
err.append("\n And \"").append(CodeGenConstants.SERVICE_INTF_CLASS_NAME).append("\" value from ");
err.append(serviceMetadataLocation.getUri().toASCIIString());
err.append("\n * ").append(serviceInterfaceClassName);
err.append("\n Do not match.");
throw new MojoExecutionException(err.toString());
}
getLog().info("Service Interface Class: " + serviceConfig.serviceInterfaceClassName);
getLog().info("Service Impl Class : " + serviceConfig.serviceImplClassName);
}
private Properties loadProperties(Location location) throws MojoExecutionException {
return loadProperties(location.getFile());
}
private Properties loadProperties(File file) throws MojoExecutionException {
Properties props = new Properties();
FileReader reader = null;
try {
reader = new FileReader(file);
props.load(reader);
return props;
}
catch (Exception e) {
throw new MojoExecutionException("Unable to read properties file: " + file, e);
}
finally {
IOUtils.closeQuietly(reader);
}
}
private ServiceConfigDetails readServiceConfig(Location serviceConfig) throws MojoExecutionException {
InputStream io = null;
try {
ServiceConfigDetails scd = new ServiceConfigDetails();
io = new FileInputStream(serviceConfig.getFile());
final Document document = new SAXBuilder().build(io);
for (Object obj : document.getRootElement().getChildren()) {
Element elem = (Element) obj;
if (elem.getName().equals("service-impl-class-name")) {
scd.serviceImplClassName = elem.getValue().trim();
continue;
}
if (elem.getName().equals("service-interface-class-name")) {
scd.serviceInterfaceClassName = elem.getValue().trim();
continue;
}
}
return scd;
}
catch (Exception e) {
throw new MojoExecutionException("Unable to read service config file: " + serviceConfig.getFile(), e);
}
finally {
IOUtil.close(io);
}
}
}