struts action class and business service layer - struts

I have a question in handling the "business validation" done on the service layer. The code belows shows a typical account fund transfer example, which validates for sufficient fund, transfer amount is lesser than defined limit.
In this example, the caller have to handle and catch the exception defined in the Action class and used the corresponding ActionError to display the error message.
It is a "must" to use exceptions for all business validation?
If I decide not to use exception for this, I will have to defined the corresponding ActionError in the business layer(which violates the coupling / cohesion) rule in a certain sense.
How should one handle the message propagating by service layer back to the Action classes?
public void transfer(String fromAccount, String toAccount, double amount) throws InsufficientFundsException, TransferLimitException, FactoryException {
try {
Account from = getAccountHome().findByPrimaryKey(
new AccountKey(fromAccount));
Account to = getAccountHome().findByPrimaryKey(
new AccountKey(toAccount));
if (from.getBalance() < amount)
throw new InsufficientFundsException(); // Add action errors
if (from.getTransferLimit() > amount)
throw new TransferLimitException(); // Add action errors
to.deposit(amount);
from.withdraw(amount);
} catch (Exception e) {
throw new FactoryException(
"cannot perform transfer. Nested exception is " + e);
}
}

Your business should be handled in the Model and any problems encountered in your business logic should be propagated to the caller, which in this case is Struts Action classes.
But you do not want to couple your Struts classes (Action, ActionForm, ActionError, ActionMessage etc) with the Model, so you basically have two ways of notifying the caller of any problems:
return some error codes that the caller can check;
throw some exceptions that the caller can catch.
I prefer using exceptions because they can be thrown to the top layers from anywhere inside the business layer no matter how deep in the execution chain. This keeps the business code clean as you don't have to bubble up error codes as it would have been the case with the first approach.
The exceptions will then be caught by the Action class who will transform them into ActionError objects to be shown in the View. Just make sure you don't overdo it and end up throwing the kitchen sink or your Action classes will become crowded with excessive try-catch blocks. Additionally you could let the exceptions propagate and create an exception handler of some sort that catches all exceptions thrown from below and redirects to appropriate Views based on the exception type.

Related

How to fail Velocity template processing with tracable message

