I had to upgrade an old project from struts 2.1.6 to 2.5.30. In the JSPs there are a lot of URLs referencing methods in actions with a !. I’ve never seen that before, but according to https://struts.apache.org/core-developers/action-configuration this seems to be a valid way and everything worked fine before.
Example from one JSP:
<s:url value="partPrices!listPurchasePrices.action?salesPartMasterPrice.headerId=%{salesPartMaster.id}" var="listPurchasePrices" includeParams="get" />
struts.xml
<action name="partPrices" method="execute" class="com.abosco.scu.ais.action.SalesPartPrice">
<result>/pages/prices.jsp</result>
<result name="input">/pages/error.jsp</result>
</action>
After upgrading i get the following error when clicking on the example link:
There is no Action mapped for namespace [/] and action name [partPrices!listPurchasePrices] associated with context path [/sauterais].
Stacktraces
There is no Action mapped for namespace [/] and action name [partPrices!listPurchasePrices] associated with context path [/sauterais]. - [unknown location]
com.opensymphony.xwork2.DefaultActionProxy.prepare(DefaultActionProxy.java:195)
org.apache.struts2.factory.StrutsActionProxy.prepare(StrutsActionProxy.java:57)
org.apache.struts2.factory.StrutsActionProxyFactory.createActionProxy(StrutsActionProxyFactory.java:32)
com.opensymphony.xwork2.DefaultActionProxyFactory.createActionProxy(DefaultActionProxyFactory.java:60)
org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:564)
org.apache.struts2.dispatcher.ExecuteOperations.executeAction(ExecuteOperations.java:79)
org.apache.struts2.dispatcher.filter.StrutsExecuteFilter.doFilter(StrutsExecuteFilter.java:86)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
com.opensymphony.sitemesh.webapp.SiteMeshFilter.doFilter(SiteMeshFilter.java:65)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
org.apache.struts2.dispatcher.filter.StrutsPrepareFilter.doFilter(StrutsPrepareFilter.java:92)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:177)
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:687)
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360)
org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399)
org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:891)
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1784)
org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
java.lang.Thread.run(Thread.java:748)
The action class, just in case. The methods are there.
package com.abosco.scu.ais.action;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.UUID;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.opensymphony.xwork2.Preparable;
import com.abosco.scu.ais.dao.SalesPartMasterDAO;
import com.abosco.scu.ais.dao.SalesPartMasterPriceDAO;
import com.abosco.scu.ais.model.SalesPartMaster;
import com.abosco.scu.ais.model.SalesPartMasterPrice;
import com.abosco.scu.ais.model.SalesPartMasterPriceExample;
public class SalesPartPrice extends UserAware implements Preparable
{
/**
* generated id
*/
private static final long serialVersionUID = 2747412237989015409L;
private static final BigDecimal ONE = new BigDecimal("1");
private static final BigDecimal HUNDRET = new BigDecimal("100");
private SalesPartMasterPriceDAO mSalesPartMasterPriceDAO;
private SalesPartMasterDAO mSalesPartMasterDAO;
private List<SalesPartMasterPrice> mSalesPartPrices;
private SalesPartMaster mSalesPartMaster = new SalesPartMaster();
private SalesPartMasterPrice mSalesPartMasterPrice = new SalesPartMasterPrice();
public final static int SALES_PRICE = 1;
public final static int PURCHASE_PRICE = 0;
private int mPriceMode = PURCHASE_PRICE;
/** Logging */
private static final Log logger = LogFactory.getLog(SalesPart.class);
/* (non-Javadoc)
* @see com.opensymphony.xwork2.Preparable#prepare()
*/
public void prepare() throws Exception
{
if (mSalesPartMasterPrice.getId()!=null)
{
mSalesPartMasterPrice = mSalesPartMasterPriceDAO.selectByPrimaryKey(mSalesPartMasterPrice.getId());
mPriceMode = StringUtils.equals(mSalesPartMasterPrice.getPriceType(), "V") ? SALES_PRICE : PURCHASE_PRICE;
}
if (mSalesPartMasterPrice.getHeaderId()!=null)
{
mSalesPartMaster = mSalesPartMasterDAO.selectByPrimaryKey(mSalesPartMasterPrice.getHeaderId());
}
initEmptyValues(mSalesPartMasterPrice);
}
/**
* for prevent null with i18n format
*/
private void initEmptyValues(SalesPartMasterPrice price)
{
if (price!=null)
{
Double zeroDouble = new Double(0.0);
if (price.getDiscount1()==null)
{
price.setDiscount1(zeroDouble);
}
if (price.getDiscount2()==null)
{
price.setDiscount2(zeroDouble);
}
if (price.getDiscount3()==null)
{
price.setDiscount3(zeroDouble);
}
if (price.getGrossPrice()==null)
{
price.setGrossPrice(zeroDouble);
}
if (price.getNetPrice()==null)
{
price.setNetPrice(zeroDouble);
}
if (price.getMaxDiscount()==null)
{
price.setMaxDiscount(zeroDouble);
}
if (price.getDiscountable()==null)
{
if (price.getPriceType()!= null && price.getPriceType().equals(SalesPartMasterPrice.PURCHASE_PRICE))
{
price.setDiscountable("B");
}
else
{
price.setDiscountable("");
}
}
if (price.getAdditionalChargeFactor()==null)
{
price.setAdditionalChargeFactor(zeroDouble);
}
if (price.getConstruction()==null)
{
price.setConstruction(zeroDouble);
}
if (price.getConnectionCosts()==null)
{
price.setConnectionCosts(zeroDouble);
}
}
}
/**
* Set the salesPartMasterDAO
*
* @param salesPartMasterDAO The salesPartMasterDAO to set.
*/
public void setSalesPartMasterPriceDAO(SalesPartMasterPriceDAO salesPartMasterPriceDAO)
{
mSalesPartMasterPriceDAO = salesPartMasterPriceDAO;
}
public String execute() throws Exception {
if (mSalesPartMasterPrice.getId()==null)
{
return INPUT;
}
return SUCCESS;
}
public String create() throws Exception {
try
{
if (mSalesPartMasterPrice.getHeaderId()==null)
{
return INPUT;
}
else
{
mSalesPartMaster = mSalesPartMasterDAO.selectByPrimaryKey(mSalesPartMasterPrice.getHeaderId());
mSalesPartMasterPrice.setPriceType(SALES_PRICE == getPriceMode() ? SalesPartMasterPrice.SALES_PRICE : SalesPartMasterPrice.PURCHASE_PRICE);
initEmptyValues(mSalesPartMasterPrice);
Calendar now = Calendar.getInstance();
boolean firstPrice = false;
// check if the new price will be the first one
if (SALES_PRICE == getPriceMode())
{
if (CollectionUtils.isEmpty(getPricesList(mSalesPartMasterPrice.getHeaderId(), SalesPartMasterPrice.SALES_PRICE)))
{
firstPrice = true;
}
}
else
{
if (CollectionUtils.isEmpty(getPricesList(mSalesPartMasterPrice.getHeaderId(), SalesPartMasterPrice.PURCHASE_PRICE)))
{
firstPrice = true;
}
}
if (firstPrice)
{
//set default valid dates to jan an dec this year
mSalesPartMasterPrice.setValidFrom(new GregorianCalendar(now.get(Calendar.YEAR),Calendar.JANUARY,1).getTime());
mSalesPartMasterPrice.setValidTo(new GregorianCalendar(now.get(Calendar.YEAR),Calendar.DECEMBER,31).getTime());
}
else
{
//set default valid dates to jan an dec nex year
mSalesPartMasterPrice.setValidFrom(new GregorianCalendar(now.get(Calendar.YEAR)+1,Calendar.JANUARY,1).getTime());
mSalesPartMasterPrice.setValidTo(new GregorianCalendar(now.get(Calendar.YEAR)+1,Calendar.DECEMBER,31).getTime());
}
}
}
catch (Exception x)
{
logger.error("Error during Price create",x);
addActionError("Fehler bei der Preisanlage");
addActionError(x.getLocalizedMessage());
}
return SUCCESS;
}
public String save() throws Exception {
boolean newPrice = false;
mPriceMode = StringUtils.equals(mSalesPartMasterPrice.getPriceType(), SalesPartMasterPrice.SALES_PRICE) ? SALES_PRICE : PURCHASE_PRICE;
try
{
if (mSalesPartMasterPrice.getId()==null)
{
newPrice = true;
if (mSalesPartMasterPrice.getDiscount1()==null)
{
mSalesPartMasterPrice.setDiscount1(new Double(0.0));
}
if (mSalesPartMasterPrice.getDiscount2()==null)
{
mSalesPartMasterPrice.setDiscount2(new Double(0.0));
}
if (mSalesPartMasterPrice.getDiscount3()==null)
{
mSalesPartMasterPrice.setDiscount3(new Double(0.0));
}
if (mSalesPartMasterPrice.getAdditionalChargeFactor()==null)
{
mSalesPartMasterPrice.setAdditionalChargeFactor(new Double(0.0));
}
if (mSalesPartMasterPrice.getConstruction()==null)
{
mSalesPartMasterPrice.setConstruction(new Double(0.0));
}
if (mSalesPartMasterPrice.getConnectionCosts()==null)
{
mSalesPartMasterPrice.setConnectionCosts(new Double(0.0));
}
mSalesPartMasterPrice.setId(UUID.randomUUID());
mSalesPartMasterPrice.setCreatedUser(getUser().getLoginName());
mSalesPartMasterPrice.setCreatedDate(new Date());
mSalesPartMasterPrice.setChangedUser(getUser().getLoginName());
mSalesPartMasterPrice.setChangedDate(new Date());
//check for valid range
//Select * from VTEIPO where VPVEID='1bbfc3ea-67c3-4262-ac69-d24f1b90b8a5' AND VPPRTP='B' and VPBIDA>20090201 and 20090301>VPVODA
if (mSalesPartMasterPriceDAO.checkDateRange(mSalesPartMasterPrice))
{
mSalesPartMasterPriceDAO.insert(mSalesPartMasterPrice);
addActionMessage(getText("message.insert",new String[] {getText("message.priceinformation")}));
}
else
{
addActionError(getText("message.priceerror"));
mSalesPartMasterPrice.setId(null);
return INPUT;
}
}
else
{
mSalesPartMasterPrice.setChangedUser(getUser().getLoginName());
mSalesPartMasterPrice.setChangedDate(new Date());
//check for valid range
if (mSalesPartMasterPriceDAO.checkDateRange(mSalesPartMasterPrice))
{
mSalesPartMasterPriceDAO.updateByPrimaryKey(mSalesPartMasterPrice);
addActionMessage(getText("message.update",new String[] {getText("message.priceinformation")}));
}
else
{
addActionError(getText("message.priceerror"));
return INPUT;
}
}
}
catch (Exception x)
{
if (newPrice)
{
mSalesPartMasterPrice.setId(null);
}
logger.error("Error during Price save",x);
addActionError("Fehler bei der Preisspeicherung");
addActionError(x.getLocalizedMessage());
}
initEmptyValues(mSalesPartMasterPrice);
return SUCCESS;
}
/* (non-Javadoc)
* @see com.opensymphony.xwork2.ActionSupport#validate()
*/
public void validateSave()
{
mPriceMode = StringUtils.equals(mSalesPartMasterPrice.getPriceType(), SalesPartMasterPrice.SALES_PRICE) ? SALES_PRICE : PURCHASE_PRICE;
// <field name="salesPartMaster.status">
// <field-validator type="requiredstring">
// <message>Bitte den Status auswählen</message>
// </field-validator>
// </field>
// <field name="salesPartMaster.partId">
// <field-validator type="requiredstring">
// <message>Bitte eine Teilenummer angeben</message>
// </field-validator>
// </field>
if (mPriceMode == PURCHASE_PRICE)
{
if (mSalesPartMasterPrice.getGrossPrice()==null)
{
addFieldError("salesPartMasterPrice.grossPrice", "Bitte den Bruttopreis angeben");
}
}
if (mSalesPartMasterPrice.getValidFrom() == null)
{
addFieldError("salesPartMasterPrice.validFrom", "Bitte das Gültigkeitsdatum angeben");
return;
}
if (mSalesPartMasterPrice.getValidTo() == null)
{
addFieldError("salesPartMasterPrice.validTo", "Bitte das Gültigkeitsdatum angeben");
return;
}
if (mSalesPartMasterPrice.getValidTo().before(mSalesPartMasterPrice.getValidFrom()))
{
addFieldError("salesPartMasterPrice.validTo", "BIS Datum kleiner als VON Datum");
}
}
public String listPurchasePrices() throws Exception {
if (mSalesPartMasterPrice.getHeaderId()==null)
{
return INPUT;
}
else
{
mSalesPartPrices = getPricesList(mSalesPartMasterPrice.getHeaderId(), SalesPartMasterPrice.PURCHASE_PRICE);
calculatePriceChange();
Collections.reverse(mSalesPartPrices);
}
return SUCCESS;
}
private List<SalesPartMasterPrice> getPricesList(UUID headerId, String priceType)
{
SalesPartMasterPriceExample search = new SalesPartMasterPriceExample();
search.setOrderByClause("VPVODA, VPBIDA");
search.createCriteria().andHeaderIdEqualTo(headerId)
.andPriceTypeEqualTo(priceType);
return mSalesPartMasterPriceDAO.selectByExample(search);
}
/**
*
*/
private void calculatePriceChange()
{
boolean firstElement = true;
double oldNetPrice = 0.0;
double oldGrossPrice = 0.0;
for (SalesPartMasterPrice price : mSalesPartPrices)
{
double netPrice = price.getNetPrice() != null ? price.getNetPrice().doubleValue() : 0.0;
double grossPrice = price.getGrossPrice() != null ? price.getGrossPrice().doubleValue() : 0.0;
if (firstElement)
{
oldNetPrice = netPrice;
oldGrossPrice = grossPrice;
firstElement = false;
}
price.setGrossPriceChange(new Double(grossPrice-oldGrossPrice));
price.setNetPriceChange(new Double(netPrice-oldNetPrice));
price.setGrossPriceChangePercent(oldGrossPrice!=0.0 ? new Double(((grossPrice-oldGrossPrice)/oldGrossPrice)*100): new Double(0.0));
price.setNetPriceChangePercent(oldNetPrice!=0.0 ? new Double(((netPrice-oldNetPrice)/oldNetPrice)*100): new Double(0.0));
//store old prices
oldNetPrice = netPrice;
oldGrossPrice = grossPrice;
}
}
public String listSalesPrices() throws Exception {
if (mSalesPartMasterPrice.getHeaderId()==null)
{
return INPUT;
}
else
{
mSalesPartPrices = getPricesList(mSalesPartMasterPrice.getHeaderId(), SalesPartMasterPrice.SALES_PRICE);
calculatePriceChange();
Collections.reverse(mSalesPartPrices);
}
mPriceMode=SALES_PRICE;
return SUCCESS;
}
public String recalculateNetPrices() throws Exception {
SalesPartMasterPriceExample search = new SalesPartMasterPriceExample();
search.createCriteria().andPriceTypeEqualTo(SalesPartMasterPrice.PURCHASE_PRICE);
mSalesPartPrices = mSalesPartMasterPriceDAO.selectByExample(search);
for (SalesPartMasterPrice price : mSalesPartPrices)
{
calculateNetPriceChange(price);
}
mSalesPartMasterPriceDAO.batchUpdate(mSalesPartPrices);
return SUCCESS;
}
public String recalculateNetPricesTest() throws Exception {
//prepare test table
int rows = mSalesPartMasterPriceDAO.insertInTestTable();
logger.info("Test Tabelle VTEIP2 gefüllt mit "+rows+ "Preissätzen");
addActionMessage("Test Tabelle VTEIP2 gefüllt mit "+rows+ "Preissätzen");
SalesPartMasterPriceExample search = new SalesPartMasterPriceExample();
search.createCriteria().andPriceTypeEqualTo(SalesPartMasterPrice.PURCHASE_PRICE);
mSalesPartPrices = mSalesPartMasterPriceDAO.selectPriceTestByExample(search);
for (SalesPartMasterPrice price : mSalesPartPrices)
{
calculateNetPriceChange(price);
}
mSalesPartMasterPriceDAO.batchUpdate(mSalesPartPrices);
addActionMessage("Nettopreisberechung in VTEIP2 durchgeführt.");
return SUCCESS;
}
/**
* @param price
*/
private void calculateNetPriceChange(SalesPartMasterPrice price)
{
// double netPrice = (price.getGrossPrice().doubleValue() * (1.0-(price.getDiscount1().doubleValue()/100.0)) * (1.0-(price.getDiscount2().doubleValue()/100.0)) * (1.0-(price.getDiscount3().doubleValue()/100.0)));
//
// BigDecimal net = new BigDecimal(new Double(netPrice).toString());
// net = net.setScale(2, RoundingMode.HALF_UP);
// price.setNetPrice(new Double(net.doubleValue()));
BigDecimal discountMultiplicator1 = ONE.subtract(new BigDecimal(price.getDiscount1().toString()).divide(HUNDRET));
BigDecimal discountMultiplicator2 = ONE.subtract(new BigDecimal(price.getDiscount2().toString()).divide(HUNDRET));
BigDecimal discountMultiplicator3 = ONE.subtract(new BigDecimal(price.getDiscount3().toString()).divide(HUNDRET));
BigDecimal net = new BigDecimal(price.getGrossPrice().toString()).multiply(discountMultiplicator1).multiply(discountMultiplicator2).multiply(discountMultiplicator3);
net = net.setScale(2, RoundingMode.HALF_UP);
price.setNetPrice(new Double(net.doubleValue()));
}
/**
* Return the salesPartMasterPrice.
*
* @return the salesPartMasterPrice.
*/
public SalesPartMasterPrice getSalesPartMasterPrice()
{
return mSalesPartMasterPrice;
}
/**
* Return the salesPartPrices.
*
* @return the salesPartPrices.
*/
public List<SalesPartMasterPrice> getSalesPartPrices()
{
return mSalesPartPrices;
}
/**
* Return the priceMode.
*
* @return the priceMode.
*/
public int getPriceMode()
{
return mPriceMode;
}
/**
* Return the priceMode.
*
* @return the priceMode.
*/
public boolean isPurchasePrice()
{
return mPriceMode == PURCHASE_PRICE;
}
/**
* Set the priceMode
*
* @param priceMode The priceMode to set.
*/
public void setPriceMode(int priceMode)
{
mPriceMode = priceMode;
}
/**
* Set the salesPartMasterPrice
*
* @param salesPartMasterPrice The salesPartMasterPrice to set.
*/
public void setSalesPartMasterPrice(SalesPartMasterPrice salesPartMasterPrice)
{
mSalesPartMasterPrice = salesPartMasterPrice;
}
/**
* Set the salesPartMasterDAO
*
* @param salesPartMasterDAO The salesPartMasterDAO to set.
*/
public void setSalesPartMasterDAO(SalesPartMasterDAO salesPartMasterDAO)
{
mSalesPartMasterDAO = salesPartMasterDAO;
}
/**
* Return the salesPartMaster.
*
* @return the salesPartMaster.
*/
public SalesPartMaster getSalesPartMaster()
{
return mSalesPartMaster;
}
}
I’ve read up about DMI and SMI from above link and from my understanding i now have to disable SMI, which was the only change i made to the struts.xml.
<package name="item" namespace="/" extends="struts-default" strict-method-invocation="false" >
Still didn’t help. What looks a bit suspect to me is the [unknown location] in the error message but otherwise i have no clue what to look for.