/* * JBoss, Home of Professional Open Source * Copyright 2009 Red Hat Inc. and/or its affiliates and other * contributors as indicated by the @author tags. All rights reserved. * See the copyright.txt 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.infinispan.config; import org.infinispan.Version; import org.infinispan.config.parsing.NamespaceFilter; import org.infinispan.config.parsing.XmlConfigurationParser; import org.infinispan.configuration.parsing.Parser; import org.infinispan.util.FileLookup; import org.infinispan.util.FileLookupFactory; import org.infinispan.util.StringPropertyReplacer; import org.infinispan.util.SysPropertyActions; import org.infinispan.util.Util; import org.infinispan.util.logging.Log; import org.infinispan.util.logging.LogFactory; import org.xml.sax.InputSource; import org.xml.sax.XMLFilter; import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLReaderFactory; import javax.xml.bind.*; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.SchemaFactory; import java.io.*; import java.lang.ref.SoftReference; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * InfinispanConfiguration encapsulates root component of Infinispan XML configuration. Can be empty * for sensible defaults throughout, however that would only give you the most basic of local, * non-clustered caches. * * @author Vladimir Blagojevic * @since 4.0 * @deprecated Use {@link Parser} instead */ @XmlRootElement(name = "infinispan") @XmlAccessorType(XmlAccessType.FIELD) @ConfigurationDoc(name = "infinispan") @Deprecated public class InfinispanConfiguration implements XmlConfigurationParser, JAXBUnmarshallable { private static final Log log = LogFactory.getLog(InfinispanConfiguration.class); public static final String VALIDATING_SYSTEM_PROPERTY = "infinispan.config.validate"; public static final String SKIP_TOKEN_REPLACEMENT = "infinispan.config.skipTokenReplacement"; public static final String SCHEMA_SYSTEM_PROPERTY = "infinispan.config.schema"; private static final String DEFAULT_SCHEMA_LOCATION = String.format("schema/infinispan-config-%s.xsd", Version.MAJOR_MINOR); public static final String SCHEMA_URL_SYSTEM_PROPERTY = "infinispan.config.schema.url"; private static final String DEFAULT_SCHEMA_URL = String.format("http://www.infinispan.org/schemas/infinispan-config-%s.xsd", Version.MAJOR_MINOR); private static SoftReference<JAXBContext> jaxbContextReference; @XmlElement private final GlobalConfiguration global = new GlobalConfiguration(); @XmlElement(name = "default") private final Configuration defaultConfiguration = new Configuration(); @XmlElement(name = "namedCache") private final List<Configuration> namedCaches = new ArrayList<Configuration>(); /** * Factory method to create an instance of Infinispan configuration. If users want to verify configuration file * correctness against Infinispan schema then appropriate schema file name should be provided as well. * <p/> * Both configuration file and schema file are looked up in following order: * <p/> * <ol> <li> using the specified ClassLoader</li> <li> if fails, the system ClassLoader</li> <li> if * fails, attempt is made to load it as a file from the disk </li> </ol> * * @param configFileName configuration file name * @param schemaFileName schema file name * @return infinispan configuration * @throws IOException if there are any issues creating InfinispanConfiguration object */ public static InfinispanConfiguration newInfinispanConfiguration(String configFileName, String schemaFileName, ClassLoader cl) throws IOException { return newInfinispanConfiguration(configFileName, schemaFileName, null, cl); } /** * Factory method to create an instance of Infinispan configuration. If users want to verify configuration file * correctness against Infinispan schema then appropriate schema file name should be provided as well. * <p/> * Both configuration file and schema file are looked up in following order: * <p/> * <ol> <li> using specified ClassLoader</li> <li> if fails, the system ClassLoader</li> <li> if * fails, attempt is made to load it as a file from the disk </li> </ol> * * @param configFileName configuration file name * @param schemaFileName schema file name * @param cbv configuration bean visitor passed to constructed InfinispanConfiguration * @return infinispan configuration * @throws IOException if there are any issues creating InfinispanConfiguration object */ public static InfinispanConfiguration newInfinispanConfiguration(String configFileName, String schemaFileName, ConfigurationBeanVisitor cbv, ClassLoader cl) throws IOException { InputStream inputStream = configFileName != null ? findInputStream(configFileName, cl) : null; InputStream schemaIS = schemaFileName != null ? findSchemaInputStream(schemaFileName) : null; try { return newInfinispanConfiguration(inputStream, schemaIS, cbv); } finally { Util.close(inputStream, schemaIS); } } /** * Factory method to create an instance of Infinispan configuration. * <p/> * Configuration file is looked up in following order: * <p/> * <ol> <li> using the specified ClassLoader</li> <li> if fails, the system ClassLoader</li> <li> if * fails, attempt is made to load it as a file from the disk </li> </ol> * * @param configFileName configuration file name * @return returns infinispan configuration * @throws IOException if there are any issues creating InfinispanConfiguration object */ public static InfinispanConfiguration newInfinispanConfiguration(String configFileName, ClassLoader cl) throws IOException { return newInfinispanConfiguration(configFileName, null, cl); } /** * Factory method to create an instance of Infinispan configuration. * * @param config configuration input stream * @return returns infinispan configuration * @throws IOException if there are any issues creating InfinispanConfiguration object */ public static InfinispanConfiguration newInfinispanConfiguration(InputStream config) throws IOException { return newInfinispanConfiguration(config, null); } /** * Factory method to create an instance of Infinispan configuration. * * @param config configuration reader * @return returns infinispan configuration * @throws IOException if there are any issues creating InfinispanConfiguration object */ public static InfinispanConfiguration newInfinispanConfiguration(Reader config) throws IOException { return newInfinispanConfiguration(config, null); } /** * Factory method to create an instance of Infinispan configuration. If users want to verify configuration file * correctness against Infinispan schema then appropriate schema input stream should be provided as well. * * @param config configuration input stream * @param schema schema inputstream * @return infinispan configuration * @throws IOException if there are any issues creating InfinispanConfiguration object */ public static InfinispanConfiguration newInfinispanConfiguration(InputStream config, InputStream schema) throws IOException { return newInfinispanConfiguration(config, schema, null); } /** * Factory method to create an instance of Infinispan configuration. If users want to verify configuration file * correctness against Infinispan schema then appropriate schema input stream should be provided as well. * * @param config configuration reader * @param schema schema inputstream * @return infinispan configuration * @throws IOException if there are any issues creating InfinispanConfiguration object */ public static InfinispanConfiguration newInfinispanConfiguration(Reader config, InputStream schema) throws IOException { return newInfinispanConfiguration(config, schema, null); } /** * Factory method to create an instance of Infinispan configuration. If users want to verify configuration file * correctness against Infinispan schema then appropriate schema input stream should be provided as well. * * @param config configuration reader * @param schema schema inputstream * @param cbv configuration bean visitor passed to constructed InfinispanConfiguration * @return infinispan configuration * @throws IOException if there are any issues creating InfinispanConfiguration object */ public static InfinispanConfiguration newInfinispanConfiguration(Reader config, InputStream schema, ConfigurationBeanVisitor cbv) throws IOException { try { Unmarshaller u = getJAXBContext().createUnmarshaller(); NamespaceFilter nf = new NamespaceFilter(); XMLReader reader = XMLReaderFactory.createXMLReader(); nf.setParent(reader); if (schema != null) { SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema"); u.setSchema(factory.newSchema(new StreamSource(schema))); u.setEventHandler(new ValidationEventHandler() { @Override public boolean handleEvent(ValidationEvent event) { int severity = event.getSeverity(); return (severity != ValidationEvent.FATAL_ERROR && severity != ValidationEvent.ERROR); } }); } SAXSource source; if (skipTokenReplacement()) { source = new SAXSource(nf, new InputSource(config)); } else { source = replaceProperties(config, nf); } u.setListener(new Unmarshaller.Listener() { @Override public void beforeUnmarshal(Object target, Object parent) { if (target instanceof JAXBUnmarshallable) { // notify the bean that it is about to be unmarshalled ((JAXBUnmarshallable) target).willUnmarshall(parent); } } }); InfinispanConfiguration ic = (InfinispanConfiguration) u.unmarshal(source); ic.accept(cbv); return ic; } catch (ConfigurationException cex) { throw cex; } catch (NullPointerException npe) { throw npe; } catch (Exception e) { IOException ioe = new IOException(e.getLocalizedMessage()); ioe.initCause(e); throw ioe; } } /** * Factory method to create an instance of Infinispan configuration. If users want to verify configuration file * correctness against Infinispan schema then appropriate schema input stream should be provided as well. * * @param config configuration input stream * @param schema schema inputstream * @param cbv configuration bean visitor passed to constructed InfinispanConfiguration * @return infinispan configuration * @throws IOException if there are any issues creating InfinispanConfiguration object */ public static InfinispanConfiguration newInfinispanConfiguration(InputStream config, InputStream schema, ConfigurationBeanVisitor cbv) throws IOException { return newInfinispanConfiguration(new InputStreamReader(config), schema, cbv); } /** * Returns the JAXB context that may be used to read and write Infinispan configurations. * * @return the JAXB context that may be used to read and write Infinispan configurations. * @throws JAXBException In case of the creation of the context failed. */ protected static synchronized JAXBContext getJAXBContext() throws JAXBException { JAXBContext context = jaxbContextReference != null ? jaxbContextReference.get() : null; if (context == null) { context = JAXBContext.newInstance(InfinispanConfiguration.class); jaxbContextReference = new SoftReference<JAXBContext>(context); } return context; } /** * Converts an instance of GlobalConfiguration, Configuration or InfinispanConfiguration to * its XML representation. * * @param compatibleConfigurationInstance * a configuration instance that support XML marshaling. * @return a string containing the formatted XML representation of the given instance. */ protected static String toXmlString(Object compatibleConfigurationInstance) { try { StringWriter writer = new StringWriter(1024); try { Marshaller m = getJAXBContext().createMarshaller(); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); m.marshal(compatibleConfigurationInstance, writer); return writer.toString(); } finally { writer.close(); } } catch (Exception e) { throw new RuntimeException(e); } } private static SAXSource replaceProperties(Reader config, XMLFilter filter) throws Exception { BufferedReader br = new BufferedReader(config); StringBuilder w = new StringBuilder(); String line; while ((line = br.readLine()) != null) { int dollar = line.indexOf('$'); if (dollar > 0 && line.indexOf('{', dollar) > 0 && line.indexOf('}', dollar) > 0) { String replacedLine = StringPropertyReplacer.replaceProperties(line); if (line.equals(replacedLine)) { log.propertyCouldNotBeReplaced(line.substring(line.indexOf('{') + 1, line.indexOf('}'))); } w.append(replacedLine); } else { w.append(line); } } return new SAXSource(filter, new InputSource(new StringReader(w.toString()))); } private static boolean skipSchemaValidation() { String s = SysPropertyActions.getProperty(VALIDATING_SYSTEM_PROPERTY); return s != null && !Boolean.parseBoolean(s); } private static boolean skipTokenReplacement() { String s = SysPropertyActions.getProperty(SKIP_TOKEN_REPLACEMENT, "false"); return s != null && Boolean.parseBoolean(s); } public static InputStream findSchemaInputStream() { return findSchemaInputStream(null); } public static InputStream findSchemaInputStream(String localPathToSchema) { boolean validating = !skipSchemaValidation(); if (!validating) return null; //1. resolve given path FileLookup fileLookup = FileLookupFactory.newInstance(); InputStream is; if (localPathToSchema != null) { // Schema's are always stored in Infinispan is = fileLookup.lookupFile(localPathToSchema, null); if (is != null) { log.debugf("Using schema %s", localPathToSchema); return is; } if (log.isDebugEnabled()) { log.debugf("Could not find schema on path %s, resolving %s to %s", localPathToSchema, SCHEMA_SYSTEM_PROPERTY, schemaPath()); } } //2. resolve local schema path in infinispan distro is = fileLookup.lookupFile(schemaPath(), null); if (is != null) { log.debugf("Using schema %s", schemaPath()); return is; } if (log.isDebugEnabled()) { log.debugf("Could not find schema on path %s, resolving %s to %s", schemaPath(), SCHEMA_SYSTEM_PROPERTY, schemaURL()); } //3. resolve URL try { is = new URL(schemaURL()).openStream(); log.debugf("Using schema %s", schemaURL()); return is; } catch (Exception e) { } log.couldNotResolveConfigurationSchema(localPathToSchema, schemaPath(), schemaURL()); return null; } public static String resolveSchemaPath() { boolean validating = !skipSchemaValidation(); if (!validating) return null; return schemaPath(); } private static String schemaPath() { return SysPropertyActions.getProperty(SCHEMA_SYSTEM_PROPERTY, DEFAULT_SCHEMA_LOCATION); } private static String schemaURL() { return SysPropertyActions.getProperty(SCHEMA_URL_SYSTEM_PROPERTY, DEFAULT_SCHEMA_URL); } private InfinispanConfiguration() { } public void accept(ConfigurationBeanVisitor v) { if (v != null) { global.accept(v); defaultConfiguration.accept(v); for (Configuration c : namedCaches) { c.accept(v); } v.visitInfinispanConfiguration(this); } } private static InputStream findInputStream(String fileName, ClassLoader cl) throws FileNotFoundException { if (fileName == null) throw new NullPointerException("File name cannot be null!"); FileLookup fileLookup = FileLookupFactory.newInstance(); InputStream is = fileLookup.lookupFile(fileName, cl); if (is == null) throw new FileNotFoundException("File " + fileName + " could not be found, either on the classpath or on the file system!"); return is; } @Override public Configuration parseDefaultConfiguration() throws ConfigurationException { return defaultConfiguration; } @Override public GlobalConfiguration parseGlobalConfiguration() { return global; } @Override public Map<String, Configuration> parseNamedConfigurations() throws ConfigurationException { Map<String, Configuration> map = new HashMap<String, Configuration>(7); for (Configuration conf : namedCaches) { map.put(conf.getName(), conf); } return map; } @Override public void willUnmarshall(Object parent) { // no-op } /** * Converts this configuration instance to an XML representation containing the current settings. * * @return a string containing the formatted XML representation of this configuration instance. */ public String toXmlString() { return toXmlString(this); } }