2011-01-20 2 views
7

VBScript에 대한 몇 가지 Runtime.exec() 호출을 통해 일부 외부 데이터를 검색 할 수밖에 없습니다. 크로스 플랫폼 유연성을 잃어 버렸기 때문에이 구현을 정말 싫어하지만 적어도 문제를 완화하기 위해 유사한 * nix 스크립트를 개발할 수 있습니다. 누구든지 묻기 전에 이 내 데이터를 수집하기 위해 외부 스크립트를 호출 할 필요가 없도록 할 수 없습니다. 나는 그 원인으로 생겨날 것이다.Java - 여러 런타임 동시 런타임 문제 .exec() InputStreams

exec() 프로세스는 Runnable까지 확장되는 사용자 정의 클래스에서 실행됩니다. BufferedReader을 사용하여 getInputStream()의 데이터를 읽습니다.

: 추가 코드가 요청 된대로 추가되었지만 추가 코드와의 관련성이 표시되지 않습니다. 포맷하는 데 시간이 걸렸기 때문에 도움이 되었기를 바랍니다. 그것은 추한하지만 건설적인 비판이 권장되는 경우 내가 개별적으로 명령을 실행하면 아, 그리고 예상대로 나는 데이터를 수집, ... 내 코드 스타일에

public class X extends JFrame implements Runnable { 

    ... 
    static final int THREADS_MAX = 4; 
    ExecutorService exec; 
    ... 
    public static void main(String[] args) { 
     ... 
     SwingUtilities.invokeLater(new X("X")); 
    } // End main(String[]) 

    public X (String title) { 
     ... 
     exec = Executors.newFixedThreadPool(THREADS_MAX); 
     ... 

     // Create all needed instances of Y 
     for (int i = 0; i < objects.length; i++) { 
     Y[i] = new Y(i); 
     } // End for(i) 

     // Initialization moved here for easy single-thread testing 
     // Undesired, of course 
     for (int i = 0; i < objects.length; i++) { 
     Y[i].initialize(parent); 
     } // End for(i) 

    } // End X 

    class Y implements Runnable { 
     // Define variables/arrays used to capture data here 
     String computerName = ""; 
     ... 

     public Y(int rowIndex) { 
     row   = rowIndex; 
     ... 
     computerName = (String)JTable.getValueAt(row, 0); 
     ... 
     exec.execute(this); 
     } // End Y(int) 

     public void run() { 
     // Initialize variables/arrays used to capture data here 
     ... 

     // Initialization should be done here for proper threading 
     //initialize(parent); 
     } // End run() 

     public void initialize(Z obj) { 
     runTime = Runtime.getRuntime(); 
     ... 

     try { 
      process = runTime.exec("cscript.exe query.vbs " + computerName); 
      stdErr = process.getErrorStream(); 
      stdIn = process.getInputStream(); 
      isrErr = new InputStreamReader(stdErr); 
      isrIn = new InputStreamReader(stdIn); 
      brErr = new BufferedReader(isrErr); 
      brIn = new BufferedReader(isrIn); 

      while ((line = brIn.readLine()) != null) { 
       // Capture, parse, and store data here 
       ... 
      } // End while 

     } catch (IOException e) { 
      System.out.println("Unable to run script"); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } finally { 
      try { 
       stdErr.close(); 
       stdIn. close(); 
       isrErr.close(); 
       isrIn. close(); 
       brErr. close(); 
       brIn. close(); 
      } catch (IOException e) { 
       System.out.println("Unable to close streams."); 
      } // End try 
     } // End try 
     } // End initialize(Z) 
     ... 
    } // End class Y 
} // End class X 

을 쉽게 이동합니다. 그러나 클래스의 run() 블록에서 명령을 실행하면 (예 : 호출이 동시에 발생 함을 의미 함) 모든 입력 스트림이 하나만 생성되는 것처럼 보입니다. 모두 BufferedReaders이 동시에 소비됩니다.

문제를 디버깅하려면 입력 스트림을 읽는 클래스의 인스턴스가 접두사로 붙은 콘솔에서 각 사용 된 행을 출력합니다. 나는 그들이 인스턴스에 인스턴스의 순서가 될 수 있음을 이해하고, 다음과 같은 것을 기대하지만, 단일 인스턴스의 라인 순서는 그대로 다음과 같습니다

내가 예상 번호를 어떻게 이상한입니다
exec 0: Line1 
exec 1: Line1 
exec 2: Line1 
exec 0: Line2 
exec 1: Line2 
exec 2: Line2 
exec 0: Line3 
exec 1: Line3 
exec 2: Line3 
... 

