View Javadoc
1   package org.apache.maven.scm.provider.accurev.cli;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *    http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.IOException;
23  import java.io.Reader;
24  import java.io.Writer;
25  import java.nio.channels.Channels;
26  import java.nio.channels.Pipe;
27  import java.nio.channels.Pipe.SinkChannel;
28  import java.nio.channels.Pipe.SourceChannel;
29  import java.nio.charset.Charset;
30  import java.util.ArrayList;
31  import java.util.Arrays;
32  import java.util.HashMap;
33  import java.util.List;
34  import java.util.Map;
35  
36  import org.apache.maven.scm.log.ScmLogger;
37  import org.codehaus.plexus.util.cli.StreamConsumer;
38  import org.codehaus.plexus.util.xml.pull.MXParser;
39  import org.codehaus.plexus.util.xml.pull.XmlPullParser;
40  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
41  
42  /**
43   * This class is required because Plexus command line won't let you get to the process stream output process.
44   * 
45   * @author ggardner
46   */
47  public abstract class XppStreamConsumer
48      extends Thread
49      implements StreamConsumer
50  
51  {
52      public ScmLogger getLogger()
53      {
54          return logger;
55      }
56  
57      private Writer writer;
58  
59      private XmlPullParser parser = new MXParser();
60  
61      private volatile boolean complete = false;
62  
63      private ScmLogger logger;
64  
65      private int lineCount = 0;
66  
67      private Reader reader;
68  
69      public XppStreamConsumer( ScmLogger logger )
70      {
71  
72          super();
73          this.logger = logger;
74          try
75          {
76              Pipe p = Pipe.open();
77              SinkChannel sink = p.sink();
78              SourceChannel source = p.source();
79              writer = Channels.newWriter( sink, Charset.defaultCharset().name() );
80              reader = Channels.newReader( source, Charset.defaultCharset().name() );
81              parser.setInput( reader );
82  
83          }
84          catch ( Exception e )
85          {
86              logger.error( "Exception initialising pipe", e );
87          }
88  
89      }
90  
91      public final void consumeLine( String line )
92      {
93          // Do not debug line here - as CommandOutputConsumer wraps this and uses
94          // the same logger
95          try
96          {
97              writer.append( line );
98              if ( lineCount == 0 )
99              {
100                 this.start();
101             }
102             lineCount++;
103             writer.flush();
104         }
105         catch ( IOException e )
106         {
107             throw new RuntimeException( "error pumping line to pipe", e );
108         }
109 
110     }
111 
112     @Override
113     public void run()
114     {
115 
116         try
117         {
118             parse( parser );
119         }
120         catch ( Exception e )
121         {
122             caughtParseException( e );
123         }
124         finally
125         {
126             synchronized ( this )
127             {
128                 
129                 try
130                 {
131                     reader.close();
132                 }
133                 catch ( IOException e )
134                 {
135                     getLogger().warn( "Error closing pipe reader", e );
136                 }
137                 
138                 complete = true;
139                 this.notifyAll();
140             }
141         }
142     }
143 
144     protected void caughtParseException( Exception e )
145     {
146         logger.warn( "Exception parsing input", e );
147 
148     }
149 
150     protected void parse( XmlPullParser p )
151         throws XmlPullParserException, IOException
152     {
153         List<String> tagPath = new ArrayList<String>();
154         int eventType = p.getEventType();
155         if ( logger.isDebugEnabled() )
156         {
157             logger.debug( "Event " + eventType );
158         }
159 
160         while ( eventType != XmlPullParser.END_DOCUMENT )
161         {
162             int lastIndex = tagPath.size() - 1;
163             String tagName;
164             switch ( eventType )
165             {
166                 case XmlPullParser.START_DOCUMENT:
167 
168                     break;
169 
170                 case XmlPullParser.START_TAG:
171                     tagName = p.getName();
172                     if ( tagName != null )
173                     {
174                         tagPath.add( tagName );
175                         int attributeCount = p.getAttributeCount();
176                         Map<String, String> attributes = new HashMap<String, String>( Math.max( attributeCount, 0 ) );
177                         for ( int i = 0; i < attributeCount; i++ )
178                         {
179                             attributes.put( p.getAttributeName( i ), p.getAttributeValue( i ) );
180                         }
181 
182                         startTag( tagPath, attributes );
183                     }
184                     break;
185 
186                 case XmlPullParser.TEXT:
187                     if ( !p.isWhitespace() )
188                     {
189                         String text = p.getText();
190                         text( tagPath, text );
191                     }
192                     break;
193 
194                 case XmlPullParser.END_TAG:
195                     tagName = p.getName();
196 
197                     if ( lastIndex < 0 || !tagName.equals( tagPath.get( lastIndex ) ) )
198                     {
199                         logger.warn( "Bad tag path: " + Arrays.toString( tagPath.toArray() ) );
200                     }
201                     endTag( tagPath );
202                     tagPath.remove( lastIndex );
203                     break;
204 
205                 default:
206                     logger.warn( "Unexpected event type " + eventType );
207                     break;
208             }
209             p.next();
210             eventType = p.getEventType();
211             if ( logger.isDebugEnabled() )
212             {
213                 logger.debug( "Event " + eventType );
214             }
215         }
216     }
217 
218     /**
219      * close the input and wait for parsing to complete
220      */
221     public void waitComplete()
222     {
223         Thread.yield();
224         try
225         {
226             writer.close();
227         }
228         catch ( IOException e1 )
229         {
230             logger.warn( "Exception flushing output", e1 );
231         }
232 
233         while ( !isComplete() )
234         {
235 
236             synchronized ( this )
237             {
238                 try
239                 {
240                     if ( !isComplete() )
241                     {
242                         this.wait( 1000 );
243                     }
244                 }
245                 catch ( Exception e )
246                 {
247                     logger.warn( e );
248 
249                 }
250             }
251         }
252 
253     }
254 
255     private boolean isComplete()
256     {
257         return complete || lineCount == 0;
258     }
259 
260     protected void startTag( List<String> tagPath, Map<String, String> attributes )
261     {
262         if ( logger.isDebugEnabled() )
263         {
264             String tagName = getTagName( tagPath );
265             logger.debug( "START_TAG: " + tagName + "(" + attributes.size() + ")" );
266         }
267     }
268 
269     protected static String getTagName( List<String> tagPath )
270     {
271         return tagPath.size() == 0 ? null : tagPath.get( tagPath.size() - 1 );
272 
273     }
274 
275     protected void endTag( List<String> tagPath )
276     {
277         if ( logger.isDebugEnabled() )
278         {
279             logger.debug( "END_TAG: " + getTagName( tagPath ) );
280         }
281 
282     }
283 
284     protected void text( List<String> tagPath, String text )
285     {
286         if ( logger.isDebugEnabled() )
287         {
288             logger.debug( "TEXT: " + text );
289         }
290 
291     }
292 
293 }