Struts 2 with convention plugin - ExceptionMapping not working - struts2

I have the following action which maps java.lang.Exception to a result of name test:
#Action(value = "getDispMeiosGruposAmbientes", results = {
#Result(name = "test", type = "chain", params = {
"actionName", "getItensSuprimento"
})}, exceptionMappings = {
#ExceptionMapping(exception = "java.lang.NullPointerException", result = "test", params = {"param1", "val1"})
})
public class TestAction extends ActionSupport {
public String execute() throws Exception {
throw new NullPointerException();
// return "test";
}
}
test result should redirect to a jsp page when an exception is thrown.
But this is not working. Maybe the syntax is wrong?
EDIT: i have changed the action and now i can redirect to an action when i return "test" string, but not when i throw an exception. Everything appears to be ok.

It turns out that i was using version 2.3.20 of Struts and there was a bug with exception mappings where thrown exceptions are not caught. I have changed to version 2.5.17 and now exception mapping works fine.
This bug is described on this question and Apache JIRA;

Related

Exception logging in ASP.NET MVC Application

Currently I log exceptions as follows-
public ActionResult Login()
{
try
{
throw new Exception("test");
}
catch (Exception ex)
{
LogException(ex);
}
return View();
}
I can get a clear & detail information about the exception from LogException() method here. See below-
Created on: 27-Dec-2016, 06.34.33 PM
Type: System.Exception
Error Description: test
Source File: ...\Controllers\LoginController.cs
Method: Login
Line: 20
Column: 17
I tried the same method in -
public class MyCustomAttribute : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
Exception ex = filterContext.Exception;
LogException(ex)
}
}
public class MyCustomAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
Exception ex = filterContext.Exception;
LogException(ex)
}
}
I also tried overloading OnException method-
public abstract class BaseController : Controller
{
public BaseController() {}
protected override void OnException(ExceptionContext filterContext)
{
Exception ex = filterContext.Exception;
filterContext.ExceptionHandled = true;
LogException(ex);
}
}
In all three abose cases I am getting no information about Source File, Method, Line and Column-
Created on: 27-Dec-2016, 06.44.45 PM
Type: System.Exception
Error Description: test
Source File:
Method: <BeginInvokeAction>b__1e
Line: 0
Column: 0
This is my method to log exception-
public static void Create(Exception exception, String rootDirectoryPath)
{
try
{
StackTrace st = new StackTrace(exception, true);
StackFrame frame = st.GetFrame(st.FrameCount - 1);
string fileName = frame.GetFileName();
string methodName = frame.GetMethod().Name;
int line = frame.GetFileLineNumber();
int col = frame.GetFileColumnNumber();
//Other code .....
}
catch (Exception)
{
//do nothing.
}
}
My question is, is it possible to retrieve Source File, Method, Line and Column information from those three cases?
I would not like to handle exceptions by writing try .. catch.. every time.
I have heard about ELMAH & Log4Net. But not sure whether those library able to supply my desired information from exceptions.
Sorry for the long question.
I'm the author of Coderr which also takes care of tracking exceptions for you.
the problem with your solution is that the exception filter (and the ASP.NET framework calls to invoke it) will be part of the stack trace, thus getting frames from the stack trace object wont work.
Also note that if the .PDB file is not included when you put your web site in production you wont get file numbers.
The most common approach is just to log exception.ToString() which will include line numbers in the stack trace (if there is a PDB file).
Why do you want to use your custom approach?
You are reinventing the wheel as ELMAH will do the job just fine. Did you give it a try? Just a Nuget package and some configuration to be fully set.
You can read more about it from Scot Hanselman or read the tutorial.

trapping an exception using IExceptionFilter and redirecting permanently binds the path to error page

