org.codehaus.enunciate.modules.amf.AMFDeploymentModule Maven / Gradle / Ivy
/*
* Copyright 2006-2008 Web Cohesion
*
* 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
*
* 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.codehaus.enunciate.modules.amf;
import com.sun.mirror.declaration.Declaration;
import com.sun.mirror.declaration.TypeDeclaration;
import flex.messaging.MessageBrokerServlet;
import freemarker.template.*;
import net.sf.jelly.apt.decorations.JavaDoc;
import net.sf.jelly.apt.freemarker.FreemarkerJavaDoc;
import org.apache.commons.digester.RuleSet;
import org.codehaus.enunciate.EnunciateException;
import org.codehaus.enunciate.apt.EnunciateFreemarkerModel;
import org.codehaus.enunciate.config.SchemaInfo;
import org.codehaus.enunciate.config.WsdlInfo;
import org.codehaus.enunciate.contract.jaxb.TypeDefinition;
import org.codehaus.enunciate.contract.jaxws.EndpointInterface;
import org.codehaus.enunciate.contract.validation.Validator;
import org.codehaus.enunciate.main.*;
import org.codehaus.enunciate.main.webapp.BaseWebAppFragment;
import org.codehaus.enunciate.main.webapp.WebAppComponent;
import org.codehaus.enunciate.modules.FreemarkerDeploymentModule;
import org.codehaus.enunciate.modules.ProjectExtensionModule;
import org.codehaus.enunciate.modules.FlexHomeAwareModule;
import org.codehaus.enunciate.modules.amf.config.AMFRuleSet;
import org.codehaus.enunciate.modules.amf.config.FlexApp;
import org.codehaus.enunciate.modules.amf.config.FlexCompilerConfig;
import org.codehaus.enunciate.modules.amf.config.License;
import org.codehaus.enunciate.template.freemarker.AccessorOverridesAnotherMethod;
import org.codehaus.enunciate.template.freemarker.ClientPackageForMethod;
import org.codehaus.enunciate.template.freemarker.ComponentTypeForMethod;
import org.codehaus.enunciate.template.freemarker.SimpleNameWithParamsMethod;
import java.io.*;
import java.net.URL;
import java.util.*;
/**
* AMF Module
*
* The AMF deployment module generates the server-side and client-side libraries used to support an
* Action Message Format API. The client-side
* library is a set of ActionScript classes that are
* type-safe wrappers around the ActionScript remoting API that are designed to add clarity and to be easy
* to consume for Flex development. Furthermore, the server-side support classes add an extra degree of security to
* your Data Services by ensuring that only your public methods are made available for invocation via AMF. There is
* also support for invoking the Adobe Flex compiler to compile
* a set of Flash applications that can be added to your
* Enunciate-generated web application.
*
* The AMF API leverages the Blaze DS package that was recently made
* available as an open source product by Adobe. To use the AMF module, you will have to have the
* Flex SDK installed.
*
* This documentation is an overview of how to use Enunciate to build your Flex Data Services (AMF API) and associated Flash
* application(s). The reader is redirected to the documentation for Flex for
* instructions on how to use Flex. There are also two sample applications you may find useful, petclinic and addressbook, that you
* will find bundled with the Enunciate distribution.
*
*
* - steps
* - configuration
* - artifacts
*
*
* Steps
*
* generate
*
* The "generate" step generates all source code for the AMF API. This includes server-side support classes and client-side
* ActionScript classes that can be used to access the API via AMF.
*
* compile
*
* During the "compile" step, the AMF module compiles the code that was generated. The generated client-side ActionScript
* classes are compiled into an SWC file that is made available as an Enunciate artifact. The SWC file can also be made
* available as a download from the deployed web application (see the configuration). It is also during the "compile" step that
* the Flex compiler is invoked on any Flex applications that are specified in the configuration.
*
* Configuration
*
* The AMF module is configured by the "amf" element under the "modules" element of the enunciate configuration file. The
* AMF module is disabled by default because of the added constraints applied to the service endpoints and because of the additional
* dependencies required by the module. To enable AMF, be sure to specify disabled="false" on the "amf" element.
*
* The "amf" element supports the following attributes:
*
*
* - The "flexHome" attribute must be supplied. It is the path to the directory where the Flex SDK is installed.
* - The "swcName" attribute specifies the name of the compiled SWC. By default, the name is determined by the Enunciate
* project label (see the main configuration docs).
* - The "swcDownloadable" attribute specifies whether the generated SWC is to be made available as a download from the
* generated web application. Default: "false".
* - The "asSourcesDownloadable" attribute specifies whether the generated ActionScript source files are downloadable from
* generated web application. Default: "false".
*
*
* The "war" element
*
* The "war" element under the "amf" element is used to configure the webapp that will host the AMF endpoints and Flex applications. It supports
* the following attributes:
*
*
* - The "amfSubcontext" attribute is the subcontext at which the amf endpoints will be mounted. Default: "/amf".
* - The "flexAppDir" attribute is the directory in the war to which the flex applications will be put. The default is the root of the war.
*
*
* The "compiler" element
*
* The "compiler" element under the "amf" element is used to configure the compiler that will be used to compile the SWC
* and the Flex applications. It supports the following attributes, associated directly to the Flex compiler options. For details,
* see the documentation for the Flex compiler.
*
*
* - contextRoot (default: the Enunciate project label)
* - flexConfig (default: "$FLEX_SDK_HOME/frameworks/flex-config.xml")
* - locale (default: unspecified)
* - optimize (boolean, default: unspecified)
* - debug (boolean, default: unspecified)
* - profile (boolean, default: unspecified)
* - strict (boolean, default: unspecified)
* - useNetwork (boolean, default: unspecified)
* - incremental (boolean, default: unspecified)
* - warnings (boolean, default: unspecified)
* - showActionscriptWarnings (boolean, default: unspecified)
* - showBindingWarnings (boolean, default: unspecified)
* - showDeprecationWarnings (boolean, default: unspecified)
* - flexCompileCommand (default "flex2.tools.Compiler")
* - swcCompileCommand (default "flex2.tools.Compc")
*
*
* The "compiler" element also supports the following subelements:
*
*
* - "JVMArg" (additional JVM arguments, passed in order to the JVM used to invoke the compiler, supports a single attribute: "value")
* - "arg" (additional compiler arguments, passed in order to the compiler, supports a single attribute: "value")
* - "license" (supports attributes "product" and "serialNumber")
*
*
* The "app" element
*
* The AMF module supports the development of Flex apps that can be compiled and packaged with the generated Enunciate app.
* The "app" element supports the folowing attributes:
*
*
* - The "name" attribute is the name of the Flex app. This attribute is required.
* - The "srcDir" attribute specifies the source directory for the application. This attribute is required.
* - The "mainMxmlFile" attribute specifies the main mxml file for the app. This attribute is required. The path to this file is resolved
* relative to the enunciate.xml file (not to the "srcDir" attribute of the app).
* - The "outputPath" attribute specified the output directory for the application, relative to the "flexAppDir".
*
*
* Example Configuration
*
* As an example, consider the following configuration:
*
*
* <enunciate>
* <modules>
* <amf disabled="false" swcName="mycompany-amf.swc"
* flexHome="/home/myusername/tools/flex-sdk-2">
* <app srcDir="src/main/flexapp" name="main" mainMxmlFile="src/main/flexapp/com/mycompany/main.mxml"/>
* <app srcDir="src/main/anotherapp" name="another" mainMxmlFile="src/main/anotherapp/com/mycompany/another.mxml"/>
* ...
* </amf>
* </modules>
* </enunciate>
*
*
* This configuration enables the AMF module and gives a specific name to the compiled SWC for the client-side ActionScript classes.
*
* There also two Flex applications defined. The first is located at "src/main/flexapp". The name of this app is "main". The MXML
* file that defines this app sits at "src/main/flexapp/com/mycompany/main.mxml", relative to the enunciate configuration file. The
* second application, rooted at "src/main/anotherapp", is named "another". The mxml file that defines this application sits at
* "src/main/anotherapp/com/mycompany/another.mxml".
*
* After the "compile" step of the AMF module, assuming everything compiles correctly, there will be two Flash applications, "main.swf" and "another.swf",
* that sit in the applications directory (see "artifacts" below).
*
* For a less contrived example, see the "petclinic" sample Enunciate project bundled with the Enunciate distribution.
*
* Artifacts
*
*
* - The "amf.client.src.dir" artifact is the directory where the client-side source code is generated.
* - The "amf.server.src.dir" artifact is the directory where the server-side source code is generated.
* - The "as3.client.swc" artifact is the packaged client-side ActionScript SWC.
* - The "flex.app.dir" artifact is the directory to which the Flex apps are compiled.
*
*
* @author Ryan Heaton
* @docFileName module_amf.html
*/
public class AMFDeploymentModule extends FreemarkerDeploymentModule implements ProjectExtensionModule, FlexHomeAwareModule {
private String amfSubcontext = "/amf/";
private String flexAppDir = null;
private final List flexApps = new ArrayList();
private final AMFRuleSet configurationRules = new AMFRuleSet();
private String flexHome = System.getProperty("flex.home") == null ? System.getenv("FLEX_HOME") : System.getProperty("flex.home");
private FlexCompilerConfig compilerConfig = new FlexCompilerConfig();
private String swcName;
private boolean swcDownloadable = false;
private boolean asSourcesDownloadable = false;
public AMFDeploymentModule() {
setDisabled(true);//disable the AMF module by default because it adds unnecessary contraints on the API.
}
/**
* @return "amf"
*/
@Override
public String getName() {
return "amf";
}
@Override
public void init(Enunciate enunciate) throws EnunciateException {
super.init(enunciate);
if (!isDisabled()) {
if (this.flexHome == null && (isSwcDownloadable() || !flexApps.isEmpty())) {
throw new EnunciateException("To compile a flex app you must specify the Flex SDK home directory, either in configuration, by setting the FLEX_HOME environment variable, or setting the 'flex.home' system property.");
}
for (FlexApp flexApp : flexApps) {
if (flexApp.getName() == null) {
throw new EnunciateException("A flex app must have a name.");
}
String srcPath = flexApp.getSrcDir();
if (srcPath == null) {
throw new EnunciateException("A source directory for the flex app '" + flexApp.getName() + "' must be supplied with the 'srcDir' attribute.");
}
File srcDir = enunciate.resolvePath(srcPath);
if (!srcDir.exists()) {
throw new EnunciateException("Source directory for the flex app '" + flexApp.getName() + "' doesn't exist.");
}
}
}
}
@Override
public void doFreemarkerGenerate() throws IOException, TemplateException, EnunciateException {
File serverGenerateDir = getServerSideGenerateDir();
File clientGenerateDir = getClientSideGenerateDir();
File xmlGenerateDir = getXMLGenerateDir();
Enunciate enunciate = getEnunciate();
if (!enunciate.isUpToDateWithSources(serverGenerateDir) ||
!enunciate.isUpToDateWithSources(clientGenerateDir) ||
!enunciate.isUpToDateWithSources(xmlGenerateDir)) {
//load the references to the templates....
URL amfEndpointTemplate = getTemplateURL("amf-endpoint.fmt");
URL amfTypeTemplate = getTemplateURL("amf-type.fmt");
URL amfTypeMapperTemplate = getTemplateURL("amf-type-mapper.fmt");
EnunciateFreemarkerModel model = getModel();
model.setFileOutputDirectory(serverGenerateDir);
TreeMap packages = new TreeMap(new Comparator() {
public int compare(String package1, String package2) {
int comparison = package1.length() - package2.length();
if (comparison == 0) {
return package1.compareTo(package2);
}
return comparison;
}
});
for (SchemaInfo schemaInfo : model.getNamespacesToSchemas().values()) {
for (TypeDefinition typeDefinition : schemaInfo.getTypeDefinitions()) {
if (!isAMFTransient(typeDefinition)) {
String packageName = typeDefinition.getPackage().getQualifiedName();
packages.put(packageName, packageName + ".amf");
}
}
}
debug("Generating the AMF externalizable types and their associated mappers...");
AMFClassnameForMethod amfClassnameForMethod = new AMFClassnameForMethod(packages);
model.put("simpleNameFor", new SimpleNameWithParamsMethod(amfClassnameForMethod));
model.put("classnameFor", amfClassnameForMethod);
for (SchemaInfo schemaInfo : model.getNamespacesToSchemas().values()) {
for (TypeDefinition typeDefinition : schemaInfo.getTypeDefinitions()) {
if (!isAMFTransient(typeDefinition)) {
model.put("type", typeDefinition);
processTemplate(amfTypeTemplate, model);
processTemplate(amfTypeMapperTemplate, model);
}
}
}
debug("Generating the AMF endpoint beans...");
for (WsdlInfo wsdlInfo : model.getNamespacesToWSDLs().values()) {
for (EndpointInterface ei : wsdlInfo.getEndpointInterfaces()) {
if (!isAMFTransient(ei)) {
model.put("endpointInterface", ei);
processTemplate(amfEndpointTemplate, model);
}
}
}
URL endpointTemplate = getTemplateURL("as3-endpoint.fmt");
URL typeTemplate = getTemplateURL("as3-type.fmt");
URL enumTypeTemplate = getTemplateURL("as3-enum-type.fmt");
//build up the list of as3Aliases...
HashMap as3Aliases = new HashMap();
for (SchemaInfo schemaInfo : model.getNamespacesToSchemas().values()) {
for (TypeDefinition typeDefinition : schemaInfo.getTypeDefinitions()) {
if (!isAMFTransient(typeDefinition)) {
as3Aliases.put(amfClassnameForMethod.convert(typeDefinition), typeDefinition.getClientSimpleName());
}
}
}
model.setFileOutputDirectory(clientGenerateDir);
HashMap conversions = new HashMap();
//todo: accept client-side package mappings?
ClientPackageForMethod as3PackageFor = new ClientPackageForMethod(conversions);
as3PackageFor.setUseClientNameConversions(true);
model.put("packageFor", as3PackageFor);
AS3UnqualifiedClassnameForMethod as3ClassnameFor = new AS3UnqualifiedClassnameForMethod(conversions);
as3ClassnameFor.setUseClientNameConversions(true);
model.put("classnameFor", as3ClassnameFor);
model.put("simpleNameFor", new SimpleNameWithParamsMethod(as3ClassnameFor));
ComponentTypeForMethod as3ComponentTypeFor = new ComponentTypeForMethod(conversions);
as3ComponentTypeFor.setUseClientNameConversions(true);
model.put("componentTypeFor", as3ComponentTypeFor);
model.put("amfClassnameFor", amfClassnameForMethod);
model.put("amfComponentTypeFor", new ComponentTypeForMethod(packages));
model.put("forEachAS3Import", new ForEachAS3ImportTransform(null, as3ClassnameFor));
model.put("accessorOverridesAnother", new AccessorOverridesAnotherMethod());
model.put("as3Aliases", as3Aliases);
debug("Generating the ActionScript types...");
for (SchemaInfo schemaInfo : model.getNamespacesToSchemas().values()) {
for (TypeDefinition typeDefinition : schemaInfo.getTypeDefinitions()) {
if (!isAMFTransient(typeDefinition)) {
model.put("type", typeDefinition);
URL template = typeDefinition.isEnum() ? enumTypeTemplate : typeTemplate;
processTemplate(template, model);
}
}
}
for (WsdlInfo wsdlInfo : model.getNamespacesToWSDLs().values()) {
for (EndpointInterface ei : wsdlInfo.getEndpointInterfaces()) {
if (!isAMFTransient(ei)) {
model.put("endpointInterface", ei);
processTemplate(endpointTemplate, model);
}
}
}
URL servicesConfigTemplate = getTemplateURL("services-config-xml.fmt");
model.setFileOutputDirectory(xmlGenerateDir);
debug("Generating the configuration files.");
processTemplate(servicesConfigTemplate, model);
}
else {
info("Skipping generation of AMF support as everything appears up-to-date...");
}
this.enunciate.addArtifact(new FileArtifact(getName(), "amf.client.src.dir", clientGenerateDir));
this.enunciate.addArtifact(new FileArtifact(getName(), "amf.server.src.dir", serverGenerateDir));
this.enunciate.addAdditionalSourceRoot(serverGenerateDir);
}
/**
* Invokes the flex compiler on the apps specified in the configuration file.
*/
protected void doFlexCompile() throws EnunciateException, IOException {
File swcFile = null;
File asSources = null;
Enunciate enunciate = getEnunciate();
if (isSwcDownloadable() || !flexApps.isEmpty()) {
if (this.flexHome == null) {
throw new EnunciateException("To compile a flex app you must specify the Flex SDK home directory, either in configuration, by setting the FLEX_HOME environment variable, or setting the 'flex.home' system property.");
}
File flexHomeDir = new File(this.flexHome);
if (!flexHomeDir.exists()) {
throw new EnunciateException("Flex home not found ('" + flexHomeDir.getAbsolutePath() + "').");
}
File javaBinDir = new File(System.getProperty("java.home"), "bin");
File javaExecutable = new File(javaBinDir, "java");
if (!javaExecutable.exists()) {
//append the "exe" for windows users.
javaExecutable = new File(javaBinDir, "java.exe");
}
String javaCommand = javaExecutable.getAbsolutePath();
if (!javaExecutable.exists()) {
warn("No java executable found in %s. We'll just hope the environment is set up to execute 'java'...", javaBinDir.getAbsolutePath());
javaCommand = "java";
}
int compileCommandIndex;
int outputFileIndex;
int sourcePathIndex;
int mainMxmlPathIndex;
List commandLine = new ArrayList();
int argIndex = 0;
commandLine.add(argIndex++, javaCommand);
for (String jvmarg : this.compilerConfig.getJVMArgs()) {
commandLine.add(argIndex++, jvmarg);
}
commandLine.add(argIndex++, "-cp");
File flexHomeLib = new File(flexHomeDir, "lib");
if (!flexHomeLib.exists()) {
throw new EnunciateException("File not found: " + flexHomeLib);
}
else {
StringBuilder builder = new StringBuilder();
Iterator flexLibIt = Arrays.asList(flexHomeLib.listFiles()).iterator();
while (flexLibIt.hasNext()) {
File flexJar = flexLibIt.next();
if (flexJar.getAbsolutePath().endsWith("jar")) {
builder.append(flexJar.getAbsolutePath());
if (flexLibIt.hasNext()) {
builder.append(File.pathSeparatorChar);
}
}
else {
debug("File %s will not be included on the classpath because it's not a jar.", flexJar);
}
}
commandLine.add(argIndex++, builder.toString());
}
compileCommandIndex = argIndex;
commandLine.add(argIndex++, null);
commandLine.add(argIndex++, "-output");
outputFileIndex = argIndex;
commandLine.add(argIndex++, null);
if (compilerConfig.getFlexConfig() == null) {
compilerConfig.setFlexConfig(new File(new File(flexHome, "frameworks"), "flex-config.xml"));
}
if (compilerConfig.getFlexConfig().exists()) {
commandLine.add(argIndex++, "-load-config");
commandLine.add(argIndex++, compilerConfig.getFlexConfig().getAbsolutePath());
}
else {
warn("Configured flex configuration file %s doesn't exist. Ignoring...", compilerConfig.getFlexConfig());
}
if (compilerConfig.getContextRoot() == null) {
if (getEnunciate().getConfig().getLabel() != null) {
compilerConfig.setContextRoot("/" + getEnunciate().getConfig().getLabel());
}
else {
compilerConfig.setContextRoot("/enunciate");
}
}
commandLine.add(argIndex++, "-compiler.context-root");
commandLine.add(argIndex++, compilerConfig.getContextRoot());
if (compilerConfig.getLocale() != null) {
commandLine.add(argIndex++, "-compiler.locale");
commandLine.add(argIndex++, compilerConfig.getLocale());
}
if (compilerConfig.getLicenses().size() > 0) {
commandLine.add(argIndex++, "-licenses.license");
for (License license : compilerConfig.getLicenses()) {
commandLine.add(argIndex++, license.getProduct());
commandLine.add(argIndex++, license.getSerialNumber());
}
}
if (compilerConfig.getOptimize() != null && compilerConfig.getOptimize()) {
commandLine.add(argIndex++, "-compiler.optimize");
}
if (compilerConfig.getDebug() != null && compilerConfig.getDebug()) {
commandLine.add(argIndex++, "-compiler.debug=true");
}
if (compilerConfig.getStrict() != null && compilerConfig.getStrict()) {
commandLine.add(argIndex++, "-compiler.strict");
}
if (compilerConfig.getUseNetwork() != null && compilerConfig.getUseNetwork()) {
commandLine.add(argIndex++, "-use-network");
}
if (compilerConfig.getIncremental() != null && compilerConfig.getIncremental()) {
commandLine.add(argIndex++, "-compiler.incremental");
}
if (compilerConfig.getShowActionscriptWarnings() != null && compilerConfig.getShowActionscriptWarnings()) {
commandLine.add(argIndex++, "-show-actionscript-warnings");
}
if (compilerConfig.getShowBindingWarnings() != null && compilerConfig.getShowBindingWarnings()) {
commandLine.add(argIndex++, "-show-binding-warnings");
}
if (compilerConfig.getShowDeprecationWarnings() != null && compilerConfig.getShowDeprecationWarnings()) {
commandLine.add(argIndex++, "-show-deprecation-warnings");
}
for (String arg : this.compilerConfig.getArgs()) {
commandLine.add(argIndex++, arg);
}
commandLine.add(argIndex++, "-compiler.services");
File xmlGenerateDir = getXMLGenerateDir();
commandLine.add(argIndex++, new File(xmlGenerateDir, "services-config.xml").getAbsolutePath());
commandLine.add(argIndex, "-include-sources");
File clientSideGenerateDir = getClientSideGenerateDir();
commandLine.add(argIndex + 1, clientSideGenerateDir.getAbsolutePath());
String swcName = getSwcName();
if (swcName == null) {
String label = "enunciate";
if ((enunciate.getConfig() != null) && (enunciate.getConfig().getLabel() != null)) {
label = enunciate.getConfig().getLabel();
}
swcName = label + "-as3-client.swc";
}
File swcCompileDir = getSwcCompileDir();
swcFile = new File(swcCompileDir, swcName);
boolean swcUpToDate = swcFile.exists() &&
enunciate.isUpToDate(xmlGenerateDir, swcCompileDir) &&
enunciate.isUpToDate(clientSideGenerateDir, swcCompileDir);
if (!swcUpToDate) {
commandLine.set(compileCommandIndex, compilerConfig.getSwcCompileCommand());
commandLine.set(outputFileIndex, swcFile.getAbsolutePath());
debug("Compiling %s for the client-side ActionScript classes...", swcFile.getAbsolutePath());
if (enunciate.isDebug()) {
StringBuilder command = new StringBuilder();
for (String commandPiece : commandLine) {
command.append(' ').append(commandPiece);
}
debug("Executing SWC compile for client-side actionscript with the command: %s", command);
}
compileSwc(commandLine);
}
else {
info("Skipping compilation of %s as everything appears up-to-date...", swcFile.getAbsolutePath());
}
//swc is compiled
while (commandLine.size() > argIndex) {
//remove the compc-specific options...
commandLine.remove(argIndex);
}
if (compilerConfig.getProfile() != null && compilerConfig.getProfile()) {
commandLine.add(argIndex++, "-compiler.profile");
}
if (compilerConfig.getWarnings() != null && compilerConfig.getWarnings()) {
commandLine.add(argIndex++, "-warnings");
}
commandLine.add(argIndex++, "-source-path");
commandLine.add(argIndex++, clientSideGenerateDir.getAbsolutePath());
commandLine.add(argIndex++, "-source-path");
sourcePathIndex = argIndex;
commandLine.add(argIndex++, null);
commandLine.add(argIndex++, "--");
mainMxmlPathIndex = argIndex;
commandLine.add(argIndex++, null);
commandLine.set(compileCommandIndex, compilerConfig.getFlexCompileCommand());
File outputDirectory = getSwfCompileDir();
debug("Creating output directory: " + outputDirectory);
outputDirectory.mkdirs();
for (FlexApp flexApp : flexApps) {
String mainMxmlPath = flexApp.getMainMxmlFile();
if (mainMxmlPath == null) {
throw new EnunciateException("A main MXML file for the flex app '" + flexApp.getName() + "' must be supplied with the 'mainMxmlFile' attribute.");
}
File mainMxmlFile = enunciate.resolvePath(mainMxmlPath);
if (!mainMxmlFile.exists()) {
throw new EnunciateException("Main MXML file for the flex app '" + flexApp.getName() + "' doesn't exist.");
}
File swfDir = outputDirectory;
if (flexApp.getOutputPath() != null && !"".equals(flexApp.getOutputPath())) {
swfDir = new File(outputDirectory, flexApp.getOutputPath());
swfDir.mkdirs();
}
File swfFile = new File(swfDir, flexApp.getName() + ".swf");
File appSrcDir = enunciate.resolvePath(flexApp.getSrcDir());
String swfFilePath = swfFile.getAbsolutePath();
boolean swfUpToDate = swfFile.exists()
&& mainMxmlFile.lastModified() < swfFile.lastModified()
&& enunciate.isUpToDate(appSrcDir, swfFile);
if (!swfUpToDate) {
commandLine.set(outputFileIndex, swfFilePath);
commandLine.set(mainMxmlPathIndex, mainMxmlFile.getAbsolutePath());
commandLine.set(sourcePathIndex, appSrcDir.getAbsolutePath());
debug("Compiling %s ...", swfFilePath);
if (enunciate.isDebug()) {
StringBuilder command = new StringBuilder();
for (String commandPiece : commandLine) {
command.append(' ').append(commandPiece);
}
debug("Executing flex compile for module %s with the command: %s", flexApp.getName(), command);
}
ProcessBuilder processBuilder = new ProcessBuilder(commandLine.toArray(new String[commandLine.size()]));
processBuilder.directory(getSwfCompileDir());
processBuilder.redirectErrorStream(true);
Process process = processBuilder.start();
BufferedReader procReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = procReader.readLine();
while (line != null) {
debug(line);
line = procReader.readLine();
}
int procCode;
try {
procCode = process.waitFor();
}
catch (InterruptedException e1) {
throw new EnunciateException("Unexpected inturruption of the Flex compile process.");
}
if (procCode != 0) {
throw new EnunciateException("Flex compile failed for module " + flexApp.getName());
}
}
else {
info("Skipping compilation of %s as everything appears up-to-date...", swfFilePath);
}
}
}
if (isAsSourcesDownloadable()) {
String label = "enunciate";
if ((enunciate.getConfig() != null) && (enunciate.getConfig().getLabel() != null)) {
label = enunciate.getConfig().getLabel();
}
asSources = new File(new File(getCompileDir(), "src"), label + "-as3-sources.zip");
enunciate.zip(asSources, getClientSideGenerateDir());
}
if (swcFile != null || asSources != null) {
List clientDeps = new ArrayList();
BaseArtifactDependency as3Dependency = new BaseArtifactDependency();
as3Dependency.setId("flex-sdk");
as3Dependency.setArtifactType("zip");
as3Dependency.setDescription("The flex SDK.");
as3Dependency.setURL("http://www.adobe.com/products/flex/");
as3Dependency.setVersion("2.0.1");
clientDeps.add(as3Dependency);
ClientLibraryArtifact as3ClientArtifact = new ClientLibraryArtifact(getName(), "as3.client.library", "ActionScript 3 Client Library");
as3ClientArtifact.setPlatform("Adobe Flex");
//read in the description from file:
as3ClientArtifact.setDescription(readResource("library_description.fmt"));
as3ClientArtifact.setDependencies(clientDeps);
if (swcFile != null) {
NamedFileArtifact clientArtifact = new NamedFileArtifact(getName(), "as3.client.swc", swcFile);
clientArtifact.setDescription("The compiled SWC.");
clientArtifact.setPublic(false);
as3ClientArtifact.addArtifact(clientArtifact);
enunciate.addArtifact(clientArtifact);
}
if (asSources != null) {
NamedFileArtifact clientArtifact = new NamedFileArtifact(getName(), "as3.client.sources", asSources);
clientArtifact.setDescription("The client-side ActionScript sources.");
clientArtifact.setPublic(false);
as3ClientArtifact.addArtifact(clientArtifact);
enunciate.addArtifact(clientArtifact);
}
enunciate.addArtifact(as3ClientArtifact);
}
}
/**
* Compiles the SWC.
*
* @param commandLine The command line.
*/
protected void compileSwc(List commandLine) throws IOException, EnunciateException {
getSwcCompileDir().mkdirs();
Process process = new ProcessBuilder(commandLine).directory(getSwcCompileDir()).redirectErrorStream(true).start();
BufferedReader procReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = procReader.readLine();
while (line != null) {
debug(line);
line = procReader.readLine();
}
int procCode;
try {
procCode = process.waitFor();
}
catch (InterruptedException e1) {
throw new EnunciateException("Unexpected inturruption of the Flex compile process.");
}
if (procCode != 0) {
throw new EnunciateException("SWC compile failed.");
}
}
@Override
protected void doCompile() throws EnunciateException, IOException {
Enunciate enunciate = getEnunciate();
if (isSwcDownloadable() || this.flexApps.size() > 0 || isAsSourcesDownloadable()) {
doFlexCompile();
if (this.flexApps.size() > 0) {
enunciate.addArtifact(new FileArtifact(getName(), "flex.app.dir", getSwfCompileDir()));
}
}
}
@Override
protected void doBuild() throws EnunciateException, IOException {
//assemble the server-side webapp fragment
BaseWebAppFragment webAppFragment = new BaseWebAppFragment(getName());
//base webapp dir...
File webappDir = new File(getBuildDir(), "webapp");
webappDir.mkdirs();
File servicesConfigFile = new File(getXMLGenerateDir(), "services-config.xml");
if (servicesConfigFile.exists()) {
getEnunciate().copyFile(servicesConfigFile, new File(new File(new File(webappDir, "WEB-INF"), "flex"), "services-config.xml"));
}
else {
throw new FileNotFoundException("File not found: " + servicesConfigFile.getAbsolutePath());
}
File swfCompileDir = getSwfCompileDir();
if ((this.flexApps.size() > 0) && (swfCompileDir != null) && (swfCompileDir.exists())) {
File flexAppDir = webappDir;
if ((getFlexAppDir() != null) && (!"".equals(getFlexAppDir()))) {
debug("Flex applications will be put into the %s subdirectory of the web application.", getFlexAppDir());
flexAppDir = new File(webappDir, getFlexAppDir());
}
getEnunciate().copyDir(swfCompileDir, flexAppDir);
}
else {
debug("No flex apps were found.");
}
webAppFragment.setBaseDir(webappDir);
//servlets.
WebAppComponent messageServlet = new WebAppComponent();
messageServlet.setClassname(MessageBrokerServlet.class.getName());
messageServlet.setName("AMFMessageServlet");
TreeSet urlMappings = new TreeSet();
for (WsdlInfo wsdlInfo : getModel().getNamespacesToWSDLs().values()) {
for (EndpointInterface ei : wsdlInfo.getEndpointInterfaces()) {
urlMappings.add(getAmfSubcontext() + ei.getServiceName());
}
}
messageServlet.setUrlMappings(urlMappings);
TreeMap initParams = new TreeMap();
initParams.put("services.configuration.file", "/WEB-INF/flex/services-config.xml");
initParams.put("flex.write.path", "/WEB-INF/flex");
messageServlet.setInitParams(initParams);
webAppFragment.setServlets(Arrays.asList(messageServlet));
getEnunciate().addWebAppFragment(webAppFragment);
}
/**
* Reads a resource into string form.
*
* @param resource The resource to read.
* @return The string form of the resource.
*/
protected String readResource(String resource) throws IOException, EnunciateException {
HashMap model = new HashMap();
model.put("sample_service_method", getModelInternal().findExampleWebMethod());
model.put("sample_resource", getModelInternal().findExampleResource());
URL res = AMFDeploymentModule.class.getResource(resource);
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
PrintStream out = new PrintStream(bytes);
try {
processTemplate(res, model, out);
out.flush();
bytes.flush();
return bytes.toString("utf-8");
}
catch (TemplateException e) {
throw new EnunciateException(e);
}
}
/**
* Whether the given type declaration is AMF-transient.
*
* @param declaration The type declaration.
* @return Whether the given tyep declaration is AMF-transient.
*/
protected boolean isAMFTransient(TypeDeclaration declaration) {
return isAMFTransient((Declaration) declaration) || isAMFTransient(declaration.getPackage());
}
/**
* Whether the given type declaration is AMF-transient.
*
* @param declaration The type declaration.
* @return Whether the given tyep declaration is AMF-transient.
*/
protected boolean isAMFTransient(Declaration declaration) {
return declaration != null && declaration.getAnnotation(AMFTransient.class) != null;
}
/**
* Get a template URL for the template of the given name.
*
* @param template The specified template.
* @return The URL to the specified template.
*/
protected URL getTemplateURL(String template) {
return AMFDeploymentModule.class.getResource(template);
}
/**
* Get the generate directory for server-side AMF classes.
*
* @return The generate directory for server-side AMF classes.
*/
public File getServerSideGenerateDir() {
return new File(getGenerateDir(), "server");
}
/**
* Get the generate directory for client-side AMF classes.
*
* @return The generate directory for client-side AMF classes.
*/
public File getClientSideGenerateDir() {
return new File(getGenerateDir(), "client");
}
/**
* Get the generate directory for XML configuration.
*
* @return The generate directory for the XML configuration.
*/
public File getXMLGenerateDir() {
return new File(getGenerateDir(), "xml");
}
/**
* The directory for the destination for the SWC.
*
* @return The directory for the destination for the SWC.
*/
public File getSwcCompileDir() {
return new File(getCompileDir(), "swc");
}
/**
* The directory for the destination for the SWF.
*
* @return The directory for the destination for the SWF.
*/
public File getSwfCompileDir() {
return new File(getCompileDir(), "swf");
}
/**
* AMF configuration rule set.
*
* @return AMF configuration rule set.
*/
@Override
public RuleSet getConfigurationRules() {
return this.configurationRules;
}
/**
* AMF validator.
*
* @return AMF validator.
*/
@Override
public Validator getValidator() {
return new AMFValidator();
}
@Override
protected ObjectWrapper getObjectWrapper() {
return new DefaultObjectWrapper() {
@Override
public TemplateModel wrap(Object obj) throws TemplateModelException {
if (obj instanceof JavaDoc) {
return new FreemarkerJavaDoc((JavaDoc) obj);
}
return super.wrap(obj);
}
};
}
/**
* The amf home directory
*
* @return The amf home directory
*/
public String getFlexHome() {
return flexHome;
}
/**
* Set the path to the AMF home directory.
*
* @param flexHome The amf home directory
*/
public void setFlexHome(String flexHome) {
this.flexHome = flexHome;
}
/**
* The amf apps to compile.
*
* @return The amf apps to compile.
*/
public List getFlexApps() {
return flexApps;
}
/**
* Adds a flex app to be compiled.
*
* @param flexApp The flex app to be compiled.
*/
public void addFlexApp(FlexApp flexApp) {
this.flexApps.add(flexApp);
}
/**
* The compiler configuration.
*
* @return The compiler configuration.
*/
public FlexCompilerConfig getCompilerConfig() {
return compilerConfig;
}
/**
* The compiler configuration.
*
* @param compilerConfig The compiler configuration.
*/
public void setCompilerConfig(FlexCompilerConfig compilerConfig) {
this.compilerConfig = compilerConfig;
}
/**
* The name of the swc file.
*
* @return The name of the swc file.
*/
public String getSwcName() {
return swcName;
}
/**
* The name of the swc file.
*
* @param swcName The name of the swc file.
*/
public void setSwcName(String swcName) {
this.swcName = swcName;
}
/**
* Whether the swc is downloadable.
*
* @return Whether the swc is downloadable.
*/
public boolean isSwcDownloadable() {
return swcDownloadable;
}
/**
* Whether the swc is downloadable.
*
* @param swcDownloadable Whether the swc is downloadable.
*/
public void setSwcDownloadable(boolean swcDownloadable) {
this.swcDownloadable = swcDownloadable;
}
/**
* Whether the generated ActionScript sources are downloadable.
*
* @return Whether the generated ActionScript sources are downloadable.
*/
public boolean isAsSourcesDownloadable() {
return asSourcesDownloadable;
}
/**
* Whether the generated ActionScript sources are downloadable.
*
* @param asSourcesDownloadable Whether the generated ActionScript sources are downloadable.
*/
public void setAsSourcesDownloadable(boolean asSourcesDownloadable) {
this.asSourcesDownloadable = asSourcesDownloadable;
}
/**
* The amf subcontext.
*
* @return The amf subcontext.
*/
public String getAmfSubcontext() {
return amfSubcontext;
}
/**
* The amf subcontext.
*
* @param amfSubcontext The amf subcontext.
*/
public void setAmfSubcontext(String amfSubcontext) {
if (amfSubcontext == null) {
throw new IllegalArgumentException("The AMF context must not be null.");
}
if ("".equals(amfSubcontext)) {
throw new IllegalArgumentException("The AMF context must not be the emtpy string.");
}
if (!amfSubcontext.startsWith("/")) {
amfSubcontext = "/" + amfSubcontext;
}
if (!amfSubcontext.endsWith("/")) {
amfSubcontext = amfSubcontext + "/";
}
this.amfSubcontext = amfSubcontext;
}
/**
* The flex app dir.
*
* @return The flex app dir.
*/
public String getFlexAppDir() {
return flexAppDir;
}
/**
* The flex app dir.
*
* @param flexAppDir The flex app dir.
*/
public void setFlexAppDir(String flexAppDir) {
this.flexAppDir = flexAppDir;
}
// Inherited.
@Override
public boolean isDisabled() {
if (super.isDisabled()) {
return true;
}
else if (getModelInternal() != null && getModelInternal().getNamespacesToWSDLs().isEmpty() && getModelInternal().getNamespacesToSchemas().isEmpty()) {
debug("AMF module is disabled because there are no endpoint interfaces nor any schema objects.");
return true;
}
return false;
}
public List getProjectSources() {
List sources = new ArrayList(Arrays.asList(getClientSideGenerateDir(), getServerSideGenerateDir()));
for (FlexApp flexApp : getFlexApps()) {
sources.add(getEnunciate().resolvePath(flexApp.getSrcDir()));
}
return sources;
}
public List getProjectTestSources() {
return Collections.emptyList();
}
public List getProjectResourceDirectories() {
return Collections.emptyList();
}
public List getProjectTestResourceDirectories() {
return Collections.emptyList();
}
}