| /* |
| * 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.config; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.util.logging.Logger; |
| import javax.xml.bind.JAXBContext; |
| import javax.xml.bind.JAXBException; |
| import javax.xml.bind.Marshaller; |
| import javax.xml.bind.Unmarshaller; |
| |
| /** |
| * The class XmlConfigUtils is used to deal with XML serialization |
| * and XML files. |
| * |
| * @author Sun Microsystems, 2006 - All rights reserved. |
| */ |
| public class XmlConfigUtils { |
| |
| /** |
| * A URI for our XML configuration namespace. This doesn't start with |
| * http:// because we are not going to publish this private schema |
| * anywhere. |
| **/ |
| public static final String NAMESPACE = |
| "jmx:com.sun.jmx.examples.scandir.config"; |
| /** |
| * A logger for this class. |
| **/ |
| private static final Logger LOG = |
| Logger.getLogger(XmlConfigUtils.class.getName()); |
| |
| // Our JAXBContext. |
| private static JAXBContext context; |
| |
| // The file name of the XML file in which an instance of this object |
| // will read and write XML data. |
| final String file; |
| |
| /** |
| * Creates a new instance of XmlConfigUtils. |
| * @param file The file name of the XML file in which an instance of this |
| * object will read and write XML data. |
| */ |
| public XmlConfigUtils(String file) { |
| this.file = file; |
| } |
| |
| /** |
| * Write the given bean to the XML file. |
| * <p> |
| * Performs an atomic write, first writing in {@code <file>.new}, then |
| * renaming {@code <file>} to {@code <file>~}, then renaming |
| * renaming {@code <file>.new} to {@code <file>}. |
| * </p> |
| * @param bean The configuration to write in the XML file. |
| * @throws IOException if write to file failed. |
| **/ |
| public synchronized void writeToFile(ScanManagerConfig bean) |
| throws IOException { |
| |
| // Creates a new file named <file>.new |
| final File f = newXmlTmpFile(file); |
| try { |
| final FileOutputStream out = new FileOutputStream(f); |
| boolean failed = true; |
| try { |
| // writes to <file>.new |
| write(bean,out,false); |
| |
| // no exception: set failed=false for finaly {} block. |
| failed = false; |
| } finally { |
| out.close(); |
| // An exception was raised: delete temporary file. |
| if (failed == true) f.delete(); |
| } |
| |
| // rename <file> to <file>~ and <file>.new to <file> |
| commit(file,f); |
| } catch (JAXBException x) { |
| final IOException io = |
| new IOException("Failed to write SessionConfigBean to " + |
| file+": "+x,x); |
| throw io; |
| } |
| } |
| |
| /** |
| * Creates an XML string representation of the given bean. |
| * @throws IllegalArgumentException if the bean class is not known by the |
| * underlying XMLbinding context. |
| * @return An XML string representation of the given bean. |
| **/ |
| public static String toString(Object bean) { |
| try { |
| final ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| final Marshaller m = createMarshaller(); |
| m.setProperty(m.JAXB_FRAGMENT,Boolean.TRUE); |
| m.marshal(bean, baos); |
| return baos.toString(); |
| } catch (JAXBException x) { |
| final IllegalArgumentException iae = |
| new IllegalArgumentException( |
| "Failed to write SessionConfigBean: "+x,x); |
| throw iae; |
| } |
| } |
| |
| /** |
| * Creates an XML clone of the given bean. |
| * <p> |
| * In other words, this method XML-serializes the given bean, and |
| * XML-deserializes a copy of that bean. |
| * </p> |
| * @return A deep-clone of the given bean. |
| * @throws IllegalArgumentException if the bean class is not known by the |
| * underlying XML binding context. |
| * @param bean The bean to clone. |
| */ |
| public static ScanManagerConfig xmlClone(ScanManagerConfig bean) { |
| final Object clone = copy(bean); |
| return (ScanManagerConfig)clone; |
| } |
| |
| /** |
| * Creates an XML clone of the given bean. |
| * <p> |
| * In other words, this method XML-serializes the given bean, and |
| * XML-deserializes a copy of that bean. |
| * </p> |
| * @throws IllegalArgumentException if the bean class is not known by the |
| * underlying XML binding context. |
| * @return A deep-clone of the given bean. |
| **/ |
| private static Object copy(Object bean) { |
| try { |
| final ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| final Marshaller m = createMarshaller(); |
| m.marshal(bean, baos); |
| final ByteArrayInputStream bais = |
| new ByteArrayInputStream(baos.toByteArray()); |
| return createUnmarshaller().unmarshal(bais); |
| } catch (JAXBException x) { |
| final IllegalArgumentException iae = |
| new IllegalArgumentException("Failed to write SessionConfigBean: "+x,x); |
| throw iae; |
| } |
| } |
| |
| /** |
| * Creates an XML clone of the given bean. |
| * <p> |
| * In other words, this method XML-serializes the given bean, and |
| * XML-deserializes a copy of that bean. |
| * </p> |
| * @return A deep-clone of the given bean. |
| * @throws IllegalArgumentException if the bean class is not known by the |
| * underlying XML binding context. |
| * @param bean The bean to clone. |
| */ |
| public static DirectoryScannerConfig xmlClone(DirectoryScannerConfig bean) { |
| final Object clone = copy(bean); |
| return (DirectoryScannerConfig)clone; |
| } |
| |
| /** |
| * Reads the configuration from the XML configuration file. |
| * @throws IOException if it fails to read the configuration. |
| * @return A {@code ScanManagerConfig} bean read from the |
| * XML configuration file. |
| **/ |
| public synchronized ScanManagerConfig readFromFile() throws IOException { |
| final File f = new File(file); |
| if (!f.exists()) |
| throw new IOException("No such file: "+file); |
| if (!f.canRead()) |
| throw new IOException("Can't read file: "+file); |
| try { |
| return read(f); |
| } catch (JAXBException x) { |
| final IOException io = |
| new IOException("Failed to read SessionConfigBean from " + |
| file+": "+x,x); |
| throw io; |
| } |
| } |
| |
| /** |
| * Reads the configuration from the given XML configuration file. |
| * @param f the file to read from. |
| * @return A {@code ScanManagerConfig} bean read from the |
| * XML configuration file. |
| * @throws javax.xml.bind.JAXBException if it fails to read the configuration. |
| */ |
| public static ScanManagerConfig read(File f) |
| throws JAXBException { |
| final Unmarshaller u = createUnmarshaller(); |
| return (ScanManagerConfig) u.unmarshal(f); |
| |
| } |
| |
| /** |
| * Writes the given bean to the given output stream. |
| * @param bean the bean to write. |
| * @param os the output stream to write to. |
| * @param fragment whether the {@code <?xml ... ?>} header should be |
| * included. The header is not included if the bean is just an |
| * XML fragment encapsulated in a higher level XML element. |
| * @throws JAXBException An XML Binding exception occurred. |
| **/ |
| public static void write(ScanManagerConfig bean, OutputStream os, |
| boolean fragment) |
| throws JAXBException { |
| writeXml((Object)bean,os,fragment); |
| } |
| |
| /** |
| * Writes the given bean to the given output stream. |
| * @param bean the bean to write. |
| * @param os the output stream to write to. |
| * @param fragment whether the {@code <?xml ... ?>} header should be |
| * included. The header is not included if the bean is just an |
| * XML fragment encapsulated in a higher level XML element. |
| * @throws JAXBException An XML Binding exception occurred. |
| **/ |
| public static void write(ResultRecord bean, OutputStream os, boolean fragment) |
| throws JAXBException { |
| writeXml((Object)bean,os,fragment); |
| } |
| |
| /** |
| * Writes the given bean to the given output stream. |
| * @param bean the bean to write. |
| * @param os the output stream to write to. |
| * @param fragment whether the {@code <?xml ... ?>} header should be |
| * included. The header is not included if the bean is just an |
| * XML fragment encapsulated in a higher level XML element. |
| * @throws JAXBException An XML Binding exception occurred. |
| **/ |
| private static void writeXml(Object bean, OutputStream os, boolean fragment) |
| throws JAXBException { |
| final Marshaller m = createMarshaller(); |
| if (fragment) m.setProperty(m.JAXB_FRAGMENT,Boolean.TRUE); |
| m.marshal(bean,os); |
| } |
| |
| // Creates a JAXB Unmarshaller. |
| private static Unmarshaller createUnmarshaller() throws JAXBException { |
| return getContext().createUnmarshaller(); |
| } |
| |
| // Creates a JAXB Marshaller - for nicely XML formatted output. |
| private static Marshaller createMarshaller() throws JAXBException { |
| final Marshaller m = getContext().createMarshaller(); |
| m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,Boolean.TRUE); |
| return m; |
| } |
| |
| // Creates a JAXBContext if needed, and returns it. |
| // The JAXBContext instance we create will be able to handle the |
| // ScanManagerConfig and ResultRecord classes, plus all the property |
| // classes they reference (DirectoryScannerBean etc...). |
| // |
| private static synchronized JAXBContext getContext() throws JAXBException { |
| if (context == null) |
| context = JAXBContext.newInstance(ScanManagerConfig.class, |
| ResultRecord.class); |
| return context; |
| } |
| |
| |
| // Creates a new XML temporary file called <basename>.new |
| // This method is used to implement atomic writing to file. |
| // The usual sequence is: |
| // |
| // Final tmp = newXmlTmpFile(basename); |
| // boolean failed = true; |
| // try { |
| // ... write to 'tmp' ... |
| // // no exception: set failed=false for finaly {} block. |
| // failed = false; |
| // } finally |
| // // failed==true means there was an exception and |
| // // commit won't be called... |
| // if (failed==true) tmp.delete(); |
| // } |
| // commit(tmp,basename) |
| // |
| private static File newXmlTmpFile(String basename) throws IOException { |
| final File f = new File(basename+".new"); |
| if (!f.createNewFile()) |
| throw new IOException("file "+f.getName()+" already exists"); |
| |
| try { |
| final OutputStream newStream = new FileOutputStream(f); |
| try { |
| final String decl = |
| "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"; |
| newStream.write(decl.getBytes("UTF-8")); |
| newStream.flush(); |
| } finally { |
| newStream.close(); |
| } |
| } catch (IOException x) { |
| f.delete(); |
| throw x; |
| } |
| return f; |
| } |
| |
| // Commit the temporary file by renaming <basename> to <baesname>~ |
| // and tmpFile to <basename>. |
| private static File commit(String basename, File tmpFile) |
| throws IOException { |
| try { |
| final String backupName = basename+"~"; |
| final File desired = new File(basename); |
| final File backup = new File(backupName); |
| backup.delete(); |
| if (desired.exists()) { |
| if (!desired.renameTo(new File(backupName))) |
| throw new IOException("can't rename to "+backupName); |
| } |
| if (!tmpFile.renameTo(new File(basename))) |
| throw new IOException("can't rename to "+basename); |
| } catch (IOException x) { |
| tmpFile.delete(); |
| throw x; |
| } |
| return new File(basename); |
| } |
| |
| /** |
| * Creates a new committed XML file for {@code <basename>}, containing only |
| * the {@code <?xml ...?>} header. |
| * <p>This method will rename {@code <basename>} to {@code <basename>~}, |
| * if it exists. |
| * </p> |
| * @return A newly created XML file containing the regular |
| * {@code <?xml ...?>} header. |
| * @param basename The name of the new file. |
| * @throws IOException if the new XML file couldn't be created. |
| */ |
| public static File createNewXmlFile(String basename) throws IOException { |
| return commit(basename,newXmlTmpFile(basename)); |
| } |
| |
| } |