I am trying to redirect unhandled exceptions to an error page by first generating a crash code and then redirecting the user to the error page. The problem is that after the first time it happens, the original url/action is permanently mapped to the error page url and doesn't even enter the OnException method anymore. Even after I fix the cause of the original exception, the url/action is still mapped to just redirect to the error page. Not sure where this is happening or how to fix it. Below is the code:
public class UnhandledExceptionFilter : IExceptionFilter
{
protected static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public void OnException(ExceptionContext filterContext)
{
var ex = filterContext.Exception;
if (ex != null)
{
string user = "";
try
{
user = Managers.UserManager.FindByUsername(filterContext.HttpContext.User.Identity.Name, true).FullName;
}
catch
{
}
var url = filterContext.HttpContext.Request.Url.ToString();
var urlRef = filterContext.HttpContext.Request.UrlReferrer.PathAndQuery;
var randStr = StringExtensions.GenerateRandomAlphaNumerics(new System.Random(), 4);
Logger.Error(ex, "crashCode={0} - user={1} - url={2} - urlRef={3}", randStr, user, url, urlRef);
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
filterContext.ExceptionHandled = true;
filterContext.Result = new RedirectResult("/misc/whoops?crash_code=" + randStr, true);
}
}
}
The problem should be solved by not passing true to the second parameter of RedirectResult. Pass false instead:
filterContext.Result = new RedirectResult("/misc/whoops?crash_code=" + randStr, false);
Passing true as the second parameter indicates the redirect is permanent. Which is, based on your question, exactly want you do not want.
See: MSDN RedirectResult and
301 vs 302 redirects

Orchard CMS ContentManager.New<>() Specified Cast Was Invalid

