org.jolokia.client.request.J4pRequest Maven / Gradle / Ivy
package org.jolokia.client.request;
/*
* Copyright 2009-2013 Roland Huss
*
* Licensed 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.
*/
import java.lang.reflect.Array;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.json.simple.*;
/**
* Request object abstracting a request to a j4p agent.
*
* @author roland
* @since Apr 24, 2010
*/
public abstract class J4pRequest {
// request type
private J4pType type;
// "GET" or "POST"
private String preferredHttpMethod;
// target configuration for this request when used as a JSR-160 proxy
private J4pTargetConfig targetConfig;
/**
* Constructor for subclasses
* @param pType type of this request
* @param pTargetConfig a target configuration if used in proxy mode or null
* if this is a direct request
*/
protected J4pRequest(J4pType pType, J4pTargetConfig pTargetConfig) {
type = pType;
targetConfig = pTargetConfig;
}
/**
* Escape a input (like the part of an path) so that it can be safely used
* e.g. as a path
*
* @param pInput input to escape
* @return the escaped input
*/
public static String escape(String pInput) {
return pInput.replaceAll("!","!!").replaceAll("/","!/");
}
/**
* Get the type of the request
*
* @return request's type
*/
public J4pType getType() {
return type;
}
/**
* Get a target configuration for use with an agent in JSR-160 proxy mode
*
* @return the target config or null
if this is a direct request
*/
public J4pTargetConfig getTargetConfig() {
return targetConfig;
}
/**
* The preferred HTTP method to use (either 'GET' or 'POST')
* @return the HTTP method to use for this request, or null
if the method should be automatically selected.
*/
public String getPreferredHttpMethod() {
return preferredHttpMethod;
}
/**
* Set the preferred HTTP method, either 'GET' or 'POST'.
*
* @param pPreferredHttpMethod HTTP method to use.
*/
public void setPreferredHttpMethod(String pPreferredHttpMethod) {
preferredHttpMethod = pPreferredHttpMethod != null ? pPreferredHttpMethod.toUpperCase() : null;
}
// ==================================================================================================
// Methods used for building up HTTP Requests and setting up the reponse
// These methods are package visible only since are used only internally
// Get the parts to build up a GET url (without the type as the first part)
abstract List getRequestParts();
// Get a JSON representation of this request
JSONObject toJson() {
JSONObject ret = new JSONObject();
ret.put("type",type.name());
if (targetConfig != null) {
ret.put("target",targetConfig.toJson());
}
return ret;
}
/**
* Create a response from a given JSON response
*
* @param pResponse http response as obtained from the Http-Request
* @return the create response
*/
abstract > R createResponse(JSONObject pResponse);
// Helper class
protected void addPath(List pParts, String pPath) {
if (pPath != null) {
// Split up path
pParts.addAll(splitPath(pPath));
}
}
/**
* Serialize an object to a string which can be uses as URL part in a GET request
* when object should be transmitted to the agent. The serialization is
* rather limited: If it is an array, the array's member's string representation are used
* in a comma separated list (without escaping so far, so the strings must not contain any
* commas themselves). If it is not an array, the string representation ist used (Object.toString()
)
* Any null
value is transformed in the special marker [null]
which on the
* agent side is converted back into a null
.
*
* You should consider POST requests when you need a more sophisticated JSON serialization.
*
* @param pArg the argument to serialize for an GET request
* @return the string representation
*/
protected String serializeArgumentToRequestPart(Object pArg) {
if (pArg != null) {
if (pArg.getClass().isArray()) {
return getArrayForArgument((Object[]) pArg);
} else if (List.class.isAssignableFrom(pArg.getClass())) {
List list = (List) pArg;
Object[] args = new Object[list.size()];
int i = 0;
for (Object e : list) {
args[i++] = e;
}
return getArrayForArgument(args);
}
}
return nullEscape(pArg);
}
/**
* Serialize an object to an string or JSON structure for write/exec POST requests.
* Serialization is up to now rather limited:
*
* -
* If the argument is
null
null is returned.
*
* -
* If the argument is of type {@see org.json.simple.JSONAware}, the it is used directly for inclusion
* in the POST request.
*
* -
* If the argument is an array, this array's content is put into
* an {@see org.json.simple.JSONArray}, where each array member is serialized recursively.
*
* -
* If the argument is a map, it is transformed into a {@see org.json.simple.JSONObject} with the keys taken
* directly from the map and the values recursively serialized to their JSON representation.
* So it is only save fto use or a simple map with string keys.
*
* -
* If the argument is a {@link Collection}, it is transformed into a {@see JSONArray} with
* the values recursively serialized to their JSON representation.
*
* -
* Otherwise the object is used directly.
*
*
*
* Future version of this lib will probably provide a more sophisticated serialization mechanism.
* This is how it is supposed to be for the next release, currently a simplified serialization is in place
*
* @param pArg the object to serialize
* @return a JSON serialized object
*/
protected Object serializeArgumentToJson(Object pArg) {
if (pArg == null) {
return null;
} else if (pArg instanceof JSONAware) {
return pArg;
} else if (pArg.getClass().isArray()) {
return serializeArray(pArg);
} else if (pArg instanceof Map) {
return serializeMap((Map) pArg);
} else if (pArg instanceof Collection) {
return serializeCollection((Collection) pArg);
} else {
return pArg instanceof Number || pArg instanceof Boolean ? pArg : pArg.toString();
}
}
// pattern used for escaping business
private static final Pattern SLASH_ESCAPE_PATTERN = Pattern.compile("((?:[^!/]|!.)*)(?:/|$)");
private static final Pattern UNESCAPE_PATTERN = Pattern.compile("!(.)");
/**
* Split up a path taking into account proper escaping (as descibed in the
* reference manual).
*
* @param pArg string to split with escaping taken into account
* @return splitted element or null if the argument was null.
*/
protected List splitPath(String pArg) {
List ret = new ArrayList();
if (pArg != null) {
Matcher m = SLASH_ESCAPE_PATTERN.matcher(pArg);
while (m.find() && m.start(1) != pArg.length()) {
ret.add(UNESCAPE_PATTERN.matcher(m.group(1)).replaceAll("$1"));
}
}
return ret;
}
// =====================================================================================================
private Object serializeCollection(Collection pArg) {
JSONArray array = new JSONArray();
for (Object value : ((Collection) pArg)) {
array.add(serializeArgumentToJson(value));
}
return array;
}
private Object serializeMap(Map pArg) {
JSONObject map = new JSONObject();
for (Map.Entry