Struts2 & Tiles: When apache.org is down my webapp

2019-03-30 06:30发布

问题:

I am building a Struts2 web application which uses tiles however I have discovered a quite frustrating problem where if apache.org is down (which seems to happen quite regularly) the web application fails to start. This is because in its standard setup the StrutsTilesListener tries to load the tiles defenitions file which includes a DOCTYPE with a public-id which points to a DTD located on tiles.apache.org.

When the application starts up the definition file is loaded using Apache Xerces via Apache Commons Digester which tries to load the DTD from tiles.apache.org but if apache.org is down then this fails and with it the whole web application wont start.

I can bypass the download from a remote location by downloading the file and placing it local and specifying the new local location in the struts definitions file, however this solution is not very portable as the location where the DTD is saved locally may be different on different developer machines and different once uploaded to a live environment so I would have to keep editing the location so suite the machine the webapp is running on which is just plain annoying.

No other xml files in the project have this problem, including the struts.xml file which also has a DTD location on apache.org so clearly there is a setup problem where Tiles is strictly requiring the DTD but other components are not. Is there any solution to this? I am running out of patience and I cannot put this webapp live knowing that if apache.org is down when I restart it the webapp wont come back up.

Struts tiles defenition file

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE tiles-definitions PUBLIC
       "-//Apache Software Foundation//DTD Tiles Configuration 2.1//EN"
       "http://tiles.apache.org/dtds/tiles-config_2_1.dtd">
<tiles-definitions>
    <definition name="master" template="/tiles/templates/master.jsp">
    </definition>
    <definition name="public" extends="master">
        <put-attribute name="header" value="/tiles/templates/public/header.jsp" />
        <put-attribute name="footer" value="/tiles/templates/public/footer.jsp" />
        <put-attribute name="templateMeta" value="/tiles/templates/public/meta.jsp" />
    </definition>  
</tiles-definitions>

Stacktrace when apache.org is down

SEVERE: Exception sending context initialized event to listener instance of class org.apache.struts2.tiles.StrutsTilesListener
java.lang.IllegalStateException: Unable to instantiate container.
    at org.apache.tiles.web.startup.TilesListener.contextInitialized(TilesListener.java:60)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3972)
    at org.apache.catalina.core.StandardContext.start(StandardContext.java:4467)
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
    at org.apache.catalina.core.StandardHost.start(StandardHost.java:785)
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
    at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443)
    at org.apache.catalina.core.StandardService.start(StandardService.java:519)
    at org.apache.catalina.core.StandardServer.start(StandardServer.java:710)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:581)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)
Caused by: org.apache.tiles.definition.DefinitionsFactoryException: I/O Error reading definitions.
    at org.apache.tiles.definition.digester.DigesterDefinitionsReader.read(DigesterDefinitionsReader.java:273)
    at org.apache.tiles.definition.UrlDefinitionsFactory.readDefinitions(UrlDefinitionsFactory.java:286)
    at org.apache.tiles.definition.UrlDefinitionsFactory.init(UrlDefinitionsFactory.java:130)
    at org.apache.tiles.impl.BasicTilesContainer.initializeDefinitionsFactory(BasicTilesContainer.java:406)
    at org.apache.tiles.impl.BasicTilesContainer.init(BasicTilesContainer.java:130)
    at org.apache.tiles.factory.TilesContainerFactory.initializeContainer(TilesContainerFactory.java:232)
    at org.apache.tiles.factory.TilesContainerFactory.createTilesContainer(TilesContainerFactory.java:198)
    at org.apache.tiles.factory.TilesContainerFactory.createContainer(TilesContainerFactory.java:163)
    at org.apache.tiles.web.startup.TilesListener.createContainer(TilesListener.java:90)
    at org.apache.struts2.tiles.StrutsTilesListener.createContainer(StrutsTilesListener.java:68)
    at org.apache.tiles.web.startup.TilesListener.contextInitialized(TilesListener.java:57)
    ... 15 more
Caused by: java.net.SocketException: Connection reset
    at java.net.SocketInputStream.read(Unknown Source)
    at java.io.BufferedInputStream.fill(Unknown Source)
    at java.io.BufferedInputStream.read1(Unknown Source)
    at java.io.BufferedInputStream.read(Unknown Source)
    at sun.net.www.http.HttpClient.parseHTTPHeader(Unknown Source)
    at sun.net.www.http.HttpClient.parseHTTP(Unknown Source)
    at sun.net.www.http.HttpClient.parseHTTP(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
    at org.apache.commons.digester.Digester.createInputSourceFromURL(Digester.java:2072)
    at org.apache.commons.digester.Digester.resolveEntity(Digester.java:1725)
    at com.sun.org.apache.xerces.internal.util.EntityResolverWrapper.resolveEntity(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.resolveEntityAsPerStax(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$DTDDriver.dispatch(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$DTDDriver.next(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$PrologDriver.next(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source)
    at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(Unknown Source)
    at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(Unknown Source)
    at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(Unknown Source)
    at org.apache.commons.digester.Digester.parse(Digester.java:1887)
    at org.apache.tiles.definition.digester.DigesterDefinitionsReader.read(DigesterDefinitionsReader.java:267)
    ... 25 more

回答1:

I have discovered the problem and it is my fault, everything I said in my question was true however it was only true because there was a mismatch between the DTD version that was declared in the tiles.xml file and the version of tiles I was using.

I am actually using Tiles 2.0.6 but was referencing the DTD from tiles 2.1 so tiles was not referencing the bundled DTD and trying to download it instead.

<!DOCTYPE tiles-definitions PUBLIC
       "-//Apache Software Foundation//DTD Tiles Configuration 2.1//EN"
       "http://tiles.apache.org/dtds/tiles-config_2_1.dtd">

Should have been

<!DOCTYPE tiles-definitions PUBLIC
       "-//Apache Software Foundation//DTD Tiles Configuration 2.0//EN"
       "http://tiles.apache.org/dtds/tiles-config_2_0.dtd">


回答2:

I got a similar exception with below root cause -

Caused by: org.apache.tiles.definition.DefinitionsFactoryException: I/O Error reading definitions.
at org.apache.tiles.definition.digester.DigesterDefinitionsReader.read(DigesterDefinitionsReader.java:273)
at org.apache.tiles.definition.UrlDefinitionsFactory.readDefinitions(UrlDefinitionsFactory.java:286)

I am using Apache Tile 3.0.

Solution:

I downloaded the file "tiles-config_3_0.dtd" and placed it in WEB-INF/dtd dir. Made below changes in the tiles-definition.xml file -

<!DOCTYPE tiles-definitions PUBLIC
   "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN"
   "jndi:/localhost/myapp/WEB-INF/dtd/tiles-config_3_0.dtd">

It works fine and does not look for absolute path of the file thereafter.



回答3:

You can grab the dtd and place it in your application. Then change the URL to reference your local copy.

Just do a "wget" or view it in your browser and save the file to your project.