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

org.codehaus.enunciate.modules.jersey.JerseyDeploymentModule Maven / Gradle / Ivy

Go to download

The Enunciate Jersey module generates the Jersey support for the Enunciated project.

There is a newer version: 1.31
Show newest version
/*
 * 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.jersey;

import freemarker.template.TemplateException;
import org.apache.commons.digester.RuleSet;
import org.codehaus.enunciate.EnunciateException;
import org.codehaus.enunciate.apt.EnunciateClasspathListener;
import org.codehaus.enunciate.apt.EnunciateFreemarkerModel;
import org.codehaus.enunciate.contract.jaxrs.ResourceMethod;
import org.codehaus.enunciate.contract.jaxrs.RootResource;
import org.codehaus.enunciate.contract.validation.Validator;
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.SpecProviderModule;
import org.codehaus.enunciate.modules.jersey.config.JerseyRuleSet;
import org.codehaus.enunciate.template.freemarker.ClassForNameMethod;

import javax.ws.rs.core.MediaType;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.*;

/**
 * 

Jersey Module

* *

The Jersey module generates and compiles the support files and classes necessary to support a REST application according to * JSR-311, using Jersey.

* * * *

Jersey Application

* *

We direct you do the documentation for JAX-RS and Jersey to * learn how to build a REST application using these technologies. However, it is important to note a few idiosyncrasies of the Enunciate-supported * Jersey application.

* *

REST subcontext

* *

Because the Jersey application is presumably deployed along with other Enunciate-supported applications (JAX-WS for SOAP, API documentation, etc.), * it will, by default, be mounted at a specific subcontext as defined in the Enunciate configuration (attribute "defaultRestSubcontext" of the * "enunciate/services/rest" element). This means that a JAX-RS resource applied at path "mypath" will actually be mounted at "rest/mypath", assuming * that "rest" is the subcontext (which it is by default).

* *

While is it recommended that the subcontext be preserved, you can disable it in the configuration for this module. Note, however, * that this increases the chance of the paths of your REST resources conflicting with the paths of your documentation, SOAP endpoints, etc. Enunciate * provides an additional check to see if a REST resource is too greedy because it has a path * parameter in the first path segment. This can also be disabled in configuration, but doing so will effectively disable the Enunciate-generated * documentation and other web service endpoints.

* *

Content Negotiation

* *

Enuncite provides content type negotiation (conneg) to Jersey that conforms to the content type negotiation of * the Enunciate REST module. This means that each resource is mounted from the REST subcontext (see above) but ALSO from a subcontext that conforms to the * id of each content type that the resource supports. So, if the content type id of the "application/xml" content type is "xml" then the resource at path * "mypath" will be mounted at both "/rest/mypath" and "/xml/mypath".

* *

The content types for each JAX-RS resource are declared by the @Produces annotation. The content type ids are customized with the * "enunciate/services/rest/content-types" element in the Enunciate configuration. Enunciate supplies providers for the "application/xml" and "application/json" * content types by default.

* *

Steps

* *

generate

* *

The generate step of the Jersey module generates the configuration files for a servlet-based Jersey application.

* *

Configuration

* *

The Jersey module supports the following attributes:

* *
    *
  • The "useSubcontext" attribute is used to enable/disable mounting the JAX-RS resources at the rest subcontext. Default: "true".
  • *
  • The "usePathBasedConneg" attribute is used to enable/disable path-based conneg (see above). Default: "true".
  • *
  • The "disableWildcardServletError" attribute is used to enable/disable the Enunciate "wildcard" resource check. Default: "false".
  • *
  • The "resourceProviderFactory" attribute is used to specify the fully-qualified classname of an instance of * com.sun.jersey.core.spi.component.ioc.IoCComponentProviderFactory that jersey will use. The default is the spring-based factory or the * jersey default instance if spring isn't enabled.
  • *
  • The "defaultNamespace" attribute is used to specify the default XML namespace. This namespace will have no prefix during XML serialization.
  • *
* *

The Jersey module also supports an arbitrary number of "init-param" child elements that can be used to specify the init parameters (e.g. * container request filters, etc.) of the Jersey servlet. The "init-param" element supports a "name" attribute and a "value" attribute.

* *

Artifacts

* *

The Jersey deployment module exports no artifacts.

* * @author Ryan Heaton * @docFileName module_jersey.html */ public class JerseyDeploymentModule extends FreemarkerDeploymentModule implements EnunciateClasspathListener, SpecProviderModule { private boolean jacksonAvailable = false; private boolean useSubcontext = true; private boolean usePathBasedConneg = true; private boolean disableWildcardServletError = false; private String resourceProviderFactory = null; private String defaultNamespace = null; private final Map servletInitParams = new HashMap(); /** * @return "jersey" */ @Override public String getName() { return "jersey"; } /** * The root resources template URL. * * @return The root resources template URL. */ public URL getRootResourceListTemplateURL() { return JerseyDeploymentModule.class.getResource("jaxrs-root-resources.list.fmt"); } /** * The providers template URL. * * @return The providers template URL. */ public URL getProvidersListTemplateURL() { return JerseyDeploymentModule.class.getResource("jaxrs-providers.list.fmt"); } /** * The jaxb types template URL. * * @return The jaxb types template URL. */ public URL getJaxbTypesTemplateURL() { return JerseyDeploymentModule.class.getResource("jaxrs-jaxb-types.list.fmt"); } /** * @return A new {@link JerseyValidator}. */ @Override public Validator getValidator() { return new JerseyValidator(!isUseSubcontext() && !isDisableWildcardServletError()); } // Inherited. @Override public void initModel(EnunciateFreemarkerModel model) { super.initModel(model); if (!isDisabled()) { Map contentTypes2Ids = model.getContentTypesToIds(); if (getEnunciate().isModuleEnabled("amf")) { //if the amf module is enabled, we'll add amf rest endpoints. contentTypes2Ids.put("application/x-amf", "amf"); } else { debug("AMF module has been disabled, so it's assumed the REST endpoints won't be available in AMF format."); } if (jacksonAvailable) { contentTypes2Ids.put("application/json", "json"); //if we can load jackson, we've got json. } else { debug("Couldn't find Jackson on the classpath, so it's assumed the REST endpoints aren't available in JSON format."); } for (RootResource resource : model.getRootResources()) { for (ResourceMethod resourceMethod : resource.getResourceMethods(true)) { Map> subcontextsByContentType = new HashMap>(); String subcontext = isUseSubcontext() ? getRestSubcontext() : ""; debug("Resource method %s of resource %s to be made accessible at subcontext \"%s\".", resourceMethod.getSimpleName(), resourceMethod.getParent().getQualifiedName(), subcontext); subcontextsByContentType.put(null, new TreeSet(Arrays.asList(subcontext))); resourceMethod.putMetaData("defaultSubcontext", subcontext); if (isUsePathBasedConneg()) { for (String producesMime : resourceMethod.getProducesMime()) { MediaType producesType = MediaType.valueOf(producesMime); for (Map.Entry contentTypeToId : contentTypes2Ids.entrySet()) { MediaType type = MediaType.valueOf(contentTypeToId.getKey()); if (producesType.isCompatible(type)) { String id = '/' + contentTypeToId.getValue(); debug("Resource method %s of resource %s to be made accessible at subcontext \"%s\" because it produces %s/%s.", resourceMethod.getSimpleName(), resourceMethod.getParent().getQualifiedName(), id, producesType.getType(), producesType.getSubtype()); String contentTypeValue = String.format("%s/%s", type.getType(), type.getSubtype()); Set subcontextList = subcontextsByContentType.get(contentTypeValue); if (subcontextList == null) { subcontextList = new TreeSet(); subcontextsByContentType.put(contentTypeValue, subcontextList); } subcontextList.add(id); } } } } resourceMethod.putMetaData("subcontexts", subcontextsByContentType); } } } } // Inherited. public void onClassesFound(Set classes) { jacksonAvailable |= classes.contains("org.codehaus.jackson.jaxrs.JacksonJsonProvider"); } public void doFreemarkerGenerate() throws EnunciateException, IOException, TemplateException { if (!isUpToDate()) { EnunciateFreemarkerModel model = getModel(); model.put("forName", new ClassForNameMethod()); processTemplate(getRootResourceListTemplateURL(), model); processTemplate(getProvidersListTemplateURL(), model); processTemplate(getJaxbTypesTemplateURL(), model); Map conentTypesToIds = model.getContentTypesToIds(); Properties mappings = new Properties(); for (Map.Entry contentTypeToId : conentTypesToIds.entrySet()) { mappings.put(contentTypeToId.getValue(), contentTypeToId.getKey()); } File file = new File(getGenerateDir(), "media-type-mappings.properties"); FileOutputStream out = new FileOutputStream(file); mappings.store(out, "JAX-RS media type mappings."); out.flush(); out.close(); Map ns2prefixes = model.getNamespacesToPrefixes(); mappings = new Properties(); for (Map.Entry ns2prefix : ns2prefixes.entrySet()) { mappings.put(ns2prefix.getKey() == null ? "" : ns2prefix.getKey(), ns2prefix.getValue()); } if (this.defaultNamespace != null) { mappings.put("--DEFAULT_NAMESPACE_ALIAS--", this.defaultNamespace); } file = new File(getGenerateDir(), "ns2prefix.properties"); out = new FileOutputStream(file); mappings.store(out, "Namespace to prefix mappings."); out.flush(); out.close(); } else { info("Skipping generation of JAX-RS support files because everything appears up-to-date."); } } @Override protected void doBuild() throws EnunciateException, IOException { super.doBuild(); File webappDir = getBuildDir(); webappDir.mkdirs(); File webinf = new File(webappDir, "WEB-INF"); File webinfClasses = new File(webinf, "classes"); getEnunciate().copyFile(new File(getGenerateDir(), "jaxrs-providers.list"), new File(webinfClasses, "jaxrs-providers.list")); getEnunciate().copyFile(new File(getGenerateDir(), "jaxrs-root-resources.list"), new File(webinfClasses, "jaxrs-root-resources.list")); getEnunciate().copyFile(new File(getGenerateDir(), "jaxrs-jaxb-types.list"), new File(webinfClasses, "jaxrs-jaxb-types.list")); getEnunciate().copyFile(new File(getGenerateDir(), "media-type-mappings.properties"), new File(webinfClasses, "media-type-mappings.properties")); getEnunciate().copyFile(new File(getGenerateDir(), "ns2prefix.properties"), new File(webinfClasses, "ns2prefix.properties")); BaseWebAppFragment webappFragment = new BaseWebAppFragment(getName()); webappFragment.setBaseDir(webappDir); WebAppComponent servletComponent = new WebAppComponent(); servletComponent.setName("jersey"); servletComponent.setClassname(EnunciateJerseyServletContainer.class.getName()); TreeMap initParams = new TreeMap(); initParams.putAll(getServletInitParams()); if (!isUsePathBasedConneg()) { initParams.put(JerseyAdaptedHttpServletRequest.FEATURE_PATH_BASED_CONNEG, Boolean.FALSE.toString()); } if (isUseSubcontext()) { initParams.put(JerseyAdaptedHttpServletRequest.PROPERTY_SERVLET_PATH, getRestSubcontext()); } if (getResourceProviderFactory() != null) { initParams.put(JerseyAdaptedHttpServletRequest.PROPERTY_RESOURCE_PROVIDER_FACTORY, getResourceProviderFactory()); } servletComponent.setInitParams(initParams); TreeSet urlMappings = new TreeSet(); for (RootResource rootResource : getModel().getRootResources()) { for (ResourceMethod resourceMethod : rootResource.getResourceMethods(true)) { String resourceMethodPattern = resourceMethod.getServletPattern(); for (Set subcontextList : ((Map>) resourceMethod.getMetaData().get("subcontexts")).values()) { for (String subcontext : subcontextList) { String servletPattern; if ("".equals(subcontext)) { servletPattern = resourceMethodPattern; } else { servletPattern = subcontext + resourceMethodPattern; } if (urlMappings.add(servletPattern)) { debug("Resource method %s of resource %s to be made accessible by servlet pattern %s.", resourceMethod.getSimpleName(), resourceMethod.getParent().getQualifiedName(), servletPattern); } } } } } servletComponent.setUrlMappings(urlMappings); webappFragment.setServlets(Arrays.asList(servletComponent)); getEnunciate().addWebAppFragment(webappFragment); } protected String getRestSubcontext() { String restSubcontext = getEnunciate().getConfig().getDefaultRestSubcontext(); //todo: override default rest subcontext? return restSubcontext; } @Override public RuleSet getConfigurationRules() { return new JerseyRuleSet(); } /** * Whether the generated sources are up-to-date. * * @return Whether the generated sources are up-to-date. */ protected boolean isUpToDate() { return enunciate.isUpToDateWithSources(getGenerateDir()); } // Inherited. public boolean isJaxwsProvider() { return false; } // Inherited. public boolean isJaxrsProvider() { return true; } /** * Whether to use the REST subcontext. * * @return Whether to use the REST subcontext. */ public boolean isUseSubcontext() { return useSubcontext; } /** * Whether to use the REST subcontext. * * @param useSubcontext Whether to use the REST subcontext. */ public void setUseSubcontext(boolean useSubcontext) { this.useSubcontext = useSubcontext; } /** * Whether to use path-based conneg. * * @return Whether to use path-based conneg. */ public boolean isUsePathBasedConneg() { return usePathBasedConneg; } /** * Whether to use path-based conneg. * * @param usePathBasedConneg Whether to use path-based conneg. */ public void setUsePathBasedConneg(boolean usePathBasedConneg) { this.usePathBasedConneg = usePathBasedConneg; } /** * The fully-qualified classname of an instance of com.sun.jersey.core.spi.component.ioc.IoCComponentProviderFactory that jersey will use. * * @return The fully-qualified classname of an instance of com.sun.jersey.core.spi.component.ioc.IoCComponentProviderFactory that jersey will use. */ public String getResourceProviderFactory() { return resourceProviderFactory; } /** * The fully-qualified classname of an instance of com.sun.jersey.core.spi.component.ioc.IoCComponentProviderFactory that jersey will use. * * @param resourceProviderFactory The fully-qualified classname of an instance of com.sun.jersey.core.spi.component.ioc.IoCComponentProviderFactory that jersey will use. */ public void setResourceProviderFactory(String resourceProviderFactory) { this.resourceProviderFactory = resourceProviderFactory; } /** * Whether to disable the greedy servlet pattern error. * * @return Whether to disable the greedy servlet pattern error. */ public boolean isDisableWildcardServletError() { return disableWildcardServletError; } /** * Whether to disable the wildcard servlet pattern error. * * @param disableWildcardServletError Whether to disable the wildcard servlet pattern error. */ public void setDisableWildcardServletError(boolean disableWildcardServletError) { this.disableWildcardServletError = disableWildcardServletError; } /** * The default namespace. This namespace will have no prefix associated with it during XML serialization. * * @return The default namespace. */ public String getDefaultNamespace() { return defaultNamespace; } /** * The default namespace. * * @param defaultNamespace The default namespace. */ public void setDefaultNamespace(String defaultNamespace) { this.defaultNamespace = defaultNamespace; } /** * Get the servlet init params. * * @return The servlet init params. */ public Map getServletInitParams() { return servletInitParams; } /** * Add a servlet init param. * * @param name The name of the init param. * @param value The value of the init param. */ public void addServletInitParam(String name, String value) { this.servletInitParams.put(name, value); } // Inherited. @Override public boolean isDisabled() { if (super.isDisabled()) { return true; } else if (getModelInternal() != null && getModelInternal().getRootResources().isEmpty()) { debug("Jersey module is disabled because there are no root resources."); return true; } return false; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy