When upgrading my application to use Spring Boot version 2.1.8.RELEASE + struts2-convention-plugin with Struts2-core version 2.5.20 the actions are not being mapped correctly and I am getting the error
com.opensymphony.xwork2.config.ConfigurationException: There is no
Action mapped for namespace [/] and action name [home] associated with
context path [].
If I decalre the actions in struts.xml they work perfectly.
Below is my current configuration, why are they not mapping?
I have tried many diffrent configs and nothing seems to work, the StrutsPrepareAndExecuteFilter is firing but not actions are found, as if Spring has not scanned them. Could this be a dependency version issue?
application.yaml
server:
port: 8080
servlet:
context-path: /
Struts2.xml
<struts>
<constant name="struts.devMode" value="false" />
<constant name="struts.convention.action.packages" value="com.myactions.action" />
<constant name="struts.convention.action.includeJars" value=".*?/myjar.*?jar(!/)?,.*?/myjar*?jar(!/)?" />
<constant name="struts.objectFactory" value="spring" />
<constant name="struts.objectFactory.spring.autoWire" value="name" />
<constant name="struts.multipart.maxSize" value="100000000" />
<constant name="struts.convention.default.parent.package" value="struts-default"/>
## THIS WORKS
<!-- <package name="home" extends="struts-default">-->
<!-- <action name="actionHome" class="com.myactions.action.HomeController" method="actionHome">-->
<!-- <result name="success">home.jsp</result>-->
<!-- </action>-->
<!-- </package>-->
</struts>
Controller
#Namespace("/")
public class HomeController extends BaseController {
#Action("home")
public String actionHome() throws Exception {
return SUCCESS;
}
}
Main
#SpringBootApplication
#ServletComponentScan
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder springApplicationBuilder) {
return springApplicationBuilder.sources(Application.class);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Struts2 Filter
#WebFilter("/*")
public class Struts2Filter extends StrutsPrepareAndExecuteFilter {
}
UPDATE
After a couple days of trials and errors, here are a few clues.
Spring Boot 2.1.8 with Struts 2.5.20 configured with struts.xml works.
Your projet POM requires at least the following dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.8</version>
</dependency>
...
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>${struts2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-spring-plugin</artifactId>
<version>2.5.20</version>
</dependency>
I would also recommend adding the Config Browser Plugin to easily list the available actions:
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-config-browser-plugin</artifactId>
<version>2.5.20</version>
</dependency>
It works when you package :
a fat JAR with an embedded Tomcat server, which is the default behavior
a WAR in an external Tomcat Server
To get rid of struts.xml and configure your actions only through Java classes, the Convention plugin is required:
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-convention-plugin</artifactId>
<version>2.5.20</version>
</dependency>
Notes :
the constant defined in struts.xml can also be migrated to the struts.properties file (default values listed here)
struts.xml takes precedence over the Convention plugin and struts.properties
When struts.xml is removed, the Convention plugin takes over and Action classes are mapped using the name of packages and classes. The default conventions (action-to-URL mappings, result path...) can also be overridden thanks to a set of annotations.
Still, as mentioned by the OP, configuration through annotations does NOT work out of the box with an embedded Tomcat (it does with an external Tomcat, though).
Best I could do so far, is make it work using an embedded Jetty instead of Tomcat, and adding the recommended configuration to struts.properties:
struts.convention.exclude.parentClassLoader=false
struts.convention.action.fileProtocols=jar,code-source
The starting log still shows errors, but I'm able to access the configured actions with no more XML.
UPDATE
After the OP's update, I digged in a bit more. It turns out the Convention Plugin works fine with Tomcat embedded and no XML needed, by doing the following:
Adding the following line to the struts.properties
struts.convention.exclude.parentClassLoader=false
Upgrading asm modules asm, asm-commons and asm-tree to release 6.2 or later to prevent errors similar to
ERROR org.apache.struts2.convention.DefaultClassFinder.<init>:95 - Unable to read class [...]
Having stepped through spring2-convention-plugin the class PackageBasedActionConfigBuilder has a method buildUrlSet which gets all the urls to classes that need to be scanned for Actions this was removing the urls to my classes and hence nothing was being scanned.
There is a condition excludeParentClassLoader which needs to be set to false (line 415).
Solution
Set below constant's to -
<constant name="struts.convention.exclude.parentClassLoader" value="false" />
<constant name="struts.convention.action.fileProtocols" value="jar,code-source" />
Related
title says it all. How do I setup a log4j2 configuration in my Open-liberty project? I've added my log4j2.xml file in the resources folder and I use the following dependency in my pom.xml:
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
This is my server.xml:
<server description="Intake Server">
<featureManager>
<feature>servlet-4.0</feature>
<feature>mpConfig-1.4</feature>
<feature>cdi-2.0</feature>
</featureManager>
<variable name="default.http.port" defaultValue="9080"/>
<variable name="default.https.port" defaultValue="9443"/>
<variable name="app.context.root" defaultValue="message"/>
<httpEndpoint httpPort="${default.http.port}"
httpsPort="${default.https.port}" id="defaultHttpEndpoint" host="*" />
<library id="log4jConfig">
<folder dir="/var/log/intake" scanInterval="5s" />
</library>
<webApplication id="intake" location="intake.war" contextRoot="${app.context.root}">
<classloader commonLibraryRef="log4jConfig"/>
</webApplication>
</server>
You'll also need to add log4j to the classpath, and there's several ways you can do that (with pros/cons to each, but I would suggest #2, unless you have more then one app that's going to be making use of log4j, in which case 3 may be the better approach):
Package it with your app (inside the war)
Add it in the server.xml via the classloader attribute under your webApplication
Drop it in the global shared library folder: ${shared.config.dir}/lib/global or ${server.config.dir}/lib/global
Set it via JVM argument: -Dlog4j.configurationFile=file:/path/to/log4j2.xml
I'll mention that there is also an archived repo regarding using log4j with Open Liberty that you may find helpful, but keep in mind that it is archived so it's likely incomplete and with no dev support.
Have a working Struts2 portlet environment where I cannot make Struts fileUpload work, in portlet mode.
I can see that
A file is uploaded to the server, created in temp space.
A multipart request is present on the original request, with the filename
and file contents, where it enters Struts classes.
I can trigger the maximum size response, the default value set in Struts.
The FileUploadInterceptor is triggered but in the Jakarta
MultipartRequest there is no file. Somewhere between the original
request and the Jakarta wrapper the file is lost.
I have tried the same approach in a stripped down Spring boot 2 application, the same problem occurs as in the main project.
Followed the code at
https://struts.apache.org/core-developers/file-upload.html
for the JSP and action classes. The portlet is created, the flow works, but no file in the action.
Standard Sping Boot 2 with extra in pom:
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-spring-plugin</artifactId>
<version>2.5.20</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
Struts.xml
<package name="uploadtest" extends="struts-portlet-default" namespace="/uploadtest">
<action name="uploadtestPrepare" class="bouvet.no.fileuploadtest.action.FileUploadSubmitAction">
<result name="success">/WEB-INF/struts2/test/fileUpload.jsp</result>
</action>
<action name="uploadTestSubmit" class="bouvet.no.fileuploadtest.action.FileUploadSubmitAction">
<result name="success">/WEB-INF/struts2/test/fileUpload.jsp</result>
</action>
</package>
and the form
<s:form action="uploadTestSubmit" method="post" enctype="multipart/form-data">
FileName: <s:property value="%{filename}"/>
<s:file name="upload" label="File" />
<s:submit/>
</s:form>
a setter
public void setUploadFileName(String filename) {
this.filename = filename;
}
Is this a bug in portlet mode or am I missing a key dependency, a component, version? Alternative method?
In the image, a breakpoint in the JSR168 dispatcher, the file is present. First code to run after the server.
breakpoint in JSR168 dispatcher
The solution in this case was to step back a step and look at what was present in the request.
The CMS the portlet is running in is in fact taking care of the upload before the upload reach the portlet environment. The jakarta wrapper I found was created by the CMS but it is outside of the portlet scope.
Solution:
public String intercept(ActionInvocation invocation) throws Exception {
final ActionContext context = invocation.getInvocationContext();
Object action = invocation.getAction();
try {
if (action instanceof FileUploadAware) {
HttpServletRequest request = (HttpServletRequest) context.get(HTTP_REQUEST);
// if (request instanceof MultipartRequestWrapper) {
File file = (File) request.getAttribute("upload");
((FileUploadAware) action).setFile(file);
// }
}
}catch (Exception e){
LOG.error("Exception? {}", e);
}finally {
return invocation.invoke();
}
Not a final solution but the concept, an interceptor that finds an upload attribute on the request and puts it on a FileUploadAware action. The attribute is a local temp file the CMS has intercepted and created.
This works for me, a special solution for this CMS or a general for portlets? Do not know.
I upgraded Struts2 version,include
free-marker-2.3.22.jar
ognl-3.0.19.jar
struts2-core-2.3.32.jar
struts2-json-plugin-2.3.32.jar
struts2-junit-plugin-2.3.32.jar
struts2-spring-plugin-2.3.32.jar
xwork-core-2.3.32.jar
and added the code to struts.xml
<constant name="struts.enable.DynamicMethodInvocation" value="true"/>
<constant name="struts.convention.action.mapallmatches" value="true"/>
I did not use freemarker but I also added the jar to the "WEB-INF/lib"
the pom.xml was upgraded too.
There was no Exception when the tomcat started.
but my project always showed "404",the console showed no messages.
what should I do ?
Keys are case sensitive in properties file. The same keys are used in struts.xml. See docs Class PropertyResourceBundle.
<constant name="struts.convention.action.mapAllMatches" value="true"/>
I used com.vaadin.tapio.googlemaps.GoogleMap component to connect with Google map from vaadin.
I tried the below code.(Vaadin 7.0.2)
public class StoresMainView extends VerticalLayout implements View {
#Override
public void enter(ViewChangeEvent event) {
setSizeFull();
GoogleMap googleMap = new GoogleMap(new LatLon(-27.47101, 153.02429), 10.0, "");
googleMap.setSizeFull();
googleMap.setImmediate(true);
googleMap.setMinZoom(4.0);
addComponent(googleMap);
}
But it gives the below error when running.I added the dependency in my pom.
Widgetset does not contain implementation for com.vaadin.tapio.googlemaps.GoogleMap. Check its component connector's #Connect mapping, widgetsets GWT module description file and re-compile your widgetset. In case you have downloaded a vaadin add-on package, you might want to refer to add-on instructions.
In my web.xml I have define the Widget set as below
<init-param>
<param-name>widgetset</param-name>
<param-value>com.client.DashboardWidgetSet</param-value>
</init-param>
And my DashboardWidgetSet as below
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 1.7.0//EN" "http://google-web-toolkit.googlecode.com/svn/tags/1.7.0/distro-source/core/src/gwt-module.dtd">
<module>
<inherits name="com.vaadin.DefaultWidgetSet" />
<inherits name="org.vaadin.cssinject.Cssinject_addonWidgetset" />
<!-- -->
<set-configuration-property name="devModeRedirectEnabled"
value="true" />
<!-- Uncomment the following to compile the widgetset for one browser only.
This can reduce the GWT compilation time significantly when debugging. The
line should be commented out before deployment to production environments.
Multiple browsers can be specified for GWT 1.7 as a comma separated list.
The supported user agents at the moment of writing were: ie6,ie8,gecko,gecko1_8,safari,opera
The value gecko1_8 is used for Firefox 3 and later and safari is used for
webkit based browsers including Google Chrome. -->
<!-- <set-property name="user.agent" value="safari"/> -->
<!-- WidgetSetOptimizer -->
<inherits name="org.vaadin.easyuploads.Widgetset" />
<inherits name="com.vaadin.tapio.googlemaps.WidgetSet" />
</module>
Any help is really appreciated.
You need to make sure that the widgetset init-param in your web.xml points to the right widgetset. The default one does not contain any information about the Google Map component's widgets.
I recently deployed inadvertently a debug version of our game typrX
(typing races at www.typrx.com - try it it's fun).
It was quickly corrected but I know it may happen again. After digging
on Google I found some info how to create 2 different profiles, one
for development mode that has the debug functions and one used for
deployment. Here is what I found from a Google IO presentation. Does
anyone have this setup? Can someone explains how to run this?
MyAppCommon.gwt.xml
<module>
...
<define-property values="debug, release" name="app.config" />
<replace-with class="myapp.debug.DebugConsole">
<when-type-is class="myapp.Console" />
<when-property-is name="app.config" value="debug" />
</replace-with>
...
</module>
MyAppDebug.gwt.xml
<module>
...
<set-property name="app.config" value="debug" />
</module>
The idea of using a specific module for debugging has been floating around for some times, and was also mentioned in this Google I/O presentation (see slide 33 from PDF or at 0h31m in the video).
The basic idea is that you have a standard GWT module, and a second debug module that inherits this standard module, configures some properties, and uses GWT's deferred binding to replace some classes with specific instances when debugging.
Then, you only have to configure your Maven / ant build to compile the appropriate module depending on wether you are in development mode or in release mode.
In my project, I did not create an "app.config" deferred binding property, but I might do that later on. What I did was the following:
Created a standard module
com/example/MainModule.gwt.xml:
<module rename-to="mainModule">
<inherits name="com.smartgwt.SmartGwt" />
<!-- (other configurations) -->
<!-- gwt-log configuration -->
<define-property name="log_level" values="OFF,DEBUG" />
<inherits name="com.allen_sauer.gwt.log.gwt-log-common" />
<!-- Locales we want to compile for -->
<extend-property name="locale" values="en" />
<extend-property name="locale" values="fr_FR" />
</module>
Created a "debug" module, that inherits the standard module and configures some additional properties for development
com/example/MainModuleDebug.gwt.xml:
<module rename-to="mainModule">
<inherits name="com.example.MainModule" />
<set-property name="user.agent" value="gecko1_8" />
<set-property name="locale" value="fr_FR"/>
<set-property name="log_level" value="DEBUG" />
</module>
Note: the rename-to attribute is very important here, since you want both modules to be deployed under the exact same name. When you compile during development, you do not want to have to change all your html host pages to point to the debug module.
Configured Maven and the gwt-maven-plugin to compile the right module
<project>
(...)
<properties>
(...)
<!--
Suffix appended to the names of the GWT modules we compile in our child projects.
Empty by default, this suffix is overriden by some profiles to specify an alternative module to compile.
-->
<gwt.module.suffix></gwt.module.suffix>
<!-- We force GWT-recompilation by default (except when using the "gwtDebug" profile - see below for more info) -->
<gwt.compiler.force>true</gwt.compiler.force>
</properties>
(...)
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>gwt-maven-plugin</artifactId>
<configuration>
(...)
<module>com.example.MainModule${gwt.module.suffix}</module>
</configuration>
</plugin>
(...)
<profiles>
<!-- This profile should be used during *DEVELOPMENT* -->
<profile>
<id>gwtDebug</id>
<properties>
<gwt.module.suffix>Debug</gwt.module.suffix>
<!-- Tells gwt-maven-plugin to recompile GWT modules only when necessary -->
<gwt.compiler.force>false</gwt.compiler.force>
</properties>
<activation>
<property>
<name>gwtDebug</name>
</property>
</activation>
</profile>
</profiles>
</project>
Simply doing "maven clean install" will compile the production module. In development, you use "mvn clean install -DgwtDebug" to activate the gwtDebug profile, which in turn compiles the debug module.
Of course, you could configure your ~/.m2/settings.xml to always define the "gwtDebug" property...
The same idea would also apply to Ant. But I'm not well versed with it.
When you starts to toy with the idea of overriding your real module with a debug module, you start to envision some very cool possibilities:
You could add performance logs, which would be pruned from the code when in production.
You could configure all your toString() methods to return something useful when in debug mode, and the empty string when in production (and thus reduce the .js size).
You may reduce the number of permutations by specifying only one locale / one browser / one log level, to speed up the compilation (but do not forget to test for other locales / browsers from time to time).