From 3dae763f359b27c4c2d13dd95193dc4f76e778ab Mon Sep 17 00:00:00 2001 From: Christian Amend Date: Fri, 24 Jul 2015 16:11:13 +0200 Subject: [PATCH] [OLINGO-731] Added html as debug output and refactored structure --- .../olingo/server/api/ODataResponse.java | 2 +- .../server/api/debug/DebugInformation.java | 118 +++++++++ .../server/api/debug/DebugResponseHelper.java | 7 +- .../olingo/server/api/debug/DebugSupport.java | 10 +- .../server/api/debug/DefaultDebugSupport.java | 17 +- .../olingo/server/core/ODataHandler.java | 10 +- .../server/core/ODataHttpHandlerImpl.java | 89 ++----- .../server/core/debug/DebugInfoBody.java | 150 ------------ .../server/core/debug/DebugInfoServer.java | 87 ------- .../core/debug/DebugResponseHelperImpl.java | 229 +++++++++--------- .../debug/{DebugInfo.java => DebugTab.java} | 2 +- .../server/core/debug/DebugTabBody.java | 149 ++++++++++++ ...oException.java => DebugTabException.java} | 88 ++++--- ...gInfoRequest.java => DebugTabRequest.java} | 25 +- ...nfoResponse.java => DebugTabResponse.java} | 26 +- ...gInfoRuntime.java => DebugTabRuntime.java} | 75 +++--- .../server/core/debug/DebugTabServer.java | 57 +++++ .../{DebugInfoUri.java => DebugTabUri.java} | 19 +- .../server/core/debug/ServerCoreDebugger.java | 137 +++++++++++ .../server/tecsvc/TechnicalServlet.java | 1 - .../olingo/server/core/ODataHandlerTest.java | 10 +- 21 files changed, 735 insertions(+), 573 deletions(-) create mode 100644 lib/server-api/src/main/java/org/apache/olingo/server/api/debug/DebugInformation.java delete mode 100644 lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugInfoBody.java delete mode 100644 lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugInfoServer.java rename lib/server-core/src/main/java/org/apache/olingo/server/core/debug/{DebugInfo.java => DebugTab.java} (98%) create mode 100644 lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabBody.java rename lib/server-core/src/main/java/org/apache/olingo/server/core/debug/{DebugInfoException.java => DebugTabException.java} (57%) rename lib/server-core/src/main/java/org/apache/olingo/server/core/debug/{DebugInfoRequest.java => DebugTabRequest.java} (82%) rename lib/server-core/src/main/java/org/apache/olingo/server/core/debug/{DebugInfoResponse.java => DebugTabResponse.java} (75%) rename lib/server-core/src/main/java/org/apache/olingo/server/core/debug/{DebugInfoRuntime.java => DebugTabRuntime.java} (70%) create mode 100644 lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabServer.java rename lib/server-core/src/main/java/org/apache/olingo/server/core/debug/{DebugInfoUri.java => DebugTabUri.java} (97%) create mode 100644 lib/server-core/src/main/java/org/apache/olingo/server/core/debug/ServerCoreDebugger.java diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataResponse.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataResponse.java index 493e7945b..7b9e9c903 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataResponse.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/ODataResponse.java @@ -31,7 +31,7 @@ import org.apache.olingo.commons.api.http.HttpStatusCode; public class ODataResponse { private int statusCode = HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(); - private Map headers = new HashMap(); + private final Map headers = new HashMap(); private InputStream content; /** diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/debug/DebugInformation.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/debug/DebugInformation.java new file mode 100644 index 000000000..9198c6b84 --- /dev/null +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/debug/DebugInformation.java @@ -0,0 +1,118 @@ +/* + * 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.olingo.server.api.debug; + +import java.util.List; +import java.util.Map; + +import org.apache.olingo.server.api.ODataRequest; +import org.apache.olingo.server.api.ODataResponse; +import org.apache.olingo.server.api.uri.UriInfo; + +/** + * This class contains all information necessary to construct a debug response. + */ +public class DebugInformation { + + private ODataRequest request; + private ODataResponse applicationResponse; + private UriInfo uriInfo; + private Exception exception; + private Map serverEnvironmentVaribles; + private List runtimeInformation; + + /** + * This method will return the ODataRequest the library created. This request will never be null but might be filled + * incompletely if there has been an exception during the request parsing. + * @return the ODataRequest the library built + */ + public ODataRequest getRequest() { + return request; + } + + public void setRequest(ODataRequest request) { + this.request = request; + } + + /** + * This method will return the ODataResponse which was filled by the Application or the library in an exception case. + * The response might be null or might not be filled completely. + * @return the response filled by the application + */ + public ODataResponse getApplicationResponse() { + return applicationResponse; + } + + public void setApplicationResponse(ODataResponse applicationResponse) { + this.applicationResponse = applicationResponse; + } + + /** + * The URI Info object the library created during URI parsing. Might be null if there was an exception during URI + * parsing. + * @return the URI Info Object + */ + public UriInfo getUriInfo() { + return uriInfo; + } + + public void setUriInfo(UriInfo uriInfo) { + this.uriInfo = uriInfo; + } + + /** + * This method will return any exception that was thrown from the application or library. Will be null if there was no + * exception. + * @return an exception if thrown. + */ + public Exception getException() { + return exception; + } + + public void setException(Exception exception) { + this.exception = exception; + } + + /** + * A map containing information about the runtime environment. Depending on the servlet or webserver used this map + * might contain different information. Will never be null but might be empty. + * @return environment variables + */ + public Map getServerEnvironmentVaribles() { + return serverEnvironmentVaribles; + } + + public void setServerEnvironmentVaribles(Map serverEnvironmentVaribles) { + this.serverEnvironmentVaribles = serverEnvironmentVaribles; + } + + /** + * This method will return all runtime information which was collected inside the library. Might be null if no data + * could be collected. + * @return runtime information collected by the library + */ + public List getRuntimeInformation() { + return runtimeInformation; + } + + public void setRuntimeInformation(List runtimeInformation) { + this.runtimeInformation = runtimeInformation; + } + +} diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/debug/DebugResponseHelper.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/debug/DebugResponseHelper.java index bf6fc563d..e2f6933b5 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/debug/DebugResponseHelper.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/debug/DebugResponseHelper.java @@ -18,10 +18,6 @@ */ package org.apache.olingo.server.api.debug; -import java.util.List; -import java.util.Map; - -import org.apache.olingo.server.api.ODataRequest; import org.apache.olingo.server.api.ODataResponse; /** @@ -38,6 +34,5 @@ public interface DebugResponseHelper { * @param runtimeInformation * @return the debug response or the raw application response in case an exception occurred. */ - ODataResponse createDebugResponse(ODataRequest request, ODataResponse applicationResponse, Exception exception, - Map serverEnvironmentVaribles, List runtimeInformation); + ODataResponse createDebugResponse(DebugInformation debugInfo); } diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/debug/DebugSupport.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/debug/DebugSupport.java index 995ba34af..7a94c4456 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/debug/DebugSupport.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/debug/DebugSupport.java @@ -18,11 +18,7 @@ */ package org.apache.olingo.server.api.debug; -import java.util.List; -import java.util.Map; - import org.apache.olingo.server.api.OData; -import org.apache.olingo.server.api.ODataRequest; import org.apache.olingo.server.api.ODataResponse; /** @@ -35,8 +31,11 @@ public interface DebugSupport { public static final String ODATA_DEBUG_HTML = "html"; public static final String ODATA_DEBUG_DOWNLOAD = "download"; + //TODO:JavaDoc void init(OData odata); + boolean isUserAuthorized(); + /** * This method should create a debug response and deliver it back to the Olingo library. This method MUST NEVER throw * an exception. @@ -46,7 +45,6 @@ public interface DebugSupport { * @param exception which has been thrown. Might be null in case there was no exception * @return a new debug response which will be send to the client */ - ODataResponse createDebugResponse(String debugFormat, ODataRequest request, ODataResponse response, - Exception exception, Map serverEnvironmentVaribles, List runtimeInformation); + ODataResponse createDebugResponse(String debugFormat, DebugInformation debugInfo); } diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/debug/DefaultDebugSupport.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/debug/DefaultDebugSupport.java index cca537f53..7079e76eb 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/debug/DefaultDebugSupport.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/debug/DefaultDebugSupport.java @@ -18,11 +18,7 @@ */ package org.apache.olingo.server.api.debug; -import java.util.List; -import java.util.Map; - import org.apache.olingo.server.api.OData; -import org.apache.olingo.server.api.ODataRequest; import org.apache.olingo.server.api.ODataResponse; /** @@ -38,18 +34,21 @@ public class DefaultDebugSupport implements DebugSupport { } @Override - public ODataResponse createDebugResponse(String debugFormat, ODataRequest request, ODataResponse applicationResponse, - Exception exception, Map serverEnvironmentVaribles, List runtimeInformation) { + public boolean isUserAuthorized() { + return true; + } + + @Override + public ODataResponse createDebugResponse(String debugFormat, DebugInformation debugInfo) { // Check if debugFormat is supported by the library if (DebugSupport.ODATA_DEBUG_JSON.equalsIgnoreCase(debugFormat) || DebugSupport.ODATA_DEBUG_HTML.equalsIgnoreCase(debugFormat) || DebugSupport.ODATA_DEBUG_DOWNLOAD.equalsIgnoreCase(debugFormat)) { - return odata.createDebugResponseHelper(debugFormat).createDebugResponse(request, applicationResponse, exception, - serverEnvironmentVaribles, runtimeInformation); + return odata.createDebugResponseHelper(debugFormat).createDebugResponse(debugInfo); } else { // Debug format is not supported by the library by default so in order to avoid an exception we will just give // back the original response from the application. - return applicationResponse; + return debugInfo.getApplicationResponse(); } } diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java index 1a0df8d45..1d1b270fd 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandler.java @@ -42,6 +42,7 @@ import org.apache.olingo.server.api.serializer.CustomContentTypeSupport; import org.apache.olingo.server.api.serializer.RepresentationType; import org.apache.olingo.server.api.serializer.SerializerException; import org.apache.olingo.server.api.uri.UriInfo; +import org.apache.olingo.server.core.debug.ServerCoreDebugger; import org.apache.olingo.server.core.uri.parser.Parser; import org.apache.olingo.server.core.uri.parser.UriParserException; import org.apache.olingo.server.core.uri.parser.UriParserSemanticException; @@ -54,15 +55,18 @@ public class ODataHandler { private final OData odata; private final ServiceMetadata serviceMetadata; private final List processors = new LinkedList(); + private final ServerCoreDebugger debugger; + private CustomContentTypeSupport customContentTypeSupport; private CustomETagSupport customETagSupport; private UriInfo uriInfo; private Exception lastThrownException; - public ODataHandler(final OData server, final ServiceMetadata serviceMetadata) { + public ODataHandler(final OData server, final ServiceMetadata serviceMetadata, ServerCoreDebugger debugger) { odata = server; this.serviceMetadata = serviceMetadata; + this.debugger = debugger; register(new DefaultRedirectProcessor()); register(new DefaultProcessor()); @@ -192,4 +196,8 @@ public class ODataHandler { public Exception getLastThrownException() { return lastThrownException; } + + public UriInfo getUriInfo(){ + return uriInfo; + } } diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java index 2bab186ad..e4435917b 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHttpHandlerImpl.java @@ -35,19 +35,19 @@ import javax.servlet.http.HttpServletResponse; import org.apache.olingo.commons.api.ODataRuntimeException; import org.apache.olingo.commons.api.http.HttpHeader; import org.apache.olingo.commons.api.http.HttpMethod; -import org.apache.olingo.server.api.ODataServerError; import org.apache.olingo.server.api.OData; import org.apache.olingo.server.api.ODataHttpHandler; +import org.apache.olingo.server.api.ODataLibraryException; import org.apache.olingo.server.api.ODataRequest; import org.apache.olingo.server.api.ODataResponse; -import org.apache.olingo.server.api.ODataLibraryException; +import org.apache.olingo.server.api.ODataServerError; import org.apache.olingo.server.api.ServiceMetadata; import org.apache.olingo.server.api.debug.DebugSupport; -import org.apache.olingo.server.api.debug.RuntimeMeasurement; import org.apache.olingo.server.api.etag.CustomETagSupport; import org.apache.olingo.server.api.processor.Processor; import org.apache.olingo.server.api.serializer.CustomContentTypeSupport; import org.apache.olingo.server.api.serializer.SerializerException; +import org.apache.olingo.server.core.debug.ServerCoreDebugger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,99 +56,52 @@ public class ODataHttpHandlerImpl implements ODataHttpHandler { private static final Logger LOG = LoggerFactory.getLogger(ODataHttpHandlerImpl.class); private final ODataHandler handler; - private final OData odata; + private final ServerCoreDebugger debugger; + private int split = 0; - // debug stuff - private final List runtimeInformation = new ArrayList(); - private DebugSupport debugSupport; - private String debugFormat; - private boolean isDebugMode = false; - public ODataHttpHandlerImpl(final OData odata, final ServiceMetadata serviceMetadata) { - this.odata = odata; - handler = new ODataHandler(odata, serviceMetadata); + debugger = new ServerCoreDebugger(odata); + handler = new ODataHandler(odata, serviceMetadata, debugger); } @Override public void process(final HttpServletRequest request, final HttpServletResponse response) { + ODataRequest odRequest = new ODataRequest(); Exception exception = null; - ODataRequest odRequest = null; ODataResponse odResponse; - resolveDebugMode(request); - int processMethodHandel = startRuntimeMeasurement("ODataHttpHandlerImpl", "process"); + debugger.resolveDebugMode(request); + int processMethodHandel = debugger.startRuntimeMeasurement("ODataHttpHandlerImpl", "process"); try { - odRequest = new ODataRequest(); - int requestHandel = startRuntimeMeasurement("ODataHttpHandlerImpl", "fillODataRequest"); + int requestHandel = debugger.startRuntimeMeasurement("ODataHttpHandlerImpl", "fillODataRequest"); fillODataRequest(odRequest, request, split); - stopRuntimeMeasurement(requestHandel); - - int responseHandel = startRuntimeMeasurement("ODataHandler", "process"); + debugger.stopRuntimeMeasurement(requestHandel); + + int responseHandel = debugger.startRuntimeMeasurement("ODataHandler", "process"); odResponse = handler.process(odRequest); - stopRuntimeMeasurement(responseHandel); + debugger.stopRuntimeMeasurement(responseHandel); // ALL future methods after process must not throw exceptions! } catch (Exception e) { exception = e; odResponse = handleException(odRequest, e); } - stopRuntimeMeasurement(processMethodHandel); + debugger.stopRuntimeMeasurement(processMethodHandel); - if (isDebugMode) { - debugSupport.init(odata); - // TODO: Should we be more careful here with response assignement in order to not loose the original response? - // TODO: How should we react to exceptions here? + if (debugger.isDebugMode()) { + Map serverEnvironmentVaribles = createEnvironmentVariablesMap(request); if (exception == null) { // This is to ensure that we have access to the thrown OData Exception - // TODO: Should we make this hack exception = handler.getLastThrownException(); } - Map serverEnvironmentVaribles = createEnvironmentVariablesMap(request); - odResponse = - debugSupport.createDebugResponse(debugFormat, odRequest, odResponse, exception, serverEnvironmentVaribles, - runtimeInformation); + debugger.createDebugResponse(request, exception, odRequest, odResponse, handler.getUriInfo(), + serverEnvironmentVaribles); } convertToHttp(response, odResponse); } - private void resolveDebugMode(HttpServletRequest request) { - if (debugSupport != null) { - // Should we read the parameter from the servlet here and ignore multiple parameters? - debugFormat = request.getParameter(DebugSupport.ODATA_DEBUG_QUERY_PARAMETER); - // Debug format is present and we have a debug support processor registered so we are in debug mode - isDebugMode = debugFormat != null; - } - } - - public int startRuntimeMeasurement(final String className, final String methodName) { - if (isDebugMode) { - int handleId = runtimeInformation.size(); - - final RuntimeMeasurement measurement = new RuntimeMeasurement(); - measurement.setTimeStarted(System.nanoTime()); - measurement.setClassName(className); - measurement.setMethodName(methodName); - - runtimeInformation.add(measurement); - - return handleId; - } else { - return 0; - } - } - - public void stopRuntimeMeasurement(final int handle) { - if (isDebugMode && handle < runtimeInformation.size()) { - long stopTime = System.nanoTime(); - RuntimeMeasurement runtimeMeasurement = runtimeInformation.get(handle); - if (runtimeMeasurement != null) { - runtimeMeasurement.setTimeStopped(stopTime); - } - } - } - private Map createEnvironmentVariablesMap(HttpServletRequest request) { LinkedHashMap environment = new LinkedHashMap(); environment.put("authType", request.getAuthType()); @@ -350,6 +303,6 @@ public class ODataHttpHandlerImpl implements ODataHttpHandler { @Override public void register(final DebugSupport debugSupport) { - this.debugSupport = debugSupport; + debugger.setDebugSupportProcessor(debugSupport); } } diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugInfoBody.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugInfoBody.java deleted file mode 100644 index e266aaec3..000000000 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugInfoBody.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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.olingo.server.core.debug; - -import java.io.IOException; -import java.io.Writer; - -import org.apache.commons.io.IOUtils; -import org.apache.olingo.commons.api.http.HttpHeader; -import org.apache.olingo.server.api.ODataResponse; - -import com.fasterxml.jackson.core.JsonGenerator; - -/** - * Response body debug information. - */ -public class DebugInfoBody implements DebugInfo { - - private static enum ResponseContent {JSON, XML, TEXT, IMAGE}; - - private final ODataResponse response; - private final ResponseContent responseContent; - - //private final String serviceRoot; -// private final boolean isXml; -// private final boolean isJson; -// private final boolean isText; -// private final boolean isImage; - - public DebugInfoBody(final ODataResponse response, final String serviceRoot) { - this.response = response; - // TODO: make header case insensitive - final String contentType = response.getHeaders().get(HttpHeader.CONTENT_TYPE); - //TODO: Differentiate better - if (contentType != null) { - responseContent = ResponseContent.JSON; - } else { - responseContent = ResponseContent.TEXT; - } -// isXml = contentType.contains("xml"); -// isJson = !isXml && contentType.startsWith(HttpContentType.APPLICATION_JSON); -// isText = isXml || isJson || contentType.startsWith("text/") -// || contentType.startsWith(HttpContentType.APPLICATION_HTTP) -// || contentType.startsWith(HttpContentType.MULTIPART_MIXED); -// isImage = !isText && contentType.startsWith("image/"); - } - - @Override - public String getName() { - return "Body"; - } - -// - @Override - public void appendJson(final JsonGenerator gen) throws IOException { - gen.writeString(getContentString()); - } - - private String getContentString() { - try { - String contentString; - switch (responseContent) { - case IMAGE: - //TODO: DecodeString as base 64 - contentString = "Currently not supported"; - break; - case JSON: - case XML: - case TEXT: - default: - // TODO: Remove IOUtils from core dependency - contentString = IOUtils.toString(response.getContent(), "UTF-8"); - break; - } - return contentString; - } catch (IOException e) { - return "Could not parse Body for Debug Output"; - } - } - -// -// @Override -// public void appendHtml(final Writer writer) throws IOException { -// final String body = getContentString(); -// if (isImage) { -// writer.append("\n"); -// } else { -// writer.append("
\n")
-//          .append(isXml || isJson ?
-//              addLinks(ODataDebugResponseWrapper.escapeHtml(isXml ? formatXml(body) : formatJson(body)), isXml) :
-//              ODataDebugResponseWrapper.escapeHtml(body))
-//          .append("
\n"); -// } -// } -// -// private String formatXml(final String xml) throws IOException { -// try { -// Transformer transformer = TransformerFactory.newInstance().newTransformer(); -// transformer.setOutputProperty(OutputKeys.INDENT, "yes"); -// transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); -// StreamResult outputTarget = new StreamResult(new StringWriter()); -// transformer.transform(new StreamSource(new StringReader(xml)), outputTarget); -// return outputTarget.getWriter().toString(); -// } catch (final TransformerException e) { -// return xml; -// } -// } -// -// private String formatJson(final String json) { -// return new GsonBuilder().disableHtmlEscaping().setPrettyPrinting().create().toJson(new JsonParser().parse(json)); -// } -// -// private String addLinks(final String source, final boolean isXml) { -// final String debugOption = ODataDebugResponseWrapper.ODATA_DEBUG_QUERY_PARAMETER + "=" -// + ODataDebugResponseWrapper.ODATA_DEBUG_HTML; -// final String urlPattern = "(" -// + (isXml ? "(?:href|src|base)=" : "\"(?:uri|media_src|edit_media|__next)\":\\p{Space}*") -// + "\")(.+?)\""; -// return (isXml ? source.replaceAll("(xmlns(?::\\p{Alnum}+)?=\")(.+?)\"", "$1$2\"") : -// source) -// .replaceAll(urlPattern, "$1$2\"") -// .replaceAll("( serverEnvironmentVaribles; - - public DebugInfoServer(Map serverEnvironmentVaribles) { - this.serverEnvironmentVaribles = serverEnvironmentVaribles; - } - - @Override - public String getName() { - return "Environment"; - } - - @Override - public void appendJson(JsonGenerator gen) throws IOException { - DebugResponseHelperImpl.appendJsonTable(gen, serverEnvironmentVaribles); - } - - @Override - public void appendHtml(Writer writer) throws IOException { - // TODO Auto-generated method stub - - } - -// private final Map environment; -// -// public DebugInfoServer(final HttpServletRequest httpServletRequest) { -// environment = new TreeMap(); -// environment.put("authType", httpServletRequest.getAuthType()); -// environment.put("localAddr", httpServletRequest.getLocalAddr()); -// environment.put("localName", httpServletRequest.getLocalName()); -// addInt("localPort", httpServletRequest.getLocalPort()); -// environment.put("pathInfo", httpServletRequest.getPathInfo()); -// environment.put("pathTranslated", httpServletRequest.getPathTranslated()); -// environment.put("remoteAddr", httpServletRequest.getRemoteAddr()); -// environment.put("remoteHost", httpServletRequest.getRemoteHost()); -// addInt("remotePort", httpServletRequest.getRemotePort()); -// environment.put("remoteUser", httpServletRequest.getRemoteUser()); -// environment.put("scheme", httpServletRequest.getScheme()); -// environment.put("serverName", httpServletRequest.getServerName()); -// addInt("serverPort", httpServletRequest.getServerPort()); -// environment.put("servletPath", httpServletRequest.getServletPath()); -// } - -// @Override -// public void appendHtml(final Writer writer) throws IOException { -// final Package pack = ODataDebugResponseWrapper.class.getPackage(); -// writer.append("

Library Version

\n") -// .append("

").append(pack.getImplementationTitle()) -// .append(" Version ").append(pack.getImplementationVersion()).append("

\n") -// .append("

Server Environment

\n"); -// ODataDebugResponseWrapper.appendHtmlTable(writer, environment); -// } -// -// private void addInt(final String name, final int number) { -// environment.put(name, number == 0 ? null : Integer.toString(number)); -// } -} diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugResponseHelperImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugResponseHelperImpl.java index 695201829..d99dd4c16 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugResponseHelperImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugResponseHelperImpl.java @@ -22,6 +22,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; +import java.io.Writer; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -32,8 +33,8 @@ import org.apache.olingo.commons.api.ODataRuntimeException; import org.apache.olingo.commons.api.format.ContentType; import org.apache.olingo.commons.api.http.HttpHeader; import org.apache.olingo.commons.api.http.HttpStatusCode; -import org.apache.olingo.server.api.ODataRequest; import org.apache.olingo.server.api.ODataResponse; +import org.apache.olingo.server.api.debug.DebugInformation; import org.apache.olingo.server.api.debug.DebugResponseHelper; import org.apache.olingo.server.api.debug.DebugSupport; import org.apache.olingo.server.api.debug.RuntimeMeasurement; @@ -62,13 +63,10 @@ public class DebugResponseHelperImpl implements DebugResponseHelper { } @Override - public ODataResponse createDebugResponse(ODataRequest request, ODataResponse applicationResponse, - Exception exception, Map serverEnvironmentVaribles, List runtimeInformation) { - + public ODataResponse createDebugResponse(DebugInformation debugInfo) { try { - final List parts = - createParts(request, applicationResponse, exception, serverEnvironmentVaribles, runtimeInformation); - + final List parts = createParts(debugInfo); + ODataResponse response = new ODataResponse(); String contentTypeString; InputStream body; @@ -78,7 +76,9 @@ public class DebugResponseHelperImpl implements DebugResponseHelper { + new Date().toString().replace(' ', '_').replace(':', '.') + ".html"); // Download is the same as html except for the above header case HTML: - body = wrapInHtml(parts); + String title = debugInfo.getRequest() == null ? + "V4 Service" : "V4 Service: " + debugInfo.getRequest().getRawODataPath(); + body = wrapInHtml(parts, title); contentTypeString = ContentType.TEXT_HTML.toContentTypeString(); break; case JSON: @@ -93,65 +93,58 @@ public class DebugResponseHelperImpl implements DebugResponseHelper { return response; } catch (IOException e) { - // Should not happen - // TODO: Check what we can do here. + // Should not happen. In case it does the library will deliver a default response. So no handling here. throw new ODataRuntimeException(e); } } - private List createParts(ODataRequest request, ODataResponse applicationResponse, Exception exception, - Map serverEnvironmentVaribles, List runtimeInformation) { - List parts = new ArrayList(); + private List createParts(DebugInformation debugInfo) { + List parts = new ArrayList(); // request - parts.add(new DebugInfoRequest(request)); + parts.add(new DebugTabRequest(debugInfo.getRequest())); // response - // TODO: Check service URI - parts.add(new DebugInfoResponse(applicationResponse, request.getRawBaseUri())); + parts.add(new DebugTabResponse(debugInfo.getApplicationResponse(), debugInfo.getRequest().getRawBaseUri())); // server + Map serverEnvironmentVaribles = debugInfo.getServerEnvironmentVaribles(); if (serverEnvironmentVaribles != null && !serverEnvironmentVaribles.isEmpty()) { - parts.add(new DebugInfoServer(serverEnvironmentVaribles)); + parts.add(new DebugTabServer(serverEnvironmentVaribles)); } -// // URI -// Throwable candidate = exception; -// while (candidate != null && !(candidate instanceof ExpressionParserException)) { -// candidate = candidate.getCause(); + // TODO:Enable URIDebugInfo + // URI +// if (uriInfo != null && (uriInfo.getFilterOption() != null || uriInfo.getOrderByOption() != null +// || uriInfo.getExpandOption() != null || uriInfo.getSelectOption() != null)) { +// parts.add(new DebugInfoUri(uriInfo)); // } -// final ExpressionParserException expressionParserException = (ExpressionParserException) candidate; -// if (uriInfo != null -// && (uriInfo.getFilter() != null || uriInfo.getOrderBy() != null -// || !uriInfo.getExpand().isEmpty() || !uriInfo.getSelect().isEmpty()) -// || expressionParserException != null && expressionParserException.getFilterTree() != null) { -// parts.add(new DebugInfoUri(uriInfo, expressionParserException)); -// } -// -// // runtime measurements + + // runtime measurements + List runtimeInformation = debugInfo.getRuntimeInformation(); if (runtimeInformation != null && !runtimeInformation.isEmpty()) { - parts.add(new DebugInfoRuntime(runtimeInformation)); + parts.add(new DebugTabRuntime(runtimeInformation)); } -// -// // exceptions - if (exception != null) { - parts.add(new DebugInfoException(exception)); + + // exceptions + if (debugInfo.getException() != null) { + parts.add(new DebugTabException(debugInfo.getException())); } return parts; } - private InputStream wrapInJson(final List parts) throws IOException { + private InputStream wrapInJson(final List parts) throws IOException { CircleStreamBuffer csb = new CircleStreamBuffer(); JsonGenerator gen = new JsonFactory().createGenerator(csb.getOutputStream(), JsonEncoding.UTF8); gen.writeStartObject(); - DebugInfo requestInfo = parts.get(0); + DebugTab requestInfo = parts.get(0); // TODO: Should we really translate to lower case here? gen.writeFieldName(requestInfo.getName().toLowerCase(Locale.ROOT)); requestInfo.appendJson(gen); - DebugInfo responseInfo = parts.get(1); + DebugTab responseInfo = parts.get(1); gen.writeFieldName(responseInfo.getName().toLowerCase(Locale.ROOT)); responseInfo.appendJson(gen); @@ -163,7 +156,7 @@ public class DebugResponseHelperImpl implements DebugResponseHelper { } else { gen.writeNullField("version"); } - for (DebugInfo part : parts.subList(2, parts.size())) { + for (DebugTab part : parts.subList(2, parts.size())) { gen.writeFieldName(part.getName().toLowerCase(Locale.ROOT)); part.appendJson(gen); } @@ -171,76 +164,72 @@ public class DebugResponseHelperImpl implements DebugResponseHelper { gen.writeEndObject(); gen.close(); + csb.close(); return csb.getInputStream(); } - private InputStream wrapInHtml(final List parts) throws IOException { + private InputStream wrapInHtml(final List parts, String title) throws IOException { StringWriter writer = new StringWriter(); -// PathInfo pathInfo = null; -// try { -// pathInfo = context.getPathInfo(); -// } catch (final ODataException e) {} -// -// writer.append("\n") -// .append("\n") -// .append("\n") -// .append("\n") -// .append("") -// .append(pathInfo == null ? "" : -// escapeHtml(pathInfo.getServiceRoot().relativize(pathInfo.getRequestUri()).getPath())) -// .append("\n") -// .append("\n") -// .append("\n") -// .append("\n"); -// char count = '0'; -// for (final DebugInfo part : parts) { -// writer.append("
\n") -// .append("
\n"); -// part.appendHtml(writer); -// writer.append("
\n"); -// } -// writer.append("\n") -// .append("\n") -// .close(); + + writer.append("\n") + .append("\n") + .append("\n") + .append("\n") + .append("") + .append(escapeHtml(title)) + .append("\n") + .append("\n") + .append("\n") + .append("\n"); + char count = '0'; + for (final DebugTab part : parts) { + writer.append("
\n") + .append("

") + .append(part.getName()) + .append("

\n") + .append("
\n") + .append("
\n"); + part.appendHtml(writer); + writer.append("
\n"); + } + writer.append("\n") + .append("\n") + .close(); byte[] bytes = writer.toString().getBytes("UTF-8"); return new ByteArrayInputStream(bytes); } @@ -262,21 +251,21 @@ public class DebugResponseHelperImpl implements DebugResponseHelper { } gen.writeEndObject(); } -// -// protected static void appendHtmlTable(final Writer writer, final Map entries) throws IOException { -// writer.append("\n\n") -// .append("\n") -// .append("\n\n"); -// for (final String name : entries.keySet()) { -// final String value = entries.get(name); -// if (value != null) { -// writer.append("") -// .append("\n"); -// } -// } -// writer.append("\n
NameValue
").append(name).append("") -// .append(ODataDebugResponseWrapper.escapeHtml(value)) -// .append("
\n"); -// } + + protected static void appendHtmlTable(final Writer writer, final Map entries) throws IOException { + writer.append("\n\n") + .append("\n") + .append("\n\n"); + for (final String name : entries.keySet()) { + final String value = entries.get(name); + if (value != null) { + writer.append("") + .append("\n"); + } + } + writer.append("\n
NameValue
").append(name).append("") + .append(escapeHtml(value)) + .append("
\n"); + } } diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugInfo.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTab.java similarity index 98% rename from lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugInfo.java rename to lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTab.java index 9c5a1d497..8847e3301 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugInfo.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTab.java @@ -27,7 +27,7 @@ import com.fasterxml.jackson.core.JsonGenerator; /** * Debug information. */ -public interface DebugInfo { +public interface DebugTab { /** * Gets the name of this debug information part, useful as title. diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabBody.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabBody.java new file mode 100644 index 000000000..43e9ba60e --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabBody.java @@ -0,0 +1,149 @@ +/* + * 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.olingo.server.core.debug; + +import java.io.IOException; +import java.io.Writer; +import java.util.regex.Pattern; + +import org.apache.commons.io.IOUtils; +import org.apache.olingo.commons.api.http.HttpHeader; +import org.apache.olingo.server.api.ODataResponse; +import org.apache.olingo.server.api.debug.DebugSupport; + +import com.fasterxml.jackson.core.JsonGenerator; + +/** + * Response body debug information. + */ +public class DebugTabBody implements DebugTab { + + private static enum ResponseContent { + JSON, XML, TEXT, IMAGE + }; + + private final ODataResponse response; + private final ResponseContent responseContent; + + private final String serviceRoot; + +// private final boolean isXml; +// private final boolean isJson; +// private final boolean isText; +// private final boolean isImage; + + public DebugTabBody(final ODataResponse response, final String serviceRoot) { + this.response = response; + this.serviceRoot = serviceRoot; + final String contentType = response.getHeaders().get(HttpHeader.CONTENT_TYPE); + // TODO: Differentiate better + if (contentType != null) { + responseContent = ResponseContent.JSON; + } else { + responseContent = ResponseContent.TEXT; + } +// isXml = contentType.contains("xml"); +// isJson = !isXml && contentType.startsWith(HttpContentType.APPLICATION_JSON); +// isText = isXml || isJson || contentType.startsWith("text/") +// || contentType.startsWith(HttpContentType.APPLICATION_HTTP) +// || contentType.startsWith(HttpContentType.MULTIPART_MIXED); +// isImage = !isText && contentType.startsWith("image/"); + } + + @Override + public String getName() { + return "Body"; + } + +// + @Override + public void appendJson(final JsonGenerator gen) throws IOException { + if (response.getContent() == null) { + gen.writeNull(); + } else { + gen.writeString(getContentString()); + } + } + + private String getContentString() { + try { + String contentString; + switch (responseContent) { + case IMAGE: + // TODO: DecodeString as base 64 + contentString = "Currently not supported"; + break; + case JSON: + case XML: + case TEXT: + default: + // TODO: Remove IOUtils from core dependency + contentString = IOUtils.toString(response.getContent(), "UTF-8"); + break; + } + return contentString; + } catch (IOException e) { + return "Could not parse Body for Debug Output"; + } + } + + @Override + public void appendHtml(final Writer writer) throws IOException { + + final String body = response.getContent() == null ? "ODataLibrary: null body." : getContentString(); + switch (responseContent) { + case XML: + writer.append("
\n");
+      writer.append(addLinks(DebugResponseHelperImpl.escapeHtml(body)));
+      writer.append("
\n"); + break; + case JSON: + writer.append("
\n");
+      writer.append(addLinks(DebugResponseHelperImpl.escapeHtml(body)));
+      writer.append("
\n"); + break; + case IMAGE: + // Make header query case insensitive + writer.append("\n"); + break; + case TEXT: + default: + writer.append("
\n");
+      writer.append(DebugResponseHelperImpl.escapeHtml(body));
+      writer.append("
\n"); + break; + } + } + + private String addLinks(final String source) { + final String debugOption = DebugSupport.ODATA_DEBUG_QUERY_PARAMETER + "=" + DebugSupport.ODATA_DEBUG_HTML; + final String urlPattern = "(" + + (responseContent == ResponseContent.XML ? + "(?:href|src|base)=" : "\"(?:uri|media_src|edit_media|__next)\":\\p{Space}*") + + "\")(.+?)\""; + return (responseContent == ResponseContent.XML ? + source.replaceAll("(xmlns(?::\\p{Alnum}+)?=\")(.+?)\"", "$1$2\"") : source) + .replaceAll(urlPattern, "$1$2\"") + .replaceAll("(Stacktrace\n"); + int count = 0; + for (final StackTraceElement stackTraceElement : exception.getStackTrace()) { + appendStackTraceElement(stackTraceElement, ++count == 1, count == exception.getStackTrace().length, writer); + } + } + + private void appendException(final Throwable throwable, final Writer writer) throws IOException { + if (throwable.getCause() != null) { + appendException(throwable.getCause(), writer); + } + final StackTraceElement details = throwable.getStackTrace()[0]; + writer.append("

").append(throwable.getClass().getCanonicalName()).append("

\n") + .append("

") + .append(DebugResponseHelperImpl.escapeHtml(getMessageText(throwable))) + .append("

\n"); + appendStackTraceElement(details, true, true, writer); + } + + private void appendStackTraceElement(final StackTraceElement stackTraceElement, + final boolean isFirst, final boolean isLast, final Writer writer) throws IOException { + if (isFirst) { + writer.append("\n\n") + .append("\n\n") + .append("\n") + .append("\n\n") + .append("\n\n"); + } + writer.append("\n\n") + .append("\n") + .append("\n\n"); + if (isLast) { + writer.append("\n
ClassMethodLine number in class
").append(stackTraceElement.getClassName()).append("").append(stackTraceElement.getMethodName()).append("").append(Integer.toString(stackTraceElement.getLineNumber())) + .append("
\n"); + } } -// -// @Override -// public void appendHtml(final Writer writer) throws IOException { -// appendException(exception, writer); -// writer.append("

Stacktrace

\n"); -// int count = 0; -// for (final StackTraceElement stackTraceElement : exception.getStackTrace()) { -// appendStackTraceElement(stackTraceElement, ++count == 1, count == exception.getStackTrace().length, writer); -// } -// } -// -// private void appendException(final Throwable throwable, final Writer writer) throws IOException { -// if (throwable.getCause() != null) { -// appendException(throwable.getCause(), writer); -// } -// final StackTraceElement details = throwable.getStackTrace()[0]; -// writer.append("

").append(throwable.getClass().getCanonicalName()).append("

\n") -// .append("

") -// .append(ODataDebugResponseWrapper.escapeHtml(getMessageText(throwable))) -// .append("

\n"); -// appendStackTraceElement(details, true, true, writer); -// } -// -// private void appendStackTraceElement(final StackTraceElement stackTraceElement, -// final boolean isFirst, final boolean isLast, final Writer writer) throws IOException { -// if (isFirst) { -// writer.append("\n\n") -// .append("\n\n") -// .append("\n") -// .append("\n\n") -// .append("\n\n"); -// } -// writer.append("\n\n") -// .append("\n") -// .append("\n\n"); -// if (isLast) { -// writer.append("\n
ClassMethodLine number in class
").append(stackTraceElement.getClassName()).append("").append(stackTraceElement.getMethodName()).append("").append(Integer.toString(stackTraceElement.getLineNumber())) -// .append("
\n"); -// } -// } } diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugInfoRequest.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabRequest.java similarity index 82% rename from lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugInfoRequest.java rename to lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabRequest.java index e28bbb9a6..8eba537bc 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugInfoRequest.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabRequest.java @@ -20,7 +20,7 @@ package org.apache.olingo.server.core.debug; import java.io.IOException; import java.io.Writer; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -31,14 +31,14 @@ import com.fasterxml.jackson.core.JsonGenerator; /** * Request debug information. */ -public class DebugInfoRequest implements DebugInfo { +public class DebugTabRequest implements DebugTab { private final String method; private final String uri; private final String protocol; private final Map headers; - public DebugInfoRequest(ODataRequest request) { + public DebugTabRequest(ODataRequest request) { method = request.getMethod() == null ? "unkown" : request.getMethod().toString(); uri = request.getRawRequestUri() == null ? "unkown" : request.getRawRequestUri(); protocol = request.getProtocol() == null ? "unkown" : request.getProtocol(); @@ -47,7 +47,7 @@ public class DebugInfoRequest implements DebugInfo { } private Map wrapHeaders(Map> allHeaders) { - Map localHeaders = new HashMap(); + Map localHeaders = new LinkedHashMap(); for (Map.Entry> entry : allHeaders.entrySet()) { String value = null; if (entry.getValue() != null) { @@ -60,19 +60,22 @@ public class DebugInfoRequest implements DebugInfo { value = value + valuePart; } } + localHeaders.put(entry.getKey(), value); } return localHeaders; } @Override public void appendHtml(final Writer writer) throws IOException { -// writer.append("

Request Method

\n") -// .append("

").append(method).append("

\n") -// .append("

Request URI

\n") -// .append("

").append(DebugResponseHelperImpl.escapeHtml(uri.toString())).append("

\n") -// .append("

Request Protocol

\n") -// .append("

").append(protocol).append("

\n"); -// writer.append("

Request Headers

\n") + writer.append("

Request Method

\n") + .append("

").append(method).append("

\n") + .append("

Request URI

\n") + .append("

").append(DebugResponseHelperImpl.escapeHtml(uri.toString())).append("

\n") + .append("

Request Protocol

\n") + .append("

").append(protocol).append("

\n"); + writer.append("

Request Headers

\n"); + DebugResponseHelperImpl.appendHtmlTable(writer, headers); + // .append("\n\n") // .append("\n") // .append("\n\n"); diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugInfoResponse.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabResponse.java similarity index 75% rename from lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugInfoResponse.java rename to lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabResponse.java index 0781d5034..5cb153ca9 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugInfoResponse.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabResponse.java @@ -30,14 +30,14 @@ import com.fasterxml.jackson.core.JsonGenerator; /** * Response debug information. */ -public class DebugInfoResponse implements DebugInfo { +public class DebugTabResponse implements DebugTab { private final ODataResponse response; private final String serviceRoot; private final HttpStatusCode status; private final Map headers; - public DebugInfoResponse(final ODataResponse applicationResponse, final String serviceRoot) { + public DebugTabResponse(final ODataResponse applicationResponse, final String serviceRoot) { this.response = applicationResponse; this.serviceRoot = serviceRoot; status = HttpStatusCode.fromStatusCode(response.getStatusCode()); @@ -67,21 +67,23 @@ public class DebugInfoResponse implements DebugInfo { } gen.writeFieldName("body"); - new DebugInfoBody(response, serviceRoot).appendJson(gen); + new DebugTabBody(response, serviceRoot).appendJson(gen); gen.writeEndObject(); } @Override public void appendHtml(final Writer writer) throws IOException { -// writer.append("

Status Code

\n") -// .append("

").append(Integer.toString(status.getStatusCode())).append(' ') -// .append(status.getInfo()).append("

\n") -// .append("

Response Headers

\n"); -// ODataDebugResponseWrapper.appendHtmlTable(writer, headers); -// if (response.getContentHeader() != null && response.getEntity() != null) { -// writer.append("

Response Body

\n"); -// new DebugInfoBody(response, serviceRoot).appendHtml(writer); -// } + writer.append("

Status Code

\n") + .append("

").append(Integer.toString(status.getStatusCode())).append(' ') + .append(status.getInfo()).append("

\n") + .append("

Response Headers

\n"); + DebugResponseHelperImpl.appendHtmlTable(writer, headers); + writer.append("

Response Body

\n"); + if (response.getContent() != null) { + new DebugTabBody(response, serviceRoot).appendHtml(writer); + } else { + writer.append("

ODataLibrary: no response body

"); + } } } diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugInfoRuntime.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabRuntime.java similarity index 70% rename from lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugInfoRuntime.java rename to lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabRuntime.java index 2465ce49b..312b4cdfe 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugInfoRuntime.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabRuntime.java @@ -31,11 +31,11 @@ import com.fasterxml.jackson.core.JsonGenerator; /** * Runtime debug information. */ -public class DebugInfoRuntime implements DebugInfo { +public class DebugTabRuntime implements DebugTab { private final RuntimeNode rootNode; - public DebugInfoRuntime(List runtimeInformation) { + public DebugTabRuntime(List runtimeInformation) { rootNode = new RuntimeNode(); for (final RuntimeMeasurement runtimeMeasurement : runtimeInformation) { rootNode.add(runtimeMeasurement); @@ -82,44 +82,39 @@ public class DebugInfoRuntime implements DebugInfo { } @Override - public void appendHtml(Writer writer) throws IOException { - // TODO Auto-generated method stub - // -// @Override -// public void appendHtml(final Writer writer) throws IOException { -// appendRuntimeNode(rootNode, "", true, writer); -// } -// -// private void appendRuntimeNode(final RuntimeNode node, final String draw, final boolean isLast, final Writer writer) -// throws IOException { -// if (node.className != null) { -// writer.append("
  • ") -// .append("") -// .append("").append(draw) -// .append(isLast ? "└" : "├").append("─ ") -// .append("").append(node.className).append(".") -// .append("").append(node.methodName).append("(…)") -// .append(""); -// long time = node.timeStopped == 0 ? 0 : (node.timeStopped - node.timeStarted) / 1000; -// writer.append("").append(time == 0 ? "unfinished" : Long.toString(time) + " µs") -// .append("\n"); -// } -// if (!node.children.isEmpty()) { -// writer.append("
      \n"); -// for (final RuntimeNode childNode : node.children) { -// appendRuntimeNode(childNode, -// node.className == null ? draw : draw + (isLast ? " " : "│") + "  ", -// node.children.indexOf(childNode) == node.children.size() - 1, -// writer); -// } -// writer.append("
    \n"); -// } -// if (node.className != null) { -// writer.append("
  • \n"); -// } -// } + public void appendHtml(final Writer writer) throws IOException { + appendRuntimeNode(rootNode, "", true, writer); + } + + private void appendRuntimeNode(final RuntimeNode node, final String draw, final boolean isLast, final Writer writer) + throws IOException { + if (node.className != null) { + writer.append("
  • ") + .append("") + .append("").append(draw) + .append(isLast ? "└" : "├").append("─ ") + .append("").append(node.className).append(".") + .append("").append(node.methodName).append("(…)") + .append(""); + long time = node.timeStopped == 0 ? 0 : (node.timeStopped - node.timeStarted) / 1000; + writer.append("").append(time == 0 ? "unfinished" : Long.toString(time) + " µs") + .append("\n"); + } + if (!node.children.isEmpty()) { + writer.append("
      \n"); + for (final RuntimeNode childNode : node.children) { + appendRuntimeNode(childNode, + node.className == null ? draw : draw + (isLast ? " " : "│") + "  ", + node.children.indexOf(childNode) == node.children.size() - 1, + writer); + } + writer.append("
    \n"); + } + if (node.className != null) { + writer.append("
  • \n"); + } } private class RuntimeNode { diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabServer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabServer.java new file mode 100644 index 000000000..4eb95ba59 --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabServer.java @@ -0,0 +1,57 @@ +/* + * 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.olingo.server.core.debug; + +import java.io.IOException; +import java.io.Writer; +import java.util.Map; + +import com.fasterxml.jackson.core.JsonGenerator; + +/** + * Server debug information. + */ +public class DebugTabServer implements DebugTab { + + private final Map serverEnvironmentVaribles; + + public DebugTabServer(Map serverEnvironmentVaribles) { + this.serverEnvironmentVaribles = serverEnvironmentVaribles; + } + + @Override + public String getName() { + return "Environment"; + } + + @Override + public void appendJson(JsonGenerator gen) throws IOException { + DebugResponseHelperImpl.appendJsonTable(gen, serverEnvironmentVaribles); + } + + @Override + public void appendHtml(Writer writer) throws IOException { + final Package pack = DebugResponseHelperImpl.class.getPackage(); + writer.append("

    Library Version

    \n") + .append("

    ").append(pack.getImplementationTitle()) + .append(" Version ").append(pack.getImplementationVersion()).append("

    \n") + .append("

    Server Environment

    \n"); + DebugResponseHelperImpl.appendHtmlTable(writer, serverEnvironmentVaribles); + } +} diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugInfoUri.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabUri.java similarity index 97% rename from lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugInfoUri.java rename to lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabUri.java index 2ddeb0756..5c50c0ff6 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugInfoUri.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/DebugTabUri.java @@ -21,18 +21,25 @@ package org.apache.olingo.server.core.debug; import java.io.IOException; import java.io.Writer; +import org.apache.olingo.server.api.uri.UriInfo; + import com.fasterxml.jackson.core.JsonGenerator; /** * URI parser debug information. */ -public class DebugInfoUri implements DebugInfo { +public class DebugTabUri implements DebugTab { + + private UriInfo uriInfo; + + public DebugTabUri(UriInfo uriInfo) { + this.uriInfo = uriInfo; + } @Override public String getName() { - // TODO Auto-generated method stub - return null; + return "URI"; } @Override @@ -69,12 +76,6 @@ public class DebugInfoUri implements DebugInfo { // return null; // } // } -// -// @Override -// public String getName() { -// return "URI"; -// } -// // @Override // public void appendJson(final JsonStreamWriter jsonStreamWriter) throws IOException { // jsonStreamWriter.beginObject(); diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/ServerCoreDebugger.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/ServerCoreDebugger.java new file mode 100644 index 000000000..fcf611df6 --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/debug/ServerCoreDebugger.java @@ -0,0 +1,137 @@ +/* + * 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.olingo.server.core.debug; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.olingo.commons.api.format.ContentType; +import org.apache.olingo.commons.api.http.HttpHeader; +import org.apache.olingo.commons.api.http.HttpStatusCode; +import org.apache.olingo.server.api.OData; +import org.apache.olingo.server.api.ODataRequest; +import org.apache.olingo.server.api.ODataResponse; +import org.apache.olingo.server.api.debug.DebugInformation; +import org.apache.olingo.server.api.debug.DebugSupport; +import org.apache.olingo.server.api.debug.RuntimeMeasurement; +import org.apache.olingo.server.api.uri.UriInfo; + +public class ServerCoreDebugger { + + private final List runtimeInformation = new ArrayList(); + private final OData odata; + + private boolean isDebugMode = false; + private DebugSupport debugSupport; + private String debugFormat; + + public ServerCoreDebugger(OData odata) { + this.odata = odata; + } + + public void resolveDebugMode(HttpServletRequest request) { + if (debugSupport != null) { + // Should we read the parameter from the servlet here and ignore multiple parameters? + debugFormat = request.getParameter(DebugSupport.ODATA_DEBUG_QUERY_PARAMETER); + if (debugFormat != null) { + debugSupport.init(odata); + isDebugMode = debugSupport.isUserAuthorized(); + } + } + } + + public ODataResponse createDebugResponse(final HttpServletRequest request, final Exception exception, + final ODataRequest odRequest, final ODataResponse odResponse, UriInfo uriInfo, + Map serverEnvironmentVaribles) { + try { + DebugInformation debugInfo = + createDebugInformation(request, exception, odRequest, odResponse, uriInfo, serverEnvironmentVaribles); + + return debugSupport.createDebugResponse(debugFormat, debugInfo); + } catch (Exception e) { + return createFailResponse(); + } + } + + private ODataResponse createFailResponse() { + ODataResponse odResponse = new ODataResponse(); + odResponse.setStatusCode(HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode()); + odResponse.setHeader(HttpHeader.CONTENT_TYPE, ContentType.TEXT_PLAIN.toContentTypeString()); + InputStream content = new ByteArrayInputStream("ODataLibrary: Could not assemble debug response.".getBytes()); + odResponse.setContent(content); + return null; + } + + private DebugInformation createDebugInformation(final HttpServletRequest request, final Exception exception, + final ODataRequest odRequest, final ODataResponse odResponse, UriInfo uriInfo, + Map serverEnvironmentVaribles) { + DebugInformation debugInfo = new DebugInformation(); + debugInfo.setRequest(odRequest); + debugInfo.setApplicationResponse(odResponse); + + debugInfo.setException(exception); + + debugInfo.setServerEnvironmentVaribles(serverEnvironmentVaribles); + + debugInfo.setUriInfo(uriInfo); + + debugInfo.setRuntimeInformation(runtimeInformation); + return debugInfo; + } + + public int startRuntimeMeasurement(final String className, final String methodName) { + if (isDebugMode) { + int handleId = runtimeInformation.size(); + + final RuntimeMeasurement measurement = new RuntimeMeasurement(); + measurement.setTimeStarted(System.nanoTime()); + measurement.setClassName(className); + measurement.setMethodName(methodName); + + runtimeInformation.add(measurement); + + return handleId; + } else { + return 0; + } + } + + public void stopRuntimeMeasurement(final int handle) { + if (isDebugMode && handle < runtimeInformation.size()) { + long stopTime = System.nanoTime(); + RuntimeMeasurement runtimeMeasurement = runtimeInformation.get(handle); + if (runtimeMeasurement != null) { + runtimeMeasurement.setTimeStopped(stopTime); + } + } + } + + public void setDebugSupportProcessor(DebugSupport debugSupport) { + this.debugSupport = debugSupport; + } + + public boolean isDebugMode() { + return isDebugMode; + } +} diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/TechnicalServlet.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/TechnicalServlet.java index 0b5922c2d..8b74a88a3 100644 --- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/TechnicalServlet.java +++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/TechnicalServlet.java @@ -36,7 +36,6 @@ import org.apache.olingo.server.api.ServiceMetadata; import org.apache.olingo.server.api.debug.DefaultDebugSupport; import org.apache.olingo.server.api.edmx.EdmxReference; import org.apache.olingo.server.api.edmx.EdmxReferenceInclude; -import org.apache.olingo.server.tecsvc.async.TechnicalAsyncService; import org.apache.olingo.server.tecsvc.data.DataProvider; import org.apache.olingo.server.tecsvc.processor.TechnicalActionProcessor; import org.apache.olingo.server.tecsvc.processor.TechnicalBatchProcessor; diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerTest.java index 236c1b74e..5b347d15a 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerTest.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerTest.java @@ -6,9 +6,9 @@ * 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 @@ -77,6 +77,7 @@ import org.apache.olingo.server.api.processor.ReferenceCollectionProcessor; import org.apache.olingo.server.api.processor.ReferenceProcessor; import org.apache.olingo.server.api.processor.ServiceDocumentProcessor; import org.apache.olingo.server.api.uri.UriInfo; +import org.apache.olingo.server.core.debug.ServerCoreDebugger; import org.apache.olingo.server.tecsvc.provider.ContainerProvider; import org.apache.olingo.server.tecsvc.provider.EdmTechProvider; import org.junit.Test; @@ -233,7 +234,8 @@ public class ODataHandlerTest { request.setMethod(HttpMethod.GET); request.setRawODataPath("EdmException"); - final ODataResponse response = new ODataHandler(odata, serviceMetadata).process(request); + final ODataResponse response = + new ODataHandler(odata, serviceMetadata, new ServerCoreDebugger(odata)).process(request); assertNotNull(response); assertEquals(HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatusCode()); } @@ -720,7 +722,7 @@ public class ODataHandlerTest { final ServiceMetadata metadata = odata.createServiceMetadata( new EdmTechProvider(), Collections. emptyList()); - ODataHandler handler = new ODataHandler(odata, metadata); + ODataHandler handler = new ODataHandler(odata, metadata, new ServerCoreDebugger(odata)); if (processor != null) { handler.register(processor);
    NameValue