| /* |
| * 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.geode.internal.config; |
| |
| import static org.apache.geode.internal.lang.SystemProperty.getProperty; |
| import static org.apache.geode.internal.lang.SystemPropertyHelper.PACKAGES_TO_SCAN; |
| |
| import java.io.StringReader; |
| import java.io.StringWriter; |
| import java.net.URL; |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.Objects; |
| import java.util.Set; |
| import java.util.stream.Collectors; |
| |
| import javax.xml.XMLConstants; |
| import javax.xml.bind.JAXBContext; |
| import javax.xml.bind.Marshaller; |
| import javax.xml.bind.Unmarshaller; |
| import javax.xml.transform.Source; |
| import javax.xml.transform.sax.SAXSource; |
| import javax.xml.validation.Schema; |
| import javax.xml.validation.SchemaFactory; |
| |
| import org.apache.commons.lang3.StringUtils; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.XMLReader; |
| import org.xml.sax.helpers.XMLReaderFactory; |
| |
| import org.apache.geode.cache.configuration.XSDRootElement; |
| import org.apache.geode.internal.classloader.ClassPathLoader; |
| import org.apache.geode.management.internal.util.ClasspathScanLoadHelper; |
| |
| public class JAXBService { |
| |
| private final Marshaller marshaller; |
| private final Unmarshaller unmarshaller; |
| private final NameSpaceFilter nameSpaceFilter; |
| |
| public JAXBService(Class<?>... xsdRootClasses) { |
| try { |
| JAXBContext jaxbContext = JAXBContext.newInstance(xsdRootClasses); |
| marshaller = jaxbContext.createMarshaller(); |
| unmarshaller = jaxbContext.createUnmarshaller(); |
| marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); |
| marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true); |
| |
| String schemas = Arrays.stream(xsdRootClasses).map(c -> { |
| XSDRootElement element = c.getAnnotation(XSDRootElement.class); |
| if (element != null && StringUtils.isNotEmpty(element.namespace()) |
| && StringUtils.isNotEmpty(element.schemaLocation())) { |
| return element.namespace() + " " + element.schemaLocation(); |
| } |
| return null; |
| }).filter(Objects::nonNull).collect(Collectors.joining(" ")); |
| |
| marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, schemas); |
| // use a custom Filter so that we can unmarshall older namespace or no namespace xml |
| XMLReader reader = XMLReaderFactory.createXMLReader(); |
| nameSpaceFilter = new NameSpaceFilter(); |
| nameSpaceFilter.setParent(reader); |
| } catch (Exception e) { |
| throw new RuntimeException(e.getMessage(), e); |
| } |
| } |
| |
| public static JAXBService create(Class<?>... xsdClasses) { |
| if (xsdClasses != null && xsdClasses.length > 0) { |
| return new JAXBService(xsdClasses); |
| } |
| // else, scan the classpath to find all the classes annotated with XSDRootElement |
| return new JAXBService(scanForClasses().toArray(new Class[0])); |
| } |
| |
| public static JAXBService createWithValidation(Class<?>... xsdClasses) { |
| JAXBService jaxbService = create(xsdClasses); |
| jaxbService.validateWithLocalCacheXSD(); |
| return jaxbService; |
| } |
| |
| static Set<String> getPackagesToScan() { |
| Set<String> packages = new HashSet<>(); |
| String sysProperty = getProperty(PACKAGES_TO_SCAN); |
| if (sysProperty != null) { |
| packages = Arrays.stream(sysProperty.split(",")).collect(Collectors.toSet()); |
| } else { |
| packages.add("*"); |
| } |
| return packages; |
| } |
| |
| private static Set<Class<?>> scanForClasses() { |
| // scan the classpath to find all the classes annotated with XSDRootElement |
| Set<String> packages = getPackagesToScan(); |
| try (ClasspathScanLoadHelper scanner = new ClasspathScanLoadHelper(packages)) { |
| return scanner.scanClasspathForAnnotation(XSDRootElement.class, |
| packages.toArray(new String[] {})); |
| } |
| } |
| |
| private void validateWith(URL url) { |
| SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); |
| try { |
| Schema schema = factory.newSchema(url); |
| marshaller.setSchema(schema); |
| } catch (SAXException e) { |
| throw new RuntimeException(e.getMessage(), e); |
| } |
| } |
| |
| public void validateWithLocalCacheXSD() { |
| // find the local Cache-1.0.xsd |
| URL local_cache_xsd = ClassPathLoader.getLatest() |
| .getResource("META-INF/schemas/geode.apache.org/schema/cache/cache-1.0.xsd"); |
| validateWith(local_cache_xsd); |
| } |
| |
| public String marshall(Object object) { |
| StringWriter sw = new StringWriter(); |
| try { |
| sw.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"); |
| marshaller.marshal(object, sw); |
| } catch (Exception e) { |
| throw new RuntimeException(e.getMessage(), e); |
| } |
| return sw.toString(); |
| } |
| |
| public <T> T unMarshall(String xml) { |
| try { |
| Source source = new SAXSource(nameSpaceFilter, new InputSource(new StringReader(xml))); |
| return (T) unmarshaller.unmarshal(source); |
| } catch (Exception e) { |
| throw new RuntimeException(e.getMessage(), e); |
| } |
| } |
| |
| public <T> T unMarshall(String xml, Class<T> klass) { |
| try { |
| Source source = new SAXSource(nameSpaceFilter, new InputSource(new StringReader(xml))); |
| return unmarshaller.unmarshal(source, klass).getValue(); |
| } catch (Exception e) { |
| throw new RuntimeException(e.getMessage(), e); |
| } |
| } |
| } |