OSDN Git Service

git-svn-id: svn+ssh://svn.sourceforge.jp/svnroot/deployer@2 bdfc86a3-8c30-0410-b963...
authoruguu <uguu@bdfc86a3-8c30-0410-b963-dc9a622ac1c7>
Wed, 16 May 2007 03:13:01 +0000 (03:13 +0000)
committeruguu <uguu@bdfc86a3-8c30-0410-b963-dc9a622ac1c7>
Wed, 16 May 2007 03:13:01 +0000 (03:13 +0000)
deployer/trunk/.classpath [new file with mode: 0644]
deployer/trunk/.project [new file with mode: 0644]
deployer/trunk/doc/spec.txt [new file with mode: 0644]
deployer/trunk/src/main/java/jp/gr/java_conf/uguu/deployer/Deployer.java [new file with mode: 0644]
deployer/trunk/src/main/java/jp/gr/java_conf/uguu/deployer/DeployerClassLoader.java [new file with mode: 0644]
deployer/trunk/src/main/java/jp/gr/java_conf/uguu/deployer/DeployerClassLoaderListener.java [new file with mode: 0644]
deployer/trunk/src/main/java/jp/gr/java_conf/uguu/deployer/DeployerListener.java [new file with mode: 0644]
deployer/trunk/src/main/java/jp/gr/java_conf/uguu/deployer/FileMonitor.java [new file with mode: 0644]
deployer/trunk/src/main/java/jp/gr/java_conf/uguu/deployer/FileMonitorListener.java [new file with mode: 0644]
deployer/trunk/src/main/java/jp/gr/java_conf/uguu/deployer/Main.java [new file with mode: 0644]

