All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.rexsl.core.XslResolver Maven / Gradle / Ivy

There is a newer version: 1.6
Show newest version
/**
 * Copyright (c) 2011-2014, ReXSL.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met: 1) Redistributions of source code must retain the above
 * copyright notice, this list of conditions and the following
 * disclaimer. 2) Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following
 * disclaimer in the documentation and/or other materials provided
 * with the distribution. 3) Neither the name of the ReXSL.com nor
 * the names of its contributors may be used to endorse or promote
 * products derived from this software without specific prior written
 * permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.rexsl.core;

import com.jcabi.aspects.Loggable;
import com.jcabi.log.Logger;
import com.jcabi.manifests.Manifests;
import com.rexsl.core.annotations.Schema;
import com.rexsl.core.annotations.Stylesheet;
import java.io.File;
import java.lang.annotation.Annotation;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotNull;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.validation.SchemaFactory;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.apache.commons.lang3.StringEscapeUtils;
import org.xml.sax.SAXException;

/**
 * Provider of JAXB {@link Marshaller} for JAX-RS framework.
 *
 * 

You don't need to use this class directly. It is made public only becuase * JAX-RS implementation should be able to discover it in classpath. * * @author Yegor Bugayenko ([email protected]) * @author Krzysztof Krason ([email protected]) * @version $Id$ * @since 0.2 */ @ToString @EqualsAndHashCode(of = { "folder", "classes", "context" }) @Provider @Produces({ MediaType.APPLICATION_XML, MediaType.TEXT_XML }) @Loggable(Loggable.DEBUG) public final class XslResolver implements ContextResolver { /** * Folder name of the location of XSD files * (name of {@link ServletContext} init parameter). */ public static final String XSD_FOLDER = "com.rexsl.core.XSD_FOLDER"; /** * Manifest xsl suffix entry name. */ private static final String XSL_SUFFIX = "ReXSL-Suffix"; /** * Folder with XSD files. * @see #setServletContext(ServletContext) */ private transient File folder; /** * Classes to process. */ private final transient Set> classes = new HashSet>(0); /** * JAXB context. */ private transient JAXBContext context; /** * Servlet request. */ private transient HttpServletRequest request; /** * Set servlet context from container, to be called by JAX-RS framework * because of {@link Context} annotation. * @param ctx The context */ @Context public void setServletContext(@NotNull final ServletContext ctx) { final String name = ctx.getInitParameter(XslResolver.XSD_FOLDER); if (name != null) { this.folder = new File(name); Logger.debug( this, "#setServletContext(%s): XSD folder set to '%s'", ctx.getClass().getName(), this.folder ); } } /** * Set request to provide information about resourse context. * @param req The request */ @Context public void setHttpServletRequest(@NotNull final HttpServletRequest req) { this.request = req; } /** * {@inheritDoc} * *

JAXBContext is thread-safe, that's why we don't synchronize here. * * @see JAXBContext is thread-safe */ @Override @NotNull public Marshaller getContext(@NotNull final Class type) { Marshaller mrsh; try { mrsh = this.buildContext(type).createMarshaller(); mrsh.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); final String header = Logger.format( "\n", StringEscapeUtils.escapeXml11(this.stylesheet(type)) ); mrsh.setProperty("com.sun.xml.bind.xmlHeaders", header); } catch (final JAXBException ex) { throw new IllegalStateException(ex); } if (this.folder == null) { Logger.debug( this, "#getContext(%s): marshaller created (no XSD validator)", type.getName() ); } else { this.addXsdValidatorToMarshaller(mrsh, type); } return mrsh; } /** * Add new class to context. * @param cls The class we should add */ public void add(@NotNull final Class cls) { synchronized (this.classes) { if (!this.classes.contains(cls)) { try { this.classes.add(cls); this.context = JAXBContext.newInstance( this.classes.toArray(new Class[this.classes.size()]) ); Logger.info( this, // @checkstyle LineLength (1 line) "#add(%s): added to JAXBContext (%d total), stylesheet: '%s'", cls.getName(), this.classes.size(), this.stylesheet(cls) ); } catch (final JAXBException ex) { throw new IllegalStateException(ex); } } } } /** * Create and return a context. * @param cls The class we should process * @return The context */ private JAXBContext buildContext(final Class cls) { this.add(cls); return this.context; } /** * Returns the name of XSL stylesheet for this type. * @param type The class * @return The name of stylesheet * @see #getContext(Class) */ private String stylesheet(final Class type) { final Annotation antn = type.getAnnotation(Stylesheet.class); String stylesheet; if (antn == null) { stylesheet = Logger.format( "/xsl/%s.xsl", type.getSimpleName() ); if (this.request != null) { try { stylesheet = new URL( this.request.getScheme(), this.request.getServerName(), this.request.getServerPort(), Logger.format( "%s%s%s", this.request.getContextPath(), stylesheet, this.suffix() ) ).toString(); } catch (final MalformedURLException ex) { throw new IllegalStateException(ex); } } } else { stylesheet = ((Stylesheet) antn).value(); } return stylesheet; } /** * Suffix for XSL files. * @return Query string with suffix or empty string. */ private String suffix() { final String suffix; if (Manifests.exists(XslResolver.XSL_SUFFIX)) { suffix = String.format( "?%s", Manifests.read(XslResolver.XSL_SUFFIX) ); } else { suffix = ""; } return suffix; } /** * Configure marhaller adding a xsd validator. * @param mrsh The marshaller, already created and ready to marshal * @param type The class to be marshalled * @see #getContext(Class) */ private void addXsdValidatorToMarshaller(final Marshaller mrsh, final Class type) { final String name = XslResolver.schema(type); if (name.isEmpty()) { Logger.debug( this, "Schema validation turned off for class '%s'", type.getName() ); } else { final File xsd = new File(this.folder, name); if (xsd.exists()) { final SchemaFactory factory = SchemaFactory.newInstance( XMLConstants.W3C_XML_SCHEMA_NS_URI ); try { mrsh.setSchema(factory.newSchema(xsd)); } catch (final SAXException ex) { throw new IllegalStateException( Logger.format( "Failed to use XSD schema from '%s' for class '%s'", xsd, type.getName() ), ex ); } try { mrsh.setEventHandler(new XsdEventHandler()); } catch (final JAXBException ex) { throw new IllegalStateException(ex); } Logger.debug( this, "Class '%s' will be validated with '%s' schema", type.getName(), xsd ); } else { Logger.warn( this, "No XSD schema for class '%s' in '%s' file", type.getName(), xsd ); } } } /** * Returns the name of XSD schema for this type. * @param type The class * @return The name of XSD file */ private static String schema(final Class type) { final Annotation antn = type.getAnnotation(Schema.class); final String schema; if (antn == null) { schema = Logger.format("%s.xsd", type.getName()); } else { if (((Schema) antn).ignore()) { schema = ""; } else { schema = ((Schema) antn).value(); } } return schema; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy