View Javadoc
1   package org.andromda.repositories.mdr;
2   
3   import java.io.File;
4   import java.io.InputStream;
5   import java.net.MalformedURLException;
6   import java.net.URL;
7   import java.util.ArrayList;
8   import java.util.Collection;
9   import java.util.HashMap;
10  import java.util.Map;
11  import javax.jmi.reflect.RefPackage;
12  import org.andromda.core.common.AndroMDALogger;
13  import org.andromda.core.common.ResourceUtils;
14  import org.apache.commons.lang.StringUtils;
15  import org.apache.log4j.Logger;
16  import org.netbeans.api.xmi.XMIInputConfig;
17  import org.netbeans.lib.jmi.xmi.XmiContext;
18  
19  /**
20   * This class supports the expansion of XML HREF references to other modules within a model. The result of the resolver
21   * should be a valid URL. This is necessary for Magic Draw as it doesn't have the entire model referenced but just the
22   * archived model.
23   *
24   * @author Matthias Bohlen
25   * @author Chad Brandon
26   */
27  public class MDRXmiReferenceResolverContext
28      extends XmiContext
29  {
30      private String[] moduleSearchPaths;
31      private static final Logger logger = Logger.getLogger(MDRXmiReferenceResolverContext.class);
32      private static final Map<String, URL> urlMap = new HashMap<String, URL>();
33  
34      /**
35       * Constructs an instance of this class.
36       *
37       * @param extents the extents.
38       * @param config the xml input config.
39       * @param moduleSearchPaths the paths to search for modules
40       */
41      public MDRXmiReferenceResolverContext(
42          RefPackage[] extents,
43          XMIInputConfig config,
44          String[] moduleSearchPaths)
45      {
46          super(extents, config);
47          this.moduleSearchPaths = moduleSearchPaths;
48      }
49  
50      /**
51       * @see org.netbeans.lib.jmi.xmi.XmiContext#toURL(String)
52       */
53      public URL toURL(final String systemId)
54      {
55          if (logger.isDebugEnabled())
56          {
57              logger.debug("attempting to resolve Xmi Href --> '" + systemId + '\'');
58          }
59  
60          final String suffix = this.getSuffix(systemId);
61  
62          // if the model URL has a suffix of '.zip' or '.jar', get
63          // the suffix without it and store it in the urlMap
64          String exts = "\\.jar|\\.zip";
65          String suffixWithExt = suffix.replaceAll(exts, "");
66          URL modelUrl = urlMap.get(suffixWithExt);
67  
68          // Several tries to construct a URL that really exists.
69          if (modelUrl == null)
70          {
71              // If systemId is a valid URL, simply use it
72              modelUrl = this.getValidURL(systemId);
73              if (modelUrl == null)
74              {
75                  // Try to find suffix in module list.
76                  final String modelUrlAsString = this.findModuleUrl(suffix);
77                  if (StringUtils.isNotBlank(modelUrlAsString))
78                  {
79                      modelUrl = this.getValidURL(modelUrlAsString);
80                  }
81                  if (modelUrl == null)
82                  {
83                      // search the classpath
84                      modelUrl = this.findModelUrlOnClasspath(systemId);
85                  }
86                  if (modelUrl == null)
87                  {
88                      // Give up and let superclass deal with it.
89                      modelUrl = super.toURL(systemId);
90                  }
91              }
92  
93              // if we've found the module model, log it
94              // and place it in the map so we don't have to
95              // find it if we need it again.
96              if (modelUrl != null)
97              {
98                  urlMap.put(suffixWithExt, modelUrl);
99              }
100         }
101         if (modelUrl != null && !this.loggedReferencedModels.contains(modelUrl))
102         {
103             AndroMDALogger.info("referenced model --> '" + modelUrl + '\'');
104             this.loggedReferencedModels.add(modelUrl);
105         }
106         return modelUrl;
107     }
108 
109     /**
110      * Keeps track of the referenced models that have been logged.
111      */
112     private final Collection<URL> loggedReferencedModels = new ArrayList<URL>();
113 
114     /**
115      * Finds a module in the module search path.
116      *
117      * @param moduleName the name of the module without any path
118      * @return the complete URL string of the module if found (null if not found)
119      */
120     private final String findModuleUrl(final String moduleName)
121     {
122         String moduleUrl = null;
123         if (this.moduleSearchPaths != null)
124         {
125             if (logger.isDebugEnabled())
126             {
127                 logger.debug("findModuleURL: moduleSearchPath.length=" + moduleSearchPaths.length);
128             }
129             for (int ctr = 0; ctr < moduleSearchPaths.length; ctr++)
130             {
131                 final String moduleSearchPath = moduleSearchPaths[ctr];
132                 if (StringUtils.isNotBlank(moduleSearchPath))
133                 {
134                     if (moduleSearchPath.endsWith(moduleName))
135                     {
136                         moduleUrl = moduleSearchPath;
137                     }
138                     else
139                     {
140                         final File candidate = new File(moduleSearchPath, moduleName);
141                         if (logger.isDebugEnabled())
142                         {
143                             logger.debug("candidate '" + candidate.toString() + "' exists=" + candidate.exists());
144                         }
145                         if (candidate.exists())
146                         {
147                             try
148                             {
149                                 moduleUrl = candidate.toURI().toURL().toExternalForm();
150                             }
151                             catch (final MalformedURLException exception)
152                             {
153                                 // ignore
154                             }
155                         }
156                     }
157                     if (moduleUrl != null && moduleName.endsWith(".zip") || moduleName.endsWith(".jar"))
158                     {
159                         // - typical case for MagicDraw
160                         moduleUrl = "jar:" + moduleUrl + "!/" + moduleName.substring(0, moduleName.length() - 4);
161                     }
162                     // - we've found a module
163                     if (StringUtils.isNotBlank(moduleUrl))
164                     {
165                         break;
166                     }
167                 }
168             }
169         }
170         return moduleUrl;
171     }
172 
173     /**
174      * Gets the suffix of the <code>systemId</code>
175      *
176      * @param systemId the system identifier.
177      * @return the suffix as a String.
178      */
179     private final String getSuffix(String systemId)
180     {
181         int lastSlash = systemId.lastIndexOf('/');
182         if (lastSlash > 0)
183         {
184             String suffix = systemId.substring(lastSlash + 1);
185             return suffix;
186         }
187         return systemId;
188     }
189 
190     /**
191      * The suffixes to use when searching for referenced models on the classpath.
192      */
193     protected static final String[] CLASSPATH_MODEL_SUFFIXES = new String[] {"xml", "xmi"};
194 
195     /**
196      * Searches for the model URL on the classpath.
197      *
198      * @param systemId the system identifier.
199      * @return the suffix as a String.
200      */
201     private final URL findModelUrlOnClasspath(final String systemId)
202     {
203         String modelName = StringUtils.substringAfterLast(systemId, "/");
204         String dot = ".";
205 
206         // remove the first prefix because it may be an archive
207         // (like magicdraw)
208         modelName = StringUtils.substringBeforeLast(modelName, dot);
209 
210         URL modelUrl = null;
211         if (StringUtils.isNotBlank(modelName))
212         {
213             modelUrl = ResourceUtils.getResource(modelName);
214             if (modelUrl == null)
215             {
216                 if (CLASSPATH_MODEL_SUFFIXES != null && CLASSPATH_MODEL_SUFFIXES.length > 0)
217                 {
218                     int suffixNum = CLASSPATH_MODEL_SUFFIXES.length;
219                     for (int ctr = 0; ctr < suffixNum; ctr++)
220                     {
221                         if (logger.isDebugEnabled())
222                         {
223                             logger.debug("searching for model reference --> '" + modelUrl + '\'');
224                         }
225                         String suffix = CLASSPATH_MODEL_SUFFIXES[ctr];
226                         modelUrl = ResourceUtils.getResource(modelName + dot + suffix);
227                         if (modelUrl != null)
228                         {
229                             break;
230                         }
231                     }
232                 }
233             }
234         }
235         return modelUrl;
236     }
237 
238     /**
239      * Returns a URL if the systemId is valid. Returns null otherwise. Catches exceptions as necessary.
240      *
241      * @param systemId the system id
242      * @return the URL (if valid)
243      */
244     private final URL getValidURL(final String systemId)
245     {
246         InputStream stream = null;
247         URL url = null;
248         try
249         {
250             url = new URL(systemId);
251             stream = url.openStream();
252             stream.close();
253         }
254         catch (final Exception exception)
255         {
256             url = null;
257         }
258         finally
259         {
260             stream = null;
261         }
262         return url;
263     }
264 }