All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.codehaus.enunciate.modules.spring_app.SpringAppDeploymentModule 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.spring_app;

import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import freemarker.ext.dom.NodeModel;
import freemarker.template.TemplateException;
import org.apache.commons.digester.RuleSet;
import org.codehaus.enunciate.EnunciateException;
import org.codehaus.enunciate.apt.EnunciateFreemarkerModel;
import org.codehaus.enunciate.config.EnunciateConfiguration;
import org.codehaus.enunciate.contract.validation.Validator;
import org.codehaus.enunciate.main.Enunciate;
import org.codehaus.enunciate.main.FileArtifact;
import org.codehaus.enunciate.main.webapp.WebAppFragment;
import org.codehaus.enunciate.modules.DeploymentModule;
import org.codehaus.enunciate.modules.FreemarkerDeploymentModule;
import org.codehaus.enunciate.modules.spring_app.config.*;
import org.codehaus.enunciate.modules.spring_app.config.security.FormBasedLoginConfig;
import org.codehaus.enunciate.modules.spring_app.config.security.OAuthConfig;
import org.codehaus.enunciate.modules.spring_app.config.security.SecurityConfig;
import org.springframework.util.AntPathMatcher;
import org.w3c.dom.Document;
import sun.misc.Service;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.jar.Manifest;

