001    package org.apache.maven.scm.provider.accurev.cli;
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    
022    import java.io.IOException;
023    import java.io.Reader;
024    import java.io.Writer;
025    import java.nio.channels.Channels;
026    import java.nio.channels.Pipe;
027    import java.nio.channels.Pipe.SinkChannel;
028    import java.nio.channels.Pipe.SourceChannel;
029    import java.nio.charset.Charset;
030    import java.util.ArrayList;
031    import java.util.Arrays;
032    import java.util.HashMap;
033    import java.util.List;
034    import java.util.Map;
035    
036    import org.apache.maven.scm.log.ScmLogger;
037    import org.codehaus.plexus.util.cli.StreamConsumer;
038    import org.codehaus.plexus.util.xml.pull.MXParser;
039    import org.codehaus.plexus.util.xml.pull.XmlPullParser;
040    import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
041    
042    /**
043     * This class is required because Plexus command line won't let you get to the process stream output process.
044     * 
045     * @author ggardner
046     */
047    public abstract class XppStreamConsumer
048        extends Thread
049        implements StreamConsumer
050    
051    {
052        public ScmLogger getLogger()
053        {
054            return logger;
055        }
056    
057        private Writer writer;
058    
059        private XmlPullParser parser = new MXParser();
060    
061        private volatile boolean complete = false;
062    
063        private ScmLogger logger;
064    
065        private int lineCount = 0;
066    
067        private Reader reader;
068    
069        public XppStreamConsumer( ScmLogger logger )
070        {
071    
072            super();
073            this.logger = logger;
074            try
075            {
076                Pipe p = Pipe.open();
077                SinkChannel sink = p.sink();
078                SourceChannel source = p.source();
079                writer = Channels.newWriter( sink, Charset.defaultCharset().name() );
080                reader = Channels.newReader( source, Charset.defaultCharset().name() );
081                parser.setInput( reader );
082    
083            }
084            catch ( Exception e )
085            {
086                logger.error( "Exception initialising pipe", e );
087            }
088    
089        }
090    
091        public final void consumeLine( String line )
092        {
093            // Do not debug line here - as CommandOutputConsumer wraps this and uses
094            // the same logger
095            try
096            {
097                writer.append( line );
098                if ( lineCount == 0 )
099                {
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    }