Return-Path: Delivered-To: apmail-sling-commits-archive@www.apache.org Received: (qmail 73726 invoked from network); 10 Mar 2010 09:20:28 -0000 Received: from unknown (HELO mail.apache.org) (140.211.11.3) by 140.211.11.9 with SMTP; 10 Mar 2010 09:20:28 -0000 Received: (qmail 25580 invoked by uid 500); 10 Mar 2010 09:19:59 -0000 Delivered-To: apmail-sling-commits-archive@sling.apache.org Received: (qmail 25520 invoked by uid 500); 10 Mar 2010 09:19:58 -0000 Mailing-List: contact commits-help@sling.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@sling.apache.org Delivered-To: mailing list commits@sling.apache.org Received: (qmail 25513 invoked by uid 99); 10 Mar 2010 09:19:58 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 10 Mar 2010 09:19:58 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 10 Mar 2010 09:19:53 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 123DE238897A; Wed, 10 Mar 2010 09:19:31 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r921261 - in /sling/trunk/bundles/servlets/post/src: main/java/org/apache/sling/servlets/post/impl/ main/java/org/apache/sling/servlets/post/impl/helper/ test/java/org/apache/sling/servlets/post/impl/ test/java/org/apache/sling/servlets/pos... Date: Wed, 10 Mar 2010 09:19:30 -0000 To: commits@sling.apache.org From: vramdal@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20100310091931.123DE238897A@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org 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: + *
    + *
  • the request has an Accept header of application/json
  • + *
  • the request is a JSON POST request (see SLING-1172)
  • + *
  • the request has a request parameter :accept=application/json
  • + *
+ * 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 RFC 2616 section 14.1 + */ +public class MediaRangeList extends TreeSet { + 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 MediaRangeList using information from the supplied HttpServletRequest. + * 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 HttpServletRequest to extract a MediaRangeList 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 MediaRangeList using a list of media ranges specified in a java.lang.String. + * The string is a comma-separated list of media ranges, as specified by the RFC.
+ * Examples: + *
    + *
  • text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5
  • + *
  • text/html;q=0.8, application/json
  • + *
+ * + * @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 type/subtype. Neither type + * or subtype should be wildcard (*). + * @return true if this MediaRangeList contains a media type that matches + * mediaType, false otherwise + * @throws IllegalArgumentException if mediaType is not on an accepted form + * @throws NullPointerException if mediaType is null + */ + 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 MediaRangeList. + * @param mediaRanges An array of possible {@link org.apache.sling.servlets.post.impl.helper.MediaRangeList.MediaRange}s + * @return One of the mediaRanges that this MediaRangeList prefers; + * or null if this MediaRangeList does not contain any of the mediaRanges + * @throws NullPointerException if mediaRanges is null or contains a null value + */ + public MediaRange prefer(Set mediaRanges) { + for (MediaRange range : this) { + for (MediaRange mediaType : mediaRanges) { + if (range.equals(mediaType)) { + return mediaType; + } + } + } + return null; + } + + /** + * Determines which of the mediaRanges specifiactions is prefered by this MediaRangeList. + * @param mediaRanges String representations of MediaRanges. The strings must be + * on the form required by {@link MediaRange#MediaRange(String)} + * @see #prefer(java.util.Set) + * @return the toString representation of the prefered MediaRange, or null + * if this MediaRangeList does not contain any of the mediaRanges + */ + public String prefer(String... mediaRanges) { + Set ranges = new HashSet(); + for (String mediaRange : mediaRanges) { + ranges.add(new MediaRange(mediaRange)); + } + return prefer(ranges).toString(); + } + + /** + * A code MediaRange represents an entry in a MediaRangeList. + * The MediaRange consists of a supertype and a subtype, + * optionally a quality factor parameter q and other arbitrary parameters. + */ + public class MediaRange implements Comparable { + private String supertype; + private double q = 1; + private Map parameters; + private String subtype; + + /** + * Constructs a MediaRange from a String expression. + * @param exp The String to constuct the MediaRange from. The string is + * expected to be on the form ( "*/*" + * | ( type "/" "*" ) + * | ( type "/" subtype ) + * ) *( ";" parameter )
+ * as specified by RFC 2616, section 14.1.
+ * Examples: + *
    + *
  • text/html;q=0.8
  • + *
  • text/html
  • + *
  • text/html;level=3
  • + *
  • text/html;level=3;q=0.7
  • + *
  • text/*
  • + *
  • */*
  • + *
+ * Note that if the supertype component is wildcard (*), then the subtype component + * must also be wildcard.
+ * The quality factor parameter must be between 0 and 1, inclusive + * (see RFC 2616 section 3.9). + * If the expression does not contain a q parameter, the MediaRange is given + * a default quality factor of 1. + * @throws IllegalArgumentException if exp can not be parsed to a valid media range + * @throws NullPointerException if exp is null + */ + public MediaRange(String exp) { + String[] parts = exp.split(";"); + this.setType(parts[0].trim()); + if (parts.length > 1) { + this.parameters = new HashMap(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 MediaRange of the given supertype and subtype. + * The quality factor is given the default value of 1. + * @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 true if this is a catch-all media range (*/*). + * @return true if this range is a catch-all media range, false 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 (q). + * @return the quality factor + */ + public double getQ() { + return q; + } + + public Map getParameters() { + return parameters != null ? parameters : new HashMap(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 adaptTo(Class 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 adaptTo(Class 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 assertProperty(JSONObject obj, String key, Class 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 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 assertInstanceOf(Object obj, Class 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 adaptTo(Class 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")); + } +}