javastruts2controllerstruts-actionstruts2-convention-plugin

Struts 2 - wrong controller mapping after upgrade


I have a problem with mapping of some Controllers after upgrade from Struts version 2.1.8.1 to 2.3.16.3. All was working before the upgrade, but now 3 out of 60+ controllers boomed with next error:

ERROR [ActionComponent] Could not execute action: //vendor-tree!index There is no Action mapped for namespace / and action name vendor-tree!index. - [unknown location]

This action must be in /vendor-tree!index not in //vendor-tree!index, but I don't get why Struts is looking only for this 3 Controllers in a wrong place. I was call it all this controllers on every request with

 <#escape x as x?html>
    <@s.action name="vendor-tree!index" executeResult="true" /> 
 </#escape>

<constant name="struts.enable.DynamicMethodInvocation" value="true" /> it doesn't change anything

Struts2: method attribute in <s:submit> button doesn't work is a different case, which not resolve this one

struts.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
        "http://struts.apache.org/dtds/struts-2.3.dtd">

<struts>

    <constant name="struts.ui.theme" value="xhtml"/>
    <constant name="struts.locale" value="bg_BG"/>
    <constant name="struts.ui.templateDir" value="template" />
    <constant name="struts.custom.i18n.resources" value="global, ajax, help, xhtml" />
    <constant name="struts.devMode" value="false" />
    <constant name="struts.enable.DynamicMethodInvocation" value="true" />
    <constant name="struts.action.extension" value="html,xhtml,,xml,json" />

    <bean type="org.apache.struts2.dispatcher.mapper.ActionMapper" name="mymapper" class="stemo.ActionMapper.HierarchicalRestActionMapper"/>
    <constant name="struts.mapper.class" value="mymapper"/>

    <!--
     enable the NamedVariablePatternMatcher to enable {variables} in the action namespace
    -->
    <bean type="com.opensymphony.xwork2.util.PatternMatcher" name="namedVariablePatternMatcher"
          class="com.opensymphony.xwork2.util.NamedVariablePatternMatcher"/>
    <constant name="struts.patternMatcher" value="namedVariablePatternMatcher"/>
    <!--
    <constant name="struts.convention.action.checkImplementsAction" value="false"/>
    <constant name="struts.convention.action.checkAnnotation" value="false"/>
    <constant name="struts.convention.action.defaultMethodName" value="index"/>
    -->

    <constant name="struts.convention.action.suffix" value="Controller"/>
    <constant name="struts.convention.action.mapAllMatches" value="true"/>
    <constant name="struts.convention.default.parent.package" value="controllers"/>
    <constant name="struts.convention.result.path" value="/"/>

    <!-- constant name="struts.convention.package.locators.basePackage" value="stemo"/ -->
    <constant name="struts.convention.package.locators" value="controllers"/>

    <!--  Overwrite Convention -->


    <package name="controllers" extends="struts-default" namespace="/" >
        <interceptors>
            <interceptor name="storeMessages" class="org.apache.struts2.interceptor.MessageStoreInterceptor" />
            <!--interceptor name="messMessage" class="stemo.ActionMapper.RedirectMessageInterceptor"/ -->
            <interceptor name="cachingHeadersInterceptor" class="stemo.ActionMapper.CachingHeadersInterceptor"/>
            <interceptor name="myCookie" class="stemo.ActionMapper.CookiesInterceptor"/>
            <interceptor name="myLogger" class="stemo.ActionMapper.LoggerInterceptor"/>
            <interceptor-stack name="myStack">
                 <!-- interceptor-ref name="messMessage"/ -->
                <interceptor-ref name="storeMessages" >
                    <param name="operationMode">AUTOMATIC</param>
                </interceptor-ref>
                 <interceptor-ref name="myCookie"/>
                <interceptor-ref name="myLogger"/>
                <interceptor-ref name="defaultStack" />
            </interceptor-stack>
        </interceptors>
        <default-interceptor-ref name="myStack"/>
    </package>

    <!-- for JBoss -->
    <constant name="struts.convention.exclude.parentClassLoader" value="true" />
    <constant name="struts.convention.action.fileProtocols" value="jar,vfsfile,vfszip" />
    <constant name="struts.convention.classLoader.excludeParent" value="false" />


    <!-- for Struts version 2.3.18+ -->
    <constant name="struts.multipart.parser" value="jakarta-stream" />
    <constant name="struts.multipart.maxSize" value="50242880" />

    <!--
    <constant name="struts.freemarker.manager.classname" value="org.apache.struts2.views.freemarker.FreemarkerManager" />

     -->
    <constant name="struts.freemarker.manager.classname" value="customFreemarkerManager"/>
    <constant name="struts.freemarker.mru.max.strong.size" value="250" />
    <constant name="struts.freemarker.templatesCache.updateDelay" value="1800" />


    <package name="json" extends="json-default">
        <action name="brand-ajax" class="stemo.json.BrandAjaxController">
            <result type="json">
                <param name="root">model</param>
                <param name="excludeProperties">LHarakt, Harakt</param>
            </result>
        </action>

        <action name="vendor-ajax" class="stemo.json.VendorAjaxController">
            <result type="json">
                <param name="root">model</param>
                <param name="excludeProperties">LHarakt, Harakt</param>
            </result>
        </action>
    </package>
