LazyQueryContainer IllegalStateException: EntityManager is closed

Hi Folks,

I’m using LazyQueryContainer 1.2.9 with vaadin 6.6.4 and a hibernate EntityManager and I’m getting the exception shown below.

My table is successfully loaded with the first 49 items. The above exception occurs when the 50th item is loaded. The Table is set to display 25
lines and I specify a container batchsize of 50.

The key parts of my code are below. Note that when creating EntityManager(…) I’ve tried using both ‘true’ and ‘false’ for the second argument specifing if the app is responsible for transaction management.

According to debug output, there are no “TRANSACTION commit” or close messages from my code after the last “TRANSACTION begin”.


Sep 9, 2011 3:19:55 PM com.vaadin.Application terminalError
SEVERE: Terminal error:
java.lang.IllegalStateException: EntityManager is closed
	at org.hibernate.ejb.EntityManagerImpl.getSession(EntityManagerImpl.java:89)
	at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:272)
	at org.vaadin.addons.lazyquerycontainer.EntityQuery.loadItems(EntityQuery.java:121)
	at org.vaadin.addons.lazyquerycontainer.LazyQueryView.queryItem(LazyQueryView.java:246)
	at org.vaadin.addons.lazyquerycontainer.LazyQueryView.getItem(LazyQueryView.java:224)
	at org.vaadin.addons.lazyquerycontainer.LazyQueryContainer.getItem(LazyQueryContainer.java:176)
	at org.vaadin.addons.lazyquerycontainer.LazyQueryContainer.getContainerProperty(LazyQueryContainer.java:186)
	at com.vaadin.ui.AbstractSelect.getContainerProperty(AbstractSelect.java:745)
	at vortex.console.ui.AssetsTable.getAssetFromItem(AssetsTable.java:917)
	at vortex.console.ui.AssetsTable.access$2(AssetsTable.java:902)
	at vortex.console.ui.AssetsTable$5.generateCell(AssetsTable.java:462)
	at com.vaadin.ui.Table.refreshRenderedCells(Table.java:1572)
	at com.vaadin.ui.Table.enableContentRefreshing(Table.java:2313)
	at com.vaadin.ui.Table.changeVariables(Table.java:2159)
	at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.handleVariableBurst(AbstractCommunicationManager.java:1299)
	at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.handleVariables(AbstractCommunicationManager.java:1219)
	at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.doHandleUidlRequest(AbstractCommunicationManager.java:735)
	at com.vaadin.terminal.gwt.server.CommunicationManager.handleUidlRequest(CommunicationManager.java:296)
	at com.vaadin.terminal.gwt.server.AbstractApplicationServlet.service(AbstractApplicationServlet.java:501)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:304)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
	at vortex.db.EntityManagerRequestFilter.doFilter(EntityManagerRequestFilter.java:69)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:224)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:164)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
	at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:929)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:405)
	at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:279)
	at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:515)
	at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:302)
	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
	at java.lang.Thread.run(Thread.java:662)
Sep 9, 2011 3:19:55 PM com.vaadin.Application terminalError
SEVERE: Terminal error:
java.lang.ArrayIndexOutOfBoundsException: 25
	at com.vaadin.ui.Table.paintContent(Table.java:2496)
	at com.vaadin.ui.AbstractComponent.paint(AbstractComponent.java:755)
	at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.writeUidlResponce(AbstractCommunicationManager.java:954)
	at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.paintAfterVariableChanges(AbstractCommunicationManager.java:841)
	at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.doHandleUidlRequest(AbstractCommunicationManager.java:767)
	at com.vaadin.terminal.gwt.server.CommunicationManager.handleUidlRequest(CommunicationManager.java:296)
	at com.vaadin.terminal.gwt.server.AbstractApplicationServlet.service(AbstractApplicationServlet.java:501)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:304)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
	at vortex.db.EntityManagerRequestFilter.doFilter(EntityManagerRequestFilter.java:69)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:224)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:164)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
	at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:929)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:405)
	at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:279)
	at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:515)
	at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:302)
	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
	at java.lang.Thread.run(Thread.java:662)
2011-09-09 15:19:55,461 DEBUG [vortex.console.ui.AssetsTable]
 transactionEnd: TRANSACTION END

Java code below:


