4

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
skaffman
  • 398,947
  • 96
  • 818
  • 769
3urdoch
  • 7,192
  • 8
  • 42
  • 58

3 Answers3

17

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">
3urdoch
  • 7,192
  • 8
  • 42
  • 58
3

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.

Ujjwal
  • 603
  • 12
  • 23
  • jndi protocol working fine on development machine. But on ubuntu server can't understand jndi. Do you know why? – hurelhuyag Sep 07 '17 at 03:05
0

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.

DaShaun
  • 3,722
  • 2
  • 27
  • 29
  • Yes I have done that but this is where the problem of the changing location on different developer machines comes in. The DTD path then has to be absolute, or relative to the working directory but the working directory isn't the same as the webapp root directory in my case its the tomcat instillation dir (or the eclipse instillation dir when developing in eclipse) – 3urdoch Mar 02 '11 at 17:23
  • It only has to be relative to the root of the application. So place the downloaded .dtd into your war file in /dtds – DaShaun Mar 02 '11 at 21:34
  • Tried that, doesn't work when i run it in eclipse it looks in the eclipse home directory and even throws a FileNotFoundException stating where it looked. "Caused by: java.io.FileNotFoundException: C:\Program Files (x86)\Eclipse Helios\tiles-config_2_1.dtd (The system cannot find the file specified)" if its running in tomcat its the same deal but the tomcat working directory. – 3urdoch Mar 03 '11 at 07:42