View Javadoc

1   /*
2    *  jDTAUS Core RI Entity Resolver
3    *  Copyright (C) 2005 Christian Schulte
4    *  <cs@schulte.it>
5    *
6    *  This library is free software; you can redistribute it and/or
7    *  modify it under the terms of the GNU Lesser General Public
8    *  License as published by the Free Software Foundation; either
9    *  version 2.1 of the License, or any later version.
10   *
11   *  This library is distributed in the hope that it will be useful,
12   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   *  Lesser General Public License for more details.
15   *
16   *  You should have received a copy of the GNU Lesser General Public
17   *  License along with this library; if not, write to the Free Software
18   *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19   *
20   */
21  package org.jdtaus.core.sax.ri.resolver;
22  
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.net.URI;
26  import java.net.URISyntaxException;
27  import java.net.URL;
28  import java.util.Enumeration;
29  import java.util.HashSet;
30  import java.util.Iterator;
31  import java.util.Locale;
32  import java.util.Map;
33  import java.util.Set;
34  import java.util.jar.Manifest;
35  import org.jdtaus.core.container.ContainerFactory;
36  import org.jdtaus.core.logging.spi.Logger;
37  import org.xml.sax.EntityResolver;
38  import org.xml.sax.InputSource;
39  import org.xml.sax.SAXException;
40  
41  /**
42   * {@code EntityResolver} implementation resolving XML schemas from classpath
43   * resources.
44   *
45   * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
46   * @version $JDTAUS: ClasspathEntityResolver.java 8641 2012-09-27 06:45:17Z schulte $
47   */
48  public class ClasspathEntityResolver implements EntityResolver
49  {
50      //--Constructors------------------------------------------------------------
51  
52  // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausConstructors
53      // This section is managed by jdtaus-container-mojo.
54  
55      /** Standard implementation constructor <code>org.jdtaus.core.sax.ri.resolver.ClasspathEntityResolver</code>. */
56      public ClasspathEntityResolver()
57      {
58          super();
59      }
60  
61  // </editor-fold>//GEN-END:jdtausConstructors
62  
63      //------------------------------------------------------------Constructors--
64      //--Dependencies------------------------------------------------------------
65  
66  // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausDependencies
67      // This section is managed by jdtaus-container-mojo.
68  
69      /**
70       * Gets the configured <code>Logger</code> implementation.
71       *
72       * @return The configured <code>Logger</code> implementation.
73       */
74      private Logger getLogger()
75      {
76          return (Logger) ContainerFactory.getContainer().
77              getDependency( this, "Logger" );
78  
79      }
80  
81      /**
82       * Gets the configured <code>Locale</code> implementation.
83       *
84       * @return The configured <code>Locale</code> implementation.
85       */
86      private Locale getLocale()
87      {
88          return (Locale) ContainerFactory.getContainer().
89              getDependency( this, "Locale" );
90  
91      }
92  
93  // </editor-fold>//GEN-END:jdtausDependencies
94  
95      //------------------------------------------------------------Dependencies--
96      //--Properties--------------------------------------------------------------
97  
98  // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausProperties
99      // This section is managed by jdtaus-container-mojo.
100 
101     /**
102      * Gets the value of property <code>defaultSchemaExtensions</code>.
103      *
104      * @return Default extensions to match classpath resources with (separated by ',').
105      */
106     private java.lang.String getDefaultSchemaExtensions()
107     {
108         return (java.lang.String) ContainerFactory.getContainer().
109             getProperty( this, "defaultSchemaExtensions" );
110 
111     }
112 
113 // </editor-fold>//GEN-END:jdtausProperties
114 
115     //--------------------------------------------------------------Properties--
116     //--EntityResolver----------------------------------------------------------
117 
118     public InputSource resolveEntity( final String publicId,
119                                       final String systemId )
120         throws SAXException, IOException
121     {
122         if ( systemId == null )
123         {
124             throw new NullPointerException( "systemId" );
125         }
126 
127         InputSource schemaSource = null;
128 
129         try
130         {
131             final URI systemUri = new URI( systemId );
132             String schemaName = systemUri.getPath();
133             if ( schemaName != null )
134             {
135                 final int lastIndexOfSlash = schemaName.lastIndexOf( '/' );
136                 if ( lastIndexOfSlash != -1 &&
137                      lastIndexOfSlash < schemaName.length() )
138                 {
139                     schemaName =
140                         schemaName.substring( lastIndexOfSlash + 1 );
141 
142                 }
143 
144                 final URL[] urls = this.getSchemaUrls();
145                 for ( int i = urls.length - 1; i >= 0; i-- )
146                 {
147                     if ( urls[i].getPath().endsWith( schemaName ) )
148                     {
149                         schemaSource = new InputSource();
150                         schemaSource.setPublicId( publicId );
151                         schemaSource.setSystemId( urls[i].toExternalForm() );
152 
153                         if ( this.getLogger().isDebugEnabled() )
154                         {
155                             this.getLogger().debug(
156                                 this.getResolvedSystemIdMessage(
157                                 this.getLocale(), systemId,
158                                 schemaSource.getSystemId() ) );
159 
160                         }
161 
162                         break;
163                     }
164                 }
165             }
166             else
167             {
168                 this.getLogger().warn( this.getUnsupportedSystemIdUriMessage(
169                     this.getLocale(), systemId, systemUri.toASCIIString() ) );
170 
171             }
172         }
173         catch ( URISyntaxException e )
174         {
175             this.getLogger().warn( this.getUnsupportedSystemIdUriMessage(
176                 this.getLocale(), systemId, e.getMessage() ) );
177 
178             schemaSource = null;
179         }
180 
181         return schemaSource;
182     }
183 
184     //----------------------------------------------------------EntityResolver--
185     //--ClasspathEntityResolver-------------------------------------------------
186 
187     /** Schema extensions. */
188     private String[] schemaExtensions;
189 
190     /** URLs of all available classpath schema resources. */
191     private URL[] schemaUrls;
192 
193     /**
194      * Creates a new {@code ClasspathEntityResolver} instance taking the
195      * extensions to match classpath resouces with.
196      *
197      * @param schemaExtensions extensions to match classpath resouces with.
198      */
199     public ClasspathEntityResolver( final String[] schemaExtensions )
200     {
201         if ( schemaExtensions != null && schemaExtensions.length > 0 )
202         {
203             this.schemaExtensions = schemaExtensions;
204         }
205     }
206 
207     /**
208      * Gets the value of property {@code schemaExtensions}.
209      *
210      * @return extensions to match classpath resources with.
211      */
212     private String[] getSchemaExtensions()
213     {
214         if ( this.schemaExtensions == null )
215         {
216             this.schemaExtensions =
217                 this.getDefaultSchemaExtensions().split( "," );
218 
219         }
220 
221         return this.schemaExtensions;
222     }
223 
224     /**
225      * Gets URLs of all available classpath schema resources.
226      *
227      * @return URLs of all available classpath schema resources.
228      */
229     private URL[] getSchemaUrls()
230     {
231         if ( this.schemaUrls == null )
232         {
233             try
234             {
235                 this.schemaUrls = this.getSchemaResources();
236             }
237             catch ( IOException e )
238             {
239                 this.getLogger().error( this.getDisabledMessage(
240                     this.getLocale(), e.getMessage() ) );
241 
242                 this.schemaUrls = null;
243             }
244         }
245 
246         return this.schemaUrls != null ? this.schemaUrls : new URL[ 0 ];
247     }
248 
249     /**
250      * Searches all available {@code META-INF/MANIFEST.MF} resources for
251      * entries whose name end with one of the extensions specified by
252      * property {@code schemaExtensions}.
253      *
254      * @return URLs of any matching resources.
255      *
256      * @throws IOException if reading or parsing fails.
257      */
258     private URL[] getSchemaResources() throws IOException
259     {
260         final ClassLoader cl = this.getClass().getClassLoader();
261         final Set schemaResources = new HashSet();
262 
263         for ( Enumeration e = cl.getResources( "META-INF/MANIFEST.MF" );
264               e.hasMoreElements(); )
265         {
266             final String[] extensions = this.getSchemaExtensions();
267             final URL manifestUrl = (URL) e.nextElement();
268             final String externalForm = manifestUrl.toExternalForm();
269             final String baseUrl =
270                 externalForm.substring( 0, externalForm.indexOf( "META-INF" ) );
271 
272             final InputStream manifestStream = manifestUrl.openStream();
273             final Manifest mf = new Manifest( manifestStream );
274 
275             manifestStream.close();
276 
277             for ( Iterator it = mf.getEntries().entrySet().iterator();
278                   it.hasNext(); )
279             {
280                 final Map.Entry entry = (Map.Entry) it.next();
281                 for ( int i = extensions.length - 1; i >= 0; i-- )
282                 {
283                     if ( entry.getKey().toString().toLowerCase().
284                         endsWith( '.' + extensions[i].toLowerCase() ) )
285                     {
286                         final URL schemaUrl =
287                             new URL( baseUrl + entry.getKey().toString() );
288 
289                         schemaResources.add( schemaUrl );
290 
291                         if ( this.getLogger().isDebugEnabled() )
292                         {
293                             this.getLogger().debug(
294                                 this.getCandidateSchemaMessage(
295                                 this.getLocale(),
296                                 schemaUrl.toExternalForm() ) );
297 
298                         }
299                     }
300                 }
301             }
302         }
303 
304         return (URL[]) schemaResources.toArray(
305             new URL[ schemaResources.size() ] );
306 
307     }
308 
309     //-------------------------------------------------ClasspathEntityResolver--
310     //--Messages----------------------------------------------------------------
311 
312 // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausMessages
313     // This section is managed by jdtaus-container-mojo.
314 
315     /**
316      * Gets the text of message <code>candidateSchema</code>.
317      * <blockquote><pre>{0} zur Liste der Schema-Kandidaten hinzugefügt.</pre></blockquote>
318      * <blockquote><pre>Added {0} to the list of candidate schema resources.</pre></blockquote>
319      *
320      * @param locale The locale of the message instance to return.
321      * @param schemaLocation Location of the candidate schema.
322      *
323      * @return Information about a schema added to the list of candidate schemas.
324      */
325     private String getCandidateSchemaMessage( final Locale locale,
326             final java.lang.String schemaLocation )
327     {
328         return ContainerFactory.getContainer().
329             getMessage( this, "candidateSchema", locale,
330                 new Object[]
331                 {
332                     schemaLocation
333                 });
334 
335     }
336 
337     /**
338      * Gets the text of message <code>resolvedSystemId</code>.
339      * <blockquote><pre>{0}
340      *        -> {1}</pre></blockquote>
341      * <blockquote><pre>{0}
342      *        -> {1}</pre></blockquote>
343      *
344      * @param locale The locale of the message instance to return.
345      * @param systemId System id of the schema.
346      * @param resolvedSystemId Resolved system id of the schema.
347      *
348      * @return Information about a resolved schema.
349      */
350     private String getResolvedSystemIdMessage( final Locale locale,
351             final java.lang.String systemId,
352             final java.lang.String resolvedSystemId )
353     {
354         return ContainerFactory.getContainer().
355             getMessage( this, "resolvedSystemId", locale,
356                 new Object[]
357                 {
358                     systemId,
359                     resolvedSystemId
360                 });
361 
362     }
363 
364     /**
365      * Gets the text of message <code>unsupportedSystemIdUri</code>.
366      * <blockquote><pre>Nicht unterstützter System-ID URI "{0}". {1}</pre></blockquote>
367      * <blockquote><pre>Unsupported system id URI "{0}". {1}</pre></blockquote>
368      *
369      * @param locale The locale of the message instance to return.
370      * @param systemIdUri Unsupported system id URI.
371      * @param cause Cause the URI is not supported.
372      *
373      * @return Information about an unsupported system id URI.
374      */
375     private String getUnsupportedSystemIdUriMessage( final Locale locale,
376             final java.lang.String systemIdUri,
377             final java.lang.String cause )
378     {
379         return ContainerFactory.getContainer().
380             getMessage( this, "unsupportedSystemIdUri", locale,
381                 new Object[]
382                 {
383                     systemIdUri,
384                     cause
385                 });
386 
387     }
388 
389     /**
390      * Gets the text of message <code>disabled</code>.
391      * <blockquote><pre>Klassenpfad-Resourcen konnten nicht verarbeitet werden. Resolver wurde deaktiviert ! {0}</pre></blockquote>
392      * <blockquote><pre>Could not process classpath resources. Resolver is disabled ! {0}</pre></blockquote>
393      *
394      * @param locale The locale of the message instance to return.
395      * @param cause Cause the resolver is disabled.
396      *
397      * @return .
398      */
399     private String getDisabledMessage( final Locale locale,
400             final java.lang.String cause )
401     {
402         return ContainerFactory.getContainer().
403             getMessage( this, "disabled", locale,
404                 new Object[]
405                 {
406                     cause
407                 });
408 
409     }
410 
411 // </editor-fold>//GEN-END:jdtausMessages
412 
413     //----------------------------------------------------------------Messages--
414 }