Pesquisa personalizada

2010/08/03

Glassfish Embedded Server Mode Doesn't Works out of Internet

The First Problem: Without Internet

Using Glassfish 3.0.1 Embedded, while I was without Internet, I got the following error while starting the GF3 Embedded server:
Aug 1, 2010 8:32:18 PM org.apache.catalina.core.StandardContext callServletContainerInitializers
SEVERE: PWC1420: Error invoking ServletContainerInitializer org.apache.jasper.runtime.TldScanner
org.apache.jasper.JasperException: PWC6177: XML parsing error on file file:/E:/downloads/devel/java/glassfish/glassfish-embedded-all-3.0.1.jar
org.apache.jasper.JasperException: PWC6177: XML parsing error on file 
        at org.apache.jasper.xmlparser.ParserUtils.parseXMLDocument(ParserUtils.java:325)
        at org.apache.jasper.xmlparser.ParserUtils.parseXMLDocument(ParserUtils.java:366)
        at org.apache.jasper.runtime.TldScanner.scanTld(TldScanner.java:526)
        at org.apache.jasper.runtime.TldScanner.scanJar(TldScanner.java:369)
        at org.apache.jasper.runtime.TldScanner.scanJars(TldScanner.java:633)
        at org.apache.jasper.runtime.TldScanner.scanTlds(TldScanner.java:282)
        at org.apache.jasper.runtime.TldScanner.onStartup(TldScanner.java:228)
        at org.apache.catalina.core.StandardContext.callServletContainerInitializers(StandardContext.java:5352)
        at com.sun.enterprise.web.WebModule.callServletContainerInitializers(WebModule.java:550)
        at org.apache.catalina.core.StandardContext.start(StandardContext.java:5263)
        at com.sun.enterprise.web.WebModule.start(WebModule.java:499)
        at org.apache.catalina.core.ContainerBase.startChildren(ContainerBase.java:1523)
        at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1190)
        at org.apache.catalina.core.StandardHost.start(StandardHost.java:975)
        at org.apache.catalina.core.ContainerBase.startChildren(ContainerBase.java:1523)
        at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1190)
        at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:529)
        at org.apache.catalina.startup.Embedded.start(Embedded.java:946)
        at com.sun.enterprise.web.WebContainer.postConstruct(WebContainer.java:591)
        at com.sun.hk2.component.AbstractWombImpl.inject(AbstractWombImpl.java:174)
        at com.sun.hk2.component.ConstructorWomb$1.run(ConstructorWomb.java:87)
        at java.security.AccessController.doPrivileged(Native Method)
        at com.sun.hk2.component.ConstructorWomb.initialize(ConstructorWomb.java:84)
        at com.sun.hk2.component.AbstractWombImpl.get(AbstractWombImpl.java:77)
        at com.sun.hk2.component.SingletonInhabitant.get(SingletonInhabitant.java:58)
        at com.sun.hk2.component.LazyInhabitant.get(LazyInhabitant.java:107)
        at com.sun.hk2.component.AbstractInhabitantImpl.get(AbstractInhabitantImpl.java:60)
        at org.glassfish.internal.data.EngineInfo.getContainer(EngineInfo.java:78)
        at com.sun.enterprise.v3.server.ApplicationLifecycle.startContainers(ApplicationLifecycle.java:716)
        at com.sun.enterprise.v3.server.ApplicationLifecycle.setupContainerInfos(ApplicationLifecycle.java:461)
        at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:262)
        at com.sun.enterprise.v3.server.ApplicationLoaderService.processApplication(ApplicationLoaderService.java:362)
        at com.sun.enterprise.v3.server.ApplicationLoaderService.postConstruct(ApplicationLoaderService.java:185)
        at com.sun.hk2.component.AbstractWombImpl.inject(AbstractWombImpl.java:174)
        at com.sun.hk2.component.ConstructorWomb$1.run(ConstructorWomb.java:87)
        at java.security.AccessController.doPrivileged(Native Method)
        at com.sun.hk2.component.ConstructorWomb.initialize(ConstructorWomb.java:84)
        at com.sun.hk2.component.AbstractWombImpl.get(AbstractWombImpl.java:77)
        at com.sun.hk2.component.SingletonInhabitant.get(SingletonInhabitant.java:58)
        at com.sun.hk2.component.LazyInhabitant.get(LazyInhabitant.java:107)
        at com.sun.hk2.component.AbstractInhabitantImpl.get(AbstractInhabitantImpl.java:60)
        at com.sun.enterprise.v3.server.AppServerStartup.run(AppServerStartup.java:236)
        at com.sun.enterprise.v3.server.AppServerStartup.start(AppServerStartup.java:128)
        at com.sun.enterprise.module.bootstrap.Main.launch(Main.java:457)
        at com.sun.enterprise.module.bootstrap.Main.launch(Main.java:401)
        at com.sun.enterprise.module.bootstrap.Main.launch(Main.java:310)
        at com.sun.enterprise.module.bootstrap.Main.launch(Main.java:303)
        at com.sun.enterprise.glassfish.bootstrap.ASEmbedded.run(ASEmbedded.java:101)
        at com.sun.enterprise.glassfish.bootstrap.AbstractMain.start(AbstractMain.java:78)
        at org.glassfish.api.embedded.Server.(Server.java:288)
        at org.glassfish.api.embedded.Server.(Server.java:61)
        at org.glassfish.api.embedded.Server$Builder.build(Server.java:158)
        at org.glassfish.api.embedded.Server$Builder.build(Server.java:140)
        at net.marciowb.bemtevi.google.WebServer.install(WebServer.java:68)
        at net.marciowb.bemtevi.google.WebServer.start(WebServer.java:87)
        at net.marciowb.bemtevi.google.GoogleEarthServer.setRunning(GoogleEarthServer.java:175)
        at net.marciowb.bemtevi.google.GoogleEarthServer.init(GoogleEarthServer.java:116)
        at net.marciowb.bemtevi.BemTeVi$InitWebServer.run(BemTeVi.java:1073)
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
        at java.lang.Thread.run(Thread.java:619)
