2013-12-21 2 views
0

Похоже, что его довольно легко решить, но я недостаточно разбираюсь в использовании пакетных файлов для его решения самостоятельно. У меня есть Java-метод, который создает построитель процессов и запускает пакетный файл в процессе. Командный файл использует команду xcopy для копирования одного каталога в другой. Пока командный файл работает в фоновом режиме, окно Java, содержащее JTextArea, отображает вывод процесса (копируемые каталоги). Окно также имеет кнопку остановки, которая вызывает следующий код:Процесс пакетного файла, убитый в Java: xcopy не закрывается

stopped = true; 
backgroundTask.cancel(true); 
backgroundTask.done(); 

сделано метод выглядит следующим образом:

protected void done() { 
    statusLabel.setText((this.getState()).toString() + " " + status); 
    stopButton.setEnabled(false); 
    bar.setIndeterminate(false); 
    if(stopped == false){ 
     JOptionPane.showMessageDialog(null, "Backup Complete."); 
     closeWindow(); 
    } 
    else if (stopped == true){ 
     JOptionPane.showMessageDialog(null, "Backup Cancelled."); 
     closeWindow(); 
    } 
} 

Теперь для того, чтобы запустить пакетный файл в фоновом режиме, я использую следующий код (первоначально предложил мне trashgod):

protected Integer doInBackground() throws IOException { 
    try { 
     ProcessBuilder pb = new ProcessBuilder(commands); 
     pb.redirectErrorStream(true); 
     Process p = pb.start(); 
     String s; 
     BufferedReader stdout = new BufferedReader(
     new InputStreamReader(p.getInputStream())); 
     while ((s = stdout.readLine()) != null && !isCancelled()) { 
      publish(s); 
     } 
     if (!isCancelled()) { 
      status = p.waitFor(); 
     } 
     p.getInputStream().close(); 
     p.getOutputStream().close(); 
     p.getErrorStream().close(); 
     p.destroy(); 
     closeWindow(); 
    } catch (IOException | InterruptedException ex) { 
     ex.printStackTrace(System.err); 
    }    
    return status; 
} 

проблема, которую я имею это: Когда я запускаю программу, файлы копировать только штраф, если я не нажать кнопку остановки на иллюминатор На переднем плане. Когда я, что он говорит мне резервная копия была отменена (как предполагается), но оставляет три дополнительных процессов, выполняющихся, которые видны в диспетчере задач: enter image description here

enter image description here

enter image description here

Я предполагаю, что первый - «расширенная утилита копирования» является виновником. Поскольку он не закрывается, он оставляет другие два процесса cmd. Однако это довольно необразованная догадка.

Когда я запускаю программу, а затем останавливаю ее, проводник Windows становится очень нестабильным, иногда зависающим, а иногда и сбоем. Навигация по папкам - особенно с копиями каталогов - очень медленный, и кажется, что каталоги продолжают копироваться даже после того, как процесс (предположительно) остановлен. Я считаю, что это потому, что эти линии никогда не достигались:

p.getInputStream().close(); 
p.getOutputStream().close(); 
p.getErrorStream().close(); 
p.destroy(); 

так что процесс никогда не был убит. Я все еще работаю над тем, чтобы полностью уничтожить процессы, когда нажата кнопка остановки, но если у кого-то есть мысли, я бы с радостью их услышал!

EDIT

Я решил опубликовать весь класс, поскольку только дает определенные методы, вероятно, не дает достаточной информации. Вот весь класс:

package diana; 

import java.awt.BorderLayout; 
import java.awt.EventQueue; 
import java.awt.Toolkit; 
import java.awt.event.*; 
import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStreamReader; 
import java.util.List; 

import javax.swing.*; 

@SuppressWarnings("serial") 
public class Progress extends JFrame { 
    public String[] commands; 
    private final JLabel statusLabel = new JLabel("Status: ", JLabel.CENTER); 
    private final JTextArea textArea = new JTextArea(20, 20); 
    private JButton stopButton = new JButton("Stop"); 
    private JProgressBar bar = new JProgressBar(); 
    private BackgroundTask backgroundTask; 
    private ProcessBuilder pb; 
    private Process p; 
    public boolean stopped = false; 

    public void setCommands(String[] cmds) { 
     commands = cmds; 
    } 
    private final ActionListener buttonActions = new ActionListener() { 
     @Override 
     public void actionPerformed(ActionEvent ae) { 
      JButton source = (JButton) ae.getSource(); 
      if (source == stopButton) { 
       stopped = true; 
       backgroundTask.cancel(true); 
       backgroundTask.done(); 
      } else { 
       backgroundTask = new BackgroundTask(commands); 
      } 
     } 
    }; 

