blob: d0bff453121aa4f1dc429eac266d64bdd8e3ea90 [file] [log] [blame]
/*
* 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.jaxrs.json.problem;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.jcr.InvalidItemStateException;
import javax.jcr.InvalidLifecycleTransitionException;
import javax.jcr.InvalidSerializedDataException;
import javax.jcr.ItemExistsException;
import javax.jcr.ItemNotFoundException;
import javax.jcr.MergeException;
import javax.jcr.NoSuchWorkspaceException;
import javax.jcr.PathNotFoundException;
import javax.jcr.ReferentialIntegrityException;
import javax.jcr.RepositoryException;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.ValueFormatException;
import javax.jcr.lock.LockException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.nodetype.InvalidNodeTypeDefinitionException;
import javax.jcr.nodetype.NoSuchNodeTypeException;
import javax.jcr.nodetype.NodeTypeExistsException;
import javax.jcr.query.InvalidQueryException;
import javax.jcr.version.VersionException;
import javax.servlet.http.HttpServletResponse;
import org.apache.sling.api.SlingException;
import org.apache.sling.api.resource.QuerySyntaxException;
import org.apache.sling.api.resource.ResourceNotFoundException;
import org.jetbrains.annotations.NotNull;
import org.osgi.annotation.versioning.ProviderType;
import com.fasterxml.jackson.databind.ObjectMapper;
@ProviderType
public class ProblemBuilder {
public static final String RESPONSE_CONTENT_TYPE = "application/problem+json";
private static final String PN_TYPE = "type";
private static final String PN_TITLE = "title";
private static final String PN_STATUS = "status";
private static final String PN_DETAIL = "detail";
private static final String PN_INSTANCE = "instance";
private int status;
private static final Set<String> RESERVED_PROPERTIES = Set.of(PN_TYPE, PN_TITLE, PN_STATUS, PN_DETAIL, PN_INSTANCE);
private Map<String, Object> properties = new HashMap<>();
public static ProblemBuilder get() {
return new ProblemBuilder();
}
private ProblemBuilder() {
withStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
public ProblemBuilder withType(@NotNull final URI type) {
properties.put(PN_TYPE, type);
return this;
}
public ProblemBuilder withTitle(@NotNull final String title) {
properties.put(PN_TITLE, title);
return this;
}
public ProblemBuilder withStatus(@NotNull final int status) {
properties.put(PN_STATUS, status);
this.status = status;
return this;
}
public ProblemBuilder fromException(Exception ex) {
if (ex instanceof SlingException) {
return fromSlingException((SlingException) ex);
}
if (ex instanceof RepositoryException) {
return fromRepositoryException((RepositoryException) ex);
}
if (ex instanceof org.apache.sling.api.resource.LoginException) {
withStatus(HttpServletResponse.SC_UNAUTHORIZED);
withDetail(ex.toString());
} else if (ex.getCause() instanceof Exception) {
fromException((Exception) ex.getCause());
withDetail(ex.toString() + "\nCause: " + properties.get(PN_DETAIL));
} else {
withStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
withDetail(ex.toString());
}
return this;
}
public ProblemBuilder fromSlingException(@NotNull SlingException exception) {
if (exception instanceof ResourceNotFoundException) {
withStatus(HttpServletResponse.SC_NOT_FOUND);
} else if (exception instanceof QuerySyntaxException) {
withStatus(HttpServletResponse.SC_BAD_REQUEST);
} else {
withStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
withDetail(exception.toString());
return this;
}
public ProblemBuilder fromRepositoryException(@NotNull RepositoryException exception) {
if (exception instanceof InvalidQueryException
|| exception instanceof NoSuchNodeTypeException
|| exception instanceof ConstraintViolationException
|| exception instanceof InvalidLifecycleTransitionException
|| exception instanceof InvalidNodeTypeDefinitionException
|| exception instanceof InvalidSerializedDataException
|| exception instanceof ReferentialIntegrityException
|| exception instanceof UnsupportedRepositoryOperationException
|| exception instanceof ValueFormatException
|| exception instanceof VersionException) {
withStatus(HttpServletResponse.SC_BAD_REQUEST);
} else if (exception instanceof javax.jcr.LoginException) {
withStatus(HttpServletResponse.SC_UNAUTHORIZED);
} else if (exception instanceof javax.jcr.AccessDeniedException
|| exception instanceof javax.jcr.security.AccessControlException) {
withStatus(HttpServletResponse.SC_FORBIDDEN);
} else if ((exception instanceof ItemNotFoundException)
|| (exception instanceof PathNotFoundException)
|| exception instanceof NoSuchWorkspaceException) {
withStatus(HttpServletResponse.SC_NOT_FOUND);
} else if (exception instanceof ItemExistsException
|| exception instanceof InvalidItemStateException
|| exception instanceof LockException
|| exception instanceof MergeException
|| exception instanceof NodeTypeExistsException) {
withStatus(HttpServletResponse.SC_CONFLICT);
} else {
withStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
withDetail(exception.toString());
return this;
}
public ProblemBuilder withDetail(@NotNull final String detail) {
properties.put(PN_DETAIL, detail);
return this;
}
public ProblemBuilder withInstance(@NotNull final URI instance) {
properties.put(PN_INSTANCE, instance);
return this;
}
public ProblemBuilder with(final String key, @NotNull final Object value) throws IllegalArgumentException {
if (RESERVED_PROPERTIES.contains(key)) {
throw new IllegalArgumentException("Property " + key + " is reserved");
}
properties.put(key, value);
return this;
}
public int getStatus() {
return status;
}
public static String statusToString(int statusCode) {
switch (statusCode) { // NOSONAR
case 100:
return "Continue";
case 101:
return "Switching Protocols";
case 102:
return "Processing (WebDAV)";
case 200:
return "OK";
case 201:
return "Created";
case 202:
return "Accepted";
case 203:
return "Non-Authoritative Information";
case 204:
return "No Content";
case 205:
return "Reset Content";
case 206:
return "Partial Content";
case 207:
return "Multi-Status (WebDAV)";
case 300:
return "Multiple Choices";
case 301:
return "Moved Permanently";
case 302:
return "Found";
case 303:
return "See Other";
case 304:
return "Not Modified";
case 305:
return "Use Proxy";
case 307:
return "Temporary Redirect";
case 400:
return "Bad Request";
case 401:
return "Unauthorized";
case 402:
return "Payment Required";
case 403:
return "Forbidden";
case 404:
return "Not Found";
case 405:
return "Method Not Allowed";
case 406:
return "Not Acceptable";
case 407:
return "Proxy Authentication Required";
case 408:
return "Request Time-out";
case 409:
return "Conflict";
case 410:
return "Gone";
case 411:
return "Length Required";
case 412:
return "Precondition Failed";
case 413:
return "Request Entity Too Large";
case 414:
return "Request-URI Too Large";
case 415:
return "Unsupported Media Type";
case 416:
return "Requested range not satisfiable";
case 417:
return "Expectation Failed";
case 422:
return "Unprocessable Entity (WebDAV)";
case 423:
return "Locked (WebDAV)";
case 424:
return "Failed Dependency (WebDAV)";
case 500:
return "Internal Server Error";
case 501:
return "Not Implemented";
case 502:
return "Bad Gateway";
case 503:
return "Service Unavailable";
case 504:
return "Gateway Time-out";
case 505:
return "HTTP Version not supported";
case 507:
return "Insufficient Storage (WebDAV)";
case 510:
return "Not Extended";
default:
return String.valueOf(statusCode);
}
}
public Problem build() {
properties.computeIfAbsent(PN_TITLE, k -> statusToString(getStatus()));
return new ObjectMapper().convertValue(properties, Problem.class);
}
public ThrowableProblem buildThrowable() {
return new ThrowableProblem(build());
}
}