Dependency Injection in Struts2 Accessing Session Scoped Beans

Dependency Injection in Struts2 Accessing Session Scoped Beans

Let's start from looking what is a Scope.Strategy by looking at the docs. It says

Pluggable scoping strategy. Enables users to provide custom
implementations of request, session, and wizard scopes. Implement and
pass to
Container.setScopeStrategy(com.opensymphony.xwork2.inject.Scope.Strategy)

Ok, assume I want to implement session scope. Then I need to know the place where I could implement it. The framework has it's extension points where where you could plug your extensions or simply extend the default implementation and provide your own custom implementations. This is easy done via looking at the BeanSelectionProvider. Then analyzing the stacktraces I decided the best point would be to extend the DefaultActionProxyFactory. Extending it requires to extend the DefaultActionProxy also.

public class MyActionProxyFactory extends DefaultActionProxyFactory {

public MyActionProxyFactory() {
super();
}

@Override
public ActionProxy createActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {

MyActionProxy proxy = new MyActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
container.inject(proxy);
container.setScopeStrategy(new MyScopeStrategy());
proxy.prepare();
return proxy;
}
}

public class MyActionProxy extends DefaultActionProxy {

protected MyActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {
super(inv, namespace, actionName, methodName, executeResult, cleanupContext);
}

@Override
protected void prepare() {
super.prepare();
}
}

public class MyScopeStrategy implements Scope.Strategy {

@Override
public <T> T findInRequest(Class<T> type, String name, Callable<? extends T> factory) throws Exception {
return null;
}

@Override
public <T> T findInSession(Class<T> type, String name, Callable<? extends T> factory) throws Exception {

ActionContext context = ActionContext.getContext();
SessionMap<String, T> sessionMap = (SessionMap<String, T>) context.getSession();

if (sessionMap == null) {
sessionMap = new SessionMap<String, T>(ServletActionContext.getRequest());
context.setSession((Map<String, Object>) sessionMap);
}

T obj = sessionMap.get(name);

if (obj == null) {
obj = factory.call();
sessionMap.put(name, obj);
}
return obj;
}

@Override
public <T> T findInWizard(Class<T> type, String name, Callable<? extends T> factory) throws Exception {
return null;
}
}

In the configuration file struts.xml you should set the property

<constant name="struts.actionProxyFactory" value="jspbean.struts.factory.MyActionProxyFactory"/>

That's all you need to inject a bean Session with the session scope. Similar implementations could be done for other scopes. Notice, that other scopes like singlton (used by default), thread, and default seems don't require such pluggable extension. And the last word is about @Scoped annotation. It's not used if you provide the beans via xml configuration. But if you supply the ContainerBuilder with the bean in any other way it's able to find annotation on it and set the corresponding scope.

Struts 2 bean configuration default scope

The default scope is singleton, but there's a scope with name default which is different. May be this question let you better understand scopes in Struts2. The framework support scopes like singleton, default, and thread other scopes you should implement manually.

Look at the source code where the scopes are defined.

To clarify the scope named default means a one instance per injection. You can find this in comments to Scope.DEFAULT.

Is there a way to bind OGNL with Struts2 UI tags

You could try having the name be the string session.order.amount and make your action implement SessionAware, and expose the session. Currently you're setting the name to the value of session.order.amount.

I don't know if it would work, and you might need to use array/collection notation, but off the top of my head I don't know why it wouldn't work.

That said, I feel like direct view-layer writes into web app internals is a Bad Idea.

Scopes in Struts 2 and standards for them

Struts 2 is running a container, it has scopes for beans. More about bean scopes is in this question. All other scopes are servlet scopes. Struts uses indirect access to these scopes using it's own structures. For example a set tag uses these scopes:

The scopes available are as follows :

application - the value will be set in application scope according to servlet spec. using the name as its key
session - the value will be set in session scope according to servlet spec. using the name as key
request - the value will be set in request scope according to servlet spec. using the name as key
page - the value will be set in page scope according to servlet sepc. using the name as key
action - the value will be set in the request scope and Struts' action context using the name as key

NOTE:

If no scope is specified, it will default to action scope.

@Inject not working in Jersey REST webservices in Struts 2

From the official CDI Plugin documentation:

Use the right @Inject

Struts 2 and it's core component XWork use it's own internal
dependency injection container. Interestingly, you could name it
JSR-330's grandma, since it is an early pre-release version of Google
Guice once developed by Crazybob Lee - the same Bob Lee that, together
with SpringSource's Rod Johnson, lead the JSR-330 specification.

That said, you will find the @Inject annotation both as
com.opensymphony.xwork2.inject.Inject and javax.inject.Inject. Don't
mix up those two - javax.inject.Inject is the one you want to use with
your Struts 2 CDI plugin and CDI integration in general
! While you
could use Struts' internal annotation as well, the effect may be
strange to undefined - so check your imports!

Then instead of com.opensymphony.xwork2.inject.Inject
use the correct one: javax.inject.Inject

Scope attribute migration from Struts1 to Struts2

From the Struts 1 DTD in the Action Element definition:

    scope        The context ("request" or "session") that is used to
access our ActionForm bean, if any. Optional if "name" is
specified, else not valid. [session]

In Struts 2, there are no Action Form beans, and Actions themselves default to request context. @meskobalazs is correct above, this can be overridden with the scope interceptor if you need session-scoped actions in Struts 2.



Related Topics



Leave a reply



Submit