/* * Copyright 2009 NCHOVY * * Licensed 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.krakenapps.jpa.impl; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.PersistenceException; import org.hibernate.MappingException; import org.hibernate.cfg.AnnotationBinder; import org.hibernate.cfg.SettingsFactory; import org.hibernate.cfg.annotations.CollectionBinder; import org.hibernate.cfg.annotations.EntityBinder; import org.hibernate.cfg.annotations.QueryBinder; import org.hibernate.ejb.Ejb3Configuration; import org.hibernate.tool.hbm2ddl.TableMetadata; import org.krakenapps.api.LoggerControlService; import org.krakenapps.jpa.JpaProfile; import org.krakenapps.jpa.EntityManagerFactoryListener; import org.krakenapps.jpa.JpaService; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleException; import org.osgi.framework.ServiceReference; import org.osgi.service.prefs.Preferences; import org.osgi.service.prefs.PreferencesService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HibernateJpaService implements JpaService { final Logger logger = LoggerFactory.getLogger(HibernateJpaService.class.getName()); private LoggerControlService loggerControl; private BundleContext bc; private Map<String, EntityManagerFactory> factoryMap; private Map<String, JpaProfile> profileMap; private Set<EntityManagerFactoryListener> listeners; private JpaConfig jpaConfig; private static String ENTITIES = "/OSGI-INF/kraken-jpa/classes"; private static String CONFIG = "/OSGI-INF/kraken-jpa/config"; public HibernateJpaService(BundleContext bc) { logger.info("Hibernate JPA Service created."); Preferences prefs = getPreferences(bc); this.bc = bc; this.factoryMap = new ConcurrentHashMap<String, EntityManagerFactory>(); this.profileMap = new ConcurrentHashMap<String, JpaProfile>(); this.listeners = new HashSet<EntityManagerFactoryListener>(); this.jpaConfig = new JpaConfig(prefs); } private Preferences getPreferences(BundleContext context) { ServiceReference ref = context.getServiceReference(PreferencesService.class.getName()); PreferencesService prefsService = (PreferencesService) context.getService(ref); Preferences prefs = prefsService.getSystemPreferences(); return prefs; } /** * Restore all entity manager factories with saved configurations */ public void start() { disableLog(SettingsFactory.class); disableLog(TableMetadata.class); disableLog(CollectionBinder.class); disableLog(AnnotationBinder.class); disableLog(EntityBinder.class); disableLog(QueryBinder.class); loadEntityManagerFactories(); } private void disableLog(Class<?> clazz) { loggerControl.setLogLevel(clazz.getName(), "info", false); loggerControl.setLogLevel(clazz.getName(), "trace", false); loggerControl.setLogLevel(clazz.getName(), "debug", false); } /** * shutdown */ public void stop() { } @Override public EntityManagerFactory createEntityManagerFactory(Properties props, List<Class<?>> entityClasses) { Ejb3Configuration c = new Ejb3Configuration(); c.setProperties(props); for (Class<?> entityClass : entityClasses) { try { c.addAnnotatedClass(entityClass); } catch (MappingException e) { logger.error("JPA: error mapping entity class ", e); } } return c.buildEntityManagerFactory(); } @Override public Set<String> getProfileNames() { return profileMap.keySet(); } @Override public JpaProfile getProfile(String factoryName) { return profileMap.get(factoryName); } @Override public void registerEntityManagerFactory(String name, Properties properties, long bundleId) throws BundleException { Bundle bundle = bc.getBundle(bundleId); if (bundle == null) { logger.warn("JPA: bundle [{}] not found.", bundleId); throw new BundleException(bundleId + " bundle not found."); } URL entitiesFile = bundle.getEntry(ENTITIES); if (entitiesFile == null) { logger.warn("JPA: entities configuration not found: bundle " + bundleId); return; } logger.trace("JPA: adding entity classes from bundle: " + bundle.getSymbolicName()); // load and override configuration URL configFile = bundle.getEntry(CONFIG); if (configFile == null) { logger.warn("JPA: config not found."); } Properties overridedProperties = new Properties(); if (configFile != null) try { overridedProperties.load(new InputStreamReader(configFile.openStream())); } catch (IOException e1) { logger.warn("JPA: config load error: ", e1); } for (Object key : properties.keySet()) { overridedProperties.put(key, properties.get(key)); } Ejb3Configuration c = new Ejb3Configuration(); c.setProperties(overridedProperties); List<String> classNames = new ArrayList<String>(); try { InputStreamReader isr = new InputStreamReader(entitiesFile.openStream()); BufferedReader br = new BufferedReader(isr); String entity = null; while ((entity = br.readLine()) != null) { entity = entity.trim(); if (entity.startsWith("#")) continue; logger.trace("JPA: adding entity class - " + entity); try { c.addAnnotatedClass(bundle.loadClass(entity)); classNames.add(entity); } catch (Exception e) { logger.error("JPA: failed to add JPA entity: ", e); } } } catch (IOException e) { logger.error("JPA: error reading from bundle: ", e); } logger.info("JPA: generating entity manager factory for bundle " + bundleId); try { // if model is broken, buildEntityManagerFactory will throw // exception EntityManagerFactory factory = c.buildEntityManagerFactory(); // verify connection (is valid password?) EntityManager em = factory.createEntityManager(); if (em.isOpen()) { try { em.getTransaction().begin(); em.getTransaction().rollback(); } finally { em.close(); } } else throw new IllegalStateException("JDBC connection open failed"); // set config JpaProfile config = new JpaProfile(); config.setName(name); config.setProperties(overridedProperties); config.setClassNames(classNames); profileMap.put(name, config); factoryMap.put(name, factory); logger.info("JPA: entity manager factory [{}] added.", name); // send event to listeners synchronized (listeners) { for (EntityManagerFactoryListener listener : listeners) { listener.factoryAdded(name, factory); } } // save information to db jpaConfig.addEntityManagerFactoryInstance(bundleId, name, overridedProperties); } catch (PersistenceException e) { logger.warn("JPA: entity manager factory not built", e); throw e; } } @Override public void unregisterEntityManagerFactory(String name) { EntityManagerFactory factory = factoryMap.remove(name); if (factory != null && factory.isOpen()) { factory.close(); } profileMap.remove(name); // send event to listeners synchronized (listeners) { for (EntityManagerFactoryListener listener : listeners) { listener.factoryRemoved(name, factory); } } // remove from db jpaConfig.removeEntityManagerFactoryInstance(name); } @Override public boolean hasEntityManagerFactory(String factoryName) { return factoryMap.get(factoryName) != null; } @Override public EntityManagerFactory getEntityManagerFactory(String factoryName) { logger.debug("JPA: get entity manager factory [{}]", factoryName); return factoryMap.get(factoryName); } @Override public EntityManager createEntityManager(String factoryName) { EntityManagerFactory factory = factoryMap.get(factoryName); if (factory != null) { return factory.createEntityManager(); } logger.error("JPA: entity manager factory [{}] not found.", factoryName); return null; } @Override public EntityManager createEntityManager(String factoryName, Map<Object, Object> map) { EntityManagerFactory factory = factoryMap.get(factoryName); if (factory != null) { return factory.createEntityManager(map); } logger.error("JPA: entity manager factory [{}] not found.", factoryName); return null; } @Override public void addEntityManagerFactoryListener(EntityManagerFactoryListener listener) { synchronized (listeners) { listeners.add(listener); } } @Override public void removeEntityManagerFactoryListener(EntityManagerFactoryListener listener) { synchronized (listeners) { listeners.remove(listener); } } private void loadEntityManagerFactories() { List<EntityManagerFactoryInstance> instances = jpaConfig.getEntityManagerFactoryInstances(); for (EntityManagerFactoryInstance instance : instances) { String factoryName = instance.getFactoryName(); try { registerEntityManagerFactory(factoryName, instance.getProperties(), instance.getBundleId()); } catch (BundleException e) { // remove saved state if model bundle is removed. jpaConfig.removeEntityManagerFactoryInstance(factoryName); } } } }