Caused by: java.net.UnknownHostException: java.sun.com
        at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:177)
        at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
        at java.net.Socket.connect(Socket.java:529)
        at java.net.Socket.connect(Socket.java:478)
        at sun.net.NetworkClient.doConnect(NetworkClient.java:163)
        at sun.net.www.http.HttpClient.openServer(HttpClient.java:394)
        at sun.net.www.http.HttpClient.openServer(HttpClient.java:529)
        at sun.net.www.http.HttpClient.(HttpClient.java:233)
        at sun.net.www.http.HttpClient.New(HttpClient.java:306)
        at sun.net.www.http.HttpClient.New(HttpClient.java:323)
        at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:860)
        at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:801)
        at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:726)
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1049)
        at java.net.URL.openStream(URL.java:1010)
        at org.apache.xerces.impl.XMLEntityManager.setupCurrentEntity(Unknown Source)
        at org.apache.xerces.impl.XMLEntityManager.startEntity(Unknown Source)
        at org.apache.xerces.impl.XMLEntityManager.startDTDEntity(Unknown Source)
        at org.apache.xerces.impl.XMLDTDScannerImpl.setInputSource(Unknown Source)
        at org.apache.xerces.impl.XMLDocumentScannerImpl$DTDDispatcher.dispatch(Unknown Source)
        at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
        at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
        at org.apache.xerces.parsers.DTDConfiguration.parse(Unknown Source)
        at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
        at org.apache.xerces.parsers.DOMParser.parse(Unknown Source)
        at org.apache.xerces.jaxp.DocumentBuilderImpl.parse(Unknown Source)
        at org.apache.jasper.xmlparser.ParserUtils.parseXMLDocument(ParserUtils.java:296)
        ... 60 more

I google the error and I found the following references to it:

But none solution was found. So, as my system needs to run out of Internet, (and I after vote to fix the GF Issue #11516) I investigate the error in deep and I found the cause :).

The Diagnostic

GF3 XML Schema Path The GF uses a special version (different from the used by Tomcat 5 or 6) of the class org.apache.jasper.xmlparser.ParserUtils and it (locally) "caches" the dtd and schemas files used by the Jasper version of the GF JSP 2.1. If you have installed GlassFish 3 you will find this DTD and schemas files at: \gf3-installation-dir\glassfish\lib\dtds and \gf3-installation-dir\glassfish\lib\schemas. This files isn't into GF Embedded version package glassfish-embedded-all-3.0.1.jar, neither glassfish-embedded-web-3.0.1.jar. So you will to deploy it with GF Embedded Server or accept to download it from the Internet as always is happens with the GF Embedded version. But if you try to put this schemas files with your server, don't matter where you try, nevertheless it doesn't work. So, why?

At runtime I inspected the program putting a breakpoint in the throwing error point at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:177) and using Netbeans I saw that the environment of ParserUtils was:

