I recommend using a maven plugin to package your logic. The reason is that the ANT antcall and XSLT tasks require file inputs (and do not support URIs). When you package these file resources into a jar (as you do with a plugin) then you can extract them to the correct relative filesystem locations as part of your mojo startup code. That means you don’t require developers to place these files into the projects themselves. This also means that you control the distribution of these key files and can replace them all at once in a new maven plugin version without having to email everyone that uses your artifacts.
The following source code example represents a java class that performs the setup and execution of your deployment logic:
package com.mycompany; import java.io.File; import java.io.IOException; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.project.MavenProject; import org.apache.tools.ant.Project; /** * Deploy the portlet onto a running WebSphere portal server. * * <p> * Algorithm: * <li>Copy resources that must be in project filesystem. * <li>Pass all maven properties required to the ANT project. * <li>Call the appropriate ANT target. * * @goal deploy * @phase install * @see http://docs.codehaus.org/display/MAVENUSER/Mojo+Developer+Cookbook * @see http://www.vineetmanohar.com/2009/11/3-ways-to-run-java-main-from-maven/ */ public class DeployMojo extends AbstractMojo { /** * @parameter default-value="${project}" */ private MavenProject project; /** * A WPS account with admin priveleges. * @parameter expression="${wpsuser}" default-value="wpadmin" */ private String wpsuser; /** * The password for wpsuser. * @parameter expression="${wpspassword}" default-value="wpadmin" */ private String wpspassword; /** * The port WPS is running on. Shown as WC_defaulthost in the WAS console. * @parameter expression="${wpsport}" default-value="10039" */ private String wpsport; /** * The web application root for the portal server. * @parameter expression="${wpscontext}" default-value="wps" */ private String wpscontext; /** * The DNS host name or IP address of the portal server host machine. * @parameter expression="${wpshost}" default-value="localhost" */ private String wpshost; /** * Skip the portlet deployment. * @parameter expression="${skipportal}" default-value="false" */ private boolean skipportal; /** * Set this property to perform a remote portal deployment. * The syntax is what you see after the @host: string in an scp command line. * For example: /opt/app/WebSphere/wp_profile/installableApps * @parameter expression="${remotedir}" */ private String remotedir; /** * A user account on the remote machine with write privileges to remotedir. * @parameter expression="${scpuser}" */ private String scpuser; /** * Password for the scpuser account. * @parameter expression="${scppassword}" */ private String scppassword; public void execute() throws MojoExecutionException, MojoFailureException { if (skipportal) { getLog().info("Skipping portal deployment."); return; } /* * Construct and configure an ANT project. */ Project antproj; try { antproj = AntDelegate.getProject(); AntDelegate.extract(new File("target/xmlaccess-input.xsl"), "/xmlaccess-input.xsl"); } catch (IOException e) { throw new MojoExecutionException("Problem reading jar resource.", e); } setProperties(antproj); /* * Execute the correct ANT target based on the plugin parameters. */ if (remotedir != null) { antproj.executeTarget("install.remote"); } else { antproj.executeTarget("install.local"); } } /** * Map all the maven environment properties into the ANT environment. */ private void setProperties(Project antproj) { antproj.setProperty("project.artifactId", project.getArtifactId()); antproj.setProperty("project.version", project.getVersion()); antproj.setProperty("wpsuser", wpsuser); antproj.setProperty("wpspassword", wpspassword); antproj.setProperty("wpsport", wpsport); antproj.setProperty("wpscontext", wpscontext); antproj.setProperty("wpshost", wpshost); antproj.setProperty("remotedir", remotedir); antproj.setProperty("scpuser", scpuser); antproj.setProperty("scppassword", scppassword); } }
The next source code example shows a class that handles common code for other mojos in your plugin:
package com.mycompany; import java.io.File; import java.io.IOException; import java.util.Calendar; import org.apache.commons.io.FileUtils; import org.apache.tools.ant.DefaultLogger; import org.apache.tools.ant.Project; import org.apache.tools.ant.ProjectHelper; import org.apache.tools.ant.util.DateUtils; /** * Extract the build.xml file from the plugin jar and configure an ANT project. * @author kstclair */ public class AntDelegate { static public Project getProject() throws IOException { File buildfile = extract(new File("target/build.xml"), "/build.xml"); /* * Find and parse the buildfile */ Project antproj = new Project(); antproj.setProperty("ant.file", buildfile.getAbsolutePath()); /* * Add a logger to see results from the buildfile */ DefaultLogger consoleLogger = new DefaultLogger(); consoleLogger.setErrorPrintStream(System.err); consoleLogger.setOutputPrintStream(System.out); consoleLogger.setMessageOutputLevel(Project.MSG_INFO); antproj.addBuildListener(consoleLogger); antproj.init(); ProjectHelper helper = ProjectHelper.getProjectHelper(); antproj.addReference("ant.projectHelper", helper); helper.parse(antproj, buildfile); return antproj; } /** * Use apache commmons-io to extract the required build files from the plugin jar * to the target directory of the project. * @param myfile * @param myUrl * @return * @throws IOException */ static public File extract(File myfile, String uri) throws IOException { if (!myfile.exists()) { FileUtils.copyInputStreamToFile(AntDelegate.class.getResourceAsStream(uri), myfile); } return myfile; } /** * Add a timestamp extension to the user file. This is intended for the Hudson environment. * @param serverTimestamp * @param rulefile * @return */ static public String calcTimestamp(boolean serverTimestamp, String rulefile) { if (serverTimestamp) { String extension = DateUtils.format(Calendar.getInstance().getTime(), ".yyyyMMddHHmmss"); rulefile = rulefile + extension; } return rulefile; } }
Some readers will notice that I extract the build.xml and the xmlaccess-input.xsl in two different spots. The reason is that AntDelegate is used by other goals that are packaged with the maven plugin.
The mojo developer cookbook shows many of the common code snippets you will use. The following portions are included in the pom.xml that define your maven plugin so that maven knows you are creating a maven-plugin (ellipses indicate missing portions of the xml file):
<packaging>maven-plugin</packaging>...<dependency> <groupId>org.apache.maven</groupId> <artifactId>maven-plugin-api</artifactId> <version>2.0</version> </dependency> <dependency> <groupId>org.apache.maven</groupId> <artifactId>maven-core</artifactId> <version>2.2.1</version> </dependency>...
Finally this snippet will ensure the maven generated site will produce good documentation for your plugin:
...<reporting> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-plugin-plugin</artifactId> <version>2.5.1</version> </plugin> </plugins> </reporting>...
The next post in this series will show how to bind this plugin to the correct build phase.
Next up: WebSphere Portal and Maven (Part 6)