How to handle exceptions with #Resource (javax.annotation.resource) - java

Can I capture exceptions from resources looked up via #Resource? In the example below, a resource is being looked up via JNDI. There may not be a JNDI when running the app locally so I want to add an exception handler to capture this & create the data source another way.
#Resource(lookup="jdbc/ds_demo",
description="Primary DataSource",
authenticationType=AuthenticationType.CONTAINER,
shareable=false,
mappedName="ds_demo")
private DataSource primaryDataSource;

Related

Wait for database connection in Spring Boot without an exception

I want to create a microservice with Spring Boot. For persistence i use a mariadb database. To wait for the database which is running in a docker container, i implemented the following code like shown here:
#Bean
public DatabaseStartupValidator databaseStartupValidator(DataSource dataSource) {
var dsv = new DatabaseStartupValidator();
dsv.setDataSource(dataSource);
dsv.setTimeout(60);
dsv.setInterval(7);
dsv.setValidationQuery(DatabaseDriver.MYSQL.getValidationQuery());
return dsv;
}
The code is working very well, my application is now waiting for the database connection. But i get an exception at startup of the application:
java.sql.SQLNonTransientConnectionException: Could not connect to Host ....
...
...
...
In the next line i get an information, that it will wait for the database:
021-04-07 21:29:40.816 INFO 16569 --- [ main] o.s.j.support.DatabaseStartupValidator : Database has not started up yet - retrying in 7 seconds (timeout in 57.65 seconds)
After that the application is starting as expected. So i think everything is working fine, but what i have to do to suppress the Exception? In the linked article it should work without an exception. Do i have to implement the "dependsOnPostProcessor" function? Which dependency i have to use? Sorry, possible a dumb question, i am new to spring boot.
to get rid of that exception you can state the below directive in your application.properties file:
logging.level.com.zaxxer.hikari=OFF
Keep in mind that if the application will not be able to get in contact with the db your spring crashes after a while due to that exception. In addition the above directive prevent you to see any logging activity related to Hikari.
In summary you hide the appearance of the exception until it is possible before the application dies due to timeout.
hoping I clarified a bit the case
Yes indeed you need to add the "depends-on" for the beans that rely on the data source. Note the following part of the documentation:
To be referenced via "depends-on" from beans that depend on database startup, like a Hibernate SessionFactory or custom data access objects that access a DataSource directly.
If I understand it well, this means that beans such as an EntityManagerFactory which rely on the database will now have to go through the DatabaseStartupValidator bean and wait for the DB startup. I don't know what caused your exception, but usually there is an EntityManagerFactory involved, so try adding the DependsOn on this object at least.
This is how the linked article is doing it:
#Bean
public static BeanFactoryPostProcessor dependsOnPostProcessor() {
return bf -> {
// Let beans that need the database depend on the DatabaseStartupValidator
// like the JPA EntityManagerFactory or Flyway
String[] flyway = bf.getBeanNamesForType(Flyway.class);
Stream.of(flyway)
.map(bf::getBeanDefinition)
.forEach(it -> it.setDependsOn("databaseStartupValidator"));
String[] jpa = bf.getBeanNamesForType(EntityManagerFactory.class);
Stream.of(jpa)
.map(bf::getBeanDefinition)
.forEach(it -> it.setDependsOn("databaseStartupValidator"));
};
}
You may not necessarily have Flyway configured, but the main thing to note is the dependency itself is referenced by the bean name databaseStartupValidator which is the name of the method that creates the bean.

GlassFish randomly selects wrong JCA ConnectionFactory for Injection

I am in the process of writing a JCA Resource Adapter for FTP and SFTP. As my goal is to be able to use one protocol completely independent from the other, I have two different ManagedConnectionFactory classes, one for each protocol. Both have different ConnectionDefinition annotations; the Glassfish domain.xml contains a resource definition for the FTP Adapter but not for the SFTP Adapter. Now, when I inject the FTPConnectionFactory into an EJB, I sometimes get an InjectionException as WELD tries to inject the SFTPConnectionFactory instead (this is of course consistent during a run of the appserver; I either consistently get the right or the wrong one). Removing the ConnectionDefinition annotation for the SFTP adapter seems to fix the problem.
So, Questions:
How can I fix this and make Glassfish inject the correct class? Can this be caused by a problem in my code or is this a Glassfish issue?
According to the spec it is legal for a RA to have multiple ConnectionDefinition annotations, as well as multiple instances of a class implementing ManagedConnectionFactory; but I couldn't find any information about having multiple different classes implementing ManagedConnectionFactory inside of the same Resource Adapter. Glassfish clearly seems to have a problem with it - is this explicitly allowed or disallowed somewhere?
The ConnectionDefinition (with FTP replaced by SFTP for the SFTP case):
#ConnectionDefinition(connectionFactory = FTPConnectionFactory.class,
connectionFactoryImpl = FTPConnectionFactoryImpl.class,
connection = FTPConnection.class, connectionImpl = FTPConnectionImpl.class)
The FTP and SFTP Factory and ManagedConnection classes share common ancestors but are not directly related - but this doesn't seem to matter as completely separating the implementations makes no difference.
The domain.xml snippet:
<resource-adapter-config resource-adapter-name="ftpconnector" thread-pool-ids="thread-pool-1" />
<connector-connection-pool name="jca/ftpConnectorPool"
resource-adapter-name="ftpconnector"
connection-definition-name="foo.bar.ftp.FTPConnection"
transaction-support="NoTransaction" match-connections="false" />
<connector-resource pool-name="jca/ftpConnectorPool" jndi-name="jca/ftpConnector" />
And the injected field:
#Resource(lookup = "jca/ftpConnector")
private FTPConnectionFactory ftpConnectionFactory;
TL;DR: This was caused by an incorrectly configured Connection Pool, where connection-definition-name does not point to a class implementing ConnectionFactory. The observed behaviour is IMO a bug and has been reported here.
While trying to reproduce this issue with Adam Biens connectorz Filesystem Resource Adapter (by adding a second set of classes and another connection definition), I found I could only reproduce the behaviour if the connector-connection-pool in the domain.xml is incorrectly defined: If I changed the connection-definition-name to point to a nonexistent class, Glassfish would randomly take one of the two defined connectors. Doublechecking my Connection Pool, I found out that I mistakenly used the Connection class instead of the ConnectionFactory class in the xml. So when the attribute does not point to a class implementing the ConnectionFactory interface, Glassfish seems to randomly choose any class from the Resource Adapter which implements ConnectionFactory, without even printing an error message.

How to read Tomcat JDBC Data Sources Resource Factory properties?

I have an tomcat 6 with an configured JNDI Tomcat JDBC Data Sources Resource Factory. Now my task is to verify that the connection pool has a minimal max size.
(If it is less, I need to disable some function or print at least a warning.)
But I don't know how to get access to that value.
The Application (Spring) access the DataSource via JNDI, but this is only the data source (org.apache.tomcat.dbcp.dbcp.BasicDataSource) but I need the Factory (org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory) because the Factory only knows the value.
So how to read the org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory.maxActive property within an application?
Not the right thing to do - but if you insist you can cast the DataSource to org.apache.tomcat.dbcp.dbcp.BasicDataSource and then call the getMaxActive method on it. The value in the property file will be set onto the factory which then initializes the corresponding properties in the DataSource. The datasource object returned might not be an instance of BasicDataSource - it might get wrapped.

JMX MXBean Attributes all UNDEFINED - Spring 3.0.x/Tomcat 6.0