CACHED_DTD_PUBLIC_IDS] String[] #6043(length=4) #6043(length=4) 
[0]] String "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" 
[1]] String "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" 
[2]] String "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" 
[3]] String "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" 
CACHED_DTD_RESOURCE_PATHS] String[] #6044(length=4) #6044(length=4) 
[0]] String "file:/E:/embedded-glassfish/domains/lib/dtdsweb-jsptaglibrary_1_1.dtd" "file:/E:/embedded-glassfish/domains/lib/dtdsweb-jsptaglibrary_1_1.dtd" 
[1]] String "file:/E:/embedded-glassfish/domains/lib/dtdsweb-jsptaglibrary_1_2.dtd" "file:/E:/embedded-glassfish/domains/lib/dtdsweb-jsptaglibrary_1_2.dtd" 
[2]] String "file:/E:/embedded-glassfish/domains/lib/dtdsweb-app_2_2.dtd" "file:/E:/embedded-glassfish/domains/lib/dtdsweb-app_2_2.dtd" 
[3]] String "file:/E:/embedded-glassfish/domains/lib/dtdsweb-app_2_3.dtd" "file:/E:/embedded-glassfish/domains/lib/dtdsweb-app_2_3.dtd" 
CACHED_SCHEMA_RESOURCE_PATHS] String[] #6045(length=4) #6045(length=4) 
[0]] String "file:/E:/embedded-glassfish/domains/lib/schemasweb-jsptaglibrary_2_0.xsd" "file:/E:/embedded-glassfish/domains/lib/schemasweb-jsptaglibrary_2_0.xsd" 
[1]] String "file:/E:/embedded-glassfish/domains/lib/schemasweb-jsptaglibrary_2_1.xsd" "file:/E:/embedded-glassfish/domains/lib/schemasweb-jsptaglibrary_2_1.xsd" 
[2]] String "file:/E:/embedded-glassfish/domains/lib/schemasweb-app_2_4.xsd" "file:/E:/embedded-glassfish/domains/lib/schemasweb-app_2_4.xsd" 
[3]] String "file:/E:/embedded-glassfish/domains/lib/schemasweb-app_2_5.xsd" "file:/E:/embedded-glassfish/domains/lib/schemasweb-app_2_5.xsd" 
DEFAULT_DTD_RESOURCE_PATHS] String[] #6044(length=4) #6044(length=4) 
[0]] String "file:/E:/embedded-glassfish/domains/lib/dtdsweb-jsptaglibrary_1_1.dtd" "file:/E:/embedded-glassfish/domains/lib/dtdsweb-jsptaglibrary_1_1.dtd" 
[1]] String "file:/E:/embedded-glassfish/domains/lib/dtdsweb-jsptaglibrary_1_2.dtd" "file:/E:/embedded-glassfish/domains/lib/dtdsweb-jsptaglibrary_1_2.dtd" 
[2]] String "file:/E:/embedded-glassfish/domains/lib/dtdsweb-app_2_2.dtd" "file:/E:/embedded-glassfish/domains/lib/dtdsweb-app_2_2.dtd" 
[3]] String "file:/E:/embedded-glassfish/domains/lib/dtdsweb-app_2_3.dtd" "file:/E:/embedded-glassfish/domains/lib/dtdsweb-app_2_3.dtd" 
DEFAULT_SCHEMA_RESOURCE_PATHS] String[] #6045(length=4) #6045(length=4) 
[0]] String "file:/E:/embedded-glassfish/domains/lib/schemasweb-jsptaglibrary_2_0.xsd" "file:/E:/embedded-glassfish/domains/lib/schemasweb-jsptaglibrary_2_0.xsd" 
[1]] String "file:/E:/embedded-glassfish/domains/lib/schemasweb-jsptaglibrary_2_1.xsd" "file:/E:/embedded-glassfish/domains/lib/schemasweb-jsptaglibrary_2_1.xsd" 
[2]] String "file:/E:/embedded-glassfish/domains/lib/schemasweb-app_2_4.xsd" "file:/E:/embedded-glassfish/domains/lib/schemasweb-app_2_4.xsd" 
[3]] String "file:/E:/embedded-glassfish/domains/lib/schemasweb-app_2_5.xsd" "file:/E:/embedded-glassfish/domains/lib/schemasweb-app_2_5.xsd" 
SCHEMA_LOCATION_ATTR] String "schemaLocation" "schemaLocation" 
dtdResourcePrefix] String "file:/E:/embedded-glassfish/domains/lib/dtds" "file:/E:/embedded-glassfish/domains/lib/dtds" 
entityResolver] WebEntityResolver #6038 org.glassfish.web.WebEntityResolver@7a7502f6 
dtdDir] File #6068 E:\embedded-glassfish\domains\lib\dtds 
knownDTDs] HashMap "size = 4" "size = 4" 
schemaDir] File #6067 E:\embedded-glassfish\domains\lib\schemas 
serverContext] ServerContextImpl #6066 com.sun.enterprise.v3.server.ServerContextImpl@2c54366 
errorHandler] MyErrorHandler #6037 org.apache.jasper.xmlparser.MyErrorHandler@35563b4b 
isDtdResourcePrefixFileUrl] boolean true true 
isSchemaResourcePrefixFileUrl] boolean true true 
log] Logger #6036 java.util.logging.Logger@54697123 
schemaCache] HashMap "size = 0" "size = 0" 
schemaResourcePrefix] String "file:/E:/embedded-glassfish/domains/lib/schemas" "file:/E:/embedded-glassfish/domains/lib/schemas" 
uri] String "file:/E:/downloads/devel/java/glassfish/glassfish-embedded-all-3.0.1.jar" "file:/E:/downloads/devel/java/glassfish/glassfish-embedded-all-3.0.1.jar" 
validate] boolean false false

Take a look again at:
CACHED_SCHEMA_RESOURCE_PATHS
"file:/E:/embedded-glassfish/domains/lib/schemasweb-jsptaglibrary_2_0.xsd" 
"file:/E:/embedded-glassfish/domains/lib/schemasweb-jsptaglibrary_2_1.xsd" 
"file:/E:/embedded-glassfish/domains/lib/schemasweb-app_2_4.xsd" 
"file:/E:/embedded-glassfish/domains/lib/schemasweb-app_2_5.xsd"
Saw? The filenames is wrong! The CACHED_SCHEMA_RESOURCE_PATHS variable is from ParserUtils and also schemaResourcePrefix that contains the value "file:/E:/embedded-glassfish/domains/lib/schemas". The value is correct, but this variable is a String type. So, I thought the error was in the code: probably the programmer was concatenating this string with the schema filename.