/**
 * 

Spring App Module

* *

The spring app deployment module produces the web app for hosting the API endpoints and documentation.

* *

The order of the spring app deployment module is 200, putting it after any of the other modules, including * the documentation deployment module. The spring app deployment module maintains soft dependencies on the other * Enunciate modules. If those modules are active, the spring app deployment modules will assemble their artifacts * into a spring-supported web application.

* * * *

Steps

* *

generate

* *

The "generate" step generates the deployment descriptors, and the Spring * configuration file. Refer to configuration to learn how to customize the deployment * descriptors and the spring config file.

* *

compile

* *

The "compile" step compiles all API source files, including the source files that were generated from other modules * (e.g. JAX-WS module, XFire module, GWT module, AMF module, etc.).

* *

build

* *

The "build" step assembles all the generated artifacts, compiled classes, and deployment descriptors into an (expanded) * war directory.

* *

All classes compiled in the compile step are copied to the WEB-INF/classes directory.

* *

A set of libraries are copied to the WEB-INF/lib directory. This set of libraries can be specified in the * configuration file. Unless specified otherwise in the configuration file, the * libraries copied will be filtered from the classpath specified to Enunciate at compile-time. The filtered libraries * are those libraries that are determined to be specific to running the Enunciate compile-time engine. All other * libraries on the classpath are assumed to be dependencies for the API and are therefore copied to WEB-INF/lib. * (If a directory is found on the classpath, its contents are copied to WEB-INF/classes.)

* *

The web.xml file is copied to the WEB-INF directory. A tranformation can be applied to the web.xml file before the copy, * if specified in the config, allowing you to apply your own servlet filters, etc. Take care to preserve the existing elements * when applying a transformation to the web.xml file, as losing data will result in missing or malfunctioning endpoints.

* *

The spring-servlet.xml file is generated and copied to the WEB-INF directory. You can specify other spring config files that * will be copied (and imported by the spring-servlet.xml file) in the configuration. This option allows you to specify spring AOP * interceptors and XFire in/out handlers to wrap your endpoints, if desired. Additional spring configuration for security is also * copied.

* *

The documentation (if found) is copied to the configured location.

* *

The other modules application are copied to the configured location. This includes GWT apps and Flex apps.

* *

package

* *

The "package" step packages the expanded war and exports it.

* *

Configuration

* * * *

The configuration for the Spring App deployment module is specified by the "spring-app" child element under the "modules" element * of the enunciate configuration file.

* *

Structure

* *

The following example shows the structure of the configuration elements for this module. Note that this shows only the structure. * Some configuration elements don't make sense when used together. For more information about the security configuration, see * Spring Application Security.

* * * <enunciate> *   <modules> *     <spring-app contextLoaderListenerClass="..."> *       <war name="..." webXMLTransform="..." webXMLTransformURL="..." *         mergeWebXML="..." mergeWebXMLURL="..." *         preBase="..." postBase="..." *         includeClasspathLibs="[true|false]" excludeDefaultLibs="[true|false]"> *         <includeLibs pattern="..." file="..."/> *         <includeLibs pattern="..." file="..."/> *         ... * *         <excludeLibs pattern="..." file="..."/> *         <excludeLibs pattern="..." file="..."/> *         ... * *         <resource-env-ref name="..." type="..."/> *         <resource-env-ref name="..." type="..."/> *         ... * *         <resource-ref name="..." type="..." auth="..."/> *         <resource-ref name="..." type="..." auth="..."/> *         ... * *         <env name="..." type="..." value="..."/> *         <env name="..." type="..." value="..."/> *         ... * *           <manifest> *             <attribute name="..." value="..."/> *             <attribute section="..." name="..." value="..."/> *             ... *           </manifest> *       </war> * *       <springImport file="..." uri="..."/> *       <springImport file="..." uri="..."/> *       ... * *       <globalServiceInterceptor interceptorClass="..." beanName="..."/> *       <globalServiceInterceptor interceptorClass="..." beanName="..."/> *       ... * *       <handlerInterceptor interceptorClass="..." beanName="..."/> *       <handlerInterceptor interceptorClass="..." beanName="..."/> *       ... * *       <handlerMapping pattern="..." beanName="..."/> *       <handlerMapping pattern="..." beanName="..."/> *       ... * *       <copyResources dir="..." pattern="..."/> *       <copyResources dir="..." pattern="..."/> *       ... * *       <security ...> *         ... *       </security> * *     </spring-app> *   </modules> * </enunciate> * * *

attributes

* *
    *
  • The "enableSecurity" attribute specifies that security should be enabled. The default is "false."
  • *
  • The "contextLoaderListenerClass" attribute specifies that FQN of the class to use as the Spring context loader listener. The default is "org.springframework.web.context.ContextLoaderListener".
  • *
  • The "doCompile" attribute specifies whether this module should take on the responsibility of compiling the server-side classes. This may not be * desired if the module is being used only for generating the war structure and configuration files. Default: "true".
  • *
  • The "doLibCopy" attribute specifies whether this module should take on the responsibility of copying libraries to WEB-INF/lib. This may not be * desired if the module is being used only for generating the war structure and configuration files. Default: "true".
  • *
  • The "doPackage" attribute specifies whether this module should take on the responsibility of packaging (zipping) up the war. This may not be * desired if the module is being used only for generating the war structure and configuration files. Default: "true".
  • *
* *

The "war" element

* *

The "war" element is used to specify configuration for the assembly of the war. It supports the following attributes:

* *
    *
  • The "name" attribute specifies the name of the war. The default is the enunciate configuration label.
  • *
  • The "webXMLTransform" attribute specifies the XSLT tranform file that the web.xml file will pass through before being copied to the WEB-INF * directory. No tranformation will be applied if none is specified.
  • *
  • The "webXMLTransformURL" attribute specifies the URL to an XSLT tranform that the web.xml file will pass through before being copied to the WEB-INF * directory. No tranformation will be applied if none is specified.
  • *
  • The "mergeWebXML" attribute specifies the web.xml file that is to be merged into the Enunciate-generated web.xml file. No file will be merged if * none is specified.
  • *
  • The "mergeWebXMLURL" attribute specifies the URL to a web.xml file that is to be merged into the Enunciate-generated web.xml file. No file will be merged if * none is specified.
  • *
  • The "preBase" attribute specifies a directory (could be gzipped) that supplies a "base" for the war. The directory contents will be copied to * the building war directory before it is provided with any Enunciate-specific files and directories.
  • *
  • The "postBase" attribute specifies a directory (could be gzipped) that supplies a "base" for the war. The directory contents will be copied to * the building war directory after it is provided with any Enunciate-specific files and directories.
  • *
  • The "includeClasspathLibs" attribute specifies whether Enunciate will use the libraries from the classpath for applying the include/exclude * filters. If "false" only the libs explicitly included by file (see below) will be filtered.
  • *
  • The "excludeDefaultLibs" attribute specifies whether Enunciate should perform its default filtering of known compile-time-only jars.
  • *
* *

Including or excluding jars from the war

* *

By default, the war is constructed by copying jars that are on the classpath to its "lib" directory (the contents of directories on the classpath * will be copied to the "classes" directory). You add a specific file to this list with the "file" attribute of the "includeLibs" element of the "war" element.

* *

Once the initial list of jars to be potentially copied is created, it is passed through an "include" filter that you may specify with nested "includeLibs" * elements. For each of these elements, you can specify a set of files to include with the "pattern" attribute. This is an * ant-style pattern matcher against the absolute path of the file (or directory). By default, all files are included. * *

Once the initial list is passed through the "include" filter, it will be passed through an "exclude" filter. There is a set of known jars that by default * will not be copied to the "lib" directory. These include the jars that ship by default with the JDK and the jars that are known to be build-time-only jars * for Enunciate. You can disable the default filter with the "excludeDefaultLibs" attribute of the "war" element. You can also specify additional jars that * are to be excluded with an arbitrary number of "excludeLibs" child elements under the "war" element in the configuration file. The "excludeLibs" element * supports either a "pattern" attribute or a "file" attribute. The "pattern" attribute is an ant-style pattern matcher against the absolute path of the * file (or directory) on the classpath that should not be copied to the destination war. The "file" attribute refers to a specific file on the filesystem * (relative paths are resolved relative to the configuration file). Furthermore, the "excludeLibs" element supports a "includeInManifest" attribute specifying * whether the exclude should be listed in the "Class-Path" attribute of the manifest, even though they are excluded in the war. The is useful if, for example, * you're assembling an "ear" with multiple war files. By default, excluded jars are not included in the manifest.

* *

You can customize the manifest for the war by the "manifest" element of the "war" element. Underneath the "manifest" element can be an arbitrary number * of "attribute" elements that can be used to specify the manifest attributes. Each "attribute" element supports a "name" attribute, a "value" attribute, and * a "section" attribute. If no section is specified, the default section is assumed. If there is no "Class-Path" attribute in the main section, one will be * provided listing the jars on the classpath.

* *

The "springImport" element

* *

The "springImport" element is used to specify a spring configuration file that will be imported by the main * spring servlet config. It supports the following attributes:

* *
    *
  • The "file" attribute specifies the spring import file on the filesystem. It will be copied to the WEB-INF directory.
  • *
  • The "uri" attribute specifies the URI to the spring import file. The URI will not be resolved at compile-time, nor will anything be copied to the * WEB-INF directory. The value of this attribute will be used to reference the spring import file in the main config file. This attribute is useful * to specify an import file on the classpath, e.g. "classpath:com/myco/spring/config.xml".
  • *
* *

One use of specifying spring a import file is to wrap your endpoints with spring interceptors and/or XFire in/out/fault handlers. This can be done * by simply declaring a bean that is an instance of your endpoint class. This bean can be advised as needed, and if it implements * org.codehaus.xfire.handler.HandlerSupport (perhaps through the use * of a mixin?), the in/out/fault handlers will be used for the XFire invocation of that endpoint.

* *

It's important to note that the type on which the bean context will be searched is the type of the endpoint interface, and then only if it exists. * If there are more than one beans that are assignable to the endpoint interface, the bean that is named the name of the service will be used. Otherwise, * the deployment of your endpoint will fail.

* *

The same procedure can be used to specify the beans to use as REST endpoints, although the XFire in/out/fault handlers will be ignored. In this case, * the bean context will be searched for each REST interface that the endpoint implements. If there is a bean that implements that interface, it will * used instead of the default implementation. If there is more than one, the bean that is named the same as the REST endpoint will be used.

* *

There also exists a mechanism to add certain AOP interceptors to all service endpoint beans. Such interceptors are referred to as "global service * interceptors." This can be done by using the "globalServiceInterceptor" element (see below), or by simply creating an interceptor that implements * org.codehaus.enunciate.modules.spring_app.EnunciateServiceAdvice or org.codehaus.enunciate.modules.spring_app.EnunciateServiceAdvisor and declaring it in your * imported spring beans file.

* *

Each global interceptor has an order. The default order is 0 (zero). If a global service interceptor implements org.springframework.core.Ordered, the * order will be respected. As global service interceptors are added, it will be assigned a position in the chain according to it's order. Interceptors * of the same order will be ordered together according to their position in the config file, with priority to those declared by the "globalServiceInterceptor" * element, then to instances of org.codehaus.enunciate.modules.spring_app.EnunciateServiceAdvice, then to instances of * org.codehaus.enunciate.modules.spring_app.EnunciateServiceAdvisor.

* *

For more information on spring bean configuration and interceptor advice, see * the spring reference documentation.

* *

The "globalServiceInterceptor" element

* *

The "globalServiceInterceptor" element is used to specify a Spring interceptor (instance of org.aopalliance.aop.Advice or * org.springframework.aop.Advisor) that is to be injected on all service endpoint beans.

* *
    *
  • The "interceptorClass" attribute specified the class of the interceptor.

    *
  • The "beanName" attribute specifies the bean name of the interceptor.

    *
* *

The "handlerInterceptor" element

* *

The "handlerInterceptor" element is used to specify a Spring interceptor (instance of org.springframework.web.servlet.HandlerInterceptor) * that is to be injected on the handler mapping.

* *
    *
  • The "interceptorClass" attribute specifies the class of the interceptor.

    *
  • The "beanName" attribute specifies the bean name of the interceptor.

    *
* *

For more information on spring bean configuration and interceptor advice, see * the spring reference documentation.

* *

The "handlerMapping" element

* *

The "handlerMapping" element is used to specify a custom Spring handler mapping.

* *
    *
  • The "pattern" attribute specifies the pattern that maps to the handler.

    *
  • The "beanName" attribute specifies the bean name of the handler.

    *
* *

For more information on spring handler mappings, see * the spring reference documentation.

* *

The "copyResources" element

* *

The "copyResources" element is used to specify a pattern of resources to copy to the compile directory. It supports the following attributes:

* *
    *
  • The "dir" attribute specifies the base directory of the resources to copy.
  • *
  • The "pattern" attribute specifies an Ant-style * pattern used to find the resources to copy. For more information, see the documentation for the * ant path matcher in the Spring * JavaDocs.
  • *
* *

Artifacts

* *

The spring app deployment module exports the following artifacts:

* *
    *
  • The "spring.app.dir" artifact is the (expanded) web app directory, exported during the build step.
  • *
  • The "spring.war.file" artifact is the packaged war, exported during the package step.
  • *
* * @author Ryan Heaton * @docFileName module_spring_app.html */ public class SpringAppDeploymentModule extends FreemarkerDeploymentModule { private WarConfig warConfig; private final List springImports = new ArrayList(); private final List copyResources = new ArrayList(); private final List globalServiceInterceptors = new ArrayList(); private final List handlerInterceptors = new ArrayList(); private String defaultAutowire = null; private String defaultDependencyCheck = null; private String contextLoaderListenerClass = "org.springframework.web.context.ContextLoaderListener"; private String dispatcherServletClass; private boolean doCompile = true; private boolean doLibCopy = true; private boolean doPackage = true; private boolean enableSecurity = false; private SecurityConfig securityConfig = new SecurityConfig(); /** * @return "spring-app" */ @Override public String getName() { return "spring-app"; } /** * @return The URL to "security-servlet.xml.fmt" */ protected URL getSecurityServletTemplateURL() { return SpringAppDeploymentModule.class.getResource("security-servlet.xml.fmt"); } /** * @return The URL to "spring-servlet.fmt" */ protected URL getApplicationContextTemplateURL() { return SpringAppDeploymentModule.class.getResource("applicationContext.xml.fmt"); } /** * @return The URL to "security-context.xml.fmt" */ protected URL getSecurityContextTemplateURL() { return SpringAppDeploymentModule.class.getResource("security-context.xml.fmt"); } /** * @return The URL to "web.xml.fmt" */ protected URL getWebXmlTemplateURL() { return SpringAppDeploymentModule.class.getResource("web.xml.fmt"); } /** * @return The URL to "web.xml.fmt" */ protected URL getMergeWebXmlTemplateURL() { return SpringAppDeploymentModule.class.getResource("merge-web-xml.fmt"); } @Override public void init(Enunciate enunciate) throws EnunciateException { super.init(enunciate); if (!isDisabled()) { //spit out any deprecation warnings... if (getDefaultDependencyCheck() != null) { warn("As of Enunciate 1.8, defaultDependencyCheck is no longer supported."); } if (getDefaultAutowire() != null) { warn("As of Enunciate 1.8, defaultAutowire is no longer supported."); } if (getDispatcherServletClass() != null) { warn("As of Enunciate 1.8, specifying the dispatcherServletClass is no longer supported."); } if ((this.warConfig != null) && (this.warConfig.getDocsDir() != null)) { warn("As of Enunciate 1.8, the \"docsDir\" attribute is no longer supported on the spring-app war config. (It was moved to the docs module war config.)"); } } } @Override public void doFreemarkerGenerate() throws IOException, TemplateException { if (!enunciate.isUpToDateWithSources(getConfigGenerateDir())) { EnunciateFreemarkerModel model = getModel(); model.setFileOutputDirectory(getConfigGenerateDir()); //standard spring configuration: model.put("endpointBeanId", new ServiceEndpointBeanIdMethod()); model.put("springImports", getSpringImportURIs()); model.put("springContextLoaderListenerClass", getContextLoaderListenerClass()); model.put("displayName", model.getEnunciateConfig().getLabel()); Object docsDir = enunciate.getProperty("docs.webapp.dir"); if (docsDir == null) { docsDir = ""; } model.put("docsDir", docsDir); if (!globalServiceInterceptors.isEmpty()) { for (GlobalServiceInterceptor interceptor : this.globalServiceInterceptors) { if ((interceptor.getBeanName() == null) && (interceptor.getInterceptorClass() == null)) { throw new IllegalStateException("A global interceptor must have either a bean name or a class set."); } } model.put("globalServiceInterceptors", this.globalServiceInterceptors); } if (!handlerInterceptors.isEmpty()) { for (HandlerInterceptor interceptor : this.handlerInterceptors) { if ((interceptor.getBeanName() == null) && (interceptor.getInterceptorClass() == null)) { throw new IllegalStateException("A handler interceptor must have either a bean name or a class set."); } } model.put("handlerInterceptors", this.handlerInterceptors); } //spring security configuration: model.put("securityEnabled", isEnableSecurity()); model.put("servletPatternToAntPattern", new ServletPatternToAntPattern()); SecurityConfig securityConfig = getSecurityConfig(); if (securityConfig.getRealmName() == null) { EnunciateConfiguration enunciateConfig = enunciate.getConfig(); if (enunciateConfig.getDescription() != null) { securityConfig.setRealmName(enunciateConfig.getDescription()); } } if (securityConfig.getKey() == null) { securityConfig.setKey(String.valueOf(System.currentTimeMillis())); } model.put("securityConfig", securityConfig); processTemplate(getApplicationContextTemplateURL(), model); if (isEnableSecurity()) { processTemplate(getSecurityServletTemplateURL(), model); processTemplate(getSecurityContextTemplateURL(), model); } } else { info("Skipping generation of spring config files as everything appears up-to-date..."); } } @Override protected void doCompile() throws EnunciateException, IOException { if (!isDoCompile()) { info("Compilation has been disabled. No server-side classes will be compiled, nor will any resources be copied."); return; } Enunciate enunciate = getEnunciate(); final File compileDir = getCompileDir(); if (!enunciate.isUpToDateWithSources(compileDir)) { enunciate.compileSources(compileDir); if (!this.copyResources.isEmpty()) { AntPathMatcher matcher = new AntPathMatcher(); for (CopyResources copyResource : this.copyResources) { String pattern = copyResource.getPattern(); if (pattern == null) { throw new EnunciateException("A pattern must be specified for copying resources."); } if (!matcher.isPattern(pattern)) { warn("'%s' is not a valid pattern. Resources NOT copied!", pattern); continue; } File basedir; if (copyResource.getDir() == null) { File configFile = enunciate.getConfigFile(); if (configFile != null) { basedir = configFile.getAbsoluteFile().getParentFile(); } else { basedir = new File(System.getProperty("user.dir")); } } else { basedir = enunciate.resolvePath(copyResource.getDir()); } for (String file : enunciate.getFiles(basedir, new PatternFileFilter(basedir, pattern, matcher))) { enunciate.copyFile(new File(file), basedir, compileDir); } } } } else { info("Skipping compilation as everything appears up-to-date..."); } } @Override protected void doBuild() throws IOException, EnunciateException { Enunciate enunciate = getEnunciate(); File buildDir = getBuildDir(); if (!enunciate.isUpToDateWithSources(buildDir)) { copyPreBase(); info("Building the expanded WAR in %s", buildDir); for (WebAppFragment fragment : enunciate.getWebAppFragments()) { if (fragment.getBaseDir() != null) { enunciate.copyDir(fragment.getBaseDir(), buildDir); } } if (isDoCompile()) { //copy the compiled classes to WEB-INF/classes. File webinf = new File(buildDir, "WEB-INF"); File webinfClasses = new File(webinf, "classes"); enunciate.copyDir(getCompileDir(), webinfClasses); } if (isDoLibCopy()) { doLibCopy(); } else { info("Lib copy has been disabled. No libs will be copied, nor any manifest written."); } generateWebXml(); copySpringConfig(); if (isEnableSecurity()) { createSecurityUI(); } copyPostBase(); } else { info("Skipping the build of the expanded war as everything appears up-to-date..."); } //export the expanded application directory. enunciate.addArtifact(new FileArtifact(getName(), "spring.app.dir", buildDir)); } /** * Copy the post base. */ protected void copyPostBase() throws IOException { Enunciate enunciate = getEnunciate(); File buildDir = getBuildDir(); //extract a post base if specified. if ((this.warConfig != null) && (this.warConfig.getPostBase() != null)) { File postBase = enunciate.resolvePath(this.warConfig.getPostBase()); if (postBase.isDirectory()) { info("Copying postBase directory %s to %s...", postBase, buildDir); enunciate.copyDir(postBase, buildDir); } else { info("Extracting postBase zip file %s to %s...", postBase, buildDir); enunciate.extractBase(new FileInputStream(postBase), buildDir); } } } /** * Copy the pre base. */ protected void copyPreBase() throws IOException { Enunciate enunciate = getEnunciate(); File buildDir = getBuildDir(); if ((this.warConfig != null) && (this.warConfig.getPreBase() != null)) { File preBase = enunciate.resolvePath(this.warConfig.getPreBase()); if (preBase.isDirectory()) { info("Copying preBase directory %s to %s...", preBase, buildDir); enunciate.copyDir(preBase, buildDir); } else { info("Extracting preBase zip file %s to %s...", preBase, buildDir); enunciate.extractBase(new FileInputStream(preBase), buildDir); } } } /** * Create the UI pages for security as needed (e.g. login page). */ protected void createSecurityUI() throws IOException { Enunciate enunciate = getEnunciate(); File buildDir = getBuildDir(); File webinf = new File(buildDir, "WEB-INF"); File jspDir = new File(webinf, "jsp"); jspDir.mkdirs(); if (getSecurityConfig().isEnableFormBasedLogin()) { //form-based login is enabled; we'll use the login page. File loginPageFile = null; FormBasedLoginConfig formBasedLoginConfig = getSecurityConfig().getFormBasedLoginConfig(); if (formBasedLoginConfig != null) { if (formBasedLoginConfig.getLoginPageFile() != null) { loginPageFile = enunciate.resolvePath(formBasedLoginConfig.getLoginPageFile()); } } if (loginPageFile != null) { enunciate.copyFile(loginPageFile, new File(jspDir, "login.jsp")); } else { enunciate.copyResource("/org/codehaus/enunciate/modules/spring_app/jsp/login.jsp", new File(jspDir, "login.jsp")); } } if (getSecurityConfig().isEnableOAuth()) { OAuthConfig oauthConfig = getSecurityConfig().getOAuthConfig(); //copy the OAuth information page. File infoPageFile = null; if (oauthConfig != null) { if (oauthConfig.getInfoPageFile() != null) { infoPageFile = enunciate.resolvePath(oauthConfig.getInfoPageFile()); } } if (infoPageFile != null) { enunciate.copyFile(infoPageFile, new File(jspDir, "oauth_info.jsp")); } else { enunciate.copyResource("/org/codehaus/enunciate/modules/spring_app/jsp/oauth.jsp", new File(jspDir, "oauth_info.jsp")); } //copy the OAuth access confirmation page. File confirmAccessPageFile = null; if (oauthConfig != null) { if (oauthConfig.getConfirmAccessPageFile() != null) { confirmAccessPageFile = enunciate.resolvePath(oauthConfig.getConfirmAccessPageFile()); } } if (confirmAccessPageFile != null) { enunciate.copyFile(confirmAccessPageFile, new File(jspDir, "confirm_access.jsp")); } else { enunciate.copyResource("/org/codehaus/enunciate/modules/spring_app/jsp/confirm_access.jsp", new File(jspDir, "confirm_access.jsp")); } //copy the OAuth access confirmed page. File accessConfirmedPageFile = null; if (oauthConfig != null) { if (oauthConfig.getAccessConfirmedPageFile() != null) { accessConfirmedPageFile = enunciate.resolvePath(oauthConfig.getAccessConfirmedPageFile()); } } if (accessConfirmedPageFile != null) { enunciate.copyFile(accessConfirmedPageFile, new File(jspDir, "access_confirmed.jsp")); } else { enunciate.copyResource("/org/codehaus/enunciate/modules/spring_app/jsp/access_confirmed.jsp", new File(jspDir, "access_confirmed.jsp")); } } } /** * Copy the spring application context and servlet config from the build dir to the WEB-INF directory. */ protected void copySpringConfig() throws IOException { Enunciate enunciate = getEnunciate(); File buildDir = getBuildDir(); File webinf = new File(buildDir, "WEB-INF"); File configDir = getConfigGenerateDir(); enunciate.copyFile(new File(configDir, "applicationContext.xml"), new File(webinf, "applicationContext.xml")); if (isEnableSecurity()) { enunciate.copyFile(new File(configDir, "security-servlet.xml"), new File(webinf, "security-servlet.xml")); enunciate.copyFile(new File(configDir, "security-context.xml"), new File(webinf, "security-context.xml")); } for (SpringImport springImport : springImports) { //copy the extra spring import files to the WEB-INF directory to be imported. if (springImport.getFile() != null) { File importFile = enunciate.resolvePath(springImport.getFile()); enunciate.copyFile(importFile, new File(webinf, importFile.getName())); } } } /** * generates web.xml to WEB-INF. Pass it through a stylesheet, if specified. */ protected void generateWebXml() throws IOException, EnunciateException { Enunciate enunciate = getEnunciate(); File buildDir = getBuildDir(); File webinf = new File(buildDir, "WEB-INF"); webinf.mkdirs(); File destWebXML = new File(webinf, "web.xml"); if (!enunciate.isUpToDateWithSources(destWebXML)) { File configDir = getConfigGenerateDir(); File webXML = new File(configDir, "web.xml"); EnunciateFreemarkerModel model = getModel(); model.setFileOutputDirectory(configDir); try { //delayed to the "build" phase to enable modules to supply their web app fragments. model.put("webAppFragments", enunciate.getWebAppFragments()); List envEntries = Collections.emptyList(); List resourceEnvRefs = Collections.emptyList(); List resourceRefs = Collections.emptyList(); if (this.warConfig != null) { envEntries = this.warConfig.getEnvEntries(); resourceEnvRefs = this.warConfig.getResourceEnvRefs(); resourceRefs = this.warConfig.getResourceRefs(); } model.put("envEntries", envEntries); model.put("resourceEnvRefs", resourceEnvRefs); model.put("resourceRefs", resourceRefs); model.put("securityEnabled", isEnableSecurity()); model.put("securityConfig", getSecurityConfig()); processTemplate(getWebXmlTemplateURL(), model); } catch (TemplateException e) { throw new EnunciateException("Error processing web.xml template file.", e); } File mergedWebXml = webXML; if ((this.warConfig != null) && (this.warConfig.getMergeWebXMLURL() != null || this.warConfig.getMergeWebXML() != null)) { URL webXmlToMerge = this.warConfig.getMergeWebXMLURL(); if (webXmlToMerge == null) { webXmlToMerge = enunciate.resolvePath(this.warConfig.getMergeWebXML()).toURL(); } try { model.put("source1", loadMergeXmlModel(webXmlToMerge.openStream())); model.put("source2", loadMergeXmlModel(new FileInputStream(webXML))); processTemplate(getMergeWebXmlTemplateURL(), model); } catch (TemplateException e) { throw new EnunciateException("Error while merging web xml files.", e); } File mergeTarget = new File(getConfigGenerateDir(), "merged-web.xml"); if (!mergeTarget.exists()) { throw new EnunciateException("Error: " + mergeTarget + " doesn't exist."); } info("Merged %s and %s into %s...", webXmlToMerge, webXML, mergeTarget); mergedWebXml = mergeTarget; } if ((this.warConfig != null) && (this.warConfig.getWebXMLTransformURL() != null || this.warConfig.getWebXMLTransform() != null)) { URL transformURL = this.warConfig.getWebXMLTransformURL(); if (transformURL == null) { transformURL = enunciate.resolvePath(this.warConfig.getWebXMLTransform()).toURL(); } info("web.xml transform has been specified as %s.", transformURL); try { StreamSource source = new StreamSource(transformURL.openStream()); Transformer transformer = new TransformerFactoryImpl().newTransformer(source); info("Transforming %s to %s.", mergedWebXml, destWebXML); transformer.transform(new StreamSource(new FileReader(mergedWebXml)), new StreamResult(destWebXML)); } catch (TransformerException e) { throw new EnunciateException("Error during transformation of the web.xml (stylesheet " + transformURL + ", file " + mergedWebXml + ")", e); } } else { enunciate.copyFile(mergedWebXml, destWebXML); } } } /** * Loads the node model for merging xml. * * @param inputStream The input stream of the xml. * @return The node model. */ protected NodeModel loadMergeXmlModel(InputStream inputStream) throws EnunciateException { try { DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); builderFactory.setNamespaceAware(false); //no namespace for the merging... Document doc = builderFactory.newDocumentBuilder().parse(inputStream); NodeModel.simplify(doc); return NodeModel.wrap(doc.getDocumentElement()); } catch (Exception e) { throw new EnunciateException("Error parsing web.xml file for merging", e); } } /** * Copies the classpath elements to WEB-INF. * * @throws IOException */ protected void doLibCopy() throws IOException { Enunciate enunciate = getEnunciate(); File buildDir = getBuildDir(); File webinf = new File(buildDir, "WEB-INF"); File webinfClasses = new File(webinf, "classes"); File webinfLib = new File(webinf, "lib"); //initialize the include filters. AntPathMatcher pathMatcher = new AntPathMatcher(); pathMatcher.setPathSeparator(File.separator); List explicitIncludes = new ArrayList(); List includePatterns = new ArrayList(); if (this.warConfig != null) { for (IncludeExcludeLibs el : this.warConfig.getIncludeLibs()) { if (el.getFile() != null) { //add explicit files to the include files list. explicitIncludes.add(el.getFile()); } String pattern = el.getPattern(); if (pattern != null) { //normalize the pattern to the platform. pattern = pattern.replace('/', File.separatorChar); if (pathMatcher.isPattern(pattern)) { //make sure that the includes pattern list only has patterns. includePatterns.add(pattern); } else { info("Pattern '%s' is not a valid pattern, so it will not be applied.", pattern); } } } } if (includePatterns.isEmpty()) { //if no include patterns are specified, the implicit pattern is "**/*". String starPattern = "**" + File.separatorChar + "*"; debug("No include patterns have been specified. Using the implicit '%s' pattern.", starPattern); includePatterns.add(starPattern); } List warLibs = new ArrayList(); if (this.warConfig == null || this.warConfig.isIncludeClasspathLibs()) { debug("Using the Enunciate classpath as the initial list of libraries to be passed through the include/exclude filter."); //prime the list of libs to include in the war with what's on the enunciate classpath. warLibs.addAll(Arrays.asList(enunciate.getEnunciateClasspath().split(File.pathSeparator))); } // Apply the "in filter" (i.e. the filter that specifies the files to be included). List includedLibs = new ArrayList(); for (String warLib : warLibs) { File libFile = new File(warLib); if (libFile.exists()) { for (String includePattern : includePatterns) { String absolutePath = libFile.getAbsolutePath(); if (absolutePath.startsWith(File.separator)) { //lob off the beginning "/" for Linux boxes. absolutePath = absolutePath.substring(1); } if (pathMatcher.match(includePattern, absolutePath)) { debug("Library '%s' passed the include filter. It matches pattern '%s'.", libFile.getAbsolutePath(), includePattern); includedLibs.add(libFile); break; } else if (enunciate.isDebug()) { debug("Library '%s' did NOT match include pattern '%s'.", includePattern); } } } } //Now, with what's left, apply the "exclude filter". boolean excludeDefaults = this.warConfig == null || this.warConfig.isExcludeDefaultLibs(); List manifestClasspath = new ArrayList(); Iterator toBeIncludedIt = includedLibs.iterator(); while (toBeIncludedIt.hasNext()) { File toBeIncluded = toBeIncludedIt.next(); if (excludeDefaults && knownExclude(toBeIncluded)) { toBeIncludedIt.remove(); } else if (this.warConfig != null) { for (IncludeExcludeLibs excludeLibs : this.warConfig.getExcludeLibs()) { boolean exclude = false; if ((excludeLibs.getFile() != null) && (excludeLibs.getFile().equals(toBeIncluded))) { exclude = true; debug("%s was explicitly excluded.", toBeIncluded); } else { String pattern = excludeLibs.getPattern(); if (pattern != null) { pattern = pattern.replace('/', File.separatorChar); if (pathMatcher.isPattern(pattern)) { String absolutePath = toBeIncluded.getAbsolutePath(); if (absolutePath.startsWith(File.separator)) { //lob off the beginning "/" for Linux boxes. absolutePath = absolutePath.substring(1); } if (pathMatcher.match(pattern, absolutePath)) { exclude = true; debug("%s was excluded because it matches pattern '%s'", toBeIncluded, pattern); } } } } if (exclude) { toBeIncludedIt.remove(); if ((excludeLibs.isIncludeInManifest()) && (!toBeIncluded.isDirectory())) { //include it in the manifest anyway. manifestClasspath.add(toBeIncluded.getName()); debug("'%s' will be included in the manifest classpath.", toBeIncluded.getName()); } break; } } } } //now add the lib files that are explicitly included. includedLibs.addAll(explicitIncludes); //now we've got the final list, copy the libs. for (File includedLib : includedLibs) { if (includedLib.isDirectory()) { info("Adding the contents of %s to WEB-INF/classes.", includedLib); enunciate.copyDir(includedLib, webinfClasses); } else { info("Including %s in WEB-INF/lib.", includedLib); enunciate.copyFile(includedLib, includedLib.getParentFile(), webinfLib); } } // write the manifest file. Manifest manifest = this.warConfig == null ? WarConfig.getDefaultManifest() : this.warConfig.getManifest(); if ((manifestClasspath.size() > 0) && (manifest.getMainAttributes().getValue("Class-Path") == null)) { StringBuilder manifestClasspathValue = new StringBuilder(); Iterator manifestClasspathIt = manifestClasspath.iterator(); while (manifestClasspathIt.hasNext()) { String entry = manifestClasspathIt.next(); manifestClasspathValue.append(entry); if (manifestClasspathIt.hasNext()) { manifestClasspathValue.append(" "); } } manifest.getMainAttributes().putValue("Class-Path", manifestClasspathValue.toString()); } File metaInf = new File(buildDir, "META-INF"); metaInf.mkdirs(); FileOutputStream manifestFileOut = new FileOutputStream(new File(metaInf, "MANIFEST.MF")); manifest.write(manifestFileOut); manifestFileOut.flush(); manifestFileOut.close(); } @Override protected void doPackage() throws EnunciateException, IOException { if (isDoPackage()) { File buildDir = getBuildDir(); File warFile = getWarFile(); Enunciate enunciate = getEnunciate(); if (!enunciate.isUpToDate(buildDir, warFile)) { if (!warFile.getParentFile().exists()) { warFile.getParentFile().mkdirs(); } info("Creating %s", warFile.getAbsolutePath()); enunciate.zip(warFile, buildDir); } else { info("Skipping war file creation as everything appears up-to-date..."); } enunciate.addArtifact(new FileArtifact(getName(), "spring.war.file", warFile)); } else { info("Packaging has been disabled. No packaging will be performed."); } } /** * The configuration for the war. * * @return The configuration for the war. */ public WarConfig getWarConfig() { return warConfig; } /** * The war file to create. * * @return The war file to create. */ public File getWarFile() { String filename = "enunciate.war"; if (getEnunciate().getConfig().getLabel() != null) { filename = getEnunciate().getConfig().getLabel() + ".war"; } if ((this.warConfig != null) && (this.warConfig.getName() != null)) { filename = this.warConfig.getName(); } return new File(getPackageDir(), filename); } /** * Set the configuration for the war. * * @param warConfig The configuration for the war. */ public void setWarConfig(WarConfig warConfig) { this.warConfig = warConfig; } /** * Get the string form of the spring imports that have been configured. * * @return The string form of the spring imports that have been configured. */ protected ArrayList getSpringImportURIs() { ArrayList springImportURIs = new ArrayList(this.springImports.size()); for (SpringImport springImport : springImports) { if (springImport.getFile() != null) { if (springImport.getUri() != null) { throw new IllegalStateException("A spring import configuration must specify a file or a URI, but not both."); } springImportURIs.add(new File(springImport.getFile()).getName()); } else if (springImport.getUri() != null) { springImportURIs.add(springImport.getUri()); } else { throw new IllegalStateException("A spring import configuration must specify either a file or a URI."); } } return springImportURIs; } /** * Add a spring import. * * @param springImports The spring import to add. */ public void addSpringImport(SpringImport springImports) { this.springImports.add(springImports); } /** * Add a copy resources. * * @param copyResources The copy resources to add. */ public void addCopyResources(CopyResources copyResources) { this.copyResources.add(copyResources); } /** * Add a global service interceptor to the spring configuration. * * @param interceptorConfig The interceptor configuration. */ public void addGlobalServiceInterceptor(GlobalServiceInterceptor interceptorConfig) { this.globalServiceInterceptors.add(interceptorConfig); } /** * Add a handler interceptor to the spring configuration. * * @param interceptorConfig The interceptor configuration. */ public void addHandlerInterceptor(HandlerInterceptor interceptorConfig) { this.handlerInterceptors.add(interceptorConfig); } /** * The value for the spring default autowiring. * * @return The value for the spring default autowiring. */ public String getDefaultAutowire() { return defaultAutowire; } /** * The value for the spring default autowiring. * * @param defaultAutowire The value for the spring default autowiring. */ public void setDefaultAutowire(String defaultAutowire) { this.defaultAutowire = defaultAutowire; } /** * The value for the spring default dependency checking. * * @return The value for the spring default dependency checking. */ public String getDefaultDependencyCheck() { return defaultDependencyCheck; } /** * The value for the spring default dependency checking. * * @param defaultDependencyCheck The value for the spring default dependency checking. */ public void setDefaultDependencyCheck(String defaultDependencyCheck) { this.defaultDependencyCheck = defaultDependencyCheck; } /** * The class to use as the context loader listener. * * @return The class to use as the context loader listener. */ public String getContextLoaderListenerClass() { return contextLoaderListenerClass; } /** * The class to use as the context loader listener. * * @param contextLoaderListenerClass The class to use as the context loader listener. */ public void setContextLoaderListenerClass(String contextLoaderListenerClass) { this.contextLoaderListenerClass = contextLoaderListenerClass; } /** * The class to use as the dispatcher servlet. * * @return The class to use as the dispatcher servlet. */ public String getDispatcherServletClass() { return dispatcherServletClass; } /** * The class to use as the dispatcher servlet. * * @param dispatcherServletClass The class to use as the dispatcher servlet. */ public void setDispatcherServletClass(String dispatcherServletClass) { this.dispatcherServletClass = dispatcherServletClass; } /** * whether this module should take on the responsibility of compiling the server-side classes. * * @return whether this module should take on the responsibility of compiling the server-side classes */ public boolean isDoCompile() { return doCompile; } /** * whether this module should take on the responsibility of compiling the server-side classes * * @param doCompile whether this module should take on the responsibility of compiling the server-side classes */ public void setDoCompile(boolean doCompile) { this.doCompile = doCompile; } /** * whether this module should take on the responsibility of copying libraries to WEB-INF/lib. * * @return whether this module should take on the responsibility of copying libraries to WEB-INF/lib */ public boolean isDoLibCopy() { return doLibCopy; } /** * whether this module should take on the responsibility of copying libraries to WEB-INF/lib * * @param doLibCopy whether this module should take on the responsibility of copying libraries to WEB-INF/lib */ public void setDoLibCopy(boolean doLibCopy) { this.doLibCopy = doLibCopy; } /** * whether this module should take on the responsibility of packaging (zipping) up the war * * @return whether this module should take on the responsibility of packaging (zipping) up the war */ public boolean isDoPackage() { return doPackage; } /** * whether this module should take on the responsibility of packaging (zipping) up the war * * @param doPackage whether this module should take on the responsibility of packaging (zipping) up the war */ public void setDoPackage(boolean doPackage) { this.doPackage = doPackage; } /** * Whether to enable security. * * @return Whether to enable security. */ public boolean isEnableSecurity() { return enableSecurity; } /** * Whether to enable security. * * @param enableSecurity Whether to enable security. */ public void setEnableSecurity(boolean enableSecurity) { this.enableSecurity = enableSecurity; } /** * The spring security configuration. * * @return The spring security configuration. */ public SecurityConfig getSecurityConfig() { return securityConfig; } /** * The spring security configuration. * * @param securityConfig The spring security configuration. */ public void setSecurityConfig(SecurityConfig securityConfig) { this.securityConfig = securityConfig; } /** * Whether to exclude a file from copying to the WEB-INF/lib directory. * * @param file The file to exclude. * @return Whether to exclude a file from copying to the lib directory. */ protected boolean knownExclude(File file) throws IOException { //instantiate a loader with this library only in its path... URLClassLoader loader = new URLClassLoader(new URL[]{file.toURL()}, null); if (loader.findResource("META-INF/enunciate/preserve-in-war") != null) { debug("%s is a known include because it contains the entry META-INF/enunciate/preserve-in-war.", file); //if a jar happens to have the enunciate "preserve-in-war" file, it is NOT excluded. return false; } else if (loader.findResource(com.sun.tools.apt.Main.class.getName().replace('.', '/').concat(".class")) != null) { debug("%s is a known exclude because it appears to be tools.jar.", file); //exclude tools.jar. return true; } else if (loader.findResource(net.sf.jelly.apt.Context.class.getName().replace('.', '/').concat(".class")) != null) { debug("%s is a known exclude because it appears to be apt-jelly.", file); //exclude apt-jelly-core.jar return true; } else if (loader.findResource(net.sf.jelly.apt.freemarker.FreemarkerModel.class.getName().replace('.', '/').concat(".class")) != null) { debug("%s is a known exclude because it appears to be the apt-jelly-freemarker libs.", file); //exclude apt-jelly-freemarker.jar return true; } else if (loader.findResource(freemarker.template.Configuration.class.getName().replace('.', '/').concat(".class")) != null) { debug("%s is a known exclude because it appears to be the freemarker libs.", file); //exclude freemarker.jar return true; } else if (loader.findResource(Enunciate.class.getName().replace('.', '/').concat(".class")) != null) { debug("%s is a known exclude because it appears to be the enunciate core jar.", file); //exclude enunciate-core.jar return true; } else if (loader.findResource("javax/servlet/ServletContext.class") != null) { debug("%s is a known exclude because it appears to be the servlet api.", file); //exclude the servlet api. return true; } else if (loader.findResource("org/codehaus/enunciate/modules/xfire_client/EnunciatedClientSoapSerializerHandler.class") != null) { debug("%s is a known exclude because it appears to be the enunciated xfire client tools jar.", file); //exclude xfire-client-tools return true; } else if (loader.findResource("javax/swing/SwingBeanInfoBase.class") != null) { debug("%s is a known exclude because it appears to be dt.jar.", file); //exclude dt.jar return true; } else if (loader.findResource("HTMLConverter.class") != null) { debug("%s is a known exclude because it appears to be htmlconverter.jar.", file); return true; } else if (loader.findResource("sun/tools/jconsole/JConsole.class") != null) { debug("%s is a known exclude because it appears to be jconsole.jar.", file); return true; } else if (loader.findResource("sun/jvm/hotspot/debugger/Debugger.class") != null) { debug("%s is a known exclude because it appears to be sa-jdi.jar.", file); return true; } else if (loader.findResource("sun/io/ByteToCharDoubleByte.class") != null) { debug("%s is a known exclude because it appears to be charsets.jar.", file); return true; } else if (loader.findResource("com/sun/deploy/ClientContainer.class") != null) { debug("%s is a known exclude because it appears to be deploy.jar.", file); return true; } else if (loader.findResource("com/sun/javaws/Globals.class") != null) { debug("%s is a known exclude because it appears to be javaws.jar.", file); return true; } else if (loader.findResource("javax/crypto/SecretKey.class") != null) { debug("%s is a known exclude because it appears to be jce.jar.", file); return true; } else if (loader.findResource("sun/net/www/protocol/https/HttpsClient.class") != null) { debug("%s is a known exclude because it appears to be jsse.jar.", file); return true; } else if (loader.findResource("sun/plugin/JavaRunTime.class") != null) { debug("%s is a known exclude because it appears to be plugin.jar.", file); return true; } else if (loader.findResource("com/sun/corba/se/impl/activation/ServerMain.class") != null) { debug("%s is a known exclude because it appears to be rt.jar.", file); return true; } else if (Service.providers(DeploymentModule.class, loader).hasNext()) { debug("%s is a known exclude because it appears to be an enunciate module.", file); //exclude by default any deployment module libraries. return true; } return false; } /** * @return 200 */ @Override public int getOrder() { return 200; } @Override public RuleSet getConfigurationRules() { return new SpringAppRuleSet(); } @Override public Validator getValidator() { return new SpringAppValidator(); } /** * The directory where the config files are generated. * * @return The directory where the config files are generated. */ protected File getConfigGenerateDir() { return new File(getGenerateDir(), "config"); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy