2010-05-12 2 views
2

임베디드 H2 데이터베이스를 사용하는 스윙 응용 프로그램에서 작업하고 있습니다. 데이터베이스를 앱과 번들로 묶고 싶지 않기 때문에 (db가 자주 업데이트되고 새로운 복사본으로 시작하기를 원합니다.) db의 압축 된 복사본을 다운로드하는 솔루션을 구현했습니다. 응용 프로그램이 처음 시작되고 추출됩니다. 추출 프로세스가 느릴 수 있으므로 추출 프로세스의 진행 상황을 표시하기 위해 ProgressMonitorInputStream을 추가했습니다. 불행히도 추출이 시작되면 진행 대화 상자가 나타나지만 전혀 업데이트되지 않습니다. 이벤트가 이벤트 발송 스레드로 전달되는 것처럼 보입니다. 이 방법은 이벤트 처리 쓰레드 (SwingUtilities.isEventDispatchThread()가 true를 돌려 준다) 때문에 UI 구성 요소를 업데이트해야합니다에 호출됩니다Swing에서 ProgressMonitorInputStream을 사용하는 동안 UI가 업데이트되지 않아 압축 파일 압축을 모니터하지 않습니다.

public static String extractDbFromArchive(String pathToArchive) { 
    if (SwingUtilities.isEventDispatchThread()) { 
     System.out.println("Invoking on event dispatch thread"); 
    } 

    // Get the current path, where the database will be extracted 
    String currentPath = System.getProperty("user.home") + File.separator + ".spellbook" + File.separator; 
    LOGGER.info("Current path: " + currentPath); 

    try { 
     //Open the archive 
     FileInputStream archiveFileStream = new FileInputStream(pathToArchive); 
     // Read two bytes from the stream before it used by CBZip2InputStream 

     for (int i = 0; i < 2; i++) { 
      archiveFileStream.read(); 
     } 

     // Open the gzip file and open the output file 
     CBZip2InputStream bz2 = new CBZip2InputStream(new ProgressMonitorInputStream(
           null, 
           "Decompressing " + pathToArchive, 
           archiveFileStream)); 
     FileOutputStream out = new FileOutputStream(ARCHIVED_DB_NAME); 

     LOGGER.info("Decompressing the tar file..."); 
     // Transfer bytes from the compressed file to the output file 
     byte[] buffer = new byte[1024]; 
     int len; 
     while ((len = bz2.read(buffer)) > 0) { 
      out.write(buffer, 0, len); 
     } 

     // Close the file and stream 
     bz2.close(); 
     out.close(); 
    } catch (FileNotFoundException e) { 
     e.printStackTrace(); 
    } catch (IOException ex) { 
     ex.printStackTrace(); 
    } 

    try { 
     TarInputStream tarInputStream = null; 
     TarEntry tarEntry; 
     tarInputStream = new TarInputStream(new ProgressMonitorInputStream(
           null, 
           "Extracting " + ARCHIVED_DB_NAME, 
           new FileInputStream(ARCHIVED_DB_NAME))); 

     tarEntry = tarInputStream.getNextEntry(); 

     byte[] buf1 = new byte[1024]; 

     LOGGER.info("Extracting tar file"); 

     while (tarEntry != null) { 
      //For each entry to be extracted 
      String entryName = currentPath + tarEntry.getName(); 
      entryName = entryName.replace('/', File.separatorChar); 
      entryName = entryName.replace('\\', File.separatorChar); 

      LOGGER.info("Extracting entry: " + entryName); 
      FileOutputStream fileOutputStream; 
      File newFile = new File(entryName); 
      if (tarEntry.isDirectory()) { 
       if (!newFile.mkdirs()) { 
        break; 
       } 
       tarEntry = tarInputStream.getNextEntry(); 
       continue; 
      } 

      fileOutputStream = new FileOutputStream(entryName); 
      int n; 
      while ((n = tarInputStream.read(buf1, 0, 1024)) > -1) { 
       fileOutputStream.write(buf1, 0, n); 
      } 

      fileOutputStream.close(); 
      tarEntry = tarInputStream.getNextEntry(); 

     } 
     tarInputStream.close(); 
    } catch (Exception e) { 
    } 

    currentPath += "db" + File.separator + DB_FILE_NAME; 

    if (!currentPath.isEmpty()) { 
     LOGGER.info("DB placed in : " + currentPath); 
    } 

    return currentPath; 
} 