I download the source code (not exactly the same source code used by the GF3, but it helped) of the ParserUtils used by the GF3 Embedded server and I found that there are at less two important variables that regulates where lives the local schema files: schemaResourcePrefix and dtdResourcePrefix. So we must find who is setting this variable. Looking at the source code, I found the method public static void setDtdResourcePrefix(String prefix) and:

146     /**
147      * Sets the path prefix for .xsd resources
148      */
149     public static void setSchemaResourcePrefix(String prefix) {
150 
151         schemaResourcePrefix = prefix;
152 
153         for (int i=0; i<CACHED_SCHEMA_RESOURCE_PATHS.length; i++) {
154             String path = CACHED_SCHEMA_RESOURCE_PATHS[i];
155             int index = path.lastIndexOf('/');
156             if (index != -1) {
157                 CACHED_SCHEMA_RESOURCE_PATHS[i] =
158                     prefix + path.substring(index+1);
159             }
160         }
161     }
162 

After investigating who is calling these methods, I found it: com.sun.enterprise.web.WebContainer! See:

409         // TODO: ParserUtils should become a @Service and it should initialize itself.
410         // TODO: there should be only one EntityResolver for both DigesterFactory
411         // and ParserUtils
412         File root = _serverContext.getInstallRoot();
413         File libRoot = new File(root, "lib");
414         File schemas = new File(libRoot, "schemas");
415         File dtds = new File(libRoot, "dtds");
416 
417         try {
418             ParserUtils.setSchemaResourcePrefix(schemas.toURI().toURL().toString());
419             ParserUtils.setDtdResourcePrefix(dtds.toURI().toURL().toString());
420             ParserUtils.setEntityResolver(habitat.getComponent(EntityResolver.class, "web"));
421         } catch(MalformedURLException e) {
422             _logger.log(Level.SEVERE, "webContainer.exceptionSetSchemasDtdsLocation", e);
423         }
424 

The Solution

So I just patched it adding a back slash to these path values. But thinking better, the error is in ParserUtils, because it must checks for the backslash and will add it if needed. But as I don't have the correct source code of ParserUtils (Guys, I hate Maven!!! What is wrong with Ant?!! Is it a new way to hide the things? What about is going with the open nature of the open source code? Oracle, IBM, are you listenning me?), I patched the WebContainer to:

412         File root = _serverContext.getInstallRoot();
413         File libRoot = new File(root, "lib");
414         File schemas = new File(libRoot, "schemas");
415         File dtds = new File(libRoot, "dtds");
416 
417         try {
418 ParserUtils.setSchemaResourcePrefix(schemas.toURI().toURL().toString() + "/"); 419 ParserUtils.setDtdResourcePrefix(dtds.toURI().toURL().toString() + "/");
420 ParserUtils.setEntityResolver(habitat.getComponent(EntityResolver.class, "web")); 421 } catch(MalformedURLException e) { 422 _logger.log(Level.SEVERE, "webContainer.exceptionSetSchemasDtdsLocation", e); 423 } 424

To apply it, copy the WebContainer code, change it with the patch above and compile it using the glassfish-embedded-all-3.0.1.jar. Adds it to your project as a library and reference it before start the server (I just use String.valueOf(WebContainer.DISPATCHER_MAX_DEPTH) to forces the JVM to load and init my patched WebContainer class before to start the GF thinks).

Putting the Schemas

lib directory from my gf3 embedded domain containing dtds and schemas to validate my xml files

After having your webcontainer patched, copy the dtd and xml schemas from your standard glassfish installation to the directory that you setted as your install root to the GF3 Embedded.

This directory is there that was specified while building your GF3 Embedded using the method EmbeddedFileSystem.Builder.installRoot. If you don't use installRoot, check it using org.glassfish.api.embedded.Server.getServer("YourServerName").getFileSystem().installRoot. I didn't use installRoot method, but only the EmbeddedFileSystem.Builder.instanceRoot and GF3 Embedded understand as been its instal root directory there bellow that I specified to instanceRoot method. So my dtd and schemas files must be putted into: E:\embedded-glassfish\domains\lib because I tell to instanceRoot that to use the directory E:\embedded-glassfish\domains\domain1 to put my GF Embedded temporary domain. So, finally, I just copied both directories dtds and schemas to E:\embedded-glassfish\domains\lib as seen in the picture. Remember to copy this files when creating a new install root directory to your GF3Embedded server (maybe including when creating a new gf3e domain to it).

A better way to deal with theses files

Instead a better solution is to copy the xml schemas to your own WebContainer patched project. So the patch changes to:

412         File root = _serverContext.getInstallRoot();
413         File libRoot = new File(root, "lib");
414         java.net.URL schemas = getClass().getResource("/res/lib/schemas");
415         java.net.URL dtds = getClass().getResource("/res/lib/dtds");
416 
417         //try {
418             ParserUtils.setSchemaResourcePrefix(schemas.toString() + "/");
419             ParserUtils.setDtdResourcePrefix(dtds.toString() + "/");
420             ParserUtils.setEntityResolver(habitat.getComponent(EntityResolver.class, "web"));
421         //} catch(MalformedURLException e) {
422         //    _logger.log(Level.SEVERE, "webContainer.exceptionSetSchemasDtdsLocation", e);
423         //}
424 

Now we get the dtds and schemas from our WebContainer pached jar file and then we don't need to copy this files while building our GFServer install root.

The Second Problem

Somebody isn't doing the job as we like... Another problem with Embedded GF lives with System.getProperty(INSTALL_ROOT_PROPERTY). As seen at com.sun.enterprise.deployment.node.SaxParserHandlerFactory it must be setted to our Embedded GF3 directory... But in my case it is setted to E:\embedded-glassfish\domains\domain1 - it's my instance root, my domain root, not my gf3 embedded install root... it's sad! And to ilustrate my vision this crazy world called gf embedded, see from the words of Sun's guys:

 1     public static SaxParserHandler newInstance() {
 2         SaxParserHandler result = null;
 3         
 4         /*
 5          *If the property com.sun.aas.installRoot is defined, use the 
 6          *original implementation (SaxParserHandler) which fetches DTDs and
 7          *schemas from the installation directory tree.  Otherwise, assume that 
 8          *the app client container is running under Java Web Start. In that
 9          *case, there is no product installation directory (at least none can
10          *be assumed).  The DTDs and schemas will be retrieved from the
11          *JWS-specific jar file instead (SaxParserHandlerBundled).
12          *

13          *bnevins, Oct 16, 2008.  On Oct. 8, 2008 installRoot was changed to be setup
14          *earlier in the startup.  As a result, Embedded GF broke.  It sets up a fake installRoot, 
15          *because there is *no* install-root.
16          *Improvement: don't just see if installRoot is set -- make sure installRoot
17          *is bonafide.
18           */ 
19         
20         if(installRootIsValid()) 
21             result = new SaxParserHandler();
22         else
23             result = new SaxParserHandlerBundled();
24 
25         return result;
26     }
27     
28     private static boolean installRootIsValid() {
29         // In the context of this class, we need to make sure that we know if we 
30         //have a route to local DTDs.  Period.
31         
32         String ir = System.getProperty(INSTALL_ROOT_PROPERTY);
33         
34         if(!ok(ir))
35             return false;
36         
37         File dtds = new File(new File(ir), "lib/dtds");
38         
39         if(!dtds.isDirectory())
40             return false;
41         
42         return true;
43     }
44 
45     private static boolean ok(String ir) {
46         return ir != null && ir.length() > 0;
47     }
48 

Notes that expects to find a directory called lib/dtds into our install root directory. So, again we can put theses files or we can change the world... And, I say:
- Hey, there is a stupid hero here, call me!

So, from the first problem until now, after several hours, there are more two jobs to do:

  • Fix the system property INSTALL_ROOT_PROPERTY to the correct gf embedded install root directory and ask to another traps appears..
    But if we set it to the correct directory, the SaxParserHandlerBundled doesn't works anymore...
  • Change my first solution copying the dtds and schemas to the /dtds and /schemas from the our jar with the patches.
Ok, let me do the both.

To do it, we must fix (break the broken code to tell truthly) the SaxParserHandlerFactory to always call SaxParserHandlerBundled. Then the solution is:

1     private static boolean ok(String ir) {
2         return "is world perfect?".equals(Boolean.TRUE.toString());
3     }
4 
And now, after changed the dtds/schemas to lives into the root of the our jar, and change the first solution to use it from this new place, I really want that it works... let me try.

Bad. I back... having bad news: org.glassfish.web.WebEntityResolver is the guy that looks for dtds and schemas into the install root directory. So, or we use the first version of our WebContainer that just append the backslash to the path of dtds and schemas to pass it to ParserUtils or, again, we change the world!

So, back again, after spending a precious time from my current project (my deadline is near dead...), I change WebContainer to use instance of a new EntityResolver capable to do the work that we expected. Also, I need to rewrite the SaxParserHandler supplied by the SaxParserHandlerFactory to work as neeeded.

A Hard Work, but now It Works

Yeah, the GF3 Embedded works without internet now! But it was too hard to fix.. and to remember, the files that I changed or created, was:

net.marciowb.web.ForABetterWorldAndSaxHandlers

   1 package net.marciowb.web;
   2 
   3 import com.sun.enterprise.deployment.node.SaxParserHandler;
   4 import com.sun.enterprise.deployment.util.DOLUtils;
   5 import java.io.InputStream;
   6 import java.util.logging.Level;
   7 import java.util.logging.Logger;
   8 import org.xml.sax.InputSource;
   9 import org.xml.sax.SAXException;
  10 
  11 /**
  12  *
  13  * @author Marcio
  14  */
  15 public class ForABetterWorldAndSaxHandlers extends SaxParserHandler {
  16 
  17     private static final Logger logger = Logger.getLogger(ForABetterWorldAndSaxHandlers.class.getName());
  18     /** prefixes for the paths to use for looking up schemas and dtds as resources */
  19     public static final String BUNDLED_SCHEMA_ROOT = "/schemas";
  20     public static final String BUNDLED_DTD_ROOT = "/dtds";
  21 
  22     /** Creates a new instance of SaxParserHandlerBundled */
  23     public ForABetterWorldAndSaxHandlers() {
  24     }
  25 
  26     /**
  27      *Returns an InputSource for the requested DTD or schema.
  28      *<p>
  29      *This implementation returns an InputSource that wraps the result
  30      *of getResourceAsStream, having
  31      *located the requested schema in the classpath.
  32      *@param the public ID of the requested entity
  33      *@param the system ID of the requested entity
  34      *@return InputSource for the requested entity; null if not available
  35      *@throws SAXException in case of errors resolving the entity
  36      */
  37     @Override
  38     public InputSource resolveEntity(String publicID, String systemID) throws SAXException {
  39         //InputSource is = (new SaxParserHandler()).resolveEntity(publicID, systemID);
  40         InputSource is = doResolveEntity(publicID, systemID);
  41         if (is == null) {
  42             logger.log(Level.WARNING, "We can't find {0} of the system {1}?", new Object[]{publicID, systemID});
  43         } else {
  44             if (is.getSystemId()==null) {
  45                 is.setSystemId(systemID);
  46             }
  47             if (is.getPublicId()==null) {
  48                 is.setPublicId(publicID);
  49             }
  50             logger.log(Level.FINE, "We found {0} of the system {1}.", new Object[]{publicID, systemID});
  51         }
  52         return is;
  53     }
  54 
  55     public InputSource doResolveEntity(String publicID, String systemID) throws SAXException {
  56         /*
  57          *This logic was patterned after that in the superclass.
  58          */
  59         try {
  60             if (publicID == null) {
  61                 // unspecified schema
  62                 if (systemID == null || systemID.lastIndexOf('/') == systemID.length()) {
  63                     return null;
  64                 }
  65 
  66                 /*
  67                  *Attempt to open a stream to the requested resource as a schema.
  68                  */
  69                 InputStream is = openSchemaStream(systemID);
  70 
  71                 if (is != null) {
  72                     /*
  73                      *We found the entity, so wrap an InputSource around the stream.
  74                      */
  75                     return new InputSource(is);
  76                 } else {
  77                     /*
  78                      *The entity was not found, so wrap an InputSource around the system ID string instead.
  79                      */
  80                     return new InputSource(systemID);
  81                 }
  82             } else {
  83                 /*
  84                  *Try to find a DTD for the entity.
  85                  */
  86                 if (getMapping().containsKey(publicID)) {
  87                     this.publicID = publicID;
  88                     InputStream is = openDTDStream(publicID);
  89                     if (is != null) {
  90                         return new InputSource(is);
  91                     }
  92                 } else if (systemID != null) {
  93                     /*
  94                      *The DTD lookup failed but a system ID was also specified.  Try
  95                      *looking up the DTD in the schemas path, because some reside
  96                      *there and were not registered in the DTD map by SaxParserHandler.
  97                      */
  98                     InputStream is = openSchemaStream(systemID);
  99                     if (is != null) {
 100                         return new InputSource(is);
 101                     }
 102 
 103                     /*
 104                      * As a last resort, try opening the DTD without going
 105                      * through the mapping table.
 106                      */
 107                     is = openStream(BUNDLED_DTD_ROOT, systemID);
 108                     if (is != null) {
 109                         return new InputSource(is);
 110                     }
 111 
 112                     is = openStream(BUNDLED_SCHEMA_ROOT, systemID);
 113                     if (is != null) {
 114                         return new InputSource(is);
 115                     }
 116                 }
 117             }
 118         } catch (Exception exc) {
 119             DOLUtils.getDefaultLogger().log(Level.SEVERE, "Error occurred", exc);
 120             throw new SAXException(exc);
 121         }
 122         return null;
 123     }
 124 
 125     /**
 126      *Returns an InputStream for the schema with the requested system ID.
 127      *@param systemID of the schema
 128      *@return an InputStream to the selected schema; null if the schema is not available
 129      */
 130     protected InputStream openSchemaStream(String systemID) {
 131         return openStream(BUNDLED_SCHEMA_ROOT, systemID);
 132     }
 133 
 134     /**
 135      *Returns an InputStream for the DTD with the requested public ID.
 136      *@param the publicID of the DTD requested
 137      *@return an InputStream to the selected DTD; null if the DTD is not available
 138      */
 139     protected InputStream openDTDStream(String publicID) {
 140         return openStream(BUNDLED_DTD_ROOT, getMapping().get(publicID));
 141     }
 142 
 143     protected InputStream openStream(final String localRoot, final String systemID) {
 144         String targetID = localRoot + "/" + systemID.substring(systemID.lastIndexOf('/') + 1);
 145         InputStream result = getClass().getResourceAsStream(targetID);
 146         if (result == null) {
 147             logger.log(Level.CONFIG, "Where is located {0} of the system {1}?", new Object[]{localRoot, systemID});
 148         } else {
 149             logger.log(Level.FINEST, "We found {0} of the system {1}.", new Object[]{localRoot, systemID});
 150         }
 151         return result;
 152     }
 153 }
 154 

