Crossfire JXClient, Trunk
DirectoryScanner.java
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * - Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  * - Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  *
15  * - Neither the name of Oracle nor the names of its
16  * contributors may be used to endorse or promote products derived
17  * from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * This source code is provided to illustrate the usage of a given feature
34  * or technique and has been deliberately simplified. Additional steps
35  * required for a production-quality application, such as security checks,
36  * input validation and proper error handling, might not be present in
37  * this sample code.
38  */
39 
40 
41 package com.sun.jmx.examples.scandir;
42 
51 import java.io.File;
52 import java.io.FileFilter;
53 import java.io.IOException;
54 import java.util.Arrays;
55 import java.util.Collections;
56 import java.util.EnumSet;
57 import java.util.HashSet;
58 import java.util.LinkedList;
59 import java.util.Set;
60 import java.util.logging.Level;
61 import java.util.logging.Logger;
62 import javax.management.AttributeChangeNotification;
63 import javax.management.InstanceNotFoundException;
64 import javax.management.ListenerNotFoundException;
65 import javax.management.MBeanNotificationInfo;
66 import javax.management.Notification;
67 import javax.management.NotificationBroadcasterSupport;
68 import javax.management.NotificationEmitter;
69 import javax.management.NotificationFilter;
70 import javax.management.NotificationListener;
71 
105 public class DirectoryScanner implements
106  DirectoryScannerMXBean, NotificationEmitter {
107 
115  public static final String FILE_MATCHES_NOTIFICATION =
116  "com.sun.jmx.examples.scandir.filematch";
117 
121  private static final Logger LOG =
122  Logger.getLogger(DirectoryScanner.class.getName());
123 
124  // Attribute : State
125  //
126  private volatile ScanState state = STOPPED;
127 
128  // The DirectoryScanner delegates the implementation of
129  // the NotificationEmitter interface to a wrapped instance
130  // of NotificationBroadcasterSupport.
131  //
132  private final NotificationBroadcasterSupport broadcaster;
133 
134  // The root directory at which this DirectoryScanner will start
135  // scanning. Constructed from config.getRootDirectory().
136  //
137  private final File rootFile;
138 
139  // This DirectoryScanner config - this is a constant which is
140  // provided at construction time by the {@link ScanManager}.
141  //
143 
144  // The set of actions for which this DirectoryScanner is configured.
145  // Constructed from config.getActions()
146  //
147  final Set<Action> actions;
148 
149  // The ResultLogManager that this DirectoryScanner will use to log
150  // info. This is a hard reference to another MBean, provided
151  // at construction time by the ScanManager.
152  // The ScanManager makes sure that the life cycle of these two MBeans
153  // is consistent.
154  //
156 
180  throws IllegalArgumentException {
181  if (logManager == null)
182  throw new IllegalArgumentException("log=null");
183  if (config == null)
184  throw new IllegalArgumentException("config=null");
185  if (config.getName() == null)
186  throw new IllegalArgumentException("config.name=null");
187 
188  broadcaster = new NotificationBroadcasterSupport();
189 
190  // Clone the config: ensure data encapsulation.
191  //
192  this.config = XmlConfigUtils.xmlClone(config);
193 
194  // Checks that the provided root directory is valid.
195  // Throws IllegalArgumentException if it isn't.
196  //
197  rootFile = validateRoot(config.getRootDirectory());
198 
199  // Initialize the Set<Action> for which this DirectoryScanner
200  // is configured.
201  //
202  if (config.getActions() == null)
203  actions = Collections.emptySet();
204  else
205  actions = EnumSet.copyOf(Arrays.asList(config.getActions()));
206  this.logManager = logManager;
207  }
208 
209  // see DirectoryScannerMXBean
210  public void stop() {
211  // switch state to stop and send AttributeValueChangeNotification
212  setStateAndNotify(STOPPED);
213  }
214 
215  // see DirectoryScannerMXBean
216  public String getRootDirectory() {
217  return rootFile.getAbsolutePath();
218  }
219 
220 
221  // see DirectoryScannerMXBean
222  public ScanState getState() {
223  return state;
224  }
225 
226  // see DirectoryScannerMXBean
228  return config;
229  }
230 
231  // see DirectoryScannerMXBean
232  public String getCurrentScanInfo() {
233  final ScanTask currentOrLastTask = currentTask;
234  if (currentOrLastTask == null) return "Never Run";
235  return currentOrLastTask.getScanInfo();
236  }
237 
238  // This variable points to the current (or latest) scan.
239  //
240  private volatile ScanTask currentTask = null;
241 
242  // see DirectoryScannerMXBean
243  public void scan() {
244  final ScanTask task;
245 
246  synchronized (this) {
247  final LinkedList<File> list;
248  switch (state) {
249  case RUNNING:
250  case SCHEDULED:
251  throw new IllegalStateException(state.toString());
252  case STOPPED:
253  case COMPLETED:
254  // only accept to scan if state is STOPPED or COMPLETED.
255  list = new LinkedList<File>();
256  list.add(rootFile);
257  break;
258  default:
259  throw new IllegalStateException(String.valueOf(state));
260  }
261 
262  // Create a new ScanTask object for our root directory file.
263  //
264  currentTask = task = new ScanTask(list,this);
265 
266  // transient state... will be switched to RUNNING when
267  // task.execute() is called. This code could in fact be modified
268  // to use java.util.concurent.Future and, to push the task to
269  // an executor. We would then need to wait for the task to
270  // complete before returning. However, this wouldn't buy us
271  // anything - since this method should wait for the task to
272  // finish anyway: so why would we do it?
273  // As it stands, we simply call task.execute() in the current
274  // thread - brave and fearless readers may want to attempt the
275  // modification ;-)
276  //
277  setStateAndNotify(SCHEDULED);
278  }
279  task.execute();
280  }
281 
282  // This method is invoked to carry out the configured actions on a
283  // matching file.
284  // Do not call this method from within synchronized() { } as this
285  // method may send notifications!
286  //
287  void actOn(File file) {
288 
289  // Which action were actually taken
290  //
291  final Set<Action> taken = new HashSet<Action>();
292  boolean logresult = false;
293 
294  // Check out which actions are configured and carry them out.
295  //
296  for (Action action : actions) {
297  switch (action) {
298  case DELETE:
299  if (deleteFile(file)) {
300  // Delete succeeded: add DELETE to the set of
301  // actions carried out.
302  taken.add(DELETE);
303  }
304  break;
305  case NOTIFY:
306  if (notifyMatch(file)) {
307  // Notify succeeded: add NOTIFY to the set of
308  // actions carried out.
309  taken.add(NOTIFY);
310  }
311  break;
312  case LOGRESULT:
313  // LOGRESULT was configured - log actions carried out.
314  // => we must execute this action as the last action.
315  // simply set logresult=true for now. We will do
316  // the logging later
317  logresult = true;
318  break;
319  default:
320  LOG.fine("Failed to execute action: " +action +
321  " - action not supported");
322  break;
323  }
324  }
325 
326  // Now is time for logging:
327  if (logresult) {
328  taken.add(LOGRESULT);
329  if (!logResult(file,taken.toArray(new Action[taken.size()])))
330  taken.remove(LOGRESULT); // just for the last trace below...
331  }
332 
333  LOG.finest("File processed: "+taken+" - "+file.getAbsolutePath());
334  }
335 
336  // Deletes a matching file.
337  private boolean deleteFile(File file) {
338  try {
339  // file.delete() is commented so that we don't do anything
340  // bad if the example is mistakenly run on the wrong set of
341  // directories.
342  //
343  /* file.delete(); */
344  System.out.println("DELETE not implemented for safety reasons.");
345  return true;
346  } catch (Exception x) {
347  LOG.fine("Failed to delete: "+file.getAbsolutePath());
348  }
349  return false;
350  }
351 
352  // Notifies of a matching file.
353  private boolean notifyMatch(File file) {
354  try {
355  final Notification n =
356  new Notification(FILE_MATCHES_NOTIFICATION,this,
357  getNextSeqNumber(),
358  file.getAbsolutePath());
359 
360  // This method *is not* called from any synchronized block, so
361  // we can happily call broadcaster.sendNotification() here.
362  // Note that verifying whether a method is called from within
363  // a synchronized block demends a thoroughful code reading,
364  // examining each of the 'parent' methods in turn.
365  //
366  broadcaster.sendNotification(n);
367  return true;
368  } catch (Exception x) {
369  LOG.fine("Failed to notify: "+file.getAbsolutePath());
370  }
371  return false;
372  }
373 
374  // Logs a result with the ResultLogManager
375  private boolean logResult(File file,Action[] actions) {
376  try {
378  return true;
379  } catch (Exception x) {
380  LOG.fine("Failed to log: "+file.getAbsolutePath());
381  }
382  return false;
383  }
384 
385 
386  // Contextual object used to store info about current
387  // (or last) scan.
388  //
389  private static class ScanTask {
390 
391  // List of Files that remain to scan.
392  // When files are discovered they are added to the list.
393  // When they are being handled, they are removed from the list.
394  // When the list is empty, the scanning is finished.
395  //
396  private final LinkedList<File> list;
397  private final DirectoryScanner scan;
398 
399  // Some statistics...
400  //
401  private volatile long scanned=0;
402  private volatile long matching=0;
403 
404  private volatile String info="Not started";
405 
406  ScanTask(LinkedList<File> list, DirectoryScanner scan) {
407  this.list = list; this.scan = scan;
408  }
409 
410  public void execute() {
411  scan(list);
412  }
413 
414  private void scan(LinkedList<File> list) {
415  scan.scan(this,list);
416  }
417 
418  public String getScanInfo() {
419  return info+" - ["+scanned+" scanned, "+matching+" matching]";
420  }
421  }
422 
423  // The actual scan logic. Switches state to RUNNING,
424  // and scan the list of given dirs.
425  // The list is a live object which is updated by this method.
426  // This would allow us to implement methods like "pause" and "resume",
427  // since all the info needed to resume would be in the list.
428  //
429  private void scan(ScanTask task, LinkedList<File> list) {
430  setStateAndNotify(RUNNING);
431  task.info = "In Progress";
432  try {
433 
434  // The FileFilter will tell us which files match and which don't.
435  //
436  final FileFilter filter = config.buildFileFilter();
437 
438  // We have two condition to end the loop: either the list is
439  // empty, meaning there's nothing more to scan, or the state of
440  // the DirectoryScanner was asynchronously switched to STOPPED by
441  // another thread, e.g. because someone called "stop" on the
442  // ScanManagerMXBean
443  //
444  while (!list.isEmpty() && state == RUNNING) {
445 
446  // Get and remove the first element in the list.
447  //
448  final File current = list.poll();
449 
450  // Increment number of file scanned.
451  task.scanned++;
452 
453  // If 'current' is a file, it's already been matched by our
454  // file filter (see below): act on it.
455  // Note that for the first iteration of this loop, there will
456  // be one single file in the list: the root directory for this
457  // scanner.
458  //
459  if (current.isFile()) {
460  task.matching++;
461  actOn(current);
462  }
463 
464  // If 'current' is a directory, then
465  // find files and directories that match the file filter
466  // in this directory
467  //
468  if (current.isDirectory()) {
469 
470  // Gets matching files and directories
471  final File[] content = current.listFiles(filter);
472  if (content == null) continue;
473 
474  // Adds all matching file to the list.
475  list.addAll(0,Arrays.asList(content));
476  }
477  }
478 
479  // The loop terminated. If the list is empty, then we have
480  // completed our task. If not, then somebody must have called
481  // stop() on this directory scanner.
482  //
483  if (list.isEmpty()) {
484  task.info = "Successfully Completed";
485  setStateAndNotify(COMPLETED);
486  }
487  } catch (Exception x) {
488  // We got an exception: stop the scan
489  //
490  task.info = "Failed: "+x;
491  if (LOG.isLoggable(Level.FINEST))
492  LOG.log(Level.FINEST,"scan task failed: "+x,x);
493  else if (LOG.isLoggable(Level.FINE))
494  LOG.log(Level.FINE,"scan task failed: "+x);
495  setStateAndNotify(STOPPED);
496  } catch (Error e) {
497  // We got an Error:
498  // Should not happen unless we ran out of memory or
499  // whatever - don't even try to notify, but
500  // stop the scan anyway!
501  //
502  state=STOPPED;
503  task.info = "Error: "+e;
504 
505  // rethrow error.
506  //
507  throw e;
508  }
509  }
510 
514  public void addNotificationListener(NotificationListener listener,
515  NotificationFilter filter, Object handback)
516  throws IllegalArgumentException {
517  broadcaster.addNotificationListener(listener, filter, handback);
518  }
519 
520  // Switch this object state to the desired value an send
521  // a notification. Don't call this method from within a
522  // synchronized block!
523  //
524  private final void setStateAndNotify(ScanState desired) {
525  final ScanState old = state;
526  if (old == desired) return;
527  state = desired;
528  final AttributeChangeNotification n =
529  new AttributeChangeNotification(this,
530  getNextSeqNumber(),System.currentTimeMillis(),
531  "state change","State",ScanState.class.getName(),
532  String.valueOf(old),String.valueOf(desired));
533  broadcaster.sendNotification(n);
534  }
535 
536 
541  public MBeanNotificationInfo[] getNotificationInfo() {
542  return new MBeanNotificationInfo[] {
543  new MBeanNotificationInfo(
544  new String[] {FILE_MATCHES_NOTIFICATION},
545  Notification.class.getName(),
546  "Emitted when a file that matches the scan criteria is found"
547  ),
548  new MBeanNotificationInfo(
549  new String[] {AttributeChangeNotification.ATTRIBUTE_CHANGE},
550  AttributeChangeNotification.class.getName(),
551  "Emitted when the State attribute changes"
552  )
553  };
554  }
555 
559  public void removeNotificationListener(NotificationListener listener)
560  throws ListenerNotFoundException {
561  broadcaster.removeNotificationListener(listener);
562  }
563 
567  public void removeNotificationListener(NotificationListener listener,
568  NotificationFilter filter, Object handback)
569  throws ListenerNotFoundException {
570  broadcaster.removeNotificationListener(listener, filter, handback);
571  }
572 
573  // Validates the given root directory, returns a File object for
574  // that directory.
575  // Throws IllegalArgumentException if the given root is not
576  // acceptable.
577  //
578  private static File validateRoot(String root) {
579  if (root == null)
580  throw new IllegalArgumentException("no root specified");
581  if (root.length() == 0)
582  throw new IllegalArgumentException("specified root \"\" is invalid");
583  final File f = new File(root);
584  if (!f.canRead())
585  throw new IllegalArgumentException("can't read "+root);
586  if (!f.isDirectory())
587  throw new IllegalArgumentException("no such directory: "+root);
588  return f;
589  }
590 
591 }
file
Once a FileSystem is created then classes in the java nio file package can be used to access files in the zip JAR file
Definition: README.txt:19
com.sun.jmx.examples.scandir.DirectoryScanner.getState
ScanState getState()
Definition: DirectoryScanner.java:222
com.sun.jmx.examples.scandir.DirectoryScanner.stop
void stop()
Definition: DirectoryScanner.java:210
com.sun.jmx.examples.scandir.config.DirectoryScannerConfig.Action
Definition: DirectoryScannerConfig.java:81
com.sun.jmx.examples.scandir.config
Definition: DirectoryScannerConfig.java:41
com.sun.jmx.examples.scandir.DirectoryScanner.logManager
final ResultLogManager logManager
Definition: DirectoryScanner.java:155
com.sun.jmx.examples.scandir.DirectoryScanner.ScanTask.execute
void execute()
Definition: DirectoryScanner.java:410
com.sun.jmx.examples.scandir.DirectoryScannerMXBean
Definition: DirectoryScannerMXBean.java:63
com.sun.jmx.examples.scandir
com.sun.jmx.examples.scandir.DirectoryScanner.getNotificationInfo
MBeanNotificationInfo[] getNotificationInfo()
Definition: DirectoryScanner.java:541
com.sun.jmx.examples.scandir.DirectoryScanner.getCurrentScanInfo
String getCurrentScanInfo()
Definition: DirectoryScanner.java:232
com.sun.jmx.examples.scandir.DirectoryScanner.removeNotificationListener
void removeNotificationListener(NotificationListener listener)
Definition: DirectoryScanner.java:559
com.sun.jmx.examples.scandir.DirectoryScanner.addNotificationListener
void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback)
Definition: DirectoryScanner.java:514
com.sun.jmx.examples.scandir.DirectoryScanner.broadcaster
final NotificationBroadcasterSupport broadcaster
Definition: DirectoryScanner.java:132
com.sun.jmx.examples.scandir.ScanManager
Definition: ScanManager.java:103
com.sun.jmx.examples.scandir.DirectoryScanner.ScanTask.list
final LinkedList< File > list
Definition: DirectoryScanner.java:396
com.sun.jmx.examples.scandir.ResultLogManager.log
void log(ResultRecord record)
Definition: ResultLogManager.java:300
com.sun.jmx.examples.scandir.DirectoryScanner.config
final DirectoryScannerConfig config
Definition: DirectoryScanner.java:142
com.sun.jmx.examples.scandir.DirectoryScanner.ScanTask.info
volatile String info
Definition: DirectoryScanner.java:404
com.sun.jmx.examples.scandir.ScanManagerMXBean.ScanState
Definition: ScanManagerMXBean.java:77
com.sun.jmx.examples
com.sun.jmx.examples.scandir.DirectoryScanner.actions
final Set< Action > actions
Definition: DirectoryScanner.java:147
com.sun.jmx.examples.scandir.ScanManagerMXBean
Definition: ScanManagerMXBean.java:66
com.sun.jmx.examples.scandir.DirectoryScanner.deleteFile
boolean deleteFile(File file)
Definition: DirectoryScanner.java:337
com.sun.jmx.examples.scandir.DirectoryScanner.notifyMatch
boolean notifyMatch(File file)
Definition: DirectoryScanner.java:353
com.sun
com.sun.jmx.examples.scandir.DirectoryScanner.scan
void scan(ScanTask task, LinkedList< File > list)
Definition: DirectoryScanner.java:429
com.sun.jmx.examples.scandir.DirectoryScanner.currentTask
volatile ScanTask currentTask
Definition: DirectoryScanner.java:240
com.sun.jmx.examples.scandir.DirectoryScanner.ScanTask.scan
void scan(LinkedList< File > list)
Definition: DirectoryScanner.java:414
com.sun.jmx.examples.scandir.DirectoryScanner.logResult
boolean logResult(File file, Action[] actions)
Definition: DirectoryScanner.java:375
com.sun.jmx.examples.scandir.DirectoryScanner
Definition: DirectoryScanner.java:105
com.sun.jmx.examples.scandir.DirectoryScanner.ScanTask.matching
volatile long matching
Definition: DirectoryScanner.java:402
com.sun.jmx.examples.scandir.DirectoryScanner.ScanTask.getScanInfo
String getScanInfo()
Definition: DirectoryScanner.java:418
com.sun.jmx.examples.scandir.DirectoryScanner.state
volatile ScanState state
Definition: DirectoryScanner.java:126
com.sun.jmx.examples.scandir.DirectoryScanner.LOG
static final Logger LOG
Definition: DirectoryScanner.java:121
com.sun.jmx.examples.scandir.DirectoryScanner.ScanTask.scan
final DirectoryScanner scan
Definition: DirectoryScanner.java:397
com.sun.jmx.examples.scandir.DirectoryScanner.getConfiguration
DirectoryScannerConfig getConfiguration()
Definition: DirectoryScanner.java:227
com.sun.jmx.examples.scandir.DirectoryScanner.scan
void scan()
Definition: DirectoryScanner.java:243
com.sun.jmx.examples.scandir.DirectoryScanner.setStateAndNotify
final void setStateAndNotify(ScanState desired)
Definition: DirectoryScanner.java:524
com.sun.jmx.examples.scandir.DirectoryScanner.validateRoot
static File validateRoot(String root)
Definition: DirectoryScanner.java:578
com.sun.jmx.examples.scandir.config.ResultRecord
Definition: ResultRecord.java:62
com.sun.jmx.examples.scandir.DirectoryScanner.getRootDirectory
String getRootDirectory()
Definition: DirectoryScanner.java:216
com.sun.jmx.examples.scandir.ResultLogManager
Definition: ResultLogManager.java:81
com.sun.jmx.examples.scandir.ScanManager.getNextSeqNumber
static synchronized long getNextSeqNumber()
Definition: ScanManager.java:961
com
com.sun.jmx.examples.scandir.config.XmlConfigUtils.xmlClone
static ScanManagerConfig xmlClone(ScanManagerConfig bean)
Definition: XmlConfigUtils.java:165
com.sun.jmx
com.sun.jmx.examples.scandir.DirectoryScanner.FILE_MATCHES_NOTIFICATION
static final String FILE_MATCHES_NOTIFICATION
Definition: DirectoryScanner.java:115
com.sun.jmx.examples.scandir.DirectoryScanner.ScanTask.ScanTask
ScanTask(LinkedList< File > list, DirectoryScanner scan)
Definition: DirectoryScanner.java:406
com.sun.jmx.examples.scandir.DirectoryScanner.removeNotificationListener
void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback)
Definition: DirectoryScanner.java:567
com.sun.jmx.examples.scandir.DirectoryScanner.DirectoryScanner
DirectoryScanner(DirectoryScannerConfig config, ResultLogManager logManager)
Definition: DirectoryScanner.java:178
com.sun.jmx.examples.scandir.DirectoryScanner.ScanTask.scanned
volatile long scanned
Definition: DirectoryScanner.java:401
com.sun.jmx.examples.scandir.DirectoryScanner.ScanTask
Definition: DirectoryScanner.java:389
com.sun.jmx.examples.scandir.DirectoryScanner.actOn
void actOn(File file)
Definition: DirectoryScanner.java:287
com.sun.jmx.examples.scandir.config.XmlConfigUtils
Definition: XmlConfigUtils.java:62
com.sun.jmx.examples.scandir.config.DirectoryScannerConfig
Definition: DirectoryScannerConfig.java:67
com.sun.jmx.examples.scandir.DirectoryScanner.rootFile
final File rootFile
Definition: DirectoryScanner.java:137