Question

I'm using JDO with datanucleus-mongodb 3.2.3 and spring 3.0.7 on top of karaf 2.2.10.

On a fresh OSGi bundle install I'm able to persist on mongodb without issues, but after a bundle reinstall, I get the following error while building a new JDO PersistenceManagerFactory:

ERROR: Bundle [my_bundle] [242] Unable to get module class path.
(java.lang.IllegalStateException: zip file closed)      

Where the ID (242) refers to the first assigned ID to the bundle (in other words, the ID of the old bundle).

The context of the previous bundle seems to be properly closed, as well as the "old" JDO PersistenceManagerFactory.

My local persistence manager factory bean class (based on the example provided in the dn site):

public class OSGiLocalPersistenceManagerFactoryBean
    extends LocalPersistenceManagerFactoryBean implements BundleContextAware {

    public static final String JDO_BUNDLE_NAME    = "org.datanucleus.api.jdo";
    public static final String JDO_PMF_CLASS_NAME = "org.datanucleus.api.jdo.JDOPersistenceManagerFactory";

    private BundleContext bundleContext;

    @Override
    protected PersistenceManagerFactory newPersistenceManagerFactory(String name) {
        return JDOHelper.getPersistenceManagerFactory(name, getClassLoader());
    }

    @Override
    protected PersistenceManagerFactory newPersistenceManagerFactory(Map props) {
        ClassLoader classLoader = getClassLoader();

        props.put("datanucleus.primaryClassLoader", classLoader);

        if (FrameworkUtil.getBundle(this.getClass()) != null) { // running in OSGi
            props.put("datanucleus.plugin.pluginRegistryClassName", "org.datanucleus.plugin.OSGiPluginRegistry");
        }

        PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory(props, classLoader);

        return pmf;
    }

    private ClassLoader getClassLoader() {
        ClassLoader classLoader = null;
        Bundle thisBundle = FrameworkUtil.getBundle(this.getClass());

        if (thisBundle != null) { // on OSGi runtime
            Bundle[] bundles = bundleContext.getBundles();

            for (Bundle bundle : bundles) {
                if (JDO_BUNDLE_NAME.equals(bundle.getSymbolicName())) {
                    try {
                        classLoader = bundle.loadClass(JDO_PMF_CLASS_NAME).getClassLoader();
                    } catch (ClassNotFoundException e) {
                        // do something fancy here ...
                    }
                    break;
                }
            }
        } else { // somewhere else
            classLoader = this.getClass().getClassLoader();
        }
        return classLoader;
    }

    @Override
    public void setBundleContext(BundleContext bundleContext) {
        this.bundleContext = bundleContext;
    }
}

Some persistence related excerpts from the context:

<!-- persistence manager factory -->
<bean id="pmf" class="[my_package].OSGiLocalPersistenceManagerFactoryBean">
    <property name="configLocation" value="classpath:datanucleus.properties" />
</bean>

<!-- persistence manager factory proxy -->
<bean id="persistenceManagerFactoryProxy"
      class="org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy">
    <property name="targetPersistenceManagerFactory" ref="pmf" />
</bean>

<!-- transactions -->
<bean id="transactionManager" class="org.springframework.orm.jdo.JdoTransactionManager">
    <property name="persistenceManagerFactory" ref="persistenceManagerFactoryProxy" />
</bean>

<tx:annotation-driven />

The error message seems to point to a classloading issue, but then, why does it fail only during re-deployment?

Any hint would be appreciated!

Was it helpful?

Solution

Make sure you also refresh the depending bundles, cause those usually stick to the old uninstalled bundle. It's still there until all references are cleared. In the Karaf shell you'll just need to issue refresh or simple refresh without the ID, this will cause the complete container to do a re-wiring :)

OTHER TIPS

For those who cannot simply refresh javax.jdo make sure you recreate your custom ClassLoader while recreating the PersistenceManagerFactory. Datanucleus will not try to load the same class twice if the ClassLoader has not changed. You will end up having ClassCastExceptions saying Entity.class cannot be cast to Entity.class.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top