/*
* Copyright 2011 Atteo.
*
* 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.atteo.moonshine.hibernate;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.SharedCacheMode;
import javax.persistence.ValidationMode;
import javax.persistence.spi.ClassTransformer;
import javax.persistence.spi.PersistenceProvider;
import javax.persistence.spi.PersistenceUnitInfo;
import javax.persistence.spi.PersistenceUnitTransactionType;
import javax.sql.DataSource;
import javax.validation.Validation;
import javax.validation.ValidatorFactory;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import org.atteo.classindex.ClassIndex;
import org.atteo.moonshine.ServiceConfiguration;
import org.atteo.moonshine.jpa.JpaService;
import org.atteo.moonshine.jpa.TransactionScopedEntityManager;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.ejb.HibernatePersistence;
import org.hibernate.service.jta.platform.spi.JtaPlatform;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.PrivateModule;
import com.google.inject.Provider;
import com.google.inject.Provides;
import com.google.inject.Scopes;
import com.google.inject.binder.ScopedBindingBuilder;
/**
* HibernateService - JPA implementation.
*/
@XmlRootElement(name = "hibernate")
@ServiceConfiguration(autoConfiguration = ""
+ "<initSchema>update</initSchema>")
public class HibernateService extends JpaService {
/**
* Automatically initialize database schema.
*
* <p>
* <ul>
* <li>validate: validate the schema, makes no changes to the database.</li>
* <li>update: update the schema.</li>
* <li>create: creates the schema, destroying previous data.</li>
* <li>create-drop: drop the schema at the end of the session.</li>
* </ul>
* Use migrations in production setups.
* </p>
*/
@XmlElement
private String initSchema = "validate";
/**
* Setting is relevant when using @GeneratedValue. It indicates whether
* or not the new IdentifierGenerator implementations are used for javax.persistence.GenerationType.AUTO,
* javax.persistence.GenerationType.TABLE and javax.persistence.GenerationType.SEQUENCE.
*/
@XmlElement
private boolean useNewIdGeneratorMappings = true;
/**
* If turned on, HibernateService will generate comments inside the SQL, for easier debugging.
*/
@XmlElement
private boolean useSqlComments = true;
/**
* Pretty print the SQL in the log and console.
*/
@XmlElement
private boolean formatSql = false;
/**
* Write all SQL statements to console. This is an alternative to setting the log category org.hibernate.SQL
* to debug.
*/
@XmlElement
private boolean showSql= false;
/**
* SQL dialect.
*/
@XmlElement
private String dialect;
/**
* List of HibernateService plugins.
*/
@XmlElementRef
@XmlElementWrapper(name = "plugins")
private List<HibernatePlugin> plugins;
/**
* Should HibernateService be loaded on first use.
*/
@XmlElement
private boolean lazyLoading = false;
private EntityManagerFactory factory;
private class EntityManagerFactoryProvider implements Provider<EntityManagerFactory> {
@Inject
private DataSource dataSource;
@Inject
private JtaPlatform jtaPlatform;
@Inject
private ValidatorFactory validatorFactory;
@Override
public EntityManagerFactory get() {
PersistenceUnitInfo info = new PersistenceUnitInfo() {
@Override
public String getPersistenceUnitName() {
String name = Thread.currentThread().getName();
String id = getId();
if (id == null) {
name += "-default";
} else {
name += "-" + id;
}
return name;
}
@Override
public String getPersistenceProviderClassName() {
return null;
}
@Override
public PersistenceUnitTransactionType getTransactionType() {
return PersistenceUnitTransactionType.JTA;
}
@Override
public DataSource getJtaDataSource() {
return dataSource;
}
@Override
public DataSource getNonJtaDataSource() {
return null;
}
@Override
public List<String> getMappingFileNames() {
return Collections.emptyList();
}
@Override
public List<URL> getJarFileUrls() {
return Collections.emptyList();
}
@Override
public URL getPersistenceUnitRootUrl() {
try {
return new URL("file:///Moonshine: please ignore this warning");
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
@Override
public List<String> getManagedClassNames() {
List<String> names = new ArrayList<>();
for (Class<?> klass : ClassIndex.getAnnotated(Entity.class)) {
names.add(klass.getCanonicalName());
}
return names;
}
@Override
public boolean excludeUnlistedClasses() {
return true;
}
@Override
public Properties getProperties() {
Properties properties = new Properties();
return properties;
}
@Override
public ClassLoader getClassLoader() {
return Thread.currentThread().getContextClassLoader();
}
@Override
public void addTransformer(ClassTransformer transformer) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public ClassLoader getNewTempClassLoader() {
return getClassLoader();
}
@Override
public SharedCacheMode getSharedCacheMode() {
return SharedCacheMode.UNSPECIFIED;
}
@Override
public ValidationMode getValidationMode() {
return ValidationMode.AUTO;
}
@Override
public String getPersistenceXMLSchemaVersion() {
return "";
}
};
PersistenceProvider provider = new HibernatePersistence();
Map<String, Object> map = new HashMap<>();
map.put("hibernate.search.autoregister_listeners", false);
if (plugins != null) {
for (HibernatePlugin plugin : plugins) {
map.putAll(plugin.getProperties());
}
}
map.put(AvailableSettings.JTA_PLATFORM, jtaPlatform);
map.put(AvailableSettings.HBM2DDL_AUTO, initSchema);
map.put(AvailableSettings.USE_NEW_ID_GENERATOR_MAPPINGS, useNewIdGeneratorMappings);
map.put(AvailableSettings.USE_SQL_COMMENTS, useSqlComments);
map.put(AvailableSettings.FORMAT_SQL, formatSql);
map.put(AvailableSettings.SHOW_SQL, showSql);
if (dialect != null) {
map.put(AvailableSettings.DIALECT, dialect);
}
map.put("javax.persistence.validation.factory", validatorFactory);
factory = provider.createContainerEntityManagerFactory(info, map);
return factory;
}
}
@Override
public Module configure() {
return new PrivateModule() {
@Provides
@Singleton
public ValidatorFactory provideValidatorFactory(Injector injector) {
GuiceConstraintValidatorFactory factory = new GuiceConstraintValidatorFactory(
injector);
factory.setDefaultFactory(Validation.byDefaultProvider()
.configure().getDefaultConstraintValidatorFactory());
return Validation.byDefaultProvider().configure()
.constraintValidatorFactory(factory)
.buildValidatorFactory();
}
@Override
protected void configure() {
bind(JtaPlatform.class).to(CustomJtaPlatform.class).in(Scopes.SINGLETON);
ScopedBindingBuilder binding;
binding = bind(EntityManagerFactory.class).toProvider(new EntityManagerFactoryProvider());
if (lazyLoading) {
binding.in(Scopes.SINGLETON);
} else {
binding.asEagerSingleton();
}
expose(EntityManagerFactory.class);
bind(EntityManager.class).to(TransactionScopedEntityManager.class).in(Scopes.SINGLETON);
expose(EntityManager.class);
}
};
}
@Override
public void close() {
if (factory != null && factory.isOpen()) {
factory.close();
}
}
}