Struts 2 refactoring code to avoid OGNL static method access - struts2

Struts 2, 2.3.20 mentioned that
Support for accessing static methods from expression will be disabled
soon, please consider re-factoring your application to avoid further
problems!
We have used OGNL static calls in validators:
#ExpressionValidator(
expression = "#foo.bar#isValidAmount(amount)",
key = "validate.amount.is.not.valid"),
Also we used it in tags
<s:set var="test"
value="#foo.bar#sampleMethod(#attr.sampleObject.property1)" />
Well, what is the best way to refactor above two usages ?!

In your code you are using a static method call. The best way is to create a method in the action class that wraps a static methods and use it in OGNL.
public class Wrapper {
public boolean isValidAmount(amount){
return foo.barr.isValidAmount(amount);
}
public Object sampleMethod(Object property1){
return foo.barr.sampleMethod(Object property1);
}
}
As soon as action bean is in the value stack you can use
#ExpressionValidator(
expression = "isValidAmount(amount)",
key = "validate.amount.is.not.valid"),
or in JSP
<s:set var="test"
value="sampleMethod(#attr.sampleObject.property1)" />

Related

How to read from an Action that is needed on every page?

I'm new to Struts2.
How does one invoke methods from a class without using an ActionSupport-implemented-class? So a simple Java class.
It's because I want to add something to a include jsp file. Therefore this value is always valid - independent of the page one is requesting.
Probably simple.
You can write your Action without extending ActionSupport class.
ActionSupport is just for convenience
ActionSupport has default implementations of common methods (e.g., execute(), input()), gives access to Action.SUCCESS, Action.ERROR and other result names, etc.
Action Class:
public class TestAction
{
public String testMethod()
{
return "success";
}
}
Struts.xml:
<action name="TestAction" class="com.TestAction" method="testMethod">
<result name="success">Mypage.jsp</result>
</action>
Struts2 architecture does not require you to extend ActionSupport class. It is that flexible. ActionSupport is just a convenience and provides basic functionality like validation etc. You can write a simple pojo action class. All you need is to return a String that will be used to forward to a resource like jsp.
For Example
public class MyAction
{
public String testAction()
{
//Perform your logic here. Note it is not mandatory to return SUCCESS here, you can actually return any String here, but make sure you map that String to a resource in your `struts.xml`
return SUCCESS;
}
}

beans properties and modelDriven in struts 2

