Coverage Report - no.sesat.mojo.DeploySesatWarfilesMojo
 
Classes in this File Line Coverage Branch Coverage Complexity
DeploySesatWarfilesMojo
0%
0/168
0%
0/68
0
 
 1  
 /*
 2  
  * Copyright (2007) Schibsted Søk AS
 3  
  * This file is part of SESAT.
 4  
  *
 5  
  *   SESAT is free software: you can redistribute it and/or modify
 6  
  *   it under the terms of the GNU Affero General Public License as published by
 7  
  *   the Free Software Foundation, either version 3 of the License, or
 8  
  *   (at your option) any later version.
 9  
  *
 10  
  *   SESAT is distributed in the hope that it will be useful,
 11  
  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13  
  *   GNU Affero General Public License for more details.
 14  
  *
 15  
  *   You should have received a copy of the GNU Affero General Public License
 16  
  *   along with SESAT.  If not, see <http://www.gnu.org/licenses/>.
 17  
  */
 18  
 
 19  
 package no.sesat.mojo;
 20  
 
 21  
 import java.io.BufferedReader;
 22  
 import java.io.BufferedWriter;
 23  
 import java.io.File;
 24  
 import java.io.FileReader;
 25  
 import java.io.FileWriter;
 26  
 import java.io.IOException;
 27  
 import java.io.StringReader;
 28  
 import java.io.StringWriter;
 29  
 import java.text.SimpleDateFormat;
 30  
 import java.util.ArrayList;
 31  
 import java.util.Calendar;
 32  
 import java.util.Date;
 33  
 import java.util.List;
 34  
 import org.apache.maven.artifact.Artifact;
 35  
 import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
 36  
 import org.apache.maven.artifact.resolver.ArtifactCollector;
 37  
 import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
 38  
 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
 39  
 import org.apache.maven.model.Profile;
 40  
 import org.apache.maven.plugin.MojoExecutionException;
 41  
 import org.apache.maven.plugin.dependency.fromConfiguration.CopyMojo;
 42  
 import org.apache.maven.project.MavenProject;
 43  
 import org.apache.maven.scm.ScmException;
 44  
 import org.apache.maven.scm.ScmFileSet;
 45  
 import org.apache.maven.scm.command.status.StatusScmResult;
 46  
 import org.apache.maven.scm.command.tag.TagScmResult;
 47  
 import org.apache.maven.scm.manager.ScmManager;
 48  
 import org.apache.maven.wagon.ConnectionException;
 49  
 import org.apache.maven.wagon.ResourceDoesNotExistException;
 50  
 import org.apache.maven.wagon.TransferFailedException;
 51  
 import org.apache.maven.wagon.Wagon;
 52  
 import org.apache.maven.wagon.authentication.AuthenticationException;
 53  
 import org.apache.maven.wagon.authorization.AuthorizationException;
 54  
 import org.apache.maven.wagon.repository.Repository;
 55  
 import org.apache.maven.wagon.repository.RepositoryPermissions;
 56  
 import org.codehaus.plexus.PlexusConstants;
 57  
 import org.codehaus.plexus.PlexusContainer;
 58  
 import org.codehaus.plexus.archiver.manager.ArchiverManager;
 59  
 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
 60  
 import org.codehaus.plexus.context.Context;
 61  
 import org.codehaus.plexus.context.ContextException;
 62  
 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
 63  
 
 64  
 /** Handles deployment of sesat and skins builds.
 65  
  * Deployment differs greatly between the development and the other profiles.
 66  
  * In development profile the behaviour just uses the superclass CopyMojo,
 67  
  *  or the dependency:copy goal, with a precondition that the outputDirectory exists.
 68  
  *  (avoids a "null" directory created).
 69  
  * In the other profiles, the profile's classifier is used, the artifact downloaded from a remote serverDeployLocation
 70  
  *  is neccessary, and then uploaded to the configured 'profile'DeployRepository which corresponds to the
 71  
  *  the environments webapp directory.
 72  
  *  Skins are expected to override these deployRepository settings.
 73  
  *
 74  
  * TODO separate mojos into local-deploy and server-deploy.
 75  
  * TODO abstract override of parameters from CopyMojo.
 76  
  *
 77  
  * @goal deploy
 78  
  * @author <a href="mailto:mick@semb.wever.org">Mick</a>
 79  
  * @version $Id: DeploySesatWarfilesMojo.java 6380 2008-04-07 11:03:05Z sshafroi $
 80  
  */
 81  
 public final class DeploySesatWarfilesMojo extends CopyMojo implements Contextualizable{
 82  
 
 83  
     // Constants -----------------------------------------------------
 84  
 
 85  0
     private static final String[] ENVIRONMENTS = new String[]{"alpha","nuclei","beta","electron","gamma","production"};
 86  
 
 87  
     private static final String TAG_ON_DEPLOY = "tag.on.deploy";
 88  
 
 89  
     private static final String DRY_RUN = "sesat.mojo.dryRun";
 90  
 
 91  
     // Attributes ----------------------------------------------------
 92  
 
 93  
     private PlexusContainer container;
 94  
 
 95  0
     private String profile = null;
 96  0
     private Date now = Calendar.getInstance().getTime();
 97  
     private MavenProject pomProject;
 98  
 
 99  
     // All of these attributes are just explicit overrides to get them into the mojo.
 100  
     //  read http://www.mail-archive.com/dev@maven.apache.org/msg60770.html
 101  
 
 102  
     /**
 103  
      * Strip artifact version during copy
 104  
      *
 105  
      * @parameter expression="${mdep.stripVersion}" default-value="false"
 106  
      * @parameter
 107  
      */
 108  0
     private boolean stripVersion = false;
 109  
 
 110  
     /**
 111  
      * Default location used for mojo unless overridden in ArtifactItem
 112  
      *
 113  
      * @parameter expression="${outputDirectory}"
 114  
      *            default-value="${project.build.directory}/dependency"
 115  
      * @optional
 116  
      * @since 1.0
 117  
      */
 118  
     private File outputDirectory;
 119  
 
 120  
     /**
 121  
      * Overwrite release artifacts
 122  
      *
 123  
      * @optional
 124  
      * @since 1.0
 125  
      * @parameter expression="${mdep.overWriteReleases}" default-value="false"
 126  
      */
 127  
     private boolean overWriteReleases;
 128  
 
 129  
     /**
 130  
      * Overwrite snapshot artifacts
 131  
      *
 132  
      * @optional
 133  
      * @since 1.0
 134  
      * @parameter expression="${mdep.overWriteSnapshots}" default-value="false"
 135  
      */
 136  
     private boolean overWriteSnapshots;
 137  
 
 138  
     /**
 139  
      * Overwrite if newer
 140  
      *
 141  
      * @optional
 142  
      * @since 2.0
 143  
      * @parameter expression="${mdep.overIfNewer}" default-value="true"
 144  
      */
 145  
     private boolean overWriteIfNewer;
 146  
 
 147  
     /**
 148  
      * Collection of ArtifactItems to work on. (ArtifactItem contains groupId,
 149  
      * artifactId, version, type, classifier, location, destFile, markerFile and overwrite.)
 150  
      * See "Usage" and "Javadoc" for details.
 151  
      *
 152  
      * @parameter
 153  
      * @required
 154  
      * @since 1.0
 155  
      */
 156  
     private ArrayList artifactItems;
 157  
 
 158  
     /**
 159  
      * Used to look up Artifacts in the remote serverDeployLocation.
 160  
      *
 161  
      * @parameter expression="${component.org.apache.maven.artifact.factory.ArtifactFactory}"
 162  
      * @required
 163  
      * @readonly
 164  
      */
 165  
     private org.apache.maven.artifact.factory.ArtifactFactory factory;
 166  
 
 167  
     /**
 168  
      * Used to look up Artifacts in the remote serverDeployLocation.
 169  
      *
 170  
      * @parameter expression="${component.org.apache.maven.artifact.resolver.ArtifactResolver}"
 171  
      * @required
 172  
      * @readonly
 173  
      */
 174  
     private org.apache.maven.artifact.resolver.ArtifactResolver resolver;
 175  
 
 176  
     /**
 177  
      * Artifact collector, needed to resolve dependencies.
 178  
      *
 179  
      * @component role="org.apache.maven.artifact.resolver.ArtifactCollector"
 180  
      * @required
 181  
      * @readonly
 182  
      */
 183  
     private ArtifactCollector artifactCollector;
 184  
 
 185  
     /**
 186  
      * @component role="org.apache.maven.artifact.metadata.ArtifactMetadataSource"
 187  
      *            hint="maven"
 188  
      * @required
 189  
      * @readonly
 190  
      */
 191  
     private ArtifactMetadataSource artifactMetadataSource;
 192  
 
 193  
     /**
 194  
      * Location of the local serverDeployLocation.
 195  
      *
 196  
      * @parameter expression="${localRepository}"
 197  
      * @readonly
 198  
      * @required
 199  
      */
 200  
     private org.apache.maven.artifact.repository.ArtifactRepository local;
 201  
 
 202  
     /**
 203  
      * List of Remote Repositories used by the resolver
 204  
      *
 205  
      * @parameter expression="${project.remoteArtifactRepositories}"
 206  
      * @readonly
 207  
      * @required
 208  
      */
 209  
     private java.util.List remoteRepos;
 210  
 
 211  
     /**
 212  
      * To look up Archiver/UnArchiver implementations
 213  
      *
 214  
      * @parameter expression="${component.org.codehaus.plexus.archiver.manager.ArchiverManager}"
 215  
      * @required
 216  
      * @readonly
 217  
      */
 218  
     private ArchiverManager archiverManager;
 219  
 
 220  
     /**
 221  
      * POM
 222  
      *
 223  
      * @parameter expression="${project}"
 224  
      * @readonly
 225  
      * @required
 226  
      */
 227  
     private MavenProject project;
 228  
 
 229  
     /**
 230  
      * Contains the full list of projects in the reactor.
 231  
      *
 232  
      * @parameter expression="${reactorProjects}"
 233  
      * @required
 234  
      * @readonly
 235  
      */
 236  
     private List reactorProjects;
 237  
 
 238  
     /**
 239  
      * If the plugin should be silent.
 240  
      *
 241  
      * @optional
 242  
      * @since 2.0
 243  
      * @parameter expression="${silent}" default-value="false"
 244  
      */
 245  
     private boolean silent;
 246  
 
 247  
     /**
 248  
      * Output absolute filename for resolved artifacts
 249  
      *
 250  
      * @optional
 251  
      * @since 2.0
 252  
      * @parameter expression="${outputAbsoluteArtifactFilename}"
 253  
      *            default-value="false"
 254  
      */
 255  
     private boolean outputAbsoluteArtifactFilename;
 256  
 
 257  
     // Static --------------------------------------------------------
 258  
 
 259  
     // Constructors --------------------------------------------------
 260  
 
 261  
     /**
 262  
      *
 263  
      */
 264  0
     public DeploySesatWarfilesMojo() {
 265  0
     }
 266  
 
 267  
     // Public --------------------------------------------------------
 268  
 
 269  
     // Contextualizable implementation ----------------------------------------------
 270  
 
 271  
     /** {@inheritDoc}
 272  
      */
 273  
     public void contextualize(final Context context) throws ContextException {
 274  
 
 275  0
         container = (PlexusContainer) context.get(PlexusConstants.PLEXUS_KEY);
 276  0
     }
 277  
 
 278  
     // CopyMojo overrides ---------------------------------------------------
 279  
 
 280  
     /** {@inheritDoc}
 281  
      */
 282  
     public void execute() throws MojoExecutionException{
 283  
 
 284  
         // only ever interested in war projects. silently ignore other projects.
 285  0
         if("war".equals(project.getPackaging())){
 286  
 
 287  0
             pushFields();
 288  
 
 289  0
             final Wagon wagon = getWagon();
 290  0
             if(null != wagon){
 291  
 
 292  
                 try{
 293  0
                     executeServerDeploy(wagon);
 294  
 
 295  
                 }finally{
 296  0
                     try{
 297  0
                        wagon.disconnect();
 298  
 
 299  0
                     }catch(ConnectionException ex){
 300  0
                         getLog().error(ex);
 301  0
                         throw new MojoExecutionException("repository wagon not disconnected", ex);
 302  0
                     }
 303  
                 }
 304  
 
 305  
             }else{
 306  
 
 307  0
                 executeLocalDeploy();
 308  
             }
 309  
         }
 310  
 
 311  0
     }
 312  
 
 313  
     // Package protected ---------------------------------------------
 314  
 
 315  
     // Protected -----------------------------------------------------
 316  
 
 317  
     // Private -------------------------------------------------------
 318  
 
 319  
     private void executeServerDeploy(final Wagon wagon) throws MojoExecutionException{
 320  
 
 321  
         // alpha|nuclei|beta|electron|gamma|production deployment goes through scpexe
 322  
         try{
 323  0
             if(ensureNoLocalModifications()){
 324  
 
 325  
                 @SuppressWarnings("unchecked")
 326  0
                 final List<ArtifactItem> theArtifactItems = getProcessedArtifactItems(stripVersion);
 327  
 
 328  0
                 for(ArtifactItem item : theArtifactItems){
 329  
 
 330  0
                     final Artifact artifact = factory.createArtifactWithClassifier(
 331  
                             item.getGroupId(),
 332  
                             item.getArtifactId(),
 333  
                             item.getVersion(),
 334  
                             item.getType(),
 335  
                             profile);
 336  
 
 337  0
                     resolver.resolve(artifact, getRemoteRepos(), getLocal());
 338  
 
 339  0
                     final String sesamSite = project.getProperties().getProperty("sesam.site");
 340  0
                     final String destName = null != sesamSite
 341  
                             ? sesamSite
 342  
                             : project.getBuild().getFinalName();
 343  
 
 344  
                     // tag the code
 345  0
                     if(Boolean.parseBoolean(project.getProperties().getProperty(TAG_ON_DEPLOY))
 346  
                             && !Boolean.getBoolean(DRY_RUN)){
 347  
 
 348  0
                         tagDeploy();
 349  
                     }
 350  
 
 351  
                     // do the upload
 352  0
                     getLog().info("Uploading " + artifact.getFile().getAbsolutePath()
 353  
                             + " to " + wagon.getRepository().getUrl() + '/' + destName + ".war");
 354  
 
 355  0
                     if(!Boolean.getBoolean(DRY_RUN)){
 356  0
                         wagon.put(artifact.getFile(), destName + ".war");
 357  
                     }
 358  
 
 359  
                     // update the version.txt
 360  0
                     getLog().info("Updating " + wagon.getRepository().getUrl() + "/version.txt");
 361  
 
 362  0
                     if(Boolean.getBoolean(DRY_RUN)){
 363  
 
 364  0
                         final StringWriter sb = new StringWriter();
 365  0
                         final BufferedWriter w = new BufferedWriter(sb);
 366  0
                         updateArtifactEntry(new BufferedReader(new StringReader("")), w);
 367  0
                         w.flush();
 368  0
                         getLog().info("version.txt entry will be \n" + sb.toString());
 369  
 
 370  0
                     }else{
 371  
 
 372  0
                         updateVersionFile(wagon);
 373  
                     }
 374  0
                 }
 375  
             }
 376  
 
 377  0
         }catch(TransferFailedException ex){
 378  0
             getLog().error(ex);
 379  0
             throw new MojoExecutionException("transfer failed", ex);
 380  0
         }catch(ResourceDoesNotExistException ex){
 381  0
             getLog().error(ex);
 382  0
             throw new MojoExecutionException("resource does not exist", ex);
 383  0
         }catch(AuthorizationException ex){
 384  0
             getLog().error(ex);
 385  0
             throw new MojoExecutionException("authorisation exception", ex);
 386  0
         }catch(ArtifactNotFoundException ex){
 387  0
             getLog().error(ex);
 388  0
             throw new MojoExecutionException("artifact not found", ex);
 389  0
         }catch(ArtifactResolutionException ex){
 390  0
             getLog().error(ex);
 391  0
             throw new MojoExecutionException("artifact resolution exception", ex);
 392  0
         }catch(ScmException ex){
 393  0
             getLog().error(ex);
 394  0
             throw new MojoExecutionException("scm exception", ex);
 395  0
         }catch(ComponentLookupException ex){
 396  0
             getLog().error(ex);
 397  0
             throw new MojoExecutionException("failed to lookup ScmManager", ex);
 398  0
         }catch(IOException ex){
 399  0
             getLog().error(ex);
 400  0
             throw new MojoExecutionException("IOException", ex);
 401  0
         }
 402  
 
 403  0
     }
 404  
 
 405  
     private void executeLocalDeploy() throws MojoExecutionException{
 406  
 
 407  
         // local-deploy behaviour comes from super implementation
 408  
         // some pre-condition checks first
 409  
 
 410  
         // 1. the output directory must exist
 411  0
         if(getOutputDirectory().exists()){
 412  
 
 413  
             // 2. output directory is writable
 414  0
             if(getOutputDirectory().canWrite()){
 415  
 
 416  0
                 super.execute();
 417  
 
 418  
             }else{
 419  
 
 420  
                 // 2.failure output directory isn't writable
 421  0
                 getLog().error(getOutputDirectory().getAbsolutePath() + " can not be written to.");
 422  
             }
 423  
         }else{
 424  
 
 425  
             // 1.failure: the output directory doesn't exist
 426  0
             getLog().error(getOutputDirectory().getAbsolutePath() + " does not exist.");
 427  0
             final String catalinaBase = System.getProperty("env.CATALINA_BASE");
 428  0
             if(null == catalinaBase || 0 == catalinaBase.length()){
 429  0
                 getLog().info("Define system variable CATALINA_BASE to enable automatic deployment.");
 430  
             }
 431  
         }
 432  
 
 433  0
     }
 434  
 
 435  
     private void pushFields(){
 436  0
         setArchiverManager(archiverManager);
 437  0
         setArtifactCollector(artifactCollector);
 438  0
         setArtifactMetadataSource(artifactMetadataSource);
 439  0
         setFactory(factory);
 440  0
         setResolver(resolver);
 441  0
     }
 442  
 
 443  
     private void loadPomProject(){
 444  
 
 445  0
         if(null == pomProject){
 446  0
             pomProject = project;
 447  
             do{
 448  0
                 pomProject = pomProject.getParent();
 449  0
             }while(!"pom".equals(pomProject.getPackaging()));
 450  
         }
 451  0
     }
 452  
     /**
 453  
      *
 454  
      * @return the wagon (already connected) to use against the profile's serverDeployLocation
 455  
      *              or null if in development profile
 456  
      * @throws org.apache.maven.plugin.MojoExecutionException
 457  
      */
 458  
     private Wagon getWagon() throws MojoExecutionException {
 459  
 
 460  0
         loadProfile();
 461  
 
 462  
         try {
 463  
 
 464  0
             Wagon wagon = null;
 465  
 
 466  0
             if(null != profile){
 467  
 
 468  0
                 final String serverDeployLocation = project.getProperties().getProperty("serverDeployLocation");
 469  
 
 470  0
                 final String protocol = serverDeployLocation.substring(0, serverDeployLocation.indexOf(':'));
 471  
 
 472  0
                 final Repository wagonRepository = new Repository();
 473  
 
 474  0
                 wagonRepository.setUrl(serverDeployLocation);
 475  
 
 476  0
                 final RepositoryPermissions permissions = new RepositoryPermissions();
 477  0
                 permissions.setFileMode("g+w");
 478  0
                 wagonRepository.setPermissions(permissions);
 479  
 
 480  0
                 wagon = (Wagon) container.lookup(Wagon.ROLE, protocol);
 481  0
                 wagon.connect(wagonRepository);
 482  
 
 483  
             }
 484  0
             return wagon;
 485  
 
 486  0
         }catch (ConnectionException ex) {
 487  0
             getLog().error(ex);
 488  0
             throw new MojoExecutionException("repository wagon not connected", ex);
 489  0
         }catch (AuthenticationException ex) {
 490  0
             getLog().error(ex);
 491  0
             throw new MojoExecutionException("repository wagon not authenticated", ex);
 492  0
         }catch (ComponentLookupException ex) {
 493  0
             getLog().error(ex);
 494  0
             throw new MojoExecutionException("repository wagon not found", ex);
 495  
         }
 496  
 
 497  
     }
 498  
 
 499  
     private void loadProfile(){
 500  
 
 501  0
         if(null == profile){
 502  
 
 503  
             @SuppressWarnings("unchecked")
 504  0
             final List<Profile> profiles = project.getActiveProfiles();
 505  
 
 506  0
             for(String entry : ENVIRONMENTS){
 507  0
                 for(Profile p : profiles){
 508  0
                     if(null != p && null != p.getId() && p.getId().equals(entry)){
 509  0
                         profile = p.getId();
 510  0
                         return;
 511  
                     }
 512  
                 }
 513  
             }
 514  
         }
 515  0
     }
 516  
 
 517  
     private void tagDeploy() throws ComponentLookupException, ScmException{
 518  
 
 519  0
         final ScmManager scmManager = (ScmManager) container.lookup(ScmManager.ROLE);
 520  0
         final String date = new SimpleDateFormat("yyyyMMddHHmm").format(now);
 521  
 
 522  0
         loadPomProject();
 523  
 
 524  0
         final TagScmResult result = scmManager.tag(
 525  
                 scmManager.makeScmRepository(project.getScm().getDeveloperConnection()),
 526  
                 new ScmFileSet(pomProject.getBasedir()), // TODO server-side copy
 527  
                 profile + "-deployments/" + date + "-" + project.getArtifactId(),
 528  
                 "sesat " + profile + " deployment");
 529  
 
 530  0
         if(!result.isSuccess()){
 531  0
             throw new ScmException(result.getCommandOutput());
 532  
         }
 533  0
     }
 534  
 
 535  
     private boolean ensureNoLocalModifications() throws ComponentLookupException, ScmException, MojoExecutionException{
 536  
 
 537  0
         if(!Boolean.getBoolean("sesat.mojo.localModifications.ignore")){
 538  
 
 539  0
             final ScmManager scmManager = (ScmManager) container.lookup(ScmManager.ROLE);
 540  
 
 541  0
             loadPomProject();
 542  
 
 543  0
             final StatusScmResult result = scmManager.status(
 544  
                     scmManager.makeScmRepository(project.getScm().getDeveloperConnection()),
 545  
                     new ScmFileSet(pomProject.getBasedir()));
 546  
 
 547  
 
 548  0
             if(!result.isSuccess()){
 549  
 
 550  0
                 getLog().error(result.getCommandOutput());
 551  0
                 throw new MojoExecutionException("Failed to ensure checkout has no modifications");
 552  
             }
 553  
 
 554  0
             if(0 < result.getChangedFiles().size()){
 555  
 
 556  0
                 throw new MojoExecutionException("Your checkout has local modifications. "
 557  
                         + "Server deploy can *only* be done with a clean workbench.");
 558  
             }
 559  
 
 560  0
             return result.isSuccess() && 0 == result.getChangedFiles().size();
 561  
         }
 562  0
         return true; // sesat.mojo.localModifications.ignore
 563  
     }
 564  
 
 565  
     private void updateVersionFile(final Wagon wagon)
 566  
             throws IOException, TransferFailedException, ResourceDoesNotExistException, AuthorizationException{
 567  
 
 568  
         // update the version.txt accordingly
 569  0
         final File versionOldFile = File.createTempFile("version-old", "txt");
 570  0
         final File versionNewFile = File.createTempFile("version-new", "txt");
 571  0
         wagon.get("version.txt", versionOldFile);
 572  
 
 573  0
         boolean updated = false;
 574  
 
 575  
         // look for our artifactId entry
 576  0
         final BufferedReader reader = new BufferedReader(new FileReader(versionOldFile));
 577  0
         final BufferedWriter writer = new  BufferedWriter(new FileWriter(versionNewFile));
 578  0
         for(String line = reader.readLine(); null != line; line = reader.readLine()){
 579  0
             if(line.equals(project.getArtifactId())){
 580  0
                 updateArtifactEntry(reader, writer);
 581  0
                 updated = true;
 582  
             }else{
 583  
                 // line remains the same
 584  0
                 writer.write(line);
 585  0
                 writer.newLine();
 586  
             }
 587  
         }
 588  0
         if(!updated){
 589  0
             writer.newLine();
 590  0
             updateArtifactEntry(reader, writer);
 591  
         }
 592  0
         reader.close();
 593  0
         writer.close();
 594  0
         wagon.put(versionNewFile, "version.txt");
 595  0
     }
 596  
 
 597  
     private void updateArtifactEntry(final BufferedReader reader, final BufferedWriter writer) throws IOException{
 598  
 
 599  
         // this line remains the same
 600  0
         writer.write(project.getArtifactId());
 601  0
         writer.newLine();
 602  
         // next line is version, author (or "deployer"), date & time,
 603  0
         reader.readLine();
 604  0
         writer.write(project.getVersion()
 605  
                 + " was last deployed by " + System.getProperty("user.name")
 606  
                 + " at " + SimpleDateFormat.getDateTimeInstance().format(now));
 607  0
         writer.newLine();
 608  
         // next line is the quote
 609  0
         for(String line = reader.readLine(); null != line && 0 < line.length(); line = reader.readLine()){};
 610  0
         final String quote = project.getProperties().getProperty("version.quote");
 611  0
         if(null != quote){
 612  0
             writer.write(quote);
 613  0
             writer.newLine();
 614  0
             writer.newLine();
 615  
         }
 616  
 
 617  0
     }
 618  
 
 619  
     // Inner classes -------------------------------------------------
 620  
 }