: 여기에 방법이다. 프로그램의 초기화를 진행하기 전에 추출을 기다릴 필요가 있기 때문에 이것을 SwingWorker로 구현하지 않았습니다. 이 메소드는 응용 프로그램의 기본 JFrame이 표시되기 전에 호출됩니다. 나는 SwingWorker + 속성을 기반으로 한 솔루션을 리스너가 변경하지는 않을 것입니다. ProgressMonitorInputStream이 정확히 필요한 부분이라고 생각합니다.하지만 뭔가 올바르게하고있는 것은 아닙니다. Sun JDK 1.6.18을 사용하고 있습니다. 어떤 도움이라도 대단히 감사하겠습니다.

답변

4

EDT에서 추출 프로세스를 실행하는 동안 진행 모니터에서도 GUI에 대한 모든 업데이트가 차단됩니다. 이것은 정확히 SwingWorker이 도움이되는 상황입니다.

사실상, EDT를 사용하여 그림을 차단하면 데이터베이스 추출을 수행 할 수 있습니다. 모든 GUI 업데이트 요청 (예 : repaint()에 대한 호출)은 대기 상태가되지만 EDT가 이미 사용 중이므로 실제로 다시 업데이트되지 않습니다.

다른 스레드로 처리를 오프로드하면 작동하는 유일한 방법이 있습니다. SwingWorker을 사용하면 더 쉽게 할 수 있습니다.

+0

여기서 내가 말하는 것을 생각해 보았습니다.하지만 스윙 엔지니어는 입력 스트림이 일반적으로 루프에서 읽혀지는 것을 염두에 두었을 것이라고 생각합니다. 루프가 정말 EDT를 완전히 호깅하면이 ProgressMonitorInputStream이 항상 쓸모 없다는 것을 의미합니다. –

+1

나는 당신이 말하는 것을 이해합니다. EDT가 아닌 다른 스레드에서이 객체를 사용하면 Swing의 규칙을 위반하는 기본 'JProgressBar'에서'setValue' (다른 것들 중에서도)를 호출 할 것이기 때문에 유스 케이스를 실제로 볼 수 없습니다. 그러나 EDT를 차단하면 GUI가 업데이트되지 않는다는 사실은 변하지 않습니다. EDT에서 I/O를해서는 안됩니다. 또한 PMIS에서 봤던 모든 예는 스트림에서 실제 읽기를 수행하기 위해 주 스레드 또는 스윙 작업자를 사용하고 있습니다. – Ash

+0

내 문제는 내 init 시퀀스로 다시 시작하기 전에 SwingWorker 스레드가 완료 될 때까지 기다려야한다는 것입니다. EDT에서 조인을 요청하면 상황이 나 빠질 것입니다. 모니터를 사용할 때 대부분 사용자가 누르는 버튼이 있습니다. 이것은 UI 요소 몇 개를 사용할 수 없도록하는 작업자 스레드를 시작하므로 사용자는 스레드가 끝날 때까지 기다릴 수 있습니다. 그러나이 추출은 UI 요소가 표시되지 않는 시점에서 발생하므로 이처럼 재생할 수 없으며 불필요한 대화 상자를 도입하지 않아 상황을 완화 할 수 없습니다. –

2

@Ash는 바로 SwingWorker입니다. done()을 사용하여 준비가되었을 때 다른 GUI 기능을 사용할 수 있습니다. done() 메서드는 "doInBackground() 메서드가 완료된 후 이벤트 발송 스레드에서 실행됩니다."

+0

나는 그것을 알고있다. 나는 어제부터 스윙 개발을하지 않는다 :-) 나는 이전에 언급 한 내용에서 이미 스윙 워커가 EDT를 다시 시작하기 전에 끝내기를 기다릴 필요가있다. 관리자는 다운로드 한 db를 사용합니다.그리고 비활성화 할 수있는 기능이 없기 때문에 GUI 기능을 활성화 할 수 있습니다. 내가 보는 방법 - 현재 설정에서 동기화 할 수있는 방법이 없다면 진행 막대에 대한 초기화 로직을 다시 작성해야합니다 ... –

+0

사용자가 초기화가 완료 될 때까지 기다려야하므로 별도의 "스플래시"창. 완료되면 해당 창을 GUI로 바꾸십시오. – trashgod

+0

나는 이것을 매우 처음부터한다. 방금 앱 초기화의 일부가 아니기 때문에 스플래시 밖에서 볼 수있는 콘텐츠가 필요했지만 실제로는 서곡이었습니다. 상관 없어요. 저는 사전 초기화 코드를 약간 리팩토링하여이 문제를 해결했습니다. 도움을 주셔서 감사합니다. +1. –

관련 문제