I am new to struts 2. I created an action class that insert data from JSP page to a bean using ModelDriven interface.
The problem is that i have some non 'Stringproperties in the bean likeLong,Date` ... etc
The problem is:
when i press submit button in the jsp page i get an error saying that it did not find the a string setter for that particular property.
for example if i have in my bean
package com.hsms.aseza.enteringApproval
Class EnteringApproval
private Date approvalDate
Date getApprovalDate()
{
return employeeId;
}
void setApprovalDate(Date employeeId)
{
this.employeeId = employeeId;
}
when the action class that implement the model driven is executed, it fires a run time exception
java.lang.NoSuchMethodException: com.hsms.aseza.enteringApproval.EnteringApproval.setApprovalDate([Ljava.lang.String;)
Is there anyway to solve this problem without writing a String setter for my Date property?
I think that your problem is conversion i.e. conversion from String to your java.util.Date Object. This class extends this which is responsible for converting from String to other types like Long, Double etc. If you check the source code for DefaultTypeConverter, you won't see any conversion for either java.util.Date or java.sql.Date. So i think what you should do is to write a converter for approvalDate. My previous post on this will guide you on the procedure, all you will need is to edit the code to suit your needs.
On your jsp, follow this datepicker example
Use the same format i.e date format used to display your date in your jsp with SimpleDateFormat to do your conversion in convertFromString method of your converter and return the converted java.util.Date or java.sql.Date.
Let me know if you have issues implementing this.
Try use the s:date tag in your jsp.
Edit:
Try use:
<s:textfield key="objEnteringApproval.approvalDate"></s:textfield>
in which objEnteringApproval is your public accessible variable in your controller.
Make sure you have initialized your bean in your action class
Private YourBean bean = new YourBean();
Or you can have it in the constructor
Make sure your getter and setter are public
If you are trying to type date on the jsp page, please use datepicker e.g. sx:datetimepicker or sj:datepicker

Enforce usage of custom HtmlHelper extensions over MVC's

Asp.net MVC provides lots of (and very useful) HtmlHelper extensions. But what if I was to provide a micro sub-framework with some extension methods that extend existing ones?
i.e. BeginForm may be rewritten to be more rich (always adding security stuff like anti forgery token and similar.
Question
In order to not rewrite all of the Asp.net MVC's HTML helper methods how can I enforce usage of mine? So that the using usual BeginForm would either throw an exception or not be accessible in the first place. The second choice is likely not possible without removing System.Web.Mvc.Html namespace from view's folder web.config file. This would mean that all of those helpers would need rewriting. And that's something I don't want to do.
The thing is that when this micro sub-framework is used it should prevent usage of standard helpers for security reasons. Period.
What other options are there for me?
Example
Suppose I would only write my own BeginForm that I would call BeginSecureForm so one would use it as:
#using Html.BeginSecureForm() {
...
#Html.EditorFor(m => m.Something)
...
}
As you can see I've used my custom helper and standard EditorFor helper as well. This means that System.Web.Mvc.Html is still included to use non-custom helpers like EditorFor.
Upper code works fine as long as you use my custom helper method... But what if some developer would forget to do so and use the normal one instead?
#using Html.BeginForm() {
...
#Html.EditorFor(m => m.Something)
...
}
Well in this case I would either like to:
Html.BeginForm not being accessible at all
Html.BeginForm throws an exception that the secure version should be used
anything else I don't know can be done to prevent usage of standard BeginForm
One possibility to achieve that is to write a custom WebViewPage and override the Html property with a custom one:
public abstract class MyWebViewPage<T> : WebViewPage<T>
{
public override void InitHelpers()
{
this.Ajax = new AjaxHelper<T>(ViewContext, this);
this.Html = new MyHtmlHelper<T>(ViewContext, this);
this.Url = new UrlHelper(ViewContext.RequestContext);
}
public new MyHtmlHelper<T> Html { get; set; }
}
and here's the custom MyHtmlHelper<T> class in which you will make obsolete the methods that you don't want to be used directly by the developers:
public class MyHtmlHelper<T>: HtmlHelper<T>
{
public MyHtmlHelper(ViewContext viewContext, IViewDataContainer viewDataContainer)
: base(viewContext, viewDataContainer)
{
}
[Obsolete("Use SecureBeginForm instead", true)]
public MvcForm BeginForm()
{
throw new Exception("Use SecureBeginForm instead.");
}
}
Alright, now all that's left to do is to switch the base type for all Razor views in the application. This could be done inside ~/Views/web.config where you will replace:
<pages pageBaseType="System.Web.Mvc.WebViewPage">
with:
<pages pageBaseType="MyAppName.Mvc.MyWebViewPage">
OK, now you could write your micro framework extension methods to the MyHtmlHelper class and thus providing your custom secure counterparts of the default methods:
public static class MyHtmlHelperExtensions
{
public static MvcForm SecureBeginForm<T>(this MyHtmlHelper<T> html)
{
var rawUrl = html.ViewContext.HttpContext.Request.Url.AbsoluteUri;
var builder = new UriBuilder(rawUrl);
builder.Scheme = Uri.UriSchemeHttps;
var form = new TagBuilder("form");
form.Attributes["action"] = builder.ToString();
form.Attributes["method"] = "post";
html.ViewContext.Writer.Write(form.ToString());
return new MvcForm(html.ViewContext);
}
}
And now inside any Razor view:
#using (Html.SecureBeginForm())
{
...
}
and when you attempt:
#using (Html.BeginForm())
{
...
}
you get a compile-time error (assuming you have enabled compilation of Razor views):
or a runtime exception if you haven't.
You are trying to achieve security in the application by forcing developers to avoid using the Html.BeginForm instead of using the secured one. Let say you somehow tricked the framework not to use Html.BeginForm so what? an young developer who is even not aware of Html.BeginForm can directly write the form HTML in the view and break the rule!
I think security has to be implemented in an application by not forcing someone to use the right tool instead of that it has to be done at the higher level of the application. In the form example itself if all the HTML forms posted to the server should have an anti forgery token then I would do the check at the higher level in the MVC pipeline. If some developer used the normal form still that module won't work and that will be taken care in the testing phase.

Spring.Net & Attribute Injection

