0

I'm a tapestry-hibernate user and I'm experiencing an issue where my session remains closed once I exceed my Executors.newFixedThreadPool(1);

I have the following code which will work perfectly for the first thread while the remaining threads experience a closed session. If I increase the thread pool to 10, all the threads will run without issue. As soon as I exceed the fixedThreadPool, I get the session closed exception. I do not know how to open it since it's managed by tapestry-hibernate. If I use newCachedThreadPool, everything works perfectly. Does anybody know what might be happening here?

public void setupRender() {
        ExecutorService executorService = Executors.newFixedThreadPool(1);

        final ConcurrentHashMap<String, Computer> map = new ConcurrentHashMap<>();
        final String key = "myKey";

        final Date date = new Date();

        List<Future> futures = new ArrayList<>();

        for (int i = 0; i < 10; i++) {
            final int thread = i;

            Future future = executorService.submit(new Callable() {

                @Override
                public String call() {
                    try {
                        Computer computer = new Computer("Test Computer thread");
                        computer = getComputer(map, key, key, computer);

                        Monitor monitor = new Monitor();
                        monitor.setComputer(computer);

                        session.save(monitor);
                        session.flush();
                        System.out.println("thread " + thread);
                        try {
                            sessionManager.commit();
                        } catch (HibernateException  ex) {
                            sessionManager.abort();
                        } finally {
                            session.close();
                        }
                    } catch (Exception ex) {
                        System.out.println("ex " + ex);
                    }
                    System.out.println( new Date().getTime() - date.getTime());
                    return "completed";
                }                

            });
            futures.add(future);
        }

        for(Future future : futures) {
            try {
                System.out.println(future.get());
            } catch (InterruptedException | ExecutionException ex) {
                Logger.getLogger(MultiThreadDemo.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public synchronized Computer getComputer(ConcurrentHashMap<String, Computer> map, String key, String thread, Computer computer) {
        if (map.putIfAbsent(key, computer) == null) {
            session.save(computer);
        } else {
            computer = map.get(key);
        }
        return computer;
    }
Code Junkie
  • 7,602
  • 26
  • 79
  • 141

1 Answers1

1

I've told you this before.... you MUST either use ParallelExecutor OR call PerThreadManager.cleanup(). You need to understand that tapestry-hibernate has PerThread scoped services that MUST be cleaned up if you are using them outside of a normal request/response (or ParallelExecutor).

I also don't think you should be calling session.close(). You should mimmic CommitAfterWorker.

It would probably look something like:

@Inject PerThreadManager perThreadManager;
@Inject HibernateSessionManager sessionManager; // this is a proxy to a per-thread value
@Inject Session session; // this is a proxy to a per-thread value

public void someMethod() {    
    ExecutorService executorService = ...;
    executorService.submit(new Callable() {
        public String call() {
            try {
                Monitor monitor = ...
                session.save(monitor);
                session.flush(); // optional
                sessionManager.commit();
            } catch (Exception ex) {
                sessionManager.abort();
            } finally {
                // this allows Session and HibernateSessionManager to
                // clean up after themselves
                perThreadManager.cleanup();
            }
            return ...
        }                
    });
}

If you choose to use the ParallelExecutor (and Invokable) instead of Executors.newFixedThreadPool(1) you can remove the references to PerThreadManager since it automatically cleans up the thread.

lance-java
  • 25,497
  • 4
  • 59
  • 101
  • Lance, I am using PerThreadManager.cleanup() ;-) I'm extending WorkQueue just like shown in this example, http://wiki.apache.org/tapestry/Tapestry5HowToWorkQueue Everything works perfectly calling the cleanup method at completion. When I didn't close the connection, I was running out of database connections, so I found it to be something I needed to do. I only ran into an issue where I restricted the thread pool to say 1 and put 10 task into the thread pool. As soon as I ran the first task, I got the session closed exception. If I used cachedThreadPool, no issue, so I wanted to understand why. – Code Junkie Nov 19 '13 at 06:04
  • Your code above shows you submitting Callable's to ```Executors.newFixedThreadPool(1)```. There is no reference to a ```WorkQueue``` or ```PerThreadManager.cleanup()```. Have you tried my suggested code? – lance-java Nov 19 '13 at 08:27
  • I will tonight, I haven't had a chance to try it yet. I just tried to keep my example simple, didnt think the worker queue code was relevent to the issue. Thanks lance. – Code Junkie Nov 19 '13 at 12:18
  • As I thought, you don't need to invoke ```session.close()```. The HibernateSessionManager does this when you call ```PerThreadManager.cleanup()```. Code [here](http://tapestry.apache.org/current/apidocs/src-html/org/apache/tapestry5/internal/hibernate/HibernateSessionManagerImpl.html) – lance-java Nov 19 '13 at 14:01
  • Works perfectly, I wasn't aware of ParallelExecutor, so going forward I see no reason not to use it. Thanks Lance. – Code Junkie Nov 19 '13 at 20:59
  • I'm surprised you're not aware of the ParallelExecutor. I mentioned it twice [here](http://stackoverflow.com/questions/19720697/too-many-connections-hibernate-and-mysql) – lance-java Nov 19 '13 at 21:27
  • Your absolutely correct, I guess I was just so hung up on the original way of doing it I completely miss understood ParallelExecutor for something else. Perhaps a little example / API link in the future showing the alternate may help to break the one track mind. Anyhow it's working great now, I may have a question in the future regarding returning an object proxy as I work towards code refactoring. Once again, thanks Lance. – Code Junkie Nov 20 '13 at 05:17