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