I want to dependency inject an attribute in ASP.NET MVC using Spring.Net, my attribute is something like this (note this is all pseudo code I've just typed in)...
public class InjectedAttribute : ActionFilterAttribute
{
private IBusinessLogic businessLogic;
public InjectedAttribute(IBusinessLogic businessLogic)
{
this.businessLogic = businessLogic;
}
public override void OnActionExecuting(ActionExecutedContext filterContext)
{
// do something with the business logic
businessLogic.DoSomethingImportant();
}
}
I'm using a controller factory to create the Controllers which are also injected with various business logic objects. I'm getting the controllers from the IoC container like this...
ContextRegistry.GetContext().GetObject("MyMVCController");
I'm configuring my Controllers like so passing in the business logic
<object name="MyMVCController" type="MyMVC.MyMVCController, MyMVC">
<constructor-arg index="0" ref="businessLogic" />
</object>
Is there a way to configure the injection of the attributes? I don't really want to put this into my attributes...
public class InjectedAttribute : ActionFilterAttribute
{
private IBusinessLogic businessLogic;
public InjectedAttribute(IBusinessLogic businessLogic)
{
this.businessLogic = ContextRegistry.GetContext().GetObject("businessLogic");
}
....
I'm configuring my Controllers like so passing in the business logic
This defines controllers as singletons meaning that they will be reused among all requests which could be catastrophic. Ensure controllers are not defined as singletons:
<object name="AnotherMovieFinder" type="MyMVC.MyMVCController, MyMVC" singleton="false">
<constructor-arg index="0" ref="businessLogic" />
</object>
Now, this being said let's go back to the main question about attributes.
Because you want constructor injection in your filters you can no longer decorate any controllers or actions with them as attribute values must be known at compile time. You need a mechanism to apply those filters at runtime to controllers/actions.
If you are using ASP.NET MVC 3 you could write a custom filter provider which will apply your action filter to desired controllers/actions by injecting dependencies into it.
If you are using an older version you could use a custom ControllerActionInvoker.

Dynamic typed ViewPage

Is this possible? Here's what I'm trying:
public ActionResult Index()
{
dynamic p = new { Name = "Test", Phone = "111-2222" };
return View(p);
}
And then my view inherits from System.Web.Mvc.ViewPage<dynamic> and tries to print out Model.Name.
I'm getting an error: '<>f__AnonymousType1.Name' is inaccessible due to its protection level
So basically, is what I'm trying to do just not possible? Why or why not?
Update: here's my view
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>
<asp:Content ...>
<%=Model.Name%>
<%=Model.Phone%>
</asp:Content>
The View constructor is built-in to the framework.
Anonymous types cannot be returned by a method; they are only valid within the scope of the method in which they are defined.
You should use a Model class that you have previously defined and pass that to your View. There is nothing wrong with passing a Model class that does not have every field defined.
Update:
I think I was wrong before. This should work. Perhaps the problem is within the View. Can you post more code? Especially the View and its constructor.
Update the Second:
Ok, I was wrong about passing an anonymous type to another method for use as a dynamic variable -- that can be done.
But I was also wrong in my belief that what you're trying to do would work. Unfortunately for you, it will not. The problem is that you are using ViewPage<TModel>, which uses a ViewDataDictionary<TModel> internally. Because they require strong types, you won't be able to use dynamic objects with them. The internal structure just doesn't use dynamic internally, and specifying dynamic as the type fails.
What would be needed is a DynamicViewPage class and corresponding DynamicViewDataDictionary class that accept object and store it internally as a dynamic. Then you could use an anonymous type and pass it to your Views.
That said, you would not gain anything. You would be able to specify your Views as you have done (i.e. <%=Model.Name%>), but you would not benefit from strong typing. There would be no intellisense and there would be no type safety. You'd do just as well to use the untyped ViewDataDictionary as #Dennis Palmer suggests.
This has been an interesting (and, unfortunately for me, absorbing) thought experiment, but I think, ultimately, that it's not going to happen. Either declare a public type and pass it to your Views, or use the untyped dictionary.
What benefit were you hoping to get from using the dynamic type here?
Using the ViewData dictionary is a very easy way of adding arbitrary objects/items to your view output.
You don't need reflection to get the property names within your View. Just use ViewData.Keys to get the collection of names.
Edit: I've just learned a bit more about dynamics myself and I think maybe you need to create your own dynamic object class that inherits from DynamicObject. You'll want to have a private dictionary in that class and then override TrySetMember and TryGetMember.
Edit Aside: I think one advantage of a strongly typed ViewModel is that you can accept it as a parameter in your POST Action methods. The MVC framework will handle the model binding and in the action method you simply have an instance of your ViewModel class. I don't think you'll have that advantage with a dynamic even if they do work.
Edit Result: Well, I tried using a class derived from DynamicObject, but VS2010 crashes when it tries to render the view. I don't get any exception, just a hard crash and Visual Studio restarts. Here's the code I came up with that causes the crash.
The custom dynamic class:
public class DynViewModel : DynamicObject
{
private Dictionary<string, object> ViewDataBag;
public DynViewModel()
{
this.ViewDataBag = new Dictionary<string, object>();
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
this.ViewDataBag[binder.Name] = value;
return true;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = this.ViewDataBag[binder.Name];
return true;
}
}
In the controller:
public ActionResult DynamicView()
{
dynamic p = new DynamicViewModel.Models.DynViewModel();
p.Name = "Test";
p.Phone = "111-2222";
return View(p);
}
My view is basically the same as what is listed in the question:
<p>Name: <%=Model.Name %></p>
<p>Phone: <%=Model.Phone %></p>
My Conclusion: This might work, but in the Beta 1 of VS2010 I can't figure out why my code causes Visual Studio to crash. I'll try it again in VS2010 Beta 2 when it is released because it is an interesting exercise in learning about dynamics. However, even if this were to work, I still don't see any advantage over using the ViewData dictionary.
Phil Haack to the rescue! Here's a blog post by Phil Haack that might help you out. It looks like it is what you were looking for. Fun With Method Missing and C# 4
The actual error here (<>f__AnonymousType1.Name' is inaccessible due to its protection level) is the result of using anonymous types. Anonymous types are implicitly internal (at least in C#), therefore they can only be accessed normally from the same assembly. Since your view is compiled into a separate assembly at runtime, it can't access the internal anonymous type.
The solution is to pass concrete/named classes as models to your view. The view itself can still use dynamic if you want.
On .NET 4.0 Anonymous types can easily be converted to ExpandoObjects and thus all the problems is fixed with the overhead of the conversion itself.
Check out here

Resources