public class AssetsTable extends Table {
    private AdminConsoleApplication 	app;
    private AssetManager		assetMgr;
    private int				linesPerPage = 25;
    private EntityContainer<Asset> 	container;

    public void init() {
	String		m = "init";
	
	log.debug(m, "Initializing");
	
	assetMgr = AssetManager.getInstance();
	
	log.debug(m, "Attach Transaction Listener");
        attachVaadinTransactionListener();
        log.debug(m, "Populate Table");
        populateAndConfigureTable();
        log.debug(m, "Ready");
    }

    /**
     * We are using session-per-request pattern with Hibernate. By using
     * Vaadin's transaction listener we can easily ensure that session is closed
     * on each request without polluting our program code with extra logic.
     */
    private void attachVaadinTransactionListener() {
        app.getContext().addTransactionListener(new TransactionListener() {
	    private static final long serialVersionUID = -3558981058139390335L;

	    @Override
            public void transactionStart(Application application,
                    			 Object transactionData) {
        	String 		m = "transactionStart";
        	
                // Transaction listener gets fired for all (Http) sessions
                // of Vaadin applications, checking to be this one.
                if (application != app)
                    return;

                if (getEntityManager().getTransaction().isActive() == false) {
        	    log.debug(m, "TRANSACTION START");
            	    getEntityManager().getTransaction().begin();
        	}
            }

	    @Override
            public void transactionEnd(Application application,
        	    			Object transactionData) {
        	String 		m = "transactionEnd";
        	
                // Transaction listener gets fired for all (Http) sessions
                // of Vaadin applications, checking to be this one.
                if (application != app)
                    return;
                
                if (RequestHandler.hasEntityManager() &&
                	getEntityManager().getTransaction().isActive() == true) {
                    log.debug(m, "TRANSACTION END");
                    getEntityManager().getTransaction().commit();
                }
            }
        });
    }

    /**
     * Helper function to get EntityManager.
     * @return EntityManager
     */
    private EntityManager getEntityManager() {
	return RequestHandler.getEntityManager(); 
    }

    protected void populateAndConfigureTable() {
	String		m = "populate";
	
	log.debug(m, "Configure Table");
	setStyleName("assettable");
	setSizeFull();
	setWidth("100%");
	
	// Allow selection of rows
        setSelectable(true);
        // Multi selection
        setMultiSelect(true);
        // Immediately notify server on events
        setImmediate(true);
        
        setColumnCollapsingAllowed(true);
        setColumnReorderingAllowed(true);
        setPageLength(linesPerPage);
        
        /*
         * Create container
         */
        loadAssets();
        
        /*
         * Add the column IDs and Labels in order shown.
         * Must do this after container is created.
         */
        log.debug(m, "Adding Columns");
        columnIds.add(ID_DEV_PHONE_NUMBER);       	columnLabels.add("Device Phone Number");
        columnIds.add(ID_DEV_PRIMARY_COUNTRY);		columnLabels.add("Device Country");
        columnIds.add(ID_USER_NAME);			columnLabels.add("User's Name");
        columnIds.add(ID_OWNER_NAME);		        columnLabels.add("Owner From User");
        columnIds.add(ID_USER_EMAIL);		        columnLabels.add("User's Name");
        columnIds.add(ID_ASSET_TAG);		        columnLabels.add("Asset Tag");
        columnIds.add(ID_OS_NAME);		        columnLabels.add("OS Name");
        columnIds.add(ID_MOBILE_PROVIDER);	        columnLabels.add("Mobile Provider");
        columnIds.add(ID_HW_VENDOR);		        columnLabels.add("HW Vendor");
        columnIds.add(ID_HW_MODEL);		        columnLabels.add("HW Model");
        columnIds.add(ID_DEV_TYPE);		        columnLabels.add("Device Type");
        columnIds.add(ID_RADIO_TYPE);		        columnLabels.add("Radio Type");
        columnIds.add(ID_CREATED);		        columnLabels.add("Registered");
        columnIds.add(ID_UPDATED);		        columnLabels.add("Last Update");
        columnIds.add(ID_STATUS);		        columnLabels.add("Status");

        /*
         * These properties are not visible, but required.
         */
        container.addContainerProperty(ID_DBID, Long.class, 0, true, true);
        container.addContainerProperty(ID_ID, String.class, "", true, true);
        
        /*
         * Define each column in the container.
         */
        for (int i = 0; i < columnIds.size(); ++i) {
            String id = columnIds.get(i).toString();
            /*
             * Adds a new property to the definition.
             * @param propertyId Id of the property.
             * @param type Value class of the property.
             * @param defaultValue Default value of the property.
             * @param readOnly Read only state of the property.
             * @param sortable Sortable state of the property.
             * @return always true.
             */
            Class<?> propClass = String.class;
            if (id.equals(ID_CREATED) || id.equals(ID_UPDATED))
        	propClass = Date.class;
            container.addContainerProperty(id, propClass, "", true, true);
        }

        if (log.isDebugOn()) {
            @SuppressWarnings("unchecked")
	    Collection<String> ids = (Collection<String>) container.getContainerPropertyIds();
            Iterator<String> iter = ids.iterator();
            log.debug(m, "Property IDs from container:");
            while (iter.hasNext()) {
        	String id = iter.next();
        	log.debug(m, "Property ID=<%s>", id);
            }
        }
        
        setVisibleColumns(columnIds.toArray());
        setColumnHeaders(columnLabels.toArray(new String[0]
));
    }