net.marciowb.web.WebEntityResolverHowToRegisterIt

   1 /*
   2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
   3  *
   4  * Copyright 1997-2008 Sun Microsystems, Inc. All rights reserved.
   5  *
   6  * The contents of this file are subject to the terms of either the GNU
   7  * General Public License Version 2 only ("GPL") or the Common Development
   8  * and Distribution License("CDDL") (collectively, the "License").  You
   9  * may not use this file except in compliance with the License. You can obtain
  10  * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
  11  * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
  12  * language governing permissions and limitations under the License.
  13  *
  14  * When distributing the software, include this License Header Notice in each
  15  * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
  16  * Sun designates this particular file as subject to the "Classpath" exception
  17  * as provided by Sun in the GPL Version 2 section of the License file that
  18  * accompanied this code.  If applicable, add the following below the License
  19  * Header, with the fields enclosed by brackets [] replaced by your own
  20  * identifying information: "Portions Copyrighted [year]
  21  * [name of copyright owner]"
  22  *
  23  * Contributor(s):
  24  *
  25  * If you wish your version of this file to be governed by only the CDDL or
  26  * only the GPL Version 2, indicate your decision by adding "[Contributor]
  27  * elects to include this software in this distribution under the [CDDL or GPL
  28  * Version 2] license."  If you don't indicate a single choice of license, a
  29  * recipient has the option to distribute your version of this file under
  30  * either the CDDL, the GPL Version 2 or to extend the choice of license to
  31  * its licensees as provided above.  However, if you add GPL Version 2 code
  32  * and therefore, elected the GPL Version 2 license, then the option applies
  33  * only if the new code is made subject to such option by the copyright
  34  * holder.
  35  *
  36  */
  37 package net.marciowb.web;
  38 
  39 import java.util.logging.Level;
  40 import java.util.logging.Logger;
  41 import org.xml.sax.EntityResolver;
  42 import org.xml.sax.InputSource;
  43 import org.xml.sax.SAXException;
  44 
  45 import org.jvnet.hk2.annotations.Service;
  46 import org.jvnet.hk2.annotations.ContractProvided;
  47 import org.jvnet.hk2.component.PostConstruct;
  48 import org.apache.catalina.startup.Constants;
  49 
  50 import java.io.IOException;
  51 import java.util.Map;
  52 
  53 import com.sun.enterprise.util.MapBuilder;
  54 
  55 /**
  56  * {@link EntityResolver} that recognizes known public IDs of JavaEE DTDs/schemas
  57  * and return a local copy.
  58  *
  59  * <p>
  60  * This implementation assumes that those files are available in
  61  * <tt>/schemas</tt> and <tt>/dtds</tt> of this Jar package (same classloader of
  62  * this class). But in different environment, different implementation can be plugged in
  63  * to perform entirely different resolution.
  64  *
  65  * How to register this service?
  66  *
  67  * @author Marcio Wesley Borges
  68  */
  69 @Service(name = "web")
  70 @ContractProvided(EntityResolver.class)
  71 public class WebEntityResolverHowToRegisterIt implements EntityResolver, PostConstruct {
  72     //@Inject
  73     //ServerContext serverContext;
  74 
  75     /**
  76      * Known DTDs.
  77      *
  78      * Expose the map so that interested party can introspect the table value and modify them.
  79      */
  80     public final Map<String/*public id*/, String/*bare file name*/> knownDTDs = new MapBuilder<String, String>().put(Constants.TldDtdPublicId_11, "web-jsptaglibrary_1_1.dtd").put(Constants.TldDtdPublicId_12, "web-jsptaglibrary_1_2.dtd").put(Constants.WebDtdPublicId_22, "web-app_2_2.dtd").put(Constants.WebDtdPublicId_23, "web-app_2_3.dtd").build();
  81 
  82     @Override
  83     public void postConstruct() {
  84         //File root = serverContext.getInstallRoot();
  85         //File libRoot = new File(root, "lib");
  86     }
  87 
  88     /**
  89      * If the parser hits one of the well-known DTDs, parse local copies instead of hitting
  90      * the remote server.
  91      */
  92     @Override
  93     public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
  94         InputSource is = doResolveEntity(publicId, systemId);
  95         if (is == null) {
  96             Logger.getLogger(WebEntityResolverHowToRegisterIt.class.getName()).log(Level.CONFIG, "Where is located entity {0} of the system {1}?", new Object[]{publicId, systemId});
  97         }
  98 
  99         return is;
 100     }
 101 
 102     private InputSource doResolveEntity(String publicId, String systemId) throws SAXException, IOException {
 103         String fileName = knownDTDs.get(publicId);
 104         if (fileName != null) {
 105             java.io.InputStream stream = getClass().getResourceAsStream("/dtds/" +fileName);
 106             if (stream == null) {
 107                 return null;
 108             }
 109             return new InputSource(stream);
 110         }
 111 
 112         return null;
 113     }
 114 }
 115 

