Getting a list of configured actions in Struts2
Asked Answered
B

2

5

In a project using Struts2 (2.3.20) I would like to run through the configured actions (name, class, namespace, method) at application startup.

I'm using

  • Struts 2.3.20
  • struts-spring-plugin
  • struts-convention-plugin

For reference: I've done some work with beans and Struts injection before so not entirely fresh on this, but I'm stuck solving the problem stated here.

Any pointers on how to obtain this would be appreciated.

Further explanation

Reading Andrea's answer below I see I need to explain what I need.

I'm building a application menu builder feature for the application. My plan is to obtain the action configurations and build a tree of "menu nodes" from information in annotations on selected action classes and methods.

My problem with the code from the config-browser is that the Configuration (xwork) doesn't seem to be available outside of Struts components. Since this is an application startup task it doesn't really fit Struts' MVC component model. I'd like to put the menu building initialization in a ServletContextListener.

Fake example

Per request here is just the connection actionconfig <-> annotation <-> my_custom_menu. From this I could produce a menu structure provided from the annotations on action classes and methods.

public class ActionMenuBuilderListener implements ServletContextListener {
  @Override
  public void contextInitialized(ServletContextEvent arg0) {
    List<ActionCfg> actions = Struts.getConfiguredActions(); // thisi is where I'd like some help
    for(ActionCfg action : actions) {
      MenuAnnotation annotation = getAnnotationFromMethodOrClass(action);
      if(annotation != null) {
        addMenuItem(action, annotation);
      }
    }
  }
}

Here ActionCfgis whatever class Struts would return for action configuration, Struts.getConfiguredActions() would be one or more calls to Struts components and addMenu(...) is where I add a menu item node to my structure. The structure is later the target from JSP-s to build menus.

I don't know how much more code to write.

My solution

For completeness I thought I'll include what came out of this.

First, I to plugged in into Struts through this ServletContextListener:

public class ActionMenuBuilderListener implements
    ServletContextListener {

    @Override
    public void contextDestroyed(ServletContextEvent arg0) {
    }

    @Override
    public void contextInitialized(ServletContextEvent event) {

        ActionMenuDispatcherListener listener =
        new ActionMenuDispatcherListener(); 
        ServletContext context = event.getServletContext();
        listener.setServletContext(context);

        Dispatcher.addDispatcherListener(listener);

    }
}

Then, I wrote the DispatcherListener:

public class ActionMenuDispatcherListener implements DispatcherListener {

private ServletContext servletContext;

...

@Override
public void dispatcherInitialized(Dispatcher dispatcher) {

    Map<String, PackageConfig> packages = dispatcher
        .getConfigurationManager().getConfiguration()
        .getPackageConfigs();

    Map<String, Map<String, ActionConfig>> runtimeActionConfigs = dispatcher
        .getConfigurationManager().getConfiguration()
        .getRuntimeConfiguration().getActionConfigs();

    for (String packageKey : runtimeActionConfigs.keySet()) {

    Map<String, ActionConfig> actionConfigs = runtimeActionConfigs
        .get(packageKey);

    for (String actionKey : actionConfigs.keySet()) {
        ActionConfig actionConfig = actionConfigs.get(actionKey);
        PackageConfig packageConfig = packages.get(actionConfig
            .getPackageName());

        if (packageConfig != null) {
        String actionName = actionConfig.getName();
        String namespace = packageConfig.getNamespace();
        try {

            ActionMenu methodAnnotation = getMethodAnnotation(actionConfig);

            if (methodAnnotation != null) {
            String annotationInfo = methodAnnotation.value();
            log.debug("[{}, {}, {}]", namespace, actionName,
                annotationInfo);
            }

        } catch (ClassNotFoundException e) {
            log.error("{}: {}", e.getClass().getSimpleName(),
                e.getMessage());
        }
        }
    }
    }
}

protected ActionMenu getMethodAnnotation(ActionConfig actionConfig)
    throws ClassNotFoundException {
    String className = actionConfig.getClassName();
    String methodName = actionConfig.getMethodName();
    Class<?> actionClass = Class.forName(className);
    try {
    Method method = actionClass.getDeclaredMethod(methodName, null);
    ActionMenu annotation = method.getAnnotation(ActionMenu.class);
    return annotation;
    } catch (NoSuchMethodException | SecurityException e) {
    // log.error("{}: {}", e.getClass().getSimpleName(),
    // e.getMessage());
    }
    return null;
}

}

Just in case someone else is thinking along those line :)

Bateau answered 9/7, 2015 at 11:2 Comment(7)
Can you show a real world (fake) example of what and how it should do ?Conlen
For the menu you only need to store action paths, and build URLs for links with URL Helper. Annotations aren't so flexible against object model that you can use for the subj, but it is a key point to extract the path for the menu component.Sylviesylvite
@Roman all the annotations buisness and info needed is pretty clear, what I can't figure out is how to get hold of the info from Struts. I'm looking into overriding the StrutsPrepareAndExecuteFilter.Bateau
It's a bad idea, you don't need override Struts2, but you can extend it with a plugin, and if you look inside it you can get more information.Sylviesylvite
@Roman, I'm looking into both alternativedBateau
Here you are, start with your approach and return to my when you rollback. Ah, forgot this won't solve your task but can help.Sylviesylvite
I guess this is the line that is the beginning of my sollution: PackageConfig packageConfig = Dispatcher.getInstance().getConfigurationManager().getConfiguration().getPackageConfig("default");. I'll start there and I'll get back to you.Bateau
D
6

First of all you need to hook into application initialization process after the configurations are loaded and parsed. One of the ways is to implement DispatcherListener which you need to add to the Dispatcher. This you can do in ServletContextListener#contextInitialized method.

The second piece of the puzzle is to get action configurations. This is pretty simple because the instance of the Dispatcher is passed as argument into dispatcherInitialized method. To get all current action configurations get RuntimeConfiguration which holds data in Map<String, Map<String, ActionConfig>>, where the first map key is package namespace, the second map key is action name and ActionConfig holds all info about action. Since you need a class name then use getClassName() method of it.

public class ActionMenuBuilderListener implements ServletContextListener,DispatcherListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        Dispatcher.addDispatcherListener(this);
    }

    @Override
    public void dispatcherInitialized(Dispatcher du) {
        Map<String, Map<String, ActionConfig>> runtimeActionConfigs = du
            .getConfigurationManager().getConfiguration().getRuntimeConfiguration()
            .getActionConfigs();
    }
    // other methods
}

And of course don't forget to register your listener in web.xml.

Dedrick answered 9/7, 2015 at 18:38 Comment(1)
Sweet. This was spot on. I will accept this answer. Thanks Andrea and Roman as well.Bateau
C
4

You are free of building this thing for your personal growth, but beware that it already exist.

It is called Config Browser Plugin (struts2-config-browser-plugin-2.3.20.jar).

It is included by default with the Maven archetypes, and you must remember of removing it before going in production.

Once imported it is available at the URL:

//www.SERVER_NAME.com:8080/WEBAPP_NAME/config-browser/actionNames

It gives you the exact informations you are looking for: actions, methods, results, parameters, mappings etc. and it looks like this:

enter image description here

Conlen answered 9/7, 2015 at 12:5 Comment(1)
Thanks. I know about the config-plugin and I have studied the code for it. I would, however use the kist of actions to look for a custom annotation and build the application menus from the info. I can see I was not clear that I needed a List of action configurations and I will edit my question.Bateau

© 2022 - 2024 — McMap. All rights reserved.