package org.openntf.maven; import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringEscapeUtils; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Execute; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; import org.codehaus.plexus.util.StringUtils; @Mojo(name = "ddehd") @Execute(goal = "ddehd") public class HeadlessDesignerBuilder extends AbstractDesignerPlugin { private static final String UNINSTALL_TXT = "uninstall.txt"; private static final String DISABLE_TXT = "disable.txt"; private static final String BUILD_TXT = "build.txt"; private static final String ENABLE_TXT = "enable.txt"; private static final String INSTALL_TXT = "install.txt"; private final Pattern NOTESPATTERN = Pattern.compile("(notes2.exe.*? )"); private static final ThreadLocal<DateFormat> NOTES_DATE_FORMAT = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { // 20160307T183605,00-05 return new SimpleDateFormat("yyyyMMdd"); }; }; /** * Path to the designer.exe (e.g. C:\Program Files\IBM\Notes\Designer.exe) */ @Parameter(property = "ddehd.designerexec", defaultValue = "designer.exe") private String m_DesignerExec; /** * Path to the notes data directory (e.g. C:\Program Files\IBM\Notes\Data) */ @Parameter(property = "ddehd.notesdata") private String m_NotesData; @Parameter(property = "ddehd.odpdirectory") private String m_ODPDirectory; /** * The template name to use. Setting this property causes the build process to create * a shared field name "$TemplateBuild" */ @Parameter private String templateBuildName; /** * The template version number to use. Setting this property in addition to * <code>templateBuildName</code> will cause this to be set in the "$TemplateBuild" * shared field as the version. If not set, it will use the Maven project's version * number. */ @Parameter private String templateBuildVersion; @Parameter(defaultValue="${project}", readonly=true, required=true) private MavenProject project; /** * Path to File with the build instructions for the headless designer. If * this value is set, all other values like updateSites, odpdirectory, * templateBuildName, templateBuildVersion, and targetdbname will be ignored. */ @Parameter(property = "ddehd.filename") private String m_Filename; public void execute() throws MojoExecutionException, MojoFailureException { getLog().info("Starting DDE HeadlessDesigner Plugin"); getLog().info("===================================="); getLog().info(buildSetupOutput("Designer Exec", m_DesignerExec)); getLog().info(buildSetupOutput("Notes Data", m_NotesData)); getLog().info(buildSetupOutput("TargetDB Name", m_TargetDBName)); getLog().info(buildSetupOutput("ODP", m_ODPDirectory)); getLog().info(buildSetupOutput("Features", m_Features == null ? "<none>" : "" + m_Features.size())); getLog().info(buildSetupOutput("Target Directory", m_TargetDir == null ? "<no targetdir!" : "" + m_TargetDir.getAbsolutePath())); if (StringUtils.isEmpty(m_NotesData) || StringUtils.isEmpty(m_TargetDBName) || StringUtils.isEmpty(m_ODPDirectory)) { getLog().info("DDE HeadlessDesigner Plugin miss some configuration (ddehd.targetdbname, ddehd.notesdata)"); throw new MojoExecutionException("DDE HeadlessDesigner Plugin miss some configuration (ddehd.targetdbname, ddehd.notesdata, ddehd.odpdirectory)"); } if (StringUtils.isEmpty(m_Filename)) { // Copy the ODP to a temporary directory File origOdp = new File(m_ODPDirectory); String buildDir = project.getBuild().getDirectory(); File tempOdp = new File(buildDir, "odp"); try { if(tempOdp.exists()) { FileUtils.deleteDirectory(tempOdp); } FileUtils.copyDirectory(origOdp, tempOdp); } catch(IOException e) { throw new MojoExecutionException("Failed to copy ODP to temporary directory", e); } // Create/overwrite the $TemplateBuild field if needed if(StringUtils.isNotEmpty(templateBuildName)) { getLog().debug("Want to build $TemplateBuild for name=" + templateBuildName + ", version=" + templateBuildVersion); configureTemplateBuild(tempOdp); } installFeature(); enableFeatures(); buildApplication(tempOdp); disableFeatures(); uninstallFeatures(); moveNSFtoTargetDirectory(); } else { customBuild(); } } private void moveNSFtoTargetDirectory() throws MojoExecutionException { getLog().info(buildReportOutput("copyNSF", "Move "+m_TargetDBName)); StringBuilder sbNotesData = new StringBuilder(); sbNotesData.append(m_NotesData); sbNotesData.append("\\"); sbNotesData.append(m_TargetDBName); File flDB = new File(sbNotesData.toString()); File flTarget = new File(m_TargetDir, m_TargetDBName); if (flDB.exists()) { try { FileUtils.copyFile(flDB, flTarget); } catch (Exception ex) { throw new MojoExecutionException("Build failed during copy of " + m_TargetDBName + " to target directory", ex); } } else { throw new MojoExecutionException("Build failed, no " + m_TargetDBName + " found in " + m_NotesData); } getLog().info(buildReportOutput("copyNSF", "Move "+m_TargetDBName + " finished.")); } private void executeDesigner(String fileName) throws MojoExecutionException { StringBuilder sbDesignerArgs = new StringBuilder("-Dcom.ibm.designer.cmd.file=\""); sbDesignerArgs.append(fileName); sbDesignerArgs.append("\""); getLog().debug("Designer call = " + sbDesignerArgs.toString()); ProcessBuilder pb = new ProcessBuilder(m_DesignerExec, "-RPARAMS", "-console", "-vmargs", sbDesignerArgs.toString()); try { Process process = pb.start(); int result = process.waitFor(); getLog().debug("DDE HeadlessDesigner ended with: " + result); boolean finished = false; int nCounter = 0; while (!finished || nCounter < 120) { finished = checkIfNotesHasFinished(); nCounter++; } if (!finished) { throw new MojoExecutionException("DDE HeadlessDesignerPlugin not finished in 120 sec timeout"); } } catch (Exception ex) { throw new MojoExecutionException("DDE HeadlessDesignerPlugin reports an error: ", ex); } getLog().debug("Designer execution ended"); } private void customBuild() throws MojoExecutionException { executeDesigner(m_Filename); } private void configureTemplateBuild(File odpPath) throws MojoExecutionException { File sharedFields = new File(odpPath, "SharedElements" + File.separator + "Fields"); sharedFields.mkdirs(); File templateBuild = new File(sharedFields, "$TemplateBuild.field"); if(templateBuild.exists()) { templateBuild.delete(); } InputStream templateXmlStream = getClass().getResourceAsStream("/templateBuild.xml"); try { String templateXml = IOUtils.toString(templateXmlStream, "UTF-8"); String templateName = templateBuildName; String templateBuildVersion = this.templateBuildVersion; if(StringUtils.isEmpty(templateBuildVersion)) { templateBuildVersion = project.getVersion(); } String templateBuildDate = NOTES_DATE_FORMAT.get().format(new Date()); templateXml = templateXml .replace("{{TemplateBuildName}}", StringEscapeUtils.escapeXml(templateName)) .replace("{{TemplateBuildDate}}", StringEscapeUtils.escapeXml(templateBuildDate)) .replace("{{TemplateBuildVersion}}", StringEscapeUtils.escapeXml(templateBuildVersion)); FileOutputStream fos = new FileOutputStream(templateBuild); try { IOUtils.write(templateXml, fos); } finally { IOUtils.closeQuietly(fos); } } catch(IOException e) { throw new MojoExecutionException("Failed to read templateBuild.xml", e); } finally { IOUtils.closeQuietly(templateXmlStream); } } private void installFeature() throws MojoExecutionException { if (m_Features != null && m_Features.size() > 0) { getLog().info(buildReportOutput("installFeatures", "Build File target/" + INSTALL_TXT)); File fileDeployUS = createInstructionFile(INSTALL_TXT); try { PrintWriter pw = new PrintWriter(fileDeployUS); pw.println("config,true,true"); for (Feature site : m_Features) { StringBuilder sb = new StringBuilder(); sb.append("com.ibm.designer.domino.tools.userlessbuild.jobs.UpdateManagerJob,-command install -from "); sb.append(site.getUrl()); sb.append(" -to file:/"); sb.append(m_NotesData); sb.append("/workspace/applications"); sb.append(" -featureId "); sb.append(site.getFeatureId()); sb.append(" -version "); sb.append(site.getVersion()); pw.println(sb.toString()); } pw.println("exit,100"); pw.close(); getLog().info(buildReportOutput("installFeatures", "Build File target/" + INSTALL_TXT + " prepared.")); executeDesigner(fileDeployUS.getAbsolutePath()); } catch (Exception ex) { throw new MojoExecutionException("Headless Designer Plugin: Could not create " + INSTALL_TXT, ex); } getLog().info(buildReportOutput("installFeatures", "Executed")); } else { getLog().info(buildReportOutput("installFeatures", "SKIPPED")); } } private File createInstructionFile(String fileName) { if (!m_TargetDir.exists()) { m_TargetDir.mkdirs(); } File fileDeployUS = new File(m_TargetDir, fileName); return fileDeployUS; } private void enableFeatures() throws MojoExecutionException { if (m_Features != null && m_Features.size() > 0) { getLog().info(buildReportOutput("enableFeatures", "Build File target/" + ENABLE_TXT)); File fileActivate = createInstructionFile(ENABLE_TXT); try { PrintWriter pw = new PrintWriter(fileActivate); pw.println("config,true,true"); for (Feature site : m_Features) { StringBuilder sb = new StringBuilder(); sb.append("com.ibm.designer.domino.tools.userlessbuild.jobs.UpdateManagerJob,-command enable -to file:/"); sb.append(m_NotesData); sb.append("/workspace/applications"); sb.append(" -featureId "); sb.append(site.getFeatureId()); sb.append(" -version "); sb.append(site.getVersion()); pw.println(sb.toString()); } pw.println("exit"); pw.close(); getLog().info(buildReportOutput("enableFeatures", "Build File target/" + ENABLE_TXT + " prepared.")); executeDesigner(fileActivate.getAbsolutePath()); } catch (Exception ex) { throw new MojoExecutionException("Headless Designer Plugin: Could not create" + ENABLE_TXT, ex); } getLog().info(buildReportOutput("enableFeatures", "Executed")); } else { getLog().info(buildReportOutput("enableFeatures", "SKIPPED")); } } private void buildApplication(File odpPath) throws MojoExecutionException { getLog().info(buildReportOutput("buildApplication", "Build File target/" + BUILD_TXT)); File fileBuild = createInstructionFile(BUILD_TXT); try { PrintWriter pw = new PrintWriter(fileBuild); pw.println("config,true,true"); pw.println("importandbuild," + odpPath.getAbsolutePath() + "/.project," + m_TargetDBName); pw.println("wait," + m_TargetDBName + ",3"); pw.println("clean"); pw.println("exit,100"); pw.close(); getLog().info(buildReportOutput("buildApplication", "Build File target/" + BUILD_TXT + " prepared.")); executeDesigner(fileBuild.getAbsolutePath()); } catch (Exception ex) { throw new MojoExecutionException("Headless Designer Plugin: Could not create " + BUILD_TXT, ex); } getLog().info(buildReportOutput("buildApplication", "Executed")); } private void disableFeatures() throws MojoExecutionException { if (m_Features != null && m_Features.size() > 0) { getLog().info(buildReportOutput("disableFeatures", "Build File target/" + DISABLE_TXT)); File fileDisable = createInstructionFile(DISABLE_TXT); try { PrintWriter pw = new PrintWriter(fileDisable); pw.println("config,true,true"); for (Feature site : m_Features) { StringBuilder sb = new StringBuilder(); sb.append("com.ibm.designer.domino.tools.userlessbuild.jobs.UpdateManagerJob,-command disable -to file:/"); sb.append(m_NotesData); sb.append("/workspace/applications"); sb.append(" -featureId "); sb.append(site.getFeatureId()); sb.append(" -version "); sb.append(site.getVersion()); pw.println(sb.toString()); } pw.println("exit,100"); pw.close(); getLog().info(buildReportOutput("disableFeatures", "Build File target/" + DISABLE_TXT + " prepared.")); executeDesigner(fileDisable.getAbsolutePath()); } catch (Exception ex) { throw new MojoExecutionException("Headless Designer Plugin: Could not create " + DISABLE_TXT, ex); } getLog().info(buildReportOutput("disableFeatures", "Executed")); } else { getLog().info(buildReportOutput("disableFeatures", "SKIPPED")); } } private void uninstallFeatures() throws MojoExecutionException { if (m_Features != null && m_Features.size() > 0) { getLog().info(buildReportOutput("uninstallFeatures", "Build File target/" + UNINSTALL_TXT)); File fileUninstall = createInstructionFile(UNINSTALL_TXT); try { PrintWriter pw = new PrintWriter(fileUninstall); pw.println("config,true,true"); for (Feature site : m_Features) { StringBuilder sb = new StringBuilder(); sb.append("com.ibm.designer.domino.tools.userlessbuild.jobs.UpdateManagerJob,-command uninstall -to file:/"); sb.append(m_NotesData); sb.append("/workspace/applications"); sb.append(" -featureId "); sb.append(site.getFeatureId()); sb.append(" -version "); sb.append(site.getVersion()); pw.println(sb.toString()); } pw.println("exit,100"); pw.close(); getLog().info(buildReportOutput("uninstallFeatures", "Build File target/" + UNINSTALL_TXT + " prepared.")); executeDesigner(fileUninstall.getAbsolutePath()); } catch (Exception ex) { throw new MojoExecutionException("Headless Designer Plugin: Could not create " + UNINSTALL_TXT, ex); } getLog().info(buildReportOutput("uninstallFeatures", "Executed")); } else { getLog().info(buildReportOutput("uninstallFeatures", "SKIPPED")); } } private boolean checkIfNotesHasFinished() throws IOException, InterruptedException { Process process = Runtime.getRuntime().exec("cmd.exe /c tasklist /fi \"IMAGENAME eq notes2.exe\""); process.waitFor(); final InputStream is = process.getInputStream(); final InputStreamReader isr = new InputStreamReader(is); final BufferedReader buff = new BufferedReader(isr); String line = new String(); while ((line = buff.readLine()) != null) { Matcher iMapSuccessMatcher = NOTESPATTERN.matcher(line); if (iMapSuccessMatcher.find()) { Thread.sleep(1000); getLog().debug("Waiting for Notes to complete building."); return false; } } return true; } public List<Feature> getFeatures() { return m_Features; } public void setFeatures(List<Feature> features) { m_Features = features; } /** * @return the templateBuildName */ public String getTemplateBuildName() { return templateBuildName; } /** * @param templateBuildName the templateBuildName to set */ public void setTemplateBuildName(String templateBuildName) { this.templateBuildName = templateBuildName; } /** * @return the templateBuildVersion */ public String getTemplateBuildVersion() { return templateBuildVersion; } /** * @param templateBuildVersion the templateBuildVersion to set */ public void setTemplateBuildVersion(String templateBuildVersion) { this.templateBuildVersion = templateBuildVersion; } }