Having:
Velocity template or macro
some object
How to validate the object (#if) and fail (stop further processing) in a way that is easily tracable to the place of failure (like throwing exception in Java).
I am looking for something like this:
#if ( ! $context.treasureMap.containsKey('gold'))
#fail('no golden treasure here')
#end
Background
I am writing a maven site page. The velocity context is injected by maven and contains POM information. I want to test existence of some information from effective pom. When the information is not available, I want to fail.
Requirements
fail Velocity processing > fail site generation > fail maven build.
error message should lead to the place of failure so the site should be fixed
preferably no configuration (no extensions, just constructs/tools contained in plain Velocity)
Tried
Strict Reference Mode
Unwanted configuration, do not want to fail on every occasion.
#evaluate('#end') aka syntax error
(Chose #end as most descriptive to my intent) Basically what I want. Fails the processing and maven build but the error message does not lead back to failure location:
ParseException: Encountered "#end" at line 1, column 1..
You need to make a method call which produce exception.See explanation:
The only place where one could run into trouble within Velocity is if there is a call to a method which throws an exception during runtime. For example, this VTL defines a String $foo and then attempts to call its substring() method on it would throw an IndexOutOfBoundsException:
#set ($foo = "bar")
#set ($bar = $foo.substring(0,10))
When the exception is thrown, the parser will stop processing and throw that exception up the stack tree where it can be caught in the method that caused the parser to execute. At that point, the exception can be handled gracefully.

NHibernate SaveOrUpdate throws NonUniqueObjectException exception

I am currently fixing an old Windows Applications and encountered NHibernate error. I've read and tried few things on the net but end up error.
Here is my code for the ISession:
Public ReadOnly Property session() As ISession
Get
If IsNothing(m_session) Then
m_session = Factory.InitConfiguration.OpenSession()
End If
Return m_session
End Get
End Property
Here is my code for the save button:
Try
session.BeginTransaction()
SetParent(x_object)
'session.clear()
session.Flush()
session.SaveOrUpdate(x_object)
session.Transaction.Commit()
compObj.IsNew = False
Return True
Catch ex As Exception
AppServices.ErrorMessage = ex.Message
session.Transaction.Rollback()
Return False
Finally
'TBA
End Try
So the problem start here, I have this date column as DateTime and AttachmentList.
The current code doesn't have any problem until the user key in the year less than 1753. However the code catch the error properly and display the message and when the user continue to fix the year-typo, it'll still catch the error (while at the watch I already get the new value) until the user close the application and reopened it.
However if I uncomment the session.clear(), it will do just fine, the user may fix their typos and continue to save record, but then when the user do the other action lets say attachment, it will get another error. The attachment action as below:
Add Attachment
Click Save button
Add new attachment
Click Save button
New Error.
So please advice me on what need to be done. I've tried merge, I've tried update,save, evict but end up error. I think my problem is how I arrange the session is the main source of the problem.
It looks like the code you have to handle tries to go on using a session having experienced a failed flush or transaction commit.
This is an anti-pattern. From NHibernate reference:
NHibernate use might lead to exceptions, usually HibernateException.
This exception can have a nested inner exception (the root cause), use
the InnerException property to access it.
If the ISession throws an exception you should immediately rollback
the transaction, call ISession.Close() and discard the ISession
instance. Certain methods of ISession will not leave the session in a
consistent state.
...
The following exception handling idiom shows the typical case in NHibernate applications:
using (ISession sess = factory.OpenSession())
using (ITransaction tx = sess.BeginTransaction())
{
// do some work
...
tx.Commit();
}
Or, when manually managing ADO.NET transactions:
ISession sess = factory.openSession();
try
{
// do some work
...
sess.Flush();
currentTransaction.Commit();
}
catch (Exception e)
{
currentTransaction.Rollback();
throw;
}
finally
{
sess.Close();
}
You must ensure the code does not try to go on using the session after an exception.
Furthermore, it looks like the session stay opened while awaiting user interaction: this is not a recommended pattern. A NHibernate session is normally short lived. Opening a session is cheap.
An usual pattern is to open it when starting to process an event from user input and close it before ending the event processing, in order to not leaving it opened while the user is gone taking a coffee.
Now you may have a hard time changing the application session management, especially if the application retains references to entities and expects them to be still bound to an opened session after having waited for user interaction.
If the choice of keeping the session opened between user interaction was done for "leveraging first level cache" (session entity cache), consider activating second level cache instead.

Suppressing NServicebus Transaction to write errors to database

I'm using NServiceBus to handle some calculation messages. I have a new requirement to handle calculation errors by writing them the same database. I'm using NHibernate as my DAL which auto enlists to the NServiceBus transaction and provides rollback in case of exceptions, which is working really well. However if I write this particular error to the database, it is also rolled back which is a problem.
I knew this would be a problem, but I thought I could just wrap the call in a new transaction with the TransactionScopeOption = Suppress. However the error data is still rolled back. I believe that's because it was using the existing session with has already enlisted in the NServiceBus transaction.
Next I tried opening a new session from the existing SessionFactory within the suppression transaction scope. However the first call to the database to retrieve or save data using this new session blocks and then times out.
InnerException: System.Data.SqlClient.SqlException
Message=Timeout expired. The timeout period elapsed prior to completion of the >operation or the server is not responding.
Finally I tried creating a new SessionFactory using it to open a new session within the suppression transaction scope. However again it blocks and times out.
I feel like I'm missing something obvious here, and would greatly appreciate any suggestions on this probably common task.
As Adam suggests in the comments, in most cases it is preferred to let the entire message fail processing, giving the built-in Retry mechanism a chance to get it right, and eventually going to the error queue. Then another process can monitor the error queue and do any required notification, including logging to a database.
However, there are some use cases where the entire message is not a failure, i.e. on the whole, it "succeeds" (whatever the business-dependent definition of that is) but there is some small part that is in error. For example, a financial calculation in which the processing "succeeds" but some human element of the data is "in error". In this case I would suggest catching that exception and sending a new message which, when processed by another endpoint, will log the information to your database.
I could see another case where you want the entire message to fail, but you want the fact that it was attempted noted somehow. This may be closest to what you are describing. In this case, create a new TransactionScope with TransactionScopeOption = Suppress, and then (again) send a new message inside that scope. That message will be sent whether or not your full message transaction rolls back.
You are correct that your transaction is rolling back because the NHibernate session is opened while the transaction is in force. Trying to open a new session inside the suppressed transaction can cause a problem with locking. That's why, most of the time, sending a new message asynchronously is part of the solution in these cases, but how you do it is dependent upon your specific business requirements.
I know I'm late to the party, but as an alternative suggestion, you coudl simply raise another separate log message, which NSB handles independently, for example:
public void Handle(DebitAccountMessage message)
{
var account = this.dbcontext.GetById(message.Id);
if (account.Balance <= 0)
{
// log request - new handler
this.Bus.Send(new DebitAccountLogMessage
{
originalMessage = message,
account = account,
timeStamp = DateTime.UtcNow
});
// throw error - NSB will handle
throw new DebitException("Not enough funds");
}
}
public void Handle(DebitAccountLogMessage message)
{
var messageString = message.originalMessage.Dump();
var accountString = message.account.Dump(DumpOptions.SuppressSecurityTokens);
this.Logger.Log(message.UniqueId, string.Format("{0}, {1}", messageString, accountString);
}

WCF throwing CommunicationException when FaultException is thrown

Solution:
A bit of tracing showed the CommunicationException was being thrown because there was an issue with my exception T not serializing correctly; because, two layers deep, I had an anonymously typed object, which was unserializable. Removing it and bubbling up the changes appeared to fix it. There was somethinge else small I did before that, but I can't remember for the life of me what it was, only that it wasn't done in the config.
I was getting messages from my traces such as:
Type 'RebuiltWCFService.Requests.HelloWorldRequest' with data contract name 'HelloWorldRequest:http://schemas.datacontract.org/2004/07/RebuiltWCFService.Requests' is not expected.
Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.
Original post
I've encountered a seemingly strange issue today that I just cant't find an answer to!
The problem: my service is throwing a CommunicationException when I throw a FaultException! It does not do this if I don't throw an exception.
In my service, I'm properly defining the fault contracts:
[OperationContract]
[FaultContract(typeof(Faults.HelloWorldFault))]
Responses.HelloWorldResponse HelloWorld(Requests.HelloWorldRequest parameter);
Then under error conditions I'm throwing an exception of the correct type:
if (_errors.Count() > 0)
{
Faults.HelloWorldFault fault = new Faults.HelloWorldFault(_errors);
throw new FaultException<Faults.HelloWorldFault>(fault, new FaultReason("There are one or more errors in the request. Check the 'Errors' property for more detaisl"));
}
And then I'm catching it on the client end:
try
{
response = client.HelloWorld(new BasicService.HelloWorldRequest() { Number = 49 });
client.Close();
Console.WriteLine(String.Format("Response message: {0}, Response number: {1}", response.Message, response.Number));
}
catch (FaultException<BasicService.HelloWorldFault> ex)
{
...
}
That all seems OK to me, and like it should work. However, as soon as I go to test my error clauses (by providing bad data, such as a missing field), the whole thing dies on me. When I throw my FaultException, the service instead throws a CommunicationException with the message
An error occurred while receiving the HTTP response to http://localhost:8732/Design_Time_Addresses/RebuiltWCFService/Service1/.
This could be due to the service endpoint binding not using the HTTP protocol.
This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down).
See server logs for more details.
Can anybody offer some insight on this one? I am using the basicHttp binding, and I've also tried it with wsHttp. I will post my config file upon request.
A FaultException is a child of CommunicationException. So there is nothing wrong in what's happening in your code.
If you are uncertain about what the exception is while handling, it is usually reported as CommunicationException. If you want to handle your specific exception in your own way, use the following structure.
try
{ ... }
catch (FaultException<MyDemoException> me)
{ ... }
catch (FaultException fe)
{ ... }
catch (CommunicationException ce)
{ ... }
catch (Exception ex)
{ ... }
In the above structure, Exception is parent of CommunicationException. CommunicationException is the parent of FaultException and so on.
System.Object
System.Exception
System.SystemException
System.ServiceModel.CommunicationException
System.ServiceModel.FaultException
System.ServiceModel.FaultException<TDetail>
System.ServiceModel.Web.WebFaultException<T>
Thanks guys, you helped me to understand the issue in a better way. My observation: I kept a List MyData - to hold any generic/dynamic collection, that was unable to serialize, so resulting to close the connection, so the communication exception.
When I removed the List from Fault Contract, and throw Fault style, it was clearly giving Fault contract exception, instead of communication exception.
Hope it helps,
HydTechie.
I ran into this problem because I had one or more breakpoints in the wrong place. I removed all breakpoints with Debug..Delete all Breakpoints (Ctrl-Alt-F9), and all of the CommunicationException exceptions disappeared and were replaced with the correct messages coming back.
Yes, the timeout is 60 seconds, so this never should have occurred, so it was probably some weird artifact of Visual Studio 2012.

nHibernate does not throw exceptions when expected (invoking Save on entity in a collection)?

I am just getting started using nHibernate, and some behavior is a bit disturbing, which is it seemingly does not throw exceptions when you try to perform some action when you have either coded it oncorrectly, or have a configuration error.
For example, I have some code:
ISessionFactory sessions = SessionManager.SessionFactory;
ISession session = sessions.OpenSession();
ITransaction tx1 = null;
tx1 = session.BeginTransaction();
IList<BusinessUnit> businessUnits = session.QueryOver<BusinessUnit>().List();
System.Diagnostics.Debug.WriteLine("Count: " + businessUnits.Count);
businessUnits[0].Name += "z";
session.Save(businessUnits[0]);
if (tx1 != null) tx1.Commit();
session.Close();
When I first coded it, I did not include the usage of a transaction:
tx1 = session.BeginTransaction();
if (tx1 != null) tx1.Commit();
So, apparently, if a transaction is not present, nHibernate doesn't (at least according to my configuration) automatically create one. Which is fine, I suppose.
But what bothers me is, if a transaction is required, and you try to perform a Save() without one, why doesn't it throw an exception?
(So far I have encountered another similar issue, I tried to load a collection, but apparently some error in my configuration prevented a successful load...so the code executes, but no exception indicating something has gone wrong).
Is there perhaps some config setting I am missing where exceptions are suppressed, or does a person have to unit test everything as no behavior can necessarily be assumed to work?
On a pre-existing object, a Save() is not automatically committed to the database, in the example above, it requires you to call:
Session.flush();
If one uses a transaction, then calling .Commit() on the transaction then invokes a flush().
So the reason no exception is thrown is because caching is default behavior by design (and if the programmer isn't aware of that then they will be very confused!!!)