| /* |
| * 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 static java.util.Arrays.asList; |
| |
| import org.apache.openejb.OpenEJBException; |
| import org.apache.openejb.config.SystemProperty; |
| import org.apache.openejb.jee.JAXBContextFactory; |
| import org.apache.openejb.loader.IO; |
| import org.apache.openejb.util.Join; |
| import org.apache.openejb.util.Messages; |
| 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.XMLReader; |
| import org.xml.sax.helpers.DefaultHandler; |
| import org.xml.sax.helpers.XMLFilterImpl; |
| |
| import javax.xml.bind.JAXBContext; |
| import javax.xml.bind.JAXBException; |
| import javax.xml.bind.MarshalException; |
| import javax.xml.bind.Marshaller; |
| import javax.xml.bind.Unmarshaller; |
| import javax.xml.bind.ValidationException; |
| import javax.xml.parsers.ParserConfigurationException; |
| import javax.xml.parsers.SAXParser; |
| import javax.xml.transform.sax.SAXSource; |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.TreeSet; |
| |
| public abstract class JaxbOpenejb { |
| |
| @SuppressWarnings({"unchecked"}) |
| public static <T> T create(final Class<T> type) { |
| if (type == null) { |
| throw new NullPointerException("type is null"); |
| } |
| |
| if (type == ConnectionManager.class) { |
| return (T) createConnectionManager(); |
| } else if (type == Connector.class) { |
| return (T) createConnector(); |
| } else if (type == Container.class) { |
| return (T) createContainer(); |
| } else if (type == Deployments.class) { |
| return (T) createDeployments(); |
| } else if (type == JndiProvider.class) { |
| return (T) createJndiProvider(); |
| } else if (type == Openejb.class) { |
| return (T) createOpenejb(); |
| } else if (type == ProxyFactory.class) { |
| return (T) createProxyFactory(); |
| } else if (type == Resource.class) { |
| return (T) createResource(); |
| } else if (type == SecurityService.class) { |
| return (T) createSecurityService(); |
| } else if (type == ServiceProvider.class) { |
| return (T) createServiceProvider(); |
| } else if (type == ServicesJar.class) { |
| return (T) createServicesJar(); |
| } else if (type == TransactionManager.class) { |
| return (T) createTransactionManager(); |
| } else if (type == Tomee.class) { |
| return (T) createTomee(); |
| } |
| throw new IllegalArgumentException("Unknown type " + type.getName()); |
| } |
| |
| public static <T> T create(final String type) { |
| if (type == null) { |
| throw new NullPointerException("type is null"); |
| } |
| |
| if (type.equalsIgnoreCase("ConnectionManager")) { |
| return (T) createConnectionManager(); |
| } else if (type.equalsIgnoreCase("Connector")) { |
| return (T) createConnector(); |
| } else if (type.equalsIgnoreCase("Container")) { |
| return (T) createContainer(); |
| } else if (type.equalsIgnoreCase("Deployments")) { |
| return (T) createDeployments(); |
| } else if (type.equalsIgnoreCase("JndiProvider")) { |
| return (T) createJndiProvider(); |
| } else if (type.equalsIgnoreCase("Openejb")) { |
| return (T) createOpenejb(); |
| } else if (type.equalsIgnoreCase("Tomee")) { |
| return (T) createTomee(); |
| } else if (type.equalsIgnoreCase("ProxyFactory")) { |
| return (T) createProxyFactory(); |
| } else if (type.equalsIgnoreCase("Resource")) { |
| return (T) createResource(); |
| } else if (type.equalsIgnoreCase("SecurityService")) { |
| return (T) createSecurityService(); |
| } else if (type.equalsIgnoreCase("ServiceProvider")) { |
| return (T) createServiceProvider(); |
| } else if (type.equalsIgnoreCase("ServicesJar")) { |
| return (T) createServicesJar(); |
| } else if (type.equalsIgnoreCase("TransactionManager")) { |
| return (T) createTransactionManager(); |
| } else if (type.equalsIgnoreCase("Service")) { |
| return (T) createService(); |
| } else if (type.equalsIgnoreCase("System-Property")) { |
| return (T) createSystemProperty(); |
| } |
| throw new IllegalArgumentException("Unknown type " + type); |
| } |
| |
| public static ServicesJar readServicesJar(final String providerPath) throws OpenEJBException { |
| |
| final ClassLoader loader = Thread.currentThread().getContextClassLoader(); |
| |
| final List<URL> sources = new ArrayList<URL>(); |
| |
| { // Find URLs in the classpath |
| final String path1 = "META-INF/" + providerPath + "/service-jar.xml"; |
| final String path2 = providerPath.replace('.', '/') + "/service-jar.xml"; |
| final List<String> paths = Arrays.asList(path1, path2); |
| |
| for (final String p : paths) { |
| try { |
| final Enumeration<URL> resources = loader.getResources(p); |
| if (resources != null) { |
| sources.addAll(Collections.list(resources)); |
| } |
| } catch (final IOException e) { |
| throw new OpenEJBException(String.format("Unable to scan for service-jar.xml file: getResource('%s')", p), e); |
| } |
| } |
| |
| if (sources.size() == 0) { |
| throw new OpenEJBException("No service-jar.xml files found: searched " + Join.join(" and ", paths)); |
| } |
| } |
| |
| final ServicesJar servicesJar = new ServicesJar(); |
| |
| { |
| for (final URL url : sources) { |
| try { |
| final InputStream read = IO.read(url); |
| try { |
| final ServicesJar jar = parseServicesJar(read); |
| servicesJar.getServiceProvider().addAll(jar.getServiceProvider()); |
| } catch (final Exception e) { |
| throw new OpenEJBException("Unable to parse service-jar.xml file for provider " + providerPath + " at " + url, e); |
| } finally { |
| IO.close(read); |
| } |
| } catch (final IOException e) { |
| throw new OpenEJBException("Unable to read service-jar.xml file for provider " + providerPath + " at " + url, e); |
| } |
| } |
| } |
| |
| return servicesJar; |
| } |
| |
| public static ServicesJar parseServicesJar(final InputStream in) throws ParserConfigurationException, SAXException, IOException { |
| final InputSource inputSource = new InputSource(in); |
| |
| final SAXParser parser = Saxs.namespaceAwareFactory().newSAXParser(); |
| |
| final ServicesJar servicesJar1 = new ServicesJar(); |
| |
| parser.parse(inputSource, new OpenEJBHandler(servicesJar1)); |
| final ServicesJar servicesJar = servicesJar1; |
| return servicesJar; |
| } |
| |
| public static Openejb readConfig(final String configFile) throws OpenEJBException { |
| InputStream in = null; |
| try { |
| if (configFile.startsWith("jar:")) { |
| final URL url = new URL(configFile); |
| in = IO.read(url); |
| } else if (configFile.startsWith("file:")) { |
| final URL url = new URL(configFile); |
| in = IO.read(url); |
| } else { |
| in = IO.read(new File(configFile)); |
| } |
| |
| if (configFile.endsWith(".json")) { |
| return JSonConfigReader.read(Openejb.class, in); |
| } |
| |
| return readConfig(new InputSource(in)); |
| } catch (final MalformedURLException e) { |
| throw new OpenEJBException("Unable to resolve location " + configFile, e); |
| } catch (final Exception e) { |
| throw new OpenEJBException("Unable to read OpenEJB configuration file at " + configFile, e); |
| } finally { |
| IO.close(in); |
| } |
| } |
| |
| public static Openejb readConfig(final InputSource in) throws IOException, SAXException, ParserConfigurationException { |
| return SaxOpenejb.parse(in); |
| } |
| |
| public static void writeConfig(final String configFile, final Openejb openejb) throws OpenEJBException { |
| OutputStream out = null; |
| try { |
| final File file = new File(configFile); |
| out = IO.write(file); |
| marshal(Openejb.class, openejb, out); |
| } catch (final IOException e) { |
| throw new OpenEJBException(messages().format("conf.1040", configFile, e.getLocalizedMessage()), e); |
| } catch (final MarshalException e) { |
| if (e.getCause() instanceof IOException) { |
| throw new OpenEJBException(messages().format("conf.1040", configFile, e.getLocalizedMessage()), e); |
| } else { |
| throw new OpenEJBException(messages().format("conf.1050", configFile, e.getLocalizedMessage()), e); |
| } |
| } catch (final ValidationException e) { |
| /* TODO: Implement informative error handling here. |
| The exception will say "X doesn't match the regular |
| expression Y" |
| This should be checked and more relevant information |
| should be given -- not everyone understands regular |
| expressions. |
| */ |
| /* NOTE: This doesn't seem to ever happen. When the object graph |
| * is invalid, the MarshalException is thrown, not this one as you |
| * would think. |
| */ |
| throw new OpenEJBException(messages().format("conf.1060", configFile, e.getLocalizedMessage()), e); |
| } catch (final JAXBException e) { |
| throw new OpenEJBException(e); |
| } finally { |
| if (out != null) { |
| try { |
| out.close(); |
| } catch (final Exception e) { |
| // no-op |
| } |
| } |
| } |
| } |
| |
| private static Messages messages() { // new is fine cause for errors only |
| return new Messages("org.apache.openejb.util.resources"); |
| } |
| |
| public static final ThreadLocal<Set<String>> currentPublicId = new ThreadLocal<Set<String>>(); |
| |
| private static final Map<Class, JAXBContext> jaxbContexts = new HashMap<Class, JAXBContext>(); |
| |
| public static <T> String marshal(final Class<T> type, final Object object) throws JAXBException { |
| final ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| |
| marshal(type, object, baos); |
| |
| return new String(baos.toByteArray()); |
| } |
| |
| public static <T> void marshal(final Class<T> type, final Object object, final OutputStream out) throws JAXBException { |
| final JAXBContext jaxbContext = getContext(type); |
| final Marshaller marshaller = jaxbContext.createMarshaller(); |
| |
| marshaller.setProperty("jaxb.formatted.output", true); |
| |
| marshaller.marshal(object, out); |
| } |
| |
| public static <T> JAXBContext getContext(final Class<T> type) throws JAXBException { |
| JAXBContext jaxbContext = jaxbContexts.get(type); |
| if (jaxbContext == null) { |
| final Thread thread = Thread.currentThread(); |
| final ClassLoader old = thread.getContextClassLoader(); |
| thread.setContextClassLoader(JaxbOpenejb.class.getClassLoader()); |
| try { |
| jaxbContext = JAXBContextFactory.newInstance(type); |
| } finally { |
| thread.setContextClassLoader(old); |
| } |
| jaxbContexts.put(type, jaxbContext); |
| } |
| return jaxbContext; |
| } |
| |
| public static <T> T unmarshal(final Class<T> type, final InputStream in, final boolean filter, |
| final String expectedNamespace, final String... aliasNamespaces) |
| throws ParserConfigurationException, SAXException, JAXBException { |
| final InputSource inputSource = new InputSource(in); |
| |
| final SAXParser parser = Saxs.namespaceAwareFactory().newSAXParser(); |
| |
| final JAXBContext ctx = getContext(type); |
| final Unmarshaller unmarshaller = ctx.createUnmarshaller(); |
| unmarshaller.setEventHandler(validationEvent -> false); |
| |
| final SAXSource source; |
| if (filter) { |
| final NamespaceFilter xmlFilter = new NamespaceFilter(parser.getXMLReader()); |
| xmlFilter.setContentHandler(unmarshaller.getUnmarshallerHandler()); |
| source = new SAXSource(xmlFilter, inputSource); |
| } else if (expectedNamespace != null) { |
| final WhitelistFilter xmlFilter = new WhitelistFilter(parser.getXMLReader(), expectedNamespace, aliasNamespaces); |
| xmlFilter.setContentHandler(unmarshaller.getUnmarshallerHandler()); |
| source = new SAXSource(xmlFilter, inputSource); |
| } else { |
| source = new SAXSource(inputSource); |
| } |
| |
| currentPublicId.set(new TreeSet<>()); |
| try { |
| return unmarshaller.unmarshal(source, type).getValue(); |
| } finally { |
| currentPublicId.set(null); |
| } |
| } |
| |
| public static <T> T unmarshal(final Class<T> type, final InputStream in, final boolean filter) throws ParserConfigurationException, SAXException, JAXBException { |
| return unmarshal(type, in, filter, null); |
| } |
| |
| @SuppressWarnings({"unchecked"}) |
| public static <T> T unmarshal(final Class<T> type, final InputStream in) throws ParserConfigurationException, SAXException, JAXBException { |
| return unmarshal(type, in, true); |
| } |
| |
| public static class WhitelistFilter extends XMLFilterImpl { |
| |
| private final Set<String> aliases; |
| private final String forcedNamespace; |
| |
| public WhitelistFilter(final XMLReader xmlReader, final String forcedNamespace, final String[] aliases) { |
| super(xmlReader); |
| this.forcedNamespace = forcedNamespace; |
| this.aliases = new HashSet<>(asList(aliases)); |
| } |
| |
| public InputSource resolveEntity(final String publicId, final String systemId) throws SAXException, IOException { |
| final Set<String> publicIds = currentPublicId.get(); |
| if (publicIds != null) { |
| publicIds.add(publicId); |
| } |
| return super.resolveEntity(publicId, systemId); |
| } |
| |
| @Override |
| public void startElement(final String inputUri, final String localName, final String qname, final Attributes atts) throws SAXException { |
| super.startElement(aliases.contains(inputUri) ? forcedNamespace : inputUri, localName, qname, atts); |
| } |
| } |
| |
| public static class NamespaceFilter extends XMLFilterImpl { |
| |
| public NamespaceFilter(final XMLReader xmlReader) { |
| super(xmlReader); |
| } |
| |
| public InputSource resolveEntity(final String publicId, final String systemId) throws SAXException, IOException { |
| final Set<String> publicIds = currentPublicId.get(); |
| if (publicIds != null) { |
| publicIds.add(publicId); |
| } |
| return super.resolveEntity(publicId, systemId); |
| } |
| |
| public void startElement(final String uri, final String localName, final String qname, final Attributes atts) throws SAXException { |
| super.startElement("http://www.openejb.org/System/Configuration", localName, qname, atts); |
| } |
| } |
| |
| |
| public static ConnectionManager createConnectionManager() { |
| return new ConnectionManager(); |
| } |
| |
| public static Connector createConnector() { |
| return new Connector(); |
| } |
| |
| public static Container createContainer() { |
| return new Container(); |
| } |
| |
| public static Deployments createDeployments() { |
| return new Deployments(); |
| } |
| |
| public static Service createService() { |
| return new Service(); |
| } |
| |
| public static SystemProperty createSystemProperty() { |
| return new SystemProperty(); |
| } |
| |
| public static JndiProvider createJndiProvider() { |
| return new JndiProvider(); |
| } |
| |
| public static Openejb createOpenejb() { |
| return new Openejb(); |
| } |
| |
| private static Tomee createTomee() { |
| return new Tomee(); |
| } |
| |
| public static ProxyFactory createProxyFactory() { |
| return new ProxyFactory(); |
| } |
| |
| public static Resource createResource() { |
| return new Resource(); |
| } |
| |
| public static SecurityService createSecurityService() { |
| return new SecurityService(); |
| } |
| |
| public static ServiceProvider createServiceProvider() { |
| return new ServiceProvider(); |
| } |
| |
| public static ServicesJar createServicesJar() { |
| return new ServicesJar(); |
| } |
| |
| public static TransactionManager createTransactionManager() { |
| return new TransactionManager(); |
| } |
| |
| private static class OpenEJBHandler extends DefaultHandler { |
| private final PropertiesAdapter propertiesAdapter = new PropertiesAdapter(); |
| private final ServicesJar servicesJar; |
| |
| private ServiceProvider provider; |
| private StringBuilder content; |
| |
| public OpenEJBHandler(final ServicesJar servicesJar1) { |
| this.servicesJar = servicesJar1; |
| } |
| |
| public void startDocument() throws SAXException { |
| } |
| |
| public void startElement(final String uri, final String localName, final String qName, final Attributes att) throws SAXException { |
| if (!localName.equals("ServiceProvider")) { |
| return; |
| } |
| |
| provider = new ServiceProvider(); |
| provider.setId(att.getValue("", "id")); |
| provider.setService(att.getValue("", "service")); |
| provider.setFactoryName(att.getValue("", "factory-name")); |
| provider.setConstructor(att.getValue("", "constructor")); |
| provider.setClassName(att.getValue("", "class-name")); |
| provider.setParent(att.getValue("", "parent")); |
| final String typesString = att.getValue("", "types"); |
| if (typesString != null) { |
| final ListAdapter listAdapter = new ListAdapter(); |
| final List<String> types = listAdapter.unmarshal(typesString); |
| provider.getTypes().addAll(types); |
| } |
| servicesJar.getServiceProvider().add(provider); |
| } |
| |
| public void characters(final char[] ch, final int start, final int length) throws SAXException { |
| if (content == null) { |
| content = new StringBuilder(); |
| } |
| content.append(ch, start, length); |
| } |
| |
| public void endElement(final String uri, final String localName, final String qName) throws SAXException { |
| if (provider == null || content == null) { |
| return; |
| } |
| |
| try { |
| provider.getProperties().putAll(propertiesAdapter.unmarshal(content.toString())); |
| } catch (final Exception e) { |
| throw new SAXException(e); |
| } |
| provider = null; |
| content = null; |
| } |
| } |
| } |