
com.composum.sling.core.servlet.AbstractServiceServlet Maven / Gradle / Ivy
package com.composum.sling.core.servlet;
import com.composum.sling.core.ResourceHandle;
import com.composum.sling.core.Restricted;
import com.composum.sling.core.mapping.MappingRules;
import com.composum.sling.core.service.RestrictedService;
import com.composum.sling.core.service.ServiceRestrictions;
import com.composum.sling.core.util.I18N;
import com.composum.sling.core.util.JsonUtil;
import com.composum.sling.core.util.LinkUtil;
import com.composum.sling.core.util.ResourceUtil;
import com.composum.sling.core.util.ResponseUtil;
import com.composum.sling.core.util.XSS;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.InstanceCreator;
import com.google.gson.stream.JsonWriter;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.request.RequestPathInfo;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.jetbrains.annotations.NotNull;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceReference;
import org.osgi.service.component.annotations.Deactivate;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Iterator;
import java.util.Map;
/**
* A basic class for all '/bin/{service}/path/to/resource' servlets.
*/
public abstract class AbstractServiceServlet extends SlingAllMethodsServlet implements RestrictedService {
public static final String PARAM_FILE = "file";
public static final String PARAM_CMD = "cmd";
public static final String PARAM_FILTER = "filter";
public static final String PARAM_ID = "id";
public static final String PARAM_INDEX = "index";
public static final String PARAM_JCR_CONTENT = "jcrContent";
public static final String PARAM_LABEL = "label";
public static final String PARAM_MIME_TYPE = "mimeType";
public static final String PARAM_NAME = "name";
public static final String PARAM_PATH = "path";
public static final String PARAM_BEFORE = "before";
public static final String PARAM_QUERY = "query";
public static final String PARAM_RESOURCE_TYPE = "resourceType";
public static final String PARAM_TITLE = "title";
public static final String PARAM_TYPE = "type";
public static final String PARAM_URL = "url";
public static final String PARAM_VALUE = "value";
public static final String PARAM_VERSION = "version";
public static final String DATE_FORMAT = "yyyy-MM-DD HH:mm:ss";
private ServiceRestrictions restrictionsService;
@Deactivate
protected void deactivate() {
restrictionsService = null;
}
protected ServiceRestrictions getRestrictions() {
if (restrictionsService == null) {
final BundleContext bundleContext = FrameworkUtil.getBundle(getClass()).getBundleContext();
final ServiceReference> reference =
bundleContext.getServiceReference(ServiceRestrictions.class.getName());
restrictionsService = (ServiceRestrictions) bundleContext.getService(reference);
}
return restrictionsService;
}
@Override
public ServiceRestrictions.Key getServiceKey() {
final Restricted restricted = getClass().getAnnotation(Restricted.class);
return restricted != null ? new ServiceRestrictions.Key(restricted.key()) : null;
}
protected boolean isEnabled(@NotNull final SlingHttpServletRequest request,
@NotNull final SlingHttpServletResponse response,
@NotNull final ServiceRestrictions.Permission needed) {
return getRestrictions().isPermissible(request, getServiceKey(), needed);
}
@NotNull
protected ServiceRestrictions.Permission methodGetPermission(@NotNull final SlingHttpServletRequest request) {
return ServiceRestrictions.Permission.read;
}
@NotNull
protected ServiceRestrictions.Permission methodPostPermission(@NotNull final SlingHttpServletRequest request) {
return ServiceRestrictions.Permission.write;
}
@NotNull
protected ServiceRestrictions.Permission methodPutPermission(@NotNull final SlingHttpServletRequest request) {
return ServiceRestrictions.Permission.write;
}
@NotNull
protected ServiceRestrictions.Permission methodDeletePermission(@NotNull final SlingHttpServletRequest request) {
return ServiceRestrictions.Permission.write;
}
//
// Servlet method implementation using ServletOperationSet
//
/**
* Each servlet implementation must provide access to its operation set for request delegation.
*/
@NotNull
protected abstract ServletOperationSet, ?> getOperations();
@Override
protected void doGet(@NotNull final SlingHttpServletRequest request,
@NotNull final SlingHttpServletResponse response)
throws ServletException, IOException {
if (isEnabled(request, response, methodGetPermission(request))) {
setNoCacheHeaders(response);
getOperations().doGet(request, response);
} else {
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}
}
@Override
protected void doPost(@NotNull final SlingHttpServletRequest request,
@NotNull final SlingHttpServletResponse response)
throws ServletException, IOException {
if (isEnabled(request, response, methodPostPermission(request))) {
setNoCacheHeaders(response);
getOperations().doPost(request, response);
} else {
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}
}
@Override
protected void doPut(@NotNull final SlingHttpServletRequest request,
@NotNull final SlingHttpServletResponse response)
throws ServletException, IOException {
if (isEnabled(request, response, methodPutPermission(request))) {
setNoCacheHeaders(response);
getOperations().doPut(request, response);
} else {
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}
}
@Override
protected void doDelete(@NotNull final SlingHttpServletRequest request,
@NotNull final SlingHttpServletResponse response)
throws ServletException, IOException {
if (isEnabled(request, response, methodDeletePermission(request))) {
setNoCacheHeaders(response);
getOperations().doDelete(request, response);
} else {
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}
}
//
// HTTP control methods
//
public static void setNoCacheHeaders(@NotNull final SlingHttpServletResponse response) {
response.setHeader("Cache-Control", "no-cache");
response.addHeader("Cache-Control", "no-store");
response.addHeader("Cache-Control", "must-revalidate");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
}
//
// service resource retrieval
//
/**
* Retrieves the resource using the suffix from the request.
*
* @param request the sling request with the resource path in the suffix
* @return the resource (NOT null
; returns a handle with an invalid resource if not resolvable)
*/
@NotNull
public static ResourceHandle getResource(SlingHttpServletRequest request) {
ResourceResolver resolver = request.getResourceResolver();
String path = getPath(request);
Resource resource = resolver.resolve(path);
if (ResourceUtil.isNonExistingResource(resource)) {
String decoded = LinkUtil.decodePath(path);
resource = resolver.resolve(decoded);
}
return ResourceHandle.use(resource);
}
public static String getPath(SlingHttpServletRequest request) {
RequestPathInfo reqPathInfo = request.getRequestPathInfo();
String path = reqPathInfo.getSuffix();
if (StringUtils.isBlank(path)) {
path = request.getParameter(PARAM_PATH);
}
path = XSS.filter(path);
path = path.replaceAll("&", "&"); // rollback encoding of '&' done by the filter()
return path;
}
/**
* @return the given resource if valid, otherwise the resource referenced by the raw suffix (no XSS filter)
* if such a resource is available - to support select and rename of nodes with invalid names (node repair)
*/
@NotNull
public static ResourceHandle tryToUseRawSuffix(@NotNull final SlingHttpServletRequest request,
@NotNull ResourceHandle resource) {
if (!resource.isValid()) {
// try to use the resource path as requested (without XSS filter) - in console context only!
String resourcePath = request.getRequestPathInfo().getSuffix();
Resource requested = null;
if (StringUtils.isNotBlank(resourcePath)) {
requested = request.getResourceResolver().getResource(resourcePath);
}
if (requested != null) {
resource = ResourceHandle.use(requested);
}
}
return resource;
}
//
// default responses...
//
protected void jsonAnswerItemExists(SlingHttpServletRequest request, SlingHttpServletResponse response)
throws IOException { // TODO use Status. Unfortunately somewhat risky.
response.setStatus(HttpServletResponse.SC_CONFLICT);
JsonWriter jsonWriter = ResponseUtil.getJsonWriter(response);
jsonWriter.beginObject();
jsonWriter.name("success").value(false);
jsonWriter.name("messages").beginArray();
jsonWriter.beginObject();
jsonWriter.name("level").value("warn");
jsonWriter.name("text").value(I18N.get(request,
"An element with the same name exists already - use a different name!"));
jsonWriter.endObject();
jsonWriter.endArray();
jsonWriter.endObject();
}
//
// JSON answer helpers
//
public static void jsonValue(JsonWriter writer, Object value) throws IOException {
if (value instanceof String) {
writer.value((String) value);
} else if (value instanceof Map) {
Map, ?> map = (Map, ?>) value;
writer.beginObject();
for (Object key : map.keySet()) {
writer.name(key.toString());
jsonValue(writer, map.get(key));
}
writer.endObject();
} else if (value instanceof Object[]) {
writer.beginArray();
for (Object v : (Object[]) value) {
jsonValue(writer, v);
}
writer.endArray();
} else if (value instanceof Iterable) {
writer.beginArray();
for (Object v : (Iterable>) value) {
jsonValue(writer, v);
}
writer.endArray();
} else if (value instanceof Iterator) {
Iterator> iterator = (Iterator>) value;
writer.beginArray();
while (iterator.hasNext()) {
jsonValue(writer, iterator.next());
}
writer.endArray();
} else if (value instanceof Calendar) {
writer.value(new SimpleDateFormat(DATE_FORMAT).format(((Calendar) value).getTime()));
} else {
writer.value(value != null ? value.toString() : null);
}
}
//
// JSON parameters parsing
//
public static T getJsonObject(SlingHttpServletRequest request, Class type)
throws IOException {
InputStream inputStream = request.getInputStream();
Reader inputReader = new InputStreamReader(inputStream, MappingRules.CHARSET.name());
// parse JSON input into a POJO of the requested type
Gson gson = JsonUtil.GSON_BUILDER.create();
return gson.fromJson(inputReader, type);
}
public static T getJsonObject(SlingHttpServletRequest request, Class type,
InstanceCreator instanceCreator)
throws IOException {
InputStream inputStream = request.getInputStream();
Reader inputReader = new InputStreamReader(inputStream, MappingRules.CHARSET.name());
// parse JSON input into a POJO of the requested type
Gson gson = new GsonBuilder().registerTypeAdapter(type, instanceCreator).create();
return gson.fromJson(inputReader, type);
}
public static T getJsonObject(String input, Class type) {
Reader inputReader = new StringReader(input);
// parse JSON input into a POJO of the requested type
Gson gson = JsonUtil.GSON_BUILDER.create();
return gson.fromJson(inputReader, type);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy