http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerWriter.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerWriter.java b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerWriter.java new file mode 100644 index 0000000..de819bc --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerWriter.java @@ -0,0 +1,317 @@ +/*************************************************************************************************************************** + * 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.juneau.serializer; + +import java.io.*; +import java.net.*; + +import org.apache.juneau.internal.*; + +/** + * Simple wrapper around a standard {@link Writer} with additional methods. + * <p> + * Modeled after the Java ProcessBuilder class so that you can chain commands to reduce + * the need for string concatenation for performance reasons. + * + * <h6 class='topic'>Example</h6> + * <p class='bcode'> + * writer.append(<js>"foo"</js>).nl().i(5).append(<js>"bar"</js>); + * </p> + * + * @author James Bognar ([email protected]) + */ +public class SerializerWriter extends Writer { + + /** The underlying writer. */ + protected final Writer out; + + /** Use-indentation flag. */ + protected final boolean useIndentation; + + /** Use-whitespace flag. */ + protected final boolean useWhitespace; + + /** Use-whitespace flag. */ + protected final boolean trimStrings; + + /** The quote character being used by this writer. */ + protected final char quoteChar; + + /** The base (e.g. <js>https://localhost:9443/contextPath"</js>) for relative URIs (e.g. <js>"my/path"</js>). */ + protected final String relativeUriBase; + + /** The base (e.g. <js>https://localhost:9443"</js>) for relative URIs with absolute paths (e.g. <js>"/contextPath/my/path"</js>). */ + protected final String absolutePathUriBase; + + /** + * @param out The writer being wrapped. + * @param useIndentation If <jk>true</jk>, calling {@link #cr(int)} will create an indentation. + * @param useWhitespace If <jk>true</jk>, calling {@link #s()} will write a space character. + * @param trimStrings If <jk>true</jk>, strings should be trimmed before they're serialized. + * @param quoteChar The character to write when {@link #q()} is called. + * @param relativeUriBase The base (e.g. <js>https://localhost:9443/contextPath"</js>) for relative URIs (e.g. <js>"my/path"</js>). + * @param absolutePathUriBase The base (e.g. <js>https://localhost:9443"</js>) for relative URIs with absolute paths (e.g. <js>"/contextPath/my/path"</js>). + */ + public SerializerWriter(Writer out, boolean useIndentation, boolean useWhitespace, boolean trimStrings, char quoteChar, String relativeUriBase, String absolutePathUriBase) { + this.out = out; + this.useIndentation = useIndentation; + this.useWhitespace = useWhitespace; + this.trimStrings = trimStrings; + this.quoteChar = quoteChar; + this.relativeUriBase = relativeUriBase; + this.absolutePathUriBase = absolutePathUriBase; + } + + /** + * Performs a carriage return. + * <p> + * Adds a newline and the specified number of tabs (if the {@code useIndentation} setting is enabled) to the output. + * + * @param depth The indentation. + * @throws IOException If a problem occurred trying to write to the writer. + * @return This object (for method chaining). + */ + public SerializerWriter cr(int depth) throws IOException { + if (useIndentation) + return nl().i(depth); + return this; + } + + /** + * Writes an indent (if the {@code useIndentation} setting is enabled), followed by text, + * followed by a newline (if the {@code useIndentation} setting is enabled). + * + * @param indent The number of tabs to indent. + * @param text The text to write. + * @throws IOException If a problem occurred trying to write to the writer. + * @return This object. + */ + public SerializerWriter appendln(int indent, String text) throws IOException { + return append(indent, true, text); + } + + /** + * Writes the specified text followed by a newline (if the {@code useIndentation} setting is enabled). + * + * @param text The text to write. + * @throws IOException If a problem occurred trying to write to the writer. + * @return This object. + */ + public SerializerWriter appendln(String text) throws IOException { + return append(0, true, text); + } + + /** + * Writes an indent (if the {@code useIndentation} setting is enabled), followed by text. + * + * @param indent The number of tabs to indent. + * @param text The text to write. + * @throws IOException If a problem occurred trying to write to the writer. + * @return This object. + */ + public SerializerWriter append(int indent, String text) throws IOException { + return append(indent, false, text); + } + + /** + * Writes an indent (if the {@code useIndentation} setting is enabled), followed by text. + * + * @param indent The number of tabs to indent. + * @param c The character to write. + * @throws IOException If a problem occurred trying to write to the writer. + * @return This object. + */ + public SerializerWriter append(int indent, char c) throws IOException { + return i(indent).append(c); + } + + /** + * Writes an indent (if the {@code useIndentation} setting is enabled), followed by text, + * optionally followed by a newline (if the {@code useIndentation} setting is enabled). + * + * @param indent The number of tabs to indent. + * @param newline If <jk>true</jk>, then a newline is written. + * @param text The text to write. + * @throws IOException If a problem occurred trying to write to the writer. + * @return This object (for method chaining). + */ + private SerializerWriter append(int indent, boolean newline, String text) throws IOException { + i(indent); + out.write(text); + if (newline) + nl(); + return this; + } + + /** + * Appends the specified object as a URI. + * <p> + * Object is converted to a <code>String</code> using <code>toString()</code>, so this will work on {@link URL} or {@link URI} objects, + * or any other type that returns a URI via it's <code>toString()</code> method. + * <p> + * If the URI is relative (i.e. without a schema and not prepended with <js>'/'</js>) the URI + * will be prepended with {@link #absolutePathUriBase} and {@link #relativeUriBase}. + * <p> + * If the URI is context-absolute (i.e. without a schema, but prepended with <js>'/'</js>) + * the URI will be prepended with {@link #absolutePathUriBase}. + * + * @param uri The URI to serialize. + * @return This object (for method chaining). + * @throws IOException If a problem occurred trying to write to the writer. + */ + public SerializerWriter appendUri(Object uri) throws IOException { + String s = uri.toString(); + if (s.indexOf("://") == -1) { + if (StringUtils.startsWith(s, '/')) { + if (absolutePathUriBase != null) + append(absolutePathUriBase); + } else { + if (relativeUriBase != null) { + append(relativeUriBase); + if (! relativeUriBase.equals("/")) + append("/"); + + } + } + } + return append(s); + } + + /** + * Adds a whitespace character to the output if the {@code useWhitespace} setting is enabled. + * + * @return This object (for method chaining). + * @throws IOException If a problem occurred trying to write to the writer. + */ + public SerializerWriter s() throws IOException { + if (useWhitespace) + out.write(' '); + return this; + } + + /** + * Adds the quote character specified by the {@code quoteChar} setting to the output. + * + * @return This object (for method chaining). + * @throws IOException If a problem occurred trying to write to the writer. + */ + public SerializerWriter q() throws IOException { + out.write(quoteChar); + return this; + } + + /** + * Writes an indent to the writer if the {@code useIndentation} setting is enabled. + * + * @param indent The number of tabs to indent. + * @throws IOException If a problem occurred trying to write to the writer. + * @return This object (for method chaining). + */ + public SerializerWriter i(int indent) throws IOException { + if (useIndentation) + for (int i = 0; i < indent; i++) + out.write('\t'); + return this; + } + + /** + * Writes a newline to the writer if the {@code useIndentation} setting is enabled. + * + * @throws IOException If a problem occurred trying to write to the writer. + * @return This object (for method chaining). + */ + public SerializerWriter nl() throws IOException { + if (useIndentation) + out.write('\n'); + return this; + } + + /** + * Writes the specified text to the writer. + * + * @param text The text to write. + * @throws IOException If a problem occurred trying to write to the writer. + * @return This object (for method chaining). + */ + public SerializerWriter append(Object text) throws IOException { + out.append(text == null ? null : text.toString()); + return this; + } + + /** + * Writes the specified text to the writer. + * + * @param text The text to write. + * @throws IOException If a problem occurred trying to write to the writer. + * @return This object (for method chaining). + */ + public SerializerWriter append(String text) throws IOException { + if (text != null) + out.append(text); + return this; + } + + /** + * Writes the specified text to the writer if b is true. + * + * @param b Boolean flag. + * @param text The text to write. + * @throws IOException If a problem occurred trying to write to the writer. + * @return This object (for method chaining). + */ + public SerializerWriter appendIf(boolean b, String text) throws IOException { + if (b) + out.write(text); + return this; + } + + /** + * Writes the specified text to the writer if b is true. + * + * @param b Boolean flag. + * @param c The text to write. + * @throws IOException If a problem occurred trying to write to the writer. + * @return This object (for method chaining). + */ + public SerializerWriter appendIf(boolean b, char c) throws IOException { + if (b) + out.write(c); + return this; + } + + + //-------------------------------------------------------------------------------- + // Overridden methods + //-------------------------------------------------------------------------------- + + @Override /* Writer */ + public SerializerWriter append(char c) throws IOException { + out.write(c); + return this; + } + + @Override /* Writer */ + public void write(char[] cbuf, int off, int len) throws IOException { + out.write(cbuf, off, len); + } + + @Override /* Writer */ + public void flush() throws IOException { + out.flush(); + } + + @Override /* Writer */ + public void close() throws IOException { + out.close(); + } +}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/serializer/StringObject.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/serializer/StringObject.java b/juneau-core/src/main/java/org/apache/juneau/serializer/StringObject.java new file mode 100644 index 0000000..8d2dc07 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/serializer/StringObject.java @@ -0,0 +1,85 @@ +/*************************************************************************************************************************** + * 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.juneau.serializer; + +import java.io.*; + +import org.apache.juneau.*; + +/** + * A serializer/object pair used for delayed object serialization. + * <p> + * Useful in certain conditions such as logging when you don't want to needlessly serialize objects. + * <p> + * Instances of this method are created by the {@link WriterSerializer#toStringObject(Object)} method. + * + * <h6 class='topic'>Example</h6> + * <p class='bcode'> + * <jc>// The POJO will not be serialized unless DEBUG is enabled.</jc> + * logger.log(<jsf>DEBUG</jsf>, <js>"Object contents are: {0}"</js>, JsonSerializer.<jsf>DEFAULT</jsf>.toObjectString(myPojo)); + * </p> + * + * @author James Bognar ([email protected]) + */ +public class StringObject implements CharSequence, Writable { + + private final WriterSerializer s; + private final Object o; + private String results; + + /** + * Constructor. + * @param s The serializer to use to serialize the object. + * @param o The object to be serialized. + */ + public StringObject(WriterSerializer s, Object o) { + this.s = s; + this.o = o; + } + + @Override /* Object */ + public String toString() { + if (results == null) + results = s.toString(o); + return results; + } + + @Override /* CharSequence */ + public int length() { + return toString().length(); + } + + @Override /* CharSequence */ + public char charAt(int index) { + return toString().charAt(index); + } + + @Override /* CharSequence */ + public CharSequence subSequence(int start, int end) { + return toString().subSequence(start, end); + } + + @Override /* Writable */ + public void writeTo(Writer w) throws IOException { + try { + s.serialize(o, w); + } catch (SerializeException e) { + throw new IOException(e); + } + } + + @Override /* Writable */ + public String getMediaType() { + return s.getMediaTypes()[0]; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/serializer/WriterSerializer.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/serializer/WriterSerializer.java b/juneau-core/src/main/java/org/apache/juneau/serializer/WriterSerializer.java new file mode 100644 index 0000000..49bec69 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/serializer/WriterSerializer.java @@ -0,0 +1,96 @@ +/*************************************************************************************************************************** + * 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.juneau.serializer; + +import java.io.*; +import java.lang.reflect.*; + +import org.apache.juneau.*; +import org.apache.juneau.annotation.*; + +/** + * Subclass of {@link Serializer} for character-based serializers. + * + * + * <h6 class='topic'>Description</h6> + * <p> + * This class is typically the parent class of all character-based serializers. + * It has 2 abstract methods to implement... + * <ul class='spaced-list'> + * <li>{@link #createSession(Object, ObjectMap, Method)} + * <li>{@link #doSerialize(SerializerSession, Object)} + * </ul> + * + * + * <h6 class='topic'>@Produces annotation</h6> + * <p> + * The media types that this serializer can produce is specified through the {@link Produces @Produces} annotation. + * <p> + * However, the media types can also be specified programmatically by overriding the {@link #getMediaTypes()} + * and {@link #getResponseContentType()} methods. + * + * @author James Bognar ([email protected]) + */ +public abstract class WriterSerializer extends Serializer { + + @Override /* Serializer */ + public boolean isWriterSerializer() { + return true; + } + + //-------------------------------------------------------------------------------- + // Other methods + //-------------------------------------------------------------------------------- + + /** + * Convenience method for serializing an object to a <code>String</code>. + * + * @param o The object to serialize. + * @return The output serialized to a string. + * @throws SerializeException If a problem occurred trying to convert the output. + */ + @Override + public final String serialize(Object o) throws SerializeException { + StringWriter w = new StringWriter(); + serialize(createSession(w), o); + return w.toString(); + } + + /** + * Identical to {@link #serialize(Object)} except throws a {@link RuntimeException} + * instead of a {@link SerializeException}. + * This is typically good enough for debugging purposes. + * + * @param o The object to serialize. + * @return The serialized object. + */ + public final String toString(Object o) { + try { + StringWriter w = new StringWriter(); + serialize(createSession(w), o); + return w.toString(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Wraps the specified object inside a {@link StringObject}. + * + * @param o The object to wrap. + * @return The wrapped object. + */ + public final StringObject toStringObject(Object o) { + return new StringObject(this, o); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/serializer/package.html ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/serializer/package.html b/juneau-core/src/main/java/org/apache/juneau/serializer/package.html new file mode 100644 index 0000000..d19a959 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/serializer/package.html @@ -0,0 +1,135 @@ +<!DOCTYPE HTML> +<!-- +/*************************************************************************************************************************** + * 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. + * + ***************************************************************************************************************************/ + --> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <style type="text/css"> + /* For viewing in Page Designer */ + @IMPORT url("../../../../../../javadoc.css"); + + /* For viewing in REST interface */ + @IMPORT url("../htdocs/javadoc.css"); + body { + margin: 20px; + } + </style> + <script> + /* Replace all @code and @link tags. */ + window.onload = function() { + document.body.innerHTML = document.body.innerHTML.replace(/\{\@code ([^\}]+)\}/g, '<code>$1</code>'); + document.body.innerHTML = document.body.innerHTML.replace(/\{\@link (([^\}]+)\.)?([^\.\}]+)\}/g, '<code>$3</code>'); + } + </script> +</head> +<body> +<p>Serializer API</p> + +<script> + function toggle(x) { + var div = x.nextSibling; + while (div != null && div.nodeType != 1) + div = div.nextSibling; + if (div != null) { + var d = div.style.display; + if (d == 'block' || d == '') { + div.style.display = 'none'; + x.className += " closed"; + } else { + div.style.display = 'block'; + x.className = x.className.replace(/(?:^|\s)closed(?!\S)/g , '' ); + } + } + } +</script> + +<a id='TOC'></a><h5 class='toc'>Table of Contents</h5> +<ol class='toc'> + <li><p><a class='doclink' href='#Serializer'>Serializer API</a></p> + <ol> + <li><p><a class='doclink' href='#SerializerGroup'>The SerializerGroup class</a></p> + </ol> + <li><p><a class='doclink' href='#DefiningSerializer'>Defining a new Serializer</a></p> +</ol> + +<!-- ======================================================================================================== --> +<a id="Serializer"></a> +<h2 class='topic' onclick='toggle(this)'>1 - Serializer API</h2> +<div class='topic'> + <p> + The serialization API is designed to be easily extensible by developers.<br> + If you are writing your own serializer, you will typically subclass directly from either {@link org.apache.juneau.serializer.WriterSerializer} + or {@link org.apache.juneau.serializer.OutputStreamSerializer}.<br> + </p> + + <!-- ======================================================================================================== --> + <a id="SerializerGroup"></a> + <h3 class='topic' onclick='toggle(this)'>1.1 - The SerializerGroup class</h3> + <div class='topic'> + <p> + The {@link org.apache.juneau.serializer.SerializerGroup} class represents a group of serializers registered with the media types they handle. + </p> + + <h6 class='topic'>Features</h6> + <p> + The <code>SerializerGroup</code> class provides the following features: + <ul class='spaced-list'> + <li>Finds serializers based on HTTP <code>Accept</code> header values. + <li>Sets common properties on all serializers in a single method call. + <li>Locks all serializers in a single method call. + <li>Clones existing groups and all serializers within the group in a single method call. + </ul> + + <p> + Refer to {@link org.apache.juneau.serializer.SerializerGroup} for additional information. + </p> + </div> +</div> + + +<!-- ======================================================================================================== --> +<a id="DefiningSerializer"></a> +<h2 class='topic' onclick='toggle(this)'>2 - Defining a new Serializer</h2> +<div class='topic'> + <p> + Defining a new serializer is quite simple if you subclass directly from {@link org.apache.juneau.serializer.WriterSerializer} + or {@link org.apache.juneau.serializer.OutputStreamSerializer}.<br> + In each case, you simply need to implement a single + method and specify a {@link org.apache.juneau.annotation.Produces} annotation. + </p> + <p> + The following example shows a simple serializer that converts images to output streams using standard JRE classes. + </p> + <p class='bcode'> + <ja>@Produces</ja>({<js>"image/png"</js>,<js>"image/jpeg"</js>}) + <jk>public static class</jk> ImageSerializer <jk>extends</jk> OutputStreamSerializer { + <ja>@Override</ja> + <jk>public void</jk> serialize(Object o, OutputStream out, SerializerSession session) <jk>throws</jk> IOException, SerializeException { + RenderedImage image = (RenderedImage)o; + String mediaType = ctx.getMediaType(); + ImageIO.<jsm>write</jsm>(image, mediaType.substring(mediaType.indexOf(<js>'/'</js>)+1), out); + } + } + </p> + <p> + Serializer that take advantage of the entire {@link org.apache.juneau.CoreApi} interface to be able to serialize arbitrary beans and POJOs is + considerably more complex and outside the scope of this document.<br> + If developing such a serializer, the best course of action would be to replicate what occurs in the {@link org.apache.juneau.json.JsonSerializer} class. + </p> +</div> + +</body> +</html> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializer.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializer.java b/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializer.java new file mode 100644 index 0000000..946f02b --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializer.java @@ -0,0 +1,78 @@ +/*************************************************************************************************************************** + * 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.juneau.soap; + +import static org.apache.juneau.soap.SoapXmlSerializerContext.*; + +import org.apache.juneau.*; +import org.apache.juneau.annotation.*; +import org.apache.juneau.serializer.*; +import org.apache.juneau.xml.*; + +/** + * Serializes POJOs to HTTP responses as XML+SOAP. + * + * + * <h6 class='topic'>Media types</h6> + * <p> + * Handles <code>Accept</code> types: <code>text/xml+soap</code> + * <p> + * Produces <code>Content-Type</code> types: <code>text/xml+soap</code> + * + * + * <h6 class='topic'>Description</h6> + * <p> + * Essentially the same output as {@link XmlDocSerializer}, except wrapped in a standard SOAP envelope. + * + * + * <h6 class='topic'>Configurable properties</h6> + * <p> + * This class has the following properties associated with it: + * <ul> + * <li>{@link SoapXmlSerializerContext} + * <li>{@link BeanContext} + * </ul> + * + * + * @author James Bognar ([email protected]) + */ +@Produces(value="text/xml+soap",contentType="text/xml") +public final class SoapXmlSerializer extends XmlSerializer { + + //-------------------------------------------------------------------------------- + // Overridden methods + //-------------------------------------------------------------------------------- + + @Override /* Serializer */ + protected void doSerialize(SerializerSession session, Object o) throws Exception { + XmlSerializerSession s = (XmlSerializerSession)session; + XmlWriter w = s.getWriter(); + w.append("<?xml") + .attr("version", "1.0") + .attr("encoding", "UTF-8") + .appendln("?>"); + w.oTag("soap", "Envelope") + .attr("xmlns", "soap", s.getProperties().getString(SOAPXML_SOAPAction, "http://www.w3.org/2003/05/soap-envelope")) + .appendln(">"); + w.sTag(1, "soap", "Body").nl(); + super.serialize(s, o); + w.eTag(1, "soap", "Body").nl(); + w.eTag("soap", "Envelope").nl(); + } + + @Override /* Serializer */ + public ObjectMap getResponseHeaders(ObjectMap properties) { + return super.getResponseHeaders(properties) + .append("SOAPAction", properties.getString(SOAPXML_SOAPAction, "http://www.w3.org/2003/05/soap-envelope")); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializerContext.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializerContext.java b/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializerContext.java new file mode 100644 index 0000000..b799afa --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializerContext.java @@ -0,0 +1,28 @@ +/*************************************************************************************************************************** + * 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.juneau.soap; + +/** + * Properties associated with the {@link SoapXmlSerializer} class. + * + * @author James Bognar ([email protected]) + */ +public final class SoapXmlSerializerContext { + + /** + * The <code>SOAPAction</code> HTTP header value to set on responses. + * <p> + * Default is <js>"http://www.w3.org/2003/05/soap-envelope"</js>. + */ + public static final String SOAPXML_SOAPAction = "SoapXmlSerializer.SOAPAction"; +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/soap/package.html ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/soap/package.html b/juneau-core/src/main/java/org/apache/juneau/soap/package.html new file mode 100644 index 0000000..89463d6 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/soap/package.html @@ -0,0 +1,41 @@ +<!DOCTYPE HTML> +<!-- +/*************************************************************************************************************************** + * 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. + * + ***************************************************************************************************************************/ + --> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <style type="text/css"> + /* For viewing in Page Designer */ + @IMPORT url("../../../../../../javadoc.css"); + + /* For viewing in REST interface */ + @IMPORT url("../htdocs/javadoc.css"); + body { + margin: 20px; + } + </style> + <script> + /* Replace all @code and @link tags. */ + window.onload = function() { + document.body.innerHTML = document.body.innerHTML.replace(/\{\@code ([^\}]+)\}/g, '<code>$1</code>'); + document.body.innerHTML = document.body.innerHTML.replace(/\{\@link (([^\}]+)\.)?([^\.\}]+)\}/g, '<code>$3</code>'); + } + </script> +</head> +<body> +<p>SOAP/XML serialization and parsing support</p> +</body> +</html> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/svl/DefaultingVar.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/svl/DefaultingVar.java b/juneau-core/src/main/java/org/apache/juneau/svl/DefaultingVar.java new file mode 100644 index 0000000..6004c93 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/svl/DefaultingVar.java @@ -0,0 +1,50 @@ +/*************************************************************************************************************************** + * 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.juneau.svl; + +import org.apache.juneau.internal.*; + +/** + * Interface for the resolution of vars with a default value if the <code>resolve()</code> method returns <jk>null</jk>. + * <p> + * For example, to resolve the system property <js>"myProperty"</js> but resolve to <js>"not found"</js> if the property doesn't exist: + * <js>"$S{myProperty,not found}"</js> + * <p> + * Subclasses must implement the {@link #resolve(VarResolverSession, String)} method. + * + * @see org.apache.juneau.svl + * @author James Bognar ([email protected]) + */ +public abstract class DefaultingVar extends SimpleVar { + + /** + * Constructor. + * + * @param name The name of this variable. + */ + public DefaultingVar(String name) { + super(name); + } + + @Override /* Var*/ + public String doResolve(VarResolverSession session, String s) { + int i = s.indexOf(','); + if (i == -1) + return resolve(session, s); + String[] s2 = StringUtils.split(s, ','); + String v = resolve(session, s2[0]); + if (v == null) + v = s2[1]; + return v; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/svl/MapVar.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/svl/MapVar.java b/juneau-core/src/main/java/org/apache/juneau/svl/MapVar.java new file mode 100644 index 0000000..cc0a385 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/svl/MapVar.java @@ -0,0 +1,46 @@ +/*************************************************************************************************************************** + * 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.juneau.svl; + +import java.util.*; + +/** + * A subclass of {@link DefaultingVar} that simply pulls values from a {@link Map}. + * + * @see org.apache.juneau.svl + * @author James Bognar ([email protected]) + */ +@SuppressWarnings("rawtypes") +public abstract class MapVar extends DefaultingVar { + + private final Map m; + + /** + * Constructor. + * + * @param name The name of this variable. + * @param m The map to pull values from. + */ + public MapVar(String name, Map m) { + super(name); + if (m == null) + throw new IllegalArgumentException("''m'' parameter cannot be null."); + this.m = m; + } + + @Override /* Var */ + public String resolve(VarResolverSession session, String varVal) { + Object o = m.get(varVal); + return (o == null ? null : o.toString()); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/svl/MultipartVar.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/svl/MultipartVar.java b/juneau-core/src/main/java/org/apache/juneau/svl/MultipartVar.java new file mode 100644 index 0000000..194c642 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/svl/MultipartVar.java @@ -0,0 +1,50 @@ +/*************************************************************************************************************************** + * 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.juneau.svl; + +import org.apache.juneau.internal.*; + +/** + * Interface for the resolution of vars that consist of a comma-delimited list. + * <p> + * (e.g. <js>"$X{foo, bar, baz}"</js>) + * + * @see org.apache.juneau.svl + * @author James Bognar ([email protected]) + */ +public abstract class MultipartVar extends SimpleVar { + + /** + * Constructor. + * + * @param name The name of this variable. + */ + public MultipartVar(String name) { + super(name); + } + + /** + * The interface that needs to be implemented for this interface. + * + * @param session The session object used for a single instance of a string resolution. + * @param args The arguments inside the variable. + * @return The resolved variable. + */ + public abstract String resolve(VarResolverSession session, String[] args); + + @Override /* Var */ + public String resolve(VarResolverSession session, String s) { + String[] s2 = s.indexOf(',') == -1 ? new String[]{s} : StringUtils.split(s, ','); + return resolve(session, s2); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/svl/SimpleVar.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/svl/SimpleVar.java b/juneau-core/src/main/java/org/apache/juneau/svl/SimpleVar.java new file mode 100644 index 0000000..9b5a2c2 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/svl/SimpleVar.java @@ -0,0 +1,46 @@ +/*************************************************************************************************************************** + * 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.juneau.svl; + +import java.io.*; + +/** + * Abstract superclass of all Simple Var Language variables that resolve to simple returned string values. + * <p> + * Note the difference between this class and {@link StreamedVar} that streams + * values to writers. + * Unlike the {@link StreamedVar} class, the returned value from this class can + * contain nested variables that will be recursively resolved by {@link VarResolver}. + * <p> + * Subclasses must implement the {@link #resolve(VarResolverSession, String)} method. + * + * @see org.apache.juneau.svl + * @author James Bognar ([email protected]) + */ +public abstract class SimpleVar extends Var { + + /** + * Constructor. + * + * @param name The variable name (e.g. <js>"C"</js> for variables of the form <js>"$C{...}"</js>) + */ + protected SimpleVar(String name) { + super(name, false); + } + + @Override /* Var */ + public void resolveTo(VarResolverSession session, Writer w, String arg) { + throw new UnsupportedOperationException("Cannot call streamTo() on SimpleVar class"); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/svl/StreamedVar.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/svl/StreamedVar.java b/juneau-core/src/main/java/org/apache/juneau/svl/StreamedVar.java new file mode 100644 index 0000000..75849bd --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/svl/StreamedVar.java @@ -0,0 +1,44 @@ +/*************************************************************************************************************************** + * 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.juneau.svl; + +/** + * Abstract superclass of all Simple Var Language variables that write their values directly to a writer. + * <p> + * Note the difference between this class and {@link SimpleVar} that returns simple string values. + * Unlike the {@link SimpleVar} class, the output from this class cannot contain nested variables. + * However, this class can be more efficient for variables that produce large amounts of output + * so that the creation of large in-memory strings is avoided. + * <p> + * Subclasses must implement the {@link #resolveTo(VarResolverSession, java.io.Writer, String)} method. + * + * @see org.apache.juneau.svl + * @author James Bognar ([email protected]) + */ +public abstract class StreamedVar extends Var { + + /** + * Constructor. + * + * @param name The variable name (e.g. <js>"C"</js> for variables of the form <js>"$C{...}"</js>) + */ + public StreamedVar(String name) { + super(name, true); + } + + @Override /* Var */ + public String resolve(VarResolverSession session, String arg) { + throw new UnsupportedOperationException("Cannot call resolve() on StreamedVar class"); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/svl/Var.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/svl/Var.java b/juneau-core/src/main/java/org/apache/juneau/svl/Var.java new file mode 100644 index 0000000..4d693be --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/svl/Var.java @@ -0,0 +1,110 @@ +/*************************************************************************************************************************** + * 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.juneau.svl; + +import static org.apache.juneau.internal.ThrowableUtils.*; + +import java.io.*; + +/** + * Abstract superclass of all Simple Var Language variables. + * <p> + * Vars are used to convert simple variables of the form <js>"$varName{varKey}"</js> into + * something else by the {@link VarResolver} class. + * <p> + * Subclasses must implement one of the following two methods: + * <ul> + * <li>{@link #resolve(VarResolverSession,String)} - For simple vars. + * <li>{@link #resolveTo(VarResolverSession,Writer,String)} - For streamed vars. + * </ul> + * Subclasses MUST implement a no-arg constructor so that class names can be passed to the {@link VarResolver#addVars(Class...)} method. + * They must also be thread safe! + * <p> + * Two direct abstract subclasses are provided to differentiated between simple and streamed vars: + * <ul> + * <li>{@link SimpleVar} + * <li>{@link StreamedVar} + * </ul> + * + * @see org.apache.juneau.svl + * @author James Bognar ([email protected]) + */ +public abstract class Var { + + private final String name; + final boolean streamed; + + /** + * Constructor. + * + * @param name The name of this variable. + * @param streamed Whether this variable is 'streamed', meaning the {@link #resolveTo(VarResolverSession, Writer, String)} + * method is implemented. If <jk>false</jk>, then the {@link #resolve(VarResolverSession, String)} method is implemented. + */ + public Var(String name, boolean streamed) { + this.name = name; + this.streamed = streamed; + + if (name == null) + illegalArg("Invalid var name. Must consist of only uppercase and lowercase ASCII letters."); + else for (int i = 0; i < name.length(); i++) { + // Need to make sure only ASCII characters are used. + char c = name.charAt(i); + if (c < 'A' || c > 'z' || (c > 'Z' && c < 'a')) + illegalArg("Invalid var name. Must consist of only uppercase and lowercase ASCII letters."); + } + } + + /** + * Return the name of this variable. + * <p> + * For example, the system property variable returns <js>"S"</js> since the format of the + * variable is <js>"$S{system.property}"</js>. + * + * @return The name of this variable. + */ + protected String getName() { + return name; + } + + /** + * The method called from {@link VarResolver}. + * Can be overridden to intercept the request and do special handling. + * Default implementation simply calls resolve(String). + * + * @param session The session object used for a single instance of a string resolution. + * @param arg The inside argument of the variable. + * @return The resolved value. + */ + protected String doResolve(VarResolverSession session, String arg) { + return resolve(session, arg); + } + + /** + * The interface that needs to be implemented for subclasses of {@link SimpleVar}. + * + * @param session The session object used for a single instance of a var resolution. + * @param arg The inside argument of the variable. + * @return The resolved value. + */ + public abstract String resolve(VarResolverSession session, String arg); + + /** + * The interface that needs to be implemented for subclasses of {@link StreamedVar}. + * + * @param session The session object used for a single instance of a var resolution. + * @param w The writer to send the resolved value to. + * @param arg The inside argument of the variable. + */ + public abstract void resolveTo(VarResolverSession session, Writer w, String arg); +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/svl/VarResolver.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/svl/VarResolver.java b/juneau-core/src/main/java/org/apache/juneau/svl/VarResolver.java new file mode 100644 index 0000000..5c6c7f1 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/svl/VarResolver.java @@ -0,0 +1,221 @@ +/*************************************************************************************************************************** + * 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.juneau.svl; + +import static java.text.MessageFormat.*; +import static org.apache.juneau.svl.VarResolverContext.*; + +import java.io.*; +import java.util.*; + +import org.apache.juneau.*; +import org.apache.juneau.ini.*; +import org.apache.juneau.svl.vars.*; + +/** + * Utility class for resolving variables of the form <js>"$X{key}"</js> in strings. + * <p> + * Variables are of the form <code>$X{key}</code>, where <code>X</code> can consist of zero or more ASCII characters.<br> + * The variable key can contain anything, even nested variables that get recursively resolved. + * <p> + * Variables are defined through the {@link #addVars(Class[])} method. + * <p> + * The {@link Var} interface defines how variables are converted to values. + * <p> + * <h6 class='topic'>Example:</h6> + * <p class='bcode'> + * <jk>public class</jk> SystemPropertiesVar <jk>extends</jk> SimpleVar { + * + * <jc>// Must have a no-arg constructor!</jc> + * <jk>public</jk> SystemPropertiesVar() { + * <jk>super</jk>(<js>"S"</js>); + * } + * + * <ja>@Override</ja> + * <jk>public</jk> String resolve(VarResolverSession session, String varVal) { + * <jk>return</jk> System.<jsm>getProperty</jsm>(varVal); + * } + * } + * + * <jc>// Create a variable resolver that resolves system properties (e.g. "$S{java.home}")</jc> + * VarResolver r = <jk>new</jk> VarResolver().addVars(SystemPropertiesVar.<js>class</js>); + * + * <jc>// Use it!</jc> + * System.<jsf>out</jsf>.println(r.resolve(<js>"java.home is set to $S{java.home}"</js>)); + * </p> + * + * <h6 class='topic'>Context objects</h6> + * <p> + * Var resolvers can have zero or more context objects associated with them. + * <p> + * Context objects are arbitrary objects associated with this var resolver, such as + * a {@link ConfigFile} object. + * They are set through the {@link #setContextObject(String, Object)} method. + * They can be any class type. + * <p> + * Context objects can be retrieved by {@link Var} classes through the {@link VarResolverSession#getSessionObject(Class, String)} method. + * + * <h6 class='topic'>Session objects</h6> + * <p> + * Session objects are considered more ephemeral than context objects. + * While a context object is unlikely to ever change, a session object may change on every + * use of the var resolver. + * For example, the server API defines various <code>Var</code> objects that use the <code>RestRequest</code> + * object as a session object for the duration of a single HTTP request. + * <p> + * Session objects are used by calling the {@link #createSession()} or {@link #createSession(Map)} methods to create an instance + * of a {@link VarResolverSession} object that contains {@link VarResolverSession#resolve(String)} and {@link VarResolverSession#resolveTo(String,Writer)} methods + * that are identical to {@link VarResolver#resolve(String)} and {@link VarResolver#resolveTo(String, Writer)} except that the <code>Var</code> objects + * have access to the session objects through the {@link VarResolverSession#getSessionObject(Class, String)} method. + * <p> + * Session objects are specified through either the {@link #createSession(Map)} method or the {@link VarResolverSession#setSessionObject(String, Object)} methods. + * + * <h6 class='topic'>Cloning</h6> + * <p> + * Var resolvers can be cloned by using the {@link #clone()} method. + * Cloning a resolver will copy it's {@link Var} class names and context objects. + * <p> + * <h6 class='topic'>Example:</h6> + * <p class='bcode'> + * <jc>// Create a resolver that copies the default resolver and adds $C and $ARG vars.</jc> + * VarResolver myVarResolver = VarResolver.<jsf>DEFAULT</jsf>.clone().addVars(ConfigVar.<jk>class</jk>, ArgsVar.<jk>class</jk>); + * </p> + * + * @see org.apache.juneau.svl + * @author James Bognar ([email protected]) + */ +public class VarResolver extends CoreApi { + + /** + * Default string variable resolver with support for system properties and environment variables: + * <p> + * <ul> + * <li><code>$S{key}</code>,<code>$S{key,default}</code> - System properties. + * <li><code>$E{key}</code>,<code>$E{key,default}</code> - Environment variables. + * </ul> + * + * @see SystemPropertiesVar + * @see EnvVariablesVar + */ + public static final VarResolver DEFAULT = new VarResolver().addVars(SystemPropertiesVar.class, EnvVariablesVar.class).lock(); + + /** + * Construct an empty var resolver with no vars. + */ + public VarResolver() {} + + /** + * Register new variables with this resolver. + * + * @param vars The variable resolver classes. + * These classes must subclass from {@link Var} and have no-arg constructors. + * @return This object (for method chaining). + */ + public VarResolver addVars(Class<?>...vars) { + checkLock(); + ContextFactory cf = getContextFactory(); + for (Class<?> v : vars) { + try { + v.newInstance(); + } catch (InstantiationException e) { + throw new UnsupportedOperationException(format("Cannot instantiate variable class {0}. Must have a public no-arg constructor.", v.getName())); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } + cf.addToProperty(SVL_vars, v); + } + return this; + } + + /** + * Associates a context object with this resolver. + * + * @param name The name of the context object. + * @param object The context object. + * @return This object (for method chaining). + */ + public VarResolver setContextObject(String name, Object object) { + getContextFactory().putToProperty(SVL_context, name, object); + return this; + } + + /** + * Creates a new resolver session with no session objects. + * <p> + * Session objects can be associated with the specified session using the {@link VarResolverSession#setSessionObject(String, Object)} method. + * + * @return A new resolver session. + */ + public VarResolverSession createSession() { + return new VarResolverSession(getContext(VarResolverContext.class), null); + } + + /** + * Same as {@link #createSession()} except allows you to specify session objects as a map. + * + * @param sessionObjects The session objects to associate with the sessino. + * @return A new resolver session. + */ + public VarResolverSession createSession(Map<String,Object> sessionObjects) { + return new VarResolverSession(getContext(VarResolverContext.class), sessionObjects); + } + + /** + * Resolve variables in the specified string. + * <p> + * This is a shortcut for calling <code>createSession(<jk>null</jk>).resolve(s);</code>. + * This method can only be used if the string doesn't contain variables that rely on the existence of session variables. + * + * @param s The input string. + * @return The string with variables resolved, or the same string if it doesn't contain any variables to resolve. + */ + public String resolve(String s) { + return createSession(null).resolve(s); + } + + /** + * Resolve variables in the specified string and sends the results to the specified writer. + * <p> + * This is a shortcut for calling <code>createSession(<jk>null</jk>).resolveTo(s, w);</code>. + * This method can only be used if the string doesn't contain variables that rely on the existence of session variables. + * + * @param s The input string. + * @param w The writer to send the result to. + * @throws IOException + */ + public void resolveTo(String s, Writer w) throws IOException { + createSession(null).resolveTo(s, w); + } + + //-------------------------------------------------------------------------------- + // Overridden methods + //-------------------------------------------------------------------------------- + + @Override /* Lockable */ + public VarResolver lock() { + super.lock(); + return this; + } + + @Override /* Lockable */ + public VarResolver clone() { + try { + return (VarResolver)super.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } +} + http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/svl/VarResolverContext.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/svl/VarResolverContext.java b/juneau-core/src/main/java/org/apache/juneau/svl/VarResolverContext.java new file mode 100644 index 0000000..d4acd0b --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/svl/VarResolverContext.java @@ -0,0 +1,113 @@ +/*************************************************************************************************************************** + * 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.juneau.svl; + +import java.util.*; +import java.util.concurrent.*; + +import org.apache.juneau.*; +import org.apache.juneau.internal.*; + +/** + * Configurable properties on the {@link VarResolver} class. + * <p> + * Used to associate {@link Var Vars} and context objects with {@link VarResolver VarResolvers}. + * <p> + * See {@link ContextFactory} for more information about context properties. + * + * @see org.apache.juneau.svl + * @author James Bognar ([email protected]) + */ +public class VarResolverContext extends Context { + + /** + * An explicit list of Java classes to be excluded from consideration as being beans (<code>Set<Class></code>). + * <p> + * Not-bean classes are typically converted to <code>Strings</code> during serialization even if they + * appear to be bean-like. + */ + public static final String SVL_vars = "Svl.vars.set"; + + /** + * Add to the list of packages whose classes should not be considered beans. + */ + public static final String SVL_vars_add = "Svl.vars.set.add"; + + /** + * Remove from the list of packages whose classes should not be considered beans. + */ + public static final String SVL_vars_remove = "Svl.vars.set.remove"; + + /** + * Context objects associated with the resolver (<code>Map$lt;String,Object></code>). + */ + public static final String SVL_context = "Svl.context.map"; + + /** + * Adds a new map entry to the {@link #SVL_context} property. + */ + public static final String SVL_context_put = "Svl.context.map.put"; + + + // Map of Vars added through addVar() method. + private final Map<String,Var> stringVars; + + private final Map<String,Object> contextObjects; + + + /** + * Constructor. + * + * @param cf The context factory to copy from. + */ + public VarResolverContext(ContextFactory cf) { + super(cf); + ContextFactory.PropertyMap pm = cf.getPropertyMap("Svl"); + + Class<?>[] varClasses = pm.get(SVL_vars, Class[].class, new Class[0]); + Map<String,Var> m = new ConcurrentSkipListMap<String,Var>(); + for (Class<?> c : varClasses) { + if (! ClassUtils.isParentClass(Var.class, c)) + throw new RuntimeException("Invalid variable class. Must extend from Var"); + try { + Var v = (Var)c.newInstance(); + m.put(v.getName(), v); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + this.stringVars = Collections.unmodifiableMap(m); + + this.contextObjects = Collections.unmodifiableMap(pm.getMap(SVL_context, String.class, Object.class, Collections.<String,Object>emptyMap())); + } + + /** + * Returns an unmodifiable map of {@link Var Vars} associated with this context. + * + * @return A map whose keys are var names (e.g. <js>"S"</js>) and values are {@link Var} instances. + */ + protected Map<String,Var> getVars() { + return stringVars; + } + + /** + * Returns the context object with the specified name. + * + * @param name The name of the context object. + * @return The context object, or <jk>null</jk> if no context object is specified with that name. + */ + protected Object getContextObject(String name) { + return contextObjects.get(name); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/svl/VarResolverSession.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/svl/VarResolverSession.java b/juneau-core/src/main/java/org/apache/juneau/svl/VarResolverSession.java new file mode 100644 index 0000000..5ec3797 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/svl/VarResolverSession.java @@ -0,0 +1,298 @@ +/*************************************************************************************************************************** + * 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.juneau.svl; + +import java.util.*; +import static java.text.MessageFormat.*; +import static org.apache.juneau.internal.StringUtils.*; + +import java.io.*; + +import org.apache.juneau.*; + +/** + * A var resolver session that combines a {@link VarResolver} with one or more session objects. + * <p> + * Instances of this class are considered light-weight and fast to construct, use, and discard. + * <p> + * This class contains the workhorse code for var resolution. + * <p> + * Instances of this class are created through the {@link VarResolver#createSession()} and {@link VarResolver#createSession(Map)} + * methods. + * <p> + * Instances of this class are NOT guaranteed to be thread safe. + * + * @see org.apache.juneau.svl + * @author James Bognar ([email protected]) + */ +public class VarResolverSession extends Session { + + private final Map<String,Object> sessionObjects; + private final VarResolverContext context; + + /** + * Constructor. + * + * @param context The {@link VarResolver} context object that contains the {@link Var Vars} and + * context objects associated with that resolver. + * @param sessionObjects + */ + public VarResolverSession(VarResolverContext context, Map<String,Object> sessionObjects) { + super(context); + this.context = context; + this.sessionObjects = (sessionObjects == null ? new TreeMap<String,Object>() : sessionObjects); + } + + /** + * Adds a session object to this session. + * + * @param name The name of the session object. + * @param o The session object. + * @return This method (for method chaining). + */ + public VarResolverSession setSessionObject(String name, Object o) { + this.sessionObjects.put(name, o); + return this; + } + + /** + * Resolve all variables in the specified string. + * + * @param s The string to resolve variables in. + * @return The new string with all variables resolved, or the same string if no variables were found. + * Null input results in a blank string. + */ + public String resolve(String s) { + + if (s == null) + return ""; + if (s.indexOf('$') == -1 && s.indexOf('\\') == -1) + return s; + + // Special case where value consists of a single variable with no embedded variables (e.g. "$X{...}"). + // This is a common case, so we want an optimized solution that doesn't involve string builders. + if (isSimpleVar(s)) { + String var = s.substring(1, s.indexOf('{')); + String val = s.substring(s.indexOf('{')+1, s.length()-1); + Var v = getVar(var); + if (v != null) { + if (v.streamed) { + StringWriter sw = new StringWriter(); + v.resolveTo(this, sw, val); + return sw.toString(); + } + s = v.doResolve(this, val); + if (s == null) + s = ""; + return resolve(s); + } + return s; + } + + try { + return resolveTo(s, new StringWriter()).toString(); + } catch (IOException e) { + throw new RuntimeException(e); // Never happens. + } + } + + /** + * Checks to see if string is of the simple form "$X{...}" with no embedded variables. + * This is a common case, and we can avoid using StringWriters. + */ + private boolean isSimpleVar(String s) { + int S1 = 1; // Not in variable, looking for $ + int S2 = 2; // Found $, Looking for { + int S3 = 3; // Found {, Looking for } + int S4 = 4; // Found } + + int length = s.length(); + int state = S1; + for (int i = 0; i < length; i++) { + char c = s.charAt(i); + if (state == S1) { + if (c == '$') { + state = S2; + } else { + return false; + } + } else if (state == S2) { + if (c == '{') { + state = S3; + } else if (c < 'A' || c > 'z' || (c > 'Z' && c < 'a')) { // False trigger "$X " + return false; + } + } else if (state == S3) { + if (c == '}') + state = S4; + else if (c == '{' || c == '$') + return false; + } else if (state == S4) { + return false; + } + } + return state == S4; + } + + /** + * Resolves variables in the specified string and sends the output to the specified writer. + * More efficient than first parsing to a string and then serializing to the writer since this + * method doesn't need to construct a large string. + * + * @param s The string to resolve variables in. + * @param out The writer to write to. + * @return The same writer. + * @throws IOException + */ + public Writer resolveTo(String s, Writer out) throws IOException { + + int S1 = 1; // Not in variable, looking for $ + int S2 = 2; // Found $, Looking for { + int S3 = 3; // Found {, Looking for } + + int state = S1; + boolean isInEscape = false; + boolean hasInternalVar = false; + boolean hasInnerEscapes = false; + String varType = null; + String varVal = null; + int x = 0, x2 = 0; + int depth = 0; + int length = s.length(); + for (int i = 0; i < length; i++) { + char c = s.charAt(i); + if (state == S1) { + if (isInEscape) { + if (c == '\\' || c == '$') { + out.append(c); + } else { + out.append('\\').append(c); + } + isInEscape = false; + } else if (c == '\\') { + isInEscape = true; + } else if (c == '$') { + x = i; + x2 = i; + state = S2; + } else { + out.append(c); + } + } else if (state == S2) { + if (isInEscape) { + isInEscape = false; + } else if (c == '\\') { + hasInnerEscapes = true; + isInEscape = true; + } else if (c == '{') { + varType = s.substring(x+1, i); + x = i; + state = S3; + } else if (c < 'A' || c > 'z' || (c > 'Z' && c < 'a')) { // False trigger "$X " + if (hasInnerEscapes) + out.append(unEscapeChars(s.substring(x, i+1), new char[]{'\\','{'})); + else + out.append(s, x, i+1); + x = i + 1; + state = S1; + hasInnerEscapes = false; + } + } else if (state == S3) { + if (isInEscape) { + isInEscape = false; + } else if (c == '\\') { + isInEscape = true; + hasInnerEscapes = true; + } else if (c == '{') { + depth++; + hasInternalVar = true; + } else if (c == '}') { + if (depth > 0) { + depth--; + } else { + varVal = s.substring(x+1, i); + varVal = (hasInternalVar ? resolve(varVal) : varVal); + Var r = getVar(varType); + if (r == null) { + if (hasInnerEscapes) + out.append(unEscapeChars(s.substring(x2, i+1), new char[]{'\\','$','{','}'})); + else + out.append(s, x2, i+1); + x = i+1; + } else { + if (r.streamed) + r.resolveTo(this, out, varVal); + else { + String replacement = r.doResolve(this, varVal); + if (replacement == null) + replacement = ""; + // If the replacement also contains variables, replace them now. + if (replacement.indexOf('$') != -1) + replacement = resolve(replacement); + out.append(replacement); + } + x = i+1; + } + state = 1; + hasInnerEscapes = false; + } + } + } + } + if (isInEscape) + out.append('\\'); + else if (state == S2) + out.append('$').append(unEscapeChars(s.substring(x+1), new char[]{'{', '\\'})); + else if (state == S3) + out.append('$').append(varType).append('{').append(unEscapeChars(s.substring(x+1), new char[]{'\\','$','{','}'})); + return out; + } + + + /** + * Returns the session object with the specified name. + * Casts it to the specified class type for you. + * + * @param c The class type to cast to. + * @param name The name of the session object. + * @return The session object. Never <jk>null</jk>. + * @throws RuntimeException If session object with specified name does not exist. + */ + @SuppressWarnings("unchecked") + public <T> T getSessionObject(Class<T> c, String name) { + T t = null; + try { + t = (T)sessionObjects.get(name); + if (t == null) { + sessionObjects.put(name, this.context.getContextObject(name)); + t = (T)sessionObjects.get(name); + } + } catch (Exception e) { + throw new RuntimeException(format("Session object ''{0}'' or context object ''SvlContext.{0}'' could not be converted to type ''{1}''.", name, c.getName())); + } + if (t == null) + throw new RuntimeException(format("Session object ''{0}'' or context object ''SvlContext.{0}'' not found.", name)); + return t; + } + + /** + * Returns the {@link Var} with the specified name. + * + * @param name The var name (e.g. <js>"S"</js>). + * @return The {@link Var} instance, or <jk>null</jk> if no <code>Var</code> is associated with the specified name. + */ + protected Var getVar(String name) { + return this.context.getVars().get(name); + } +}
