/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.openejb.config.sys; import org.apache.openejb.config.AppModule; import org.apache.openejb.config.BeanProperties; import org.apache.openejb.config.DeploymentModule; import org.apache.openejb.config.EjbModule; import org.apache.openejb.config.EnvEntriesPropertiesDeployer; import org.apache.openejb.config.PojoConfiguration; import org.apache.openejb.config.WebModule; import org.apache.openejb.loader.SystemInstance; import org.apache.openejb.util.LogCategory; import org.apache.openejb.util.Logger; import org.apache.openejb.util.Saxs; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import javax.xml.parsers.ParserConfigurationException; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Properties; public class SaxAppCtxConfig { private static final Logger LOGGER = Logger.getInstance(LogCategory.OPENEJB_STARTUP_CONFIG, SaxAppContextConfig.class); public static void parse(final AppModule appModule, final InputSource source, final EnvEntriesPropertiesDeployer envEntriesDeployer, final BeanProperties beanProperties) throws SAXException, ParserConfigurationException, IOException { Saxs.factory() .newSAXParser() .parse(source, new SaxAppContextConfig(appModule, envEntriesDeployer, beanProperties)); } private static class SaxAppContextConfig extends StackHandler { private static final Collection<String> IMPORT_ALIASES = Arrays.asList("import", "include"); private static final Collection<String> APPLICATION_ALIASES = Arrays.asList("appcontext", "app-context", "application"); private static final Collection<String> POJOS_ALIASES = Arrays.asList("pojocontexts", "pojo-contexts", "pojos"); private static final Collection<String> POJO_ALIASES = Arrays.asList("pojo"); private static final Collection<String> BEAN_CONTEXTS_ALIASES = Arrays.asList("beancontexts", "bean-contexts", "ejbs"); private static final Collection<String> WEBAPP_ALIASES = Arrays.asList("webapps", "webcontexts", "web-contexts", "wars"); private static final Collection<String> MODULE_ALIASES = Arrays.asList("modulecontext", "module"); private static final Collection<String> BEAN_CONTEXT_ALIASES = Arrays.asList("ejb", "beancontext", "bean-context"); private static final Collection<String> CONFIGURATION_ALIASES = Arrays.asList("configuration", "properties", "settings"); private static final Collection<String> RESOURCES_ALIASES = Arrays.asList("resources"); private static final Collection<String> SERVICE_ALIASES = Arrays.asList("service"); private static final Collection<String> RESOURCE_ALIASES = Arrays.asList("resource"); private static final Collection<String> ENV_ENTRIES_ALIASES = Arrays.asList("enventries", "env-entries"); private static final Collection<String> ENV_ENTRY_ALIASES = Arrays.asList("enventry", "env-entry"); private final AppModule module; private final EnvEntriesPropertiesDeployer envEntriesDeployer; private final BeanProperties beanPropertiesDeployer; public SaxAppContextConfig(final AppModule appModule, final EnvEntriesPropertiesDeployer envEntriesDeployer, final BeanProperties beanProperties) { this.module = appModule; this.envEntriesDeployer = envEntriesDeployer; this.beanPropertiesDeployer = beanProperties; } @Override public void startDocument() throws SAXException { push(new Document()); } private class Document extends DefaultHandler { @Override public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) throws SAXException { // by default don't care about root tag if (!APPLICATION_ALIASES.contains(localName.toLowerCase()) && SystemInstance.get().getOptions().get("openejb.configuration.strict-tags", false)) { throw new IllegalStateException("Unsupported Element: " + localName); } push(new Root()); } } private class Root extends DefaultHandler { @Override public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) throws SAXException { final String name = localName.toLowerCase(); if (CONFIGURATION_ALIASES.contains(name)) { push(new Configuration("", module.getProperties())); } else if (ENV_ENTRIES_ALIASES.contains(name)) { push(new EnvEntries()); } else if (BEAN_CONTEXTS_ALIASES.contains(name)) { push(new BeanContexts(null)); } else if (MODULE_ALIASES.contains(name)) { push(new ModuleContext(attributes.getValue("id"))); } else if (WEBAPP_ALIASES.contains(name)) { push(new WebAppContext(attributes.getValue("id"))); } else if (POJOS_ALIASES.contains(name)) { push(new Pojos()); } else if (RESOURCES_ALIASES.contains(name)) { push(new ResourcesConfig()); } else if (IMPORT_ALIASES.contains(name)) { importFile(attributes.getValue("path")); push(new DefaultHandler()); // just to keep the stack consistent } else { throw new IllegalStateException("Unsupported Element: " + localName); } } private void importFile(final String path) throws SAXException { final File file = new File(path); if (file.exists()) { try (final InputStream inputStream = new FileInputStream(file)) { parse(module, new InputSource(inputStream), envEntriesDeployer, beanPropertiesDeployer); } catch (final ParserConfigurationException | IOException e) { throw new SAXException(e); } } else { // try in the classpath final ClassLoader cl = module.getClassLoader(); if (cl != null) { try (final InputStream is = cl.getResourceAsStream(path)) { if (is != null) { try { parse(module, new InputSource(is), envEntriesDeployer, beanPropertiesDeployer); } catch (final ParserConfigurationException | IOException e) { throw new SAXException(e); } } else { LOGGER.warning("Can't find " + path); } } catch (final IOException e) { LOGGER.warning("Can't find " + path, e); } } else { LOGGER.warning("Can't find " + path + ", no classloader for the module " + module); } } } } private final class Configuration extends Content { private final Properties properties; private final String prefix; private Configuration(final String prefix, final Properties properties) { this.properties = properties; this.prefix = prefix; } @Override public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) { push(new Configuration(prefix + localName + ".", properties)); } @Override public void setValue(final String text) { if (properties == null) { return; } try { for (final Map.Entry<Object, Object> entry : new PropertiesAdapter().unmarshal(text).entrySet()) { properties.put(prefix + entry.getKey(), entry.getValue()); } } catch (final Exception e) { throw new RuntimeException(e); } } } private final class MultipleConfiguration extends Content { private final Collection<Properties> properties; private final String prefix; private MultipleConfiguration(final String prefix, final Collection<Properties> properties) { this.properties = properties; this.prefix = prefix; } @Override public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) { push(new MultipleConfiguration(prefix + localName + ".", properties)); } @Override public void setValue(final String text) { if (properties == null) { return; } try { for (final Properties p : properties) { for (final Map.Entry<Object, Object> entry : new PropertiesAdapter().unmarshal(text).entrySet()) { p.put(prefix + entry.getKey(), entry.getValue()); } } } catch (final Exception e) { throw new RuntimeException(e); } } } private class Pojos extends DefaultHandler { protected final List<PojoConfig> genericConfigs = new ArrayList<PojoConfig>(); protected final Collection<String> aliases; private Pojos() { this(POJO_ALIASES); } protected Pojos(final Collection<String> aliases) { this.aliases = aliases; } @Override public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) throws SAXException { if (aliases.contains(localName.toLowerCase())) { final PojoConfig beanConfig = newItem(); final String id = attributes.getValue("id"); if (id == null || "*".equals(id)) { genericConfigs.add(beanConfig); } push(childParser(id, beanConfig)); } else { throw new IllegalStateException("Unsupported Element: " + localName); } } protected DefaultHandler childParser(final String id, final PojoConfig beanConfig) { return new Pojo(id, beanConfig); } protected PojoConfig newItem() { return new PojoConfig(); } @Override public void endElement(final String uri, final String localName, final String qName) throws SAXException { for (final PojoConfig generic : genericConfigs) { for (final PojoConfiguration config : module.getPojoConfigurations().values()) { for (final String key : generic.getProperties().stringPropertyNames()) { if (!config.getProperties().containsKey(key)) { config.getProperties().put(key, generic.getProperties().get(key)); } } } } } } private class ModuleContext extends DefaultHandler { protected final String id; ModuleContext(final String id) { this.id = id; } @Override public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) throws SAXException { final String name = localName.toLowerCase(); if (BEAN_CONTEXTS_ALIASES.contains(name)) { push(new BeanContexts(id)); } else if (POJOS_ALIASES.contains(name)) { push(new Pojos()); } else if (CONFIGURATION_ALIASES.contains(name)) { push(new MultipleConfiguration("", propertiesForModule(id))); } else { throw new IllegalStateException("Unsupported Element: " + localName); } } protected Collection<Properties> propertiesForModule(final String id) { final Collection<Properties> props = new ArrayList<Properties>(); for (final DeploymentModule m : module.getDeploymentModule()) { if (acceptModule(id, m)) { props.add(m.getProperties()); } } return props; } } private final class WebAppContext extends ModuleContext { private WebAppContext(final String id) { super(id); } @Override protected Collection<Properties> propertiesForModule(final String id) { final Collection<Properties> props = new ArrayList<Properties>(); for (final WebModule m : module.getWebModules()) { if (acceptModule(id, m)) { props.add(m.getProperties()); } } return props; } } private class BeanContexts extends Pojos { private final String id; public BeanContexts(final String id) { super(BEAN_CONTEXT_ALIASES); this.id = id; } @Override protected DefaultHandler childParser(final String beanId, final PojoConfig beanConfig) { return new BeanContext(id, beanId, (BeanContextConfig) beanConfig); } @Override protected PojoConfig newItem() { return new BeanContextConfig(); } @Override public void endElement(final String uri, final String localName, final String qName) throws SAXException { for (final PojoConfig generic : genericConfigs) { // BeanContextConfig if (generic.hasProperties()) { beanPropertiesDeployer.addGlobalProperties(id, generic.getProperties()); } } } } private class Pojo extends DefaultHandler { protected final PojoConfig pojoConfig; protected final String id; public Pojo(final String id, final PojoConfig beanConfig) { this.id = id; pojoConfig = beanConfig; } @Override public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) throws SAXException { if (CONFIGURATION_ALIASES.contains(localName.toLowerCase())) { push(new Configuration("", pojoConfig.getProperties())); } else { throw new IllegalStateException("Unsupported Element: " + localName); } } @Override public void endElement(final String uri, final String localName, final String qName) throws SAXException { module.getPojoConfigurations().put(id, new PojoConfiguration(pojoConfig.getProperties())); } } private class BeanContext extends Pojo { private final String moduleId; public BeanContext(final String moduleId, final String id, final BeanContextConfig beanConfig) { super(id, beanConfig); this.moduleId = moduleId; } @Override public void endElement(final String uri, final String localName, final String qName) throws SAXException { for (final EjbModule ejbModule : module.getEjbModules()) { if (!acceptModule(moduleId, ejbModule)) { continue; } if (pojoConfig.hasProperties()) { beanPropertiesDeployer.addProperties(id, pojoConfig.getProperties()); } } } } private class ResourcesConfig extends DefaultHandler { @Override public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) throws SAXException { final String name = localName.toLowerCase(); if (SERVICE_ALIASES.contains(name)) { push(new DeclaredServiceElement(module.getServices())); } else if (RESOURCE_ALIASES.contains(name)) { push(new ResourceElement(module.getResources())); } else { throw new IllegalStateException("Unsupported Element: " + localName); } get().startElement(uri, localName, qName, attributes); } } private class EnvEntries extends DefaultHandler { @Override public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) throws SAXException { if (ENV_ENTRY_ALIASES.contains(localName.toLowerCase())) { push(new EnvEntry()); get().startElement(uri, localName, qName, attributes); } else { throw new IllegalStateException("Unsupported Element: " + localName); } } } private class EnvEntry extends Content { private String key; @Override public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) { if (ENV_ENTRY_ALIASES.contains(localName.toLowerCase())) { key = attributes.getValue("key"); } else { throw new IllegalStateException("Unsupported Element: " + localName); } } @Override public void setValue(final String text) { envEntriesDeployer.addEnvEntries(key, text); } } } private static boolean acceptModule(final String id, final DeploymentModule ejbModule) { return id == null || id.equals(ejbModule.getModuleId()); } }