I have a question about closing resources (AutoCloseable or not) that are static members of a Java class. In my particular situation I have a class that manages connections to a MongoDB instance using a static MongoClient instance variable. How would I ensure that this client is closed as the documentation recommends when my application terminates? I am using this class as part of the back end to a Java webapp which is run in a container (Tomcat 7). I could not override the Object's finalize() method to close the client because that is called on an instance of the class and would have no effect on static members correct? Here is my example code:
public class MyManager {
//This needs to be closed when the application terminates
private static MongoClient CLIENT;
static {
...
CLIENT = new MongoClient("localhost", 27017);
...
}
public static DB getSomeDB(String dbName) {
return CLIENT.getDB(dbName);
}
//more factory methods
...
//Would this work?
#Override
protected void finalize() throws Throwable {
CLIENT.close();
}
}
Can someone tell me how to best handle this situation and in general with resources such as a database connection or JDBC driver? Thanks!
We are using Spring and simply create a bean which calls the close() method once it's being destroyed:
#Bean(name = "mongoClient", destroyMethod = "close")
public MongoClient mongoClient() throws MongoException, UnknownHostException {
...
Related
I'm new using Jedis and I'm not sure if the configuration i used is correct.
I'm trying to use Jedis in a API server application as a cache used by my handlers. For this reason the environment is a multi-threaded one and i'm looking for the correct configuration for this scenario.
I found lot of example mentioning the "JedisPool" such as the following:
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
ShardedJedisPool pool = new ShardedJedisPool(jedisPoolConfig, shards);
try (ShardedJedis jedis = pool.getResource()) {
jedis.set("a", "foo");
}
pool.close();
But in my case i need to use a UnifiedJadis object in order to have access to jsonSet and jsonGet methods, which "normal" Jedis does not provide.
This is what i've done.
This is the class i use to manage a UnifiedJedis object as singleton.
public class MyDB{
//singleton
public static UnifiedJedis jedisPooled;
//called at startup
public void init() throws DBConnectionException {
ConnectionPoolConfig poolConfig = RedisConfig.getRediConfiguration();
jedisPooled = new JedisPooled(poolConfig, dbUrl, dbPort);
}
}
At application startup i initialize the connection and inject the singleton everywhere is needed (e.g. in my handlers).
public class App{
public static void main(String[] args) throws Exception {
MyDB db = new MyDB();
db.init();
//...inject db where needed
}
}
Finally in my handlers I use the variable injected to use the jedisPooled methods jsonGet and jsonSet.
public class MyHandler{
// in the MyHandler constructor i receive the db variable
#Override
public void handle() throws Exception {
//...
db.jedisPooled.jsonSet(redisKey, Path.of("."), myPojo);
}
}
My question is: since there is no method "getResource" in the UnifiedJedis object, i can i get a connection from the pool and then release it after i send a command? Is it automatically done by the UnifiedJedis methods?
Is my solution correct?
Thank you
UnifiedJedis could act differently depending on parameters.
In your object creation, I can see new JedisPooled(poolConfig, dbUrl, dbPort);, which means you are actually creating and using JedisPooled[1]. This narrows down the possible behaviors of UnifiedJedis and so I am providing answer to your questions only for JedisPooled.
The Answer:
In JedisPooled you don't have to get a connection from pool and release it after sending a command. It is automatically done JedisPooled methods.
Note [1]: JedisPooled is a sub-class of UnifiedJedis. That's why you could set a JedisPooled object in a UnifiedJedis variable.
I have created a class in Spring boot to establish a global javers object that can be used by all classes. This is my code.
#Component
public class JaversInstance {
public static final Javers javers;
static
{
ConnectionProvider connectionProvider = new ConnectionProvider() {
#Override
public Connection getConnection() throws SQLException {
String url = "any_url";
Properties props = new Properties();
props.setProperty("user", "test");
props.setProperty("password", "test");
DriverManager.getConnection(url, props);
System.out.println("CONNECTION PROVIDER invoked");
return DriverManager.getConnection(url, props);
}
};
JaversSqlRepository sqlRepository = SqlRepositoryBuilder
.sqlRepository()
.withConnectionProvider(connectionProvider)
.withDialect(DialectName.MYSQL).build();
System.out.println("JAVERS instance creation");
javers = JaversBuilder.javers().registerJaversRepository(sqlRepository).build();
}
private JaversInstance() {
}
}
Output:
JAVERS instance creation
CONNECTION PROVIDER invoked
CONNECTION PROVIDER invoked
CONNECTION PROVIDER invoked
CONNECTION PROVIDER invoked
CONNECTION PROVIDER invoked
CONNECTION PROVIDER invoked
CONNECTION PROVIDER invoked
CONNECTION PROVIDER invoked
CONNECTION PROVIDER invoked
Can someone tell me what has happened here. Why the getConnection() is called so many times? Is this any kind of retry?
It happens as many times as the anonymous class of ConnectionProvider is loaded. The following code will help you understand it better:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Main {
static Comparator<Integer> comparator;
static {
comparator = new Comparator() {
#Override
public int compare(Object o1, Object o2) {
System.out.println("Hello");
return 0;
}
};
}
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
list.add(40);
list.add(20);
list.add(10);
list.add(30);
Collections.sort(list, comparator);
}
}
Output:
Hello
Hello
Hello
Although the ConnectionProvider is instantiated inside a static block, its overridden method is neither static (can't be) nor related to the static block itself but to the instance connectionProvider.
Basically, you implement a method of an anonymous class. I suppose the ConnectionProvider is an interface, then defining a class implementing the very same interface would be effectively the same as your code:
static
{
ConnectionProvider connectionProvider = new MyConnectionProvider();
}
The internals of the getConnection method is not bound to the static block, the instance connectionProvider itself is. There are multiple calls since the method has been invoked multiple times from an instance defined within the static block.
I'm currently using a pool connection(Hikari) and an abstract factory pattern to implement my MySQL queries in Java like this:
MySqlFactoryDAO.java
public class MySqlFactoryDAO extends FactoryDAO {
private static HikariDataSource connPool_;
public static Connection createConnection() throws SQLException {
if (connPool_ == null) {
// Load database configuration
PropertiesFile props = FactoryConfig.getConfig().getDatabaseProperties();
connPool_ = new HikariDataSource();
connPool_.setJdbcUrl(props.getString(Params.DB_URL,""));
connPool_.setUsername(props.getString(Params.DB_USER,"root"));
connPool_.setPassword(props.getString(Params.DB_PASSWORD,"root"));
}
return connPool_.getConnection();
}
//-------------------------------------------------------------------------
public ProductDAO getProductDAO() {
return new ProductMySQLFactoryDAO();
}
}
ProductMySQLFactoryDAO.java
public class ProductMySQLFactoryDAO implements ProductDAO {
public int insertProduct(String name) {
...
Connection conn = MySqlFactoryDAO.createConnection();
...
}
}
I was wondering if this code is thread safe and I think that there is a problem at the time of initialization of coonPool_. I have read things like "Initialization-on-demand_holder_idiom" in wikipedia but I am not sure about it. Does anyone have a better implementation of this to solve this problem or just a better new one?
No, it's not thread-safe. Two threads might call createConnection() concurrently, both see the pool as null, and both create a new DataSource.
The method needs to be synchronized. Or the pool must be created at class initializing time:
private static final HikariDataSource DATA_SOURCE = createDataSource();
Your connPool_ field should be private, too.
I'm trying to write test a case with mockito for a class that is injected with a ComboPooledDataSource.
I'm got an error saying that ComboPooledDataSource is a final class and cannot be mocked. So, I was thinking of mocking the interface(either DataSource) and using it for the test case but am not sure how to do it.
private Datasource cpds;
private Connection connection;
#Test
public void test() throws Exception {
connection = getConn();
cpds = mock(DataSource.class);
when(cpds.getConnection()).thenReturn(connection);
accessor = new comboPoolUser(cpds);
accessor.setConnection();
}
method in comboPoolUser that calls getConnection:
public void setConnection() {
try {
connection = comboPooledDataSource.getConnection();
} catch (SQLException e) {
throw new RuntimeException("error",e);
}
}
my "comboPoolUser" constructor takes in a ComboPooledDataSource but I'm trying to mock a "DataSource" for the test so I'm getting a "cannot find symbol: constructor comboPoolUser(javax.sql.DataSource)" error. What's the correct way to do this?
If your ComboPoolUser only requires the methods that are on the DataSource interface, change the constructor so that it takes in a DataSource. Currently the only thing it's doing is getting the connection, so on the face of it it looks possible. Generally, passing dependencies by interface helps maintain encapsulation.
Alternatively, if the ComboPoolUser is your own class, you could write a wrapper around the ComboPoolDataSource, give the wrapper an interface of your own, and make it so think that it's testable by inspection (so every method in the wrapper is just a delegation to the ComboPoolDataSoruce). You can then mock the interface to the wrapper.
I have the following EJB class instantiated in an application running in JBoss 5
public interface ISlaveServer {
public String getId();
public String getName();
}
#Remote
public interface IMasterServer {
public String getId();
public void addSlave(ISlaveServer slaveServer);
public void removeSlave(ISlaveServer slaveServer);
}
#Stateless
#RemoteBinding(jndiBinding = "MasterServer")
public class MasterServer implements IMasterServer, Serializable {
UUID id = UUID.randomUUID().toString();
public String getId() { return id.toString(); }
public void addSlave(ISlaveServer slaveServer) { ... }
public void removeSlave(ISlaveServer slaveServer) { ... }
}
I Have the following class instantiated in an application running in Tomcat 6
public static class SlaveServer implements ISlaveServer, Serializable {
UUID id = UUID.randomUUID().toString();
public String getId() { return id.toString(); }
public String getName() { return "SlaveServer"; }
}
Finally I have the following code also running in the Tomcat based application...
Properties properties = new Properties();
properties.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
properties.put("java.naming.factory.url.pkgs", "org.jboss.naming.client");
properties.put("java.naming.provider.url", "jnp://localhost:1099");
try {
Context ctx = new InitialContext(properties);
IMasterServer masterServer = (IMasterServer) ctx.lookup("MasterServer");
String masterId = masterServer.getId();
masterServer.addVideoServer(new SlaveServer());
}
catch(NamingException e) {
e.printStackTrace();
}
Everything is working fine up until the call to
masterServer.addVideoServer(new SlaveServer());
at which time I get the following exception...
java.lang.ClassNotFoundException: org.test.SlaveServerTest$SlaveServer (no security manager: RMI class loader disabled)
From what I can tell this exception might be originating from the remote JBoss server because the remote calls are working ( masterServer.getId() works fine ). Just the call where I am passing a locally implemented object is failing.
What do I need to do to get this working?
The SlaveServer class is Serializable. This means that this class must be made available to both the client (the JNDI snippet) and the server (MasterServer). When a class cannot be found on the server, RMI has the capability of downloading code from a remote location. However, executing code downloaded from a remote client is a potentially dangerous operation, this is only allowed if a security manager has been installed.
You'll need to either include the SlaveServer class in the application containing MasterServer (or some server class path), or you'll need to stop using Serializable.
The "static" was there because the original SlaveServer class was a nested class.
I have moved the class to the top-level (thus removing the static) and it is still a no go; I get the same exception.
It seems to me I need to do something like CORBA's "activation" to my SlaveServer. That way the JBoss server should be able to get the stubs for my SlaveServer inside Tomcat.
/Edit
There is no implementation of ISlaveServer in the JBoss application. I want it to pass a "remote reference" from the Tomcat application to the JBoss application so it shouldn't really be serializing it anyway (just a reference to it).
To get this working I needed to implement the ISlaveServer interface as an RMI remote interface.
public interface ISlaveServer extends java.rmi.Remote {
...
}
and make sure that the SlaveServer class was a proper RemoteObject...
public class SlaveServer extends java.rmi.RemoteObject implements ISlaveServer {
}
Finally I had to make sure the SlaveServer was exported through RMI before using it...
static SlaveServer slaveServer = new SlaveServer();
Properties properties = new Properties();
properties.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
properties.put("java.naming.factory.url.pkgs", "org.jboss.naming.client");
properties.put("java.naming.provider.url", "jnp://localhost:1099");
try {
Context ctx = new InitialContext(properties);
IMasterServer masterServer = (IMasterServer) ctx.lookup("MasterServer");
String masterId = masterServer.getId();
masterServer.addVideoServer((ISlaveServer)UnicastRemoteObject.exportObject(slaveServer, 0));
}
catch(NamingException e) {
e.printStackTrace();
}
This correctly communicates with the Remote EJB and passes a reference to my SlaveServer object which the EJB can use to communicate back with the calling VM.
FYI SlaveServer is static because in RMI it is your responsibility to hold on to a reference to the real object since RMI only holds weak-references. If you don't you will get "Object not in table" errors from RMI.