I am in the early stages of developing a new module.
Much of it is laid out in terms of the models etc. Also have the migrations all set up and my database now has the tables for my module.
I am encountering the following error when calling ContentManager.New<myPart> and would like some help please.
Error is this:
An unhandled exception has occurred and the request was terminated. Please refresh the page. If the error persists, go back
Specified cast is not valid.
System.InvalidCastException: Specified cast is not valid.
at Orchard.ContentManagement.ContentCreateExtensions.New[T]
(IContentManager manager, String contentType)
The chunk of code that fires the exception is this:
public static T New<T>(this IContentManager manager, string contentType) where T : class, IContent {
var contentItem = manager.New(contentType);
if (contentItem == null)
return null;
var part = contentItem.Get<T>();
if (part == null)
throw new InvalidCastException();
return part;
}
Here are the various parts to my module that are related to the operation i am struggling with:
ContentPart
public class GoogleMapsSettingsPart : ContentPart<GoogleMapsSettingsPartRecord>
{
public string ApiKey {
get { return Record.ApiKey; }
set { Record.ApiKey = value; }
}
}
ContentPartRecord
public class GoogleMapsSettingsPartRecord : ContentPartRecord
{
public virtual string ApiKey { get; set; }
}
Handler
public GoogleMapsSettingsPartHandler(IRepository<GoogleMapsSettingsPartRecord> repository)
{
Filters.Add(StorageFilter.For(repository));
}
Migration for this table
// Settings Table
SchemaBuilder.CreateTable("GoogleMapsSettingsPartRecord", table => table
.ContentPartRecord()
.Column("ApiKey", DbType.String, c => c.WithLength(60))
);
Some of the code from the controller for this model etc
public AdminController(IContentManager contentManager, IShapeFactory shapeFactory, IServiceLocatorService serviceLocatorService, INotifier notifier)
{
_contentManager = contentManager;
_serviceLocatorService = serviceLocatorService;
_notifier = notifier;
Shape = shapeFactory;
T = NullLocalizer.Instance;
}
/// <summary>
/// Display Settings
/// </summary>
/// <returns></returns>
public ActionResult Settings()
{
var settings = _serviceLocatorService.GoogleMapsSettings;
var editor = CreateSettingsEditor(settings);
var model = _services.ContentManager.BuildEditor(settings);
return View((object)model);
}
Finally - the Services where my call throws this exception
private GoogleMapsSettingsPart _settings;
public GoogleMapsSettingsPart GoogleMapsSettings
{
get {
if (_settings == null)
{
_settings = _contentManager.Query<GoogleMapsSettingsPart, GoogleMapsSettingsPartRecord>().List().FirstOrDefault();
if (_settings == null)
{
_settings = _contentManager.New<GoogleMapsSettingsPart>("GoogleMapsSettings");
}
}
return _settings;
}
}
The actual line where the exception happens is _settings = _contentManager.New<GoogleMapsSettingsPart>("GoogleMapsSettings");
I have tried all sorts of stuff in place of "GoogleMapsSettings" though nothing is working.
I'm pretty sure at this point it's something simple, though it's avoiding me..My limited knowledge of Orchard is stumping me
Any help would be appreciated :)
The exception is thrown because your content type does not have the part you specified to get.
_contentManager.New<GoogleMapsSettingsPart>("GoogleMapsSettings");
This method call creates a new content item of type GoogleMapSettings and gets the content item as a GoogleMapsSettingsPart. However, it seems that GoogleMapSettings content type does not have a GoogleMapsSettingsPart. That's why the exception gets thrown here.
var part = contentItem.Get<T>();
if (part == null)
throw new InvalidCastException();
You must either attach the part dynamically to your content type or do it in a migration (or manually in the admin, but that's not a good idea). Your migration should look like this.
this.ContentDefinitionManager.AlterTypeDefinition("GoogleMapsSettings",
alt => alt
.WithPart("GoogleMapsSettingsPart");
Ok, so I fixed it...
My understanding of how Orchard works is still very much in the learning stages.
for this particular operation I didn't want to have a content type in the admin - though not sure why after adding the ContentType it still didn't work...
anyway, adding the lines below to my handler took care of the rest. I believe it's actually creating a temporary type so one isn't needed in the system.
public GoogleMapsSettingsPartHandler(IRepository<GoogleMapsSettingsPartRecord> repository)
{
Filters.Add(new ActivatingFilter<GoogleMapsSettingsPart>("GoogleMapsSettings"));
Filters.Add(StorageFilter.For(repository));
Filters.Add(new TemplateFilterForRecord<GoogleMapsSettingsPartRecord>("GoogleMapsSettings", "Parts/GoogleMapsSettings"));
}
I'v got the same error, but in my case it was everything ok with migration class.
The reason was unlucky merge, which deleted my driver class of my part.
Just look at this code of Activating method of ContentPartDriverCoordinator class. In my case there was no partInfo for my content part and resulted part became ContentPart, so casting throws an exception
var partInfos = _drivers.SelectMany(cpp => cpp.GetPartInfo()).ToList();
foreach (var typePartDefinition in contentTypeDefinition.Parts) {
var partName = typePartDefinition.PartDefinition.Name;
var partInfo = partInfos.FirstOrDefault(pi => pi.PartName == partName);
var part = partInfo != null
? partInfo.Factory(typePartDefinition)
: new ContentPart { TypePartDefinition = typePartDefinition };
context.Builder.Weld(part);
}

Elmah not logging exceptions for http post requests in MVC app - if the request contains XML

I've come across a weird problem in my MVC4 (RC) application. (running on .NET 4.0)
I have just setup Elmah for logging exceptions / errors.
I basically installed the Elmah.MVC and elmah.sqlserver NuGet packages. (versions 2.0.0 and 1.2 respectively)
It seemed to work fine out of the box - I can go to the elmah page and view errors:
http://myserver/elmah
for example, if I create some 404 errors, they appear in this log.
What is not working is this: I have a standard MVC controller with a [HttpPost] action. I've set it up so it will always throw an exception:
public class TestController : Controller
{
[HttpPost]
[ValidateInput(false)]
public void Testing()
{
throw new Exception("uh oh");
}
}
I then try to post data to this controller via jQuery:
$.post('/Test/Testing', {test_data: 'This is some test data'});
Ok, this works. The response returns the typical yellow screen of death, and the error is caught and logged in Elmah.
However, if I try to post something like XML/HTML the error is not logged in Elmah. I still get the same response from the server back (yellow screen of death), but nothing in Elmah.
$.post('/Test/Testing', {test_data: '<test><test1>This is some test data</test1></test>'});
Why? It doesn't make sense.
Notice I have already turned off the request validation on the action. If I didn't do that, then posting XML/HTML data would cause this exception:
A potentially dangerous Request.Form value was detected from the client
NuGet would also refuse to log that exception too - which I believe is a bug:
http://code.google.com/p/elmah/issues/detail?id=217
So what is the cause of this problem that I'm experiencing? It it a bug related to the issue I found above?
It just seems quite an unfortunate situation that I can't log exceptions just because the request contained XML/HTML.
Surely there is a way around this?
ELMAH does not catch HttpRequestValidationException by default and if a user sends an invalid request it will be missed in ELMAH's report. so it's necessary to define and use this global filter as well:
public class ElmahRequestValidationErrorFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
if (context.Exception is HttpRequestValidationException)
ErrorLog.GetDefault(HttpContext.Current).Log(new Error(context.Exception));
}
}
I have a work around for now, which someone suggested on http://code.google.com/p/elmah/issues/detail?id=217
You can force ASP to use the older request validation logic by adding this into the <system.web> section of your Web.config:
<httpRuntime requestValidationMode="2.0" />
This will effect the app globally which is not really that great of a situation.
If anyone has something better, please let me know.
Looks like this was fixed after the current release (as of this writing) 1.2.2. I ended up explicitly logging the error in the log. This won't go through the normal processing as unhandled exceptions (email notifications, etc) but it will log it to the error log.
catch (Exception ex)
{
var newEx = new HttpException(500, "Kaboom!!!", ex);
// Adding this explicit ELMAH logging because of ELMAH bug:
// https://code.google.com/p/elmah/issues/detail?id=217
// Waiting for a new ELMAH release with the fix
var elmahError = new Elmah.Error(newEx);
var elmahLog = Elmah.ErrorLog.GetDefault(HttpContext.ApplicationInstance.Context);
elmahLog.Log(elmahError);
throw newEx;
}
There was a bug in Elmah that was fixed a while back, but I don't believe any of the builds on the google code site are recent enough to include it.
As I mentioned in my comment, the issue is that if you access data through the model (MVC) or control (webforms), no problem; but if you access request.Form["inputId"] (which Elmah does when building one object), it throws an exception every time.
What I did:
Export the google code project to my github account
Clone the new repo to my workstation using VS2013
Select the NETFX 4.5 build configuration
Build the solution, and run the demo
Test my case in the demo by adding the following to default.aspx:
Run some tests
After I was happy with my tests, I added the new dlls to my solution by:
Removing the current Elmah.dll reference
Copy/paste Elmah.dll, Elmah.AspNet.dll, and AntiXssLibrary.dll to a 'lib' folder in my solution
Add references to the three new dlls
Update web.config so that it looks like the demo's web.config (things are very similar, but different (Elmah = Elmah.AspNet))
Remove the Elmah.mvc reference (it caused problems for me)
Remove filters.Add(new HandleErrorAttribute()); from FilterConfig (this allows custom errors to continue to work)
In the Elmah source, the crucial piece is the use of request.Unvalidated in HttpRequestValidation.cs
With thanks to Amarisi for the code, I've reworked it into my Global.asax.cs files Application_Error method to log all the exceptions Elmah fails to log.
It looks like this (edited down but should work):
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
// ELMAH will not log exceptions arising from POST requests
// when the form values contains 'potentially dangerous' chars
// e.g. html tags
// Adding this explicit ELMAH logging because of ELMAH bug:
// https://code.google.com/p/elmah/issues/detail?id=217
// Waiting for a new ELMAH release with the fix
// get form object
System.Collections.Specialized.NameValueCollection RequestForm = Request.Unvalidated.Form;
// get combined text of form values
string formValues = string.Join(",", RequestForm.AllKeys.Select(key => RequestForm[key]));
// is this the type of POST that Elmah fails on?
if (formValues.Contains("<"))
{
// log the exception manually
var elmahError = new Elmah.Error(exception);
var elmahLog = Elmah.ErrorLog.GetDefault(Context);
elmahLog.Log(elmahError);
}
}
}
Contains("<") isn't the best test. I doubt it covers everything that Elmah fails to log.
Would love to see if anyone can improve this further.
You can get Elmah to fire by throwing an HttpException instead of a normal exception.
Our solution was to wrap our controller action code with a try/catch block, and in the catch block wrap the exception that is thrown by your code in an HttpException and throw that instead. Like this:
[HttpPost]
[ValidateInput(false)]
public ActionResult MyAction(FormCollection riskyData)
{
try
{
//... your code here ...
}
catch (Exception ex)
{
throw new HttpException(500, "Internal Server Error", ex);
}
}
Add the following classes to your project:
public class ElmahErrorLogModuleFix : ErrorLogModule
{
protected override void LogException(Exception e, HttpContext context)
{
if (e == null)
throw new ArgumentNullException("e");
ExceptionFilterEventArgs args = new ExceptionFilterEventArgs(e, (object)context);
this.OnFiltering(args);
if (args.Dismissed)
return;
ErrorLogEntry entry = (ErrorLogEntry)null;
try
{
//FIX STARTS
//Error error = new Error(e, context);
Error error = CreateErrorSafe(e, context);
//FIX ENDS
ErrorLog errorLog = this.GetErrorLog(context);
error.ApplicationName = errorLog.ApplicationName;
string id = errorLog.Log(error);
entry = new ErrorLogEntry(errorLog, id, error);
}
catch (Exception ex)
{
Trace.WriteLine((object)ex);
}
if (entry == null)
return;
this.OnLogged(new ErrorLoggedEventArgs(entry));
}
public static Error CreateErrorSafe(Exception e, HttpContext context)
{
try
{
var safeFormCollection = new NameValueCollection();
var form = context.Request.Form;
var additionalMessage = string.Empty;
foreach (var key in form.AllKeys)
{
try
{
safeFormCollection.Add(key, form[key]);
}
catch (Exception)
{
safeFormCollection.Add(key, "_invalid input data_");
additionalMessage += "Form parameter with name=" + key + " has dangerous value. " + Environment.NewLine;
}
}
//if no invalid values in form then do as elmah does
if (string.IsNullOrEmpty(additionalMessage))
{
return new Error(e, context);
}
var exception = new Exception(additionalMessage, e);
var error = new Error(exception);
error.HostName = TryGetMachineName(context, null);
IPrincipal user = context.User;
if (user != null && NullString(user.Identity.Name).Length > 0)
error.User = user.Identity.Name;
HttpRequest request = context.Request;
//this._serverVariables = Error.CopyCollection(request.ServerVariables);
error.ServerVariables.Add(CopyCollection(request.ServerVariables));
if (error.ServerVariables != null && error.ServerVariables["AUTH_PASSWORD"] != null)
error.ServerVariables["AUTH_PASSWORD"] = "*****";
error.QueryString.Add(CopyCollection(request.QueryString));
error.Form.Add(CopyCollection(safeFormCollection));
error.Cookies.Add(CopyCollection(request.Cookies));
return error;
}
catch (Exception logEx)
{
return new Error(new Exception("Error when trying to process error catched by elmah", logEx));
}
}
/// <summary>
/// Elmah dll method in Environment.cs
/// </summary>
/// <param name="context"></param>
/// <param name="unknownName"></param>
/// <returns></returns>
public static string TryGetMachineName(HttpContext context, string unknownName)
{
if (context != null)
{
try
{
return context.Server.MachineName;
}
catch (HttpException ex)
{
}
catch (SecurityException ex)
{
}
}
try
{
return System.Environment.MachineName;
}
catch (SecurityException ex)
{
}
return NullString(unknownName);
}
/// <summary>
/// Elmah method in Mask.cs
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public static string NullString(string s)
{
if (s != null)
return s;
else
return string.Empty;
}
/// <summary>
/// Elmah method in Error.cs
/// </summary>
/// <param name="collection"></param>
/// <returns></returns>
private static NameValueCollection CopyCollection(NameValueCollection collection)
{
if (collection == null || collection.Count == 0)
//FIX HERE: cannot allow reutrn null collection as elmah does, because of exception. fix as below
//return (NameValueCollection)null;
return new NameValueCollection();
//FIX ENDS
else
return new NameValueCollection(collection);
}
/// <summary>
/// Elmah method in Error.cs
/// </summary>
/// <param name="cookies"></param>
/// <returns></returns>
private static NameValueCollection CopyCollection(HttpCookieCollection cookies)
{
if (cookies == null || cookies.Count == 0)
//FIX HERE: cannot allow reutrn null collection as elmah does, because of exception. fix as below
//return (NameValueCollection)null;
return new NameValueCollection();
//FIX ENDS
NameValueCollection nameValueCollection = new NameValueCollection(cookies.Count);
for (int index = 0; index < cookies.Count; ++index)
{
HttpCookie httpCookie = cookies[index];
nameValueCollection.Add(httpCookie.Name, httpCookie.Value);
}
return nameValueCollection;
}
}
and
public class ElmahErrorMailModuleFix : ErrorMailModule
{
private bool _reportAsynchronously2;
protected override void OnInit(HttpApplication application)
{
base.OnInit(application);
IDictionary config = (IDictionary)this.GetConfig();
if (config == null)
return;
_reportAsynchronously2 = Convert.ToBoolean(GetSetting(config, "async", bool.TrueString));
}
protected override void OnError(Exception e, HttpContext context)
{
if (e == null)
throw new ArgumentNullException("e");
ExceptionFilterEventArgs args = new ExceptionFilterEventArgs(e, (object)context);
this.OnFiltering(args);
if (args.Dismissed)
return;
//FIX STARTS
//Error error = new Error(e, context);
Error error = ElmahErrorLogModuleFix.CreateErrorSafe(e, context);
//FIX ENDS
if (this._reportAsynchronously2)
this.ReportErrorAsync(error);
else
this.ReportError(error);
}
/// <summary>
/// Elmah method in ErrorMailModule.cs
/// </summary>
/// <param name="config"></param>
/// <param name="name"></param>
/// <param name="defaultValue"></param>
/// <returns></returns>
private static string GetSetting(IDictionary config, string name, string defaultValue)
{
string str = ElmahErrorLogModuleFix.NullString((string)config[(object)name]);
if (str.Length == 0)
{
if (defaultValue == null)
throw new global::Elmah.ApplicationException(string.Format("The required configuration setting '{0}' is missing for the error mailing module.", (object)name));
str = defaultValue;
}
return str;
}
}
These classes inherit from ErrorLogModule and ErrorMailModule and rewrite methods where the Error class is created, so that the HttpRequestValidationException exception will not raise.
Then add these to your Web.config:
<add name="ErrorLog" type="YourProject.SomeFolder.ElmahErrorLogModuleFix, YourProject" preCondition="managedHandler" />
<!--and for email module-->
to use these classes instead of the original ones. A bit of a dirty hack, but it works.
Credit goes to the poster of message #17 found here.

