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 8641 2012-09-27 06:45:17Z 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 ( 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}