001package 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 022import java.io.IOException; 023import java.io.Reader; 024import java.io.Writer; 025import java.nio.channels.Channels; 026import java.nio.channels.Pipe; 027import java.nio.channels.Pipe.SinkChannel; 028import java.nio.channels.Pipe.SourceChannel; 029import java.nio.charset.Charset; 030import java.util.ArrayList; 031import java.util.Arrays; 032import java.util.HashMap; 033import java.util.List; 034import java.util.Map; 035 036import org.apache.maven.scm.log.ScmLogger; 037import org.codehaus.plexus.util.cli.StreamConsumer; 038import org.codehaus.plexus.util.xml.pull.MXParser; 039import org.codehaus.plexus.util.xml.pull.XmlPullParser; 040import 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 */ 047public 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 synchronized ( this ) 236 { 237 try 238 { 239 if ( !isComplete() ) 240 { 241 this.wait( 1000 ); 242 } 243 } 244 catch ( Exception e ) 245 { 246 logger.warn( e ); 247 } 248 } 249 } 250 251 } 252 253 private boolean isComplete() 254 { 255 return complete || lineCount == 0; 256 } 257 258 protected void startTag( List<String> tagPath, Map<String, String> attributes ) 259 { 260 if ( logger.isDebugEnabled() ) 261 { 262 String tagName = getTagName( tagPath ); 263 logger.debug( "START_TAG: " + tagName + "(" + attributes.size() + ")" ); 264 } 265 } 266 267 protected static String getTagName( List<String> tagPath ) 268 { 269 return tagPath.size() == 0 ? null : tagPath.get( tagPath.size() - 1 ); 270 } 271 272 protected void endTag( List<String> tagPath ) 273 { 274 if ( logger.isDebugEnabled() ) 275 { 276 logger.debug( "END_TAG: " + getTagName( tagPath ) ); 277 } 278 } 279 280 protected void text( List<String> tagPath, String text ) 281 { 282 if ( logger.isDebugEnabled() ) 283 { 284 logger.debug( "TEXT: " + text ); 285 } 286 } 287 288}