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:
- Java.net: Embedded glassfish in offline mode
- Java.net: Deploy war in embedded Glassfish
- Glassfish Issue #11516
The Diagnostic
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
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.
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 } 4And 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:- Zip with Sources and compiled jar: Netbeans Project
Reference it in your project before theglassfish-embedded-all-3.0.1.jar
. - Compiled jar: Just the library that you need to refer
Use it together with theglassfish-embedded-all-3.0.1.jar
archive.
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: development, it, java, tips, web