/* * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.tools.visualvm.core.datasource; import com.sun.tools.visualvm.core.datasupport.Utils; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; /** * Storage for a DataSource. * * @author Jiri Sedlacek */ public final class Storage { private static final String VISUALVM_TMP_DIR = System.getProperty("visualvm.tmpdir"); // NOI18N private static final String TEMPORARY_STORAGE_DIRNAME = "visualvm.dat"; // NOI18N private static final String PERSISTENT_STORAGE_DIRNAME = "repository"; // NOI18N private static final Logger LOGGER = Logger.getLogger(Storage.class.getName()); /** * Default extension for storage file. */ public static final String DEFAULT_PROPERTIES_EXT = ".properties"; // NOI18N private static final Object temporaryStorageDirectoryLock = new Object(); // @GuardedBy temporaryStorageDirectory private static File temporaryStorageDirectory; private static final Object temporaryStorageDirectoryStringLock = new Object(); // @GuardedBy temporaryStorageDirectoryString private static String temporaryStorageDirectoryString; private static final Object persistentStorageDirectoryLock = new Object(); // @GuardedBy persistentStorageDirectory private static File persistentStorageDirectory; private static final Object persistentStorageDirectoryStringLock = new Object(); // @GuardedBy persistentStorageDirectoryString private static String persistentStorageDirectoryString; private final File directory; private final File propertiesFile; private Properties properties; /** * Creates new instance of Storage for storing temporary data. The Storage * directory is initialized by getTemporaryStorageDirectory() value. * * @since VisualVM 1.2 */ public Storage() { this(new File(getTemporaryStorageDirectoryString())); // Do not create immediately } /** * Creates new instance of Storage. * * @param directory directory where storage data will be stored. */ public Storage(File directory) { this(directory, null); } /** * Creates new instance of Storage. * * @param directory directory where storage data will be stored. * @param propertiesFile filename of storage file. */ public Storage(File directory, String propertiesFile) { if (directory == null) throw new NullPointerException("Directory cannot be null"); // NOI18N if (directory.isFile()) throw new IllegalArgumentException("Not a valid directory: " + directory); // NOI18N this.directory = directory; this.propertiesFile = propertiesFile != null ? new File(directory, propertiesFile) : null; } /** * Returns true if storage directory exists, false otherwise. * * @return true if storage directory exists, false otherwise. */ public synchronized boolean directoryExists() { return directory.exists(); } /** * Returns storage directory. * * @return storage directory. */ public synchronized File getDirectory() { if (!Utils.prepareDirectory(directory)) throw new IllegalStateException("Cannot create storage directory " + directory); // NOI18N return directory; } /** * Returns defined custom property. * * @param key property name. * @return defined custom property. */ public String getCustomProperty(String key) { return getCustomProperties(new String[] { key })[0]; } /** * Returns defined custom properties. * * @param keys property names. * @return defined custom properties. */ public synchronized String[] getCustomProperties(String[] keys) { String[] values = new String[keys.length]; Properties prop = getCustomProperties(false); if (prop != null) for (int i = 0; i < keys.length; i++) values[i] = prop.getProperty(keys[i]); return values; } /** * Sets persistent custom property. * Since VisualVM 1.2 clears the property for null value. * * @param key property name. * @param value property value or (since VisualVM 1.2) null */ public void setCustomProperty(String key, String value) { setCustomProperties(new String[] { key }, new String[] { value }); } /** * Sets persistent custom properties. * Since VisualVM 1.2 a property is cleared for null value. * * @param keys property names. * @param values property values. */ public synchronized void setCustomProperties(String[] keys, String[] values) { Properties prop = getCustomProperties(true); for (int i = 0; i < keys.length; i++) if (values[i] != null) prop.put(keys[i], values[i]); else prop.remove(keys[i]); storeCustomProperties(); // NOTE: this could be done lazily if storeCustomProperties() was public } /** * Clears custom property. * * @param key property name * @since VisualVM 1.2 */ public void clearCustomProperty(String key) { clearCustomProperties(new String[] { key }); } /** * Clears custom properties. * * @param keys property names * @since VisualVM 1.2 */ public synchronized void clearCustomProperties(String[] keys) { Properties prop = getCustomProperties(false); if (prop != null) for (int i = 0; i < keys.length; i++) prop.remove(keys[i]); storeCustomProperties(); // NOTE: this could be done lazily if storeCustomProperties() was public } /** * Returns true if the Storage contains any custom properties, false otherwise. * * @return true if the Storage contains any custom properties, false otherwise * @since VisualVM 1.2 */ public synchronized boolean hasCustomProperties() { Properties prop = getCustomProperties(false); return prop != null && !prop.isEmpty(); } /** * Saves persistent custom properties to a file. * * @param file file where the properties will be saved. */ public synchronized void saveCustomPropertiesTo(File file) { if (file == null) throw new NullPointerException("File cannot be null"); // NOI18N if (file.isDirectory()) throw new IllegalArgumentException("Not a valid file: " + file); // NOI18N Properties prop = getCustomProperties(false); if (prop != null && !prop.isEmpty()) storeProperties(prop, file); } /** * Deletes properties file. */ public synchronized void deleteCustomPropertiesStorage() { if (propertiesFile != null && propertiesFile.exists()) if (!propertiesFile.delete()) propertiesFile.deleteOnExit(); } /** * Returns default storage directory for temporary (runtime) DataSource data * * @return default storage directory for temporary (runtime) DataSource data */ public static String getTemporaryStorageDirectoryString() { synchronized(temporaryStorageDirectoryStringLock) { if (temporaryStorageDirectoryString == null) { String tmpDir = VISUALVM_TMP_DIR != null ? VISUALVM_TMP_DIR : System.getProperty("java.io.tmpdir"); // NOI18N temporaryStorageDirectoryString = new File(tmpDir).getAbsolutePath() + File.separator + TEMPORARY_STORAGE_DIRNAME; } return temporaryStorageDirectoryString; } } /** * Returns default storage directory for temporary (runtime) DataSource data. * This directory is deleted when VisualVM session finishes, eventually on * new VisualVM session startup. * * @return default storage directory for temporary (runtime) DataSource data */ public static File getTemporaryStorageDirectory() { synchronized(temporaryStorageDirectoryLock) { if (temporaryStorageDirectory == null) { String temporaryStorageString = getTemporaryStorageDirectoryString(); temporaryStorageDirectory = new File(temporaryStorageString); if (temporaryStorageDirectory.exists() && temporaryStorageDirectory.isFile()) throw new IllegalStateException("Cannot create temporary storage directory " + temporaryStorageString + ", file in the way"); // NOI18N if (temporaryStorageDirectory.exists() && (!temporaryStorageDirectory.canRead() || !temporaryStorageDirectory.canWrite())) throw new IllegalStateException("Cannot access temporary storage directory " + temporaryStorageString + ", read&write permission required"); // NOI18N if (!Utils.prepareDirectory(temporaryStorageDirectory)) throw new IllegalStateException("Cannot create temporary storage directory " + temporaryStorageString); // NOI18N } return temporaryStorageDirectory; } } /** * Returns default storage directory for persistent DataSource data * * @return default storage directory for persistent DataSource data */ public static String getPersistentStorageDirectoryString() { synchronized(persistentStorageDirectoryStringLock) { if (persistentStorageDirectoryString == null) persistentStorageDirectoryString = new File(System.getProperty("netbeans.user")).getAbsolutePath() + File.separator + PERSISTENT_STORAGE_DIRNAME; // NOI18N return persistentStorageDirectoryString; } } /** * Returns default storage directory for persistent DataSource data * * @return default storage directory for persistent DataSource data */ public static File getPersistentStorageDirectory() { synchronized(persistentStorageDirectoryLock) { if (persistentStorageDirectory == null) { String persistentStorageString = getPersistentStorageDirectoryString(); persistentStorageDirectory = new File(persistentStorageString); if (persistentStorageDirectory.exists() && persistentStorageDirectory.isFile()) throw new IllegalStateException("Cannot create persistent storage directory " + persistentStorageString + ", file in the way"); // NOI18N if (persistentStorageDirectory.exists() && (!persistentStorageDirectory.canRead() || !persistentStorageDirectory.canWrite())) throw new IllegalStateException("Cannot access persistent storage directory " + persistentStorageString + ", read&write permission required"); // NOI18N if (!Utils.prepareDirectory(persistentStorageDirectory)) throw new IllegalStateException("Cannot create persistent storage directory " + persistentStorageString); // NOI18N } return persistentStorageDirectory; } } /** * Returns true if persistent storage directory exists, false otherwise. * @return true if persistent storage directory exists, false otherwise. */ public static boolean persistentStorageDirectoryExists() { return new File(getPersistentStorageDirectoryString()).isDirectory(); } private void storeCustomProperties() { if (properties != null && propertiesFile != null) storeProperties(properties, propertiesFile); } private Properties getCustomProperties(boolean createEmpty) { if (properties == null && propertiesFile != null) properties = loadProperties(propertiesFile); if (properties == null && createEmpty) properties = new Properties(); return properties; } private static Properties loadProperties(File file) { if (!file.exists() || !file.isFile()) return null; InputStream is = null; BufferedInputStream bis = null; try { is = new FileInputStream(file); bis = new BufferedInputStream(is); Properties properties = new Properties(); properties.loadFromXML(bis); return properties; } catch (Exception e) { LOGGER.log(Level.SEVERE, "Error loading properties", e); // NOI18N return null; } finally { try { if (bis != null) bis.close(); if (is != null) is.close(); } catch (Exception e) { LOGGER.log(Level.SEVERE, "Problem closing input stream", e); // NOI18N } } } private static void storeProperties(Properties properties, File file) { Utils.prepareDirectory(file.getParentFile()); // Directories may not be created yet OutputStream os = null; BufferedOutputStream bos = null; try { os = new FileOutputStream(file); bos = new BufferedOutputStream(os); properties.storeToXML(os, null); } catch (Exception e) { LOGGER.log(Level.SEVERE, "Error storing properties", e); // NOI18N } finally { try { if (bos != null) bos.close(); if (os != null) os.close(); } catch (Exception e) { LOGGER.log(Level.SEVERE, "Problem closing output stream", e); // NOI18N } } } }