/** * Copyright (C) 2012 Stephan Classen * Based on guice-perist (Copyright (C) 2010 Google, Inc.) * * 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 com.github.sclassen.guicejpa; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.inject.matcher.Matchers.any; import java.lang.reflect.AnnotatedElement; import java.util.ArrayList; import java.util.List; import java.util.Properties; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.transaction.UserTransaction; import org.aopalliance.intercept.MethodInterceptor; import com.google.inject.AbstractModule; import com.google.inject.matcher.Matcher; import com.google.inject.matcher.Matchers; /** * Main module of the jpa-persistence guice extension. * <p/> * Add either a {@link ApplicationManagedPersistenceUnitModule} or a * {@link ContainerManagedPersistenceUnitModule} per persistence unit using the methods * <ul> * <li>{@link #add(ApplicationManagedPersistenceUnitModule)}</li> * <li>{@link #addApplicationManagedPersistenceUnit(String)}</li> * <li>{@link #addApplicationManagedPersistenceUnit(String, Properties)}</li> * <li>{@link #add(ContainerManagedPersistenceUnitModule)}</li> * <li>{@link #addContainerManagedPersistenceUnit(String)}</li> * <li>{@link #addContainerManagedPersistenceUnit(String, Properties)}</li> * </ul> * <p/> * If container managed persistence units have been added and JTA transactions are supported. * Use {@link #setUserTransactionJndiName(String)} to define the JNDI name of the * {@link UserTransaction} provided by the container. * * @author Stephan Classen */ public final class PersistenceModule extends AbstractModule { // ---- Members /** List of all module builders. */ private final List<PersistenceUnitBuilder> moduleBuilders = new ArrayList<PersistenceUnitBuilder>(); /** * List of all persistence unit modules. * If this list is empty it means that configure has not yet been called */ private final List<AbstractPersistenceUnitModule> modules = new ArrayList<AbstractPersistenceUnitModule>(); /** Container for holding all registered persistence units. */ private final PersistenceUnitContainer puContainer = new PersistenceUnitContainer(); /** * The JNDI name to lookup the {@link UserTransaction}. */ private String utJndiName; /** * The {@link UserTransactionFacade}. */ private UserTransactionFacade utFacade = null; /** * The {@link PersistenceExceptionTranslator}. */ private PersistenceExceptionTranslator<?> peTranslator = null; // ---- Methods /** * Adds an application managed persistence unit. * * @param puName the name of the persistence unit as specified in the persistence.xml. Must not be {@code null}. * @return a builder to further configure the persistence unit. */ public PersistenceUnitBuilder addApplicationManagedPersistenceUnit(String puName) { checkNotNull(puName); return add(new ApplicationManagedPersistenceUnitModule(puName)); } /** * Adds an application managed persistence unit. * * @param puName the name of the persistence unit as specified in the persistence.xml. Must not be {@code null}. * @param properties the properties to pass to the {@link EntityManagerFactory}. Must not be {@code null}. * @return a builder to further configure the persistence unit. */ public PersistenceUnitBuilder addApplicationManagedPersistenceUnit(String puName, Properties properties) { checkNotNull(puName); checkNotNull(properties); return add(new ApplicationManagedPersistenceUnitModule(puName, properties)); } /** * Adds an application managed persistence unit. * * @param module the module of the persistence unit. Must not be {@code null}. * @return a builder to further configure the persistence unit. */ public PersistenceUnitBuilder add(ApplicationManagedPersistenceUnitModule module) { ensureConfigurHasNotYetBeenExecuted(); checkNotNull(module); final PersistenceUnitBuilder builder = new PersistenceUnitBuilder(module); moduleBuilders.add(builder); return builder; } /** * Adds an container managed persistence unit. * * @param emfJndiName the JNDI name of the {@link EntityManagerFactory}. Must not be {@code null}. * @return a builder to further configure the persistence unit. */ public PersistenceUnitBuilder addContainerManagedPersistenceUnit(String emfJndiName) { checkNotNull(emfJndiName); return add(new ContainerManagedPersistenceUnitModule(emfJndiName)); } /** * Adds an container managed persistence unit. * * @param emfJndiName the JNDI name of the {@link EntityManagerFactory}. Must not be {@code null}. * @param properties the properties to pass to the {@link EntityManager}. Must not be {@code null}. * @return a builder to further configure the persistence unit. */ public PersistenceUnitBuilder addContainerManagedPersistenceUnit(String emfJndiName, Properties properties) { checkNotNull(emfJndiName); checkNotNull(properties); return add(new ContainerManagedPersistenceUnitModule(emfJndiName, properties)); } /** * Adds an container managed persistence unit. * * @param module the module of the persistence unit. Must not be {@code null}. * @return a builder to further configure the persistence unit. */ public PersistenceUnitBuilder add(ContainerManagedPersistenceUnitModule module) { ensureConfigurHasNotYetBeenExecuted(); checkNotNull(module); final PersistenceUnitBuilder builder = new PersistenceUnitBuilder(module); moduleBuilders.add(builder); return builder; } /** * Setter for defining the JNDI name of the container managed {@link UserTransaction}. * * @param utJndiName the JNDI name of the container managed {@link UserTransaction}. */ public void setUserTransactionJndiName(String utJndiName) { ensureConfigurHasNotYetBeenExecuted(); this.utJndiName = utJndiName; } /** * Setter for defining the {@link PersistenceExceptionTranslator}. * * @param peTranslator the {@link PersistenceExceptionTranslator} implementation */ public void setPersistenceExceptionTranslator(PersistenceExceptionTranslator<?> peTranslator) { ensureConfigurHasNotYetBeenExecuted(); this.peTranslator = peTranslator; } /** * {@inheritDoc} */ @Override protected void configure() { if (configureHasNotBeenExecutedYet()) { if (0 == moduleBuilders.size()) { addError("no persistence units defined. At least one persistence unit is required."); return; } initUserTransactionFacade(); for (PersistenceUnitBuilder builder : moduleBuilders) { final AbstractPersistenceUnitModule module = builder.build(); puContainer.add(module.getPersistenceService(), module.getUnitOfWork()); modules.add(module); } } for (AbstractPersistenceUnitModule module : modules) { install(module); final Matcher<AnnotatedElement> matcher = Matchers.annotatedWith(Transactional.class); final MethodInterceptor transactionInterceptor = module.getTransactionInterceptor(utFacade, peTranslator); bindInterceptor(matcher, any(), transactionInterceptor); bindInterceptor(any(), matcher, transactionInterceptor); } bind(PersistenceService.class).annotatedWith(AllPersistenceUnits.class).toInstance(puContainer); bind(UnitOfWork.class).annotatedWith(AllPersistenceUnits.class).toInstance(puContainer); bind(PersistenceFilter.class).toInstance(new PersistenceFilter(puContainer)); } /** * @return {@code true} if {@link #configure()} has not yet been invoked. */ private boolean configureHasNotBeenExecutedYet() { return modules.size() == 0; } /** * Make sure that the {@link #configure()} method has not been executed yet. */ private void ensureConfigurHasNotYetBeenExecuted() { if (configureHasNotBeenExecutedYet()) { return; } throw new IllegalStateException("cannot change a module after creating the injector."); } /** * Initializes the field {@link #utFacade} with the {@link UserTransaction} obtained by a * JNDI lookup. */ private void initUserTransactionFacade() { if (null != utJndiName) { try { final InitialContext ctx = new InitialContext(); final UserTransaction txn = (UserTransaction) ctx.lookup(utJndiName); utFacade = new UserTransactionFacade(txn); } catch (NamingException e) { addError("lookup for UserTransaction with JNDI name '%s' failed", utJndiName); } } } }