    private void displayGUI(String[] cmds) { 
     commands = cmds; 
     JFrame frame = new JFrame("Backup Progress"); 
     frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 
     JPanel panel = new JPanel(); 
     panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 
     panel.setLayout(new BorderLayout(5, 5)); 
     JScrollPane sp = new JScrollPane(); 
     sp.setBorder(BorderFactory.createTitledBorder("Output: ")); 
     sp.setViewportView(textArea); 
     textArea.setText(null); 
     stopButton.setEnabled(true); 
     backgroundTask = new BackgroundTask(commands); 
     backgroundTask.execute(); 
     bar.setIndeterminate(true); 
     stopButton.addActionListener(buttonActions); 
     JPanel buttonPanel = new JPanel(); 
     buttonPanel.add(stopButton); 
     buttonPanel.add(bar); 
     panel.add(statusLabel, BorderLayout.PAGE_START); 
     panel.add(sp, BorderLayout.CENTER); 
     panel.add(buttonPanel, BorderLayout.PAGE_END); 
     frame.setContentPane(panel); 
     frame.pack(); 
     frame.setLocationByPlatform(true); 
     frame.setVisible(true); 
    } 

    /* Close current window */ 
    public void closeWindow() throws IOException { 
     p.getInputStream().close(); 
     p.getOutputStream().close(); 
     p.getErrorStream().close(); 
     p.destroy(); 
     WindowEvent close = new WindowEvent(this, WindowEvent.WINDOW_CLOSING); 
     Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(close); 
     System.exit(0); 
    } 

    private class BackgroundTask extends SwingWorker<Integer, String> { 
     private int status; 
     public String[] commands; 
     public BackgroundTask(String[] cmds) { 
      commands = cmds; 
      statusLabel.setText((this.getState()).toString()); 
     } 

     @Override 
     protected Integer doInBackground() throws IOException { 
      try { 
       pb = new ProcessBuilder(commands); 
       pb.redirectErrorStream(true); 
       p = pb.start(); 
       String s; 
       BufferedReader stdout = new BufferedReader(
        new InputStreamReader(p.getInputStream())); 
       while ((s = stdout.readLine()) != null && !isCancelled()) { 
        publish(s); 
       } 
       if (!isCancelled()) { 
        status = p.waitFor(); 
       } 
       closeWindow(); 
      } catch (IOException | InterruptedException ex) { 
       ex.printStackTrace(System.err); 
      } 
      return status; 
     } 

     @Override 
     protected void process(List<String> messages) { 
      statusLabel.setText((this.getState()).toString()); 
      for (String message : messages) { 
       textArea.append(message + "\n"); 
      } 
     } 

     @Override 
     protected void done() { 
      statusLabel.setText((this.getState()).toString() + " " + status); 
      stopButton.setEnabled(false); 
      bar.setIndeterminate(false); 
      if (stopped == false) { 
       JOptionPane.showMessageDialog(null, "Backup Complete."); 
       try { 
        closeWindow(); 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } 
      } else if (stopped == true) { 
       JOptionPane.showMessageDialog(null, "Backup Cancelled."); 
       try { 
        closeWindow(); 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } 
      } 
     } 
    } 

    public void run(String[] cmds) { 
     commands = cmds; 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       new Progress().displayGUI(commands); 
      } 
     }); 
    } 
} 

Еще раз, я не могу взять кредит на этот код, как это в основном обеспечивается SO члена trashgod. Кроме того, пожалуйста, извините за любые заявления, оставленные там для отладки, которые я, возможно, забыл удалить.

ответ

1

Одна из моих мыслей заключается в том, что нецелесообразно ожидать, что сможет остановить этот общий процесс. Вы запускаете оболочку и передаете ей команду, а затем останавливаете исходную программу - что она должна делать, чтобы остановить копирование? Если вы пользовались командной оболочкой, вы можете ввести control-C, но я не знаю, есть ли эквивалент программирования, доступный для Java, который будет делать то же самое.

+0

Это хороший момент. Я надеялся, что существует способ фактически полностью уничтожить весь процесс с Java. Думаю, мы это увидим. – DerStrom8

1

Есть несколько вещей, которые выделяются

BufferedReader#readLine является метод блокировки и может не отвечать на прерванном флаг текущего потока (и разблокирование)

Вы окружили всю логику в одном try-catch, что означает, что если выбрано InterruptedException, вы пропустите всю часть кода, который вы пытаетесь использовать, чтобы избавиться от процесса.

