Friday, March 29, 2013

Այլ ծրագրի աշխատեցնելը Java ծրագրից

Ինչ որ մի անգամ ինձ պետք էր անել այնպես որ օգտագործողի գրաֆիկական ինտերֆեյսում (GUI) անընդհատ ռեժիմով արտածվեն մեկ այլ պրոցեսում աշխատեցրած ծրագրի արտածման ստանդարտ հոսքի (stdout) և սխալների ստանդարտ հոսքի (stderr) հաղորդագրությունները։ Այս գրառման մեջ ես պարզեցրած օրինակով ցույց կտամ, թե ինչպես դա արեցի։

1. NetBeans աշխատանքային միջավայրում ստեղծենք ScriptRunner անունով պրոյեկտը և նրա լռելությամբ ստեղծված scriptrunner փաթեթում ավելացնենք JFrame դասից ժառանգված MainWindow անունով պատուհանը։

2. Պատուհանի վրա ավելացնենք արտաքին սկրիպտի ճանապարհը նշելու, այն աշխատեցնելու և արդյունքներն արտածելու հետևյալ երեք օբյեկտները։
  • txtScript : JTextField - սկրիպտի կամ հրամանի ճանապարհը նշելու դաշտ,
  • btnRun : JButton - աշխատանքը սկսելու կոճակ,
  • areaResults : JTextArea - արդյունքներն արտածելու տիրույթ:
Դասավորենք օբյեկտներն այնպես, որ նրանք ունենան ստորև բերված նկարի տեսքը։

3. "Run" կոճակի կոնտեքստային մենյուից ընտրենք Events->Action->actionPerformed գործողությունը, որի հետևանքով պատուհանի կոդի հատվածում կստեղծվի btnRunActionPerformed անունով մեթոդի դատարկ մարմինը։ Այս մեթոդը կանչվելու է, երբ սեղմենք "Run" կոճակը։
private void btnRunActionPerformed(java.awt.event.ActionEvent evt) { 
  //
}


Արտաքին ծրագիրն աշխատեցնում եմ Process դասի օգնությամբ, որը հնարավորություն է տալիս իր getInputStream մեթոդից ստեղծել կարդալու հոսք և կարդալ արտաքին ծրագրի արտածման ու սխալների ստանդարտ հոսքերի արտածումները։ Օրինա, եթե պետք է Java ծրագրից աշխատեցնել ls ծրագիրը, ապա պետք է նախ ProcessBuilder դասի կոնստրուկտորին տալ աշխատեցվելիք ծրագրի անունը, ապա կանչելով նրա start մեթոդը, ստեղծել նոր պրոցես։
Process ps = (new ProcessBuilder("ls")).start();
InputStream is = ps.getInputStream();
Պրոցեսի տված հաղորդագրությունները սինխրոն կարդալու համար ստեղծել եմ մի thread, որը կարդում է պրոցեսի արտածման հոսքի հետ կապված ընթերցման հոսքի պարունակությունը և արտածում է areaResults դաշտում։

Ահա btnRunActionPerformed մեթոդի իրականացումը։
private void btnRunActionPerformed(java.awt.event.ActionEvent evt) {                                       
  try {
    // պրոցեսի ստեղծում
    ProcessBuilder pb = new ProcessBuilder(txtScript.getText());
    // սխալների հոսքն ուղղել stdout-ի վրա
    pb.redirectErrorStream(true); 
    // պրոցեսի գործարկում
    Process procFlow = pb.start();
    
    // վերցնել պրոցեսի արտածման հոսքը
    InputStream inp = procFlow.getInputStream();
    final BufferedReader reader = 
            new BufferedReader(new InputStreamReader(inp));

    // thread-ի ստեղծում
    Thread outputThread = new Thread() {
      @Override
      public void run() 
      {
        try {
          // ապահովել reader-ը,
          synchronized(reader) {
            String text = "";
            // քանի դեռ կարդալու բան կա, կարդալ
            while(null != (text = reader.readLine())) {
              // տեքստը կցել areaResults-ի պարունակության պոչից
              areaResults.append(text + "\n");
              // կուրսորը տեղափոխել տեքստի վերջը
              areaResults.setCaretPosition(areaResults.getText().length());
            }
          }
        }
        catch (Exception e) {}
      }
    };
    
    // կարդացող thread-ի գործարկում
    outputThread.start();
  }
  catch(IOException ex) {
    JOptionPane.showMessageDialog(this, ex.getMessage(), 
               "Error", JOptionPane.ERROR_MESSAGE);
  } 
}

* * *
Ծրագիրը փորձարկելու համար ստեղծել եմ Bash-ի մի սխրիպտ, որը երկու վայրկյան ընդմիջումներով հաղորդագրություններ է արտածում արտածման ստանդարտ հոսքի վրա։
#!/bin/bash

for m in 1 2 3 4 5 6 7 8 9 10 11 12
do
  echo Message number: $m
  sleep 2
done
Ծրագիրն աշխատեցնելուց հետո txtScript դաշտում ներմուում եմ սկրիպտի ճանապարհը ու սեղմում եմ Run կոճակը։ Արդյունքն այն է, ինչ ես սպասում էի։


No comments:

Post a Comment