/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.test.jmx.xmbean;
import java.beans.PropertyEditor;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.Descriptor;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.modelmbean.ModelMBeanAttributeInfo;
import javax.management.modelmbean.ModelMBeanInfo;
import org.jboss.mx.metadata.MBeanInfoConversion;
import org.jboss.mx.modelmbean.ModelMBeanConstants;
import org.jboss.mx.modelmbean.ModelMBeanInvoker;
import org.jboss.mx.persistence.PersistenceManager;
import org.jboss.logging.Logger;
import org.jboss.util.StringPropertyReplacer;
import org.jboss.util.propertyeditor.PropertyEditors;
/**
* XML File Persistence Manager. <p>
*
* Persists the MBean to the file system using an XML file.
*
* The file has to follow this (simple) DTD:
* <pre>
* <!ELEMENT attribute (#PCDATA)>
* <!ATTLIST attribute
* name CDATA #REQUIRED
* type CDATA #REQUIRED
* >
* <!ELEMENT attributes (attribute*)>
* </pre>
*
* @author Heiko.Rupp@cellent.de
* @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>.
* @version $Revision: 81036 $
*/
public class XMLFilePersistenceManager extends Object
implements PersistenceManager
{
protected static Logger log = Logger.getLogger(XMLFilePersistenceManager.class);
/** A flag set to true to prevent attribute updates from within load
* triggering stores.
*/
protected boolean isLoading;
// Constructors --------------------------------------------------
public XMLFilePersistenceManager()
{
super();
}
// Public --------------------------------------------------------
/**
* Loads the attributes from the given file
*
* @param mbean to store to
* @param metadata with file location etc.
* @exception MBeanException
*/
public void load(ModelMBeanInvoker mbean, MBeanInfo metadata)
throws MBeanException
{
log.debug("load, resource:" + mbean.getResource());
if (metadata == null)
{
return;
}
if (log.isTraceEnabled())
log.trace("metadata: " + metadata);
File storeFile = getStoreFile(metadata, false);
if (storeFile == null)
{
return;
}
try
{
AttributeList attributes = new AttributeList();
FileInputStream fis = new FileInputStream(storeFile);
BufferedReader buf = new BufferedReader(new InputStreamReader(fis));
String line, line2;
while ((line = buf.readLine()) != null)
{
log.trace("Line: " + line);
if (line.equals("<attributes>"))
continue;
if (line.equals("</attributes>"))
continue;
// trim leading spaces
line = line.substring(line.indexOf("<"));
if (!line.startsWith("<attribute"))
{
log.warn("Unknown line, skipping " + line);
continue;
}
// read attribute name
int pos = line.indexOf("name=\"") + 6; // skip name="
line2 = line.substring(pos);
int pos2 = line2.indexOf("\"");
String name = line2.substring(0, pos2);
log.debug("name: " + name);
// read attribute type
pos = line.indexOf("type=\"") + 6; // skip type="
line2 = line.substring(pos);
pos2 = line2.indexOf("\"");
String type = line2.substring(0, pos2);
log.debug("type: " + type);
// read attribute value
pos = line.indexOf(">");
pos2 = line.lastIndexOf("<");
String value = line.substring(pos + 1, pos2);
log.debug("value :" + value);
Object oVal = convert(value, type);
Attribute att = new Attribute(name, oVal);
attributes.add(att);
} // while
mbean.setAttributes(attributes);
}
catch (Exception e)
{
log.error("Error loading MBean state", e);
}
setIsLoading(false);
}
/** What we need to get here is 1) the persist location, and 2) the entire
* contents of the mbean. #2 contains the entire contents (state) of the
* model object, as well as the meta data that the mbean provides.
* As such, serializing this (MBeanInfo) object (brute force) in effect
* serializes the model as well.
*
* @param metadata
* @exception MBeanException
*/
public void store(MBeanInfo metadata) throws MBeanException
{
if (isLoading())
{
return;
}
ModelMBeanInfo mmeta =
MBeanInfoConversion.toModelMBeanInfo(metadata, true);
log.debug("store");
if (log.isTraceEnabled())
log.trace("metadata: " + metadata);
File storeFile = getStoreFile(metadata, true);
if (storeFile == null)
{
return;
}
try
{
log.debug("Storing to file: " + storeFile.getAbsolutePath());
FileOutputStream fos = new FileOutputStream(storeFile);
MBeanAttributeInfo[] mais = mmeta.getAttributes();
StringBuffer buf = new StringBuffer();
buf.append("<attributes>\n");
for (int i = 0; i < mais.length; i++)
{
ModelMBeanAttributeInfo mai = (ModelMBeanAttributeInfo) mais[i];
buf.append(" <attribute name=\"" + mai.getName() + "\" ");
buf.append("type=\"" + mai.getType() + "\">");
log.debug("Trying to load " + mai.getName());
Descriptor aDesc = mai.getDescriptor();
if (aDesc==null)
throw new Exception("aDesc is null");
log.debug(aDesc.toString());
Object att = aDesc.getFieldValue(ModelMBeanConstants.ATTRIBUTE_VALUE);
if (att!=null)
buf.append(att.toString());
else
log.warn("att was null");
buf.append("</attribute>\n");
}
buf.append("</attributes>");
log.trace(buf.toString());
fos.write(buf.toString().getBytes());
fos.close();
}
catch (Exception e)
{
e.printStackTrace();
throw new MBeanException(e, "Error in persisting MBean.");
}
}
// Protected -----------------------------------------------------
protected boolean isLoading()
{
return isLoading;
}
protected void setIsLoading(boolean newIsLoading)
{
isLoading = newIsLoading;
}
/**
* Obtain the store location from the ModelMBean Descriptor.
* If the file name does not end on ".xml", it will be converted
* to do so.
* @param metadata
* @param createFile
* @return
* @throws MBeanException
*/
protected File getStoreFile(MBeanInfo metadata, boolean createFile)
throws MBeanException
{
Descriptor d = ((ModelMBeanInfo) metadata).getMBeanDescriptor();
String dirPath =
(String) d.getFieldValue(ModelMBeanConstants.PERSIST_LOCATION);
String file = (String) d.getFieldValue(ModelMBeanConstants.PERSIST_NAME);
if (dirPath == null)
{
log.debug(
"No "
+ ModelMBeanConstants.PERSIST_LOCATION
+ " descriptor value found, using '.'");
dirPath = ".";
}
if (file == null)
{
log.debug(
"No "
+ ModelMBeanConstants.PERSIST_NAME
+ " descriptor value found");
return null;
}
dirPath = StringPropertyReplacer.replaceProperties(dirPath);
file = StringPropertyReplacer.replaceProperties(file);
// tack on .xml if not there
if (!file.endsWith(".xml"))
file = file + ".xml";
File dir = new File(dirPath);
File storeFile = new File(dir, file);
boolean exists = storeFile.exists();
log.debug("Store file is: " + storeFile.getAbsolutePath());
if (exists == false && createFile == true)
{
dir.mkdirs();
try
{
storeFile.createNewFile();
}
catch (IOException e)
{
throw new MBeanException(e, "Failed to create store file");
}
}
else if (exists == false)
{
storeFile = null;
}
return storeFile;
}
/**
* Convert val into an Object of type type -- same as in twiddle.setCommand
* @param val The given value
* @param oType the wanted return type
* @return the value in the correct representation
* @throws Exception various :)
*/
private Object convert(String val, String oType) throws Exception
{
PropertyEditor editor = PropertyEditors.getEditor(oType);
editor.setAsText(val);
return editor.getValue();
}
}