1 17 package org.apache.geronimo.deployment.hot; 18 19 import org.apache.commons.logging.LogFactory; 20 import org.apache.commons.logging.Log; 21 import org.apache.geronimo.deployment.cli.DeployUtils; 22 import org.apache.geronimo.kernel.repository.Artifact; 23 import org.apache.geronimo.kernel.config.IOUtil; 24 25 import java.io.File ; 26 import java.io.Serializable ; 27 import java.io.IOException ; 28 import java.util.Map ; 29 import java.util.HashMap ; 30 import java.util.HashSet ; 31 import java.util.Iterator ; 32 import java.util.List ; 33 import java.util.LinkedList ; 34 import java.util.Set ; 35 36 46 public class DirectoryMonitor implements Runnable { 47 private static final Log log = LogFactory.getLog(DirectoryMonitor.class); 48 49 public static interface Listener { 50 55 boolean isServerRunning(); 56 57 64 boolean isFileDeployed(File file, String configId); 65 66 73 long getDeploymentTime(File file, String configId); 74 75 79 void started(); 80 81 89 boolean validateFile(File file, String configId); 90 91 98 String fileAdded(File file); 99 100 104 boolean fileRemoved(File file, String configId); 105 106 String fileUpdated(File file, String configId); 107 108 112 String getModuleId(String config); 113 114 } 115 116 private int pollIntervalMillis; 117 private File directory; 118 private boolean done = false; 119 private Listener listener; private final Map files = new HashMap (); 121 private volatile String workingOnConfigId; 122 123 public DirectoryMonitor(File directory, Listener listener, int pollIntervalMillis) { 124 this.directory = directory; 125 this.listener = listener; 126 this.pollIntervalMillis = pollIntervalMillis; 127 } 128 129 public int getPollIntervalMillis() { 130 return pollIntervalMillis; 131 } 132 133 public void setPollIntervalMillis(int pollIntervalMillis) { 134 this.pollIntervalMillis = pollIntervalMillis; 135 } 136 137 public Listener getListener() { 138 return listener; 139 } 140 141 public void setListener(Listener listener) { 142 this.listener = listener; 143 } 144 145 public File getDirectory() { 146 return directory; 147 } 148 149 154 public void setDirectory(File directory) { 155 if (!directory.isDirectory() || !directory.canRead()) { 156 throw new IllegalArgumentException ("Cannot monitor directory " + directory.getAbsolutePath()); 157 } 158 this.directory = directory; 159 } 160 161 public synchronized boolean isDone() { 162 return done; 163 } 164 165 public synchronized void close() { 166 this.done = true; 167 } 168 169 public void removeModuleId(Artifact id) { 170 log.info("Hot deployer notified that an artifact was removed: "+id); 171 if(id.toString().equals(workingOnConfigId)) { 172 return; } 178 synchronized(files) { 179 for (Iterator it = files.keySet().iterator(); it.hasNext();) { 180 String path = (String ) it.next(); 181 FileInfo info = (FileInfo) files.get(path); 182 Artifact target = Artifact.create(info.getConfigId()); 183 if(id.matches(target)) { File file = new File (path); 185 if(file.exists()) { log.info("Hot deployer deleting "+id); 187 if(!IOUtil.recursiveDelete(file)) { 188 log.error("Hot deployer unable to delete "+path); 189 } 190 it.remove(); 191 } 192 } 193 } 194 } 195 } 196 197 public void run() { 198 boolean serverStarted = false, initialized = false; 199 while (!done) { 200 try { 201 Thread.sleep(pollIntervalMillis); 202 } catch (InterruptedException e) { 203 continue; 204 } 205 try { 206 if (listener != null) { 207 if (!serverStarted && listener.isServerRunning()) { 208 serverStarted = true; 209 } 210 if (serverStarted) { 211 if (!initialized) { 212 initialized = true; 213 initialize(); 214 listener.started(); 215 } else { 216 scanDirectory(); 217 } 218 } 219 } 220 } catch (Exception e) { 221 log.error("Error during hot deployment", e); 222 } 223 } 224 } 225 226 public void initialize() { 227 File parent = directory; 228 File [] children = parent.listFiles(); 229 for (int i = 0; i < children.length; i++) { 230 File child = children[i]; 231 if (!child.canRead()) { 232 continue; 233 } 234 FileInfo now = child.isDirectory() ? getDirectoryInfo(child) : getFileInfo(child); 235 now.setChanging(false); 236 try { 237 now.setConfigId(calculateModuleId(child)); 238 if (listener == null || listener.isFileDeployed(child, now.getConfigId())) { 239 if (listener != null) { 240 now.setModified(listener.getDeploymentTime(child, now.getConfigId())); 241 } 242 log.info("At startup, found "+now.getPath()+" with deploy time "+now.getModified()+" and file time "+new File (now.getPath()).lastModified()); 243 files.put(now.getPath(), now); 244 } 245 } catch (Exception e) { 246 log.error("Unable to scan file " + child.getAbsolutePath() + " during initialization", e); 247 } 248 } 249 } 250 251 254 private void scanDirectory() { 255 File parent = directory; 256 File [] children = parent.listFiles(); 257 if (!directory.exists() || children == null) { 258 log.error("Hot deploy directory has disappeared! Shutting down directory monitor."); 259 done = true; 260 return; 261 } 262 synchronized (files) { 263 Set oldList = new HashSet (files.keySet()); 264 List actions = new LinkedList (); 265 for (int i = 0; i < children.length; i++) { 266 File child = children[i]; 267 if (!child.canRead()) { 268 continue; 269 } 270 FileInfo now = child.isDirectory() ? getDirectoryInfo(child) : getFileInfo(child); 271 FileInfo then = (FileInfo) files.get(now.getPath()); 272 if (then == null) { now.setNewFile(true); 274 files.put(now.getPath(), now); 275 log.debug("New File: " + now.getPath()); 276 } else { 277 oldList.remove(then.getPath()); 278 if (now.isSame(then)) { if (then.isChanging()) { 280 log.debug("File finished changing: " + now.getPath()); 281 if (then.isNewFile()) { 283 actions.add(new FileAction(FileAction.NEW_FILE, child, then)); 284 } else { 285 actions.add(new FileAction(FileAction.UPDATED_FILE, child, then)); 286 } 287 then.setChanging(false); 288 } } else if(then.isNewFile() || now.getModified() > then.getModified()) { 290 now.setConfigId(then.getConfigId()); 293 now.setNewFile(then.isNewFile()); 294 files.put(now.getPath(), now); 295 log.debug("File Changed: " + now.getPath()); 296 } 297 } 298 } 299 for (Iterator it = oldList.iterator(); it.hasNext();) { 301 String name = (String ) it.next(); 302 FileInfo info = (FileInfo) files.get(name); 303 log.debug("File removed: " + name); 304 if (info.isNewFile()) { files.remove(name); 306 } else { 307 actions.add(new FileAction(FileAction.REMOVED_FILE, new File (name), info)); 308 } 309 } 310 if (listener != null) { 311 for (Iterator it = actions.iterator(); it.hasNext();) { 313 FileAction action = (FileAction) it.next(); 314 if (!listener.validateFile(action.child, action.info.getConfigId())) { 315 resolveFile(action); 316 it.remove(); 317 } 318 } 319 for (Iterator it = actions.iterator(); it.hasNext();) { 321 FileAction action = (FileAction) it.next(); 322 try { 323 if (action.action == FileAction.REMOVED_FILE) { 324 workingOnConfigId = action.info.getConfigId(); 325 if (listener.fileRemoved(action.child, action.info.getConfigId())) { 326 files.remove(action.child.getPath()); 327 } 328 workingOnConfigId = null; 329 } else if (action.action == FileAction.NEW_FILE) { 330 String result = listener.fileAdded(action.child); 331 if (result != null) { 332 if (!result.equals("")) { 333 action.info.setConfigId(result); 334 } else { 335 action.info.setConfigId(calculateModuleId(action.child)); 336 } 337 action.info.setNewFile(false); 338 } 339 } else if (action.action == FileAction.UPDATED_FILE) { 340 workingOnConfigId = action.info.getConfigId(); 341 String result = listener.fileUpdated(action.child, action.info.getConfigId()); 342 FileInfo update = action.info; 343 if (result != null) { 344 if (!result.equals("")) { 345 update.setConfigId(result); 346 } else { 347 update.setConfigId(calculateModuleId(action.child)); 348 } 349 } 350 workingOnConfigId = null; 351 } 352 } catch (Exception e) { 353 log.error("Unable to " + action.getActionName() + " file " + action.child.getAbsolutePath(), e); 354 } finally { 355 resolveFile(action); 356 } 357 } 358 } 359 } 360 } 361 362 private void resolveFile(FileAction action) { 363 if (action.action == FileAction.REMOVED_FILE) { 364 files.remove(action.child.getPath()); 365 } else { 366 action.info.setChanging(false); 367 } 368 } 369 370 private String calculateModuleId(File module) { 371 String moduleId = null; 372 try { 373 moduleId = DeployUtils.extractModuleIdFromArchive(module); 374 } catch (Exception e) { 375 try { 376 moduleId = DeployUtils.extractModuleIdFromPlan(module); 377 } catch (IOException e2) { 378 log.warn("Unable to calculate module ID for file " + module.getAbsolutePath() + " [" + e2.getMessage() + "]"); 379 } 380 } 381 if (moduleId == null) { 382 int pos = module.getName().lastIndexOf('.'); 383 moduleId = pos > -1 ? module.getName().substring(0, pos) : module.getName(); 384 moduleId = listener.getModuleId(moduleId); 385 } 386 return moduleId; 387 } 388 389 394 private FileInfo getDirectoryInfo(File dir) { 395 FileInfo info = new FileInfo(dir.getAbsolutePath()); 396 info.setSize(0); 397 info.setModified(getLastModifiedInDir(dir)); 398 return info; 399 } 400 401 private long getLastModifiedInDir(File dir) { 402 long value = dir.lastModified(); 403 File [] children = dir.listFiles(); 404 long test; 405 for (int i = 0; i < children.length; i++) { 406 File child = children[i]; 407 if (!child.canRead()) { 408 continue; 409 } 410 if (child.isDirectory()) { 411 test = getLastModifiedInDir(child); 412 } else { 413 test = child.lastModified(); 414 } 415 if (test > value) { 416 value = test; 417 } 418 } 419 return value; 420 } 421 422 private FileInfo getFileInfo(File child) { 423 FileInfo info = new FileInfo(child.getAbsolutePath()); 424 info.setSize(child.length()); 425 info.setModified(child.lastModified()); 426 return info; 427 } 428 429 private static class FileAction { 430 private static int NEW_FILE = 1; 431 private static int UPDATED_FILE = 2; 432 private static int REMOVED_FILE = 3; 433 private int action; 434 private File child; 435 private FileInfo info; 436 437 public FileAction(int action, File child, FileInfo info) { 438 this.action = action; 439 this.child = child; 440 this.info = info; 441 } 442 443 public String getActionName() { 444 return action == NEW_FILE ? "deploy" : action == UPDATED_FILE ? "redeploy" : "undeploy"; 445 } 446 } 447 448 private static class FileInfo implements Serializable { 449 private String path; 450 private long size; 451 private long modified; 452 private boolean newFile; 453 private boolean changing; 454 private String configId; 455 456 public FileInfo(String path) { 457 this.path = path; 458 newFile = false; 459 changing = true; 460 } 461 462 public String getPath() { 463 return path; 464 } 465 466 public long getSize() { 467 return size; 468 } 469 470 public void setSize(long size) { 471 this.size = size; 472 } 473 474 public long getModified() { 475 return modified; 476 } 477 478 public void setModified(long modified) { 479 this.modified = modified; 480 } 481 482 public boolean isNewFile() { 483 return newFile; 484 } 485 486 public void setNewFile(boolean newFile) { 487 this.newFile = newFile; 488 } 489 490 public boolean isChanging() { 491 return changing; 492 } 493 494 public void setChanging(boolean changing) { 495 this.changing = changing; 496 } 497 498 public String getConfigId() { 499 return configId; 500 } 501 502 public void setConfigId(String configId) { 503 this.configId = configId; 504 } 505 506 public boolean isSame(FileInfo info) { 507 if (!path.equals(info.path)) { 508 throw new IllegalArgumentException ("Should only be used to compare two files representing the same path!"); 509 } 510 return size == info.size && modified == info.modified; 511 } 512 } 513 } 514 | Popular Tags |