</struts>                     

problem controller

package stemo.controllers;

import com.opensymphony.xwork2.ModelDriven;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
import org.apache.struts2.interceptor.SessionAware;
import org.apache.struts2.rest.DefaultHttpHeaders;
import org.apache.struts2.rest.HttpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import stemo.hibernate.ObjectUtils;
import stemo.hibernate.model.DAO.VendorDAO;
import stemo.hibernate.model.entity.Vendor;
import stemo.hibernate.model.filters.*;

import java.util.List;
import java.util.Map;


@Results({
        @Result(name = "success", type = "freemarker",  location = "/vendortree.ftl")
})
public class VendorTreeController implements ModelDriven<Object>, SessionAware
{

    private VendorDAO vendorDAO;
    private List<Vendor> vendors;

    private String sklad_check;
    private String promo_check;
    private String newprice_check;
    private String lowprice_check;
    private String newproduct_check;
    private String oldproduct_check;
    private String withgift_check;
    private String sch_check;

    private FilterManager filters = null;

    private Map<String, Object> sessionMap;


    @Autowired
    public void setVendorDAO(VendorDAO vendorDAO)
    {
        this.vendorDAO = vendorDAO;
    }


    private FilterManager initFilters()
    {
        if (filters != null)
        {
            return filters;
        }
        filters = new FilterManager();
        if (!ObjectUtils.isEmpty(sklad_check))
        {
            StoreFilter f = new StoreFilter();
            filters.addFilter(f);
        }

        if (!ObjectUtils.isEmpty(promo_check))
        {
            PromoFilter f = new PromoFilter();
            filters.addFilter(f);
        }

        if (!ObjectUtils.isEmpty(newproduct_check))
        {
            NewProductFilter f = new NewProductFilter();
            filters.addFilter(f);
        }

        if (!ObjectUtils.isEmpty(withgift_check))
        {
            ProductsWithGift f = new ProductsWithGift();
            filters.addFilter(f);
        }

        if (!ObjectUtils.isEmpty(newprice_check))
        {
            ProductsWithNewPrices f = new ProductsWithNewPrices();
            filters.addFilter(f);
        }

        if (!ObjectUtils.isEmpty(lowprice_check))
        {
            ProductsWithLowerPrices f = new ProductsWithLowerPrices();
            filters.addFilter(f);
        }

        if (!ObjectUtils.isEmpty(oldproduct_check))
        {
            OldProductsFilter f = new OldProductsFilter();
            filters.addFilter(f);
        }

        if (!ObjectUtils.isEmpty(sch_check))
        {
            SecondHandFilter f = new SecondHandFilter();
            filters.addFilter(f);
        }

        return filters;

    }


    public HttpHeaders index()
    {
        sklad_check = (String) sessionMap.get("sklad_check");
        promo_check= (String) sessionMap.get("promo_check");
        newprice_check = (String) sessionMap.get("newprice_check");
        lowprice_check = (String) sessionMap.get("lowprice_check");
        newproduct_check = (String) sessionMap.get("newproduct_check");
        oldproduct_check = (String) sessionMap.get("oldproduct_check");
        withgift_check = (String) sessionMap.get("withgift_check");
        sch_check = (String) sessionMap.get("sch_check");

        FilterManager filters = initFilters();
        vendors = vendorDAO.findAll(filters);
        return new DefaultHttpHeaders("success").disableCaching();
    }


