I'm plannning a class which dynamically opens a connection to a database to store some settings.
The class should automatically close the connection if not used for a specific time or if the calling method ends.
Here is my solution so far, only showing the relevant parts:
public class DBSettings {
// ...
private int ConnectionTimeout = 5000;
private ExecutorService Executor;
private long LastTimeUsed;
// ...
public DBSettings(DBConnection Conn) {
Connection = new DBConnection(); // class handles Connection to Database
// ...
}
private void connect() {
try {
if (!Connection.isOpen()) {
Connection.openConnection(DBUrl, DBUserName, DBPassword);
LastTimeUsed = System.currentTimeMillis();
Executor = Executors.newSingleThreadExecutor();
Runnable closeRunnable = new Runnable() {
#Override
public void run() {
while (System.currentTimeMillis() < (LastTimeUsed + ConnectionTimeout)) {
try {
Thread.sleep(500);
} catch (InterruptedException e) { }
}
disconnect();
}
};
Executor.submit(closeRunnable);
}
} catch (Exception e) { // ... }
}
private void disconnect() {
if (Connection!=null) {
if (Executor!=null) {
Executor.shutdown();
}
Connection.closeConnection();
}
}
public void setValue(String group, String key, String value) {
// ...
LastTimeUsed = System.currentTimeMillis();
}
}
ExecutorService stops fine after 5 seconds and the connection closes.
But unfortunately keeps running for at least 5 seconds if the caller-method ends.
My test program:
private static void testDBSettings(DBConnection Conn) {
// using my class
DBSettings settings = new DBSettings(Conn);
// set first value, open connection
settings.setValue("Groupname", "Keyname", "Value");
Thread.sleep(1000);
// set second value, extend connection lifetime
settings.setValue("otherGroupname", "otherKeyname", "otherValue");
Thread.sleep(1000);
// here is the problem: after "settings" goes out of scope my class should
// stop or terminate the ExecutorService without the need to call an extra method.
}
I read a lot about Threads but couldn't find a solution.
tried another approach with ScheduledExecutorService - same result.
finalize() doesn't work because it is only called by garbage colletion.
Can anyone help me?
There is no way to track this in java.
Closest that you can do is use try-with-resource
try(MyClass myClass = new MyClass()){
myClass.doSomething();
} // Here myClass.close(); will be called.
Related
I have an ExecutorService (thread pool size = 4) that is handling a number of Callables. Each of them opens a database connection (Hikari Connection pool) and closes it again.
If I now call shutdownNow() on the ExecutorService I do also wait for the termination of the currently running tasks. However, eventhough awaitTermination does not produce a timeout - thus all running tasks should have been terminated, and all database operations should have finished - I get an org.h2.jdbc.JdbcSQLException stating the following:
General error: "java.lang.IllegalStateException: Reading from nio:database.mv.db failed; file length -1 read length 256 at 711665 [1.4.196/1]"; SQL statement: SELECT * FROM PERSON WHERE id = ? [50000-196]
In addition, I do close the Hikari connection pool far later than shutting down the ExecutorService. Do you have any ideas what I could search for?
EDIT:
Here is the basic code structure - I think I have mentioned all necessary items. Note, that the exception mentioned does not get thrown every time - but most of the time:
class DatabaseManager {
private HikariDataSource datasource;
private static DatabaseManager instance;
public static DatabaseManager getInstance() {
if (instance == null) {
instance = new DatabaseManager();
}
return instance;
}
public Connection getConnection() { datasource.getConnection(); }
private DatabaseManager() {
// initialize with parameters for a H2 database
this.datasource = new HikariDataSource();
}
public void shutdown() {
if (this.datasource != null) {
this.datasource.shutdown();
}
this.datasource = null;
}
}
class SimpleCallable extends Callable<SomeType> {
String information;
public SomeCallable(String info) { this.information = info; }
public SomeType call() {
// omitted try-catch
Connection connection = DatabaseManager.getInstance().getConnection();
// doing some things with connection (reading and writing data), the Method invoked is static and synchronized
// within this method the exception mentioned above is thrown
SomeType someType = SomeTypeHelper.transferToDB(connection, information);
connection.close();
return someType;
}
}
class SimpleTask extends Runnable {
public void run() {
ExecutorService service = new Executors.newFixedThreadPool(4);
for (i=0; i<1000; i++) {
SimpleCallable callable = new SimpleCallable("random text");
FutureTask task = new FutureTask(callable);
service.submit(task);
}
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
// nothing to do
}
service.shutdownNow();
try {
if (!service.awaitTermination(60, TimeUnit.SECONDS)) {
System.out.println("timeout"); // but will never be printed
}
}
catch (InterruptedException e) {
// nothing to do
}
}
}
class App {
public static void main(String[] args) {
SimpleTask task = new SimpleTask();
new Thread(task).start();
DatabaseManager.getInstance().shutdown();
}
}
I am attempting to use connection pooling for Executor Service.
I am facing some problem when connection pool config is initialSize=3, maxToal=5, maxIdle=5.
I need to process 10 services at a time for every minute. But its picking only 5 services for every minute.
If i configure initialSize=3, maxToal=10, maxIdle=10 then its picking 10 services for every minute..
I am new to multithreading and connection. Below is my code snippet. Please provide suggestion.
public class TestScheduledExecutorService {
public static void main (String a[]) {
ScheduledExecutorService service = null;
try {
TestObject runnableBatch = new TestObject() {
public void run() {
testMethod ();
}
};
service = Executors.newSingleThreadScheduledExecutor();
service.scheduleAtFixedRate(runnableBatch, 0, 30, TimeUnit.SECONDS);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class TestObject implements Runnable{
public void testMethod (int inc) {
ExecutorService service = null;
try {
service = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
service.submit(new TestService());
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void run() {
}
}
public class TestService implements Callable{
Connection conn = null;
public void process(Connection conn) {
try {
if (conn != null) {
System.out.println("Thread & Connection pool conn : "+Thread.currentThread() + " :: " +conn);
// service process here
} else {
System.out.println("Connection pool conn is null : ");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
#Override
public Object call() throws Exception {
ConnectionPoolTest cp = ConnectionPoolTest.getInstance();
BasicDataSource bds = cp.getBasicDataSource();
conn = bds.getConnection();
System.out.println(" call() "); **// it prints only 5 times for every minute eventhough total services are 10**
process(conn);
return null;
}
}
public class ConnectionPoolTest {
private static ConnectionPoolTest dataSource = new ConnectionPoolTest();
private static BasicDataSource basicDataSource = null;
private ConnectionPoolTest() {
}
public static ConnectionPoolTest getInstance() {
if (dataSource == null)
dataSource = new ConnectionPoolTest();
return dataSource;
}
public BasicDataSource getBasicDataSource() throws Exception {
try {
basicDataSource = new BasicDataSource();
basicDataSource.setInitialSize(3);
basicDataSource.setMaxTotal(10);
basicDataSource.setMaxIdle(10);
} catch (Exception e) {
throw e;
}
return basicDataSource;
}
}
For Executor Service
initialSize : Specified Number of Threads to spin , when New executor is created.
maxTotal : Number of Threads that can exist at max peak load.
maxIdle : Number of Thread that are kept active even if load goes below threshold.
As you mentioned, you want to pick up 10 number of tasks in parallel, we should have maxTotal set at 10. intialSize can be configured to a number that you think is optimal at the start , lets say 3 - 5. maxIdle is the number of threads you want to keep active , we generally assume how many threads are required if tasks are submitted. though there is no standard recomendation, vaues might be determined a number of various factors like .
Distribution of task submitted during the minute
Duration of Task
Urgency of executing those tasks in parallel.
As you mentioned you need 10 parallel tasks, then you will have to configure 10 as maxTotal, considering your task distribution and Duration causes overlap. If duration is pretty small , and distribution is even you can also survive with a lower number too.
So I'm trying to create a client/server program. I want to know when my client disconnects of his own accord, so I've setup a heartbeat system. Every 6 seconds my client sends a ping to my server, if the client doesn't send a ping for a total of 30 seconds the client is considered disconnected and removed from the current connections list (for which I plan to implement a GUI). Or at least, that's the plan.
ConnectionManager.java
public class ConnectionManager implements Runnable{
static Socket connection;
private ArrayList<Thread> allConnections;
private ArrayList<Connection> allConnectionList;
private ServerSocket server;
private int id = 0;
public ConnectionManager() {
allConnections = new ArrayList<Thread>();
allConnectionList = new ArrayList<Connection>();
}
#Override
public void run() {
try {
server = new ServerSocket(5555);
System.out.println("Server is running!");
while(true) {
connection = server.accept();
Connection a = new Connection(connection, id);
Runnable runnable = a;
allConnectionList.add(a);
allConnections.add(new Thread(runnable));
allConnections.get(allConnections.size() - 1).start();
id++;
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void removeConnection(int id) {
allConnections.remove(id);
allConnectionList.remove(id);
}
Connection.java
public class Connection implements Runnable {
private Socket a;
public boolean amIActive;
private int id;
public Connection(Socket a, int id) {
amIActive = true;
this.a = a;
this.id = id;
}
public void onConnect() {
try {
String TimeStamp = new java.util.Date().toString();
String formattedAddress = a.getInetAddress().toString().replace("/", "");
System.out.println("Received connection from: " + formattedAddress + " at " + TimeStamp);
Runnable runnable = new ConnectionListener(this);
Thread connectionThread = new Thread(runnable);
connectionThread.start();
String returnCode = "Server repsonded to " + a.getInetAddress().toString().replace("/", "") + " at "+ TimeStamp + (char) 13;
BufferedOutputStream os = new BufferedOutputStream(a.getOutputStream());
OutputStreamWriter osw = new OutputStreamWriter(os, "US-ASCII");
osw.write(returnCode);
osw.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void run() {
onConnect();
System.out.println("We got this far!");
while(amIActive) {
whileTrue();
}
System.out.println("This code never gets run because we get stuck in the while loop above");
Main.b.removeConnection(id);
System.out.println("Connection was closed from " + a.getInetAddress());
}
public void setOffline(boolean state) {
this.amIActive = state;
}
public void whileTrue() {
}
public Socket getSocket() {
return a;
}
ConnectionListener.java
public class ConnectionListener implements Runnable{
public Connection myConnection;
public boolean receivedHeartbeat;
public int missedHeartbeats = 0;
public ConnectionListener(Connection a) {
this.myConnection = a;
}
#Override
public void run() {
Runnable runnable = new Heartbeat(this);
Thread thread = new Thread(runnable);
thread.start();
while(myConnection.amIActive) {
try {
BufferedInputStream is;
is = new BufferedInputStream(myConnection.getSocket().getInputStream());
InputStreamReader isr = new InputStreamReader(is);
StringBuffer process = new StringBuffer();
int character;
while((character = isr.read()) != 13) { //GETTING STUCK HERE BECAUSE STUPID.
if(character == -1) {
myConnection.setOffline(true);
} else {
process.append((char)character);
}
}
handleInput(process);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void handleInput(StringBuffer process) {
String messageSent = process.toString();
if(messageSent.equals("Ping!")) {
receivedHeartbeat = true;
}
}
Heartbeat.java
public class Heartbeat implements Runnable{
private ConnectionListener b;
public Heartbeat(ConnectionListener a) {
b = a;
}
#Override
public void run() {
while(true) {
try {
Thread.sleep(1000);
if(b.missedHeartbeats > 5) {
b.myConnection.amIActive = false;
System.out.println("Setting amIActiveToFalse!");
}
if(b.receivedHeartbeat) {
b.receivedHeartbeat = false;
} else {
b.missedHeartbeats++;
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
My console is spammed with System.out.println("Setting amIActiveToFalse!"); from Heartbeat.java. But the while loop in Connection.java keeps running. I believe this might be something to do with my threading, but I can't figure it out.
When you have a non-volatile variable, there is no guarentee of visability of a change in one thread to another. In particular, if the JVM detects that a thread doesn't alter a boolean it can inline it, meaning you will never see the value change.
The simple solution is to make the boolean volatile and it will not be inlined and one thread will see when another changes it.
For more details http://vanillajava.blogspot.com/2012/01/demonstrating-when-volatile-is-required.html
The trivial answer to this is: make the variable volatile.
Without this, it is allowed for the thread changing the value to basically keep its updates in cache, committing them to main memory some time later.
This allows threaded code to run much faster, since it can keep its variables in cache rather than having to fetch from main memory. However, the consequence of this is that other threads don't see the update.
Making the variable volatile prevents this from happening: a thread always reads the value from main memory, and writes are immediately committed.
I say that this is the trivial answer because it doesn't necessarily fix all of your problems. There may also be an atomicity issue: in between one thread reading the variable and writing it again, another thread might sneak in and change its value, which may or may not put the first thread into an undefined state from the perspective of its invariants.
Specifically:
if(b.receivedHeartbeat) { b.receivedHeartbeat = false;
It is possible that some other thread can change b.receivedHeartbeat to false after this thread evaluates it to true, so this iteration is erroneously counted as a "non-missed" heartbeat.
This can be fixed by making the variable a (non-volatile) AtomicBoolean, on which there is an atomic compare-and-set method, which avoids such race conditions.
Java Concurrency In Practice is a great reference on these issues, I wholeheartedly recommend it. Look for the topics "visibility" and "atomicity".
Also read the advanced chapter on the Java Memory Model. That made me doubt myself at first, but made me a much stronger programmer after I digested it.
There are a couple issues I saw while debugging the code you posted, but I was able to successfully get the heartbeat functionality working.
In the Connection Listener class I don't think the if statement with .equals("Ping!") will match, because of the newline character at the end of each line.
In the Connection Listener class I would probably put the socket's Input Stream at the top of the loop not inside the loop. (I don't think this will break it but it's probably nicer this way)
ConnectionListener Updates:
public void run() {
Runnable runnable = new Heartbeat(this);
Thread thread = new Thread(runnable);
thread.start();
BufferedReader br = null;
try {
//is = new BufferedInputStream(myConnection.getSocket().getInputStream());
br = new BufferedReader(new InputStreamReader(myConnection.getSocket().getInputStream()));
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
while(myConnection.amIActive) {
try {
String processLine = br.readLine();
System.out.println("handleInput:" + processLine);
handleInput(processLine);
} catch (Exception e) {
System.out.println("Exception!");
e.printStackTrace();
}
}
}
public void handleInput(String messageSent) {
if(messageSent.startsWith("Ping!")) { //Need to use startsWith, or add newline character
receivedHeartbeat = true;
System.out.println("receivedHeartbeat!");
}
}
Also, in your Heartbeat class make sure you reset the missedHeartbeats counter to 0 on true:
if(b.receivedHeartbeat) {
b.receivedHeartbeat = false;
b.missedHeartbeats = 0;
} else {
b.missedHeartbeats++;
}
I have a problem where my class is performing the first run method after which it is not proceeding into a second, overidden run method.
The program execution beings in a controller class which has a main method and a thread pool:
public class RunnableController {
// Main method
public static void main(String[] args) throws InterruptedException {
try {
RunnableController controller = new RunnableController();
controller.initializeDb();
controller.initialiseThreads();
System.out.println("Polling");
} catch (Exception e) {
e.printStackTrace();
}
}
private void initialiseThreads() {
try {
threadExecutorRead = Executors.newFixedThreadPool(10);
PollingSynchronizer read = new PollingSynchronizer(incomingQueue, dbConncetion);
threadExecutorRead.submit(read);
} catch (Exception e){
e.printStackTrace();
}
}
}
My poller class which fetches new data and should do updating simulateously:
public class PollingSynchronizer implements Runnable {
public PollingSynchronizer(Collection<KamMessage> incomingQueue,
Connection dbConnection) {
super();
this.incomingQueue = incomingQueue;
this.dbConnection = dbConnection;
}
private int seqId;
public int getSeqId() {
return seqId;
}
public void setSeqId(int seqId) {
this.seqId = seqId;
}
// The method which runs Polling action and record the time at which it is done
public void run() {
int seqId = 0;
while (true) {
List<KamMessage> list = null;
try {
list = fullPoll(seqId);
if (!list.isEmpty()) {
seqId = list.get(0).getSequence();
incomingQueue.addAll(list);
this.outgoingQueue = incomingQueue;
System.out.println("waiting 3 seconds");
System.out.println("new incoming message");
Thread.sleep(3000);//at this wait I should execute run()
//when I debug my execution stops here and throws " Class not found Exception "
// its does not enters the message processor class
MessageProcessor processor = new MessageProcessor() {
//the run method which should fetch the message processor class.
final public void run() {
MessageProcessor(outgoingQueue).generate(outgoingQueue);
}
};
new Thread(processor).start();
}
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
}
My message processor class:
public abstract class MessageProcessor implements Runnable {
private Connection dbConnection;
Statement st = null;
ResultSet rs = null;
PreparedStatement pstmt = null;
private Collection<KamMessage> outgoingQueue;
public KamMsg804 MessageProcessor(Collection<KamMessage> outgoingQueue,
Connection dbConnection) {
this.outgoingQueue = outgoingQueue;
this.dbConnection = dbConnection;
return (KpiMsg804) fetchedMessages;
}
public Collection<KamMessage> generate(Collection<KamMessage> outgoingQueue) {
while (true) {
try {
while (rs.next()) {
KamMessage filedClass = convertRecordsetToPojo(rs);
outgoingQueue.add(filedClass);
}
for (KamMessage pojoClass : outgoingQueue) {
KamMsg804 updatedValue = createKamMsg804(pojoClass);
System.out.print(" " + pojoClass.getSequence());
System.out.print(" " + pojoClass.getTableName());
System.out.print(" " + pojoClass.getAction());
System.out.print(" " + updatedValue.getKeyInfo1());
System.out.print(" " + updatedValue.getKeyInfo2());
System.out.println(" " + pojoClass.getEntryTime());
}
return outgoingQueue;
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
My problem is exactly at the second run(9 method where I am getting exception in MessageProcessor class and it loops back to Polling.
How do I implement multithreading here, as when the thread sleeps for 3 seocnds in polling it should simultaneously update the database.
After which, how can the data be fed and updated back into the db.
My program flow - I have three classes:
1.Controller
2.PollerSynchro
3.Msgprocessor
I have database records, which are converted into POJO form and stored in a Collection. With these POJOs my classes try to do multiprocessing and updating in a single stretch.
Controller - has the thread pool, initiates poller class with poll method - done
Poller - should poll for new incoming messages and stores it in incoming queue - done
MsgProcessor - should look for new incoming messages and pass them from outgoing queue to incoming queue - also done
Problem:
Now my problem is
I have to implement this update while the poll thread sleeps for 3 sec,
In my code for the second void run() method in the Poller class, the outgoing queue is not passed and fed to the messageprocessor class for updating. My flow of execution only just loops back to first run method and am getting Class exception.
Please help me to solve these problems.
I can't sugar coat this, your code is a mess. However, as far as why your message processor code is not being executed, you never actually start the thread you created with this code:
MessageProcessor processor = new MessageProcessor() {
// the run method which should fetch the message processor class.
final public void run() {
MessageProcessor(outgoingQueue).generate(outgoingQueue);
}
};
Ignoring the confusingly named method being called, your code should look more like this:
Message processor = new MessageProcessor() {
// the run method which should fetch the message processor class.
final public void run() {
MessageProcessor(outgoingQueue).generate(outgoingQueue);
}
};
new Thread(processor).start();
I'm trying to start a JMXConnectorServer for management and debug purposes. But I don't want this service to prevent application from exiting normally when the last non-daemon thread is terminated.
In other words, I want the following program to terminate immediately:
public class Main {
public static void main(final String[] args) throws IOException {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
JMXServiceURL jmxUrl = new JMXServiceURL("rmi", null, 0);
JMXConnectorServer connectorServer =
JMXConnectorServerFactory.newJMXConnectorServer(jmxUrl, null, mbs);
connectorServer.start();
}
}
I play with similar issue and wrote this class:
public final class HardDaemonizer extends Thread {
private final Runnable target;
private final String newThreadName;
public HardDaemonizer(Runnable target, String name, String newThreadName) {
super(name == null ? "Daemonizer" : name);
setDaemon(true);
this.target = target;
this.newThreadName = newThreadName;
}
#Override
public void run() {
try {
List<Thread> tb = getSubThreads();
target.run();
List<Thread> ta = new java.util.ArrayList<>(getSubThreads());
ta.removeAll(tb);
for (Thread thread : ta) {
thread.setName(newThreadName);
}
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException ex) {
Logger.getLogger(HardDaemonizer.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static Thread daemonize(String daemonizerName, String newThreadName, Runnable target) {
HardDaemonizer daemonizer = new HardDaemonizer(target, daemonizerName, newThreadName);
daemonizer.start();
return daemonizer;
}
private static List<Thread> getSubThreads() {
ThreadGroup group = Thread.currentThread().getThreadGroup().getParent();
Thread[] threads = new Thread[group.activeCount()];
group.enumerate(threads);
return java.util.Arrays.asList(threads);
}
}
You can use it in this way:
HardDaemonizer.daemonize(null, "ConnectorServer", new Runnable(){
#Override
public void run() {
try {
connectorServer.start();
} catch (IOException ex) {
Logger.getLogger(Ralph.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
Be careful - it's tricky!
EDIT
Agh... It's not solution for you. It hard-daemonize connector thread only and this thread will be killed when jvm stops. Additionaly you can customize name of this thread.
Alternatively you can add flag completed and sleep in loop in daemonize method until connector server start up.
SIMPLIFIED
This is simplified daemonizer without tricky thread renaming:
public abstract class Daemonizer<T> extends Thread {
private final T target;
private boolean completed = false;
private Exception cause = null;
public Daemonizer(T target) {
super(Daemonizer.class.getSimpleName());
setDaemon(true);
this.target = target;
}
#Override
public void run() {
try {
act(target);
} catch (Exception ex) {
cause = ex;
}
completed = true;
try {
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException ex) {
java.util.logging.Logger.getLogger(Daemonizer.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
}
public abstract void act(final T target) throws Exception;
public static void daemonize(Daemonizer daemonizer) throws Exception {
daemonizer.start();
while (!daemonizer.completed) {
Thread.sleep(50);
}
if (daemonizer.cause != null) {
throw daemonizer.cause;
}
}
}
Usage:
Daemonizer.daemonize(new Daemonizer<JMXConnectorServer>(server) {
#Override
public void act(JMXConnectorServer server) throws Exception {
server.start();
}
});
Yeah, you will need to so a connectorServer.stop(); at some point.
Edit:
In reading your comments, it sounds like you should do something like:
connectorServer.start();
try {
// create thread-pool
ExecutorService threadPool = Executors...
// submit jobs to the thread-pool
...
threadPool.shutdown();
// wait for the submitted jobs to finish
threadPool.awaitTermination(Long.MAX_LONG, TimeUnit.SECONDS);
} finally {
connectorServer.stop();
}
#Nicholas' idea of the shutdown hook is a good one. Typically, however, I had my main thread wait on some sort of variable that is set from a shutdown() JMX operation. Something like:
public CountDownLatch shutdownLatch = new CountDownLatch(1);
...
// in main
connectorServer.start();
try {
// do the main-thread stuff
shutdownLatch.await();
} finally {
connectorServer.stop();
}
// in some JMX exposed operation
public void shutdown() {
Main.shutdownLatch.countDown();
}
As an aside, you could use my SimpleJMX package to manage your JMX server for you.
JmxServer jmxServer = new JmxServer(8000);
jmxServer.start();
try {
// register our lookupCache object defined below
jmxServer.register(lookupCache);
jmxServer.register(someOtherObject);
} finally {
jmxServer.stop();
}
From my experience, the JMXConnectorServer is only running in a user thread when you create it explicitly.
If you instead configure RMI access for the platform MBean server via system properties, the implicitly created JMX connector server will run as daemon process and not prevent the JMV shutdown. To do this, your code would shrink to the following
public class Main {
public static void main(final String[] args) throws IOException {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
}
}
but you'll need to set the following system properties:
-Dcom.sun.management.jmxremote.port=1919
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
You could add a JVM Shutdown Hook to stop the connector server.
===== UPDATE =====
Not sure why your shutdown hook doesn't work. Perhaps you can supply your sample code. Here's an example:
public static void main(String[] args) {
try {
log("Creating Connector Server");
final JMXConnectorServer jcs = JMXConnectorServerFactory.newJMXConnectorServer(new JMXServiceURL("rmi", "localhost", 12387), null, ManagementFactory.getPlatformMBeanServer());
Thread jcsStopper = new Thread("JCS-Stopper") {
public void run() {
if(jcs.isActive()) {
try {
jcs.stop();
log("Connector Server Stopped");
} catch (Exception e) {
log("Failed to stop JCS");
e.printStackTrace();
}
}
}
};
jcsStopper.setDaemon(false);
Runtime.getRuntime().addShutdownHook(jcsStopper);
log("Registered Server Stop Task");
jcs.start();
log("Server Started");
Thread.sleep(3000);
System.exit(0);
} catch (Exception ex) {
ex.printStackTrace(System.err);
}
}
Output is:
[main]:Creating Connector Server
[main]:Registered Server Stop Task
[main]:Server Started
[JCS-Stopper]:Connector Server Stopped
String port = getProperty("com.sun.management.jmxremote.port");
if (port == null) {
port = String.valueOf(getAvailablePort());
System.setProperty("com.sun.management.jmxremote.port", port);
System.setProperty("com.sun.management.jmxremote.ssl", "false");
System.setProperty("com.sun.management.jmxremote.authenticate", "false");
sun.management.Agent.startAgent();
}
log.info(InetAddress.getLocalHost().getCanonicalHostName() + ":" + port);