1 /*
   2  * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package jdk.jshell.execution;
  26 
  27 import java.io.DataInputStream;
  28 import java.io.IOException;
  29 import java.io.InputStream;
  30 import java.io.OutputStream;
  31 import java.util.regex.Matcher;
  32 import java.util.regex.Pattern;
  33 
  34 /**
  35  * Read from an InputStream which has been packetized and write its contents
  36  * to the named OutputStreams.
  37  *
  38  * @author Jan Lahoda
  39  * @see Util#demultiplexInput(java.io.InputStream, jdk.jshell.execution.ECLogger, java.io.OutputStream, java.io.OutputStream, java.io.OutputStream...) 
  40  */
  41 class DemultiplexInput extends Thread {
  42 
  43     private static final Pattern AUX_FORMAT = Pattern.compile("aux(\\d+)");
  44     private final DataInputStream delegate;
  45     private final PipeInputStream command;
  46     private final OutputStream out;
  47     private final OutputStream err;
  48     private final OutputStream[] aux;
  49     private final ECLogger log;
  50 
  51     DemultiplexInput(InputStream input, PipeInputStream command, ECLogger log,
  52             OutputStream out, OutputStream err, OutputStream... aux) {
  53         super("output reader");
  54         this.delegate = new DataInputStream(input);
  55         this.command = command;
  56         this.out = out;
  57         this.err = err;
  58         this.aux = aux;
  59         this.log = log;
  60     }
  61 
  62     @Override
  63     public void run() {
  64         try {
  65             while (true) {
  66                 int nameLen = delegate.read();
  67                 if (nameLen == (-1)) {
  68                     break;
  69                 }
  70                 byte[] name = new byte[nameLen];
  71                 DemultiplexInput.this.delegate.readFully(name);
  72                 int dataLen = delegate.read();
  73                 byte[] data = new byte[dataLen];
  74                 DemultiplexInput.this.delegate.readFully(data);
  75                 String chan = new String(name, "UTF-8");
  76                 switch (chan) {
  77                     case "err":
  78                         err.write(data);
  79                         break;
  80                     case "out":
  81                         out.write(data);
  82                         break;
  83                     case "command":
  84                         for (byte b : data) {
  85                             command.write(Byte.toUnsignedInt(b));
  86                         }
  87                         break;
  88                     default: {
  89                         Matcher mat = AUX_FORMAT.matcher(chan);
  90                         if (mat.matches()) {
  91                             int i = Integer.parseInt(mat.group(1));
  92                             if (i >= 1 && i < aux.length) {
  93                                 aux[i-1].write(data);
  94                                 log.debug("WROTE on %s : %s", chan, data);
  95                                 break;
  96                             }
  97                         }
  98                         log.debug("Unexpected channel name: %s", chan);
  99                         break;
 100                     }
 101                 }
 102             }
 103         } catch (IOException ex) {
 104             log.debug(ex, "Failed reading output");
 105         } finally {
 106             command.close();
 107         }
 108     }
 109 
 110 }