All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.isomorphic.maven.mojo.AbstractPackagerMojo Maven / Gradle / Ivy

Go to download

An officially supported collection of goals useful for using SmartClient / SmartGWT products in a Maven environment.

There is a newer version: 1.4.5
Show newest version
package com.isomorphic.maven.mojo;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import static com.isomorphic.maven.packaging.License.ANALYTICS_MODULE;
import static com.isomorphic.maven.packaging.License.ENTERPRISE;
import static com.isomorphic.maven.packaging.License.MESSAGING_MODULE;
import static com.isomorphic.maven.packaging.License.POWER;

import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.apache.commons.io.filefilter.WildcardFileFilter;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHost;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.maven.model.Model;
import org.apache.maven.model.Parent;
import org.apache.maven.model.building.DefaultModelBuildingRequest;
import org.apache.maven.model.building.ModelBuildingException;
import org.apache.maven.model.building.ModelBuildingRequest;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.ProjectBuildingRequest.RepositoryMerging;
import org.apache.maven.project.ProjectModelResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.isomorphic.maven.packaging.Distribution;
import com.isomorphic.maven.packaging.Downloads;
import com.isomorphic.maven.packaging.License;
import com.isomorphic.maven.packaging.Module;
import com.isomorphic.maven.packaging.Product;
import com.isomorphic.maven.util.HttpRequestManager;