    protected void loadAssets() {
	String		m = "loadAssets";
	
	log.debug(m, "Get EntityManager");
	EntityManager em = getEntityManager();
	
	/*
	 * EntityContainer JavaDoc:
	 * Constructor which configures query definition for accessing JPA entities.
	 * @param entityManager The JPA EntityManager.
	 * @param applicationManagedTransactions True if application manages transactions instead of container.
	 * @param detachedEntities True if entities are detached from PersistenceContext.
	 * @param compositeItems True f items are wrapped to CompositeItems.
	 * @param entityClass The entity class.
	 * @param batchSize The batch size.
	 * @param nativeSortPropertyIds Properties participating in the native sort.
	 * @param nativeSortPropertyAscendingStates List of property sort directions for the native sort.
	 */
	log.debug(m, "Create LazyQueryContainer");
	container = new EntityContainer<Asset>(em, 
		false, 			// True if application manages transactions instead of container.
		true, 			// True if entities are detached from PersistenceContext.
		true, 			// True if items are wrapped to CompositeItems.
		Asset.class,		// The entity class.
		linesPerPage * 2, 	// The batch size.
		new Object[] { ID_DBID }, // Properties participating in the native sort.
                new boolean[] { true }); // List of property sort directions for the native sort.
	
        log.debug(m, "Add Container data source");
        // Set table container
        setContainerDataSource(container);
        
        log.debug(m, "Creating GeneratedColumn classes");
        
        /*
         * Primary User full name
         */
        addGeneratedColumn(ID_USER_NAME, new ColumnGenerator() {
	    public Component generateCell(Table source,
        	    final Object itemId, Object columnId) {
        	String		m = "generateCell-" + ID_USER_NAME;

        	Asset asset = getAssetFromItem(itemId, m);
        	if (asset == null) {
        	    return null;
        	}

        	Entity entity = EntityTool.getByType(asset, EntityType.USER);
        	if (entity == null) {
        	    log.debug(m, "No User entity found");
        	    return null;
        	}

        	String value = EntityTool.getName(entity);
        	if (value == null) {
        	    log.debug(m, "No value found");
        	    return null;
        	}
        	log.debug(m, "Value=<%s>", value);

        	Label label = new Label(value);
        	
        	return label;
            }
        });

... lots more column generators ....

Hi Folks,

I’m still stuck on this one. Anybody have any suggestions?

Thanks in advance!

mike

I’ve been trying to debug this by adding some debug messages to LazyQueryContainer and have these new observations, though not a fix yet.

There are two passes made through lazyquerycontainer.EntityQuery.loadItems(). The first pass succeeds with these debug statements:


queryItem: batch=50 index=0 startIndex=0 count=50 getQuery.size=59
loadItems: EntityManager isOpen=true select=<select e from Asset as e order by e.dbID asc>

For the second pass the EntityManager is not Open:


queryItem: batch=50 index=50 startIndex=50 count=9 getQuery.size=59
loadItems: EntityManager isOpen=false select=<select e from Asset as e order by e.dbID asc>
Sep 14, 2011 3:32:19 PM com.vaadin.Application terminalError
SEVERE: Terminal error:
java.lang.IllegalStateException: EntityManager is closed
	at org.hibernate.ejb.EntityManagerImpl.getSession(EntityManagerImpl.java:89)
	at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:272)
	at org.vaadin.addons.lazyquerycontainer.EntityQuery.loadItems(EntityQuery.java:122)
	at org.vaadin.addons.lazyquerycontainer.LazyQueryView.queryItem(LazyQueryView.java:247)
	at org.vaadin.addons.lazyquerycontainer.LazyQueryView.getItem(LazyQueryView.java:224)
	at org.vaadin.addons.lazyquerycontainer.LazyQueryContainer.getItem(LazyQueryContainer.java:176)
	at org.vaadin.addons.lazyquerycontainer.LazyQueryContainer.getContainerProperty(LazyQueryContainer.java:186)
	at com.vaadin.ui.AbstractSelect.getContainerProperty(AbstractSelect.java:745)
	at vortex.console.ui.AssetsTable.getAssetFromItem(AssetsTable.java:913)
	at vortex.console.ui.AssetsTable.access$0(AssetsTable.java:898)
	at vortex.console.ui.AssetsTable$4.generateCell(AssetsTable.java:458)
.... truncated ....

The loadItems() method from LQC looks like this:


