Author: vramdal
Date: Wed Mar 10 09:19:30 2010
New Revision: 921261
URL: http://svn.apache.org/viewvc?rev=921261&view=rev
Log:
SLING-1336 Implement JSON response option for SlingPostServlet
Added:
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/JSONResponse.java
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/MediaRangeList.java
sling/trunk/bundles/servlets/post/src/test/java/org/apache/sling/servlets/post/impl/helper/
sling/trunk/bundles/servlets/post/src/test/java/org/apache/sling/servlets/post/impl/helper/JsonResponseTest.java
sling/trunk/bundles/servlets/post/src/test/java/org/apache/sling/servlets/post/impl/helper/MediaRangeListTest.java
Modified:
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/SlingPostServlet.java
sling/trunk/bundles/servlets/post/src/test/java/org/apache/sling/servlets/post/impl/SlingPostServletTest.java
Modified:
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/SlingPostServlet.java
URL:
http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/SlingPostServlet.java?rev=921261&r1=921260&r2=921261&view=diff
==============================================================================
---
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/SlingPostServlet.java
(original)
+++
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/SlingPostServlet.java
Wed Mar 10 09:19:30 2010
@@ -37,6 +37,8 @@ import org.apache.sling.servlets.post.Sl
import org.apache.sling.servlets.post.SlingPostOperation;
import org.apache.sling.servlets.post.SlingPostProcessor;
import org.apache.sling.servlets.post.impl.helper.DateParser;
+import org.apache.sling.servlets.post.impl.helper.JSONResponse;
+import org.apache.sling.servlets.post.impl.helper.MediaRangeList;
import org.apache.sling.servlets.post.impl.helper.NodeNameGenerator;
import org.apache.sling.servlets.post.impl.operations.CopyOperation;
import org.apache.sling.servlets.post.impl.operations.DeleteOperation;
@@ -156,7 +158,7 @@ public class SlingPostServlet extends Sl
SlingHttpServletResponse response) throws IOException {
// prepare the response
- HtmlResponse htmlResponse = new HtmlResponse();
+ HtmlResponse htmlResponse = createHtmlResponse(request);
htmlResponse.setReferer(request.getHeader("referer"));
SlingPostOperation operation = getSlingPostOperation(request);
@@ -199,6 +201,27 @@ public class SlingPostServlet extends Sl
htmlResponse.send(response, isSetStatus(request));
}
+ /**
+ * Creates an instance of a HtmlResponse.
+ * @param req The request being serviced
+ * @return a {...@link
org.apache.sling.servlets.post.impl.helper.JSONResponse} if any of these
conditions are true:
+ * <ul>
+ * <li> the request has an <code>Accept</code> header of
<code>application/json</code></li>
+ * <li>the request is a JSON POST request (see SLING-1172)</li>
+ * <li>the request has a request parameter
<code>:accept=application/json</code></li>
+ * </ul>
+ * or a {...@link org.apache.sling.api.servlets.HtmlResponse} otherwise
+ */
+ HtmlResponse createHtmlResponse(SlingHttpServletRequest req) {
+ @SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"})
+ MediaRangeList mediaRangeList = new MediaRangeList(req);
+ if (mediaRangeList.prefer("text/html",
JSONResponse.RESPONSE_CONTENT_TYPE).equals(JSONResponse.RESPONSE_CONTENT_TYPE))
{
+ return new JSONResponse();
+ } else {
+ return new HtmlResponse();
+ }
+ }
+
private SlingPostOperation getSlingPostOperation(
SlingHttpServletRequest request) {
String operation =
request.getParameter(SlingPostConstants.RP_OPERATION);
Added:
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/JSONResponse.java
URL:
http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/JSONResponse.java?rev=921261&view=auto
==============================================================================
---
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/JSONResponse.java
(added)
+++
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/JSONResponse.java
Wed Mar 10 09:19:30 2010
@@ -0,0 +1,171 @@
+/*
+ * 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.sling.servlets.post.impl.helper;
+
+import org.apache.sling.api.servlets.HtmlResponse;
+import org.apache.sling.commons.json.JSONArray;
+import org.apache.sling.commons.json.JSONException;
+import org.apache.sling.commons.json.JSONObject;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * Represents a JSON response to be sent to the client. For backward
compatibility,
+ * this extends {...@link org.apache.sling.api.servlets.HtmlResponse}.
+ */
+public class JSONResponse extends HtmlResponse {
+ private JSONObject json = new JSONObject();
+ private JSONArray changes = new JSONArray();
+ private Boolean delayedIsCreateRequest;
+ static final String PROP_CHANGES = "changes";
+ static final String PROP_TYPE = "type";
+ static final String PROP_ARGUMENT = "argument";
+ public static final String RESPONSE_CONTENT_TYPE = "application/json";
+ static final String RESPONSE_CHARSET = "UTF-8";
+ private Throwable error;
+
+ public JSONResponse() throws JSONResponseException {
+ try {
+ json = new JSONObject();
+ changes = new JSONArray();
+ json.put(PROP_CHANGES, changes);
+ if (delayedIsCreateRequest != null) {
+ this.setCreateRequest(this.delayedIsCreateRequest);
+ }
+ } catch (Throwable e) {
+ throw new JSONResponseException(e);
+ }
+ }
+
+ @Override
+ public void onChange(String type, String... arguments) throws
JSONResponseException {
+ try {
+ JSONObject change = new JSONObject();
+ change.put(PROP_TYPE, type);
+ for (String argument : arguments) {
+ change.accumulate(PROP_ARGUMENT, argument);
+ }
+ changes.put(change);
+ } catch (JSONException e) {
+ throw new JSONResponseException(e);
+ }
+ }
+
+ @Override
+ public void setError(Throwable error) {
+ try {
+ this.error = error;
+ JSONObject jsonError = new JSONObject();
+ jsonError.put("class", error.getClass().getName());
+ jsonError.put("message", error.getMessage());
+ json.put("error", jsonError);
+ } catch (JSONException e) {
+ throw new JSONResponseException(e);
+ }
+ }
+
+ @Override
+ public Throwable getError() {
+ return this.error;
+ }
+
+ @Override
+ public void setCreateRequest(boolean isCreateRequest) {
+ if (json != null) {
+ super.setCreateRequest(isCreateRequest);
+ } else {
+ // This is called by HtmlResponse constructor, before our json
object is initiated.
+ // Store this in a member variable, so we can set it from our own
constructor.
+ this.delayedIsCreateRequest = isCreateRequest;
+ }
+ }
+
+ @Override
+ public void setProperty(String name, Object value) {
+ try {
+ this.json.put(name, value);
+ } catch (Throwable e) {
+ throw new JSONResponseException("Error setting JSON property '" +
name + "' to '" + value + "'", e);
+ }
+ }
+
+ @Override
+ public Object getProperty(String name) throws JSONResponseException {
+ try {
+ if (json.has(name)) {
+ return json.get(name);
+ } else {
+ return null;
+ }
+ } catch (JSONException e) {
+ throw new JSONResponseException("Error getting JSON property '" +
name + "'", e);
+ }
+ }
+
+ @SuppressWarnings({"ThrowableResultOfMethodCallIgnored"})
+ @Override
+ public void send(HttpServletResponse response, boolean setStatus) throws
IOException {
+ String path = getPath();
+ if (getProperty(PN_STATUS_CODE) == null) {
+ if (getError() != null) {
+ setStatus(500, getError().toString());
+ setTitle("Error while processing " + path);
+ } else {
+ if (isCreateRequest()) {
+ setStatus(201, "Created");
+ setTitle("Content created " + path);
+ } else {
+ setStatus(200, "OK");
+ setTitle("Content modified " + path);
+ }
+ }
+ }
+
+ String referer = getReferer();
+ if (referer == null) {
+ referer = "";
+ }
+ setReferer(referer);
+ response.setContentType(RESPONSE_CONTENT_TYPE);
+ response.setCharacterEncoding(RESPONSE_CHARSET);
+
+ try {
+ json.write(response.getWriter());
+ } catch (JSONException e) {
+ IOException ioe = new IOException("Error creating JSON response");
+ ioe.initCause(e);
+ throw ioe;
+ }
+ }
+
+ JSONObject getJson() {
+ return json;
+ }
+
+ public class JSONResponseException extends RuntimeException {
+
+ public JSONResponseException(String message, Throwable exception) {
+ super(message, exception);
+ }
+
+ public JSONResponseException(Throwable e) {
+ super("Error building JSON response", e);
+ }
+ }
+}
Added:
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/MediaRangeList.java
URL:
http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/MediaRangeList.java?rev=921261&view=auto
==============================================================================
---
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/MediaRangeList.java
(added)
+++
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/MediaRangeList.java
Wed Mar 10 09:19:30 2010
@@ -0,0 +1,326 @@
+/*
+ * 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.sling.servlets.post.impl.helper;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * Facilitates parsing of the Accept HTTP request header.
+ * See <a
href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1">RFC 2616
section 14.1</a>
+ */
+public class MediaRangeList extends TreeSet<MediaRangeList.MediaRange> {
+ public static final String HEADER_ACCEPT = "Accept";
+ public static final String PARAM_ACCEPT = ":http-equiv-accept";
+ public static final String WILDCARD = "*";
+ boolean matchesAll = false;
+
+ private static final Logger log =
LoggerFactory.getLogger(MediaRangeList.class);
+
+ /**
+ * Constructs a <code>MediaRangeList</code> using information from the
supplied <code>HttpServletRequest</code>.
+ * if the request contains a {...@link #PARAM_ACCEPT} query parameter, the
query parameter value overrides any
+ * {...@link #HEADER_ACCEPT} header value.
+ * If the request contains no {...@link #PARAM_ACCEPT} parameter, or the
parameter value is empty, the value of the
+ * {...@link #HEADER_ACCEPT} is used. If both values are missing, it is
assumed that the client accepts all media types,
+ * as per the RFC. See also {...@link
MediaRangeList#MediaRangeList(java.lang.String)}
+ * @param request The <code>HttpServletRequest</code> to extract a
<code>MediaRangeList</code> from
+ */
+ public MediaRangeList(HttpServletRequest request) {
+ String queryParam = request.getParameter(PARAM_ACCEPT);
+ if (queryParam != null && queryParam.trim().length() != 0) {
+ init(queryParam);
+ } else {
+ init(request.getHeader(HEADER_ACCEPT));
+ }
+ }
+
+ /**
+ * Constructs a <code>MediaRangeList</code> using a list of media ranges
specified in a <code>java.lang.String</code>.
+ * The string is a comma-separated list of media ranges, as specified by
the RFC.<br />
+ * Examples:
+ * <ul>
+ * <li><code>text/*;q=0.3, text/html;q=0.7, text/html;level=1,
text/html;level=2;q=0.4, */*;q=0.5</code></li>
+ * <li><code>text/html;q=0.8, application/json</code></li>
+ * </ul>
+ *
+ * @param listStr The list of media range specifications
+ */
+ public MediaRangeList(String listStr) {
+ try {
+ init(listStr);
+ } catch (Throwable t) {
+ log.error("Error building MediaRangeList from '" + listStr + "' -
will assume client accepts all media types", t);
+ init(null);
+ }
+ }
+
+ private void init(String headerValue) {
+ if (headerValue == null || headerValue.trim().length() == 0) {
+ // RFC 2616: "If no Accept header field is present,
+ // then it is assumed that the client accepts all media types."
+ this.matchesAll = true;
+ this.add(new MediaRange(WILDCARD + "/" + WILDCARD));
+ } else {
+ String[] mediaTypes = headerValue.split(",");
+ for (String type : mediaTypes) {
+ try {
+ MediaRange range = new MediaRange(type);
+ this.add(range);
+ if (range.matchesAll()) {
+ this.matchesAll = true;
+ }
+ } catch (Throwable throwable) {
+ log.warn("Error registering media type " + type,
throwable);
+ }
+ }
+ }
+ }
+
+ /**
+ * Determines if this MediaRangeList contains a given media type.
+ * @param mediaType A string on the form <code>type/subtype</code>.
Neither <code>type</code>
+ * or <code>subtype</code> should be wildcard (<code>*</code>).
+ * @return <code>true</code> if this <code>MediaRangeList</code> contains
a media type that matches
+ * <code>mediaType</code>, <code>false</code> otherwise
+ * @throws IllegalArgumentException if <code>mediaType</code> is not on an
accepted form
+ * @throws NullPointerException if <code>mediaType</code> is
<code>null</code>
+ */
+ public boolean contains(String mediaType) {
+ //noinspection SuspiciousMethodCalls
+ MediaRange comp = new MediaRange(mediaType);
+ return this.matchesAll || this.contains(comp);
+ }
+
+ /**
+ * Given a list of media types, returns the one is preferred by this
<code>MediaRangeList</code>.
+ * @param mediaRanges An array of possible {...@link
org.apache.sling.servlets.post.impl.helper.MediaRangeList.MediaRange}s
+ * @return One of the <code>mediaRanges</code> that this
<code>MediaRangeList</code> prefers;
+ * or <code>null</code> if this <code>MediaRangeList</code> does not
contain any of the <code>mediaRanges</code>
+ * @throws NullPointerException if <code>mediaRanges</code> is
<code>null</code> or contains a <code>null</code> value
+ */
+ public MediaRange prefer(Set<MediaRange> mediaRanges) {
+ for (MediaRange range : this) {
+ for (MediaRange mediaType : mediaRanges) {
+ if (range.equals(mediaType)) {
+ return mediaType;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Determines which of the <code>mediaRanges</code> specifiactions is
prefered by this <code>MediaRangeList</code>.
+ * @param mediaRanges String representations of <code>MediaRange</code>s.
The strings must be
+ * on the form required by {...@link MediaRange#MediaRange(String)}
+ * @see #prefer(java.util.Set)
+ * @return the <code>toString</code> representation of the prefered
<code>MediaRange</code>, or <code>null</code>
+ * if this <code>MediaRangeList</code> does not contain any of the
<code>mediaRanges</code>
+ */
+ public String prefer(String... mediaRanges) {
+ Set<MediaRange> ranges = new HashSet<MediaRange>();
+ for (String mediaRange : mediaRanges) {
+ ranges.add(new MediaRange(mediaRange));
+ }
+ return prefer(ranges).toString();
+ }
+
+ /**
+ * A code <code>MediaRange</code> represents an entry in a
<code>MediaRangeList</code>.
+ * The <code>MediaRange</code> consists of a <code>supertype</code> and a
<code>subtype</code>,
+ * optionally a quality factor parameter <code>q</code> and other
arbitrary parameters.
+ */
+ public class MediaRange implements Comparable<MediaRange> {
+ private String supertype;
+ private double q = 1;
+ private Map<String, String> parameters;
+ private String subtype;
+
+ /**
+ * Constructs a <code>MediaRange</code> from a <code>String</code>
expression.
+ * @param exp The <code>String</code> to constuct the
<code>MediaRange</code> from. The string is
+ * expected to be on the form ( "*/*"
+ * | ( type "/" "*" )
+ * | ( type "/" subtype )
+ * ) *( ";" parameter )<br/>
+ * as specified by RFC 2616, section 14.1. <br/>
+ * Examples:
+ * <ul>
+ * <li><code>text/html;q=0.8</code></li>
+ * <li><code>text/html</code></li>
+ * <li><code>text/html;level=3</code></li>
+ * <li><code>text/html;level=3;q=0.7</code></li>
+ * <li><code>text/*</code></li>
+ * <li><code>*/*</code></li>
+ * </ul>
+ * Note that if the supertype component is wildcard (<code>*</code>),
then the subtype component
+ * must also be wildcard.<br />
+ * The quality factor parameter must be between <code>0</code> and
<code>1</code>, inclusive
+ * (see <a
href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.9">RFC 2616
section 3.9</a>).
+ * If the expression does not contain a <code>q</code> parameter, the
<code>MediaRange</code> is given
+ * a default quality factor of <code>1</code>.
+ * @throws IllegalArgumentException if <code>exp</code> can not be
parsed to a valid media range
+ * @throws NullPointerException if <code>exp</code> is
<code>null</code>
+ */
+ public MediaRange(String exp) {
+ String[] parts = exp.split(";");
+ this.setType(parts[0].trim());
+ if (parts.length > 1) {
+ this.parameters = new HashMap<String, String>(parts.length -
1);
+ }
+ for (int i = 1, partsLength = parts.length; i < partsLength; i++) {
+ String parameter = parts[i];
+ String[] keyValue = parameter.split("=");
+ if (keyValue[0].equals("q")) {
+ this.q = Double.parseDouble(keyValue[1]);
+ if (this.q < 0 || this.q > 1) {
+ throw new IllegalArgumentException("Quality factor out
of bounds: " + exp);
+ }
+ }
+ this.parameters.put(keyValue[0], keyValue[1]);
+ }
+ }
+
+ /**
+ * Constructs a <code>MediaRange</code> of the given
<code>supertype</code> and <code>subtype</code>.
+ * The quality factor is given the default value of <code>1</code>.
+ * @param supertype The super type of the media range
+ * @param subtype The sub type of the media range
+ */
+ MediaRange(String supertype, String subtype) {
+ this.setType(supertype, subtype);
+ }
+
+
+ /**
+ * Returns <code>true</code> if this is a catch-all media range
(<code>*/*</code>).
+ * @return <code>true</code> if this range is a catch-all media range,
<code>false</code> otherwise
+ */
+ public boolean matchesAll() {
+ return this.supertype.equals(WILDCARD) &&
this.subtype.equals(WILDCARD);
+ }
+
+ private void setType(String supertype, String subtype) {
+ this.supertype = supertype == null ? WILDCARD : supertype;
+ this.subtype = subtype == null ? WILDCARD : subtype;
+ if (this.supertype.equals(WILDCARD) &&
!this.subtype.equals(WILDCARD)) {
+ throw new IllegalArgumentException("Supertype cannot be
wildcard if subtype is not");
+ }
+ }
+
+ private void setType(String typeDef) {
+ String[] parts = typeDef.split("/");
+ this.setType(parts[0], parts[1]);
+ }
+
+ MediaRange(String supertype, String subtype, double q) {
+ this(supertype, subtype);
+ this.q = q;
+ }
+
+
+ public String getParameter(String key) {
+ if (parameters != null) {
+ return parameters.get(key);
+ } else {
+ return null;
+ }
+ }
+
+ public String getSupertype() {
+ return supertype;
+ }
+
+ public String getSubtype() {
+ return subtype;
+ }
+
+ /**
+ * Get the value of the quality factor parameter (<code>q</code>).
+ * @return the quality factor
+ */
+ public double getQ() {
+ return q;
+ }
+
+ public Map<String, String> getParameters() {
+ return parameters != null ? parameters : new HashMap<String,
String>(0);
+ }
+
+ /* -- Comparable implementation -- */
+ public int compareTo(MediaRange o) {
+ double diff = this.q - o.getQ();
+ if (diff == 0) {
+ // Compare parameters
+ int paramDiff = o.getParameters().size() -
this.getParameters().size();
+ if (paramDiff != 0) {
+ return paramDiff;
+ }
+ // Compare wildcards
+ if (this.supertype.equals(WILDCARD) &&
!o.getSupertype().equals(WILDCARD)) {
+ return 1;
+ } else if (!this.supertype.equals(WILDCARD) &&
o.getSupertype().equals(WILDCARD)) {
+ return -1;
+ }
+ if (this.subtype.equals(WILDCARD) &&
!o.getSubtype().equals(WILDCARD)) {
+ return 1;
+ } else if (!this.subtype.equals(WILDCARD) &&
o.getSubtype().equals(WILDCARD)) {
+ return -1;
+ }
+ // Compare names
+ return this.toString().compareTo(o.toString());
+ } else {
+ return diff > 0 ? -1 : 1;
+ }
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof MediaRange) {
+ MediaRange mr = (MediaRange) obj;
+ return mr.getSupertype().equals(this.supertype) &&
mr.getSubtype().equals(this.subtype);
+ }
+ return super.equals(obj);
+ }
+
+ public boolean equals(String s) {
+ return (this.supertype + "/" + this.subtype).equals(s);
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer buf = new StringBuffer(this.supertype + "/" +
this.subtype);
+ if (parameters != null) {
+ String delimiter = ";";
+ for (String key : parameters.keySet()) {
+ buf.append(delimiter);
+ buf.append(key).append("=").append(parameters.get(key));
+ }
+ }
+ return buf.toString();
+ }
+ }
+}
Modified:
sling/trunk/bundles/servlets/post/src/test/java/org/apache/sling/servlets/post/impl/SlingPostServletTest.java
URL:
http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/test/java/org/apache/sling/servlets/post/impl/SlingPostServletTest.java?rev=921261&r1=921260&r2=921261&view=diff
==============================================================================
---
sling/trunk/bundles/servlets/post/src/test/java/org/apache/sling/servlets/post/impl/SlingPostServletTest.java
(original)
+++
sling/trunk/bundles/servlets/post/src/test/java/org/apache/sling/servlets/post/impl/SlingPostServletTest.java
Wed Mar 10 09:19:30 2010
@@ -20,8 +20,11 @@ package org.apache.sling.servlets.post.i
import junit.framework.TestCase;
+import org.apache.sling.api.servlets.HtmlResponse;
import org.apache.sling.commons.testing.sling.MockSlingHttpServletRequest;
import org.apache.sling.servlets.post.SlingPostConstants;
+import org.apache.sling.servlets.post.impl.helper.JSONResponse;
+import org.apache.sling.servlets.post.impl.helper.MediaRangeList;
public class SlingPostServletTest extends TestCase {
@@ -53,6 +56,22 @@ public class SlingPostServletTest extend
servlet.isSetStatus(req));
}
+ public void testGetJsonResponse() {
+ MockSlingHttpServletRequest req = new
MockSlingHttpServletRequest(null, null, null, null, null) {
+ @Override
+ public String getHeader(String name) {
+ return name.equals(MediaRangeList.HEADER_ACCEPT) ?
"application/json" : super.getHeader(name);
+ }
+
+ public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+ return null;
+ }
+ };
+ SlingPostServlet servlet = new SlingPostServlet();
+ HtmlResponse result = servlet.createHtmlResponse(req);
+ assertTrue(result instanceof JSONResponse);
+ }
+
private static class StatusParamSlingHttpServletRequest extends
MockSlingHttpServletRequest {
@@ -75,5 +94,9 @@ public class SlingPostServletTest extend
void setStatusParam(String statusParam) {
this.statusParam = statusParam;
}
+
+ public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+ return null;
+ }
}
}
Added:
sling/trunk/bundles/servlets/post/src/test/java/org/apache/sling/servlets/post/impl/helper/JsonResponseTest.java
URL:
http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/test/java/org/apache/sling/servlets/post/impl/helper/JsonResponseTest.java?rev=921261&view=auto
==============================================================================
---
sling/trunk/bundles/servlets/post/src/test/java/org/apache/sling/servlets/post/impl/helper/JsonResponseTest.java
(added)
+++
sling/trunk/bundles/servlets/post/src/test/java/org/apache/sling/servlets/post/impl/helper/JsonResponseTest.java
Wed Mar 10 09:19:30 2010
@@ -0,0 +1,266 @@
+/*
+ * 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.sling.servlets.post.impl.helper;
+
+import junit.framework.TestCase;
+import org.apache.sling.api.servlets.HtmlResponse;
+import org.apache.sling.commons.json.JSONArray;
+import org.apache.sling.commons.json.JSONException;
+import org.apache.sling.commons.json.JSONObject;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.Locale;
+
+public class JsonResponseTest extends TestCase {
+ protected JSONResponse res;
+
+ public void setUp() throws Exception {
+ res = new JSONResponse();
+ super.setUp();
+ }
+
+ public void testOnChange() throws Exception {
+ res.onChange("modified", "argument1", "argument2");
+ Object prop = res.getProperty("changes");
+ JSONArray changes = assertInstanceOf(prop, JSONArray.class);
+ assertEquals(1, changes.length());
+ Object obj = changes.get(0);
+ JSONObject change = assertInstanceOf(obj, JSONObject.class);
+ assertEquals("modified", assertProperty(change,
JSONResponse.PROP_TYPE, String.class));
+ JSONArray arguments = assertProperty(change,
JSONResponse.PROP_ARGUMENT, JSONArray.class);
+ assertEquals(2, arguments.length());
+ }
+
+ public void testSetProperty() throws Exception {
+ res.setProperty("prop", "value");
+ assertProperty(res.getJson(), "prop", String.class);
+ }
+
+ @SuppressWarnings({"ThrowableInstanceNeverThrown"})
+ public void testSetError() throws IOException, JSONException {
+ String errMsg = "Dummy error";
+ res.setError(new Error(errMsg));
+ MockHttpServletResponse resp = new MockHttpServletResponse();
+ res.send(resp, true);
+ JSONObject json = res.getJson();
+ JSONObject error = assertProperty(json, "error", JSONObject.class);
+ assertProperty(error, "class", Error.class.getName());
+ assertProperty(error, "message", errMsg);
+ }
+
+ public void testSend() throws Exception {
+ res.onChange("modified", "argument1");
+ MockHttpServletResponse response = new MockHttpServletResponse();
+ res.send(response, true);
+ JSONObject result = new JSONObject(response.getOutput().toString());
+ assertProperty(result, HtmlResponse.PN_STATUS_CODE,
HttpServletResponse.SC_OK);
+ assertEquals(JSONResponse.RESPONSE_CONTENT_TYPE,
response.getContentType());
+ assertEquals(JSONResponse.RESPONSE_CHARSET,
response.getCharacterEncoding());
+ }
+
+ private static <T> T assertProperty(JSONObject obj, String key, Class<T>
clazz) throws JSONException {
+ assertTrue("JSON object does not have property " + key, obj.has(key));
+ return assertInstanceOf(obj.get(key), clazz);
+ }
+
+ @SuppressWarnings({"unchecked"})
+ private static <T> T assertProperty(JSONObject obj, String key, T
expected) throws JSONException {
+ T res = (T) assertProperty(obj, key, expected.getClass());
+ assertEquals(expected, res);
+ return res;
+ }
+
+ @SuppressWarnings({"unchecked"})
+ private static <T> T assertInstanceOf(Object obj, Class<T> clazz) {
+ try {
+ return (T) obj;
+ } catch (ClassCastException e) {
+ TestCase.fail("Object is of unexpected type. Expected: " +
clazz.getName() + ", actual: " + obj.getClass().getName());
+ return null;
+ }
+ }
+
+ private static class MockHttpServletResponse implements
HttpServletResponse {
+
+ private StringBuffer output = new StringBuffer();
+ private String contentType;
+ private String encoding;
+
+ public StringBuffer getOutput() {
+ return output;
+ }
+
+ public void addCookie(Cookie cookie) {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".addCookie");
+ }
+
+ public boolean containsHeader(String s) {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".containsHeader");
+ }
+
+ public String encodeURL(String s) {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".encodeURL");
+ }
+
+ public String encodeRedirectURL(String s) {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".encodeRedirectURL");
+ }
+
+ public String encodeUrl(String s) {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".encodeUrl");
+ }
+
+ public String encodeRedirectUrl(String s) {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".encodeRedirectUrl");
+ }
+
+ public void sendError(int i, String s) throws IOException {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".sendError");
+ }
+
+ public void sendError(int i) throws IOException {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".sendError");
+ }
+
+ public void sendRedirect(String s) throws IOException {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".sendRedirect");
+ }
+
+ public void setDateHeader(String s, long l) {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".setDateHeader");
+ }
+
+ public void addDateHeader(String s, long l) {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".addDateHeader");
+ }
+
+ public void setHeader(String s, String s1) {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".setHeader");
+ }
+
+ public void addHeader(String s, String s1) {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".addHeader");
+ }
+
+ public void setIntHeader(String s, int i) {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".setIntHeader");
+ }
+
+ public void addIntHeader(String s, int i) {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".addIntHeader");
+ }
+
+ public void setStatus(int i) {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".setStatus");
+ }
+
+ public void setStatus(int i, String s) {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".setStatus");
+ }
+
+ public String getCharacterEncoding() {
+ return encoding;
+ }
+
+ public String getContentType() {
+ return contentType;
+ }
+
+ public ServletOutputStream getOutputStream() throws IOException {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".getOutputStream");
+ }
+
+ public PrintWriter getWriter() throws IOException {
+ MockHttpServletResponse.MockWriter writer = new MockWriter(output);
+ return new PrintWriter(writer);
+ }
+
+ public void setCharacterEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+ public void setContentLength(int i) {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".setContentLength");
+ }
+
+ public void setContentType(String contentType) {
+ this.contentType = contentType;
+ }
+
+ public void setBufferSize(int i) {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".setBufferSize");
+ }
+
+ public int getBufferSize() {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".getBufferSize");
+ }
+
+ public void flushBuffer() throws IOException {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".flushBuffer");
+ }
+
+ public void resetBuffer() {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".resetBuffer");
+ }
+
+ public boolean isCommitted() {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".isCommitted");
+ }
+
+ public void reset() {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".reset");
+ }
+
+ public void setLocale(Locale locale) {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".setLocale");
+ }
+
+ public Locale getLocale() {
+ throw new UnsupportedOperationException("Not implemented: " +
getClass().getName() + ".getLocale");
+ }
+
+ private class MockWriter extends Writer {
+ private StringBuffer buf;
+
+ public MockWriter(StringBuffer output) {
+ buf = output;
+ }
+
+ @Override
+ public void write(char[] cbuf, int off, int len) throws
IOException {
+ buf.append(cbuf, off, len);
+ }
+
+ @Override
+ public void flush() throws IOException {
+ buf.setLength(0);
+ }
+
+ @Override
+ public void close() throws IOException {
+ buf = null;
+ }
+ }
+ }
+
+}
Added:
sling/trunk/bundles/servlets/post/src/test/java/org/apache/sling/servlets/post/impl/helper/MediaRangeListTest.java
URL:
http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/test/java/org/apache/sling/servlets/post/impl/helper/MediaRangeListTest.java?rev=921261&view=auto
==============================================================================
---
sling/trunk/bundles/servlets/post/src/test/java/org/apache/sling/servlets/post/impl/helper/MediaRangeListTest.java
(added)
+++
sling/trunk/bundles/servlets/post/src/test/java/org/apache/sling/servlets/post/impl/helper/MediaRangeListTest.java
Wed Mar 10 09:19:30 2010
@@ -0,0 +1,66 @@
+/*
+ * 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.sling.servlets.post.impl.helper;
+
+import junit.framework.TestCase;
+import org.apache.sling.commons.testing.sling.MockSlingHttpServletRequest;
+
+public class MediaRangeListTest extends TestCase {
+ protected MediaRangeList rangeList;
+
+ public void setUp() throws Exception {
+ super.setUp();
+ rangeList = new MediaRangeList("text/*;q=0.3, text/html;q=0.7,
text/html;level=1,\n" +
+ " text/html;level=2;q=0.4, */*;q=0.5");
+ }
+
+ public void testContains() throws Exception {
+ assertTrue(rangeList.contains("text/html"));
+ assertTrue(rangeList.contains("application/json")); // Since rangeList
contains */*
+ assertTrue(rangeList.contains("text/plain"));
+ }
+
+ public void testPrefer() throws Exception {
+ assertEquals("text/html;level=1",
rangeList.prefer("text/html;level=1", "*/*"));
+ }
+
+ public void testPreferJson() {
+ MediaRangeList rangeList = new MediaRangeList("text/html;q=0.8,
application/json");
+ assertEquals("application/json", rangeList.prefer("text/html",
"application/json"));
+ }
+
+ public void testHttpEquivParam() {
+ MockSlingHttpServletRequest req = new
MockSlingHttpServletRequest(null, null, null, null, null) {
+ @Override
+ public String getHeader(String name) {
+ return name.equals(MediaRangeList.HEADER_ACCEPT) ?
"text/plain" : super.getHeader(name);
+ }
+
+ @Override
+ public String getParameter(String name) {
+ return name.equals(MediaRangeList.PARAM_ACCEPT) ? "text/html"
: super.getParameter(name);
+ }
+
+ public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+ return null;
+ }
+ };
+ MediaRangeList rangeList = new MediaRangeList(req);
+ assertTrue("Did not contain media type from query param",
rangeList.contains("text/html"));
+ assertFalse("Contained media type from overridden Accept header",
rangeList.contains("text/plain"));
+ }
+}