Что-то вроде этого может быть немного лучше. InputStream#read еще ваша ахиллесова пята, а потому, что я теперь проверить isCancelled, прежде чем пытаться что-то читать, это менее вероятно, вызовет большой вопрос

InputStream is = null; 
Process p = null; 
try { 
    ProcessBuilder pb = new ProcessBuilder(commands); 
    pb.redirectErrorStream(true); 
    p = pb.start(); 

    StringBuilder sb = new StringBuilder(128); 
    is = p.getInputStream(); 
    int in = -1; 
    while (!isCancelled() && (in = is.read()) != -1) { 
     sb.append((char)in)); 
     if (((char)in) == '\n') { 
      publish(sb.toString()); 
      sb.delete(0, sb.length()); 
     } 
    } 
    if (!isCancelled()) { 
     status = p.waitFor(); 
    } else { 
     p.destroy(); 
    } 
} catch (IOException ex) { 
    ex.printStackTrace(System.err); 
} catch (InterruptedException ex) { 
    ex.printStackTrace(System.err); 
    try { 
     p.destroy(); 
    } catch (Exception exp) { 
    } 
} finally { 
    try { 
     is.close(); 
    } catch (Exception exp) { 
    } 
    // Make sure you are re-syncing this to the EDT first... 
    closeWindow(); 
} 

(пь Введенные непосредственно, поэтому я не проверял)

+0

Хм, единственная проблема, с которой я сталкиваюсь, заключается в том, что она частично заменяет собой несколько других методов, которые требуются для остальной части программы. Возможно, мне придется опубликовать весь класс, чтобы найти способ заставить его работать без кардинального изменения структуры всего этого. Я продолжу рассматривать этот пример, но чтобы проверить, могу ли я заставить его работать для моих целей. Весьма признателен. – DerStrom8

+0

Итак, самое значительное изменение - это чтение выходного символа char, а не использование BufferedReader ... У меня были проблемы с этим в прошлом, поэтому не делайте этого. Остальное просто ставит больше контроля над обработкой исключений;) – MadProgrammer

+0

Хорошо, я думаю, что понимаю сейчас. Но у меня возникает новый вопрос: длится ли чтение выходного символа по характеру? Эта программа работает очень медленно, как есть, и я предпочел бы, чтобы она не шла медленнее;) – DerStrom8

1

Вы не сказали, выполняет ли ваш командный файл какие-либо действия в дополнение к вызову xcopy. Если нет, вы можете захотеть использовать Java для копирования файла вместо запуска внешнего процесса. Гораздо проще прервать ваш собственный код, чем остановить внешний процесс:

static void copyTree(final Path source, final Path destination) 
throws IOException { 
    if (Files.isDirectory(source)) { 
     Files.walkFileTree(source, new SimpleFileVisitor<Path>() 
     { 
      @Override 
      public FileVisitResult preVisitDirectory(Path dir, 
             BasicFileAttributes attributes) 
      throws IOException { 
       if (Thread.interrupted()) { 
        throw new InterruptedIOException(); 
       } 

       Path destinationDir = 
        destination.resolve(source.relativize(dir)); 
       Files.createDirectories(destinationDir); 

       BasicFileAttributeView view = 
        Files.getFileAttributeView(destinationDir, 
         BasicFileAttributeView.class); 
       view.setTimes(
        attributes.lastModifiedTime(), 
        attributes.lastAccessTime(), 
        attributes.creationTime()); 

       return FileVisitResult.CONTINUE; 
      } 

      @Override 
      public FileVisitResult visitFile(Path file, 
             BasicFileAttributes attributes) 
      throws IOException { 
       if (Thread.interrupted()) { 
        throw new InterruptedIOException(); 
       } 

       Files.copy(file, 
        destination.resolve(source.relativize(file)), 
        StandardCopyOption.COPY_ATTRIBUTES, 
        LinkOption.NOFOLLOW_LINKS); 

       return FileVisitResult.CONTINUE; 
      } 
     }); 
    } else { 
     Files.copy(source, destination, 
      StandardCopyOption.COPY_ATTRIBUTES, 
      LinkOption.NOFOLLOW_LINKS); 
    } 
} 
+0

Забавно, что вы должны упомянуть об этом, поскольку это то, что я рассматривал на некоторое время. Тем не менее, я поместил много работы в пакетные файлы, и они используются для создания каталогов, а также для копирования файлов на них, поэтому я думаю, что для этой версии программы я хотел бы придерживаться их на данный момент. Я уже включил мысль в новую версию, которая в ближайшем будущем не будет полагаться на пакетные файлы (как только я получу эту версию). Моя первая версия использовала ТОЛЬКО командные файлы, и на самом деле это версия 2. – DerStrom8