/**
 * A base class meant to deal with prerequisites to install / deploy goals,
 * which are basically to resolve the files in a given distribution to a
 * collection of Maven artifacts suitable for installation or deployment to some
 * Maven repository.
 * 

* The resulting artifacts are provided to this object's {@link #doExecute(Set)} * method. */ public abstract class AbstractPackagerMojo extends AbstractBaseMojo { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractPackagerMojo.class); private static final HttpHost HOST = new HttpHost("www.smartclient.com", -1, "https"); private HttpRequestManager httpWorker; /** * If true, the optional analytics module (bundled and distributed * separately) has been licensed and should be downloaded with the * distribution specified by {@link #license}. * * @since 1.0.0 */ @Parameter(property = "includeAnalytics", defaultValue = "false") protected Boolean includeAnalytics; /** * The date on which the Isomorphic build was made publicly available at http://www.smartclient.com/builds * /, in yyyy-MM-dd format. e.g., 2013-25-12. Used to determine both * remote and local file locations. *
* Note that if no value is provided, an attempt is made to discover the date of the * latest distribution currently published to the Isomorphic build server. *
* Default value is: The date of the most recent distribution. * * @since 1.0.0 */ @Parameter(property = "buildDate") protected String buildDate; /** * The Isomorphic version number of the specified {@link #product}. e.g., * 9.1d, 4.0p. Used to determine both remote and local file locations. * * @since 1.0.0 */ @Parameter(property = "buildNumber", required = true) protected String buildNumber; /** * Typically one of: LGPL, EVAL, PRO, POWER, ENTERPRISE. Although it is also * valid to specify optional modules ANALYTICS_MODULE or MESSAGING_MODULE, * generally prefer the {@link #includeAnalytics} / * {@link #includeMessaging} properties, respectively, to cause the optional * modules to be included with the base installation / deployment. * * @since 1.0.0 */ @Parameter(property = "license", required = true) protected License license; /** * If true, the optional messaging module (bundled and distributed * separately) has been licensed and should be downloaded with the * distribution specified by {@link #license}. * * @since 1.0.0 */ @Parameter(property = "includeMessaging", defaultValue = "false") protected Boolean includeMessaging; /** * If true, any file previously downloaded / unpacked will be overwritten * with this execution. Useful in the case of an interrupted download. Note * that this setting has no effect on unzip operations. * * @since 1.0.0 */ @Parameter(property = "overwrite", defaultValue = "false") protected Boolean overwrite; /** * If true, makes a copy of the given distribution in a 'latest' subdirectory. * Can be useful for bookmarking documentation, etc. but adds additional install time * and storage requirements. * * @since 1.4.0 */ @Parameter(property = "copyToLatestFolder", defaultValue = "false") protected Boolean copyToLatestFolder; /** * If true, no attempt is made to download any remote distribution. Files * will be loaded instead from a path constructed of the following parts * (e.g., C:/downloads/SmartGWT/PowerEdition/4.1d/2013-12-25/zip): * *

    *
  • {@link #workdir}
  • *
  • {@link #product}
  • *
  • {@link #license}
  • *
  • {@link #buildNumber}
  • *
  • {@link #buildDate}
  • *
  • "zip"
  • *
* * @since 1.0.0 */ @Parameter(property = "skipDownload", defaultValue = "false") protected Boolean skipDownload; /** * If true, no attempt it made to extract the contents of any distribution. * Only useful in the case where some manual intervention is required * between download and another step. For example, it would be possible to * first run the download goal, manipulate the version number of some * dependency in some POM, and then run the install goal with * skipExtraction=false to prevent the modified POM from being overwritten. *

* This is the kind of thing that should generally be avoided, however. */ @Parameter(property = "skipExtraction", defaultValue = "false") protected Boolean skipExtraction; /** * One of SMARTGWT, SMARTCLIENT, or SMARTGWT_MOBILE. * * @since 1.0.0 */ @Parameter(property = "product", defaultValue = "SMARTGWT") protected Product product; /** * If true, artifacts should be versioned with the 'SNAPSHOT' qualifier, in the case of development * builds only. The setting has no effect on patch builds. *

* If false, each artifact's POM file is modified to remove the unwanted * qualifier. This can be useful if you need to deploy a development build * to a production environment. * * @since 1.0.0 */ @Parameter(property = "snapshots", defaultValue = "true") protected Boolean snapshots; /** * The path to some directory that is to be used for storing downloaded * files, working copies, and so on. * * @since 1.0.0 */ @Parameter(property = "workdir", defaultValue = "${java.io.tmpdir}/${project.artifactId}") protected File workdir; /** * The id of a server * configuration containing authentication credentials for the * smartclient.com website, used to download licensed products. *

* Not strictly necessary for unprotected (LGPL) distributions. * * @since 1.0.0 */ @Parameter(property = "serverId", defaultValue = "smartclient-developer") protected String serverId; /** * The point where a subclass is able to manipulate the collection of * artifacts prepared for it by this object's {@link #execute()} method. * * @param artifacts * A collection of Maven artifacts resulting from the download * and preparation of a supported Isomorphic SDK. * @throws MojoExecutionException * When any fatal error occurs. e.g., there is no distribution * to work with. * @throws MojoFailureException * When any non-fatal error occurs. e.g., documentation cannot * be copied to some other folder. */ public abstract void doExecute(Set artifacts) throws MojoExecutionException, MojoFailureException; /** * Provides some initialization and validation steps around the collection * and transformation of an Isomorphic SDK. * * @throws MojoExecutionException * When any fatal error occurs. * @throws MojoFailureException * When any non-fatal error occurs. */ public void execute() throws MojoExecutionException, MojoFailureException { // allow execution to proceed without login credentials - it may be that // they're not required UsernamePasswordCredentials credentials = getCredentials(serverId); if (credentials == null) { LOGGER.warn("No server configured with id '{}'. Will be unable to authenticate.", serverId); } String buildNumberFormat = "\\d.*\\.\\d.*[d|p]"; if (!buildNumber.matches(buildNumberFormat)) { throw new MojoExecutionException(String.format( "buildNumber '%s' must take the form [major].[minor].[d|p]. e.g., 4.1d", buildNumber, buildNumberFormat)); } httpWorker = new HttpRequestManager(HOST, credentials, settings.getActiveProxy()); Downloads dl = new Downloads(httpWorker); try { httpWorker.login(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); dateFormat.setLenient(false); if (buildDate == null) { Distribution d = Distribution.get(product, license); LOGGER.info("No buildDate provided. Contacting Isomorphic build server to " + "look for the most recent distribution..."); String link = dl.findCurrentBuild(d, buildNumber); if (link == null) { throw new MojoExecutionException("No build found for the given distribution."); } LOGGER.debug("Extracting date from server response: '{}'", link); buildDate = StringUtils.substringAfterLast(link, "/"); LOGGER.info("buildDate set to '{}'", buildDate); } dateFormat.parse(buildDate); File basedir = FileUtils.getFile(workdir, product.toString(), license.toString(), buildNumber, buildDate); // add optional modules to the list of downloads List licenses = new ArrayList(); licenses.add(license); if (license == POWER || license == ENTERPRISE) { if (includeAnalytics) { licenses.add(ANALYTICS_MODULE); } if (includeMessaging) { licenses.add(MESSAGING_MODULE); } } // collect the maven artifacts and send them along to the abstract method Set artifacts = collect(licenses, basedir); String[] executables = { "bat", "sh", "command" }; Collection scripts = FileUtils.listFiles(basedir, executables, true); if (copyToLatestFolder) { File bookmarkable = new File(basedir.getParent(), "latest"); LOGGER.info("Copying distribution to '{}'", bookmarkable.getAbsolutePath()); try { FileUtils.forceMkdir(bookmarkable); FileUtils.cleanDirectory(bookmarkable); FileUtils.copyDirectory(basedir, bookmarkable, FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter("zip"))); scripts.addAll(FileUtils.listFiles(bookmarkable, executables, true)); } catch (IOException e) { throw new MojoFailureException("Unable to copy distribution contents", e); } } for (File script : scripts) { script.setExecutable(true); LOGGER.debug("Enabled execute permissions on file '{}'", script.getAbsolutePath()); } doExecute(artifacts); } catch (ParseException e) { throw new MojoExecutionException(String.format( "buildDate '%s' must take the form yyyy-MM-dd.", buildDate)); } finally { httpWorker.logout(); } } /** * Download the specified distributions, if necessary, extract resources * from them, and use the results to create Maven artifacts as appropriate: *

* Try to install all of the main artifacts - e.g., those found in lib/*.jar * and assembly/*.zip *
* Try to match main artifacts to 'subartifacts' by name and attach them * (with classifiers as necessary) * * @param downloads * The list of licenses top be included in the distribution. * Multiple licenses are only allowed to support the inclusion of * optional modules. * @param basedir * The directory into which results should be written * @return A collection of Maven artifacts resulting from the download and * preparation of a supported Isomorphic SDK. * @throws MojoExecutionException * When any fatal error occurs. */ private Set collect(List downloads, File basedir) throws MojoExecutionException { File downloadTo = new File(basedir, "zip"); downloadTo.mkdirs(); Downloads downloadManager = new Downloads(httpWorker); downloadManager.setToFolder(downloadTo); downloadManager.setOverwriteExistingFiles(overwrite); File[] existing = downloadTo.listFiles(); List distributions = new ArrayList(); try { if (!skipDownload) { distributions.addAll(downloadManager.fetch(product, buildNumber, buildDate, downloads.toArray(new License[0]))); } else if (existing != null) { LOGGER.info("Creating local distribution from '{}'", downloadTo.getAbsolutePath()); Distribution distribution = Distribution.get(product, license); distribution.getFiles().addAll(Arrays.asList(existing)); distributions.add(distribution); } if (!skipExtraction) { LOGGER.info("Unpacking downloaded file/s to '{}'", basedir); for (Distribution distribution : distributions) { distribution.unpack(basedir); } } // it doesn't strictly read this way, but we're looking for // lib/*.jar, pom/*.xml, assembly/*.zip // TODO it'd be better if this didn't have to know where the files // were located after unpacking Collection files = FileUtils.listFiles( basedir, FileFilterUtils.or(FileFilterUtils.suffixFileFilter("jar"), FileFilterUtils.suffixFileFilter("xml"), FileFilterUtils.suffixFileFilter("zip")), FileFilterUtils.or(FileFilterUtils.nameFileFilter("lib"), FileFilterUtils.nameFileFilter("pom"), FileFilterUtils.nameFileFilter("assembly")) ); if (files.isEmpty()) { throw new MojoExecutionException( String .format( "There don't appear to be any files to work with at '%s'. Check earlier log entries for clues.", basedir.getAbsolutePath())); } Set result = new TreeSet(); for (File file : files) { try { String base = FilenameUtils.getBaseName(file.getName() .replaceAll("_", "-")); // poms don't need anything else if ("xml".equals(FilenameUtils.getExtension(file.getName()))) { result.add(new Module(getModelFromFile(file))); continue; } // for each jar/zip, find the matching pom IOFileFilter filter = new WildcardFileFilter(base + ".pom"); Collection poms = FileUtils.listFiles(basedir, filter, TrueFileFilter.INSTANCE); if (poms.size() != 1) { LOGGER .warn( "Expected to find exactly 1 POM matching artifact with name '{}', but found {}. Skpping installation.", base, poms.size()); continue; } Model model = getModelFromFile(poms.iterator().next()); Module module = new Module(model, file); /* * Find the right javadoc bundle, matched on prefix. e.g., * smartgwt-eval -> smartgwt-javadoc isomorphic-core-rpc -> * isomorphic-javadoc and add it to the main artifact with * the javadoc classifier. This seems appropriate as long as * a) there is no per-jar javadoc b) naming conventions are * adhered to (or can be corrected by plugin at extraction) */ int index = base.indexOf("-"); String prefix = base.substring(0, index); Collection doc = FileUtils.listFiles(new File(basedir, "doc"), FileFilterUtils.prefixFileFilter(prefix), FileFilterUtils.nameFileFilter("lib")); if (doc.size() != 1) { LOGGER .debug( "Found {} javadoc attachments with prefix '{}'. Skipping attachment.", doc.size(), prefix); } else { module.attach(doc.iterator().next(), "javadoc"); } result.add(module); } catch (ModelBuildingException e) { throw new MojoExecutionException("Error building model from POM", e); } } return result; } catch (IOException e) { throw new MojoExecutionException("Failure during assembly collection", e); } } /**f * Read the given POM so it can be used as the source of coordinates, etc. * during artifact construction. Note that if this object's * {@link #snapshots} property is true, and we're working with a development * build ({@link #buildNumber} ends with 'd'), the POM is modified to remove * the SNAPSHOT qualifier. * * @param pom * the POM file containing the artifact metadata * @return A Maven model to be used at * {@link com.isomorphic.maven.packaging.Module#Module(Model)} * Module construction * @throws ModelBuildingException * if the Model cannot be built from the given POM * @throws IOException * if the Model cannot be built from the given POM */ private Model getModelFromFile(File pom) throws ModelBuildingException, IOException { if (buildNumber.endsWith("d") && !snapshots) { LOGGER.info( "Rewriting file to remove SNAPSHOT qualifier from development POM '{}'", pom.getName()); String content = FileUtils.readFileToString(pom); content = content.replaceAll("-SNAPSHOT", ""); FileUtils.write(pom, content); } ProjectModelResolver resolver = new ProjectModelResolver(repositorySystemSession, null, repositorySystem, remoteRepositoryManager, project.getRemoteProjectRepositories(), RepositoryMerging.POM_DOMINANT, null); ModelBuildingRequest request = new DefaultModelBuildingRequest(); request.setModelResolver(resolver); request.setPomFile(pom); Model model = modelBuilder.buildRawModel(pom, 0, false).get(); Parent parent = model.getParent(); if (parent != null) { model.setGroupId(parent.getGroupId()); model.setVersion(parent.getVersion()); } return model; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy