Java tutorial
package org.apache.maven.plugins.scmpublish; /* * 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 java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.time.DurationFormatUtils; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.scm.CommandParameter; import org.apache.maven.scm.CommandParameters; import org.apache.maven.scm.ScmBranch; import org.apache.maven.scm.ScmException; import org.apache.maven.scm.ScmFileSet; import org.apache.maven.scm.ScmResult; import org.apache.maven.scm.command.add.AddScmResult; import org.apache.maven.scm.command.checkin.CheckInScmResult; import org.apache.maven.scm.manager.NoSuchScmProviderException; import org.apache.maven.scm.manager.ScmManager; import org.apache.maven.scm.provider.ScmProvider; import org.apache.maven.scm.provider.ScmUrlUtils; import org.apache.maven.scm.provider.svn.AbstractSvnScmProvider; import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository; import org.apache.maven.scm.repository.ScmRepository; import org.apache.maven.scm.repository.ScmRepositoryException; import org.apache.maven.settings.Server; import org.apache.maven.settings.Settings; import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest; import org.apache.maven.settings.crypto.SettingsDecrypter; import org.apache.maven.settings.crypto.SettingsDecryptionRequest; import org.apache.maven.settings.crypto.SettingsDecryptionResult; import org.apache.maven.shared.release.config.ReleaseDescriptor; import org.apache.maven.shared.release.scm.ScmRepositoryConfigurator; /** * Base class for the scm-publish mojos. */ public abstract class AbstractScmPublishMojo extends AbstractMojo { /** * Location of the scm publication tree: * <code>scm:<scm_provider><delimiter><provider_specific_part></code>. * Example: * <code>scm:svn:https://svn.apache.org/repos/infra/websites/production/maven/content/plugins/maven-scm-publish-plugin-LATEST/</code> */ @Parameter(property = "scmpublish.pubScmUrl", defaultValue = "${project.distributionManagement.site.url}", required = true) protected String pubScmUrl; /** * If the checkout directory exists and this flag is activated, the plugin will try an SCM-update instead * of delete then checkout. */ @Parameter(property = "scmpublish.tryUpdate", defaultValue = "false") protected boolean tryUpdate; /** * Location where the scm check-out is done. By default, scm checkout is done in build (target) directory, * which is deleted on every <code>mvn clean</code>. To avoid this and get better performance, configure * this location outside build structure and set <code>tryUpdate</code> to <code>true</code>. * See <a href="http://maven.apache.org/plugins/maven-scm-publish-plugin/various-tips.html#Improving_SCM_Checkout_Performance"> * Improving SCM Checkout Performance</a> for more information. */ @Parameter(property = "scmpublish.checkoutDirectory", defaultValue = "${project.build.directory}/scmpublish-checkout") protected File checkoutDirectory; /** * Display list of added, deleted, and changed files, but do not do any actual SCM operations. */ @Parameter(property = "scmpublish.dryRun") private boolean dryRun; /** * Run add and delete commands, but leave the actually checkin for the user to run manually. */ @Parameter(property = "scmpublish.skipCheckin") private boolean skipCheckin; /** * SCM log/checkin comment for this publication. */ @Parameter(property = "scmpublish.checkinComment", defaultValue = "Site checkin for project ${project.name}") private String checkinComment; /** * Patterns to exclude from the scm tree. */ @Parameter protected String excludes; /** * Patterns to include in the scm tree. */ @Parameter protected String includes; /** * List of SCM provider implementations. * Key is the provider type, eg. <code>cvs</code>. * Value is the provider implementation (the role-hint of the provider), eg. <code>cvs</code> or <code>cvs_native</code>. * @see ScmManager.setScmProviderImplementation */ @Parameter private Map<String, String> providerImplementations; /** * The SCM manager. */ @Component private ScmManager scmManager; /** * Tool that gets a configured SCM repository from release configuration. */ @Component protected ScmRepositoryConfigurator scmRepositoryConfigurator; /** * The serverId specified in the settings.xml, which should be used for the authentication. */ @Parameter private String serverId; /** * The SCM username to use. */ @Parameter(property = "username") protected String username; /** * The SCM password to use. */ @Parameter(property = "password") protected String password; /** * Use a local checkout instead of doing a checkout from the upstream repository. <b>WARNING</b>: This will only work * with distributed SCMs which support the file:// protocol * TODO: we should think about having the defaults for the various SCM providers provided via Modello! */ @Parameter(property = "localCheckout", defaultValue = "false") protected boolean localCheckout; /** * The outputEncoding parameter of the site plugin. This plugin will corrupt your site * if this does not match the value used by the site plugin. */ @Parameter(property = "outputEncoding", defaultValue = "${project.reporting.outputEncoding}") protected String siteOutputEncoding; /** * Do not delete files to the scm */ @Parameter(property = "scmpublish.skipDeletedFiles", defaultValue = "false") protected boolean skipDeletedFiles; /** */ @Parameter(defaultValue = "${basedir}", readonly = true) protected File basedir; /** */ @Component protected Settings settings; @Component private SettingsDecrypter settingsDecrypter; /** * Collections of paths not to delete when checking content to delete. * If your site has subdirectories published by an other mechanism/build */ @Parameter protected String[] ignorePathsToDelete; /** * SCM branch to use. For github, you must configure with <code>gh-pages</code>. */ @Parameter(property = "scmpublish.scm.branch") protected String scmBranch; /** * Configure svn automatic remote url creation. */ @Parameter(property = "scmpublish.automaticRemotePathCreation", defaultValue = "true") protected boolean automaticRemotePathCreation; /** * Filename extensions of files which need new line normalization. */ private final static String[] NORMALIZE_EXTENSIONS = { "html", "css", "js" }; /** * Extra file extensions to normalize line ending (will be added to default * <code>html</code>,<code>css</code>,<code>js</code> list) */ @Parameter protected String[] extraNormalizeExtensions; private Set<String> normalizeExtensions; protected ScmProvider scmProvider; protected ScmRepository scmRepository; protected void logInfo(String format, Object... params) { getLog().info(String.format(format, params)); } protected void logWarn(String format, Object... params) { getLog().warn(String.format(format, params)); } protected void logError(String format, Object... params) { getLog().error(String.format(format, params)); } private File relativize(File base, File file) { return new File(base.toURI().relativize(file.toURI()).getPath()); } protected boolean requireNormalizeNewlines(File f) throws IOException { if (normalizeExtensions == null) { normalizeExtensions = new HashSet<String>(Arrays.asList(NORMALIZE_EXTENSIONS)); if (extraNormalizeExtensions != null) { normalizeExtensions.addAll(Arrays.asList(extraNormalizeExtensions)); } } return FilenameUtils.isExtension(f.getName(), normalizeExtensions); } private ReleaseDescriptor setupScm() throws ScmRepositoryException, NoSuchScmProviderException { String scmUrl; if (localCheckout) { // in the release phase we have to change the checkout URL // to do a local checkout instead of going over the network. String provider = ScmUrlUtils.getProvider(pubScmUrl); String delimiter = ScmUrlUtils.getDelimiter(pubScmUrl); String providerPart = "scm:" + provider + delimiter; // X TODO: also check the information from releaseDescriptor.getScmRelativePathProjectDirectory() // X TODO: in case our toplevel git directory has no pom. // X TODO: fix pathname once I understand this. scmUrl = providerPart + "file://" + "target/localCheckout"; logInfo("Performing a LOCAL checkout from " + scmUrl); } ReleaseDescriptor releaseDescriptor = new ReleaseDescriptor(); releaseDescriptor.setInteractive(settings.isInteractiveMode()); if (username == null || password == null) { for (Server server : settings.getServers()) { if (server.getId().equals(serverId)) { SettingsDecryptionRequest decryptionRequest = new DefaultSettingsDecryptionRequest(server); SettingsDecryptionResult decryptionResult = settingsDecrypter.decrypt(decryptionRequest); if (!decryptionResult.getProblems().isEmpty()) { // todo throw exception? } if (username == null) { username = decryptionResult.getServer().getUsername(); } if (password == null) { password = decryptionResult.getServer().getPassword(); } break; } } } releaseDescriptor.setScmPassword(password); releaseDescriptor.setScmUsername(username); releaseDescriptor.setWorkingDirectory(basedir.getAbsolutePath()); releaseDescriptor.setLocalCheckout(localCheckout); releaseDescriptor.setScmSourceUrl(pubScmUrl); if (providerImplementations != null) { for (Map.Entry<String, String> providerEntry : providerImplementations.entrySet()) { logInfo("Changing the default '%s' provider implementation to '%s'.", providerEntry.getKey(), providerEntry.getValue()); scmManager.setScmProviderImplementation(providerEntry.getKey(), providerEntry.getValue()); } } scmRepository = scmRepositoryConfigurator.getConfiguredRepository(releaseDescriptor, settings); scmProvider = scmRepositoryConfigurator.getRepositoryProvider(scmRepository); return releaseDescriptor; } protected void checkoutExisting() throws MojoExecutionException { if (scmProvider instanceof AbstractSvnScmProvider) { checkCreateRemoteSvnPath(); } logInfo("%s the pub tree from %s into %s", (tryUpdate ? "Updating" : "Checking out"), pubScmUrl, checkoutDirectory); if (checkoutDirectory.exists() && !tryUpdate) { try { FileUtils.deleteDirectory(checkoutDirectory); } catch (IOException e) { logError(e.getMessage()); throw new MojoExecutionException("Unable to remove old checkout directory: " + e.getMessage(), e); } } boolean forceCheckout = false; if (!checkoutDirectory.exists()) { if (tryUpdate) { logInfo("TryUpdate is configured but no local copy currently available: forcing checkout."); } checkoutDirectory.mkdirs(); forceCheckout = true; } try { ScmFileSet fileSet = new ScmFileSet(checkoutDirectory, includes, excludes); ScmResult scmResult = null; if (tryUpdate && !forceCheckout) { scmResult = scmProvider.update(scmRepository, fileSet); } else { int attempt = 0; while (scmResult == null) { try { if (scmBranch == null) { scmResult = scmProvider.checkOut(scmRepository, fileSet); } else { ScmBranch scmBranch = new ScmBranch(this.scmBranch); scmResult = scmProvider.checkOut(scmRepository, fileSet, scmBranch); } } catch (ScmException e) { // give it max 2 times to retry if (attempt++ < 2) { try { // wait 3 seconds Thread.sleep(3 * 1000); } catch (InterruptedException ie) { // noop } } else { throw e; } } } } checkScmResult(scmResult, "check out from SCM"); } catch (ScmException e) { logError(e.getMessage()); throw new MojoExecutionException("An error occurred during the checkout process: " + e.getMessage(), e); } catch (IOException e) { logError(e.getMessage()); throw new MojoExecutionException("An error occurred during the checkout process: " + e.getMessage(), e); } } private void checkCreateRemoteSvnPath() throws MojoExecutionException { getLog().debug( "AbstractSvnScmProvider used, so we can check if remote url exists and eventually create it."); AbstractSvnScmProvider svnScmProvider = (AbstractSvnScmProvider) scmProvider; try { boolean remoteExists = svnScmProvider.remoteUrlExist(scmRepository.getProviderRepository(), null); if (remoteExists) { return; } } catch (ScmException e) { throw new MojoExecutionException(e.getMessage(), e); } String remoteUrl = ((SvnScmProviderRepository) scmRepository.getProviderRepository()).getUrl(); if (!automaticRemotePathCreation) { // olamy: return ?? that will fail during checkout IMHO :-) logWarn("Remote svn url %s does not exist and automatic remote path creation disabled.", remoteUrl); return; } logInfo("Remote svn url %s does not exist: creating.", remoteUrl); File baseDir = null; try { // create a temporary directory for svnexec baseDir = File.createTempFile("scm", "tmp"); baseDir.delete(); baseDir.mkdirs(); // to prevent fileSet cannot be empty ScmFileSet scmFileSet = new ScmFileSet(baseDir, new File("")); CommandParameters commandParameters = new CommandParameters(); commandParameters.setString(CommandParameter.SCM_MKDIR_CREATE_IN_LOCAL, Boolean.FALSE.toString()); commandParameters.setString(CommandParameter.MESSAGE, "Automatic svn path creation: " + remoteUrl); svnScmProvider.mkdir(scmRepository.getProviderRepository(), scmFileSet, commandParameters); // new remote url so force checkout! if (checkoutDirectory.exists()) { FileUtils.deleteDirectory(checkoutDirectory); } } catch (IOException e) { throw new MojoExecutionException(e.getMessage(), e); } catch (ScmException e) { throw new MojoExecutionException(e.getMessage(), e); } finally { if (baseDir != null) { try { FileUtils.forceDeleteOnExit(baseDir); } catch (IOException e) { throw new MojoExecutionException(e.getMessage(), e); } } } } public void execute() throws MojoExecutionException, MojoFailureException { // setup the scm plugin with help from release plugin utilities try { setupScm(); } catch (ScmRepositoryException e) { throw new MojoExecutionException(e.getMessage(), e); } catch (NoSuchScmProviderException e) { throw new MojoExecutionException(e.getMessage(), e); } boolean tmpCheckout = false; if (checkoutDirectory.getPath().contains("${project.")) { try { tmpCheckout = true; checkoutDirectory = File.createTempFile("maven-scm-publish", ".checkout"); checkoutDirectory.delete(); checkoutDirectory.mkdir(); } catch (IOException ioe) { throw new MojoExecutionException(ioe.getMessage(), ioe); } } try { scmPublishExecute(); } finally { if (tmpCheckout) { FileUtils.deleteQuietly(checkoutDirectory); } } } /** * Check-in content from scm checkout. * * @throws MojoExecutionException */ protected void checkinFiles() throws MojoExecutionException { if (skipCheckin) { return; } ScmFileSet updatedFileSet = new ScmFileSet(checkoutDirectory); try { long start = System.currentTimeMillis(); CheckInScmResult checkinResult = checkScmResult( scmProvider.checkIn(scmRepository, updatedFileSet, new ScmBranch(scmBranch), checkinComment), "check-in files to SCM"); logInfo("Checked in %d file(s) to revision %s in %s", checkinResult.getCheckedInFiles().size(), checkinResult.getScmRevision(), DurationFormatUtils.formatPeriod(start, System.currentTimeMillis(), "H' h 'm' m 's' s'")); } catch (ScmException e) { throw new MojoExecutionException("Failed to perform SCM checkin", e); } } protected void deleteFiles(Collection<File> deleted) throws MojoExecutionException { if (skipDeletedFiles) { logInfo("Deleting files is skipped."); return; } List<File> deletedList = new ArrayList<File>(); for (File f : deleted) { deletedList.add(relativize(checkoutDirectory, f)); } ScmFileSet deletedFileSet = new ScmFileSet(checkoutDirectory, deletedList); try { getLog().debug("Deleting files: " + deletedList); checkScmResult(scmProvider.remove(scmRepository, deletedFileSet, "Deleting obsolete site files."), "delete files from SCM"); } catch (ScmException e) { throw new MojoExecutionException("Failed to delete removed files to SCM", e); } } /** * Add files to scm. * * @param added files to be added * @throws MojoFailureException * @throws MojoExecutionException */ protected void addFiles(Collection<File> added) throws MojoFailureException, MojoExecutionException { List<File> addedList = new ArrayList<File>(); Set<File> createdDirs = new HashSet<File>(); Set<File> dirsToAdd = new TreeSet<File>(); createdDirs.add(relativize(checkoutDirectory, checkoutDirectory)); for (File f : added) { for (File dir = f.getParentFile(); !dir.equals(checkoutDirectory); dir = dir.getParentFile()) { File relativized = relativize(checkoutDirectory, dir); // we do the best we can with the directories if (createdDirs.add(relativized)) { dirsToAdd.add(relativized); } else { break; } } addedList.add(relativize(checkoutDirectory, f)); } for (File relativized : dirsToAdd) { try { ScmFileSet fileSet = new ScmFileSet(checkoutDirectory, relativized); getLog().debug("scm add directory: " + relativized); AddScmResult addDirResult = scmProvider.add(scmRepository, fileSet, "Adding directory"); if (!addDirResult.isSuccess()) { getLog().debug( " Error adding directory " + relativized + ": " + addDirResult.getCommandOutput()); } } catch (ScmException e) { // } } // remove directories already added ! addedList.removeAll(dirsToAdd); ScmFileSet addedFileSet = new ScmFileSet(checkoutDirectory, addedList); getLog().debug("scm add files: " + addedList); try { CommandParameters commandParameters = new CommandParameters(); commandParameters.setString(CommandParameter.MESSAGE, "Adding new site files."); commandParameters.setString(CommandParameter.FORCE_ADD, Boolean.TRUE.toString()); checkScmResult(scmProvider.add(scmRepository, addedFileSet, commandParameters), "add new files to SCM"); } catch (ScmException e) { throw new MojoExecutionException("Failed to add new files to SCM", e); } } private <T extends ScmResult> T checkScmResult(T result, String failure) throws MojoExecutionException { if (!result.isSuccess()) { String msg = "Failed to " + failure + ": " + result.getProviderMessage() + " " + result.getCommandOutput(); logError(msg); throw new MojoExecutionException(msg); } return result; } public boolean isDryRun() { return dryRun; } public abstract void scmPublishExecute() throws MojoExecutionException, MojoFailureException; public void setPubScmUrl(String pubScmUrl) { // Fix required for Windows, which fit other OS as well if (pubScmUrl.startsWith("scm:svn:")) { pubScmUrl = pubScmUrl.replaceFirst("file:/[/]*", "file:///"); } this.pubScmUrl = pubScmUrl; } }