I've been trying to get a sample JMX MXBean working in a Spring-configured webapp, but any basic attributes on the MXBean are coming up as UNDEFINED when I connect with jconsole.
Java interface/classes:
public interface IJmxBean { // marker interface for spring config, see below
}
public interface MgmtMXBean { // lexical convention for MXBeans - mgmt interface
public int getAttribute();
}
public class Mgmt implements IJmxBean, MgmtMXBean { // actual JMX bean
private IServiceBean serviceBean; // service bean injected by Spring
private int attribute = 0;
#Override
public int getAttribute() {
if(serviceBean != null) {
attribute = serviceBean.getRequestedAttribute();
}
return attribute;
}
public void setServiceBean(IServiceBean serviceBean) {
this.serviceBean = serviceBean;
}
}
Spring JMX config:
<beans>
<context:component-scan base-package="...">
<context:include-filter type="assignable" expression="...IJmxBean" />
</context:component-scan>
<context:mbean-export />
</beans>
Here's what I know so far:
The element is correctly instantiating a bean named "mgmt". I've got logging in a zero-argument public constructor that indicates it gets constructed.
is correctly automatically detecting and registering the MgmtMXBean interface with my Tomcat 6.0 container. I can connect to the MBeanServer in Tomcat with jconsole and drill down to the Mgmt MXBean.
When examining the MXBean, "Attribute" is always listed as UNDEFINED, but jconsole can tell the correct type of the attribute. Further, hitting "Refresh" in jconsole does not actually invoke the getter method of "Attribute"- I have logging in the getter method to indicate if it is being invoked (similar to the constructor logging that works) and I see nothing in the logs.
At this point I'm not sure what I'm doing wrong. I've tried a number of things, including constructing an explicit Spring MBeanExporter instance and registering the MXBean by hand, but it either results in the MBean/MXBean not getting registered with Tomcat's MBean server or an Attribute value of UNDEFINED.
For various reasons, I'd prefer not to have to use Spring's #ManagedResource/#ManagedAttribute annotations.
Is there something that I'm missing in the Spring docs or MBean/MXBean specs?
ISSUE RESOLVED: Thanks to prompting by Jon Stevens (above), I went back and re-examined my code and Spring configuration files:
Throwing an exception in the getAttribute() method is a sure way to get "Unavailable" to show up as the attribute's value in JConsole. In my case:
The Spring JMX config file I was using was lacking the default-autowire="" attribute on the root <beans> element;
The code presented above checks to see if serviceBean != null. Apparently I write better code on stackoverflow.com than in my test code, since my test code wasn't checking for that. Nor did I have implements InitializingBean or #PostConstruct to check for serviceBean != null like I normally do on almost all the other beans I use;
The code invoking the service bean was before the logging, so I never saw any log messages about getter methods being entered;
JConsole doesn't report when attribute methods throw exceptions;
The NPE did not show up in the Tomcat logs.
Once I resolved the issue with serviceBean == null, everything worked perfectly. Regardless, +1 to Jon for providing a working demo, since there are literally 50 different ways to configure MBeans/MXBeans within Spring.
I've recently built a sample Spring based webapp that very cleanly enables JMX for latest versions of Spring, Hibernate and Ehcache.
It has examples for both EntityManager based access and DAO access (including transactions!). It also shows how to do annotation based injection in order to negate having to use Spring's xml config for beans. There is even a SpringMVC based example servlet using annotations. Basically, this is a Spring based version of a fairly powerful application server running on top of any servlet engine.
It isn't documented yet, but I'll get to that soon. Take a look at the configuration files and source code and it should be pretty clear.
The motivation behind this is that I got tired of all of the crazy blog posts with 50 different ways to set things up and finally made a single simple source that people can work from. It is up on github so feel free to fork the project and do whatever you want with it.
https://github.com/lookfirst/fallback

JNDI ClassCastException

I am attempting to use JNDI with a custom DataSource called CEDataSource. From my understanding for this to work I would have to create a custom factory as well.
So I created a custom factory that would return the CEDataSource object but now when I attempt to use this in Java with
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
// Look up our data source
CEDataSource ds = (CEDataSource)envCtx.lookup("jdbc/cePu");
I get the exception ClassCastException
"CEDataSource cannot be mapped to CEDataSource". I added the CEDataSource and the CEDataSourceFactory to the TOMCAT/lib folder as well as referenced this same jar on my deployed application.
Any help would be greatly appreciated on why this possible error may occur. Thanks
"CEDataSource cannot be mapped to CEDataSource" seems to point to the fact that it's not the same "CEDataSource" in both places.
What could be different is the classloader and this usually happens if you have the same jars/.class(es) in multiple locations.
Do you have multiple copies of your jar?
Try to have a single copy, maybe in the shared tomcat lib so it's loaded by the same classloader no matter from where you access it from.
It is actually not too difficult to start Tomcat under an Eclipse debug session (just put all the Bootstrap.jar in a project and add the System properties in the JVM parameters). II've done that many times, if only to dissect the bowels of that feline. Once this is done you can break on the class cast exception of the JNDI connection factory and you will then be able to see if your factory is called or not.
From what I remember Tomcat uses the DBCP DataSource. Actually repackaged under com.apache.tomcat.dbcp.dbcp.DataSource (IIRC).
So I would not be surprised if this is what you end up with as a result of your look-up.
With hindsight, I now realize I also forgot to mention that if any underlying class (for instance a JDBC driver) needed to create the instance of your CEDataSource is missing you also get this ClassCastException. Fair enough, but you always focus on the class itself and not on the other jars...
CEDataSource ds = (CEDataSource)envCtx.lookup("jdbc/cePu");
The lookup you are doing on jdbc/cePu is not of class type CEDataSource , it belongs to some other class type, that is why you are getting class cast exception. if you could show me the configuration for jdbc/cePu that would be helpful.

Categories

Resources