Error when using Bitsy in a WAR

Issue #14 resolved
xlorepdarkhelm created an issue

When I build a bitsy graph, and expose it to two different servlets, it works on the first servlet, but when I try the second servlet, I get the following error:

com.lambdazen.bitsy.BitsyException: INSTANCE_ALREADY_EXISTS: A BitsyGraph object with the same path has been registered with the MBeanServer. Creating multiple instances of BitsyGraph (without calling shutdown) will cause data corruption. Path C:\Users\QTB0065\AppData\Local\Temp\pampas\graph

I am not sure how I would go about solving this.

I am attempting to use Bitsy in a Glassfish 3.1 environment, through Jython, to handle a relatively small graph. I have successfully been able to create the graph, but I am now unable to continue and use the graph with other servlets.

My eventual goal would be to have a single process that is updating my graph (or builds the graph the first time), but multiple processes that can access the graph and view its contents read-only. The building/updating process might be a scheduled EJB, while the read-only functionality will be RESTful servlets.

Please help.

Comments (4)

  1. xlorepdarkhelm reporter

    This error implies that the BitsyGraph exists already, somewhere (JMX?). Is there a way I can find that instance from one servlet to the other?

  2. Sridhar Ramachandran repo owner

    You are right about JMX. Check out line 80 in https://bitbucket.org/lambdazen/bitsy/src/26997f2ac8c23c00e576f1d4eacf4205ae379b5b/src/main/java/com/lambdazen/bitsy/BitsyGraph.java?at=master&fileviewer=file-view-default

    Every BitsyGraph object is like an instance of a database. Only one object can be created for a path, otherwise the file operations will overwrite each other. However, the database object is thread-safe and you can operate on the same object from any number of threads. In other words, your servlet container can hit the same BitsyGraph object from any number of servlets simultaneously.

    Would you be able to store it in the ServletContext object as recommended at http://stackoverflow.com/questions/123657/how-can-i-share-a-variable-or-object-between-two-or-more-servlets

    The best time to create the object is when the application is loaded. This avoids the first servlet hit from paying the price for the database startup. I think the there is a way to do this using the ContextListener -- not 100% sure. Check out http://docs.oracle.com/javaee/6/api/javax/servlet/ServletContextListener.html

    Does this help?

  3. xlorepdarkhelm reporter

    Just in case you are curious, the solution I had to do was to use a Singleton Session Enterprise Java Bean, which maintains my connection, and then I can connect to that bean in my code.

    The fun on this is I am running inside of Glassfish 3.1, with JEE6 and JSE7. BUT, because I am partially a glutton for punishment, I am running my application within Jython 2.7, and am using Flask. so, my EJB looks like:

    package pampas.ejb;
    
    import java.nio.file.Path;
    import java.nio.file.Paths;
    
    import javax.annotation.PostConstruct;
    import javax.annotation.PreDestroy;
    import javax.ejb.ConcurrencyManagement;
    import javax.ejb.ConcurrencyManagementType;
    import javax.ejb.LocalBean;
    import javax.ejb.Singleton;
    import javax.ejb.Startup;
    import javax.ejb.TransactionManagement;
    import javax.ejb.TransactionManagementType;
    
    import com.lambdazen.bitsy.BitsyGraph;
    
    /**
     * Session Bean implementation class GraphBean
     */
    @Startup
    @Singleton
    @TransactionManagement(TransactionManagementType.BEAN)
    @ConcurrencyManagement(ConcurrencyManagementType.BEAN)
    @LocalBean
    public class GraphBean {
        private BitsyGraph graph;
    
        @PostConstruct
        void init() {
            Path graph_path = Paths.get(System.getProperty("java.io.tmpdir"), "pampas", "graph");
            graph_path.toFile().mkdirs();
            BitsyGraph graph = new BitsyGraph(graph_path);
            setInst(graph);
        }
    
        @PreDestroy
        void destroy() {
            getInst().shutdown();
        }
    
        private void setInst(BitsyGraph graph) {
            this.graph = graph;
        }
    
        public BitsyGraph getInst() {
            return graph;
        }
    }
    

    My Jython code looks like:

    from __future__ import division, absolute_import, print_function
    
    import collections
    import java.lang.Class
    import java.lang.Runtime
    import javax.naming.InitialContext
    
    import concurrent.futures
    import flask
    import flask_restful
    import werkzeug.local
    
    import com.lambdazen.bitsy.BitsyException
    import com.tinkerpop.blueprints.Direction
    import contextlib2 as contextlib
    import javax.persistence.Persistence as JPA
    import org.joda.time.DateTime
    
    
    app = flask.Flask(__name__.split('.')[0])
    api = flask_restful.Api(app)
    
    
    def get_graph():
        try:
            return flask.g._graph
    
        except AttributeError:
            jndi_context = javax.naming.InitialContext()
            graph = jndi_context.lookup(u'java:global/pampas-be/GraphBean')
            flask.g._graph = graph.inst
            return flask.g._graph
    
    
    graph = werkzeug.local.LocalProxy(get_graph)
    
    
    @contextlib.contextmanager
    def graph_session():
        session = graph.newTransaction()
        try:
            yield session
        except:
            session.rollback()
        finally:
            session.commit()
    
    
    class Indi(flask_restful.Resource):
        FIELDS = {
            u'indi_key',
            u'name',
            u'link',
            u'tooltip',
            u'status',
            u'last_updated',
            u'message',
            u'suppressed',
            u'key_value',
            u'edit_list',
        }
    
        def get(self, indi_key):
            def get_indi(indi):
                return {
                    field: indi.getProperty(field)
                    if field not in {u'last_updated'} or not indi.getProperty(field)
                    else org.joda.time.DateTime(
                        indi.getProperty(field)
                    ).toString()
                    for field in self.FIELDS
                }
            with graph_session() as trans:
                indi = None
                for node in trans.getVertices(u'indi_key', indi_key):
                    indi = node
                    break
                ret = {u'current': get_indi(indi)}
                children = []
                for edge in indi.getEdges(com.tinkerpop.blueprints.Direction.IN, u'ancestor'):
                    child = edge.getVertex(com.tinkerpop.blueprints.Direction.OUT)
                    children.append(get_indi(child))
                if children:
                    ret[u'indis'] = children
                return ret
    
    api.add_resource(Indi, u'/indi/<string:indi_key>')
    

    And this all works together beautifully.

  4. Log in to comment