001package org.apache.maven.scm.provider.perforce.command.tag;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import org.apache.maven.scm.ScmException;
023import org.apache.maven.scm.ScmFileSet;
024import org.apache.maven.scm.ScmResult;
025import org.apache.maven.scm.ScmTagParameters;
026import org.apache.maven.scm.command.tag.AbstractTagCommand;
027import org.apache.maven.scm.command.tag.TagScmResult;
028import org.apache.maven.scm.provider.ScmProviderRepository;
029import org.apache.maven.scm.provider.perforce.PerforceScmProvider;
030import org.apache.maven.scm.provider.perforce.command.PerforceCommand;
031import org.apache.maven.scm.provider.perforce.command.PerforceInfoCommand;
032import org.apache.maven.scm.provider.perforce.repository.PerforceScmProviderRepository;
033import org.codehaus.plexus.util.IOUtil;
034import org.codehaus.plexus.util.cli.CommandLineException;
035import org.codehaus.plexus.util.cli.CommandLineUtils;
036import org.codehaus.plexus.util.cli.Commandline;
037
038import java.io.BufferedReader;
039import java.io.DataOutputStream;
040import java.io.File;
041import java.io.IOException;
042import java.io.InputStreamReader;
043import java.io.OutputStream;
044import java.util.List;
045
046/**
047 * @author Mike Perham
048 * @author Olivier Lamy
049 *
050 */
051public class PerforceTagCommand
052    extends AbstractTagCommand
053    implements PerforceCommand
054{
055    private String actualRepoLocation = null;
056
057
058    protected ScmResult executeTagCommand( ScmProviderRepository repo, ScmFileSet files, String tag, String message )
059        throws ScmException
060    {
061        return executeTagCommand( repo, files, tag, new ScmTagParameters( message ) );
062    }
063
064    /**
065     * {@inheritDoc}
066     */
067    protected ScmResult executeTagCommand( ScmProviderRepository repo, ScmFileSet files, String tag,
068                                           ScmTagParameters scmTagParameters )
069        throws ScmException
070    {
071        PerforceScmProviderRepository prepo = (PerforceScmProviderRepository) repo;
072        actualRepoLocation = PerforceScmProvider.getRepoPath( getLogger(), prepo, files.getBasedir() );
073
074        PerforceTagConsumer consumer = new PerforceTagConsumer();
075        createLabel( repo, files, tag, consumer, false );
076        if ( consumer.isSuccess() )
077        {
078            syncLabel( repo, files, tag, consumer );
079        }
080        if ( consumer.isSuccess() )
081        {
082            // Now update the label if we need to lock it
083            if ( shouldLock() )
084            {
085                consumer = new PerforceTagConsumer();
086                createLabel( repo, files, tag, consumer, true );
087            }
088        }
089
090        if ( consumer.isSuccess() )
091        {
092            // Unclear what to pass as the first arg
093            return new TagScmResult( "p4 label -i", consumer.getTagged() );
094        }
095
096        // Unclear what to pass as the first arg
097        return new TagScmResult( "p4 label -i", "Tag failed", consumer.getOutput(), false );
098    }
099
100    private boolean shouldLock()
101    {
102        return Boolean.valueOf( System.getProperty( "maven.scm.locktag", "true" ) ).booleanValue();
103    }
104
105    private void syncLabel( ScmProviderRepository repo, ScmFileSet files, String tag, PerforceTagConsumer consumer )
106    {
107        Commandline cl =
108            createLabelsyncCommandLine( (PerforceScmProviderRepository) repo, files.getBasedir(), files, tag );
109        try
110        {
111            if ( getLogger().isDebugEnabled() )
112            {
113                getLogger().debug( PerforceScmProvider.clean( "Executing: " + cl.toString() ) );
114            }
115            CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer();
116            int exitCode = CommandLineUtils.executeCommandLine( cl, consumer, err );
117
118            if ( exitCode != 0 )
119            {
120                String cmdLine = CommandLineUtils.toString( cl.getCommandline() );
121
122                StringBuilder msg = new StringBuilder( "Exit code: " + exitCode + " - " + err.getOutput() );
123                msg.append( '\n' );
124                msg.append( "Command line was:" + cmdLine );
125
126                throw new CommandLineException( msg.toString() );
127            }
128        }
129        catch ( CommandLineException e )
130        {
131            if ( getLogger().isErrorEnabled() )
132            {
133                getLogger().error( "CommandLineException " + e.getMessage(), e );
134            }
135        }
136    }
137
138    private void createLabel( ScmProviderRepository repo, ScmFileSet files, String tag, PerforceTagConsumer consumer,
139                              boolean lock )
140    {
141        Commandline cl = createLabelCommandLine( (PerforceScmProviderRepository) repo, files.getBasedir() );
142        DataOutputStream dos = null;
143        InputStreamReader isReader = null;
144        InputStreamReader isReaderErr = null;
145        try
146        {
147            if ( getLogger().isDebugEnabled() )
148            {
149                getLogger().debug( PerforceScmProvider.clean( "Executing: " + cl.toString() ) );
150            }
151            Process proc = cl.execute();
152            OutputStream out = proc.getOutputStream();
153            dos = new DataOutputStream( out );
154            String label = createLabelSpecification( (PerforceScmProviderRepository) repo, tag, lock );
155            if ( getLogger().isDebugEnabled() )
156            {
157                getLogger().debug( "LabelSpec: " + NEWLINE + label );
158            }
159            dos.write( label.getBytes() );
160            dos.close();
161            out.close();
162            // TODO find & use a less naive InputStream multiplexer
163            isReader = new InputStreamReader( proc.getInputStream() );
164            isReaderErr = new InputStreamReader( proc.getErrorStream() );
165            BufferedReader stdout = new BufferedReader( isReader );
166            BufferedReader stderr = new BufferedReader( isReaderErr );
167            String line;
168            while ( ( line = stdout.readLine() ) != null )
169            {
170                if ( getLogger().isDebugEnabled() )
171                {
172                    getLogger().debug( "Consuming stdout: " + line );
173                }
174                consumer.consumeLine( line );
175            }
176            while ( ( line = stderr.readLine() ) != null )
177            {
178                if ( getLogger().isDebugEnabled() )
179                {
180                    getLogger().debug( "Consuming stderr: " + line );
181                }
182                consumer.consumeLine( line );
183            }
184            stderr.close();
185            stdout.close();
186        }
187        catch ( CommandLineException e )
188        {
189            if ( getLogger().isErrorEnabled() )
190            {
191                getLogger().error( "CommandLineException " + e.getMessage(), e );
192            }
193        }
194        catch ( IOException e )
195        {
196            if ( getLogger().isErrorEnabled() )
197            {
198                getLogger().error( "IOException " + e.getMessage(), e );
199            }
200        }
201        finally
202        {
203            IOUtil.close( dos );
204            IOUtil.close( isReader );
205            IOUtil.close( isReaderErr );
206        }
207    }
208
209    public static Commandline createLabelCommandLine( PerforceScmProviderRepository repo, File workingDirectory )
210    {
211        Commandline command = PerforceScmProvider.createP4Command( repo, workingDirectory );
212
213        command.createArg().setValue( "label" );
214        command.createArg().setValue( "-i" );
215        return command;
216    }
217
218    public static Commandline createLabelsyncCommandLine( PerforceScmProviderRepository repo, File workingDirectory,
219                                                          ScmFileSet files, String tag )
220    {
221        Commandline command = PerforceScmProvider.createP4Command( repo, workingDirectory );
222
223        command.createArg().setValue( "labelsync" );
224        command.createArg().setValue( "-l" );
225        command.createArg().setValue( tag );
226
227        List<File> fs = files.getFileList();
228        for ( File file : fs )
229        {
230            command.createArg().setValue( file.getPath() );
231        }
232        return command;
233    }
234
235    private static final String NEWLINE = "\r\n";
236
237    /*
238     * Label: foo-label
239     * View: //depot/path/to/repos/...
240     * Owner: mperham
241     */
242    public String createLabelSpecification( PerforceScmProviderRepository repo, String tag, boolean lock )
243    {
244        StringBuilder buf = new StringBuilder();
245        buf.append( "Label: " ).append( tag ).append( NEWLINE );
246        buf.append( "View: " ).append( PerforceScmProvider.getCanonicalRepoPath( actualRepoLocation ) ).append(
247            NEWLINE );
248        String username = repo.getUser();
249        if ( username == null )
250        {
251            // I have no idea why but Perforce doesn't default the owner to the current user.
252            // Since the user is not explicitly set, we use 'p4 info' to query for the current user.
253            username = PerforceInfoCommand.getInfo( getLogger(), repo ).getEntry( "User name" );
254        }
255        buf.append( "Owner: " ).append( username ).append( NEWLINE );
256        buf.append( "Options: " ).append( lock ? "" : "un" ).append( "locked" ).append( NEWLINE );
257        return buf.toString();
258    }
259}