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 8743 2012-10-07 03:06:20Z 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 ( final 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 ( final IOException e )
238             {
239                 this.getLogger().error( this.getDisabledMessage(
240                     this.getLocale(), e.getMessage() ) );
241 
242                 this.schemaUrls = null;
243             }
244             catch ( final URISyntaxException e )
245             {
246                 this.getLogger().error( this.getDisabledMessage(
247                     this.getLocale(), e.getMessage() ) );
248 
249                 this.schemaUrls = null;
250             }
251         }
252 
253         return this.schemaUrls != null ? this.schemaUrls : new URL[ 0 ];
254     }
255 
256     /**
257      * Searches all available {@code META-INF/MANIFEST.MF} resources for
258      * entries whose name end with one of the extensions specified by
259      * property {@code schemaExtensions}.
260      *
261      * @return URLs of any matching resources.
262      *
263      * @throws IOException if reading or parsing fails.
264      * @throws URISyntaxException if creating a schema URI fails.
265      */
266     private URL[] getSchemaResources() throws IOException, URISyntaxException
267     {
268         final ClassLoader cl = this.getClass().getClassLoader();
269         final Set/*<URI>*/ schemaResources = new HashSet();
270 
271         for ( final Enumeration e = cl.getResources( "META-INF/MANIFEST.MF" );
272               e.hasMoreElements(); )
273         {
274             final String[] extensions = this.getSchemaExtensions();
275             final URL manifestUrl = (URL) e.nextElement();
276             final String externalForm = manifestUrl.toExternalForm();
277             final String baseUrl =
278                 externalForm.substring( 0, externalForm.indexOf( "META-INF" ) );
279 
280             final InputStream manifestStream = manifestUrl.openStream();
281             final Manifest mf = new Manifest( manifestStream );
282 
283             manifestStream.close();
284 
285             for ( final Iterator it = mf.getEntries().entrySet().iterator();
286                   it.hasNext(); )
287             {
288                 final Map.Entry entry = (Map.Entry) it.next();
289                 for ( int i = extensions.length - 1; i >= 0; i-- )
290                 {
291                     if ( entry.getKey().toString().toLowerCase().
292                         endsWith( '.' + extensions[i].toLowerCase() ) )
293                     {
294                         final URL schemaUrl =
295                             new URL( baseUrl + entry.getKey().toString() );
296 
297                         schemaResources.add( new URI( schemaUrl.toString() ) );
298 
299                         if ( this.getLogger().isDebugEnabled() )
300                         {
301                             this.getLogger().debug(
302                                 this.getCandidateSchemaMessage(
303                                 this.getLocale(),
304                                 schemaUrl.toExternalForm() ) );
305 
306                         }
307                     }
308                 }
309             }
310         }
311 
312         final URL[] urls = new URL[ schemaResources.size() ];
313         final Iterator it = schemaResources.iterator();
314         for ( int i = 0; it.hasNext(); i++ )
315         {
316             urls[i] = ( (URI) it.next() ).toURL();
317         }
318 
319         return urls;
320     }
321 
322     //-------------------------------------------------ClasspathEntityResolver--
323     //--Messages----------------------------------------------------------------
324 
325 // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausMessages
326     // This section is managed by jdtaus-container-mojo.
327 
328     /**
329      * Gets the text of message <code>candidateSchema</code>.
330      * <blockquote><pre>{0} zur Liste der Schema-Kandidaten hinzugefügt.</pre></blockquote>
331      * <blockquote><pre>Added {0} to the list of candidate schema resources.</pre></blockquote>
332      *
333      * @param locale The locale of the message instance to return.
334      * @param schemaLocation Location of the candidate schema.
335      *
336      * @return Information about a schema added to the list of candidate schemas.
337      */
338     private String getCandidateSchemaMessage( final Locale locale,
339             final java.lang.String schemaLocation )
340     {
341         return ContainerFactory.getContainer().
342             getMessage( this, "candidateSchema", locale,
343                 new Object[]
344                 {
345                     schemaLocation
346                 });
347 
348     }
349 
350     /**
351      * Gets the text of message <code>resolvedSystemId</code>.
352      * <blockquote><pre>{0}
353      *        -> {1}</pre></blockquote>
354      * <blockquote><pre>{0}
355      *        -> {1}</pre></blockquote>
356      *
357      * @param locale The locale of the message instance to return.
358      * @param systemId System id of the schema.
359      * @param resolvedSystemId Resolved system id of the schema.
360      *
361      * @return Information about a resolved schema.
362      */
363     private String getResolvedSystemIdMessage( final Locale locale,
364             final java.lang.String systemId,
365             final java.lang.String resolvedSystemId )
366     {
367         return ContainerFactory.getContainer().
368             getMessage( this, "resolvedSystemId", locale,
369                 new Object[]
370                 {
371                     systemId,
372                     resolvedSystemId
373                 });
374 
375     }
376 
377     /**
378      * Gets the text of message <code>unsupportedSystemIdUri</code>.
379      * <blockquote><pre>Nicht unterstützter System-ID URI "{0}". {1}</pre></blockquote>
380      * <blockquote><pre>Unsupported system id URI "{0}". {1}</pre></blockquote>
381      *
382      * @param locale The locale of the message instance to return.
383      * @param systemIdUri Unsupported system id URI.
384      * @param cause Cause the URI is not supported.
385      *
386      * @return Information about an unsupported system id URI.
387      */
388     private String getUnsupportedSystemIdUriMessage( final Locale locale,
389             final java.lang.String systemIdUri,
390             final java.lang.String cause )
391     {
392         return ContainerFactory.getContainer().
393             getMessage( this, "unsupportedSystemIdUri", locale,
394                 new Object[]
395                 {
396                     systemIdUri,
397                     cause
398                 });
399 
400     }
401 
402     /**
403      * Gets the text of message <code>disabled</code>.
404      * <blockquote><pre>Klassenpfad-Resourcen konnten nicht verarbeitet werden. Resolver wurde deaktiviert ! {0}</pre></blockquote>
405      * <blockquote><pre>Could not process classpath resources. Resolver is disabled ! {0}</pre></blockquote>
406      *
407      * @param locale The locale of the message instance to return.
408      * @param cause Cause the resolver is disabled.
409      *
410      * @return .
411      */
412     private String getDisabledMessage( final Locale locale,
413             final java.lang.String cause )
414     {
415         return ContainerFactory.getContainer().
416             getMessage( this, "disabled", locale,
417                 new Object[]
418                 {
419                     cause
420                 });
421 
422     }
423 
424 // </editor-fold>//GEN-END:jdtausMessages
425 
426     //----------------------------------------------------------------Messages--
427 }