I’m working to perfect my build script for using Gradle with Apache Pluto. Apache Pluto comes with an Ant task named passemble that creates a new web.xml file with some additional information. Unfortunately there doesn’t seem to be a way to specify in Eclipse that I want to use this particular web.xml (instead of the one in /WebContent/WEB-INF) when I go to export the application as war. Therefore I’m using Gradle’s war task to do the job. Unfortunately Gradle’s war task wants to compile my source files first which I don’t want it to do. I’ll explain why in a minute. I just wanted to say that I can execute the war task successfully if I include the following in my dependencies block:
compile 'javax.portlet:portlet-api:2.0'
Unfortunately if I do that and I use the war file that Gradle creates when I go to deploy the portlet I get a ClassCastException telling me that my portlet class cannot be cast to javax.portlet.Portlet. I can get my application to deploy successfully, however, if I just replace the web.xml file in /WebContent/WEB-INF with the one created by passemble and then use Eclipse to export the application as a war.
So my question is: How do I tell the war task not to compile my source files, but instead use the class files that Eclipse creates?
Here’s my build script
apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'eclipse-wtp'
configurations {
passemble
}
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
passemble 'org.apache.portals.pluto:pluto-ant-tasks:2.0.3'
}
task passemble {
description = 'Assemble project so that it may be run on Apache Pluto'
doLast {
println 'creating taskdef passemble...'
ant.taskdef(name: 'passemble', classname: 'org.apache.pluto.ant.AssembleTask', classpath: configurations.passemble.asPath)
println 'executing task passemble...'
ant.passemble(
webxml:
'./src/main/webapp/WEB-INF/web.xml',
portletxml: './src/main/webapp/WEB-INF/portlet.xml',
destfile: 'web.xml')
}
}
war {
// prevent Gradle from copying './src/main/webapp/WEB-INF/web.xml'
duplicatesStrategy = 'EXCLUDE'
webXml = file('web.xml')
destinationDir = project.file('.')
webInf {
from 'src/main/webapp/WEB-INF/portlet.xml'
}
}
PS. The reason I want to keep the /WebContent/WEB-INF/web.xml file unchanged is that the passemble task will copy the contents of the web-app element’s body. So each time I run passemble its going to duplicate some of the information already there.
If I understand correctly, you want a portlet WAR file built by something so that it deploys correctly. You’re having trouble with Eclipse because it’s using a web.xml that is incorrect and you’re having trouble with Gradle because when you run the portlet you get a ClassCastException. I’m not sure the approach you’re asking about is going to be possible or useful.
If I were in your situation, I’d probably try to look at why the Gradle build produces something different from the Eclipse build. Can you post information about what Java compiler and version you are running on your system, what version of Gradle, what portlet container you’re using and possibly a stacktrace of the error message you receive?
C:\javac -version javac 1.7.0_07
C:\gradle --version Gradle 1.7
Yes, I know it’s an old version of Gradle.
I’m using Apache Pluto 2.0.3 bundled with Apache Tomcat 7.0.21 as the portlet container. <
It would be useful because than I could keep my web.xml as is:
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app id="WebApp_ID">
</web-app>
Each time I execute the passemble task it adds content similar to the following to web-app’s element’s body:
<servlet>
<servlet-name>HelloWorldPortlet</servlet-name>
<servlet-class>org.apache.pluto.container.driver.PortletServlet</servlet-class>
<init-param>
<param-name>portlet-name</param-name>
<param-value>HelloWorldPortlet</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>HelloWorldPortlet</servlet-name>
<url-pattern>/PlutoInvoker/HelloWorldPortlet</url-pattern>
</servlet-mapping>
So my resulting web.xml file looks something like this.
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app id="WebApp_ID">
<servlet>
<servlet-name>HelloWorldPortlet</servlet-name>
<servlet-class>org.apache.pluto.container.driver.PortletServlet</servlet-class>
<init-param>
<param-name>portlet-name</param-name>
<param-value>HelloWorldPortlet</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>HelloWorldPortlet</servlet-name>
<url-pattern>/PlutoInvoker/HelloWorldPortlet</url-pattern>
</servlet-mapping>
</web-app>
Now if I run the passemble task again it’s going to duplicate some of that information:
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app id="WebApp_ID">
<servlet>
<servlet-name>HelloWorldPortlet</servlet-name>
<servlet-class>org.apache.pluto.container.driver.PortletServlet</servlet-class>
<init-param>
<param-name>portlet-name</param-name>
<param-value>HelloWorldPortlet</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>HelloWorldPortlet</servlet-name>
<url-pattern>/PlutoInvoker/HelloWorldPortlet</url-pattern>
</servlet-mapping>
<!-- The following is already in the web.xml file -->
<servlet>
<servlet-name>HelloWorldPortlet</servlet-name>
<servlet-class>org.apache.pluto.container.driver.PortletServlet</servlet-class>
<init-param>
<param-name>portlet-name</param-name>
<param-value>HelloWorldPortlet</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>HelloWorldPortlet</servlet-name>
<url-pattern>/PlutoInvoker/HelloWorldPortlet</url-pattern>
</servlet-mapping>
</web-app>
Obviously I don’t want that to happen.
Right, I should clarify that my comments around usefulness were about bundling the class files from Eclipse rather than have Gradle compile and package the WAR file. Although Eclipse uses its own Java compiler, the generated WAR between Eclipse and Gradle should work. Do you have a stacktrace of the error you get when trying to use the Gradle-built WAR? This may help me understand why the portlet isn’t running.
I also see that you’re running an older version of Java 7, have you tried to upgrade it to a more recent version of Java 7? Also, what does the generated portlet.xml look like?
I came up with a better solution than the one I started with.
apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'eclipse-wtp'
configurations {
passemble
}
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
passemble 'org.apache.portals.pluto:pluto-ant-tasks:2.0.3'
}
sourceSets {
main {
java {
srcDirs "src"
}
resources {
srcDir "src"
}
}
}
eclipse {
wtp {
component {
resource deployPath: '/', sourcePath: '/WebContent', tag: 'defaultRootSource'
resource deployPath: '/WEB-INF/classes', sourcePath: '/src'
}
}
}
task passemble {
description = 'Assemble project so that it may be run on Apache Pluto'
doLast {
println 'creating taskdef passemble...'
ant.taskdef(name: 'passemble', classname: 'org.apache.pluto.ant.AssembleTask', classpath: configurations.passemble.asPath)
println 'creating temporary web.xml file...'
File tmp = file('web.xml')
tmp.createNewFile()
PrintWriter printWriter = new PrintWriter(tmp);
printWriter.println '<?xml version="1.0" encoding="UTF-8" standalone="no"?>'
printWriter.println '<!DOCTYPE web-app PUBLIC'
printWriter.println '
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"'
printWriter.println '
"http://java.sun.com/dtd/web-app_2_3.dtd">'
printWriter.println '<web-app id="WebApp_ID">'
printWriter.println '</web-app>'
printWriter.close();
println 'executing task passemble...'
ant.passemble(
webxml: 'web.xml',
portletxml: 'WebContent/WEB-INF/portlet.xml',
destfile: 'WebContent/WEB-INF/web.xml')
println 'deleting temporary web.xml file...'
delete file('web.xml')
}
}
You want to develop a portlet web application. You are using Eclipse with WTP plugins (that I am not familiar with). And there is a problem that you need to process application classes/resources/input web.xml to build correct web.xml that will be part of your application. My simple search found some relevant links showing that there were people who had this problem before and there can be a plugin addressing this. Most likely this plugin will add a builder step to your project to do ‘passemble’ either internally or by spawning Ant build.
Now you want to address your problem by using Gradle and see another problems. Your solution is to create isolated passemble task that will process sources and generate the output. Seems good enough for your development workflow but the WAR is not properly built. It is not sufficient if you want to really have a good way how to build your application (command line, continuous build). One hint to help you with your classpath / dependency problem is at http://www.gradle.org/docs/current/userguide/war_plugin.html#N1301A - you likely want to declare your portlet API JAR dependency as compileProvided. It will be added to compilation classpath and it won’t be packaged into your application as this causes conflicts between same classes loaded by your Tomcat/Pluto container at system (global) level and by your application level in its webapp context.
I don’t need another plugin to work with Ant inside Gradle. See Chapter 17. Using Ant from Gradle
I no longer need to overwrite the war task. In fact, that task will fail if I tried it. Instead I use Eclipse to export my project as a war. Unfortunately I still need to use the war plugin in my build script otherwise I get the following…
Could not find method resource() for arguments [{deployPath=/, sourcePath=/WebContent, tag=defaultRootSource}] on org.gradle.plugins.ide.eclipse.model.EclipseWtp_Decorated@31153b19.
My problem has been solved.
PS. I just realized that I don’t need to use the PrinterWriter in my build script. I just need to make sure there’s a basic web.xml file in the root of my application.