View Javadoc

1   /*
2    * Copyright 2004-2005 Alvaro Gonzalez de Paz (kortsoft)
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License")
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package kortsoft.kmx.deployer;
17  
18  import java.io.File;
19  import java.io.FilenameFilter;
20  import java.util.ArrayList;
21  import java.util.HashMap;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.Set;
25  import java.util.logging.Level;
26  import java.util.logging.Logger;
27  
28  
29  /***
30   * KmxDameon responsability is to check modified or new components to be deployed.
31   * It must be assigned the directories to check (in the future it could check 
32   * other sources) with the method <code>setWatchDirectories(List<File>)</code>.
33   * Takes the modified or new components and selects a Deployer taken within:
34   * <ul>
35   *   <li>Default Deployers (directory and jar)</li>
36   *   <li>Extra Deployers (based on a <code>FilenameFilter</code>)</li>
37   * </ul>
38   * <p>
39   * These <em>Extra</em> deployers must be assigned with the method
40   * <code>addExtraDeployer(Class<? extends Deployer>,FilenameFilter)</code>.
41   * </p>
42   * <a href="KmxDaemon.java.html"> <b><i>View Source </i> </b> </a>
43   * 
44   * @author Alvaro
45   * @version $Revision: 1.2 $
46   * 
47   * @todo Refactor Deployer selection and invocation to a "DeployerManager"
48   * 
49   */
50  public class KmxDaemon implements KmxDaemonInterface, KmxDaemonMBean {
51  
52      private Map<File, Class< ? extends Deployer>> deployersCache = new HashMap<File, Class< ? extends Deployer>>();
53  
54      private Map<FilenameFilter, Class< ? extends Deployer>> extraDeployers = new HashMap<FilenameFilter, Class< ? extends Deployer>>();
55  
56      private List<File> watchDirectories;
57  
58      private Map<File,Long> lastLoading;;
59      
60      private ClassLoader baseClassLoader;
61  
62      /***
63       * Check modifications in a directory. New files and modified
64       * files/directories are returned as a List <File>. It is passed a Map with
65       * the timestamp of the file/directory last load, so it can be compared with
66       * the modification timestamp.
67       * 
68       * @param directory
69       * @param lastLoadings
70       * @return
71       * 
72       * @todo Implement
73       */
74      protected List<File> checkDirectoryChanges(File directory,
75              Map<File, Long> lastLoadings) {
76          long now=System.currentTimeMillis();
77          List<File> toLoadFiles = new ArrayList<File>();
78          String[] fileNames = directory.list();
79          for (int i = 0; i < fileNames.length; i++) {
80              String fileName = fileNames[i];
81              File checkedFile = new File(directory, fileName);
82              Long lastLoading = lastLoadings.get(checkedFile);
83              long lastModified = checkedFile.lastModified();
84              if (lastLoading == null) // New File!
85                  toLoadFiles.add(checkedFile);
86              else if (lastLoading <= checkedFile.lastModified())
87                  // Modified File!
88                  toLoadFiles.add(checkedFile);
89              lastLoadings.put(checkedFile, now);
90          }
91          return toLoadFiles;
92      }
93  
94      /***
95       * Loads an instance of a Deployer suitable for the specified file. First
96       * seek for the deployer class in a caché if it has been loaded previously
97       * (now it is a reload). If it doesn't find the file in the caché, look for
98       * a deployer for that type of file (both in advanced Deployer and in
99       * default deployes).
100      * 
101      * @param jarFile
102      * @return
103      */
104     protected Deployer loadDeployer(File element)
105             throws DeployerCreationException {
106         Class< ? extends Deployer> deployerClass = null;
107         if (!deployersCache.containsKey(element)) {
108             deployerClass = checkDeployerType(element);
109             if (deployerClass == null)
110                 deployerClass = checkDefaultDeployerType(element);
111             deployersCache.put(element, deployerClass);
112         } else
113             deployerClass = deployersCache.get(element);
114         try {
115             Deployer deployer = deployerClass.newInstance();
116             deployer.setParent(getBaseClassLoader());
117             return deployer;
118         } catch (InstantiationException e) {
119             throw new DeployerCreationException(e);
120         } catch (IllegalAccessException e) {
121             throw new DeployerCreationException(e);
122         }
123 
124     }
125 
126     /***
127      * @param element
128      * @return
129      */
130     private Class< ? extends Deployer> checkDefaultDeployerType(File element) {
131         if (element.isDirectory())
132             return ComponentDirDeployer.class;
133         if (element.getName().toLowerCase().endsWith(".jar"))
134             return JarDeployer.class;
135         else
136             return SimpleFileDeployer.class;
137     }
138 
139     /***
140      * @param element
141      * @return
142      */
143     private Class< ? extends Deployer> checkDeployerType(File element) {
144         Set<FilenameFilter> filters = extraDeployers.keySet();
145         for (FilenameFilter filter : filters) {
146             if (filter.accept(element.getParentFile(), element.getName()))
147                 return extraDeployers.get(filter);
148         }
149         return null;
150     }
151 
152 
153     /***
154      * @param name
155      * @param filter
156      */
157     public void addExtraDeployer(Class< ? extends Deployer> deployerType,
158             FilenameFilter filter) {
159         this.extraDeployers.put(filter, deployerType);
160     }
161 
162     /***
163      * @param watchDirectories
164      */
165     public void setWatchDirectories(List<File> watchDirectories) {
166         synchronized (watchDirectories) {
167             this.watchDirectories = watchDirectories;
168         }
169     }
170 
171     /***
172      * Executes one iteration of loading.
173      * 
174      */
175     public void runDaemon() {       
176         List<File> auxDirectories=new ArrayList<File>();
177         synchronized (watchDirectories){
178            if (watchDirectories==null || (watchDirectories.size()==0)) 
179                return;
180            for (File f : watchDirectories)
181                auxDirectories.add(f);
182        }
183        if (lastLoading==null)
184            lastLoading=new HashMap<File,Long>();
185        for (File directory : auxDirectories){
186            List<File> toLoads=checkDirectoryChanges(directory,lastLoading);
187            for (File toLoad : toLoads){
188                try {
189                    Deployer deployer = loadDeployer(toLoad);
190                    deployer.deployElement(toLoad);
191                } catch (DeployerCreationException e) {
192                    error("Error creating deployer for File "
193                            + toLoad.getName());
194                }
195            }
196        }               
197     }
198 
199     /***
200      * @param string
201      */
202     private void error(String string) {
203        Logger.getLogger(this.getClass().getName()).log(Level.FINE,string);        
204     }
205     /***
206      * @return Returns the watchDirectories.
207      */
208     public List<File> getWatchDirectories() {
209         return watchDirectories;
210     }
211     /***
212      * @return Returns the baseClassLoader.
213      */
214     public ClassLoader getBaseClassLoader() {
215         return baseClassLoader;
216     }
217     /***
218      * @param baseClassLoader The baseClassLoader to set.
219      */
220     public void setBaseClassLoader(ClassLoader baseClassLoader) {
221         this.baseClassLoader = baseClassLoader;
222     }
223 }