Server: WildFly10
JPA with eclipseLink 2.6.3-M1
JavaEE7
I have the following EJB:
#Stateless
#LocalBean
public class HandleRollbackComponent {
private static Logger logger = Logger.getLogger(HandleRollbackComponent.class);
#EJB
private Tws14WSBatchChRequestsFacade tws14wsBatchChRequestsFacade;
public void doSomething() {
// first off go and fetch an instance of tws14 from the db
logger.debug("*************************************************");
logger.debug("1. First off go and fetch an instance of tws14 from the db");
String batchChReqId = "103";
Tws14WSBatchChRequests tws14wsBatchChRequests = tws14wsBatchChRequestsFacade.find(new BigDecimal(batchChReqId));
logger.debug("2. Found instance of tws14: " + tws14wsBatchChRequests);
logger.debug("2.1 CARD PLASTIC : " + tws14wsBatchChRequests.getCardPlastic());
try {
logger.debug("3. Now call a method that throws the EJBTrxnRollBackException....");
doSomethingThatThrowsEJBTransactionRolledbackException(tws14wsBatchChRequests);
logger.debug("---> This line should not be logged if exception was thrown....");
} catch (Exception e) {
logger.debug("5. Caught the exception....");
} finally {
logger.debug("6. Finally try and get a fresh instance from the db again...");
tws14wsBatchChRequests = tws14wsBatchChRequestsFacade.find(new BigDecimal(batchChReqId));
logger.debug("7. Was able to get instance from db: " + tws14wsBatchChRequests);
logger.debug("8. Try and update the instance of tws again...");
tws14wsBatchChRequestsFacade.edit(tws14wsBatchChRequests);
logger.debug("9. Could update the instance without problems.....");
logger.debug("10. Check the OrderCards value: " + tws14wsBatchChRequests.getOrderCards() );
}
logger.debug("11. Done...");
}
public void doSomethingThatThrowsEJBTransactionRolledbackException(Tws14WSBatchChRequests tws14wsBatchChRequests) {
logger.debug("4. Set some invalid values on tws14 in an attempt to get exception thrown...");
tws14wsBatchChRequests.setOrderCards("N");
tws14wsBatchChRequests.setOrderCards("");
tws14wsBatchChRequests.setCardPlastic(null);
tws14wsBatchChRequestsFacade.edit(tws14wsBatchChRequests);
}
}
When I call doSomething() this is what I see:
First off go and fetch an instance of tws14 from the db
Found instance of tws14: za.co.fnds.persistence.entities.Tws14WSBatchChRequests[ batchChRequestId=103 ]
2.1 CARD PLASTIC : NBCRFLI_PIN
Now call a method that throws the EJBTrxnRollBackException....
Set some invalid values on tws14 in an attempt to get exception thrown...
---> This line should not be logged if exception was thrown....
Finally trying to get a fresh instance from the db again...
Was able to get instance from db: za.co.fnds.persistence.entities.Tws14WSBatchChRequests[ batchChRequestId=103 ]
Try and update the instance of tws again...
Could update the instance without problems.....
Check the OrderCards value:
Done...
My question is why is the program not going into the catch clause because my logs indicates that a javax.validation.ConstraintViolationException was thrown. Why is the bold log above still show? What am I missing? Is there a way I'm supposed to be handling this program structure in an EJB?
To verify that the implementation is wrong is the method doSomethingThatThrowsEJBTransactionRolledbackException. You can explicitly throw the exception and see if the cath works.
public void doSomething() {
try {
doSomethingThatThrowsEJBTransactionRolledbackException(new Tws14WSBatchChRequests());
} catch (Exception e) {
logger.warn(e.getMessage(), e);
}
}
public void doSomethingThatThrowsEJBTransactionRolledbackException(Tws14WSBatchChRequests tws14wsBatchChRequests) {
throw new EJBTransactionRolledbackException();
}
If the exception is catching, then your code is not throwing anything
Related
I am trying to learn how to handle exception in APIs servers so I followed this where he has built API for birds, he finally reached to APIs like this:
#GetMapping(value = "/params")
public Bird getBirdRequestParam(#RequestParam("birdId") Long birdId) throws EntityNotFoundException {
Bird bird = birdRepository.findOne(birdId);
if(bird == null){
throw new EntityNotFoundException(Bird.class, "id", birdId.toString());
}
return bird;
}
and the ControllerAdvice has a method:
#ExceptionHandler(EntityNotFoundException.class)
protected ResponseEntity<Object> handleEntityNotFound(
EntityNotFoundException ex) {
ApiError apiError = new ApiError(NOT_FOUND);
apiError.setMessage(ex.getMessage());
return buildResponseEntity(apiError);
}
the response will be a bird like this
{
"id": 1,
"scientificName": "Atlantic canary",
"specie": "serinus canaria",
"mass": 10,
"length": 11
}
or an exception details like this:
{
"apierror": {
"status": "NOT_FOUND",
"timestamp": "09-04-2018 10:11:44",
"message": "Bird was not found for parameters {id=2}"
}
but the problem is with my server that contacts with API
I am using :
public void gett(#RequestParam Long id) {
ResponseEntity<Bird> responseEntity = restTemplate.getForEntity("http://localhost:8181/params" + id, Bird.class);
bird = responseEntity.getBody();
model.addAttribute("birdform", bird);
return "bird";
}
the getForEntity is waiting for a response with bird body but an exception may be thrown in server and the response may be json of the error.
how to handle this problem in my client server?
in other words :
how to know in my client server that the api server has thrown an exception in json form.???
I have tried to get the response in "Object" variable and then try to know if it was excption or bird with "instance of" expression like this code
#GetMapping("/getbird")
public String getAll(#RequestParam Long id, Model model) {
ResponseEntity<Object> responseEntity = restTemplate.getForEntity("http://localhost:8181/api/bird/getone?id=" + id, Object.class);
if (responseEntity.getBody() instanceof Bird.class) {
Bird bird= (Bird) responseEntity.getBody();
model.addAttribute("Bird", bird);
return "bird-form";
}
else{
// something else
return "someview";
}
}
but first thing it didnot work (the instance of always return false)
the second thing is that this is a hard work to do with all my controllers' actions.
I hope that i could explain my problem clearly .
thanks....
You don't need to check the body of the response to see if you got an error. restTemplate.getForEntity() (and other methods) would throw an HttpClientErrorException if you get a 4XX response, or HttpServerErrorException if you get a 5XX response from the call.
To catch these exceptions, you can define a #ControllerAdvice with proper methods for exception handling:
#ControllerAdvice
class GlobalControllerExceptionHandler {
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) // 500
#ExceptionHandler(HttpClientErrorException.class)
public ApiError handle4xxFromClient(HttpClientErrorException ex) {
// construct and return a custom ApiError object
}
}
See https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc for details.
Two solutions :
Use a custom implementation error handler in your reste template.
restTemplate.setErrorHandler(customerErrorHandler)
Use the http status, rest template will throw an exception if it gets a bad http status from the server it is querying. Catch the exception and get the ApiError Object from the exception, and then throw and exception of your own that will be handled by your exception handler in your client server.
For these solutions to work, the server your are querying needs to send the right http status code when something wrong happens.
As soon as my code gets to my while(rs.next()) loop it produces the ResultSet is closed exception. What causes this exception and how can I correct for it?
EDIT: I notice in my code that I am nesting while(rs.next()) loop with another (rs2.next()), both result sets coming from the same DB, is this an issue?
Sounds like you executed another statement in the same connection before traversing the result set from the first statement. If you're nesting the processing of two result sets from the same database, you're doing something wrong. The combination of those sets should be done on the database side.
This could be caused by a number of reasons, including the driver you are using.
a) Some drivers do not allow nested statements. Depending if your driver supports JDBC 3.0 you should check the third parameter when creating the Statement object. For instance, I had the same problem with the JayBird driver to Firebird, but the code worked fine with the postgres driver. Then I added the third parameter to the createStatement method call and set it to ResultSet.HOLD_CURSORS_OVER_COMMIT, and the code started working fine for Firebird too.
static void testNestedRS() throws SQLException {
Connection con =null;
try {
// GET A CONNECTION
con = ConexionDesdeArchivo.obtenerConexion("examen-dest");
String sql1 = "select * from reportes_clasificacion";
Statement st1 = con.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY,
ResultSet.HOLD_CURSORS_OVER_COMMIT);
ResultSet rs1 = null;
try {
// EXECUTE THE FIRST QRY
rs1 = st1.executeQuery(sql1);
while (rs1.next()) {
// THIS LINE WILL BE PRINTED JUST ONCE ON
// SOME DRIVERS UNLESS YOU CREATE THE STATEMENT
// WITH 3 PARAMETERS USING
// ResultSet.HOLD_CURSORS_OVER_COMMIT
System.out.println("ST1 Row #: " + rs1.getRow());
String sql2 = "select * from reportes";
Statement st2 = con.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
// EXECUTE THE SECOND QRY. THIS CLOSES THE FIRST
// ResultSet ON SOME DRIVERS WITHOUT USING
// ResultSet.HOLD_CURSORS_OVER_COMMIT
st2.executeQuery(sql2);
st2.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
rs1.close();
st1.close();
}
} catch (SQLException e) {
} finally {
con.close();
}
}
b) There could be a bug in your code. Remember that you cannot reuse the Statement object, once you re-execute a query on the same statement object, all the opened resultsets associated with the statement are closed. Make sure you are not closing the statement.
Also, you can only have one result set open from each statement. So if you are iterating through two result sets at the same time, make sure they are executed on different statements. Opening a second result set on one statement will implicitly close the first.
http://java.sun.com/javase/6/docs/api/java/sql/Statement.html
The exception states that your result is closed. You should examine your code and look for all location where you issue a ResultSet.close() call. Also look for Statement.close() and Connection.close(). For sure, one of them gets called before rs.next() is called.
You may have closed either the Connection or Statement that made the ResultSet, which would lead to the ResultSet being closed as well.
Proper jdbc call should look something like:
try {
Connection conn;
Statement stmt;
ResultSet rs;
try {
conn = DriverManager.getConnection(myUrl,"","");
stmt = conn.createStatement();
rs = stmt.executeQuery(myQuery);
while ( rs.next() ) {
// process results
}
} catch (SqlException e) {
System.err.println("Got an exception! ");
System.err.println(e.getMessage());
} finally {
// you should release your resources here
if (rs != null) {
rs.close();
}
if (stmt != null) {
stmt.close();
}
if (conn != null) {
conn.close();
}
}
} catch (SqlException e) {
System.err.println("Got an exception! ");
System.err.println(e.getMessage());
}
you can close connection (or statement) only after you get result from result set. Safest way is to do it in finally block. However close() could also throe SqlException, hence the other try-catch block.
I got same error everything was correct only i was using same statement interface object to execute and update the database.
After separating i.e. using different objects of statement interface for updating and executing query i resolved this error. i.e. do get rid from this do not use same statement object for both updating and executing the query.
Check whether you have declared the method where this code is executing as static. If it is static there may be some other thread resetting the ResultSet.
make sure you have closed all your statments and resultsets before running rs.next. Finaly guarantees this
public boolean flowExists( Integer idStatusPrevious, Integer idStatus, Connection connection ) {
LogUtil.logRequestMethod();
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = connection.prepareStatement( Constants.SCRIPT_SELECT_FIND_FLOW_STATUS_BY_STATUS );
ps.setInt( 1, idStatusPrevious );
ps.setInt( 2, idStatus );
rs = ps.executeQuery();
Long count = 0L;
if ( rs != null ) {
while ( rs.next() ) {
count = rs.getLong( 1 );
break;
}
}
LogUtil.logSuccessMethod();
return count > 0L;
} catch ( Exception e ) {
String errorMsg = String
.format( Constants.ERROR_FINALIZED_METHOD, ( e.getMessage() != null ? e.getMessage() : "" ) );
LogUtil.logError( errorMsg, e );
throw new FatalException( errorMsg );
} finally {
rs.close();
ps.close();
}
A ResultSetClosedException could be thrown for two reasons.
1.) You have opened another connection to the database without closing all other connections.
2.) Your ResultSet may be returning no values. So when you try to access data from the ResultSet java will throw a ResultSetClosedException.
It happens also when using a ResultSet without being in a #Transactional method.
ScrollableResults results = getScrollableResults("select e from MyEntity e");
while (results.next()) {
...
}
results.close();
if MyEntity has eager relationships with other entities. the second time results.next() is invoked the ResultSet is closed exception is raised.
so if you use ScrollableResults on entities with eager relationships make sure your method is run transactionally.
"result set is closed" happened to me when using tag <collection> in MyBatis nested (one-to-many) xml <select> statement
A Spring solution could be to have a (Java) Spring #Service layer, where class/methods calling MyBatis select-collection statements are annotated with
#Transactional(propagation = Propagation.REQUIRED)
annotations being:
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
this solution does not require to set the following datasource properties (i.e., in JBoss EAP standalone*.xml):
<xa-datasource-property name="downgradeHoldCursorsUnderXa">**true**\</xa-datasource-property>
<xa-datasource-property name="resultSetHoldability">**1**</xa-datasource-property>
We have background job running that requires a user to be active. The following way works fine but we have to hard-code the password which is not ideal.
try {
session = repository.login(new SimpleCredentials("admin", "admin".toCharArray()));
} catch (RepositoryException ex) {
log.error("SessionHelper - login issue", ex);
}
We attempt a better way to set active user without setting password as follows:
Map<String, Object> params = new HashMap<String, Object>();
params.put(ResourceResolverFactory.SUBSERVICE, "theService");
ResourceResolver resolver = null;
try {
resolver = resolverFactory.getServiceResourceResolver(params);
} catch (LoginException e) {
log.error("LoginException", e);
}
Session session = resolver.adaptTo(Session.class);
// Next, create pages and add properties ...
We then try to create pages and set properties. This works fine for couple of milliseconds where some pages are created but then throws Exception to indicate session is closed although never closed and the location where exception gets thrown is unpredictable.
javax.jcr.RepositoryException: This session has been closed. See the chained exception for a trace of where the session was closed.
...
Caused by: java.lang.Exception: Stack trace of where session-admin-20077 was originally closed
We want to know whether there is any way to set the timeout? Any recommendations appreciated.
You should obtain the session always through the ResourceResolverFactory using the getServiceResourceResolver method (getAdministrativeResourceResolver method is actually deprecated and should be avoided), execute than your code in the same try/catch block and define a finally block where you can make sure that the obtained resolver/session is closed properly. If you follow this princip, you will probably never experience problems with closed or unclosed sessions.
#org.apache.felix.scr.annotations.Component(...)
public class MyComponent {
#org.apache.felix.scr.annotations.Reference
private org.apache.sling.api.resource.ResourceResolverFactory resourceResolverFactory;
public void myaction() {
org.apache.sling.api.resource.ResourceResolver resolver = null;
try {
Map<String, Object> authInfo = new HashMap<String, Object>();
authInfo.put(ResourceResolverFactory.SUBSERVICE, getClass.getName());
resolver = resourceResolverFactory.getServiceResourceResolver(authInfo);
javax.jcr.Session session = resolver.adaptTo(javax.jcr.Session.class);
javax.jcr.Node node = session.getNode("/jcr/path/to/the-node");
// do something with the node
session.save();
} catch(LoginException e) {
// Handle cannot obtain instance of the resource resolver
} catch(RepositoryException e) {
//handle the repository exception
} finally {
//do not forget to close the resolver, otherwise this can cause huge performance problems
if(resolver != null) {
resolver.close();
}
}
}
}
In order to obtain service resource resolver, you need also to configure the user.mapping in the OSGI-Service org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl for example as follows.:
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="sling:OsgiConfig"
user.mapping="[tld.mycompany.mypackage=admin]"
user.default="admin"/>
This way you can set and offer very advanced access control policies for your services.
If you are in a sling servlet, be carefull which resource resolver you are using. In the normal case, you will not need a resource resolver with administrative rights, but take the one provided by the SlingHttpServletRequest. The resource resolver is closed by sling at the end of the request, don't close it manually.
If you are using the admin session I suggest to not log it in like you did, but with the following method (asuming from your code you have the SlingRepository already injected with #Reference):
repository.loginAdministrative(null);
And to prevent the error I would use the following pattern:
Session session = null;
try {
session = repository.loginAdministrative(null);
//do what you need to do
} catch (RepositoryException e) {
//handle exception
} finally {
if (session != null && session.isLive()) {
session.logout();
}
}
And one last note, open and close the sessionin the same thread, so not keeping the session alive in a service, except it is an EventListener.
My custom implementation of a ProductDisplayCmd looks like this...
public void performExecute( ) throws ECException {
super.performExecute();
(my code here)
Now, if a product is unavailable, the super throws an ECApplicationException with this message:
com.ibm.commerce.exception.ECApplicationException: The catalog entry
number "253739" and part number "9788703055992" is not valid for the
current contract.
With a SEO enabled URL, I get redirected to our custom 404 page ("Gee sorry, that product is no longer available. Try one of our fantastic alternatives...")
http://bktestapp01.tm.dom/shop/sbk/bent-isager-nielsen-efterforskerne
With the old-style URL, i instead get an error page due to an untrapped exception.
http://bktestapp01.tm.dom/webapp/wcs/stores/servlet/ProductDisplay?langId=100&storeId=10651&catalogId=10013&club=SBK&productId=253739
Since I can catch the exception, I suppose I have the option of manually redirecting to the 404 page, but is that the way to go? In particular: The exception type does not seem to tell me exactly what is wrong, so I might accidentally make a 404 out of another kind of error.
Here's what I ended up with: Catch the exception from super, then decide if the reason it was thrown is that the product is unavailable. If so, then redirect to the 404 page, else re-throw exception.
Implementation:
public void performExecute( ) throws ECException {
try {
super.performExecute();
} catch (final ECApplicationException e) {
// Let's see if the problem is something that should really be causing a redirect
makeProductHelperAndRedirectTo404IfProductNotAvailable(e);
// If we get here, noting else was thrown
log.error("The reason super.performExecute threw an ECException is unknown and so we can't recover. Re-throwing it.");
throw e;
}
...and in the makeProductblablabla method:
private ProductDataHelper makeProductHelperAndRedirectTo404IfProductNotAvailable(final ECException cause) throws ECSystemException,
ECApplicationException {
final ProductDataHelper productHelper;
try {
log.trace("Trying to determine if the reason super.performExecute threw an ECException is that the product is unavailable in the store. The execption is attached to this logline.", cause);
productHelper = makeProductHelper(getProductId());
if (productHelper != null) {
if (!productHelper.isActiveInClub()) {
log.trace("Decided that the reason super.performExecute threw an ECException is that the product is unavailable in the store. The execption is attached to this logline. NB! That exception is DISCARDED", cause);
final String pn = productHelper.getISBN();
final ECApplicationException systemException = new ECApplicationException(ECMessage._ERR_PROD_NOT_EXISTING, this.getClass().getName(), "productIsPublished", new Object[]{ pn });
systemException.setErrorTaskName("ProductDisplayErrorView");
throw systemException;
}
}
return productHelper;
} catch (RemoteException e) {
log.error("I was trying to determine if the reason super.performExecute threw an ECException is that the product is unavailable in the store. The original ECException is attached to this logline. NB! That exception is DISCARDED", cause);
throw new ECSystemException(ECMessage._ERR_GENERIC, super.getClass().getName(), "performExecute",ECMessageHelper.generateMsgParms(e.getMessage()), e);
} catch (NamingException e) {
log.error("I was trying to determine if the reason super.performExecute threw an ECException is that the product is unavailable in the store. The original ECException is attached to this logline. NB! That exception is DISCARDED", cause);
throw new ECSystemException(ECMessage._ERR_GENERIC, super.getClass().getName(), "performExecute",ECMessageHelper.generateMsgParms(e.getMessage()), e);
} catch (FinderException e) {
log.error("I was trying to determine if the reason super.performExecute threw an ECException is that the product is unavailable in the store. The original ECException is attached to this logline. NB! That exception is DISCARDED", cause);
throw new ECSystemException(ECMessage._ERR_GENERIC, super.getClass().getName(), "performExecute",ECMessageHelper.generateMsgParms(e.getMessage()), e);
} catch (CreateException e) {
log.error("I was trying to determine if the reason super.performExecute threw an ECException is that the product is unavailable in the store. The original ECException is attached to this logline. NB! That exception is DISCARDED", cause);
throw new ECSystemException(ECMessage._ERR_GENERIC, super.getClass().getName(), "performExecute",ECMessageHelper.generateMsgParms(e.getMessage()), e);
}
}
I have some code in a test as follows:
#Test
public void testRetrieveMongoDBFailUnkownHost()
{
//Set up test port and host on DSMongo
MyMongo mongoTest = new MyMongo();
mongoTest.setHost("failure");
mongoTest.setPort("0");
//attempt to make the connection
try
{
mongoTest.attemptMongoConnection();
assertTrue(false);
}
catch (Exception e)
{
assertEquals("Incorrect error message received: " + e.getMessage(),"Error (3013) : Unknown database host.", e.getMessage());
}
}
And the attempt MongoConnection() method runs the new Mongo(host, port) method which should fail with an unknown host exception. It isn't failing on my machine (no matter what string I put in instead of failure) but it is failing on my colleagues machine. So the test fails on my machine and passes on his (i.e. he gets the exception). Any ideas cause I am stumped!
Thanks
Paul
EDIT: The code in the attempt Connection Method is
*/
public static void attemptMongoConnection() throws MYException
{
try {
singleMongo = new Mongo(getHost(), getPort());
Logger.debug("Retrieved Mongo database from " + host);
} catch (UnknownHostException e) {
Logger.error("Unknown Host Exception", e);
throw new MYException(MYMessage.MY_UNKNOWN_HOST);
} catch (MongoException e) {
Logger.error("Mongo error", e);
throw new MYException(MYMessage.DS_MONGO_ERROR);
}
}
where singleMOngo is a Mongo variable and the getHost and getPort are the ones we have set (.e. failure and 0).
I have found this was a problem with the DNS somewhere. When I ran it at home (from where I originally made the post) it failed and seems to hav been resolving the name of "failure" so when I instead entered something like "localhost_123" it works perfectly.
I have come into the office this morning and it works with "failure" again. Doing some further digging it seems therefore that my router or something at home is resolving "failure" to an address it is aware of which is not present on the network here in the office.
Thanks for all those who looked at this. Very bizarre.