com.sun.enterprise.deployment.node.SaxParserHandlerFactory

   1 /*
   2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
   3  *
   4  * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
   5  *
   6  * The contents of this file are subject to the terms of either the GNU
   7  * General Public License Version 2 only ("GPL") or the Common Development
   8  * and Distribution License("CDDL") (collectively, the "License").  You
   9  * may not use this file except in compliance with the License. You can obtain
  10  * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
  11  * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
  12  * language governing permissions and limitations under the License.
  13  *
  14  * When distributing the software, include this License Header Notice in each
  15  * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
  16  * Sun designates this particular file as subject to the "Classpath" exception
  17  * as provided by Sun in the GPL Version 2 section of the License file that
  18  * accompanied this code.  If applicable, add the following below the License
  19  * Header, with the fields enclosed by brackets [] replaced by your own
  20  * identifying information: "Portions Copyrighted [year]
  21  * [name of copyright owner]"
  22  *
  23  * Contributor(s):
  24  *
  25  * If you wish your version of this file to be governed by only the CDDL or
  26  * only the GPL Version 2, indicate your decision by adding "[Contributor]
  27  * elects to include this software in this distribution under the [CDDL or GPL
  28  * Version 2] license."  If you don't indicate a single choice of license, a
  29  * recipient has the option to distribute your version of this file under
  30  * either the CDDL, the GPL Version 2 or to extend the choice of license to
  31  * its licensees as provided above.  However, if you add GPL Version 2 code
  32  * and therefore, elected the GPL Version 2 license, then the option applies
  33  * only if the new code is made subject to such option by the copyright
  34  * holder.
  35  */
  36 
  37 package com.sun.enterprise.deployment.node;
  38 
  39 import net.marciowb.web.ForABetterWorldAndSaxHandlers;
  40 import static com.sun.enterprise.util.SystemPropertyConstants.INSTALL_ROOT_PROPERTY;
  41 
  42 import java.io.File;
  43 
  44 /**
  45  *Provides the appropriate implementation depending on the current
  46  *runtime environment.
  47  *
  48  * @author tjquinn
  49  */
  50 public class SaxParserHandlerFactory {
  51 
  52     /** Creates a new instance of SaxParserHandlerFactory */
  53     public SaxParserHandlerFactory() {
  54     }
  55 
  56     public static SaxParserHandler newInstance() {
  57         SaxParserHandler result = null;
  58 
  59         /*
  60          *If the property com.sun.aas.installRoot is defined, use the
  61          *original implementation (SaxParserHandler) which fetches DTDs and
  62          *schemas from the installation directory tree.  Otherwise, assume that
  63          *the app client container is running under Java Web Start. In that
  64          *case, there is no product installation directory (at least none can
  65          *be assumed).  The DTDs and schemas will be retrieved from the
  66          *JWS-specific jar file instead (SaxParserHandlerBundled).
  67          *
  68          *bnevins, Oct 16, 2008.  On Oct. 8, 2008 installRoot was changed to be setup
  69          *earlier in the startup.  As a result, Embedded GF broke.  It sets up a fake installRoot,
  70          *because there is *no* install-root.
  71          *Improvement: don't just see if installRoot is set -- make sure installRoot
  72          *is bonafide.
  73           */
  74 
  75         if(installRootIsValid())
  76             result = new SaxParserHandler();
  77         else
  78             result = new ForABetterWorldAndSaxHandlers();
  79 
  80         return result;
  81     }
  82 
  83     private static boolean installRootIsValid() {
  84         // In the context of this class, we need to make sure that we know if we
  85         //have a route to local DTDs.  Period.
  86 
  87         String ir = System.getProperty(INSTALL_ROOT_PROPERTY);
  88 
  89         if(!ok(ir))
  90             return false;
  91 
  92         File dtds = new File(new File(ir), "lib/dtds");
  93 
  94         if(!dtds.isDirectory())
  95             return false;
  96 
  97         return true;
  98     }
  99 
 100     private static boolean ok(String ir) {
 101         return "is the world a perfect place?".equals(Boolean.TRUE.toString());
 102     }
 103 
 104 }
 105 

com.sun.enterprise.web.WebContainer

   1         // TODO: ParserUtils should become a @Service and it should initialize itself.
   2         // TODO: there should be only one EntityResolver for both DigesterFactory
   3         // and ParserUtils
   4         File root = _serverContext.getInstallRoot();
   5         
   6         java.net.URL schemas = getClass().getResource("/schemas");
   7         java.net.URL dtds = getClass().getResource("/dtds");
   8 
   9         //try {            
  10             ParserUtils.setSchemaResourcePrefix(schemas.toString() + "/");
  11             ParserUtils.setDtdResourcePrefix(dtds.toString() + "/");
  12             ParserUtils.setEntityResolver(new WebEntityResolverHowToRegisterIt());
  13         //} catch(MalformedURLException e) {
  14         //    _logger.log(Level.SEVERE, "webContainer.exceptionSetSchemasDtdsLocation", e);
  15         //}
  16 
  17         instanceName = _serverContext.getInstanceName();
  18 

The Patch

If you prefer, I put the download bellow to simplifier your work: It's all needed.

I will go back to my work for now. Bye.

Ps.: Oracle, feel free to pay me for the time that I expended to fix it. I lost more than 15 precious hours of work devoted to resolve this.

Labels: , , , ,