    @Override
    public Object getModel()
    {
        return vendors;
    }

    @Override
    public void setSession(Map<String, Object> stringObjectMap)
    {
        sessionMap = stringObjectMap;
    }
}

worked controller

package stemo.controllers;

import com.opensymphony.xwork2.ModelDriven;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
import org.apache.struts2.interceptor.SessionAware;
import org.apache.struts2.rest.DefaultHttpHeaders;
import org.apache.struts2.rest.HttpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import stemo.hibernate.model.DAO.PenaltyDAO;
import stemo.hibernate.model.entity.PenaltyInfo;
import stemo.hibernate.model.entity.User;
import stemo.hibernate.model.entity.date.MonthItem;

import java.util.List;
import java.util.Map;


@Results({
        @Result(name = "success", type = "freemarker", location = "/penalty_details.ftl"),
        @Result(name = "list", type = "freemarker", location = "/penalty_all_details.ftl"),
        @Result(name = "error", type = "redirect", location = "/products")
})
public class PenaltyController implements ModelDriven<Object>, SessionAware
{

    private Map<String, Object> sessionMap;
    private List<PenaltyInfo> penaltyInfo;
    private String date;
    private Double penalty;
    private PenaltyDAO penaltyDAO;


    @Autowired
    public void setPenaltyDAO(PenaltyDAO penaltyDAO)
    {
        this.penaltyDAO = penaltyDAO;
    }


    @Override
    public void setSession(Map<String, Object> stringObjectMap)
    {
        this.sessionMap = stringObjectMap;
    }

    @Override
    public Object getModel()
    {
        return penaltyInfo;
    }

    public String getDate()
    {
        return date;
    }

    public void setDate(String date)
    {
        this.date = date;
    }

    public Double getPenalty()
    {
        return penalty;
    }

//GET

    public HttpHeaders index()
    {
        User user = (User) sessionMap.get("user");
        if ((user != null))
        {

            //months = MonthItem.getMonths(null);
            if (date == null)
            {
                date = MonthItem.getCurrent();
            }
            penaltyInfo = penaltyDAO.getList(user,MonthItem.toDate(date));
            penalty = penaltyDAO.getPenalty(user,MonthItem.toDate(date));
            return new DefaultHttpHeaders("success").disableCaching();
        }
        else
        return new DefaultHttpHeaders("error").disableCaching();
    }

    public HttpHeaders list()
    {
        User user = (User) sessionMap.get("user");
        if ((user != null))
        {
            penaltyInfo = penaltyDAO.getAllList(user);
            penalty = user.getPenalty();
            return new DefaultHttpHeaders("list").disableCaching();
        }
        else
        return new DefaultHttpHeaders("error").disableCaching();
    }

}

custom mapper is

public class HierarchicalRestActionMapper extends RestActionMapper {

    protected void parseNameAndNamespace(String uri, ActionMapping mapping, ConfigurationManager configManager) {

        Set<String> actions = new HashSet<String>();
        PackageConfig packageConfig;

        Configuration configuration = configManager.getConfiguration();

        // get the current set of action names in our packages of interest
        Map<String, PackageConfig> packageConfigs = configuration.getPackageConfigs();
        for (Map.Entry<String, PackageConfig> packageEntry : packageConfigs.entrySet()) {
            packageConfig = packageEntry.getValue();
            actions.addAll(packageConfig.getActionConfigs().keySet());
        }

        // parse the uri into namespace, action, method and id
        Map<String, String> params = URIActionMethodParser.process(uri, actions);

        if (params != null) {
            // update the mapping
            mapping.setNamespace(params.get("namespace"));
            params.remove("namespace");
            mapping.setName(params.get("action"));
            params.remove("action");
            mapping.setParams((Map) params);
        }
    }
}

Do you have any idea why so ?


Solution

  • When I execute action tag without input method

    <@s.action name="vendor-tree" executeResult="true"/>

    Struts throw missing execute method error. I renamed mine input method to execute and all now is working.