OSDN Git Service

git-svn-id: svn+ssh://svn.sourceforge.jp/svnroot/deployer@8 bdfc86a3-8c30-0410-b963...
authoruguu <uguu@bdfc86a3-8c30-0410-b963-dc9a622ac1c7>
Sat, 26 May 2007 19:18:21 +0000 (19:18 +0000)
committeruguu <uguu@bdfc86a3-8c30-0410-b963-dc9a622ac1c7>
Sat, 26 May 2007 19:18:21 +0000 (19:18 +0000)
deployer/trunk/src/main/java/jp/sourceforge/deployer/Deployer.java
deployer/trunk/src/main/java/jp/sourceforge/deployer/DeployerClassLoader.java
deployer/trunk/src/main/java/jp/sourceforge/deployer/FileMonitor.java
deployer/trunk/src/main/java/jp/sourceforge/deployer/FileMonitorFailException.java [new file with mode: 0644]

index 2cdd1d1..e761942 100644 (file)
@@ -121,10 +121,10 @@ public class Deployer {
      * アーカイブ・ファイルを監視し、配置、配置解除を行い、リスナーにイベントを通知します。\r
      * </p>\r
      * \r
-     * @throws IOException\r
-     *             入出力エラーが発生した場合。\r
+     * @throws FileMonitorFailException\r
+     *             ファイルの監視に失敗した場合。\r
      */\r
-    public void monitor() throws IOException {\r
+    public void monitor() throws FileMonitorFailException {\r
         this.fileMonitor.monitor();\r
     }\r
 \r
index a907d5b..2ebdd54 100644 (file)
@@ -34,8 +34,14 @@ public class DeployerClassLoader extends ClassLoader {
      */\r
     private static final int                      TRY_DISPOSE_INTERVAL = 1000;\r
 \r
+    /**\r
+     * 内部クラスローダの弱参照。参照の有効性を調べることで、クラスローダの破棄が成功したかどうかを判定するために使用しています。\r
+     */\r
     private WeakReference<InternalURLClassLoader> wrCL;\r
 \r
+    /**\r
+     * 内部クラスローダ。クラスローダの実際の処理は、この内部クラスローダが行っています。\r
+     */\r
     private InternalURLClassLoader                cl;\r
 \r
     /**\r
@@ -55,47 +61,14 @@ public class DeployerClassLoader extends ClassLoader {
      *             指定されたディレクトリの形式が不正である場合。\r
      */\r
     public DeployerClassLoader(File[] fileDirectories, File[] jarDirectories, ClassLoader parent) throws MalformedURLException {\r
-        super(parent);\r
-        this.initialize(fileDirectories, jarDirectories);\r
-    }\r
-\r
-    /**\r
-     * <p>\r
-     * {@link DeployerClassLoader}インスタンスを初期化します。\r
-     * </p>\r
-     * \r
-     * @param fileDirectories\r
-     *            クラス・ファイル、リソースなどを検索するディレクトリ。複数指定することが出来ます(例えば、classes/、conf/のように)。<br>\r
-     *            nullの場合は無視します。配列中のnullも無視します。\r
-     * @param jarDirectories\r
-     *            jarファイルを検索するディレクトリ。このディレクトリ以下に存在するjarファイルを読み込みます。<br>\r
-     *            nullの場合は無視します。配列中のnullも無視します。\r
-     * @throws MalformedURLException\r
-     *             指定されたディレクトリの形式が不正である場合。\r
-     */\r
-    public DeployerClassLoader(File[] fileDirectories, File[] jarDirectories) throws MalformedURLException {\r
         super();\r
-        this.initialize(fileDirectories, jarDirectories);\r
-    }\r
 \r
-    /**\r
-     * <p>\r
-     * クラスローダーを初期化します。\r
-     * </p>\r
-     * \r
-     * @param fileDirectories\r
-     *            クラス・ファイル、リソースなどを検索するディレクトリ。複数指定することが出来ます(例えば、classes/,\r
-     *            conf/のように)。<br>\r
-     *            nullの場合は無視します。配列中のnullも無視します。\r
-     * @param jarDirectories\r
-     *            jarファイルを検索するディレクトリ。このディレクトリ以下に存在するjarファイルを読み込みます。<br>\r
-     *            nullの場合は無視します。配列中のnullも無視します。\r
-     * @throws MalformedURLException\r
-     *             指定されたディレクトリの形式が不正である場合。\r
-     */\r
-    private void initialize(File[] fileDirectories, File[] jarDirectories) throws MalformedURLException {\r
+        /*\r
+         * クラスローダの検索パスとして追加するURLを収集します。\r
+         */\r
         List<URL> urlList = new ArrayList<URL>();\r
 \r
+        // クラス・ファイル、リソースなどを検索するディレクトリのパスを追加します。\r
         if (fileDirectories != null) {\r
             for (File f : fileDirectories) {\r
                 if (f != null) {\r
@@ -104,6 +77,7 @@ public class DeployerClassLoader extends ClassLoader {
             }\r
         }\r
 \r
+        // jarファイルのパスを追加します。\r
         if (jarDirectories != null) {\r
             for (File f : jarDirectories) {\r
                 if (f != null) {\r
@@ -112,12 +86,38 @@ public class DeployerClassLoader extends ClassLoader {
             }\r
         }\r
 \r
-        this.cl = new InternalURLClassLoader(urlList.toArray(new URL[0]));\r
+        /*\r
+         * 内部クラスローダを初期化します。\r
+         */\r
+        if (parent == null) {\r
+            this.cl = new InternalURLClassLoader(urlList.toArray(new URL[0]));\r
+        } else {\r
+            this.cl = new InternalURLClassLoader(urlList.toArray(new URL[0]), parent);\r
+        }\r
+        // 内部クラスローダの破棄を確認するため、弱参照を保持します。\r
         this.wrCL = new WeakReference<InternalURLClassLoader>(this.cl);\r
     }\r
 \r
     /**\r
      * <p>\r
+     * {@link DeployerClassLoader}インスタンスを初期化します。\r
+     * </p>\r
+     * \r
+     * @param fileDirectories\r
+     *            クラス・ファイル、リソースなどを検索するディレクトリ。複数指定することが出来ます(例えば、classes/、conf/のように)。<br>\r
+     *            nullの場合は無視します。配列中のnullも無視します。\r
+     * @param jarDirectories\r
+     *            jarファイルを検索するディレクトリ。このディレクトリ以下に存在するjarファイルを読み込みます。<br>\r
+     *            nullの場合は無視します。配列中のnullも無視します。\r
+     * @throws MalformedURLException\r
+     *             指定されたディレクトリの形式が不正である場合。\r
+     */\r
+    public DeployerClassLoader(File[] fileDirectories, File[] jarDirectories) throws MalformedURLException {\r
+        this(fileDirectories, jarDirectories, null);\r
+    }\r
+\r
+    /**\r
+     * <p>\r
      * 指定されたディレクトリ以下のjarファイルを検索し、そのURLをリストとして返します。\r
      * </p>\r
      * \r
@@ -141,7 +141,7 @@ public class DeployerClassLoader extends ClassLoader {
         }\r
 \r
         for (File f : files) {\r
-            if (f.isFile() && f.getName().endsWith(".jar")) {\r
+            if (f.isFile() && f.getName().toLowerCase().endsWith(".jar")) {\r
                 urlList.add(f.toURL());\r
             } else if (f.isDirectory()) {\r
                 urlList.addAll(this.getJarFileList(f));\r
@@ -161,10 +161,7 @@ public class DeployerClassLoader extends ClassLoader {
      * <blockquote> cl.dispose(0); </blockquote>\r
      */\r
     public void dispose() {\r
-        this.cl = null;\r
-        while (this.wrCL.get() != null) {\r
-            System.gc();\r
-        }\r
+        this.dispose(0);\r
     }\r
 \r
     /**\r
@@ -181,12 +178,19 @@ public class DeployerClassLoader extends ClassLoader {
     public void dispose(int timeout) {\r
         this.cl = null;\r
 \r
+        // タイムアウト時間が経過するまで、破棄を試みます。\r
         long t = System.currentTimeMillis();\r
         while (((System.currentTimeMillis() - t) < timeout) || timeout <= 0) {\r
+            // 内部クラスローダを破棄するため、強制的にガベージコレクションを試みます。\r
             System.gc();\r
+\r
+            // 内部クラスローダへの弱参照がnullになっている場合、破棄されたことを表します。\r
+            // 従って、処理を抜けます。\r
             if (this.wrCL.get() == null) {\r
                 break;\r
             }\r
+\r
+            // 処理の間隔をあけるため、単位時間の間、処理を中断します。\r
             try {\r
                 Thread.sleep(TRY_DISPOSE_INTERVAL);\r
             } catch (InterruptedException e) {\r
@@ -194,6 +198,8 @@ public class DeployerClassLoader extends ClassLoader {
             }\r
         }\r
 \r
+        // 弱参照がnullではない場合、タイムアウトしたことを表します。\r
+        // 従って、例外をスローします。\r
         if (this.wrCL.get() != null) {\r
             throw new ClassLoaderUnloadFailException();\r
         }\r
@@ -263,6 +269,13 @@ public class DeployerClassLoader extends ClassLoader {
         return this.cl.loadClass(name, resolve);\r
     }\r
 \r
+    /**\r
+     * <p>\r
+     * 内部クラスローダ。必要なメソッドのアクセス修飾子をpublicに変更するために定義します。\r
+     * </p>\r
+     * \r
+     * @author uguu\r
+     */\r
     private class InternalURLClassLoader extends URLClassLoader {\r
 \r
         public InternalURLClassLoader(URL[] urls) {\r
index 230ead6..ba130c4 100644 (file)
@@ -2,8 +2,12 @@
 package jp.sourceforge.deployer;\r
 \r
 import java.io.File;\r
+import java.io.FileInputStream;\r
 import java.io.IOException;\r
+import java.security.MessageDigest;\r
+import java.security.NoSuchAlgorithmException;\r
 import java.util.ArrayList;\r
+import java.util.Arrays;\r
 import java.util.HashMap;\r
 import java.util.List;\r
 import java.util.Map;\r
@@ -29,6 +33,112 @@ public class FileMonitor {
 \r
     private Map<String, MonitoringFileInfo> monitoringFileMap = new HashMap<String, MonitoringFileInfo>();\r
 \r
+    private boolean                         checkLength       = true;\r
+\r
+    private boolean                         checkLastModified = true;\r
+\r
+    private boolean                         checkContent      = false;\r
+\r
+    private String                          hashAlgorithm     = "MD5";\r
+\r
+    /**\r
+     * <p>\r
+     * ファイル・サイズを監視するかどうかを取得します。初期値はtrueです。\r
+     * </p>\r
+     * \r
+     * @return ファイル・サイズを監視する場合はtrue、監視しない場合はfalse。\r
+     */\r
+    public boolean isCheckLength() {\r
+        return this.checkLength;\r
+    }\r
+\r
+    /**\r
+     * <p>\r
+     * ファイル・サイズを監視するかどうかを設定します。\r
+     * </p>\r
+     * \r
+     * @param checkLength\r
+     *            ファイル・サイズを監視する場合はtrue、監視しない場合はfalse。\r
+     */\r
+    public void setCheckLength(boolean checkLength) {\r
+        this.checkLength = checkLength;\r
+    }\r
+\r
+    /**\r
+     * <p>\r
+     * 最終更新日時を監視するかどうかを取得します。初期値はtrueです。\r
+     * </p>\r
+     * \r
+     * @return 最終更新日時を監視する場合はtrue、監視しない場合はfalse。\r
+     */\r
+    public boolean isCheckLastModified() {\r
+        return this.checkLastModified;\r
+    }\r
+\r
+    /**\r
+     * <p>\r
+     * 最終更新日時を監視するかどうかを設定します。\r
+     * </p>\r
+     * \r
+     * @param checkLastModified\r
+     *            最終更新日時を監視する場合はtrue、監視しない場合はfalse。\r
+     */\r
+    public void setCheckLastModified(boolean checkLastModified) {\r
+        this.checkLastModified = checkLastModified;\r
+    }\r
+\r
+    /**\r
+     * <p>\r
+     * ファイルの内容(ハッシュ値)を監視するかどうかを取得します。初期値はfalseです。\r
+     * </p>\r
+     * \r
+     * @return ファイルの内容を監視する場合はtrue、監視しない場合はfalse。\r
+     */\r
+    public boolean isCheckContent() {\r
+        return this.checkContent;\r
+    }\r
+\r
+    /**\r
+     * <p>\r
+     * ファイルの内容(ハッシュ値)を監視するかどうかを設定します。\r
+     * </p>\r
+     * <p>\r
+     * ファイルの内容を監視すると、監視のパフォーマンスが低下する場合があります。\r
+     * </p>\r
+     * \r
+     * @param checkContent\r
+     *            ファイルの内容を監視する場合はtrue、監視しない場合はfalse。\r
+     */\r
+    public void setCheckContent(boolean checkContent) {\r
+        this.checkContent = checkContent;\r
+    }\r
+\r
+    /**\r
+     * <p>\r
+     * ファイルの内容を比較するときに使用するハッシュ・アルゴリズムを取得します。初期値は"MD5"です。\r
+     * </p>\r
+     * \r
+     * @return ハッシュ・アルゴリズム。\r
+     */\r
+    public String getHashAlgorithm() {\r
+        return this.hashAlgorithm;\r
+    }\r
+\r
+    /**\r
+     * <p>\r
+     * ファイルの内容を比較するときに使用するハッシュ・アルゴリズムを設定します。\r
+     * </p>\r
+     * <p>\r
+     * ハッシュ値の計算は{@link MessageDigest}クラスを使用します。従って、このメソッドで指定するハッシュ・アルゴリズムは{@link MessageDigest}クラスがサポートするアルゴリズムである必要があります。{@link MessageDigest}クラスがサポートしないハッシュ・アルゴリズムを設定した場合、{@link #monitor()}メソッドの呼び出しで{@link FileMonitorFailException}例外がスローされる可能性があります。\r
+     * </p>\r
+     * \r
+     * @param hashAlgorithm\r
+     *            ハッシュ・アルゴリズム。\r
+     */\r
+    public void setHashAlgorithm(String hashAlgorithm) {\r
+        this.hashAlgorithm = hashAlgorithm;\r
+    }\r
+\r
     /**\r
      * <p>\r
      * 指定したディレクトリの指定したパターンに合致するファイルを監視する{@link FileMonitor}インスタンスを初期化します。\r
@@ -121,21 +231,32 @@ public class FileMonitor {
      * ファイルの作成、更新、削除を監視し、リスナーにイベントを通知します。\r
      * </p>\r
      * \r
-     * @throws IOException\r
-     *             入出力エラーが発生した場合。\r
+     * @throws FileMonitorFailException\r
+     *             ファイルの監視に失敗した場合。\r
      */\r
-    public void monitor() throws IOException {\r
-        this.checkCreateOrUpdate(this.baseDirectory);\r
+    public void monitor() throws FileMonitorFailException {\r
+        try {\r
+            this.checkCreateOrUpdate(this.baseDirectory);\r
+        } catch (NoSuchAlgorithmException e) {\r
+            throw new FileMonitorFailException(e);\r
+        } catch (IOException e) {\r
+            throw new FileMonitorFailException(e);\r
+        }\r
         this.checkDelete();\r
         this.checkClear();\r
     }\r
 \r
-    private void checkCreateOrUpdate(File dir) throws IOException {\r
+    private void checkCreateOrUpdate(File dir) throws IOException, NoSuchAlgorithmException {\r
+        if (dir == null) {\r
+            return;\r
+        }\r
+\r
         File[] files = dir.listFiles();\r
         if (files == null) {\r
-            throw new IOException("ディレクトリ\"" + dir.getAbsolutePath() + "\"に格納されているファイルの一覧の取得に失敗しました。");\r
+            return;\r
         }\r
-        for (File f : dir.listFiles()) {\r
+\r
+        for (File f : files) {\r
             if (f.isFile()) {\r
                 this.checkFileCreateOrUpdate(f);\r
             } else {\r
@@ -144,16 +265,14 @@ public class FileMonitor {
         }\r
     }\r
 \r
-    private void checkFileCreateOrUpdate(File file) {\r
+    private void checkFileCreateOrUpdate(File file) throws NoSuchAlgorithmException, IOException {\r
         if (this.filePattern.matcher(file.getAbsolutePath()).matches()) {\r
             synchronized (this.monitoringFileMap) {\r
                 if (!this.monitoringFileMap.containsKey(file.getAbsolutePath())) {\r
                     // ファイルがマップに存在しない場合、新規のモニター対象。\r
                     MonitoringFileInfo info = new MonitoringFileInfo();\r
                     info.path = file.getAbsolutePath();\r
-                    info.length = file.length();\r
-                    info.lastModified = file.lastModified();\r
-                    info.hashValue = ""; // TODO: ハッシュ値を算出し、格納する。\r
+                    this.setMonitoringFileInfo(file, info);\r
                     info.checked = true;\r
                     this.monitoringFileMap.put(file.getAbsolutePath(), info);\r
                     // イベントを通知する。\r
@@ -164,12 +283,8 @@ public class FileMonitor {
                 } else {\r
                     // ファイルがマップに存在する場合、変更のモニター対象。\r
                     MonitoringFileInfo info = this.monitoringFileMap.get(file.getAbsolutePath());\r
-                    if (info.length != file.length() || info.lastModified != file.lastModified()) { // TODO:\r
-                        // ハッシュ値を比較する。\r
-                        info.length = file.length();\r
-                        info.lastModified = file.lastModified();\r
-                        info.hashValue = ""; // TODO: ハッシュ値を算出し、格納する。\r
-                        info.checked = true;\r
+                    if (this.isModified(file, info)) {\r
+                        this.setMonitoringFileInfo(file, info);\r
                         // イベントを通知する。\r
                         FileMonitorListener[] listeners = this.getListeners();\r
                         for (FileMonitorListener l : listeners) {\r
@@ -182,6 +297,54 @@ public class FileMonitor {
         }\r
     }\r
 \r
+    private boolean isModified(File file, MonitoringFileInfo info) throws NoSuchAlgorithmException, IOException {\r
+        if (this.checkLength) {\r
+            if (file.length() != info.length) {\r
+                return true;\r
+            }\r
+        }\r
+        if (this.checkLastModified) {\r
+            if (file.lastModified() != info.lastModified) {\r
+                return true;\r
+            }\r
+        }\r
+        if (this.checkContent) {\r
+            if (!Arrays.equals(this.getHashData(file), info.hashData)) {\r
+                return true;\r
+            }\r
+        }\r
+        return false;\r
+    }\r
+\r
+    private void setMonitoringFileInfo(File file, MonitoringFileInfo info) throws NoSuchAlgorithmException, IOException {\r
+        if (this.checkLength) {\r
+            info.length = file.length();\r
+        }\r
+        if (this.checkLastModified) {\r
+            info.lastModified = file.lastModified();\r
+        }\r
+        if (this.checkContent) {\r
+            info.hashData = this.getHashData(file);\r
+        }\r
+    }\r
+\r
+    private byte[] getHashData(File file) throws IOException, NoSuchAlgorithmException {\r
+        MessageDigest md = MessageDigest.getInstance(this.hashAlgorithm);\r
+\r
+        FileInputStream fileIn = new FileInputStream(file);\r
+        try {\r
+            byte[] buf = new byte[1024];\r
+            int len;\r
+            while ((len = fileIn.read(buf)) != -1) {\r
+                md.update(buf, 0, len);\r
+            }\r
+        } finally {\r
+            fileIn.close();\r
+        }\r
+\r
+        return md.digest();\r
+    }\r
+\r
     private void checkDelete() {\r
         synchronized (this.monitoringFileMap) {\r
             List<String> removeFileList = new ArrayList<String>();\r
@@ -220,7 +383,7 @@ public class FileMonitor {
 \r
         long    lastModified;\r
 \r
-        String  hashValue;\r
+        byte[]  hashData;\r
 \r
         boolean checked = false;\r
 \r
diff --git a/deployer/trunk/src/main/java/jp/sourceforge/deployer/FileMonitorFailException.java b/deployer/trunk/src/main/java/jp/sourceforge/deployer/FileMonitorFailException.java
new file mode 100644 (file)
index 0000000..54515b0
--- /dev/null
@@ -0,0 +1,25 @@
+\r
+package jp.sourceforge.deployer;\r
+\r
+/**\r
+ * <p>\r
+ * ファイルの監視に失敗したことを表す例外です。\r
+ * </p>\r
+ * \r
+ * @author uguu\r
+ */\r
+public class FileMonitorFailException extends Exception {\r
+\r
+    /**\r
+     * <p>\r
+     * インスタンスを初期化します。\r
+     * </p>\r
+     * \r
+     * @param e\r
+     *            例外の原因。\r
+     */\r
+    public FileMonitorFailException(Exception e) {\r
+        super("ファイルの監視に失敗しました。", e);\r
+    }\r
+\r
+}\r