Gridarta Editor
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 }
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
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.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
list
This document describes some hints and requirements for general development on the CrossfireEditor If you plan to make changes to the editor code or setup please read the following and keep it in derived from a basic editor application called Gridder by Pasi Ker�nen so please communicate with best through the cf devel mailing list
Definition: Developer_README.txt:13
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