diff --git a/pom.xml b/pom.xml index 4ef1622c..e7abc804 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.orderofthebee.support-tools support-tools-parent - 1.2.1.0 + 1.2.2.0-SNAPSHOT pom OOTBee Support Tools - Parent @@ -702,5 +702,28 @@ + + acs-docker-23.1 + + + 23.1.0 + 23.1.0 + DockerfileWithBuildUser + -Dencryption.keystore.type=JCEKS -Dencryption.cipherAlgorithm=DESede/CBC/PKCS5Padding -Dencryption.keyAlgorithm=DESede -Dencryption.keystore.location=/usr/local/tomcat/shared/classes/alfresco/extension/keystore/keystore -Dmetadata-keystore.password=mp6yc0UD9e -Dmetadata-keystore.aliases=metadata -Dmetadata-keystore.metadata.password=oKIWzVdEdA -Dmetadata-keystore.metadata.algorithm=DESede -Dsolr.secureComms=secret -Dsolr.sharedSecret=secret -Dalfresco.restApi.basicAuthScheme=true + 2.0.8.2 + -Dalfresco.secureComms=secret -Dalfresco.secureComms.secret=secret + + + + + + org.apache.activemq + activemq-broker + 5.18.3 + runtime + + + + \ No newline at end of file diff --git a/repository/pom.xml b/repository/pom.xml index 7b037a9b..01e8aefb 100644 --- a/repository/pom.xml +++ b/repository/pom.xml @@ -5,7 +5,7 @@ org.orderofthebee.support-tools support-tools-parent - 1.2.1.0 + 1.2.2.0-SNAPSHOT support-tools-repo diff --git a/share/pom.xml b/share/pom.xml index f7d7bf17..4e0515f5 100644 --- a/share/pom.xml +++ b/share/pom.xml @@ -5,7 +5,7 @@ org.orderofthebee.support-tools support-tools-parent - 1.2.1.0 + 1.2.2.0-SNAPSHOT support-tools-share @@ -42,6 +42,14 @@ 3.0.1 provided + + + + jakarta.servlet + jakarta.servlet-api + 5.0.0 + provided + @@ -79,6 +87,11 @@ javax.servlet-api + + jakarta.servlet + jakarta.servlet-api + + org.apache.commons commons-compress diff --git a/share/src/main/java/org/orderofthebee/addons/support/tools/share/ContentStreamer.java b/share/src/main/java/org/orderofthebee/addons/support/tools/share/ContentStreamer.java index 85ecefe3..5e895326 100644 --- a/share/src/main/java/org/orderofthebee/addons/support/tools/share/ContentStreamer.java +++ b/share/src/main/java/org/orderofthebee/addons/support/tools/share/ContentStreamer.java @@ -32,18 +32,14 @@ import java.util.Date; import java.util.Map; import java.util.TimeZone; - -import javax.servlet.ServletOutputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import java.util.function.Supplier; import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.extensions.surf.util.URLEncoder; import org.springframework.extensions.webscripts.Cache; -import org.springframework.extensions.webscripts.WebScriptRequest; -import org.springframework.extensions.webscripts.WebScriptResponse; +import org.springframework.extensions.webscripts.Status; /** * @author Axel Faust @@ -78,391 +74,11 @@ public class ContentStreamer protected static ThreadLocal s_dateFormat = new ThreadLocal<>(); - // Request/Response abstractions to simplify our code - protected interface Request - { - - /** - * Returns the value of the specified request header as a String. If the request did not include a header of the - * specified name, this method returns null. If there are multiple headers with the same name, this method returns the - * first head in the request. The header name is case insensitive. You can use this method with any request header. - * - * @param name - * a String specifying the - * header name - * - * @return a String containing the - * value of the requested - * header, or null - * if the request does not - * have a header of that name - * - * @see HttpServletRequest#getHeader(String) - * @see WebScriptRequest#getHeader(String) - * - */ - String getHeader(String name); - } - - protected interface Response - { - - /** - *

- * Sets the status code for this response. This method is used to set the return status code when there is no error (for example, - * for the status codes SC_OK or SC_MOVED_TEMPORARILY). If there is an error, and the caller wishes to invoke an error-page defined - * in the web application, the sendError method should be used instead. - *

- * - *

- * The container clears the buffer and sets the Location header, preserving cookies and other headers. - *

- * - * @param status - * the status code - * - * @see HttpServletResponse#setStatus(int) - * @see WebScriptResponse#setStatus(int) - */ - void setStatus(int status); - - /** - * Sets a response header with the given name and value.If the header had already been set, the new value overwrites the previous - * one. The containsHeader method can be used to test for the presence of a header before setting its value. - * - * @param name - * the name of the header - * @param value - * the header value If it contains octet string, - * it should be encoded according to RFC 2047 - * (http://www.ietf.org/rfc/rfc2047.txt) - * - * @see HttpServletResponse#setHeader(String, String) - * @see WebScriptResponse#setHeader(String, String) - * - */ - void setHeader(String name, String value); - - /** - * Sets the Content Type - * - * @param contentType - * String - * - * @see HttpServletResponse#setContentType(String) - * @see WebScriptResponse#setContentType(String) - */ - void setContentType(String contentType); - - /** - * Sets the Content Encoding - * - * @param contentEncoding - * String - * - * @see WebScriptResponse#setContentEncoding(String) - */ - void setContentEncoding(String contentEncoding); - - /** - * Sets the Cache control - * - * @param cache - * cache control - * - * @see WebScriptResponse#setCache(Cache) - */ - void setCache(Cache cache); - - /** - * Returns a {@link OutputStream} suitable for writing binary data in the response. The servlet container does not encode the binary - * data. - * - * @return a {@link OutputStream} for writing binary data - * - * @exception IOException - * if an input or output exception occurred - * - * @see HttpServletResponse#getOutputStream() - * @see WebScriptResponse#getOutputStream() - */ - OutputStream getOutputStream() throws IOException; - } - - protected static class WebScriptRequestWrapper implements Request - { - - protected final WebScriptRequest req; - - protected WebScriptRequestWrapper(final WebScriptRequest req) - { - this.req = req; - } - - /** - * - * {@inheritDoc} - */ - @Override - public String getHeader(final String name) - { - return this.req.getHeader(name); - } - } - - protected static class HttpServletRequestWrapper implements Request - { - - protected final HttpServletRequest req; - - protected HttpServletRequestWrapper(final HttpServletRequest req) - { - this.req = req; - } - - /** - * - * {@inheritDoc} - */ - @Override - public String getHeader(final String name) - { - return this.req.getHeader(name); - } - } - - protected static class WebScriptResponseWrapper implements Response - { - - protected final WebScriptResponse res; - - protected WebScriptResponseWrapper(final WebScriptResponse res) - { - this.res = res; - } - - /** - * - * {@inheritDoc} - */ - @Override - public void setStatus(final int status) - { - this.res.setStatus(status); - } - - /** - * - * {@inheritDoc} - */ - @Override - public void setHeader(final String name, final String value) - { - this.res.setHeader(name, value); - } - - /** - * - * {@inheritDoc} - */ - @Override - public void setContentType(final String contentType) - { - this.res.setContentType(contentType); - } - - /** - * - * {@inheritDoc} - */ - @Override - public void setContentEncoding(final String contentEncoding) - { - this.res.setContentEncoding(contentEncoding); - } - - /** - * - * {@inheritDoc} - */ - @Override - public void setCache(final Cache cache) - { - this.res.setCache(cache); - } - - /** - * - * {@inheritDoc} - */ - @Override - public OutputStream getOutputStream() throws IOException - { - return this.res.getOutputStream(); - } - - } - - protected static class HttpServletResponseWrapper implements Response - { - - protected final HttpServletResponse res; - - protected HttpServletResponseWrapper(final HttpServletResponse res) - { - this.res = res; - } - - /** - * - * {@inheritDoc} - */ - @Override - public void setStatus(final int status) - { - this.res.setStatus(status); - } - - /** - * - * {@inheritDoc} - */ - @Override - public void setHeader(final String name, final String value) - { - this.res.setHeader(name, value); - } - - /** - * - * {@inheritDoc} - */ - @Override - public void setContentType(final String contentType) - { - this.res.setContentType(contentType); - } - - /** - * - * {@inheritDoc} - */ - @Override - public void setContentEncoding(final String contentEncoding) - { - this.res.setCharacterEncoding(contentEncoding); - } - - @Override - public void setCache(final Cache cache) - { - // copied from WebScriptServletResponseImpl and adapted to avoid +-concatenation - // set Cache-Control - final StringBuilder cacheControl = new StringBuilder(64); - String pragma = ""; - if (cache.getIsPublic()) - { - cacheControl.append("public"); - } - if (cache.getNeverCache()) - { - if (cacheControl.length() > 0) - { - cacheControl.append(", "); - } - cacheControl.append(NO_CACHE); - pragma = NO_CACHE; - } - if (cache.getMaxAge() != null && cache.getNeverCache() == false) - { - if (cacheControl.length() > 0) - { - cacheControl.append(", "); - } - cacheControl.append("max-age=").append(cache.getMaxAge()); - } - if (cache.getMustRevalidate() && cache.getNeverCache() == false) - { - if (cacheControl.length() > 0) - { - cacheControl.append(", "); - } - cacheControl.append("must-revalidate"); - } - if (cacheControl.length() > 0) - { - final String cacheControlValue = cacheControl.toString(); - this.res.setHeader("Cache-Control", cacheControlValue); - if (LOGGER.isDebugEnabled()) - { - LOGGER.debug("Cache - set response header Cache-Control: " + cacheControl); - } - // special case for IE Ajax request handling - if (NO_CACHE.equals(cacheControlValue)) - { - this.res.setHeader("Expires", "Thu, 01 Jan 1970 00:00:00 GMT"); - } - } - if (pragma.length() > 0) - { - this.res.setHeader("Pragma", pragma); - if (LOGGER.isDebugEnabled()) - { - LOGGER.debug("Cache - set response header Pragma: " + pragma); - } - } - - // set ETag - if (cache.getETag() != null) - { - final String eTag = "\"" + cache.getETag() + "\""; - this.res.setHeader("ETag", eTag); - if (LOGGER.isDebugEnabled()) - { - LOGGER.debug("Cache - set response header ETag: " + eTag); - } - } - - // set Last Modified - if (cache.getLastModified() != null) - { - this.res.setDateHeader("Last-Modified", cache.getLastModified().getTime()); - if (LOGGER.isDebugEnabled()) - { - final SimpleDateFormat formatter = getHTTPDateFormat(); - final String lastModified = formatter.format(cache.getLastModified()); - LOGGER.debug("Cache - set response header Last-Modified: " + lastModified); - } - } - } - - /** - * - * {@inheritDoc} - */ - @Override - public OutputStream getOutputStream() throws IOException - { - return this.res.getOutputStream(); - } - - } - // provide method variants for specific request / response types - protected void streamContent(final WebScriptRequest req, final WebScriptResponse res, final File file, final Long modifiedTime, + protected void streamContent(final Supplier req, final Supplier res, final File file, final Long modifiedTime, final boolean attach, final String attachFileName, final Map model, final String mimetype) throws IOException { - final WebScriptRequestWrapper reqWrapper = new WebScriptRequestWrapper(req); - final WebScriptResponseWrapper resWrapper = new WebScriptResponseWrapper(res); - this.streamContent(reqWrapper, resWrapper, file, modifiedTime, attach, attachFileName, model, mimetype); - } - - protected void streamContent(final HttpServletRequest req, final HttpServletResponse res, final File file, final Long modifiedTime, - final boolean attach, final String attachFileName, final Map model, final String mimetype) throws IOException - { - final HttpServletRequestWrapper reqWrapper = new HttpServletRequestWrapper(req); - final HttpServletResponseWrapper resWrapper = new HttpServletResponseWrapper(res); - this.streamContent(reqWrapper, resWrapper, file, modifiedTime, attach, attachFileName, model, mimetype); + this.streamContent(req.get(), res.get(), file, modifiedTime, attach, attachFileName, model, mimetype); } // copied from Repo-tier ContentStreamer and base classes since they aren't available in Share @@ -650,7 +266,7 @@ protected boolean processSingleRange(final Response res, final File file, final LOGGER.debug("Failed to parse range header - returning 416 status code: " + err.getMessage()); } - res.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); + res.setStatus(Status.STATUS_REQUESTED_RANGE_NOT_SATISFIABLE); res.setHeader(HEADER_CONTENT_RANGE, "\"*\""); res.getOutputStream().close(); return true; @@ -659,7 +275,7 @@ protected boolean processSingleRange(final Response res, final File file, final // set Partial Content status and range headers final String contentRange = "bytes " + Long.toString(r.start) + "-" + Long.toString(r.end) + "/" + Long.toString(file.length()); - res.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); + res.setStatus(Status.STATUS_PARTIAL_CONTENT); res.setContentType(mimetype); res.setHeader(HEADER_CONTENT_RANGE, contentRange); res.setHeader(HEADER_CONTENT_LENGTH, Long.toString((r.end - r.start) + 1L)); @@ -777,13 +393,13 @@ protected static class Range implements Comparable * Constructor * * @param contentType - * Mimetype of the range content + * Mimetype of the range content * @param start - * Start position in the parent entity + * Start position in the parent entity * @param end - * End position in the parent entity + * End position in the parent entity * @param entityLength - * Length of the parent entity + * Length of the parent entity */ Range(final String contentType, final long start, final long end, final long entityLength) { @@ -797,16 +413,16 @@ protected static class Range implements Comparable * Factory method to construct a byte range from a range header value. * * @param range - * Range header value + * Range header value * @param contentType - * Mimetype of the range + * Mimetype of the range * @param entityLength - * Length of the parent entity + * Length of the parent entity * * @return Range * * @throws IllegalArgumentException - * for an invalid range + * for an invalid range */ static Range constructRange(String range, final String contentType, final long entityLength) { @@ -855,19 +471,6 @@ static Range constructRange(String range, final String contentType, final long e } } - /** - * Output the header bytes for a multi-part byte range header - */ - void outputHeader(final ServletOutputStream os) throws IOException - { - // output multi-part boundry separator - os.println(MULTIPART_BYTERANGES_BOUNDRY_SEP); - // output content type and range size sub-header for this part - os.println(this.contentType); - os.println(this.getContentRange()); - os.println(); - } - /** * @return the length in bytes of the byte range content including the header bytes */ @@ -908,12 +511,12 @@ public int compareTo(final Range o) } // copied from Repo-tier WebDAVHelper - protected final static String encodeURL(final String s) + protected static final String encodeURL(final String s) { return encodeURL(s, null); } - protected final static String encodeURL(final String s, final String userAgent) + protected static final String encodeURL(final String s, final String userAgent) { return URLEncoder.encode(s); } diff --git a/share/src/main/java/org/orderofthebee/addons/support/tools/share/JakartaHttpServletRequestWrapper.java b/share/src/main/java/org/orderofthebee/addons/support/tools/share/JakartaHttpServletRequestWrapper.java new file mode 100644 index 00000000..5cdd72bd --- /dev/null +++ b/share/src/main/java/org/orderofthebee/addons/support/tools/share/JakartaHttpServletRequestWrapper.java @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2016 - 2023 Order of the Bee + * + * This file is part of OOTBee Support Tools + * + * OOTBee Support Tools is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * OOTBee Support Tools is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OOTBee Support Tools. If not, see + * . + * + * Linked to Alfresco + * Copyright (C) 2005 - 2023 Alfresco Software Limited. + */ +package org.orderofthebee.addons.support.tools.share; + +import jakarta.servlet.http.HttpServletRequest; + +/** + * This class is used to wrap a (Java EE) {@link HttpServletRequest servlet request} for use in the {@link ContentStreamer} and + * {@link LogFileHandler}. + * + * @author Axel Faust + */ +public class JakartaHttpServletRequestWrapper implements Request +{ + + protected final HttpServletRequest req; + + protected JakartaHttpServletRequestWrapper(final HttpServletRequest req) + { + this.req = req; + } + + /** + * + * {@inheritDoc} + */ + @Override + public String getHeader(final String name) + { + return this.req.getHeader(name); + } +} \ No newline at end of file diff --git a/share/src/main/java/org/orderofthebee/addons/support/tools/share/JakartaHttpServletResponseWrapper.java b/share/src/main/java/org/orderofthebee/addons/support/tools/share/JakartaHttpServletResponseWrapper.java new file mode 100644 index 00000000..d3aca011 --- /dev/null +++ b/share/src/main/java/org/orderofthebee/addons/support/tools/share/JakartaHttpServletResponseWrapper.java @@ -0,0 +1,208 @@ +/** + * Copyright (C) 2016 - 2023 Order of the Bee + * + * This file is part of OOTBee Support Tools + * + * OOTBee Support Tools is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * OOTBee Support Tools is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OOTBee Support Tools. If not, see + * . + * + * Linked to Alfresco + * Copyright (C) 2005 - 2023 Alfresco Software Limited. + */ +package org.orderofthebee.addons.support.tools.share; + +import java.io.IOException; +import java.io.OutputStream; +import java.text.SimpleDateFormat; + +import jakarta.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.extensions.webscripts.Cache; + +/** + * This class is used to wrap a (Java EE) {@link HttpServletResponse servlet response} for use in the {@link ContentStreamer} and + * {@link LogFileHandler}. + * + * @author Axel Faust + */ +public class JakartaHttpServletResponseWrapper implements Response +{ + + private static final Logger LOGGER = LoggerFactory.getLogger(JakartaHttpServletResponseWrapper.class); + + private final HttpServletResponse res; + + public JakartaHttpServletResponseWrapper(final HttpServletResponse res) + { + this.res = res; + } + + /** + * + * {@inheritDoc} + */ + @Override + public void setStatus(final int status) + { + this.res.setStatus(status); + } + + /** + * + * {@inheritDoc} + */ + @Override + public void setHeader(final String name, final String value) + { + this.res.setHeader(name, value); + } + + /** + * + * {@inheritDoc} + */ + @Override + public void setContentType(final String contentType) + { + this.res.setContentType(contentType); + } + + /** + * + * {@inheritDoc} + */ + @Override + public void setContentEncoding(final String contentEncoding) + { + this.res.setCharacterEncoding(contentEncoding); + } + + /** + * + * {@inheritDoc} + */ + @Override + public void setCache(final Cache cache) + { + // copied from WebScriptServletResponseImpl and adapted to avoid +-concatenation + // set Cache-Control + final StringBuilder cacheControl = new StringBuilder(64); + String pragma = ""; + if (cache.getIsPublic()) + { + cacheControl.append("public"); + } + if (cache.getNeverCache()) + { + if (cacheControl.length() > 0) + { + cacheControl.append(", "); + } + cacheControl.append(ContentStreamer.NO_CACHE); + pragma = ContentStreamer.NO_CACHE; + } + if (cache.getMaxAge() != null && cache.getNeverCache() == false) + { + if (cacheControl.length() > 0) + { + cacheControl.append(", "); + } + cacheControl.append("max-age=").append(cache.getMaxAge()); + } + if (cache.getMustRevalidate() && cache.getNeverCache() == false) + { + if (cacheControl.length() > 0) + { + cacheControl.append(", "); + } + cacheControl.append("must-revalidate"); + } + if (cacheControl.length() > 0) + { + final String cacheControlValue = cacheControl.toString(); + this.res.setHeader("Cache-Control", cacheControlValue); + LOGGER.debug("Cache - set response header Cache-Control: {}", cacheControl); + // special case for IE Ajax request handling + if (ContentStreamer.NO_CACHE.equals(cacheControlValue)) + { + this.res.setHeader("Expires", "Thu, 01 Jan 1970 00:00:00 GMT"); + } + } + if (pragma.length() > 0) + { + this.res.setHeader("Pragma", pragma); + LOGGER.debug("Cache - set response header Pragma: {}", pragma); + } + + // set ETag + if (cache.getETag() != null) + { + final String eTag = "\"" + cache.getETag() + "\""; + this.res.setHeader("ETag", eTag); + LOGGER.debug("Cache - set response header ETag: {}", eTag); + } + + // set Last Modified + if (cache.getLastModified() != null) + { + this.res.setDateHeader("Last-Modified", cache.getLastModified().getTime()); + if (LOGGER.isDebugEnabled()) + { + final SimpleDateFormat formatter = ContentStreamer.getHTTPDateFormat(); + final String lastModified = formatter.format(cache.getLastModified()); + LOGGER.debug("Cache - set response header Last-Modified: {}", lastModified); + } + } + } + + /** + * + * {@inheritDoc} + */ + @Override + public OutputStream getOutputStream() throws IOException + { + return this.res.getOutputStream(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isCommitted() + { + return res.isCommitted(); + } + + /** + * {@inheritDoc} + */ + @Override + public void reset() + { + res.reset(); + } + + /** + * {@inheritDoc} + */ + @Override + public void sendError(int status, String message) throws IOException + { + res.sendError(status, message); + } + +} \ No newline at end of file diff --git a/share/src/main/java/org/orderofthebee/addons/support/tools/share/JakartaLogFileGetServlet.java b/share/src/main/java/org/orderofthebee/addons/support/tools/share/JakartaLogFileGetServlet.java new file mode 100644 index 00000000..60f5d9d8 --- /dev/null +++ b/share/src/main/java/org/orderofthebee/addons/support/tools/share/JakartaLogFileGetServlet.java @@ -0,0 +1,158 @@ +/** + * Copyright (C) 2016 - 2023 Order of the Bee + * + * This file is part of OOTBee Support Tools + * + * OOTBee Support Tools is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * OOTBee Support Tools is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OOTBee Support Tools. If not, see + * . + * + * Linked to Alfresco + * Copyright (C) 2005 - 2023 Alfresco Software Limited. + */ +package org.orderofthebee.addons.support.tools.share; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.context.ApplicationContext; +import org.springframework.extensions.surf.FrameworkBean; +import org.springframework.extensions.surf.LinkBuilder; +import org.springframework.extensions.surf.LinkBuilderFactory; +import org.springframework.extensions.surf.RequestContext; +import org.springframework.extensions.surf.UserFactory; +import org.springframework.extensions.surf.WebFrameworkServiceRegistry; +import org.springframework.extensions.surf.support.ServletRequestContext; +import org.springframework.extensions.surf.util.URLDecoder; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.connector.User; +import org.springframework.web.context.support.WebApplicationContextUtils; + +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +/** + * @author Axel Faust + */ +@WebServlet(name = "OOTBee Support Tools - Log File Download", urlPatterns = { "/ootbee-support-tools/log4j-log-file/*" }) +public class JakartaLogFileGetServlet extends HttpServlet +{ + + private static final long serialVersionUID = 5162376729083905078L; + + protected LogFileHandler logFileHandler; + + protected WebFrameworkServiceRegistry serviceRegistry; + + protected FrameworkBean frameworkBean; + + protected LinkBuilder linkBuilder; + + protected UserFactory userFactory; + + protected Method userFactoryInitialiseUserMethod; + + /** + * + * {@inheritDoc} + */ + @Override + public void init() throws ServletException + { + super.init(); + try + { + final Method getWebApplicationContextMethod = WebApplicationContextUtils.class.getMethod("getRequiredWebApplicationContext", + ServletContext.class); + final ApplicationContext context = (ApplicationContext) getWebApplicationContextMethod.invoke(null, this.getServletContext()); + this.logFileHandler = context.getBean(LogFileHandler.class); + this.serviceRegistry = context.getBean("webframework.service.registry", WebFrameworkServiceRegistry.class); + this.frameworkBean = context.getBean("framework.utils", FrameworkBean.class); + this.linkBuilder = context.getBean("webframework.factory.linkbuilder.servlet", LinkBuilderFactory.class).newInstance(); + this.userFactory = context.getBean("user.factory", UserFactory.class); + + this.userFactoryInitialiseUserMethod = UserFactory.class.getMethod("initialiseUser", RequestContext.class, + HttpServletRequest.class, String.class); + } + catch (NoSuchMethodException | SecurityException | InvocationTargetException | IllegalAccessException e) + { + throw new ServletException("Unable to init servlet", e); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void doGet(final HttpServletRequest req, final HttpServletResponse res) throws ServletException, IOException + { + if (req.getCharacterEncoding() == null) + { + req.setCharacterEncoding("UTF-8"); + } + + User user = null; + + final ServletRequestContext context = new ServletRequestContext(this.serviceRegistry, this.frameworkBean, this.linkBuilder); + final String userEndpointId = (String) context.getAttribute(RequestContext.USER_ENDPOINT); + try + { + user = (User) this.userFactoryInitialiseUserMethod.invoke(this.userFactory, context, req, userEndpointId); + } + catch (InvocationTargetException e) + { + res.sendError(Status.STATUS_INTERNAL_SERVER_ERROR, e.getCause().getMessage()); + } + catch (final IllegalAccessException e) + { + res.sendError(Status.STATUS_INTERNAL_SERVER_ERROR, e.getMessage()); + } + + if (user != null && user.isAdmin()) + { + final String requestURI = req.getRequestURI(); + String filePath = URLDecoder.decode(requestURI.substring(req.getContextPath().length() + req.getServletPath().length() + 1)); + if (!filePath.startsWith("/")) + { + filePath = "/" + filePath; + } + + final Map model = new HashMap<>(); + final Status status = new Status(); + final Cache cache = new Cache(); + cache.setNeverCache(true); + model.put("status", status); + model.put("cache", cache); + + final String attachParam = req.getParameter("a"); + final boolean attach = attachParam != null && Boolean.parseBoolean(attachParam); + + final JakartaHttpServletRequestWrapper reqW = new JakartaHttpServletRequestWrapper(req); + final JakartaHttpServletResponseWrapper resW = new JakartaHttpServletResponseWrapper(res); + this.logFileHandler.handleLogFileRequest(filePath, attach, () -> reqW, () -> resW, model); + } + else + { + res.sendError(Status.STATUS_FORBIDDEN); + } + } + +} diff --git a/share/src/main/java/org/orderofthebee/addons/support/tools/share/JakartaLogFilesZIPPostServlet.java b/share/src/main/java/org/orderofthebee/addons/support/tools/share/JakartaLogFilesZIPPostServlet.java new file mode 100644 index 00000000..9dd3c8e4 --- /dev/null +++ b/share/src/main/java/org/orderofthebee/addons/support/tools/share/JakartaLogFilesZIPPostServlet.java @@ -0,0 +1,170 @@ +/** + * Copyright (C) 2016 - 2023 Order of the Bee + * + * This file is part of OOTBee Support Tools + * + * OOTBee Support Tools is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * OOTBee Support Tools is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OOTBee Support Tools. If not, see + * . + * + * Linked to Alfresco + * Copyright (C) 2005 - 2023 Alfresco Software Limited. + */ +package org.orderofthebee.addons.support.tools.share; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.context.ApplicationContext; +import org.springframework.extensions.surf.FrameworkBean; +import org.springframework.extensions.surf.LinkBuilder; +import org.springframework.extensions.surf.LinkBuilderFactory; +import org.springframework.extensions.surf.RequestContext; +import org.springframework.extensions.surf.UserFactory; +import org.springframework.extensions.surf.WebFrameworkServiceRegistry; +import org.springframework.extensions.surf.support.ServletRequestContext; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.connector.User; +import org.springframework.extensions.webscripts.servlet.FormData; +import org.springframework.web.context.support.WebApplicationContextUtils; + +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +/** + * @author Axel Faust + */ +@WebServlet(name = "OOTBee Support Tools - Log ZIP File Download", urlPatterns = { "/ootbee-support-tools/log4j-log-files.zip" }) +public class JakartaLogFilesZIPPostServlet extends HttpServlet +{ + + private static final long serialVersionUID = 6594146886154738251L; + + protected LogFileHandler logFileHandler; + + protected WebFrameworkServiceRegistry serviceRegistry; + + protected FrameworkBean frameworkBean; + + protected LinkBuilder linkBuilder; + + protected UserFactory userFactory; + + protected Method userFactoryInitialiseUserMethod; + + protected Constructor formDataCtor; + + /** + * + * {@inheritDoc} + */ + @Override + public void init() throws ServletException + { + super.init(); + try + { + final Method getWebApplicationContextMethod = WebApplicationContextUtils.class.getMethod("getRequiredWebApplicationContext", + ServletContext.class); + final ApplicationContext context = (ApplicationContext) getWebApplicationContextMethod.invoke(null, this.getServletContext()); + this.logFileHandler = context.getBean(LogFileHandler.class); + this.serviceRegistry = context.getBean("webframework.service.registry", WebFrameworkServiceRegistry.class); + this.frameworkBean = context.getBean("framework.utils", FrameworkBean.class); + this.linkBuilder = context.getBean("webframework.factory.linkbuilder.servlet", LinkBuilderFactory.class).newInstance(); + this.userFactory = context.getBean("user.factory", UserFactory.class); + + this.userFactoryInitialiseUserMethod = UserFactory.class.getMethod("initialiseUser", RequestContext.class, + HttpServletRequest.class, String.class); + this.formDataCtor = FormData.class.getConstructor(HttpServletRequest.class); + } + catch (NoSuchMethodException | SecurityException | InvocationTargetException | IllegalAccessException e) + { + throw new ServletException("Unable to init servlet", e); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void doPost(final HttpServletRequest req, final HttpServletResponse res) throws ServletException, IOException + { + if (req.getCharacterEncoding() == null) + { + req.setCharacterEncoding("UTF-8"); + } + + User user = null; + + final ServletRequestContext context = new ServletRequestContext(this.serviceRegistry, this.frameworkBean, this.linkBuilder); + final String userEndpointId = (String) context.getAttribute(RequestContext.USER_ENDPOINT); + try + { + user = (User) this.userFactoryInitialiseUserMethod.invoke(this.userFactory, context, req, userEndpointId); + } + catch (InvocationTargetException e) + { + res.sendError(Status.STATUS_INTERNAL_SERVER_ERROR, e.getCause().getMessage()); + } + catch (final IllegalAccessException e) + { + res.sendError(Status.STATUS_INTERNAL_SERVER_ERROR, e.getMessage()); + } + + if (user != null && user.isAdmin()) + { + final Map model = new HashMap<>(); + final Status status = new Status(); + final Cache cache = new Cache(); + cache.setNeverCache(true); + model.put("status", status); + model.put("cache", cache); + + final FormData rqData; + try + { + rqData = this.formDataCtor.newInstance(req); + } + catch (InvocationTargetException | InstantiationException | IllegalAccessException e) + { + res.sendError(Status.STATUS_INTERNAL_SERVER_ERROR, e.getMessage()); + return; + } + + final List filePaths = new ArrayList<>(); + final String[] paths = rqData.getParameters().get("paths"); + filePaths.addAll(Arrays.asList(paths)); + + final JakartaHttpServletRequestWrapper reqW = new JakartaHttpServletRequestWrapper(req); + final JakartaHttpServletResponseWrapper resW = new JakartaHttpServletResponseWrapper(res); + this.logFileHandler.handleLogZipRequest(filePaths, () -> reqW, () -> resW, model); + } + else + { + res.sendError(Status.STATUS_FORBIDDEN); + } + } + +} diff --git a/share/src/main/java/org/orderofthebee/addons/support/tools/share/LegacyHttpServletRequestWrapper.java b/share/src/main/java/org/orderofthebee/addons/support/tools/share/LegacyHttpServletRequestWrapper.java new file mode 100644 index 00000000..101c5ae9 --- /dev/null +++ b/share/src/main/java/org/orderofthebee/addons/support/tools/share/LegacyHttpServletRequestWrapper.java @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2016 - 2023 Order of the Bee + * + * This file is part of OOTBee Support Tools + * + * OOTBee Support Tools is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * OOTBee Support Tools is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OOTBee Support Tools. If not, see + * . + * + * Linked to Alfresco + * Copyright (C) 2005 - 2023 Alfresco Software Limited. + */ +package org.orderofthebee.addons.support.tools.share; + +import javax.servlet.http.HttpServletRequest; + +/** + * This class is used to wrap a (Java EE) {@link HttpServletRequest servlet request} for use in the {@link ContentStreamer} and + * {@link LogFileHandler}. + * + * @author Axel Faust + */ +public class LegacyHttpServletRequestWrapper implements Request +{ + + protected final HttpServletRequest req; + + protected LegacyHttpServletRequestWrapper(final HttpServletRequest req) + { + this.req = req; + } + + /** + * + * {@inheritDoc} + */ + @Override + public String getHeader(final String name) + { + return this.req.getHeader(name); + } +} \ No newline at end of file diff --git a/share/src/main/java/org/orderofthebee/addons/support/tools/share/LegacyHttpServletResponseWrapper.java b/share/src/main/java/org/orderofthebee/addons/support/tools/share/LegacyHttpServletResponseWrapper.java new file mode 100644 index 00000000..bed8df53 --- /dev/null +++ b/share/src/main/java/org/orderofthebee/addons/support/tools/share/LegacyHttpServletResponseWrapper.java @@ -0,0 +1,208 @@ +/** + * Copyright (C) 2016 - 2023 Order of the Bee + * + * This file is part of OOTBee Support Tools + * + * OOTBee Support Tools is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * OOTBee Support Tools is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OOTBee Support Tools. If not, see + * . + * + * Linked to Alfresco + * Copyright (C) 2005 - 2023 Alfresco Software Limited. + */ +package org.orderofthebee.addons.support.tools.share; + +import java.io.IOException; +import java.io.OutputStream; +import java.text.SimpleDateFormat; + +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.extensions.webscripts.Cache; + +/** + * This class is used to wrap a (Java EE) {@link HttpServletResponse servlet response} for use in the {@link ContentStreamer} and + * {@link LogFileHandler}. + * + * @author Axel Faust + */ +public class LegacyHttpServletResponseWrapper implements Response +{ + + private static final Logger LOGGER = LoggerFactory.getLogger(LegacyHttpServletResponseWrapper.class); + + private final HttpServletResponse res; + + public LegacyHttpServletResponseWrapper(final HttpServletResponse res) + { + this.res = res; + } + + /** + * + * {@inheritDoc} + */ + @Override + public void setStatus(final int status) + { + this.res.setStatus(status); + } + + /** + * + * {@inheritDoc} + */ + @Override + public void setHeader(final String name, final String value) + { + this.res.setHeader(name, value); + } + + /** + * + * {@inheritDoc} + */ + @Override + public void setContentType(final String contentType) + { + this.res.setContentType(contentType); + } + + /** + * + * {@inheritDoc} + */ + @Override + public void setContentEncoding(final String contentEncoding) + { + this.res.setCharacterEncoding(contentEncoding); + } + + /** + * + * {@inheritDoc} + */ + @Override + public void setCache(final Cache cache) + { + // copied from WebScriptServletResponseImpl and adapted to avoid +-concatenation + // set Cache-Control + final StringBuilder cacheControl = new StringBuilder(64); + String pragma = ""; + if (cache.getIsPublic()) + { + cacheControl.append("public"); + } + if (cache.getNeverCache()) + { + if (cacheControl.length() > 0) + { + cacheControl.append(", "); + } + cacheControl.append(ContentStreamer.NO_CACHE); + pragma = ContentStreamer.NO_CACHE; + } + if (cache.getMaxAge() != null && cache.getNeverCache() == false) + { + if (cacheControl.length() > 0) + { + cacheControl.append(", "); + } + cacheControl.append("max-age=").append(cache.getMaxAge()); + } + if (cache.getMustRevalidate() && cache.getNeverCache() == false) + { + if (cacheControl.length() > 0) + { + cacheControl.append(", "); + } + cacheControl.append("must-revalidate"); + } + if (cacheControl.length() > 0) + { + final String cacheControlValue = cacheControl.toString(); + this.res.setHeader("Cache-Control", cacheControlValue); + LOGGER.debug("Cache - set response header Cache-Control: {}", cacheControl); + // special case for IE Ajax request handling + if (ContentStreamer.NO_CACHE.equals(cacheControlValue)) + { + this.res.setHeader("Expires", "Thu, 01 Jan 1970 00:00:00 GMT"); + } + } + if (pragma.length() > 0) + { + this.res.setHeader("Pragma", pragma); + LOGGER.debug("Cache - set response header Pragma: {}", pragma); + } + + // set ETag + if (cache.getETag() != null) + { + final String eTag = "\"" + cache.getETag() + "\""; + this.res.setHeader("ETag", eTag); + LOGGER.debug("Cache - set response header ETag: {}", eTag); + } + + // set Last Modified + if (cache.getLastModified() != null) + { + this.res.setDateHeader("Last-Modified", cache.getLastModified().getTime()); + if (LOGGER.isDebugEnabled()) + { + final SimpleDateFormat formatter = ContentStreamer.getHTTPDateFormat(); + final String lastModified = formatter.format(cache.getLastModified()); + LOGGER.debug("Cache - set response header Last-Modified: {}", lastModified); + } + } + } + + /** + * + * {@inheritDoc} + */ + @Override + public OutputStream getOutputStream() throws IOException + { + return this.res.getOutputStream(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isCommitted() + { + return res.isCommitted(); + } + + /** + * {@inheritDoc} + */ + @Override + public void reset() + { + res.reset(); + } + + /** + * {@inheritDoc} + */ + @Override + public void sendError(int status, String message) throws IOException + { + res.sendError(status, message); + } + +} \ No newline at end of file diff --git a/share/src/main/java/org/orderofthebee/addons/support/tools/share/LogFileGetServlet.java b/share/src/main/java/org/orderofthebee/addons/support/tools/share/LegacyLogFileGetServlet.java similarity index 88% rename from share/src/main/java/org/orderofthebee/addons/support/tools/share/LogFileGetServlet.java rename to share/src/main/java/org/orderofthebee/addons/support/tools/share/LegacyLogFileGetServlet.java index 0611f294..98e493ce 100644 --- a/share/src/main/java/org/orderofthebee/addons/support/tools/share/LogFileGetServlet.java +++ b/share/src/main/java/org/orderofthebee/addons/support/tools/share/LegacyLogFileGetServlet.java @@ -27,6 +27,7 @@ import java.util.Map; import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -49,7 +50,8 @@ /** * @author Axel Faust */ -public class LogFileGetServlet extends HttpServlet +@WebServlet(name = "OOTBee Support Tools - Log File Download", urlPatterns = { "/ootbee-support-tools/log4j-log-file/*" }) +public class LegacyLogFileGetServlet extends HttpServlet { private static final long serialVersionUID = 5162376729083905078L; @@ -107,8 +109,7 @@ protected void doGet(final HttpServletRequest req, final HttpServletResponse res if (user != null && user.isAdmin()) { final String requestURI = req.getRequestURI(); - String filePath = URLDecoder - .decode(requestURI.substring(req.getContextPath().length() + req.getServletPath().length() + 1)); + String filePath = URLDecoder.decode(requestURI.substring(req.getContextPath().length() + req.getServletPath().length() + 1)); if (!filePath.startsWith("/")) { filePath = "/" + filePath; @@ -124,7 +125,9 @@ protected void doGet(final HttpServletRequest req, final HttpServletResponse res final String attachParam = req.getParameter("a"); final boolean attach = attachParam != null && Boolean.parseBoolean(attachParam); - this.logFileHandler.handleLogFileRequest(filePath, attach, req, res, model); + final LegacyHttpServletRequestWrapper reqW = new LegacyHttpServletRequestWrapper(req); + final LegacyHttpServletResponseWrapper resW = new LegacyHttpServletResponseWrapper(res); + this.logFileHandler.handleLogFileRequest(filePath, attach, () -> reqW, () -> resW, model); } else { diff --git a/share/src/main/java/org/orderofthebee/addons/support/tools/share/LogFilesZIPPostServlet.java b/share/src/main/java/org/orderofthebee/addons/support/tools/share/LegacyLogFilesZIPPostServlet.java similarity index 89% rename from share/src/main/java/org/orderofthebee/addons/support/tools/share/LogFilesZIPPostServlet.java rename to share/src/main/java/org/orderofthebee/addons/support/tools/share/LegacyLogFilesZIPPostServlet.java index 457851e0..4d35bce6 100644 --- a/share/src/main/java/org/orderofthebee/addons/support/tools/share/LogFilesZIPPostServlet.java +++ b/share/src/main/java/org/orderofthebee/addons/support/tools/share/LegacyLogFilesZIPPostServlet.java @@ -30,6 +30,7 @@ import java.util.Map; import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -52,7 +53,8 @@ /** * @author Axel Faust */ -public class LogFilesZIPPostServlet extends HttpServlet +@WebServlet(name = "OOTBee Support Tools - Log ZIP File Download", urlPatterns = { "/ootbee-support-tools/log4j-log-files.zip" }) +public class LegacyLogFilesZIPPostServlet extends HttpServlet { private static final long serialVersionUID = 6594146886154738251L; @@ -121,7 +123,9 @@ protected void doPost(final HttpServletRequest req, final HttpServletResponse re final String[] paths = rqData.getParameters().get("paths"); filePaths.addAll(Arrays.asList(paths)); - this.logFileHandler.handleLogZipRequest(filePaths, req, res, model); + final LegacyHttpServletRequestWrapper reqW = new LegacyHttpServletRequestWrapper(req); + final LegacyHttpServletResponseWrapper resW = new LegacyHttpServletResponseWrapper(res); + this.logFileHandler.handleLogZipRequest(filePaths, () -> reqW, () -> resW, model); } else { diff --git a/share/src/main/java/org/orderofthebee/addons/support/tools/share/LogFileGet.java b/share/src/main/java/org/orderofthebee/addons/support/tools/share/LogFileGet.java index b829c695..d250b69e 100644 --- a/share/src/main/java/org/orderofthebee/addons/support/tools/share/LogFileGet.java +++ b/share/src/main/java/org/orderofthebee/addons/support/tools/share/LogFileGet.java @@ -55,6 +55,8 @@ public void execute(final WebScriptRequest req, final WebScriptResponse res) thr final String attachParam = req.getParameter("a"); final boolean attach = attachParam != null && Boolean.parseBoolean(attachParam); - this.logFileHandler.handleLogFileRequest(filePath, attach, req, res, model); + final WebScriptRequestWrapper reqW = new WebScriptRequestWrapper(req); + final WebScriptResponseWrapper resW = new WebScriptResponseWrapper(res); + this.logFileHandler.handleLogFileRequest(filePath, attach, () -> reqW, () -> resW, model); } } diff --git a/share/src/main/java/org/orderofthebee/addons/support/tools/share/LogFileHandler.java b/share/src/main/java/org/orderofthebee/addons/support/tools/share/LogFileHandler.java index b2f7f082..83b37d9e 100644 --- a/share/src/main/java/org/orderofthebee/addons/support/tools/share/LogFileHandler.java +++ b/share/src/main/java/org/orderofthebee/addons/support/tools/share/LogFileHandler.java @@ -25,13 +25,12 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.nio.file.Files; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import java.util.function.Supplier; import org.alfresco.util.ParameterCheck; import org.alfresco.util.TempFileProvider; @@ -43,7 +42,6 @@ import org.springframework.extensions.webscripts.Status; import org.springframework.extensions.webscripts.WebScriptException; import org.springframework.extensions.webscripts.WebScriptRequest; -import org.springframework.extensions.webscripts.WebScriptResponse; /** * This class is used to consolidate the log file handling logic that may need to be shared between web script and servlet based log file @@ -178,17 +176,8 @@ public void setContentStreamer(final ContentStreamer contentStreamer) this.contentStreamer = contentStreamer; } - protected void handleLogFileRequest(final String filePath, final boolean attach, final WebScriptRequest req, - final WebScriptResponse res, final Map model) throws IOException - { - final File file = validateFilePath(filePath); - - final String mimetype = determineMimetypeFromFileName(file); - this.contentStreamer.streamContent(req, res, file, file.lastModified(), attach, file.getName(), model, mimetype); - } - - protected void handleLogFileRequest(final String filePath, final boolean attach, final HttpServletRequest req, - final HttpServletResponse res, final Map model) throws IOException + protected void handleLogFileRequest(final String filePath, final boolean attach, final Supplier req, + final Supplier res, final Map model) throws IOException { try { @@ -199,41 +188,24 @@ protected void handleLogFileRequest(final String filePath, final boolean attach, } catch (final WebScriptException wsex) { - if (!res.isCommitted()) + Response rres = res.get(); + if (!rres.isCommitted()) { - res.reset(); - res.sendError(wsex.getStatus(), wsex.getMessage()); + rres.reset(); + rres.sendError(wsex.getStatus(), wsex.getMessage()); } - else + else if (!(rres instanceof WebScriptResponseWrapper)) { LOGGER.info("Could not send error via committed response", wsex); } - } - } - - protected void handleLogZipRequest(final List filePaths, final WebScriptRequest req, final WebScriptResponse res, - final Map model) throws IOException - { - final List files = LogFileHandler.validateFilePaths(filePaths); - final File logFileZip = TempFileProvider.createTempFile("ootbee-support-tools-logFiles", "zip"); - try - { - this.createZip(files, logFileZip); - - this.contentStreamer.streamContent(req, res, logFileZip, logFileZip.lastModified(), false, "log-files.zip", model, - "application/zip"); - } - finally - { - // eager cleanup - if (!logFileZip.delete()) + else { - logFileZip.deleteOnExit(); + throw wsex; } } } - protected void handleLogZipRequest(final List filePaths, final HttpServletRequest req, final HttpServletResponse res, + protected void handleLogZipRequest(final List filePaths, final Supplier req, final Supplier res, final Map model) throws IOException { try @@ -250,7 +222,7 @@ protected void handleLogZipRequest(final List filePaths, final HttpServl finally { // eager cleanup - if (!logFileZip.delete()) + if (!Files.deleteIfExists(logFileZip.toPath())) { logFileZip.deleteOnExit(); } @@ -258,15 +230,20 @@ protected void handleLogZipRequest(final List filePaths, final HttpServl } catch (final WebScriptException wsex) { - if (!res.isCommitted()) + Response rres = res.get(); + if (!rres.isCommitted()) { - res.reset(); - res.sendError(wsex.getStatus(), wsex.getMessage()); + rres.reset(); + rres.sendError(wsex.getStatus(), wsex.getMessage()); } - else + else if (!(rres instanceof WebScriptResponseWrapper)) { LOGGER.info("Could not send error via committed response", wsex); } + else + { + throw wsex; + } } } diff --git a/share/src/main/java/org/orderofthebee/addons/support/tools/share/LogFilesZIPPost.java b/share/src/main/java/org/orderofthebee/addons/support/tools/share/LogFilesZIPPost.java index f737ea66..e160efc2 100644 --- a/share/src/main/java/org/orderofthebee/addons/support/tools/share/LogFilesZIPPost.java +++ b/share/src/main/java/org/orderofthebee/addons/support/tools/share/LogFilesZIPPost.java @@ -66,7 +66,9 @@ public void execute(final WebScriptRequest req, final WebScriptResponse res) thr final String[] paths = rqData.getParameters().get("paths"); filePaths.addAll(Arrays.asList(paths)); - this.logFileHandler.handleLogZipRequest(filePaths, req, res, model); + final WebScriptRequestWrapper reqW = new WebScriptRequestWrapper(req); + final WebScriptResponseWrapper resW = new WebScriptResponseWrapper(res); + this.logFileHandler.handleLogZipRequest(filePaths, () -> reqW, () -> resW, model); } } diff --git a/share/src/main/java/org/orderofthebee/addons/support/tools/share/Request.java b/share/src/main/java/org/orderofthebee/addons/support/tools/share/Request.java new file mode 100644 index 00000000..8a35a318 --- /dev/null +++ b/share/src/main/java/org/orderofthebee/addons/support/tools/share/Request.java @@ -0,0 +1,50 @@ +/** + * Copyright (C) 2016 - 2023 Order of the Bee + * + * This file is part of OOTBee Support Tools + * + * OOTBee Support Tools is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * OOTBee Support Tools is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OOTBee Support Tools. If not, see + * . + * + * Linked to Alfresco + * Copyright (C) 2005 - 2023 Alfresco Software Limited. + */ +package org.orderofthebee.addons.support.tools.share; + +/** + * This interface is used to abstract the actual request away for our {@link ContentStreamer} and {@link LogFileHandler}. + * + * @author Axel Faust + */ +public interface Request +{ + + /** + * Returns the value of the specified request header as a String. If the request did not include a header of the + * specified name, this method returns null. If there are multiple headers with the same name, this method returns the + * first head in the request. The header name is case insensitive. You can use this method with any request header. + * + * @param name + * a String specifying the + * header name + * + * @return a String containing the + * value of the requested + * header, or null + * if the request does not + * have a header of that name + * + */ + String getHeader(String name); +} \ No newline at end of file diff --git a/share/src/main/java/org/orderofthebee/addons/support/tools/share/Response.java b/share/src/main/java/org/orderofthebee/addons/support/tools/share/Response.java new file mode 100644 index 00000000..c73c8663 --- /dev/null +++ b/share/src/main/java/org/orderofthebee/addons/support/tools/share/Response.java @@ -0,0 +1,129 @@ +/** + * Copyright (C) 2016 - 2023 Order of the Bee + * + * This file is part of OOTBee Support Tools + * + * OOTBee Support Tools is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * OOTBee Support Tools is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OOTBee Support Tools. If not, see + * . + * + * Linked to Alfresco + * Copyright (C) 2005 - 2023 Alfresco Software Limited. + */ +package org.orderofthebee.addons.support.tools.share; + +import java.io.IOException; +import java.io.OutputStream; + +import org.springframework.extensions.webscripts.Cache; + +/** + * This interface is used to abstract the actual response away for our {@link ContentStreamer} and {@link LogFileHandler}. + * + * @author Axel Faust + */ +public interface Response +{ + + /** + *

+ * Sets the status code for this response. This method is used to set the return status code when there is no error (for example, + * for the status codes SC_OK or SC_MOVED_TEMPORARILY). If there is an error, and the caller wishes to invoke an error-page defined + * in the web application, the sendError method should be used instead. + *

+ * + *

+ * The container clears the buffer and sets the Location header, preserving cookies and other headers. + *

+ * + * @param status + * the status code + * + */ + void setStatus(int status); + + /** + * Sets a response header with the given name and value.If the header had already been set, the new value overwrites the previous + * one. The containsHeader method can be used to test for the presence of a header before setting its value. + * + * @param name + * the name of the header + * @param value + * the header value If it contains octet string, + * it should be encoded according to RFC 2047 + * (http://www.ietf.org/rfc/rfc2047.txt) + * + */ + void setHeader(String name, String value); + + /** + * Sets the Content Type + * + * @param contentType + * String + * + */ + void setContentType(String contentType); + + /** + * Sets the Content Encoding + * + * @param contentEncoding + * String + * + */ + void setContentEncoding(String contentEncoding); + + /** + * Sets the Cache control + * + * @param cache + * cache control + * + */ + void setCache(Cache cache); + + /** + * Returns a {@link OutputStream} suitable for writing binary data in the response. The servlet container does not encode the binary + * data. + * + * @return a {@link OutputStream} for writing binary data + * + * @exception IOException + * if an input or output exception occurred + * + */ + OutputStream getOutputStream() throws IOException; + + /** + * Retrieves the flag denoting whether the response was already committed. + * + * @return {@code true} if the response was already committed + */ + boolean isCommitted(); + + /** + * Reset the response. + */ + void reset(); + + /** + * Send an error status. + * + * @param status + * the status code + * @param message + * the error message + */ + void sendError(int status, String message) throws IOException; +} \ No newline at end of file diff --git a/share/src/main/java/org/orderofthebee/addons/support/tools/share/WebScriptRequestWrapper.java b/share/src/main/java/org/orderofthebee/addons/support/tools/share/WebScriptRequestWrapper.java new file mode 100644 index 00000000..28869b85 --- /dev/null +++ b/share/src/main/java/org/orderofthebee/addons/support/tools/share/WebScriptRequestWrapper.java @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2016 - 2023 Order of the Bee + * + * This file is part of OOTBee Support Tools + * + * OOTBee Support Tools is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * OOTBee Support Tools is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OOTBee Support Tools. If not, see + * . + * + * Linked to Alfresco + * Copyright (C) 2005 - 2023 Alfresco Software Limited. + */ +package org.orderofthebee.addons.support.tools.share; + +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * This class is used to wrap a {@link WebScriptRequest web script request} for use in the {@link ContentStreamer} and + * {@link LogFileHandler}. + * + * @author Axel Faust + */ +public class WebScriptRequestWrapper implements Request +{ + + protected final WebScriptRequest req; + + protected WebScriptRequestWrapper(final WebScriptRequest req) + { + this.req = req; + } + + /** + * + * {@inheritDoc} + */ + @Override + public String getHeader(final String name) + { + return this.req.getHeader(name); + } +} \ No newline at end of file diff --git a/share/src/main/java/org/orderofthebee/addons/support/tools/share/WebScriptResponseWrapper.java b/share/src/main/java/org/orderofthebee/addons/support/tools/share/WebScriptResponseWrapper.java new file mode 100644 index 00000000..14f806fb --- /dev/null +++ b/share/src/main/java/org/orderofthebee/addons/support/tools/share/WebScriptResponseWrapper.java @@ -0,0 +1,141 @@ +/** + * Copyright (C) 2016 - 2023 Order of the Bee + * + * This file is part of OOTBee Support Tools + * + * OOTBee Support Tools is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * OOTBee Support Tools is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OOTBee Support Tools. If not, see + * . + * + * Linked to Alfresco + * Copyright (C) 2005 - 2023 Alfresco Software Limited. + */ +package org.orderofthebee.addons.support.tools.share; + +import java.io.IOException; +import java.io.OutputStream; + +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.WebScriptResponse; + +/** + * This class is used to wrap a {@link WebScriptResponse web script response} for use in the {@link ContentStreamer} and + * {@link LogFileHandler}. + * + * @author Axel Faust + */ +public class WebScriptResponseWrapper implements Response +{ + + private final WebScriptResponse res; + + public WebScriptResponseWrapper(final WebScriptResponse res) + { + this.res = res; + } + + /** + * @return the res + */ + public WebScriptResponse getWebScriptResponse() + { + return res; + } + + /** + * + * {@inheritDoc} + */ + @Override + public void setStatus(final int status) + { + this.res.setStatus(status); + } + + /** + * + * {@inheritDoc} + */ + @Override + public void setHeader(final String name, final String value) + { + this.res.setHeader(name, value); + } + + /** + * + * {@inheritDoc} + */ + @Override + public void setContentType(final String contentType) + { + this.res.setContentType(contentType); + } + + /** + * + * {@inheritDoc} + */ + @Override + public void setContentEncoding(final String contentEncoding) + { + this.res.setContentEncoding(contentEncoding); + } + + /** + * + * {@inheritDoc} + */ + @Override + public void setCache(final Cache cache) + { + this.res.setCache(cache); + } + + /** + * + * {@inheritDoc} + */ + @Override + public OutputStream getOutputStream() throws IOException + { + return this.res.getOutputStream(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isCommitted() + { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public void reset() + { + // NO-OP + } + + /** + * {@inheritDoc} + */ + @Override + public void sendError(int status, String message) + { + // NO-OP + } +} \ No newline at end of file diff --git a/share/src/main/resources/META-INF/web-fragment.xml b/share/src/main/resources/META-INF/web-fragment.xml deleted file mode 100644 index 78e876ff..00000000 --- a/share/src/main/resources/META-INF/web-fragment.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - ${moduleId}-web-fragment - - - - - - - - - OOTBee Support Tools - Log File Download - org.orderofthebee.addons.support.tools.share.LogFileGetServlet - - - - OOTBee Support Tools - Log ZIP File Download - org.orderofthebee.addons.support.tools.share.LogFilesZIPPostServlet - - - - OOTBee Support Tools - Log File Download - /ootbee-support-tools/log4j-log-file/* - - - - OOTBee Support Tools - Log ZIP File Download - /ootbee-support-tools/log4j-log-files.zip - - - \ No newline at end of file