Merge pull request #877 from apache/merge-master-to-7xx-2024-02-16

Merge master to 7-x-x 2024-02-16
This commit is contained in:
Lukasz Lenart
2024-02-21 08:05:18 +01:00
committed by GitHub
18 changed files with 591 additions and 332 deletions
+1 -1
View File
@@ -58,7 +58,7 @@ jobs:
publish_results: true
- name: "Upload artifact"
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # 4.3.0
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # 4.3.1
with:
name: SARIF file
path: results.sarif
@@ -99,4 +99,10 @@ public class File extends UIBean {
public void setSize(String size) {
this.size = size;
}
@Override
@StrutsTagAttribute(description="Ignored during file upload")
public void setValue(String value) {
// ignores provided value
}
}
@@ -371,6 +371,6 @@
<td class="tag-attribute"></td>
<td class="tag-attribute">false</td>
<td class="tag-attribute">String</td>
<td class="tag-attribute">Preset the value of input element.</td>
<td class="tag-attribute">Ignored during file upload</td>
</tr>
</table>
@@ -30,7 +30,7 @@ import org.apache.struts2.dispatcher.mapper.ActionMapper;
import org.apache.struts2.dispatcher.multipart.MultiPartRequest;
import org.apache.struts2.views.freemarker.FreemarkerManager;
import org.apache.struts2.views.velocity.VelocityConstants;
import org.apache.struts2.views.velocity.VelocityManager;
import org.apache.struts2.views.velocity.VelocityManagerInterface;
import java.util.Map;
import java.util.Set;
@@ -56,7 +56,7 @@ public class ShowBeansAction extends ActionNamesAction {
bindings.put(ActionMapper.class.getName(), addBindings(container, ActionMapper.class, StrutsConstants.STRUTS_MAPPER_CLASS));
bindings.put(MultiPartRequest.class.getName(), addBindings(container, MultiPartRequest.class, StrutsConstants.STRUTS_MULTIPART_PARSER));
bindings.put(FreemarkerManager.class.getName(), addBindings(container, FreemarkerManager.class, StrutsConstants.STRUTS_FREEMARKER_MANAGER_CLASSNAME));
bindings.put(VelocityManager.class.getName(), addBindings(container, VelocityManager.class, VelocityConstants.STRUTS_VELOCITY_MANAGER_CLASSNAME));
bindings.put(VelocityManagerInterface.class.getName(), addBindings(container, VelocityManagerInterface.class, VelocityConstants.STRUTS_VELOCITY_MANAGER_CLASSNAME));
bindings.put(UrlRenderer.class.getName(), addBindings(container, UrlRenderer.class, StrutsConstants.STRUTS_URL_RENDERER));
}
@@ -33,7 +33,6 @@ public class FileHandler extends AbstractTagHandler implements TagGenerator {
a.addDefaultToEmpty("name", params.get("name"))
.add("type", "file")
.addIfExists("size", params.get("size"))
.addIfExists("value", params.get("nameValue"))
.addIfTrue("disabled", params.get("disabled"))
.addIfExists("accept", params.get("accept"))
.addIfExists("tabindex", params.get("tabindex"))
@@ -28,7 +28,6 @@ public class FileTest extends AbstractCommonAttributesTest {
public void testRenderTextField() {
tag.setName("name");
tag.setValue("val1");
tag.setSize("10");
tag.setDisabled("true");
tag.setAccept("accept_");
@@ -43,7 +42,7 @@ public class FileTest extends AbstractCommonAttributesTest {
map.putAll(tag.getParameters());
theme.renderTag(getTagName(), context);
String output = writer.getBuffer().toString();
String expected = s("<input name='name' type='file' size='10' value='val1' disabled='disabled' accept='accept_' tabindex='1' id='id1' class='class1' style='style1' title='title'></input>");
String expected = s("<input name='name' type='file' size='10' disabled='disabled' accept='accept_' tabindex='1' id='id1' class='class1' style='style1' title='title'></input>");
assertEquals(expected, output);
}
@@ -51,8 +51,9 @@ public class ContentTypeInterceptor extends AbstractInterceptor {
}
if (request.getContentLength() > 0) {
final String encoding = request.getCharacterEncoding();
InputStream is = request.getInputStream();
InputStreamReader reader = new InputStreamReader(is);
InputStreamReader reader = encoding == null ? new InputStreamReader(is) : new InputStreamReader(is, encoding);
handler.toObject(invocation, reader, target);
}
return invocation.invoke();
@@ -0,0 +1,154 @@
/*
* 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.struts2.rest;
import com.mockobjects.dynamic.AnyConstraintMatcher;
import com.mockobjects.dynamic.Mock;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ActionSupport;
import junit.framework.TestCase;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import org.apache.struts2.dispatcher.mapper.ActionMapping;
import org.apache.struts2.rest.handler.ContentTypeHandler;
import org.springframework.mock.web.MockHttpServletRequest;
public class ContentTypeInterceptorTest extends TestCase {
public void testRequestWithoutEncoding() throws Exception {
ContentTypeInterceptor interceptor = new ContentTypeInterceptor();
ActionSupport action = new ActionSupport();
Mock mockActionInvocation = new Mock(ActionInvocation.class);
Mock mockContentTypeHandler = new Mock(ContentTypeHandler.class);
mockContentTypeHandler.expect("toObject", new AnyConstraintMatcher() {
public boolean matches(Object[] args) {
return true;
}
});
mockActionInvocation.expectAndReturn("invoke", Action.SUCCESS);
mockActionInvocation.expectAndReturn("getAction", action);
Mock mockContentTypeHandlerManager = new Mock(ContentTypeHandlerManager.class);
mockContentTypeHandlerManager.expectAndReturn("getHandlerForRequest", new AnyConstraintMatcher() {
public boolean matches(Object[] args) {
return true;
}
}, mockContentTypeHandler.proxy());
interceptor.setContentTypeHandlerSelector((ContentTypeHandlerManager) mockContentTypeHandlerManager.proxy());
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContent(new byte[] {1});
ActionContext.of()
.withActionMapping(new ActionMapping())
.withServletRequest(request)
.bind();
interceptor.intercept((ActionInvocation) mockActionInvocation.proxy());
mockContentTypeHandlerManager.verify();
mockActionInvocation.verify();
mockContentTypeHandler.verify();
}
public void testRequestWithEncodingAscii() throws Exception {
final Charset charset = StandardCharsets.US_ASCII;
ContentTypeInterceptor interceptor = new ContentTypeInterceptor();
ActionSupport action = new ActionSupport();
Mock mockActionInvocation = new Mock(ActionInvocation.class);
Mock mockContentTypeHandler = new Mock(ContentTypeHandler.class);
mockContentTypeHandler.expect("toObject", new AnyConstraintMatcher() {
public boolean matches(Object[] args) {
InputStreamReader in = (InputStreamReader) args[1];
return charset.equals(Charset.forName(in.getEncoding()));
}
});
mockActionInvocation.expectAndReturn("invoke", Action.SUCCESS);
mockActionInvocation.expectAndReturn("getAction", action);
Mock mockContentTypeHandlerManager = new Mock(ContentTypeHandlerManager.class);
mockContentTypeHandlerManager.expectAndReturn("getHandlerForRequest", new AnyConstraintMatcher() {
public boolean matches(Object[] args) {
return true;
}
}, mockContentTypeHandler.proxy());
interceptor.setContentTypeHandlerSelector((ContentTypeHandlerManager) mockContentTypeHandlerManager.proxy());
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContent(new byte[] {1});
request.setCharacterEncoding(charset.name());
ActionContext.of()
.withActionMapping(new ActionMapping())
.withServletRequest(request)
.bind();
interceptor.intercept((ActionInvocation) mockActionInvocation.proxy());
mockContentTypeHandlerManager.verify();
mockActionInvocation.verify();
mockContentTypeHandler.verify();
}
public void testRequestWithEncodingUtf() throws Exception {
final Charset charset = StandardCharsets.UTF_8;
ContentTypeInterceptor interceptor = new ContentTypeInterceptor();
ActionSupport action = new ActionSupport();
Mock mockActionInvocation = new Mock(ActionInvocation.class);
Mock mockContentTypeHandler = new Mock(ContentTypeHandler.class);
mockContentTypeHandler.expect("toObject", new AnyConstraintMatcher() {
public boolean matches(Object[] args) {
InputStreamReader in = (InputStreamReader) args[1];
return charset.equals(Charset.forName(in.getEncoding()));
}
});
mockActionInvocation.expectAndReturn("invoke", Action.SUCCESS);
mockActionInvocation.expectAndReturn("getAction", action);
Mock mockContentTypeHandlerManager = new Mock(ContentTypeHandlerManager.class);
mockContentTypeHandlerManager.expectAndReturn("getHandlerForRequest", new AnyConstraintMatcher() {
public boolean matches(Object[] args) {
return true;
}
}, mockContentTypeHandler.proxy());
interceptor.setContentTypeHandlerSelector((ContentTypeHandlerManager) mockContentTypeHandlerManager.proxy());
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContent(new byte[] {1});
request.setCharacterEncoding(charset.name());
ActionContext.of()
.withActionMapping(new ActionMapping())
.withServletRequest(request)
.bind();
interceptor.intercept((ActionInvocation) mockActionInvocation.proxy());
mockContentTypeHandlerManager.verify();
mockActionInvocation.verify();
mockContentTypeHandler.verify();
}
}
@@ -1,101 +1,110 @@
/*
* 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.struts2.sitemesh;
import com.opensymphony.module.sitemesh.HTMLPage;
import com.opensymphony.sitemesh.Content;
import com.opensymphony.sitemesh.compatability.Content2HTMLPage;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.inject.Inject;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.apache.struts2.views.velocity.VelocityManager;
import org.apache.velocity.context.Context;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* Extends OldDecorator2NewStrutsDecorator to add Struts functionality for Velocity
*/
public class OldDecorator2NewStrutsVelocityDecorator extends OldDecorator2NewStrutsDecorator {
private static final Logger LOG = LogManager.getLogger(OldDecorator2NewStrutsFreemarkerDecorator.class);
private static VelocityManager velocityManager;
@Inject(required = false)
public static void setVelocityManager(VelocityManager mgr) {
velocityManager = mgr;
}
public OldDecorator2NewStrutsVelocityDecorator(com.opensymphony.module.sitemesh.Decorator oldDecorator) {
this.oldDecorator = oldDecorator;
}
/**
* Applies the decorator, using the relevent contexts
*
* @param content The content
* @param request The servlet request
* @param response The servlet response
* @param servletContext The servlet context
* @param ctx The action context for this request, populated with the server state
*/
protected void render(Content content, HttpServletRequest request, HttpServletResponse response, ServletContext servletContext, ActionContext ctx) throws ServletException, IOException {
if (velocityManager == null) {
throw new ServletException("Missing freemarker dependency");
}
try {
// init (if needed)
velocityManager.init(servletContext);
// get encoding
String encoding = getEncoding();
HTMLPage htmlPage = new Content2HTMLPage(content, request);
// get the template and context
org.apache.velocity.Template template = velocityManager.getVelocityEngine().getTemplate(oldDecorator.getPage(), encoding);
Context context = velocityManager.createContext(ctx.getValueStack(), request, response);
// put the page in the context
context.put("page", htmlPage);
context.put("head", htmlPage.getHead());
context.put("title", htmlPage.getTitle());
context.put("body", htmlPage.getBody());
// finally, render it
PrintWriter writer = response.getWriter();
template.merge(context, writer);
writer.flush();
} catch (Exception e) {
String msg = "Error applying decorator to request: " + request.getRequestURL() + "?" + request.getQueryString() + " with message:" + e.getMessage();
LOG.error(msg, e);
throw new ServletException(msg, e);
}
}
}
/*
* 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.struts2.sitemesh;
import com.opensymphony.module.sitemesh.HTMLPage;
import com.opensymphony.sitemesh.Content;
import com.opensymphony.sitemesh.compatability.Content2HTMLPage;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.inject.Inject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.views.velocity.VelocityManager;
import org.apache.struts2.views.velocity.VelocityManagerInterface;
import org.apache.velocity.context.Context;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* Extends OldDecorator2NewStrutsDecorator to add Struts functionality for Velocity
*/
public class OldDecorator2NewStrutsVelocityDecorator extends OldDecorator2NewStrutsDecorator {
private static final Logger LOG = LogManager.getLogger(OldDecorator2NewStrutsFreemarkerDecorator.class);
private static VelocityManagerInterface velocityManager;
@Inject(required = false)
public static void setVelocityManager(VelocityManagerInterface mgr) {
velocityManager = mgr;
}
/**
* @deprecated since 6.4.0
*/
@Deprecated
public static void setVelocityManager(VelocityManager mgr) {
setVelocityManager((VelocityManagerInterface) mgr);
}
public OldDecorator2NewStrutsVelocityDecorator(com.opensymphony.module.sitemesh.Decorator oldDecorator) {
this.oldDecorator = oldDecorator;
}
/**
* Applies the decorator, using the relevent contexts
*
* @param content The content
* @param request The servlet request
* @param response The servlet response
* @param servletContext The servlet context
* @param ctx The action context for this request, populated with the server state
*/
protected void render(Content content, HttpServletRequest request, HttpServletResponse response, ServletContext servletContext, ActionContext ctx) throws ServletException, IOException {
if (velocityManager == null) {
throw new ServletException("Missing freemarker dependency");
}
try {
// init (if needed)
velocityManager.init(servletContext);
// get encoding
String encoding = getEncoding();
HTMLPage htmlPage = new Content2HTMLPage(content, request);
// get the template and context
org.apache.velocity.Template template = velocityManager.getVelocityEngine().getTemplate(oldDecorator.getPage(), encoding);
Context context = velocityManager.createContext(ctx.getValueStack(), request, response);
// put the page in the context
context.put("page", htmlPage);
context.put("head", htmlPage.getHead());
context.put("title", htmlPage.getTitle());
context.put("body", htmlPage.getBody());
// finally, render it
PrintWriter writer = response.getWriter();
template.merge(context, writer);
writer.flush();
} catch (Exception e) {
String msg = "Error applying decorator to request: " + request.getRequestURL() + "?" + request.getQueryString() + " with message:" + e.getMessage();
LOG.error(msg, e);
throw new ServletException(msg, e);
}
}
}
@@ -1,193 +1,197 @@
/*
* 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.struts2.sitemesh;
import com.opensymphony.module.sitemesh.*;
import com.opensymphony.module.sitemesh.util.OutputConverter;
import com.opensymphony.xwork2.ActionContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.StrutsStatics;
import org.apache.struts2.dispatcher.Dispatcher;
import org.apache.struts2.dispatcher.listener.StrutsListener;
import org.apache.struts2.views.velocity.VelocityManager;
import org.apache.velocity.Template;
import org.apache.velocity.context.Context;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.tools.view.VelocityView;
import org.apache.velocity.tools.view.VelocityViewServlet;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.StringWriter;
/**
* <p>This is a SiteMesh Velocity view servlet.</p>
*
* <p>It overrides the SiteMesh servlet to rely on the
* Velocity Manager in Struts instead of creating it's
* own manager</p>
*/
public class VelocityDecoratorServlet extends VelocityViewServlet {
private static final Logger LOG = LogManager.getLogger(VelocityDecoratorServlet.class);
private static final long serialVersionUID = -6731485159371716918L;
protected VelocityManager velocityManager;
protected String defaultContentType;
/**
* <p>Initializes servlet, toolbox and Velocity template engine.
* Called by the servlet container on loading.</p>
*
* <p>NOTE: If no charset is specified in the default.contentType
* property (in your velocity.properties) and you have specified
* an output.encoding property, then that will be used as the
* charset for the default content-type of pages served by this
* servlet.</p>
*
* @param config servlet configuration
*/
public void init(ServletConfig config) throws ServletException {
super.init(config);
Dispatcher dispatcher = Dispatcher.getInstance(getServletContext());
if (dispatcher == null) {
throw new IllegalStateException("Unable to find the Dispatcher in the Servlet Context. Is '" + StrutsListener.class.getName() + "' missing in web.xml?");
}
velocityManager = dispatcher.getContainer().getInstance(VelocityManager.class);
velocityManager.init(config.getServletContext());
// do whatever we have to do to init Velocity
getVelocityView().setVelocityEngine(velocityManager.getVelocityEngine());
// toolboxManager = velocityManager.getToolboxManager();
// we can get these now that velocity is initialized
defaultContentType = getVelocityProperty(VelocityView.CONTENT_TYPE_KEY, VelocityView.DEFAULT_CONTENT_TYPE);
String encoding = getVelocityProperty(RuntimeConstants.ENCODING_DEFAULT, VelocityView.DEFAULT_OUTPUT_ENCODING);
// For non Latin-1 encodings, ensure that the charset is
// included in the Content-Type header.
if (!VelocityView.DEFAULT_OUTPUT_ENCODING.equalsIgnoreCase(encoding)) {
int index = defaultContentType.lastIndexOf("charset");
if (index < 0) {
// the charset specifier is not yet present in header.
// append character encoding to default content-type
defaultContentType += "; charset=" + encoding;
} else {
// The user may have configuration issues.
getVelocityView().getVelocityEngine().getLog().warn("VelocityViewServlet: Charset was already " + "specified in the Content-Type property. " + "Output encoding property will be ignored.");
}
}
getVelocityView().getVelocityEngine().getLog().info("VelocityViewServlet: Default content-type is: " + defaultContentType);
}
public Template handleRequest(HttpServletRequest request, HttpServletResponse response, Context context) {
HTMLPage htmlPage = (HTMLPage) request.getAttribute(RequestConstants.PAGE);
String template;
context.put("base", request.getContextPath());
// For backwards compatibility with apps that used the old VelocityDecoratorServlet
// that extended VelocityServlet instead of VelocityViewServlet
context.put("req", request);
context.put("res", response);
if (htmlPage == null) {
context.put("title", "Title?");
context.put("body", "<p>Body?</p>");
context.put("head", "<!-- head -->");
template = request.getServletPath();
} else {
try {
context.put("title", OutputConverter.convert(htmlPage.getTitle()));
{
StringWriter buffer = new StringWriter();
htmlPage.writeBody(OutputConverter.getWriter(buffer));
context.put("body", buffer.toString());
}
{
StringWriter buffer = new StringWriter();
htmlPage.writeHead(OutputConverter.getWriter(buffer));
context.put("head", buffer.toString());
}
} catch (IOException e) {
LOG.error("IOException handle request template", e);
}
context.put("page", htmlPage);
DecoratorMapper decoratorMapper = getDecoratorMapper();
Decorator decorator = decoratorMapper.getDecorator(request, htmlPage);
template = decorator.getPage();
}
return getTemplate(template);
}
private DecoratorMapper getDecoratorMapper() {
Factory factory = Factory.getInstance(new Config(getServletConfig()));
return factory.getDecoratorMapper();
}
/**
* <p>Creates and returns an initialized Velocity context.</p>
*
* @param request servlet request from client
* @param response servlet reponse to client
*/
protected Context createContext(HttpServletRequest request, HttpServletResponse response) {
Context context = (Context) request.getAttribute(VelocityManager.KEY_VELOCITY_STRUTS_CONTEXT);
if (context == null) {
ActionContext ctx = ServletActionContext.getActionContext(request);
context = velocityManager.createContext(ctx.getValueStack(), request, response);
}
return context;
}
/**
* <p>
* Sets the content type of the response. This is available to be overridden
* by a derived class.
* </p>
* <p>The default implementation is:</p>
* <pre>
*
* response.setContentType(defaultContentType);
*
* </pre>
* <p>
* where defaultContentType is set to the value of the default.contentType
* property, or "text/html" if that is not set.</p>
*
* @param request servlet request from client
* @param response servlet reponse to client
*/
protected void setContentType(HttpServletRequest request, HttpServletResponse response) {
response.setContentType(defaultContentType);
}
}
/*
* 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.struts2.sitemesh;
import com.opensymphony.module.sitemesh.Config;
import com.opensymphony.module.sitemesh.Decorator;
import com.opensymphony.module.sitemesh.DecoratorMapper;
import com.opensymphony.module.sitemesh.Factory;
import com.opensymphony.module.sitemesh.HTMLPage;
import com.opensymphony.module.sitemesh.RequestConstants;
import com.opensymphony.module.sitemesh.util.OutputConverter;
import com.opensymphony.xwork2.ActionContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.dispatcher.Dispatcher;
import org.apache.struts2.dispatcher.listener.StrutsListener;
import org.apache.struts2.views.velocity.VelocityManager;
import org.apache.struts2.views.velocity.VelocityManagerInterface;
import org.apache.velocity.Template;
import org.apache.velocity.context.Context;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.tools.view.VelocityView;
import org.apache.velocity.tools.view.VelocityViewServlet;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.StringWriter;
/**
* <p>This is a SiteMesh Velocity view servlet.</p>
*
* <p>It overrides the SiteMesh servlet to rely on the
* Velocity Manager in Struts instead of creating it's
* own manager</p>
*/
public class VelocityDecoratorServlet extends VelocityViewServlet {
private static final Logger LOG = LogManager.getLogger(VelocityDecoratorServlet.class);
private static final long serialVersionUID = -6731485159371716918L;
protected VelocityManagerInterface velocityManager;
protected String defaultContentType;
/**
* <p>Initializes servlet, toolbox and Velocity template engine.
* Called by the servlet container on loading.</p>
*
* <p>NOTE: If no charset is specified in the default.contentType
* property (in your velocity.properties) and you have specified
* an output.encoding property, then that will be used as the
* charset for the default content-type of pages served by this
* servlet.</p>
*
* @param config servlet configuration
*/
public void init(ServletConfig config) throws ServletException {
super.init(config);
Dispatcher dispatcher = Dispatcher.getInstance(getServletContext());
if (dispatcher == null) {
throw new IllegalStateException("Unable to find the Dispatcher in the Servlet Context. Is '" + StrutsListener.class.getName() + "' missing in web.xml?");
}
velocityManager = dispatcher.getContainer().getInstance(VelocityManagerInterface.class);
velocityManager.init(config.getServletContext());
// do whatever we have to do to init Velocity
getVelocityView().setVelocityEngine(velocityManager.getVelocityEngine());
// toolboxManager = velocityManager.getToolboxManager();
// we can get these now that velocity is initialized
defaultContentType = getVelocityProperty(VelocityView.CONTENT_TYPE_KEY, VelocityView.DEFAULT_CONTENT_TYPE);
String encoding = getVelocityProperty(RuntimeConstants.ENCODING_DEFAULT, VelocityView.DEFAULT_OUTPUT_ENCODING);
// For non Latin-1 encodings, ensure that the charset is
// included in the Content-Type header.
if (!VelocityView.DEFAULT_OUTPUT_ENCODING.equalsIgnoreCase(encoding)) {
int index = defaultContentType.lastIndexOf("charset");
if (index < 0) {
// the charset specifier is not yet present in header.
// append character encoding to default content-type
defaultContentType += "; charset=" + encoding;
} else {
// The user may have configuration issues.
getVelocityView().getVelocityEngine().getLog().warn("VelocityViewServlet: Charset was already " + "specified in the Content-Type property. " + "Output encoding property will be ignored.");
}
}
getVelocityView().getVelocityEngine().getLog().info("VelocityViewServlet: Default content-type is: " + defaultContentType);
}
public Template handleRequest(HttpServletRequest request, HttpServletResponse response, Context context) {
HTMLPage htmlPage = (HTMLPage) request.getAttribute(RequestConstants.PAGE);
String template;
context.put("base", request.getContextPath());
// For backwards compatibility with apps that used the old VelocityDecoratorServlet
// that extended VelocityServlet instead of VelocityViewServlet
context.put("req", request);
context.put("res", response);
if (htmlPage == null) {
context.put("title", "Title?");
context.put("body", "<p>Body?</p>");
context.put("head", "<!-- head -->");
template = request.getServletPath();
} else {
try {
context.put("title", OutputConverter.convert(htmlPage.getTitle()));
{
StringWriter buffer = new StringWriter();
htmlPage.writeBody(OutputConverter.getWriter(buffer));
context.put("body", buffer.toString());
}
{
StringWriter buffer = new StringWriter();
htmlPage.writeHead(OutputConverter.getWriter(buffer));
context.put("head", buffer.toString());
}
} catch (IOException e) {
LOG.error("IOException handle request template", e);
}
context.put("page", htmlPage);
DecoratorMapper decoratorMapper = getDecoratorMapper();
Decorator decorator = decoratorMapper.getDecorator(request, htmlPage);
template = decorator.getPage();
}
return getTemplate(template);
}
private DecoratorMapper getDecoratorMapper() {
Factory factory = Factory.getInstance(new Config(getServletConfig()));
return factory.getDecoratorMapper();
}
/**
* <p>Creates and returns an initialized Velocity context.</p>
*
* @param request servlet request from client
* @param response servlet reponse to client
*/
protected Context createContext(HttpServletRequest request, HttpServletResponse response) {
Context context = (Context) request.getAttribute(VelocityManager.KEY_VELOCITY_STRUTS_CONTEXT);
if (context == null) {
ActionContext ctx = ServletActionContext.getActionContext(request);
context = velocityManager.createContext(ctx.getValueStack(), request, response);
}
return context;
}
/**
* <p>
* Sets the content type of the response. This is available to be overridden
* by a derived class.
* </p>
* <p>The default implementation is:</p>
* <pre>
*
* response.setContentType(defaultContentType);
*
* </pre>
* <p>
* where defaultContentType is set to the value of the default.contentType
* property, or "text/html" if that is not set.</p>
*
* @param request servlet request from client
* @param response servlet reponse to client
*/
protected void setContentType(HttpServletRequest request, HttpServletResponse response) {
response.setContentType(defaultContentType);
}
}
@@ -18,27 +18,35 @@
*/
package org.apache.struts2.sitemesh;
import com.opensymphony.sitemesh.webapp.SiteMeshWebAppContext;
import com.opensymphony.sitemesh.webapp.SiteMeshFilter;
import com.opensymphony.sitemesh.DecoratorSelector;
import com.opensymphony.module.sitemesh.Factory;
import com.opensymphony.module.sitemesh.Config;
import com.opensymphony.module.sitemesh.Factory;
import com.opensymphony.sitemesh.DecoratorSelector;
import com.opensymphony.sitemesh.webapp.SiteMeshFilter;
import com.opensymphony.sitemesh.webapp.SiteMeshWebAppContext;
import com.opensymphony.xwork2.inject.Inject;
import org.apache.struts2.views.velocity.VelocityManager;
import org.apache.struts2.views.velocity.VelocityManagerInterface;
import jakarta.servlet.*;
import org.apache.struts2.views.velocity.VelocityManager;
/**
* Core Filter for integrating SiteMesh into a Java web application.
*/
public class VelocityPageFilter extends SiteMeshFilter {
@Inject(required=false)
public static void setVelocityManager(VelocityManager mgr) {
public static void setVelocityManager(VelocityManagerInterface mgr) {
OldDecorator2NewStrutsVelocityDecorator.setVelocityManager(mgr);
}
/**
* @deprecated since 6.4.0
*/
@Deprecated
public static void setVelocityManager(VelocityManager mgr) {
setVelocityManager((VelocityManagerInterface) mgr);
}
private FilterConfig filterConfig;
public void init(FilterConfig filterConfig) {
@@ -49,7 +49,7 @@ public class VelocityBeanSelectionProvider extends AbstractBeanSelectionProvider
@Override
public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
alias(VelocityManager.class, VelocityConstants.STRUTS_VELOCITY_MANAGER_CLASSNAME, builder, props);
alias(VelocityManagerInterface.class, VelocityConstants.STRUTS_VELOCITY_MANAGER_CLASSNAME, builder, props);
}
}
@@ -62,7 +62,7 @@ import static org.apache.struts2.views.util.ContextUtil.STRUTS;
/**
* Manages the environment for Velocity result types
*/
public class VelocityManager {
public class VelocityManager implements VelocityManagerInterface {
private static final Logger LOG = LogManager.getLogger(VelocityManager.class);
@@ -105,6 +105,7 @@ public class VelocityManager {
* @return a reference to the VelocityEngine used by <strong>all</strong> Struts Velocity results except directly
* accessed *.vm pages (unless otherwise configured)
*/
@Override
public VelocityEngine getVelocityEngine() {
return velocityEngine;
}
@@ -117,6 +118,7 @@ public class VelocityManager {
* @param res the current HttpServletResponse
* @return a new StrutsVelocityContext
*/
@Override
public Context createContext(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
Context context = buildToolContext();
if (context == null) {
@@ -182,6 +184,7 @@ public class VelocityManager {
*
* @param context the current servlet context
*/
@Override
public synchronized void init(ServletContext context) {
if (velocityEngine == null) {
velocityEngine = newVelocityEngine(context);
@@ -0,0 +1,38 @@
/*
* 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.struts2.views.velocity;
import com.opensymphony.xwork2.util.ValueStack;
import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.context.Context;
/**
* @since 6.4.0
*/
public interface VelocityManagerInterface {
Context createContext(ValueStack stack, HttpServletRequest req, HttpServletResponse res);
VelocityEngine getVelocityEngine();
void init(ServletContext context);
}
@@ -29,6 +29,7 @@ import org.apache.struts2.StrutsConstants;
import org.apache.struts2.result.StrutsResultSupport;
import org.apache.struts2.views.JspSupportServlet;
import org.apache.struts2.views.velocity.VelocityManager;
import org.apache.struts2.views.velocity.VelocityManagerInterface;
import org.apache.velocity.Template;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.context.Context;
@@ -85,9 +86,9 @@ public class VelocityResult extends StrutsResultSupport {
private static final long serialVersionUID = 7268830767762559424L;
private static final Logger LOG = LogManager.getLogger(VelocityResult.class);
private String defaultEncoding;
private VelocityManager velocityManager;
private transient VelocityManagerInterface velocityManager;
private JspFactory jspFactory = JspFactory.getDefaultFactory();
public VelocityResult() {
@@ -97,17 +98,25 @@ public class VelocityResult extends StrutsResultSupport {
public VelocityResult(String location) {
super(location);
}
@Inject(StrutsConstants.STRUTS_I18N_ENCODING)
public void setDefaultEncoding(String val) {
defaultEncoding = val;
}
@Inject
public void setVelocityManager(VelocityManager mgr) {
public void setVelocityManager(VelocityManagerInterface mgr) {
this.velocityManager = mgr;
}
/**
* @deprecated since 6.4.0
*/
@Deprecated
public void setVelocityManager(VelocityManager mgr) {
setVelocityManager((VelocityManagerInterface) mgr);
}
/**
* Creates a Velocity context from the action, loads a Velocity template and executes the
* template. Output is written to the servlet output stream.
@@ -232,7 +241,23 @@ public class VelocityResult extends StrutsResultSupport {
* @param location the name of the template that is being used
* @return the a minted Velocity context.
*/
protected Context createContext(VelocityManager velocityManager, ValueStack stack, HttpServletRequest request, HttpServletResponse response, String location) {
protected Context createContext(VelocityManagerInterface velocityManager,
ValueStack stack,
HttpServletRequest request,
HttpServletResponse response,
String location) {
return velocityManager.createContext(stack, request, response);
}
/**
* @deprecated since 6.4.0
*/
@Deprecated
protected Context createContext(VelocityManager velocityManager,
ValueStack stack,
HttpServletRequest request,
HttpServletResponse response,
String location) {
return createContext((VelocityManagerInterface) velocityManager, stack, request, response, location);
}
}
@@ -26,6 +26,7 @@ import org.apache.struts2.components.template.BaseTemplateEngine;
import org.apache.struts2.components.template.Template;
import org.apache.struts2.components.template.TemplateRenderingContext;
import org.apache.struts2.views.velocity.VelocityManager;
import org.apache.struts2.views.velocity.VelocityManagerInterface;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.context.Context;
@@ -41,14 +42,22 @@ import java.util.Map;
*/
public class VelocityTemplateEngine extends BaseTemplateEngine {
private static final Logger LOG = LogManager.getLogger(VelocityTemplateEngine.class);
private VelocityManager velocityManager;
private VelocityManagerInterface velocityManager;
@Inject
public void setVelocityManager(VelocityManager mgr) {
public void setVelocityManager(VelocityManagerInterface mgr) {
this.velocityManager = mgr;
}
/**
* @deprecated since 6.4.0
*/
@Deprecated
public void setVelocityManager(VelocityManager mgr) {
setVelocityManager((VelocityManagerInterface) mgr);
}
public void renderTemplate(TemplateRenderingContext templateContext) throws Exception {
// get the various items required from the stack
Map actionContext = templateContext.getStack().getContext();
@@ -25,6 +25,10 @@
<struts>
<bean name="struts" type="org.apache.struts2.views.velocity.VelocityManagerInterface"
class="org.apache.struts2.views.velocity.VelocityManager"/>
<!-- Deprecated since 6.4.0 -->
<bean name="struts" class="org.apache.struts2.views.velocity.VelocityManager"/>
<bean type="org.apache.struts2.components.template.TemplateEngine" name="vm"
+11 -11
View File
@@ -116,11 +116,11 @@
<hibernate-validator.version>8.0.1.Final</hibernate-validator.version>
<jackson.version>2.16.0</jackson.version>
<log4j2.version>2.22.1</log4j2.version>
<maven-surefire-plugin.version>3.0.0</maven-surefire-plugin.version>
<maven-surefire-plugin.version>3.2.5</maven-surefire-plugin.version>
<mockito.version>5.8.0</mockito.version>
<ognl.version>3.3.4</ognl.version>
<sitemesh.version>2.5.0</sitemesh.version>
<slf4j.version>2.0.9</slf4j.version>
<slf4j.version>2.0.11</slf4j.version>
<spring.platformVersion>6.0.13</spring.platformVersion>
<tiles.version>3.0.8</tiles.version>
<tiles-request.version>1.0.7</tiles-request.version>
@@ -272,7 +272,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.1</version>
<version>3.4.0</version>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
@@ -282,7 +282,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.6.0</version>
<version>3.6.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -352,7 +352,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.1.0</version>
<version>3.4.1</version>
<executions>
<execution>
<id>enforce</id>
@@ -418,7 +418,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>3.9.0</version>
<version>3.12.1</version>
<configuration>
<relativizeDecorationLinks>false</relativizeDecorationLinks>
</configuration>
@@ -814,7 +814,7 @@
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
@@ -829,17 +829,17 @@
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.13.0</version>
<version>2.15.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.13.0</version>
<version>3.14.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.10.0</version>
<version>1.11.0</version>
</dependency>
<dependency>
<groupId>commons-el</groupId>
@@ -927,7 +927,7 @@
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.24.2</version>
<version>3.25.2</version>
<scope>test</scope>
</dependency>