7

मैं जावा एप्लिकेशन लिख रहा हूं जिसे अपाचे कॉमन्स एक्ज़िक लाइब्रेरी का उपयोग करके बाहरी कमांड लाइन एप्लिकेशन का उपयोग करने की आवश्यकता है। जिस एप्लिकेशन को चलाने की ज़रूरत है वह काफी लंबा लोडिंग समय है, इसलिए हर बार एक नई प्रक्रिया बनाने के बजाय एक उदाहरण जीवित रखना बेहतर होगा। जिस तरह से आवेदन काम बहुत आसान है। एक बार शुरू होने के बाद, यह कुछ नए इनपुट की प्रतीक्षा करता है और आउटपुट के रूप में कुछ डेटा उत्पन्न करता है, जिनमें से दोनों एप्लिकेशन के मानक I/O का उपयोग करते हैं।अपाचे कॉमन्स एक्ज़ेक का उपयोग करके कमांड में एकाधिक इनपुट प्रदान करने और आउटपुट निकालने में समस्या

तो विचार कमांडलाइन को निष्पादित करना होगा, और फिर पंपस्ट्रीमहैंडलर को तीन अलग-अलग धाराओं (आउटपुट, त्रुटि और इनपुट) के साथ उपयोग करने और एप्लिकेशन के साथ बातचीत करने के लिए उन धाराओं का उपयोग करने के लिए होगा। अब तक, मेरे पास यह परिदृश्य मूल परिदृश्यों में है जहां मेरे पास एक इनपुट है, एक आउटपुट और एप्लिकेशन बंद हो जाता है। लेकिन जैसे ही मैं दूसरा लेनदेन करने की कोशिश कर रहा हूं, कुछ गलत हो जाता है।

मेरी पार्टिशन मौजूद बनाई होने के बाद, मैं अपने निर्वाहक बना सकते हैं और इसलिए तरह इसे लॉन्च:

this.executor = new DefaultExecutor(); 

PipedOutputStream stdout = new PipedOutputStream(); 
PipedOutputStream stderr = new PipedOutputStream(); 
PipedInputStream stdin = new PipedInputStream(); 
PumpStreamHandler streamHandler = new PumpStreamHandler(stdout, stderr, stdin); 

this.executor.setStreamHandler(streamHandler); 

this.processOutput = new BufferedInputStream(new PipedInputStream(stdout)); 
this.processError = new BufferedInputStream(new PipedInputStream(stderr)); 
this.processInput = new BufferedOutputStream(new PipedOutputStream(stdin)); 

this.resultHandler = new DefaultExecuteResultHandler(); 
this.executor.execute(cmdLine, resultHandler); 

मैं तो तीन अलग अलग सूत्र, एक एक अन्य धारा से निपटने में से प्रत्येक के शुरू करने के लिए आगे बढ़ें। मेरे पास तीन सिंक्रोनसक्यूयू भी हैं जो इनपुट और आउटपुट को नियंत्रित करते हैं (इनपुट इनपुट के लिए इनपुट के रूप में उपयोग किया जाता है, एक आउटपुट को सूचित करने के लिए एक नया कमान लॉन्च किया गया है और आउटपुट के लिए एक)। उदाहरण के लिए, इनपुट धारा धागा इस तरह दिखता है:

while (!killThreads) { 
    String input = inputQueue.take(); 

    processInput.write(input.getBytes()); 
    processInput.flush(); 

    IOQueue.put(input); 
} 

अगर मैं जबकि पाश को हटा दें और सिर्फ एक बार इस पर अमल, सब कुछ पूरी तरह से काम करने लगता है। जाहिर है, अगर मैं इसे फिर से निष्पादित करने का प्रयास करता हूं, तो पंपस्ट्रीमहैंडलर अपवाद फेंकता है क्योंकि इसे दो अलग-अलग धागे से एक्सेस किया गया है।

यहां मुद्दा यह है कि यह प्रसंस्करण की तरह लगता है कि थ्रेड समाप्त होने तक इनपुट वास्तव में फ़्लश नहीं किया जाता है। डीबग होने पर, थ्रेड समाप्त होने के बाद कमांड लाइन एप्लिकेशन केवल वास्तव में अपना इनपुट प्राप्त करता है, लेकिन अगर लूप रखा जाता है तो उसे कभी नहीं मिलता है। मैंने प्रक्रिया को प्राप्त करने के लिए कई अलग-अलग चीजों की कोशिश की है इनपुट फ्लश करने के लिए लेकिन कुछ भी काम नहीं करता है।

क्या किसी ने पहले कुछ भी ऐसा करने का प्रयास किया है? क्या मुझसे कुछ छूट रहा है? किसी भी तरह की सहायता का स्वागत किया जाएगा!

+0

आपको अपनी समस्या पर सबसे अनुभवी 'आंखों' के लिए अपनी पोस्ट में एक जावा टैग जोड़ना चाहिए। सौभाग्य। – shellter

उत्तर

8

मैं इस काम को करने के लिए एक रास्ता तय करना समाप्त कर दिया। कॉमन्स एक्ज़ेक लाइब्रेरी के कोड के अंदर देखकर, मैंने देखा कि पंपस्ट्रीम हैंडलर द्वारा उपयोग किए गए स्ट्रीमपंपर्स प्रत्येक बार इन्हें कुछ नया डेटा आने वाले फ्लश नहीं करते थे। यही कारण है कि कोड ने काम किया जब मैंने इसे केवल एक बार निष्पादित किया, क्योंकि यह स्वचालित रूप से फ़्लश हो गया और स्ट्रीम बंद कर दिया। इसलिए मैंने कक्षाएं बनाईं जिन्हें मैंने ऑटोफ्लशिंग स्ट्रीमपम्पर और ऑटोफ्लशिंग पम्पस्ट्रीम हैंडलर कहा। बाद में सामान्य पंपस्ट्रीम हैंडलर जैसा ही है लेकिन सामान्य लोगों की बजाय ऑटोफ्लशिंग स्ट्रीमपंपर्स का उपयोग करता है। ऑटोफ्लशिंग स्ट्रीमपम्पर एक मानक स्ट्रीमपम्पर के समान होता है, लेकिन हर बार जब यह कुछ लिखता है तो इसकी आउटपुट स्ट्रीम फ्लश करता है।

मैंने इसे बहुत व्यापक रूप से परीक्षण किया है और ऐसा लगता है कि यह अच्छी तरह से काम करता है। उन सभी के लिए धन्यवाद जिन्होंने इसे समझने की कोशिश की है!

+2

एक भाई की मदद करें? मुझे एक ही समस्या है, क्या आप मुझे "ठोस" कर सकते हैं और आपके द्वारा लिखे गए कोड को पोस्ट कर सकते हैं (ऑटोफ्लशिंग स्ट्रीमपम्पर और ऑटोफ्लशिंग पंपस्ट्रीम हैंडलर) यहां या एक गिस्ट या कुछ? उस पहिया को फिर से आविष्कार करने में कोई समझ नहीं है ... आपकी पोस्ट के लिए धन्यवाद! – GroovyCakes

+2

इससे बहुत मदद मिली, लेकिन @GroovyCakes के रूप में, एक गिस्ट ने और अधिक मदद की होगी - तो यहां एक है। ध्यान दें कि मैं इसका उपयोग कर रहा हूं, ओपी का उपयोग नहीं किया गया है। https://gist.github.com/4653381 –

+0

क्या कोई उदाहरण मौजूद है जो ऑटोफ्लशिंग स्ट्रीमपम्पर और ऑटोफ्लशिंग पंपस्ट्रीम हैंडलर का उपयोग करता है? – Johan

1

मेरे उद्देश्यों के लिए, यह पता चला है कि मुझे केवल "ExecuteStreamHandler" को ओवरराइड करने की आवश्यकता है।

class SendReceiveStreamHandler implements ExecuteStreamHandler 

आप GitHub here पर एक सार के रूप में पूरी कक्षा को देख सकते हैं: यहाँ मेरी समाधान है, जो एक StringBuilder में stderr कैप्चर करता है, और आप stdin के लिए चीजों को स्ट्रीम और stdout से बातें प्राप्त करने के लिए अनुमति देता है।

+0

आपके कोड में, रिसीवर, ट्रांसफर कॉम्प्लेटेवेंट और डेटा रीसेवइवेंट आपके आयात में नहीं है। इन वर्गों में कौन सा पैकेज शामिल है? – Yeti

0

प्रक्रिया के STDIN में एक से अधिक आदेश लिखने के लिए सक्षम होने के लिए, मैं एक नया

import java.io.BufferedWriter; 
import java.io.File; 
import java.io.IOException; 
import java.io.OutputStreamWriter; 
import java.util.Map; 

import org.apache.commons.exec.CommandLine; 
import org.apache.commons.exec.DefaultExecutor; 
import org.apache.commons.lang3.CharEncoding; 

public class ProcessExecutor extends DefaultExecutor { 

    private BufferedWriter processStdinput; 

    @Override 
    protected Process launch(CommandLine command, Map env, File dir) throws IOException { 
     Process process = super.launch(command, env, dir); 
     processStdinput = new BufferedWriter(new OutputStreamWriter(process.getOutputStream(), CharEncoding.UTF_8)); 
     return process; 
    } 

    /** 
    * Write a line in the stdin of the process. 
    * 
    * @param line 
    *   does not need to contain the carriage return character. 
    * @throws IOException 
    *    in case of error when writing. 
    * @throws IllegalStateException 
    *    if the process was not launched. 
    */ 
    public void writeLine(String line) throws IOException { 
     if (processStdinput != null) { 
      processStdinput.write(line); 
      processStdinput.newLine(); 
      processStdinput.flush(); 
     } else { 
      throw new IllegalStateException(); 
     } 
    } 

} 

इस नए निर्वाहक का उपयोग करने के लिए, मैं PumpStreamHandler भीतर पाइप धारा रखें कि से बचने के लिए बनाने के लिए है पंपस्ट्रीम हैंडलर द्वारा बंद होने के लिए एसटीडीआईएन।

ProcessExecutor executor = new ProcessExecutor(); 
executor.setExitValue(0); 
executor.setWorkingDirectory(workingDirectory); 
executor.setWatchdog(new ExecuteWatchdog(ExecuteWatchdog.INFINITE_TIMEOUT)); 
executor.setStreamHandler(new PumpStreamHandler(outHanlder, outHanlder, new PipedInputStream(new PipedOutputStream()))); 
executor.execute(commandLine, this); 

आप निष्पादक writeLine() विधि का उपयोग कर सकते हैं या अपना स्वयं का बना सकते हैं।