    public List<Item> loadItems(final int startIndex, final int count) {
	System.err.format("loadItems: EntityManager isOpen=%s select=<%s>\n", entityManager.isOpen(), selectPsql);
        javax.persistence.Query query = entityManager.createQuery(selectPsql);
        if (selectParameters != null) {
            for (String parameterKey : selectParameters.keySet()) {
                query.setParameter(parameterKey, selectParameters.get(parameterKey));
            }
        } 
        query.setFirstResult(startIndex);
        query.setMaxResults(count);

        List<?> entities = query.getResultList();
        List<Item> items = new ArrayList<Item>();
        for (Object entity : entities) {
            if (queryDefinition.isDetachedEntities()) {
        	System.err.format("loadItems: EntityManager detach\n");
                entityManager.detach(entity);
            }
            items.add(toItem(entity));
        }

	System.err.format("loadItems: DONE: EntityManager isOpen=%s\n", entityManager.isOpen());
        return items;
    }

The EntityManager is being closed somewhere, but I can’t figure out where. This failure occurs after 50 items have been loaded out of a total of 59 items. The batchSize given to LQC is 50 so this is probably not a coincidence.

I’ve checked for calls to EntityManager.close() in LQC and don’t see any.

I’ve put trace calls in my own code looking for closures of EntityManager and don’t see any.

I’ve observed that LQC is making 2 separate web requests which may be mucking with EntityManager. During the first pass building the first 25 items in the container (the number of rows in the Table to be displayed), everything works fine. That is part of the original request. When LQC loads the next 25 it makes a new web request to my server. I think it’s still using the EntityManager object passed to it when the EntityContainer() object is created. If this is true, I’m not sure how this is suppose to work.

Still stuck. :frowning:

mike

LQC does not make requests to your web server.

Check how your web application manages your entity manager: do you create it yourself ? Do you allocate one for each HTTP request and close it at the end of the request ? This is a very common pattern. If so, you have to be careful not to close it and pull the rug under the container.

Ah, yes I have it working now. I solved the problem by making sure my Request Filter at the http level did not close EntityManager at the end of the
request. This leaves it available for LQC to continue to use.

Hey,

i was using a Spring OpenEntityManagerInViewFilter and everything was working great until i was trying out the LQC. I hit the same problem.

Keeping the session open after the request ends fixes the problem, but who closes the session?

What is the best strategy to manage the entity manager in this case? Keep a single EntityManager per session? If many clients use the application at once, this will be a scalability issue, or not?

I hope someone can give me a hint. Thanks

Entity managers are lightweight objects. There are two typical patterns. If your user sees other people’s data, and must be in sync, the entity manager is opened on the http request and closed when it ends. If your user sees only his own data, you may keep an entity manager attached to your application (and if you need to see synced data, create new ones to fetch was is actually in the database).

An entity manager is just a cache for objects, from which you execute your transactions.

Entity managers can be controlled by the application server, in which case you don’t allocate them, the container does it in accordance to the needs of transaction management (e.g., on each request, from a pool). I would have expected Spring to do this for you, maybe there are options to let it do this under tc ?