출력의 맨 처음 줄 (Microsoft (R) Windows Script Host Version 5.7)의 인스턴스이지만이 행 다음에 입력 스트림에서 하나의 프로세스 만 계속 데이터를 생성하며 모든 판독기는 다음 예와 같이이 스트림을 임의로 소비합니다.

exec 2: Microsoft (R) Windows Script Host Version 5.7 
exec 0: Microsoft (R) Windows Script Host Version 5.7 
exec 1: Microsoft (R) Windows Script Host Version 5.7 
exec 0: line2 
exec 1: line3 
exec 2: line4 
... 

설상가상으로 독자들은 실속하고 readLine()은 null을 반환하지 않습니다. 이 유형의 동작에는 버퍼 크기와 관련이있을 수 있지만 짧은 출력에서도 두 개의 동시 스레드 만 실행하면 여전히 동일한 동작을 나타냅니다. stdErr에 아무 것도 캡처되지 않아 문제가 있음을 나타냅니다.

이것이 스크립트 호스트의 제한 사항인지 확인하기 위해 START 스크립트의 여러 인스턴스를 동시에 배치하는 배치 파일을 만들었습니다. 이것은 cmd 쉘의 외부에서 Java의으로 실행되었다고 명시해야하며 자체 쉘을 여러 개 실행해야합니다. 그러나 각 동시 인스턴스는 예상 결과를 완전히 반환하고 올바르게 작동합니다.

편집 :

try { 
    Thread.sleep((int)(Math.random() * 1200)); 
} catch (InterruptedException e) { 
    System.out.println("Can't sleep!"); 
} // End try 
initialize(monitor); 

를 내 코드로 : 다른 문제 해결 아이디어로, 나는 동시성을 다시 활성화,하지만 내 Y.run() 블록에 다음을 삽입하여 내 초기화 방법을 비틀하기로 결정했다. 처음 몇 줄에 여러 개의 출력이 표시되기 시작하지만 동일한 제작자를 소비하는 여러 소비자로 신속하게 되돌아 가고 첫 번째 완성 된 스트림이 닫히 자마자 나머지 소비자는 예외를 발생시킵니다. 다음 소비자는 IOException: Read error을 발사하고 나머지는 IOException: Stream closed을 발사합니다!

는 maaartinus에 따르면, 이제 문제가 원하지 않는 행동의 원인이 무엇되고, 여러 실행하는 동시 InputStreams 수 있습니까? 어떻게 독립적으로 입력 스트림을 잡을 수 있습니까? 필자가 피할 수만 있다면 데이터를 다시 처리하기 위해 임시 파일에 기록하지 않아도됩니다.

+1

는'그래서 혼란 스러워요? - 다중 입력을 처리 할 수있는 자바 동시에 여부 스트림입니다'확실히, 그것은이다. Windows Script Host에 문제가있을 수 있습니다. 사소한 출력을내는 사소한 스크립트 (또는 다른 프로그램)를 사용하려고합니다. – maaartinus

+1

일부 필드가 정적 일 수 있으므로 이상하게 들립니다. 더 많은 수업을 게시 할 수 있습니까? 설상가상으로 –

+1

'는 결코 즉, 두 개의 스레드 프로세스 당, 각 스트림에 대한 자신의 스레드를 사용해보십시오 null.' 반환, 독자는 실속 내의 readLine(). 각 스트림에는 작은 버퍼가 있으며 프로세스가 가득 차면 프로세스가 차단됩니다 (다른 스트림에서는 아무것도 얻지 못합니다). – maaartinus

답변

5

나는 당신이 IO 변수의 범위에주의 할 필요가 있다고 생각합니다. (; 자바 MultiExec %의 javac의 MultiExec.java)

[2] tomcat* 
[0] Thu Jan 20 18:38:31 CST 2011 
[3] 368 /etc/apache2/apache2.conf 
[1] active 
[1] bounce 
[1] corrupt 
[1] defer 
[1] deferred 
[1] etc 
[1] flush 
[1] hold 
[1] incoming 
[1] lib 
[1] maildrop 
[1] pid 
[1] private 
[1] public 
[1] saved 
[1] trace 
[1] usr 
[1] var 

여기에 4 자식 프로세스에서 동시 입력 스트림과 함께 완벽하게 작동하는 빠른 코드 ... 위의 코드에서

import java.io.*; 

public class MultiExec { 

     private final static String[] comLines = { 
         "date", 
         "ls /var/spool/postfix", 
         "ls -F /usr/local/bin", 
         "wc -l /etc/apache2/apache2.conf"}; 

     public void execute() { 
       for (int i = 0 ; i < comLines.length ; i++) { 
         ExecutableChild ec = new ExecutableChild (i, comLines[i]); 
         new Thread (ec).start(); 
     }} 

     public class ExecutableChild implements Runnable { 

       private int prIndex; 
       private String executable; 

       public ExecutableChild (int k, String cmd) { 
         prIndex = k; 
         executable = cmd; 
       } 

       public void run() { 
         try { 
           Process child = Runtime.getRuntime().exec(executable); 
           BufferedReader br = new BufferedReader (new InputStreamReader (
                   child.getInputStream())); 
           for (String s = br.readLine() ; s != null ; s = br.readLine()) 
             System.out.println ("[" + prIndex + "] " + s); 
           br.close(); 
         } catch (IOException ioex) { 
           System.err.println ("IOException for process #"+ 
               prIndex+ ": " + ioex.getMessage()); 
     }}} 

     public static void main (String[] args) { 
       new MultiExec().execute(); 
     } 
} 

출력은 시도에 대한 소스 코드를 얻은 경우 논의 할 수 있습니다. 좋은 소원, - M. S.

============================================== =================

편집 : DN : 단선 출력에 대한 우려를 알고 있습니다. ... 작은 스크립트가

#!/usr/bin/perl -w 
foreach (1..50) { 
     print "$_\n"; 
} 

자바 코드 위의 편집 된 버전을 수 있습니다 ... comLines이 변경되었습니다, 및 Thread.sleep를 매일에 println()

공용 클래스 MultiExec 이후에 추가 {여기

 private final static String[] comLines = { 
         "ls /var/spool/postfix", 
         "perl count50.pl", 
         "cat MultiExec.java", 
         "head -40 /etc/apache2/apache2.conf"}; 

     public void execute() { 
       for (int i = 0 ; i < comLines.length ; i++) { 
         ExecutableChild ec = new ExecutableChild (i, comLines[i]); 
         new Thread (ec).start(); 
     }} 

     public class ExecutableChild implements Runnable { 

       private int prIndex; 
       private String executable; 

       public ExecutableChild (int k, String cmd) { 
         prIndex = k; 
         executable = cmd; 
       } 

       public void run() { 
         try { 
           Process child = Runtime.getRuntime().exec(executable); 
           BufferedReader br = new BufferedReader (new InputStreamReader (
                   child.getInputStream())); 
           for (String s = br.readLine() ; s != null ; s = br.readLine()) { 
             System.out.println ("[" + prIndex + "] " + s); 
             try { 
               Thread.sleep (20); 
             } catch (InterruptedException intex) { 
           }} 
           br.close(); 
         } catch (IOException ioex) { 
           System.err.println ("IOException for process #"+ 
                   prIndex+ ": " + ioex.getMessage()); 
     }}} 

     public static void main (String[] args) { 
       new MultiExec().execute(); 
}} 

는 출력 (후 컴파일/실행) 지금