Single action class for multiple dynamic URIs throws exception when concurrent requesting

I've developed a web site using Struts2 as a controller and integrated it with Spring and Hibernate to do the business logic and DB stuff. The website's URIs are http://my.domian.com/URI; which {URI} is dynamically generated thorough the admin cms. The mapping of each uri to the servlet are done with help of Apache mod_rewrite, as follow:
RewriteCond %{HTTP_HOST} ^www\.domain\.com
RewriteRule ^([a-zA-Z0-9_-]+)$ /dynamic\.action?f=$1 [QSA,L]
(Before any further information, is this a good and suitable approach?)
The struts configuration is just a typically-academic one as:
<package name="Default" extends="struts-default" namespace="/">
...
<action name="dynamic" class="DynamicContentAction">
<result name="index">/content/web/dynamic/index.jsp</result>
</action>
</package>
DynamicContentAction is extending ActionSupport and implementing ServletRequestAware, ServletContextAware. I'm checking a couple of things (such as a current visiting language which is identified as a subdomain), looking up in the database that the requested uri is valid or not, generating that uri's content and setting a couple of runtime global variables (such as current visiting page id, layout config due to current visiting language ...) and put it on a Request object in this servlet.
Everything looks good and even works perfectly ok, unless too many dynamic pages being requested by a single user concurrently. "Too Many" in my case is at least 9-10 pages. In this case it throws exceptions, different ones! Sometimes the HttpServletRequest request is null, sometimes ServletContext servletContext is null, some other times these are ok, but the runtime variables are null which is used in business logic or db querying.
I've googled about it and found out that this action is being instantiated "Per Request". Isn't this so? If there is an action per request, what's wrong with this conflict or "nullability thing". Should I do some thread-like thing in that action, beyond the threading of struts?
I'd be so appreciated if you could help me out or point me a direction.
Here is simplified version of DynamicContentAction.java
public class DynamicContentAction extends ActionSupport implements ServletRequestAware, ServletContextAware {
private HttpServletRequest request;
private ServletContext servletContext;
private ResourceSelectorService resourceSelectorService;
private String f = null;
public String execute() {
if ( f != null ) {
HashMap<String, Object> resolvedURI = resourceSelectorService.resolveURI(f);
if ( resolvedURI.get("ERROR").equals(true) ) {
//Generating nice 404 error page content
} else {
//Generating Content
//and put it on request object as:
//request.setAttribute("attrName", resourceContent);
}
}
else {
//Generating nice 404 error page content
}
request = null;
servletContext = null;
f = null;
return "index";
}
#Override
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
#Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
public void setF(String f) {
this.f = f;
}
public String getF() {
return f;
}
}
as I'm writing this post, I have came to the knowledge that this class is NOT thread-safe. Is it? So I've changed it a little bit as follow:
Here is newer version of DynamicContentAction.java
public class DynamicContentAction extends ActionSupport {
private ResourceSelectorService resourceSelectorService;
private String f = null;
public String execute() {
if ( f != null ) {
final HttpServletRequest request = ServletActionContext.getRequest();
final ServletContext servletContext = ServletActionContext.getServletContext();
HashMap<String, Object> resolvedURI = resourceSelectorService.resolveURI(f);
if ( resolvedURI.get("ERROR").equals(true) ) {
//Generating nice 404 error page content
} else {
//Generating Content
//and put it on request object as:
//request.setAttribute("attrName", resourceContent);
}
f = null;
}
else {
//Generating nice 404 error page content
}
return "index";
}
public void setF(String f) {
this.f = f;
}
public String getF() {
return f;
}
}
and the Null thing problem is almost gone, but there is still conflict with the generated content. For example if user try to open:
http:// www.domain.com/A
http:// www.domain.com/B
http:// www.domain.com/C
http:// www.domain.com/D
http:// www.domain.com/E
simultaneously, all of the pages will be rendered in browser, but the content of A is shown in A and B, C is correct, and there is a very good chance that the content of D and E are incorrect too.

Resources