001/* 002 * jDTAUS Core RI Entity Resolver 003 * Copyright (C) 2005 Christian Schulte 004 * <cs@schulte.it> 005 * 006 * This library is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 2.1 of the License, or any later version. 010 * 011 * This library is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with this library; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 019 * 020 */ 021package org.jdtaus.core.sax.ri.resolver; 022 023import java.io.IOException; 024import java.io.InputStream; 025import java.net.URI; 026import java.net.URISyntaxException; 027import java.net.URL; 028import java.util.Enumeration; 029import java.util.HashSet; 030import java.util.Iterator; 031import java.util.Locale; 032import java.util.Map; 033import java.util.Set; 034import java.util.jar.Manifest; 035import org.jdtaus.core.container.ContainerFactory; 036import org.jdtaus.core.logging.spi.Logger; 037import org.xml.sax.EntityResolver; 038import org.xml.sax.InputSource; 039import org.xml.sax.SAXException; 040 041/** 042 * {@code EntityResolver} implementation resolving XML schemas from classpath 043 * resources. 044 * 045 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 046 * @version $JDTAUS: ClasspathEntityResolver.java 8743 2012-10-07 03:06:20Z schulte $ 047 */ 048public class ClasspathEntityResolver implements EntityResolver 049{ 050 //--Constructors------------------------------------------------------------ 051 052// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausConstructors 053 // This section is managed by jdtaus-container-mojo. 054 055 /** Standard implementation constructor <code>org.jdtaus.core.sax.ri.resolver.ClasspathEntityResolver</code>. */ 056 public ClasspathEntityResolver() 057 { 058 super(); 059 } 060 061// </editor-fold>//GEN-END:jdtausConstructors 062 063 //------------------------------------------------------------Constructors-- 064 //--Dependencies------------------------------------------------------------ 065 066// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausDependencies 067 // This section is managed by jdtaus-container-mojo. 068 069 /** 070 * Gets the configured <code>Logger</code> implementation. 071 * 072 * @return The configured <code>Logger</code> implementation. 073 */ 074 private Logger getLogger() 075 { 076 return (Logger) ContainerFactory.getContainer(). 077 getDependency( this, "Logger" ); 078 079 } 080 081 /** 082 * Gets the configured <code>Locale</code> implementation. 083 * 084 * @return The configured <code>Locale</code> implementation. 085 */ 086 private Locale getLocale() 087 { 088 return (Locale) ContainerFactory.getContainer(). 089 getDependency( this, "Locale" ); 090 091 } 092 093// </editor-fold>//GEN-END:jdtausDependencies 094 095 //------------------------------------------------------------Dependencies-- 096 //--Properties-------------------------------------------------------------- 097 098// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausProperties 099 // 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}