[0] active 
[1] 1 
[2] import java.io.*; 
[3] # 
[2] 
[0] bounce 
[1] 2 
[3] # Based upon the NCSA server configuration files originally by Rob McCool. 
[2] public class MultiExec { 
[1] 3 
[0] corrupt 
[3] # 
[1] 4 
[2] 
[0] defer 
[3] # This is the main Apache server configuration file. It contains the 
[2]  private final static String[] comLines = { 
[0] deferred 
[1] 5 
[3] # configuration directives that give the server its instructions. 
[2]      "ls /var/spool/postfix", 
[0] etc 
[1] 6 
[3] # See http://httpd.apache.org/docs/2.2/ for detailed information about 
[2]      "perl count50.pl", 
[0] flush 
[1] 7 
[3] # the directives. 
[2]      "cat MultiExec.java", 
[1] 8 
[0] hold 
[3] # 
[1] 9 
[2]      "head -40 /etc/apache2/apache2.conf"}; 
[0] incoming 
[3] # Do NOT simply read the instructions in here without understanding 
[2] 
[0] lib 
[1] 10 
[3] # what they do. They're here only as hints or reminders. If you are unsure 
[1] 11 
[2]  public void execute() { 
[0] maildrop 
[3] # consult the online docs. You have been warned. 
[2]    for (int i = 0 ; i < comLines.length ; i++) { 
[0] pid 
[1] 12 
[3] # 
[1] 13 
[2]      ExecutableChild ec = new ExecutableChild (i, comLines[i]); 
[0] private 
[3] # The configuration directives are grouped into three basic sections: 
[1] 14 
[2]      new Thread (ec).start(); 
[0] public 
[3] # 1. Directives that control the operation of the Apache server process as a 
[2]  }} 
[1] 15 
[0] saved 
[3] #  whole (the 'global environment'). 
[1] 16 
[0] trace 
[2] 
[3] # 2. Directives that define the parameters of the 'main' or 'default' server, 
[0] usr 
[2]  public class ExecutableChild implements Runnable { 
[1] 17 
[3] #  which responds to requests that aren't handled by a virtual host. 
[0] var 
[2] 
[1] 18 
[3] #  These directives also provide default values for the settings 
[1] 19 
[2]    private int prIndex; 
[3] #  of all virtual hosts. 
[1] 20 
[2]    private String executable; 
[3] # 3. Settings for virtual hosts, which allow Web requests to be sent to 
[2] 
[1] 21 
[3] #  different IP addresses or hostnames and have them handled by the 
[1] 22 
[2]    public ExecutableChild (int k, String cmd) { 
[3] #  same Apache server process. 
[1] 23 
[2]      prIndex = k; 
[3] # 
[1] 24 
[2]      executable = cmd; 
[3] # Configuration and logfile names: If the filenames you specify for many 
[2]    } 
[1] 25 
[3] # of the server's control files begin with "/" (or "drive:/" for Win32), the 
[2] 
[1] 26 
[3] # server will use that explicit path. If the filenames do *not* begin 
[1] 27 
[2]    public void run() { 
[3] # with "/", the value of ServerRoot is prepended -- so "/var/log/apache2/foo.log" 
[1] 28 
[2]      try { 
[3] # with ServerRoot set to "" will be interpreted by the 
[1] 29 
[2]        Process child = Runtime.getRuntime().exec(executable); 
[3] # server as "//var/log/apache2/foo.log". 
[1] 30 
[2]        BufferedReader br = new BufferedReader (new InputStreamReader (
[3] # 
[1] 31 
[2]                child.getInputStream())); 
[3] 
[1] 32 
[2]        for (String s = br.readLine() ; s != null ; s = br.readLine()) { 
[3] ### Section 1: Global Environment 
[1] 33 
[2]          System.out.println ("[" + prIndex + "] " + s); 
[3] # 
[1] 34 
[2]          try { 
[3] # The directives in this section affect the overall operation of Apache, 
[1] 35 
[2]            Thread.sleep (20); 
[3] # such as the number of concurrent requests it can handle or where it 

...... 

입력 스트림이 정상적으로 작동하고 있으며 여기에 문제가 있다고 생각하지 않습니다. 죄송 너무 오래 회신에 대해 점점. 최선을 기원하며 코드를보기를 기다리고 있습니다. - M.S.

+0

내가 사용하는 예제가 a) cscript 명령만큼 복잡하지 않고 b) 한 가지만 제외하고 모두 한 줄을 반환한다는 점을 알아 채지 못할 수도 있습니다. 모든 의도와 목적에 기술적으로 비슷한 문제가있을 수 있습니다 :) 어쨌든, 거의 모든 코드입니다. 나머지 클래스 변수는 추출 된 데이터를 저장하는 데 사용되며 모든 생성자 또는 메서드 외부에서 모두 정적 (비 정적)으로 정의됩니다. 나는 대화를 옮기는 데 도움이되는 좀 더 많은 구조 (많은 것은 무의미 함)를 게시 할 것이다. 지금까지의 노력은 대단히 감사합니다! –

+0

안녕하세요. 다시 DN으로 수정 된 답장이 게시됩니다. 어떤 도움이되어서 기쁘다. - M.S. –

+0

그것은 명백하게 나를 나머지 30 분의 시간 (heh의 일부분을 위해 바빴다)에 가져 갔다. 시간을내어 오늘 밤에는 더 이상 볼 수 없게됩니다. : P –

3

stdErrstdIn이 올바른 범위에 있는지 확인하십시오. 이 경우 Y에 신고해야합니다.

당신이 X에서 그들을 선언하는 경우, 다음과 같은 코드를 실행할 때마다 :

stdErr = process.getErrorStream(); 
stdIn = process.getInputStream(); 

변수가 재 할당되며, Y의 모든 인스턴스가 동일한 스트림을 참조 할 것.

+1

이것은 내가 직업에 의한 프로그래머가 아닌 이유를 설명하는 이것과 같은 단순한 오류입니다. 그러나 내가 위에서 말했듯이, 그는 먼저 이것을 대답했고 나는 그것을 깨닫지 못했습니다. 고맙습니다! –