View Javadoc

1   package com.atlassian.plugins.rest.doclet.generators.resourcedoc;
2   
3   import com.atlassian.annotations.ExperimentalApi;
4   import com.sun.jersey.api.model.AbstractResource;
5   import com.sun.jersey.api.model.AbstractResourceMethod;
6   import com.sun.jersey.server.wadl.WadlGenerator;
7   import com.sun.jersey.server.wadl.generators.resourcedoc.WadlGeneratorResourceDocSupport;
8   import com.sun.jersey.server.wadl.generators.resourcedoc.model.ResourceDocType;
9   import com.sun.research.ws.wadl.Method;
10  import com.sun.research.ws.wadl.Resource;
11  import org.slf4j.Logger;
12  import org.slf4j.LoggerFactory;
13  import org.w3c.dom.Document;
14  import org.w3c.dom.NamedNodeMap;
15  import org.w3c.dom.Node;
16  import org.w3c.dom.NodeList;
17  
18  import java.net.URL;
19  import java.util.HashMap;
20  import javax.xml.namespace.QName;
21  import javax.xml.parsers.DocumentBuilder;
22  import javax.xml.parsers.DocumentBuilderFactory;
23  
24  /**
25   * This class generates the WADL description of rest resources and considers the rest plugin module descriptors
26   * configured inside the atlassian-plugin.xml file when generating the resource path.
27   *
28   * It builds up a map that contains a mapping of a package name to a resource path.
29   * The full resource path is concatenated of the following strings:
30   * 1) path as configured for rest plugin module descriptor: e.g. api
31   * 2) version as configured for rest plugin module descriptor e.g. 2.0.alpha1
32   * 3) path of the rest end point e.g. worklog
33   * 
34   * e.g. /api/2.0.alpha1/worklog/
35   */
36  public class AtlassianWadlGeneratorResourceDocSupport extends WadlGeneratorResourceDocSupport
37  {
38      private HashMap<String, ResourcePathInformation> resourcePathInformation;
39  
40      private static final Logger LOG = LoggerFactory.getLogger(AtlassianWadlGeneratorResourceDocSupport.class);
41      private static final String ATLASSIAN_PLUGIN_XML = "atlassian-plugin.xml";
42  
43      public AtlassianWadlGeneratorResourceDocSupport()
44      {
45          super();
46      }
47  
48      public AtlassianWadlGeneratorResourceDocSupport(WadlGenerator wadlGenerator, ResourceDocType resourceDoc)
49      {
50          super(wadlGenerator, resourceDoc);
51      }
52  
53      @Override
54      public void init() throws Exception
55      {
56          super.init();
57          parseAtlassianPluginXML();
58      }
59  
60      private void parseAtlassianPluginXML()
61      {
62          //get the factory
63          DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
64  
65          try
66          {
67              final URL resource = getClass().getClassLoader().getResource(ATLASSIAN_PLUGIN_XML);
68              if (resource == null) return;
69              LOG.info("Found " + ATLASSIAN_PLUGIN_XML + " file! Looking for rest plugin module descriptors...");
70  
71              DocumentBuilder db = dbf.newDocumentBuilder();
72  
73              resourcePathInformation = new HashMap<String, ResourcePathInformation>();
74  
75              final Document document = db.parse(resource.toExternalForm());
76              final NodeList restPluginModuleDescriptors = document.getElementsByTagName("rest");
77              final int numPluginModuleDescriptors = restPluginModuleDescriptors.getLength();
78              LOG.info("Found " + numPluginModuleDescriptors + " rest plugin module descriptors.");
79  
80              for (int i = 0; i < numPluginModuleDescriptors; i++)
81              {
82                  final Node node = restPluginModuleDescriptors.item(i);
83  
84                  final NamedNodeMap attributes = node.getAttributes();
85                  final Node pathItem = attributes.getNamedItem("path");
86                  final Node versionItem = attributes.getNamedItem("version");
87                  if (pathItem == null || versionItem == null)
88                      continue;
89  
90                  String resourcePath = pathItem.getNodeValue();
91                  String version = versionItem.getNodeValue();
92  
93                  LOG.info("Found rest end point with path '" + resourcePath + "' and version '" + version + "'");
94  
95                  //Remove leading slash
96                  if (resourcePath.indexOf("/") != -1)
97                  {
98                      resourcePath = resourcePath.substring(resourcePath.indexOf("/") + 1);
99                  }
100 
101                 final NodeList list = node.getChildNodes();
102                 for (int j = 0; j < list.getLength(); j++)
103                 {
104                     final Node child = list.item(j);
105                     if (child.getNodeName().equals("package"))
106                     {
107                         final String packageName = child.getFirstChild().getNodeValue();
108                         LOG.info("Map package '" + packageName + "' to resource path '" + resourcePath + "' and version '" + version + "'");
109                         resourcePathInformation.put(packageName, new ResourcePathInformation(resourcePath, version));
110                     }
111                 }
112             }
113         }
114         catch (Exception ex)
115         {
116             LOG.error("Failed to read " + ATLASSIAN_PLUGIN_XML + " and parse rest plugin module descriptor information. Reason", ex);
117         }
118     }
119 
120     @Override
121     public Resource createResource(AbstractResource r, String path)
122     {
123         final Resource result = super.createResource(r, path);
124         boolean resourcePathChanged = false;
125         for (String packageName : resourcePathInformation.keySet())
126         {
127             if (r.getResourceClass().getPackage().getName().startsWith(packageName))
128             {
129                 final ResourcePathInformation pathInformation = resourcePathInformation.get(packageName);
130                 final String newPath = pathInformation.getPath() + "/" + pathInformation.getVersion() + "/" + result.getPath();
131                 result.setPath(newPath);
132                 resourcePathChanged = true;
133                 LOG.info("Setting resource path of rest end point '" + r.getResourceClass().getCanonicalName() + "' to '" + newPath + "'");
134                 break;
135             }
136         }
137         if (!resourcePathChanged)
138         {
139             LOG.info("Resource path of rest end point '" + r.getResourceClass().getCanonicalName() + "' unchanged no mapping to rest plugin module descriptor found.");
140         }
141 
142         return result;
143     }
144 
145     @Override
146     public Method createMethod(final AbstractResource r, final AbstractResourceMethod m)
147     {
148         final Method method = super.createMethod(r, m);
149         if (m.getMethod().getAnnotation(ExperimentalApi.class) != null)
150         {
151             method.getOtherAttributes().put(new QName("experimental"), Boolean.TRUE.toString());
152         }
153         return method;
154     }
155 
156     public class ResourcePathInformation
157     {
158         private final String path;
159         private final String version;
160 
161         public ResourcePathInformation(String path, String version)
162         {
163             this.path = path;
164             this.version = version;
165         }
166 
167         public String getVersion()
168         {
169             return version;
170         }
171 
172         public String getPath()
173         {
174             return path;
175         }
176     }
177 
178 }