| /* |
| * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * - Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * - Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * - Neither the name of Oracle nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
| * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /* |
| * This source code is provided to illustrate the usage of a given feature |
| * or technique and has been deliberately simplified. Additional steps |
| * required for a production-quality application, such as security checks, |
| * input validation and proper error handling, might not be present in |
| * this sample code. |
| */ |
| |
| |
| package com.sun.jmx.examples.scandir; |
| |
| import static com.sun.jmx.examples.scandir.ScanManager.getNextSeqNumber; |
| import static com.sun.jmx.examples.scandir.ScanDirConfigMXBean.SaveState.*; |
| import com.sun.jmx.examples.scandir.config.XmlConfigUtils; |
| import com.sun.jmx.examples.scandir.config.DirectoryScannerConfig; |
| import com.sun.jmx.examples.scandir.config.FileMatch; |
| import com.sun.jmx.examples.scandir.config.ScanManagerConfig; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.util.Date; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| import javax.management.*; |
| import javax.xml.bind.JAXBException; |
| |
| /** |
| * <p>The <code>ScanDirConfig</code> MBean is in charge of the |
| * <i>scandir</i> application configuration. |
| * </p> |
| * <p>The <code>ScanDirConfig</code> MBean is able to |
| * load and save the <i>scandir</i> application configuration to and from an |
| * XML file. |
| * </p> |
| * <p> |
| * It will let you also interactively modify that configuration, which you |
| * can later save to the file, by calling {@link #save}, or discard, by |
| * reloading the file without saving - see {@link #load}. |
| * </p> |
| * <p> |
| * There can be as many <code>ScanDirConfigMXBean</code> registered |
| * in the MBeanServer as you like, but only one of them will be identified as |
| * the current configuration of the {@link ScanManagerMXBean}. |
| * You can switch to another configuration by calling {@link |
| * ScanManagerMXBean#setConfigurationMBean |
| * ScanManagerMXBean.setConfigurationMBean}. |
| * </p> |
| * <p> |
| * Once the current configuration has been loaded (by calling {@link #load}) |
| * or modified (by calling one of {@link #addDirectoryScanner |
| * addDirectoryScanner}, {@link #removeDirectoryScanner removeDirectoryScanner} |
| * or {@link #setConfiguration setConfiguration}) it can be pushed |
| * to the {@link ScanManagerMXBean} by calling {@link |
| * ScanManagerMXBean#applyConfiguration |
| * ScanManagerMXBean.applyConfiguration(true)} - |
| * <code>true</code> means that we apply the configuration from memory, |
| * without first reloading the file. |
| * </p> |
| * <p> |
| * The <code>ScanDirConfig</code> uses the XML annotated Java Beans defined |
| * in the {@link com.sun.jmx.examples.scandir.config} package. |
| * </p> |
| * <p> |
| * <u>Note:</u> The <code>ScanDirConfig</code> should probably use |
| * {@code java.nio.channels.FileLock} and lock its configuration file so that |
| * two <code>ScanDirConfig</code> object do not share the same file, but it |
| * doesn't. Feel free to improve the application in that way. |
| * </p> |
| * @author Sun Microsystems, 2006 - All rights reserved. |
| */ |
| public class ScanDirConfig extends NotificationBroadcasterSupport |
| implements ScanDirConfigMXBean, MBeanRegistration { |
| |
| /** |
| * A logger for this class. |
| **/ |
| private static final Logger LOG = |
| Logger.getLogger(ScanDirConfig.class.getName()); |
| |
| // We will emit a notification when the save state of this object |
| // chenges. We use directly the base notification class, with a |
| // notification type that indicates the new state at which the |
| // object has arrived. |
| // |
| // All these notification types will have the same prefix, which is |
| // 'com.sun.jmx.examples.scandir.config'. |
| // |
| private final static String NOTIFICATION_PREFIX = |
| ScanManagerConfig.class.getPackage().getName(); |
| |
| /** |
| * The <i>com.sun.jmx.examples.scandir.config.saved</i> notification |
| * indicates that the configuration data was saved. |
| **/ |
| public final static String NOTIFICATION_SAVED = |
| NOTIFICATION_PREFIX+".saved"; |
| /** |
| * The <i>com.sun.jmx.examples.scandir.config.loaded</i> notification |
| * indicates that the configuration data was loaded. |
| **/ |
| public final static String NOTIFICATION_LOADED = |
| NOTIFICATION_PREFIX+".loaded"; |
| |
| /** |
| * The <i>com.sun.jmx.examples.scandir.config.modified</i> notification |
| * indicates that the configuration data was modified. |
| **/ |
| public final static String NOTIFICATION_MODIFIED = |
| NOTIFICATION_PREFIX+".modified"; |
| |
| // The array of MBeanNotificationInfo that will be exposed in the |
| // ScanDirConfigMXBean MBeanInfo. |
| // We will pass this array to the NotificationBroadcasterSupport |
| // constructor. |
| // |
| private static MBeanNotificationInfo[] NOTIFICATION_INFO = { |
| new MBeanNotificationInfo( |
| new String[] {NOTIFICATION_SAVED}, |
| Notification.class.getName(), |
| "Emitted when the configuration is saved"), |
| new MBeanNotificationInfo( |
| new String[] {NOTIFICATION_LOADED}, |
| Notification.class.getName(), |
| "Emitted when the configuration is loaded"), |
| new MBeanNotificationInfo( |
| new String[] {NOTIFICATION_MODIFIED}, |
| Notification.class.getName(), |
| "Emitted when the configuration is modified"), |
| }; |
| |
| // The ScanDirConfigMXBean configuration data. |
| private volatile ScanManagerConfig config; |
| |
| // The name of the configuration file |
| private String filename = null; |
| |
| // The name of this configuration. This is usually both equal to |
| // config.getName() and objectName.getKeyProperty(name). |
| private volatile String configname = null; |
| |
| // This object save state. CREATED is the initial state. |
| // |
| private volatile SaveState status = CREATED; |
| |
| /** |
| * Creates a new {@link ScanDirConfigMXBean}. |
| * <p>{@code ScanDirConfigMXBean} can be created by the {@link |
| * ScanManagerMXBean}, or directly by a remote client, using |
| * {@code createMBean} or {@code registerMBean}. |
| * </p> |
| * <p>{@code ScanDirConfigMXBean} created by the {@link |
| * ScanManagerMXBean} will be unregistered by the |
| * {@code ScanManagerMXBean}. {@code ScanDirConfigMXBean} created |
| * directly by a remote client will not be unregistered by the |
| * {@code ScanManagerMXBean} - this will remain to the responsibility of |
| * the code/client that created them. |
| * </p> |
| * <p>This object is created empty, you should call load() if you want it |
| * to load its data from the configuration file. |
| * </p> |
| * @param filename The configuration file used by this MBean. |
| * Can be null (in which case load() and save() will fail). |
| * Can point to a file that does not exists yet (in which case |
| * load() will fail if called before save(), and save() will |
| * attempt to create that file). Can point to an existing file, |
| * in which case load() will load that file and save() will save |
| * to that file. |
| * |
| **/ |
| public ScanDirConfig(String filename) { |
| this(filename,null); |
| } |
| |
| /** |
| * Create a new ScanDirConfig MBean with an initial configuration. |
| * @param filename The name of the configuration file. |
| * @param initialConfig an initial configuration. |
| **/ |
| public ScanDirConfig(String filename, ScanManagerConfig initialConfig) { |
| super(NOTIFICATION_INFO); |
| this.filename = filename; |
| this.config = initialConfig; |
| } |
| |
| |
| // see ScanDirConfigMXBean |
| public void load() throws IOException { |
| if (filename == null) |
| throw new UnsupportedOperationException("load"); |
| |
| synchronized(this) { |
| config = new XmlConfigUtils(filename).readFromFile(); |
| if (configname != null) config = config.copy(configname); |
| else configname = config.getName(); |
| |
| status=LOADED; |
| } |
| sendNotification(NOTIFICATION_LOADED); |
| } |
| |
| // see ScanDirConfigMXBean |
| public void save() throws IOException { |
| if (filename == null) |
| throw new UnsupportedOperationException("load"); |
| synchronized (this) { |
| new XmlConfigUtils(filename).writeToFile(config); |
| status = SAVED; |
| } |
| sendNotification(NOTIFICATION_SAVED); |
| } |
| |
| // see ScanDirConfigMXBean |
| public ScanManagerConfig getConfiguration() { |
| synchronized (this) { |
| return XmlConfigUtils.xmlClone(config); |
| } |
| } |
| |
| |
| // sends a notification indicating the new save state. |
| private void sendNotification(String type) { |
| final Object source = (objectName==null)?this:objectName; |
| final Notification n = new Notification(type,source, |
| getNextSeqNumber(), |
| "The configuration is "+ |
| type.substring(type.lastIndexOf('.')+1)); |
| sendNotification(n); |
| } |
| |
| |
| /** |
| * Allows the MBean to perform any operations it needs before being |
| * registered in the MBean server. If the name of the MBean is not |
| * specified, the MBean can provide a name for its registration. If |
| * any exception is raised, the MBean will not be registered in the |
| * MBean server. |
| * @param server The MBean server in which the MBean will be registered. |
| * @param name The object name of the MBean. This name is null if the |
| * name parameter to one of the createMBean or registerMBean methods in |
| * the MBeanServer interface is null. In that case, this method will |
| * try to guess its MBean name by examining its configuration data. |
| * If its configuration data is null (nothing was provided in the |
| * constructor) or doesn't contain a name, this method returns {@code null}, |
| * and registration will fail. |
| * <p> |
| * Otherwise, if {@code name} wasn't {@code null} or if a default name could |
| * be constructed, the name of the configuration will be set to |
| * the value of the ObjectName's {@code name=} key, and the configuration |
| * data will always be renamed to reflect this change. |
| * </p> |
| * |
| * @return The name under which the MBean is to be registered. |
| * @throws Exception This exception will be caught by the MBean server and |
| * re-thrown as an MBeanRegistrationException. |
| */ |
| public ObjectName preRegister(MBeanServer server, ObjectName name) |
| throws Exception { |
| if (name == null) { |
| if (config == null) return null; |
| if (config.getName() == null) return null; |
| name = ScanManager. |
| makeMBeanName(ScanDirConfigMXBean.class,config.getName()); |
| } |
| objectName = name; |
| mbeanServer = server; |
| synchronized (this) { |
| configname = name.getKeyProperty("name"); |
| if (config == null) config = new ScanManagerConfig(configname); |
| else config = config.copy(configname); |
| } |
| return name; |
| } |
| |
| /** |
| * Allows the MBean to perform any operations needed after having |
| * been registered in the MBean server or after the registration has |
| * failed. |
| * <p>This implementation does nothing</p> |
| * @param registrationDone Indicates whether or not the MBean has been |
| * successfully registered in the MBean server. The value false means |
| * that the registration has failed. |
| */ |
| public void postRegister(Boolean registrationDone) { |
| // Nothing to do here. |
| } |
| |
| /** |
| * Allows the MBean to perform any operations it needs before being |
| * unregistered by the MBean server. |
| * <p>This implementation does nothing</p> |
| * @throws Exception This exception will be caught by the MBean server and |
| * re-thrown as an MBeanRegistrationException. |
| */ |
| public void preDeregister() throws Exception { |
| // Nothing to do here. |
| } |
| |
| /** |
| * Allows the MBean to perform any operations needed after having been |
| * unregistered in the MBean server. |
| * <p>This implementation does nothing</p> |
| */ |
| public void postDeregister() { |
| // Nothing to do here. |
| } |
| |
| // see ScanDirConfigMXBean |
| public String getConfigFilename() { |
| return filename; |
| } |
| |
| // see ScanDirConfigMXBean |
| public void setConfiguration(ScanManagerConfig config) { |
| synchronized (this) { |
| if (config == null) { |
| this.config = null; |
| return; |
| } |
| |
| if (configname == null) |
| configname = config.getName(); |
| |
| this.config = config.copy(configname); |
| status = MODIFIED; |
| } |
| sendNotification(NOTIFICATION_MODIFIED); |
| } |
| |
| // see ScanDirConfigMXBean |
| public DirectoryScannerConfig |
| addDirectoryScanner(String name, String dir, String filePattern, |
| long sizeExceedsMaxBytes, long sinceLastModified) { |
| final DirectoryScannerConfig scanner = |
| new DirectoryScannerConfig(name); |
| scanner.setRootDirectory(dir); |
| if (filePattern!=null||sizeExceedsMaxBytes>0||sinceLastModified>0) { |
| final FileMatch filter = new FileMatch(); |
| filter.setFilePattern(filePattern); |
| filter.setSizeExceedsMaxBytes(sizeExceedsMaxBytes); |
| if (sinceLastModified > 0) |
| filter.setLastModifiedBefore(new Date(new Date().getTime() |
| -sinceLastModified)); |
| scanner.addIncludeFiles(filter); |
| } |
| synchronized (this) { |
| config.putScan(scanner); |
| status = MODIFIED; |
| } |
| LOG.fine("config: "+config); |
| sendNotification(NOTIFICATION_MODIFIED); |
| return scanner; |
| } |
| |
| // see ScanDirConfigMXBean |
| public DirectoryScannerConfig removeDirectoryScanner(String name) |
| throws IOException, InstanceNotFoundException { |
| final DirectoryScannerConfig scanner; |
| synchronized (this) { |
| scanner = config.removeScan(name); |
| if (scanner == null) |
| throw new IllegalArgumentException(name+": scanner not found"); |
| status = MODIFIED; |
| } |
| sendNotification(NOTIFICATION_MODIFIED); |
| return scanner; |
| } |
| |
| // see ScanDirConfigMXBean |
| public SaveState getSaveState() { |
| return status; |
| } |
| |
| // These methods are used by ScanManager to guess a configuration name from |
| // a configuration filename. |
| // |
| static String DEFAULT = "DEFAULT"; |
| |
| private static String getBasename(String name) { |
| final int dot = name.indexOf('.'); |
| if (dot<0) return name; |
| if (dot==0) return getBasename(name.substring(1)); |
| return name.substring(0,dot); |
| } |
| |
| static String guessConfigName(String configFileName,String defaultFile) { |
| try { |
| if (configFileName == null) return DEFAULT; |
| final File f = new File(configFileName); |
| if (f.canRead()) { |
| final String confname = XmlConfigUtils.read(f).getName(); |
| if (confname != null && confname.length()>0) return confname; |
| } |
| final File f2 = new File(defaultFile); |
| if (f.equals(f2)) return DEFAULT; |
| final String guess = getBasename(f.getName()); |
| if (guess == null) return DEFAULT; |
| if (guess.length()==0) return DEFAULT; |
| return guess; |
| } catch (Exception x) { |
| return DEFAULT; |
| } |
| } |
| |
| // Set by preRegister() |
| private volatile MBeanServer mbeanServer; |
| private volatile ObjectName objectName; |
| |
| } |