/** * Copyright (C) 2003-2008 eXo Platform SAS. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see<http://www.gnu.org/licenses/>. */ package org.etk.core.rest.wadl; import java.net.URI; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.ws.rs.core.MediaType; import javax.xml.bind.JAXBElement; import javax.xml.namespace.QName; import org.etk.core.rest.impl.resource.AbstractResourceDescriptorImpl; import org.etk.core.rest.method.MethodParameter; import org.etk.core.rest.resource.AbstractResourceDescriptor; import org.etk.core.rest.resource.ResourceMethodDescriptor; import org.etk.core.rest.resource.ResourceMethodMap; import org.etk.core.rest.resource.SubResourceLocatorDescriptor; import org.etk.core.rest.resource.SubResourceMethodDescriptor; import org.etk.core.rest.resource.SubResourceMethodMap; import org.etk.core.rest.wadl.research.Application; import org.etk.core.rest.wadl.research.Param; import org.etk.core.rest.wadl.research.ParamStyle; import org.etk.core.rest.wadl.research.RepresentationType; import org.etk.core.rest.wadl.research.Resources; /** * This class manages process of creation WADL document which describe * {@link AbstractResourceDescriptor}. * */ public final class WadlProcessor { /** * See {@link WadlGenerator}. */ private final WadlGenerator wadlGenerator; /** * Constructs new instance of WadlProcessor which use default WadlGenerator. */ public WadlProcessor() { this.wadlGenerator = new BaseWadlGeneratorImpl(); } /** * Constructs new instance of WadlProcessor with specified WadlGenerator. * * @param wadlGenerator {@link WadlGenerator} */ public WadlProcessor(WadlGenerator wadlGenerator) { this.wadlGenerator = wadlGenerator; } /** * Process {@link AbstractResourceDescriptor} for build its WADL * representation. * * @param resourceDescriptor see {@link AbstractResourceDescriptor} * @param baseURI base URI of resource, e. g. servlet context * @return {@link Application} */ public Application process(AbstractResourceDescriptor resourceDescriptor, URI baseURI) { // Root component of WADL representation Application wadlApp = wadlGenerator.createApplication(); // Container for resources Resources wadlResources = wadlGenerator.createResources(); if (baseURI != null) wadlResources.setBase(baseURI.toString()); org.etk.core.rest.wadl.research.Resource wadlResource = processResource(resourceDescriptor); wadlResources.getResource().add(wadlResource); wadlApp.setResources(wadlResources); return wadlApp; } /** * @param resourceDescriptor see {@link AbstractResourceDescriptor} * @return see {@link WadlGenerator#createResponse()} */ private org.etk.core.rest.wadl.research.Resource processResource(AbstractResourceDescriptor resourceDescriptor) { org.etk.core.rest.wadl.research.Resource wadlResource = wadlGenerator.createResource(resourceDescriptor); // Keeps common parameters for resource. Map<String, Param> wadlResourceParams = new HashMap<String, Param>(); ResourceMethodMap<ResourceMethodDescriptor> resourceMethods = resourceDescriptor.getResourceMethods(); for (List<ResourceMethodDescriptor> l : resourceMethods.values()) { for (ResourceMethodDescriptor rmd : l) { org.etk.core.rest.wadl.research.Method wadlMethod = processMethod(rmd, wadlResourceParams); if (wadlMethod == null) continue; wadlResource.getMethodOrResource().add(wadlMethod); } } // Add parameters to a resource for (Param p : wadlResourceParams.values()) wadlResource.getParam().add(p); processSubResourceMethods(wadlResource, resourceDescriptor); processSubResourceLocators(wadlResource, resourceDescriptor); return wadlResource; } /** * Process sub-resource methods. * * @param wadlResource see * {@link org.etk.core.rest.wadl.research.Resource} * @param resourceDescriptor see {@link AbstractResourceDescriptor} */ private void processSubResourceMethods(org.etk.core.rest.wadl.research.Resource wadlResource, AbstractResourceDescriptor resourceDescriptor) { // Keeps common parameter for sub-resource. Map<String, Map<String, Param>> wadlCommonSubResourceParams = new HashMap<String, Map<String, Param>>(); // Mapping resource path to resource. Map<String, org.etk.core.rest.wadl.research.Resource> wadlSubResources = new HashMap<String, org.etk.core.rest.wadl.research.Resource>(); SubResourceMethodMap subresourceMethods = resourceDescriptor.getSubResourceMethods(); for (ResourceMethodMap<SubResourceMethodDescriptor> rmm : subresourceMethods.values()) { for (List<SubResourceMethodDescriptor> l : rmm.values()) { for (SubResourceMethodDescriptor srmd : l) { String path = srmd.getPathValue().getPath(); org.etk.core.rest.wadl.research.Resource wadlSubResource = wadlSubResources.get(path); // There is no any resource for 'path' yet. if (wadlSubResource == null) { wadlSubResource = wadlGenerator.createResource(path); Map<String, Param> wadlResourceParams = new HashMap<String, Param>(); org.etk.core.rest.wadl.research.Method wadlMethod = processMethod(srmd, wadlResourceParams); if (wadlMethod == null) continue; wadlSubResource.getMethodOrResource().add(wadlMethod); // Remember sub-resource and parameters. wadlSubResources.put(path, wadlSubResource); wadlCommonSubResourceParams.put(path, wadlResourceParams); } else { // Get parameters for sub-resource that was created by one of previous // iteration. Map<String, Param> wadlResourceParams = wadlCommonSubResourceParams.get(path); // Add new method. org.etk.core.rest.wadl.research.Method wadlMethod = processMethod(srmd, wadlResourceParams); if (wadlMethod == null) continue; wadlSubResource.getMethodOrResource().add(wadlMethod); } } } } // Add sub-resources to the root resource. for (Map.Entry<String, org.etk.core.rest.wadl.research.Resource> entry : wadlSubResources.entrySet()) { String path = entry.getKey(); org.etk.core.rest.wadl.research.Resource wadlSubResource = entry.getValue(); for (Param wadlSubParam : wadlCommonSubResourceParams.get(path).values()) wadlSubResource.getParam().add(wadlSubParam); wadlResource.getMethodOrResource().add(wadlSubResource); } } /** * Process sub-resource locators. * * @param wadlResource see * {@link org.etk.core.rest.wadl.research.Resource} * @param resourceDescriptor see {@link AbstractResourceDescriptor} */ private void processSubResourceLocators(org.etk.core.rest.wadl.research.Resource wadlResource, AbstractResourceDescriptor resourceDescriptor) { for (SubResourceLocatorDescriptor srld : resourceDescriptor.getSubResourceLocators().values()) { AbstractResourceDescriptor subResourceDescriptor = new AbstractResourceDescriptorImpl(srld.getMethod().getReturnType()); org.etk.core.rest.wadl.research.Resource wadlSubResource = processResource(subResourceDescriptor); wadlSubResource.setPath(srld.getPathValue().getPath()); wadlResource.getMethodOrResource().add(wadlSubResource); } } /** * @param rmd see {@link ResourceMethodDescriptor} * @param wadlResourceParams for adding parameters which must be in parent * @return {@link org.etk.core.rest.wadl.research.Method} */ private org.etk.core.rest.wadl.research.Method processMethod(ResourceMethodDescriptor rmd, Map<String, Param> wadlResourceParams) { org.etk.core.rest.wadl.research.Method wadlMethod = wadlGenerator.createMethod(rmd); // See description of this in // BaseWadlGeneratorImpl.createMethod(ResourceMethodDescriptor) if (wadlMethod == null) return null; org.etk.core.rest.wadl.research.Request wadlRequest = processRequest(rmd, wadlResourceParams); if (wadlRequest != null) wadlMethod.setRequest(wadlRequest); org.etk.core.rest.wadl.research.Response wadlResponse = processResponse(rmd); if (wadlResponse != null) wadlMethod.setResponse(wadlResponse); return wadlMethod; } /** * @param rmd see {@link ResourceMethodDescriptor} * @param wadlResourceParams for adding parameters which must be in parent * @return {@link org.etk.core.rest.wadl.research.Request} */ private org.etk.core.rest.wadl.research.Request processRequest(ResourceMethodDescriptor rmd, Map<String, Param> wadlResourceParams) { org.etk.core.rest.wadl.research.Request wadlRequest = wadlGenerator.createRequest(); for (MethodParameter methodParameter : rmd.getMethodParameters()) { if (methodParameter.getAnnotation() == null) { for (MediaType mediaType : rmd.consumes()) { RepresentationType wadlRepresentation = wadlGenerator.createRequestRepresentation(mediaType); wadlRequest.getRepresentation().add(wadlRepresentation); } } Param wadlParam = processParam(methodParameter); if (wadlParam != null) { if (wadlParam.getStyle() == ParamStyle.QUERY || wadlParam.getStyle() == ParamStyle.HEADER /* || wadlParam.getStyle() == ParamStyle.MATRIX */) { wadlRequest.getParam().add(wadlParam); } else { // If matrix or path template parameter then add in map for add in // parent element wadlResourceParams.put(wadlParam.getName(), wadlParam); } } } // NOTE If there are no any representation and parameters then request is // null. return wadlRequest.getRepresentation().isEmpty() && wadlRequest.getParam().isEmpty() ? null : wadlRequest; } /** * @param rmd see {@link ResourceMethodDescriptor} * @return {@link org.etk.core.rest.wadl.research.Response} */ private org.etk.core.rest.wadl.research.Response processResponse(ResourceMethodDescriptor rmd) { org.etk.core.rest.wadl.research.Response wadlResponse = null; if (rmd.getResponseType() != void.class) { wadlResponse = wadlGenerator.createResponse(); for (MediaType mediaType : rmd.produces()) { RepresentationType wadlRepresentation = wadlGenerator.createResponseRepresentation(mediaType); // Element can represent normal response or fault response JAXBElement<RepresentationType> wadlRepresentationElement = new JAXBElement<RepresentationType>(new QName("http://research.sun.com/wadl/2006/10", "representation"), RepresentationType.class, wadlRepresentation); wadlResponse.getRepresentationOrFault().add(wadlRepresentationElement); } } return wadlResponse; } /** * @param methodParameter see {@link MethodParameter} * @return {@link Param} */ private Param processParam(MethodParameter methodParameter) { Param wadlParam = null; // Skip parameters without annotation (entity) and parameters with javax.ws.rs.core.Context. // Context parameter dependent of environment and not used in WADL representation if (methodParameter.getAnnotation() != null && methodParameter.getAnnotation().annotationType() != javax.ws.rs.core.Context.class) wadlParam = wadlGenerator.createParam(methodParameter); return wadlParam; } }