/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.isis.core.runtime.system.persistence; import java.util.Map; import java.util.Properties; import java.util.Set; import javax.jdo.JDOHelper; import javax.jdo.PersistenceManagerFactory; import com.google.common.base.Joiner; import com.google.common.collect.Maps; import org.datanucleus.PersistenceNucleusContext; import org.datanucleus.PropertyNames; import org.datanucleus.api.jdo.JDOPersistenceManagerFactory; import org.datanucleus.metadata.MetaDataListener; import org.datanucleus.metadata.MetaDataManager; import org.datanucleus.store.StoreManager; import org.datanucleus.store.schema.SchemaAwareStoreManager; import org.apache.isis.core.commons.components.ApplicationScopedComponent; import org.apache.isis.core.commons.config.IsisConfiguration; import org.apache.isis.core.commons.factory.InstanceUtil; import org.apache.isis.core.metamodel.spec.ObjectSpecification; import org.apache.isis.core.metamodel.specloader.SpecificationLoader; import org.apache.isis.objectstore.jdo.datanucleus.CreateSchemaObjectFromClassMetadata; import org.apache.isis.objectstore.jdo.datanucleus.DataNucleusPropertiesAware; import org.apache.isis.objectstore.jdo.metamodel.facets.object.query.JdoNamedQuery; import org.apache.isis.objectstore.jdo.metamodel.facets.object.query.JdoQueryFacet; public class DataNucleusApplicationComponents implements ApplicationScopedComponent { public static final String CLASS_METADATA_LOADED_LISTENER_KEY = "classMetadataLoadedListener"; static final String CLASS_METADATA_LOADED_LISTENER_DEFAULT = CreateSchemaObjectFromClassMetadata.class.getName(); /////////////////////////////////////////////////////////////////////////// // JRebel support /////////////////////////////////////////////////////////////////////////// private static DataNucleusApplicationComponents instance; /** * For JRebel plugin */ public static MetaDataManager getMetaDataManager() { return instance != null ? ((JDOPersistenceManagerFactory)instance.persistenceManagerFactory).getNucleusContext().getMetaDataManager() : null; } public static void markAsStale() { if(instance != null) { instance.stale = true; } } private boolean stale = false; public boolean isStale() { return stale; } /////////////////////////////////////////////////////////////////////////// private final Set<String> persistableClassNameSet; private final IsisConfiguration jdoObjectstoreConfig; private final SpecificationLoader specificationLoader; private final Map<String, String> datanucleusProps; private Map<String, JdoNamedQuery> namedQueryByName; private PersistenceManagerFactory persistenceManagerFactory; public DataNucleusApplicationComponents( final IsisConfiguration configuration, final SpecificationLoader specificationLoader, final Map<String, String> datanucleusProps, final Set<String> persistableClassNameSet) { this.specificationLoader = specificationLoader; this.datanucleusProps = datanucleusProps; this.persistableClassNameSet = persistableClassNameSet; this.jdoObjectstoreConfig = configuration; initialize(); // for JRebel plugin instance = this; } private void initialize() { persistenceManagerFactory = createPmfAndSchemaIfRequired(persistableClassNameSet, datanucleusProps); namedQueryByName = catalogNamedQueries(persistableClassNameSet); } private static boolean isSchemaAwareStoreManager(Map<String,String> datanucleusProps) { // we create a throw-away instance of PMF so that we can probe whether DN has // been configured with a schema-aware store manager or not. final JDOPersistenceManagerFactory probePmf = (JDOPersistenceManagerFactory)JDOHelper.getPersistenceManagerFactory(datanucleusProps); try { final PersistenceNucleusContext nucleusContext = probePmf.getNucleusContext(); final StoreManager storeManager = nucleusContext.getStoreManager(); return storeManager instanceof SchemaAwareStoreManager; } finally { probePmf.close(); } } // REF: http://www.datanucleus.org/products/datanucleus/jdo/schema.html private PersistenceManagerFactory createPmfAndSchemaIfRequired(final Set<String> persistableClassNameSet, final Map<String, String> datanucleusProps) { PersistenceManagerFactory persistenceManagerFactory; if(isSchemaAwareStoreManager(datanucleusProps)) { // rather than reinvent too much of the wheel, we reuse the same property that DN would check // for if it were doing the auto-creation itself (read from isis.properties) final boolean createSchema = isSet(datanucleusProps, PropertyNames.PROPERTY_SCHEMA_AUTOCREATE_ALL); if(createSchema) { // we *don't* use DN's eager loading (autoStart), because doing so means that it attempts to // create the table before the schema (for any entities annotated @PersistenceCapable(schema=...) // // instead, we manually create the schema ourselves // (if the configured StoreMgr supports it, and if requested in isis.properties) // datanucleusProps.put(PropertyNames.PROPERTY_SCHEMA_AUTOCREATE_ALL, "false"); // turn off, cos want to do the schema object ourselves... datanucleusProps.put(PropertyNames.PROPERTY_SCHEMA_AUTOCREATE_SCHEMA, "false"); datanucleusProps.put(PropertyNames.PROPERTY_SCHEMA_AUTOCREATE_TABLES, "true"); // but have DN do everything else... datanucleusProps.put(PropertyNames.PROPERTY_SCHEMA_AUTOCREATE_COLUMNS, "true"); datanucleusProps.put(PropertyNames.PROPERTY_SCHEMA_AUTOCREATE_CONSTRAINTS, "true"); persistenceManagerFactory = JDOHelper.getPersistenceManagerFactory(datanucleusProps); createSchema(persistenceManagerFactory, persistableClassNameSet, datanucleusProps); } else { persistenceManagerFactory = JDOHelper.getPersistenceManagerFactory(datanucleusProps); } } else { // we *DO* use DN's eager loading (autoStart), because it seems that DN requires this (for neo4j at least) // otherwise NPEs occur later. configureAutoStart(persistableClassNameSet, datanucleusProps); persistenceManagerFactory = JDOHelper.getPersistenceManagerFactory(this.datanucleusProps); } return persistenceManagerFactory; } private void configureAutoStart(final Set<String> persistableClassNameSet, final Map<String, String> datanucleusProps) { final String persistableClassNames = Joiner.on(',').join(persistableClassNameSet); // ref: http://www.datanucleus.org/products/datanucleus/jdo/autostart.html datanucleusProps.put(PropertyNames.PROPERTY_AUTOSTART_MECHANISM, "Classes"); datanucleusProps.put(PropertyNames.PROPERTY_AUTOSTART_MODE, "Checked"); datanucleusProps.put(PropertyNames.PROPERTY_AUTOSTART_CLASSNAMES, persistableClassNames); } private void createSchema( final PersistenceManagerFactory persistenceManagerFactory, final Set<String> persistableClassNameSet, final Map<String, String> datanucleusProps) { JDOPersistenceManagerFactory jdopmf = (JDOPersistenceManagerFactory) persistenceManagerFactory; final PersistenceNucleusContext nucleusContext = jdopmf.getNucleusContext(); final SchemaAwareStoreManager schemaAwareStoreManager = (SchemaAwareStoreManager)nucleusContext.getStoreManager(); final MetaDataManager metaDataManager = nucleusContext.getMetaDataManager(); registerMetadataListener(metaDataManager, datanucleusProps); schemaAwareStoreManager.createSchemaForClasses(persistableClassNameSet, asProperties(datanucleusProps)); } private boolean isSet(final Map<String, String> props, final String key) { return Boolean.parseBoolean( props.get(key) ); } private void registerMetadataListener( final MetaDataManager metaDataManager, final Map<String, String> datanucleusProps) { final MetaDataListener listener = createMetaDataListener(); if(listener == null) { return; } if(listener instanceof DataNucleusPropertiesAware) { ((DataNucleusPropertiesAware) listener).setDataNucleusProperties(datanucleusProps); } // and install the listener for any classes that are lazily loaded subsequently // (shouldn't be any, this is mostly backwards compatibility with previous design). metaDataManager.registerListener(listener); } private MetaDataListener createMetaDataListener() { final String classMetadataListenerClassName = jdoObjectstoreConfig.getString( CLASS_METADATA_LOADED_LISTENER_KEY, CLASS_METADATA_LOADED_LISTENER_DEFAULT); return classMetadataListenerClassName != null ? InstanceUtil.createInstance(classMetadataListenerClassName, MetaDataListener.class) : null; } private static Properties asProperties(final Map<String, String> props) { final Properties properties = new Properties(); properties.putAll(props); return properties; } private Map<String, JdoNamedQuery> catalogNamedQueries(Set<String> persistableClassNames) { final Map<String, JdoNamedQuery> namedQueryByName = Maps.newHashMap(); for (final String persistableClassName: persistableClassNames) { final ObjectSpecification spec = specificationLoader.loadSpecification(persistableClassName); final JdoQueryFacet facet = spec.getFacet(JdoQueryFacet.class); if (facet == null) { continue; } for (final JdoNamedQuery namedQuery : facet.getNamedQueries()) { namedQueryByName.put(namedQuery.getName(), namedQuery); } } return namedQueryByName; } public PersistenceManagerFactory getPersistenceManagerFactory() { return persistenceManagerFactory; } }