diff --git a/deployer/trunk/.classpath b/deployer/trunk/.classpath
new file mode 100644 (file)
index 0000000..1e6d54b
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<classpath>\r
+       <classpathentry kind="src" path="src/main/java"/>\r
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>\r
+       <classpathentry kind="output" path="bin"/>\r
+</classpath>\r
diff --git a/deployer/trunk/.project b/deployer/trunk/.project
new file mode 100644 (file)
index 0000000..7d62ad1
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<projectDescription>\r
+       <name>deployer</name>\r
+       <comment></comment>\r
+       <projects>\r
+       </projects>\r
+       <buildSpec>\r
+               <buildCommand>\r
+                       <name>org.eclipse.jdt.core.javabuilder</name>\r
+                       <arguments>\r
+                       </arguments>\r
+               </buildCommand>\r
+       </buildSpec>\r
+       <natures>\r
+               <nature>org.eclipse.jdt.core.javanature</nature>\r
+       </natures>\r
+</projectDescription>\r
diff --git a/deployer/trunk/doc/spec.txt b/deployer/trunk/doc/spec.txt
new file mode 100644 (file)
index 0000000..bfa2df2
--- /dev/null
@@ -0,0 +1,43 @@
+    \8ed\97l\8f\91\r
+    ======\r
+\r
+\8aT\97v\r
+----\r
+\r
+\81@war\83t\83@\83C\83\8b\82Ì\82æ\82¤\82É\81A\83N\83\89\83\83t\83@\83C\83\8b\81Ajar\83t\83@\83C\83\8b\81A\83\8a\83\\81[\83X\82ð\8ai\94[\82µ\82½\83A\81[\83J\83C\83u\82Ì\83z\83b\83g\83f\83v\83\8d\83C\82ð\8ds\82¢\81A\83N\83\89\83X\83\8d\81[\83_\81[\82ð\8d\\92z\82·\82é\8b@\94\\82ð\8e\9d\82¿\82Ü\82·\81B\r
+\r
+\83A\81[\83J\83C\83u\82Ì\8d\\91¢\r
+----------------\r
+\r
+\81@\83A\81[\83J\83C\83u\82Í\88È\89º\82Ì\97v\91f\82Å\8d\\90¬\82³\82ê\82Ü\82·\81B\r
+\r
+- \83N\83\89\83\83t\83@\83C\83\8b\81B\r
+- jar \83t\83@\83C\83\8b\81B\r
+- \82»\82Ì\91¼\82Ì\83t\83@\83C\83\8b(\83\8a\83\\81[\83X)\81B\r
+\r
+\81@\83A\81[\83J\83C\83u\93à\82Ì\83f\83B\83\8c\83N\83g\83\8a\8d\\91¢\82Ídeployer.properties\90Ý\92è\83t\83@\83C\83\8b\82Å\8ew\92è\82Å\82«\82Ü\82·\81B\r
+\r
+\83f\83v\83\8d\83C\r
+--------\r
+\r
+\81@ArchiveClassLoader\82Í\81A\8ew\92è\82³\82ê\82½\83A\81[\83J\83C\83\83t\83@\83C\83\8b\82©\82ç\83N\83\89\83X\83\8d\81[\83_\81[\82ð\8d\\92z\82µ\82Ü\82·\81B\83A\81[\83J\83C\83\83t\83@\83C\83\8b\82Í\88ê\8e\9e\83f\83B\83\8c\83N\83g\83\8a(1)\82É\93W\8aJ\82³\82ê\81A\88È\8d~\82Í\93W\8aJ\82³\82ê\82½\83t\83@\83C\83\8b\82ð\8eQ\8fÆ\82µ\82Ü\82·(2)\81B\93Ç\82Ý\8d\9e\82Ý\8c³\82Ì\83A\81[\83J\83C\83\83t\83@\83C\83\8b\82Í\92è\8aú\93I\82É\8aÄ\8e\8b\82³\82ê\82Ä\82¢\82Ü\82·(3)\81B\83t\83@\83C\83\8b\82ª\91\8dÝ\82µ\82È\82­\82È\82Á\82½\8fê\8d\87\81A\83A\83\93\83f\83v\83\8d\83C\82µ\82Ü\82·\81B\83t\83@\83C\83\8b\82ª\95Ï\8dX\82³\82ê\82Ä\82¢\82½\8fê\8d\87(4)\81A\8dÄ\83f\83v\83\8d\83C\82³\82ê\82Ü\82·\81BArchiveClassLoader\82ÉArchiveClassLoaderListener\82ð\93o\98^\82·\82é\82±\82Æ\82Å\81A\83f\83v\83\8d\83C\81A\83A\83\93\83f\83v\83\8d\83C\8e\9e\82Ì\83C\83x\83\93\83g\82ð\8eó\82¯\8eæ\82é\82±\82Æ\82ª\82Å\82«\82Ü\82·\81B\r
+\81@ArchiveDirectoryMonitor\82Í\81A\8ew\92è\82³\82ê\82½\83f\83B\83\8c\83N\83g\83\8a\82ð\8aÄ\8e\8b\82µ\81A\83A\81[\83J\83C\83\83t\83@\83C\83\8b\82ª\94z\92u\82³\82ê\82½\8fê\8d\87\82ÉArchiveClassLoader\82ð\8d\\92z\82µ\82Ü\82·\81B\r
+\r
+(1) \88ê\8e\9e\83f\83B\83\8c\83N\83g\83\8a\82ÍArchiveClassLoader\8f\89\8aú\89»\8e\9e\82É\8ew\92è\82·\82é\82±\82Æ\82ª\82Å\82«\82Ü\82·\81B\82Ü\82½\81Adeployer.properties\82É\8ew\92è\82·\82é\82±\82Æ\82à\82Å\82«\82Ü\82·\81B\r
+(2) \83A\81[\83J\83C\83\83t\83@\83C\83\8b\82Í\81A\93W\8aJ\8e\9e\82¾\82¯\83t\83@\83C\83\8b \83\8d\83b\83N\82³\82ê\81A\93W\8aJ\82ª\8fI\97¹\82·\82é\82Æ\83\8d\83b\83N\82ª\89ð\8f\9c\82³\82ê\82Ü\82·\81B\r
+(3) \8aÄ\8e\8b\8aÔ\8au\82ÍArchiveClassLoader\8f\89\8aú\89»\8e\9e\82É\8ew\92è\82·\82é\82±\82Æ\82ª\82Å\82«\82Ü\82·\81B\82Ü\82½\81Adeployer.properties\82É\8ew\92è\82·\82é\82±\82Æ\82à\82Å\82«\82Ü\82·\81B\r
+(4) \95Ï\8dX\82Ì\8c\9f\92m\95û\96@\82Í\81A\83^\83C\83\80\83X\83^\83\93\83v\81A\83t\83@\83C\83\8b \83T\83C\83Y\81A\83t\83@\83C\83\8b\82Ì\93à\97e\81A\82Å\82·\81B\r
+\r
+\83N\83\89\83X\82Ì\8c\9f\8dõ\8f\87\8f\98\r
+----------------\r
+\r
+1) \90e\83N\83\89\83X\83\8d\81[\83_\81[\81B\r
+2) \83N\83\89\83\83t\83@\83C\83\8b\82Ì\83f\83B\83\8c\83N\83g\83\8a\81B\r
+3) jar\83t\83@\83C\83\8b\81B\r
+\r
+\83\8a\83\\81[\83X\82Ì\8c\9f\8dõ\8f\87\8f\98\r
+------------------\r
+\r
+1) \90e\83N\83\89\83X\83\8d\81[\83_\81[\81B\r
+2) \83A\81[\83J\83C\83u\93à\82Ì\83t\83@\83C\83\8b\81B\r
+3) jar\83t\83@\83C\83\8b\81B\r
diff --git a/deployer/trunk/src/main/java/jp/gr/java_conf/uguu/deployer/Deployer.java b/deployer/trunk/src/main/java/jp/gr/java_conf/uguu/deployer/Deployer.java
new file mode 100644 (file)
index 0000000..6b9e679
--- /dev/null
@@ -0,0 +1,244 @@
+\r
+package jp.gr.java_conf.uguu.deployer;\r
+\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.FileNotFoundException;\r
+import java.io.FileOutputStream;\r
+import java.io.IOException;\r
+import java.nio.channels.FileLock;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+import java.util.regex.Pattern;\r
+import java.util.zip.ZipEntry;\r
+import java.util.zip.ZipInputStream;\r
+\r
+public class Deployer {\r
+\r
+    private List<DeployerListener> listenerList = new ArrayList<DeployerListener>();\r
+\r
+    private File                   deployDirectory;\r
+\r
+    private Pattern                filePattern;\r
+\r
+    private File                   workDirectory;\r
+\r
+    private FileMonitor            fileMonitor;\r
+\r
+    public Deployer(File deployDirectory, Pattern filePattern, File workDirectory) {\r
+        if (deployDirectory == null) {\r
+            throw new IllegalArgumentException("deployDirectory is null.");\r
+        }\r
+        if (!deployDirectory.isDirectory()) {\r
+            throw new IllegalArgumentException("deployDirectory is not directory.");\r
+        }\r
+        if (filePattern == null) {\r
+            throw new IllegalArgumentException("filePattern is null.");\r
+        }\r
+        if (workDirectory == null) {\r
+            throw new IllegalArgumentException("workDirectory is null.");\r
+        }\r
+        if (!workDirectory.isDirectory()) {\r
+            throw new IllegalArgumentException("workDirectory is not directory.");\r
+        }\r
+\r
+        this.deployDirectory = deployDirectory;\r
+        this.filePattern = filePattern;\r
+        this.workDirectory = workDirectory;\r
+\r
+        // ディレクトリの監視を開始する。\r
+        this.fileMonitor = new FileMonitor(this.deployDirectory, this.filePattern);\r
+        this.fileMonitor.addListener(new FileMonitorListenerImpl());\r
+    }\r
+\r
+    public void addListener(DeployerListener listener) {\r
+        if (listener == null) {\r
+            throw new IllegalArgumentException("listener is null.");\r
+        }\r
+        synchronized (this.listenerList) {\r
+            this.listenerList.add(listener);\r
+        }\r
+    }\r
+\r
+    public void removeListener(DeployerListener listener) {\r
+        synchronized (this.listenerList) {\r
+            this.listenerList.remove(listener);\r
+        }\r
+    }\r
+\r
+    public void monitor() throws IOException {\r
+        this.fileMonitor.monitor();\r
+    }\r
+\r
+    private void deploy(File file) throws IOException {\r
+        // イベントを通知する。\r
+        synchronized (this.listenerList) {\r
+            for (DeployerListener l : this.listenerList) {\r
+                l.deployStart(this, file);\r
+            }\r
+        }\r
+        // デプロイ先のディレクトリを削除する。\r
+        File destDir = this.getDestDirectory(file, this.deployDirectory, this.workDirectory);\r
+        if (destDir.exists()) {\r
+            this.delete(destDir);\r
+            this.deleteWithParent(destDir.getParentFile());\r
+        }\r
+        // デプロイ先のディレクトリを作成する。\r
+        if (!destDir.mkdirs()) {\r
+            throw new IOException("ディレクトリ\"" + destDir.getAbsolutePath() + "\"の作成に失敗しました。");\r
+        }\r
+        // デプロイ先に展開する。\r
+        this.decompress(file, destDir);\r
+        // イベントを通知する。\r
+        synchronized (this.listenerList) {\r
+            for (DeployerListener l : this.listenerList) {\r
+                l.deployEnd(this, file, destDir);\r
+            }\r
+        }\r
+    }\r
+\r
+    private void undeploy(File file) throws IOException {\r
+        File destDir = this.getDestDirectory(file, this.deployDirectory, this.workDirectory);\r
+        // イベントを通知する。\r
+        synchronized (this.listenerList) {\r
+            for (DeployerListener l : this.listenerList) {\r
+                l.undeployStart(this, file, destDir);\r
+            }\r
+        }\r
+        // デプロイ先のディレクトリを削除する。\r
+        this.delete(destDir);\r
+        this.deleteWithParent(destDir.getParentFile());\r
+        // イベントを通知する。\r
+        synchronized (this.listenerList) {\r
+            for (DeployerListener l : this.listenerList) {\r
+                l.undeployEnd(this, file);\r
+            }\r
+        }\r
+    }\r
+\r
+    private void decompress(File file, File destDir) throws IOException {\r
+        FileInputStream fileIn = null;\r
+        while (fileIn == null) {\r
+            try {\r
+                fileIn = new FileInputStream(file);\r
+            } catch (FileNotFoundException e) {\r
+                fileIn = null;\r
+                try {\r
+                    Thread.sleep(1000);\r
+                } catch (InterruptedException e1) {\r
+                    e1.printStackTrace();\r
+                }\r
+            }\r
+        }\r
+        try {\r
+            ZipInputStream zipIn = new ZipInputStream(fileIn);\r
+            try {\r
+                FileLock lock = fileIn.getChannel().lock(0, Long.MAX_VALUE, true);\r
+                try {\r
+                    ZipEntry zipEntry;\r
+                    while ((zipEntry = zipIn.getNextEntry()) != null) {\r
+                        if (!zipEntry.isDirectory()) {\r
+                            File f = new File(destDir, zipEntry.getName().replace('/', File.separatorChar));\r
+                            if (!f.createNewFile()) {\r
+                                throw new IOException("ファイル\"" + f.getAbsolutePath() + "\"の作成に失敗しました。");\r
+                            }\r
+                            // ファイルを展開します。\r
+                            FileOutputStream fileOut = new FileOutputStream(f);\r
+                            try {\r
+                                byte[] buf = new byte[1024];\r
+                                int len;\r
+                                while ((len = zipIn.read(buf)) != -1) {\r
+                                    fileOut.write(buf, 0, len);\r
+                                }\r
+                            } finally {\r
+                                fileOut.close();\r
+                            }\r
+                            // イベントを通知します。\r
+                            synchronized (this.listenerList) {\r
+                                for (DeployerListener l : this.listenerList) {\r
+                                    l.deployFile(this, file, destDir, f);\r
+                                }\r
+                            }\r
+                        } else {\r
+                            File dir = new File(destDir, zipEntry.getName().replace('/', File.separatorChar));\r
+                            if (!dir.mkdirs()) {\r
+                                throw new IOException("ディレクトリ\"" + dir.getAbsolutePath() + "\"の作成に失敗しました。");\r
+                            }\r
+                        }\r
+                    }\r
+                } finally {\r
+                    lock.release();\r
+                }\r
+            } finally {\r
+                zipIn.close();\r
+            }\r
+        } finally {\r
+            fileIn.close();\r
+        }\r
+    }\r
+\r
+    private void delete(File dir) throws IOException {\r
+        for (File f : dir.listFiles()) {\r
+            if (!f.isDirectory()) {\r
+                if (!f.delete()) {\r
+                    throw new IOException("ファイル\"" + f.getAbsolutePath() + "\"の削除に失敗しました。");\r
+                }\r
+            } else {\r
+                this.delete(f);\r
+            }\r
+        }\r
+        if (!dir.delete()) {\r
+            throw new IOException("ディレクトリ\"" + dir.getAbsolutePath() + "\"の削除に失敗しました。");\r
+        }\r
+    }\r
+\r
+    private void deleteWithParent(File dir) throws IOException {\r
+        if (this.workDirectory.getAbsolutePath().equals(dir.getAbsolutePath())) {\r
+            return;\r
+        }\r
+        if (dir.listFiles().length > 0) {\r
+            return;\r
+        }\r
+        File parentDir = dir.getParentFile();\r
+        if (!dir.delete()) {\r
+            throw new IOException("ディレクトリ\"" + dir.getAbsolutePath() + "\"の削除に失敗しました。");\r
+        }\r
+        this.deleteWithParent(parentDir);\r
+    }\r
+\r
+    private File getDestDirectory(File archiveFile, File deployDirectory, File workDirectory) {\r
+        String name = archiveFile.getAbsolutePath().substring(deployDirectory.getAbsolutePath().length());\r
+        File dir = new File(workDirectory, name);\r
+        return dir;\r
+    }\r
+\r
+    private class FileMonitorListenerImpl implements FileMonitorListener {\r
+\r
+        public void create(FileMonitor monitor, File file) {\r
+            try {\r
+                Deployer.this.deploy(file);\r
+            } catch (Exception e) {\r
+                e.printStackTrace();\r
+            }\r
+        }\r
+\r
+        public void delete(FileMonitor monitor, File file) {\r
+            try {\r
+                Deployer.this.undeploy(file);\r
+            } catch (Exception e) {\r
+                e.printStackTrace();\r
+            }\r
+        }\r
+\r
+        public void update(FileMonitor monitor, File file) {\r
+            try {\r
+                Deployer.this.undeploy(file);\r
+                Deployer.this.deploy(file);\r
+            } catch (Exception e) {\r
+                e.printStackTrace();\r
+            }\r
+        }\r
+\r
+    }\r
+\r
+}\r
diff --git a/deployer/trunk/src/main/java/jp/gr/java_conf/uguu/deployer/DeployerClassLoader.java b/deployer/trunk/src/main/java/jp/gr/java_conf/uguu/deployer/DeployerClassLoader.java
new file mode 100644 (file)
index 0000000..2028915
--- /dev/null
@@ -0,0 +1,217 @@
+\r
+package jp.gr.java_conf.uguu.deployer;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.net.MalformedURLException;\r
+import java.net.URL;\r
+import java.util.ArrayList;\r
+import java.util.Enumeration;\r
+import java.util.List;\r
+import java.util.Vector;\r
+import java.util.jar.JarEntry;\r
+import java.util.jar.JarFile;\r
+\r
+public class DeployerClassLoader extends ClassLoader {\r
+\r
+    private List<DeployerClassLoaderListener> listenerList = new ArrayList<DeployerClassLoaderListener>();\r
+\r
+    private File                              deployDirectory;\r
+\r
+    private String                            classDirectory;\r
+\r
+    private String                            jarDirectory;\r
+\r
+    public DeployerClassLoader(File deployDirectory, String classDirectory, String jarDirectory, ClassLoader parent) throws IOException {\r
+        super(parent);\r
+        this.initialize(deployDirectory, classDirectory, jarDirectory);\r
+    }\r
+\r
+    public DeployerClassLoader(File deployDirectory, String classDirectory, String jarDirectory) throws IOException {\r
+        super();\r
+        this.initialize(deployDirectory, classDirectory, jarDirectory);\r
+    }\r
+\r
+    private void initialize(File deployDirectory, String classDirectory, String jarDirectory) throws IOException {\r
+        if (deployDirectory == null) {\r
+            throw new IllegalArgumentException("deployDirectory is null.");\r
+        }\r
+        if (!deployDirectory.isDirectory()) {\r
+            throw new IllegalArgumentException("deployDirectory is not directory.");\r
+        }\r
+        if (classDirectory == null) {\r
+            throw new IllegalArgumentException("classDirectory is null.");\r
+        }\r
+        if (jarDirectory == null) {\r
+            throw new IllegalArgumentException("jarDirectory is null.");\r
+        }\r
+\r
+        this.deployDirectory = deployDirectory;\r
+        this.classDirectory = classDirectory;\r
+        this.jarDirectory = jarDirectory;\r
+    }\r
+\r
+    public void addListener(DeployerClassLoaderListener listener) {\r
+        if (listener == null) {\r
+            throw new IllegalArgumentException("listener is null.");\r
+        }\r
+        synchronized (this.listenerList) {\r
+            this.listenerList.add(listener);\r
+        }\r
+    }\r
+\r
+    public void removeListener(DeployerClassLoaderListener listener) {\r
+        synchronized (this.listenerList) {\r
+            this.listenerList.remove(listener);\r
+        }\r
+    }\r
+\r
+    public void dispose() {\r
+        // TODO 定義済みクラスをうまく解放する方法を考える。\r
+    }\r
+\r
+    @Override\r
+    protected Class<?> findClass(String name) throws ClassNotFoundException {\r
+//        String resName = "/" + name.replace('.', '/') + ".class";\r
+        String resName = name.replace('.', '/') + ".class";\r
+        URL url = this.findResource(resName);\r
+        if (url == null) {\r
+            throw new ClassNotFoundException("\"" + name + "\"クラスが見つかりませんでした。");\r
+        }\r
+\r
+        try {\r
+            InputStream in = url.openStream();\r
+            try {\r
+                // classファイルを読み込む。\r
+                ByteArrayOutputStream byteOut = new ByteArrayOutputStream();\r
+                try {\r
+                    byte[] buf = new byte[1024];\r
+                    int len;\r
+                    while ((len = in.read(buf)) != -1) {\r
+                        byteOut.write(buf, 0, len);\r
+                    }\r
+                } finally {\r
+                    byteOut.close();\r
+                }\r
+                // クラスを定義する。\r
+                byte[] classData = byteOut.toByteArray();\r
+                Class<?> clazz = this.defineClass(name, classData, 0, classData.length);\r
+                // イベントを通知する。\r
+                synchronized (this.listenerList) {\r
+                    for (DeployerClassLoaderListener l : this.listenerList) {\r
+                        l.findClass(clazz, url);\r
+                    }\r
+                }\r
+                return clazz;\r
+            } finally {\r
+                in.close();\r
+            }\r
+        } catch (IOException e) {\r
+            throw new ClassNotFoundException("\"" + name + "\"クラスの読み込みに失敗しました。", e);\r
+        }\r
+    }\r
+\r
+    @Override\r
+    protected URL findResource(String name) {\r
+        URL url;\r
+        // クラス ディレクトリから探す。\r
+        try {\r
+            url = this.findResourceForClassDirectory(new File(this.deployDirectory, this.classDirectory), name);\r
+        } catch (MalformedURLException e) {\r
+            throw new RuntimeException(e);\r
+        }\r
+        if (url != null) {\r
+            return url;\r
+        }\r
+        // jarディレクトリから探す。\r
+        List<File> jarFileList = this.getJarFileList(new File(this.deployDirectory, this.jarDirectory));\r
+        for (File jarFile : jarFileList) {\r
+            try {\r
+                url = this.findResourceForJarFile(jarFile, name);\r
+            } catch (IOException e) {\r
+                throw new RuntimeException(e);\r
+            }\r
+            if (url != null) {\r
+                return url;\r
+            }\r
+        }\r
+        return null;\r
+    }\r
+\r
+    @Override\r
+    protected Enumeration<URL> findResources(String name) throws IOException {\r
+        Vector<URL> urlVector = new Vector<URL>();\r
+        URL url;\r
+        // クラス ディレクトリから探す。\r
+        try {\r
+            url = this.findResourceForClassDirectory(new File(this.deployDirectory, this.classDirectory), name);\r
+        } catch (MalformedURLException e) {\r
+            throw new RuntimeException(e);\r
+        }\r
+        if (url != null) {\r
+            urlVector.add(url);\r
+        }\r
+        // jarディレクトリから探す。\r
+        List<File> jarFileList = this.getJarFileList(new File(this.deployDirectory, this.jarDirectory));\r
+        for (File jarFile : jarFileList) {\r
+            try {\r
+                url = this.findResourceForJarFile(jarFile, name);\r
+            } catch (IOException e) {\r
+                throw new RuntimeException(e);\r
+            }\r
+            if (url != null) {\r
+                urlVector.add(url);\r
+            }\r
+        }\r
+        return urlVector.elements();\r
+    }\r
+\r
+    private URL findResourceForClassDirectory(File classDir, String resourceName) throws MalformedURLException {\r
+        String resName = resourceName.replace('/', File.separatorChar);\r
+        File resFile = new File(classDir, resName);\r
+        if (resFile.exists()) {\r
+            URL url = new URL("file:/" + resFile.getAbsolutePath().replace(File.separatorChar, '/'));\r
+            return url;\r
+        } else {\r
+            return null;\r
+        }\r
+    }\r
+\r
+    private List<File> getJarFileList(File jarDir) {\r
+        List<File> jarFileList = new ArrayList<File>();\r
+\r
+        File[] files = jarDir.listFiles();\r
+        if (files == null) {\r
+            return new ArrayList<File>();\r
+        }\r
+        for (File file : files) {\r
+            if (file.isFile()) {\r
+                if (file.getName().endsWith(".jar")) {\r
+                    jarFileList.add(file);\r
+                }\r
+            } else {\r
+                jarFileList.addAll(this.getJarFileList(file));\r
+            }\r
+        }\r
+\r
+        return jarFileList;\r
+    }\r
+\r
+    private URL findResourceForJarFile(File jarFile, String resourceName) throws IOException {\r
+        JarFile jar = new JarFile(jarFile);\r
+        try {\r
+            JarEntry entry = jar.getJarEntry(resourceName);\r
+            if (entry != null) {\r
+                URL url = new URL("jar:file:/" + jarFile.getAbsolutePath().replace(File.separatorChar, '/') + "!/" + entry.getName());\r
+                return url;\r
+            } else {\r
+                return null;\r
+            }\r
+        } finally {\r
+            jar.close();\r
+        }\r
+    }\r
+\r
+}\r
diff --git a/deployer/trunk/src/main/java/jp/gr/java_conf/uguu/deployer/DeployerClassLoaderListener.java b/deployer/trunk/src/main/java/jp/gr/java_conf/uguu/deployer/DeployerClassLoaderListener.java
new file mode 100644 (file)
index 0000000..61468ed
--- /dev/null
@@ -0,0 +1,10 @@
+\r
+package jp.gr.java_conf.uguu.deployer;\r
+\r
+import java.net.URL;\r
+\r
+public interface DeployerClassLoaderListener {\r
+\r
+    void findClass(Class clazz, URL url);\r
+\r
+}\r
diff --git a/deployer/trunk/src/main/java/jp/gr/java_conf/uguu/deployer/DeployerListener.java b/deployer/trunk/src/main/java/jp/gr/java_conf/uguu/deployer/DeployerListener.java
new file mode 100644 (file)
index 0000000..8e4d207
--- /dev/null
@@ -0,0 +1,18 @@
+\r
+package jp.gr.java_conf.uguu.deployer;\r
+\r
+import java.io.File;\r
+\r
+public interface DeployerListener {\r
+\r
+    void deployStart(Deployer deployer, File file);\r
+\r
+    void deployFile(Deployer deployer, File file, File destDirectory, File deployFile);\r
+\r
+    void deployEnd(Deployer deployer, File file, File destDirectory);\r
+\r
+    void undeployStart(Deployer deployer, File file, File destDirectory);\r
+\r
+    void undeployEnd(Deployer deployer, File file);\r
+\r
+}\r
diff --git a/deployer/trunk/src/main/java/jp/gr/java_conf/uguu/deployer/FileMonitor.java b/deployer/trunk/src/main/java/jp/gr/java_conf/uguu/deployer/FileMonitor.java
new file mode 100644 (file)
index 0000000..b260704
--- /dev/null
@@ -0,0 +1,163 @@
+\r
+package jp.gr.java_conf.uguu.deployer;\r
+\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.regex.Pattern;\r
+\r
+public class FileMonitor {\r
+\r
+    private List<FileMonitorListener>       listenerList      = new ArrayList<FileMonitorListener>();\r
+\r
+    private File                            baseDirectory;\r
+\r
+    private Pattern                         filePattern;\r
+\r
+    private Map<String, MonitoringFileInfo> monitoringFileMap = new HashMap<String, MonitoringFileInfo>();\r
+\r
+    public FileMonitor(File baseDirectory, Pattern filePattern) {\r
+        if (baseDirectory == null) {\r
+            throw new IllegalArgumentException("baseDirectory is null.");\r
+        }\r
+        if (!baseDirectory.isDirectory()) {\r
+            throw new IllegalArgumentException("baseDirectory is not directory.");\r
+        }\r
+        if (filePattern == null) {\r
+            throw new IllegalArgumentException("filePattern is null.");\r
+        }\r
+        this.baseDirectory = baseDirectory;\r
+        this.filePattern = filePattern;\r
+    }\r
+\r
+    public FileMonitor(File baseDirectory) {\r
+        this(baseDirectory, Pattern.compile(".*"));\r
+    }\r
+\r
+    public void addListener(FileMonitorListener listener) {\r
+        if (listener == null) {\r
+            throw new IllegalArgumentException("listener is null.");\r
+        }\r
+        synchronized (this.listenerList) {\r
+            this.listenerList.add(listener);\r
+        }\r
+    }\r
+\r
+    public void removeListener(FileMonitorListener listener) {\r
+        synchronized (this.listenerList) {\r
+            this.listenerList.remove(listener);\r
+        }\r
+    }\r
+\r
+    private FileMonitorListener[] getListeners() {\r
+        synchronized (this.listenerList) {\r
+            return this.listenerList.toArray(new FileMonitorListener[0]);\r
+        }\r
+    }\r
+\r
+    public void monitor() throws IOException {\r
+        this.checkCreateOrUpdate(this.baseDirectory);\r
+        this.checkDelete();\r
+        this.checkClear();\r
+    }\r
+\r
+    private void checkCreateOrUpdate(File dir) throws IOException {\r
+        File[] files = dir.listFiles();\r
+        if (files == null) {\r
+            throw new IOException("ディレクトリ\"" + dir.getAbsolutePath() + "\"に格納されているファイルの一覧の取得に失敗しました。");\r
+        }\r
+        for (File f : dir.listFiles()) {\r
+            if (f.isFile()) {\r
+                this.checkFileCreateOrUpdate(f);\r
+            } else {\r
+                this.checkCreateOrUpdate(f);\r
+            }\r
+        }\r
+    }\r
+\r
+    private void checkFileCreateOrUpdate(File file) {\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
+                    info.checked = true;\r
+                    this.monitoringFileMap.put(file.getAbsolutePath(), info);\r
+                    // イベントを通知する。\r
+                    FileMonitorListener[] listeners = this.getListeners();\r
+                    for (FileMonitorListener l : listeners) {\r
+                        l.create(this, file);\r
+                    }\r
+                } 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
+                        // イベントを通知する。\r
+                        FileMonitorListener[] listeners = this.getListeners();\r
+                        for (FileMonitorListener l : listeners) {\r
+                            l.update(this, file);\r
+                        }\r
+                    }\r
+                    info.checked = true;\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    private void checkDelete() {\r
+        synchronized (this.monitoringFileMap) {\r
+            List<String> removeFileList = new ArrayList<String>();\r
+            for (MonitoringFileInfo info : this.monitoringFileMap.values()) {\r
+                if (!info.checked) {\r
+                    File f = new File(info.path);\r
+                    if (!f.exists()) {\r
+                        removeFileList.add(info.path);\r
+                        // イベントを通知する。\r
+                        FileMonitorListener[] listeners = this.getListeners();\r
+                        for (FileMonitorListener l : listeners) {\r
+                            l.delete(this, f);\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+            for (String path : removeFileList) {\r
+                this.monitoringFileMap.remove(path);\r
+            }\r
+        }\r
+    }\r
+\r
+    private void checkClear() {\r
+        synchronized (this.monitoringFileMap) {\r
+            for (MonitoringFileInfo info : this.monitoringFileMap.values()) {\r
+                info.checked = false;\r
+            }\r
+        }\r
+    }\r
+\r
+    private class MonitoringFileInfo {\r
+\r
+        String  path;\r
+\r
+        long    length;\r
+\r
+        long    lastModified;\r
+\r
+        String  hashValue;\r
+\r
+        boolean checked = false;\r
+\r
+    }\r
+\r
+}\r
diff --git a/deployer/trunk/src/main/java/jp/gr/java_conf/uguu/deployer/FileMonitorListener.java b/deployer/trunk/src/main/java/jp/gr/java_conf/uguu/deployer/FileMonitorListener.java
new file mode 100644 (file)
index 0000000..ca6028d
--- /dev/null
@@ -0,0 +1,14 @@
+\r
+package jp.gr.java_conf.uguu.deployer;\r
+\r
+import java.io.File;\r
+\r
+public interface FileMonitorListener {\r
+\r
+    void create(FileMonitor monitor, File file);\r
+\r
+    void update(FileMonitor monitor, File file);\r
+\r
+    void delete(FileMonitor monitor, File file);\r
+\r
+}\r
diff --git a/deployer/trunk/src/main/java/jp/gr/java_conf/uguu/deployer/Main.java b/deployer/trunk/src/main/java/jp/gr/java_conf/uguu/deployer/Main.java
new file mode 100644 (file)
index 0000000..26456e7
--- /dev/null
@@ -0,0 +1,87 @@
+\r
+package jp.gr.java_conf.uguu.deployer;\r
+\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.net.URL;\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+import java.util.regex.Pattern;\r
+\r
+public class Main implements DeployerListener, DeployerClassLoaderListener {\r
+\r
+    public static void main(String[] args) throws Exception {\r
+        new Main().execute();\r
+    }\r
+\r
+    private Map<String, DeployerClassLoader> classLoaderMap = new HashMap<String, DeployerClassLoader>();\r
+\r
+    public void execute() throws IOException {\r
+        Deployer deployer = new Deployer(new File("E:\\work\\deploy"), Pattern.compile(".*\\.zip|.*\\.jar"), new File("E:\\work\\work"));\r
+        deployer.addListener(this);\r
+        while (true) {\r
+            deployer.monitor();\r
+            try {\r
+                Thread.sleep(1000);\r
+            } catch (InterruptedException e) {\r
+                e.printStackTrace();\r
+            }\r
+        }\r
+    }\r
+\r
+    public void deployEnd(Deployer deployer, File file, File destDirectory) {\r
+        try {\r
+            final DeployerClassLoader cl = new DeployerClassLoader(destDirectory, "classes", "lib");\r
+            cl.addListener(this);\r
+            this.classLoaderMap.put(file.getAbsolutePath(), cl);\r
+\r
+            Runnable runnable = new Runnable() {\r
+\r
+                public void run() {\r
+                    try {\r
+                        Class<?> clazz = cl.loadClass("Main");\r
+                        Object obj = clazz.newInstance();\r
+                        clazz.getMethod("main").invoke(obj);\r
+                    } catch (Exception e) {\r
+                        e.printStackTrace();\r
+                    }\r
+                }\r
+\r
+            };\r
+            Thread t = new Thread(runnable);\r
+            t.start();\r
+        } catch (IOException e) {\r
+            e.printStackTrace();\r
+        }\r
+        // ログ。\r
+        System.out.println("deployEnd: " + file.getAbsolutePath());\r
+    }\r
+\r
+    public void deployFile(Deployer deployer, File file, File destDirectory, File deployFile) {\r
+        // ログ。\r
+        System.out.println("deployFile: " + file.getAbsolutePath() + ", " + deployFile.getAbsolutePath());\r
+    }\r
+\r
+    public void deployStart(Deployer deployer, File file) {\r
+        // ログ。\r
+        System.out.println("deployStart: " + file.getAbsolutePath());\r
+    }\r
+\r
+    public void undeployEnd(Deployer deployer, File file) {\r
+        DeployerClassLoader cl = this.classLoaderMap.remove(file.getAbsolutePath());\r
+        cl.dispose();\r
+        // ログ。\r
+        System.out.println("undeployEnd: " + file.getAbsolutePath());\r
+    }\r
+\r
+    public void undeployStart(Deployer deployer, File file, File destDirectory) {\r
+        // ログ。\r
+        System.out.println("undeployStart: " + file.getAbsolutePath());\r
+    }\r
+\r
+    public void findClass(Class clazz, URL url) {\r
+        // ログ。\r
+        System.out.println("findClass: " + clazz.getName() + " [" + url.toExternalForm() + "]");\r
+    }\r
+\r
+}\r