/*
* #%L
* BroadleafCommerce Common Libraries
* %%
* Copyright (C) 2009 - 2014 Broadleaf Commerce
* %%
* 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.
* #L%
*/
package org.broadleafcommerce.common.util.tenant;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.broadleafcommerce.common.site.domain.Catalog;
import org.broadleafcommerce.common.site.domain.Site;
import org.broadleafcommerce.common.util.TransactionUtils;
import org.broadleafcommerce.common.web.BroadleafRequestContext;
import org.hibernate.ejb.HibernateEntityManager;
import org.springframework.orm.jpa.EntityManagerHolder;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
/**
* The utility methods in this class provide a way to ignore the currently configured site/catalog contexts and instead
* explicitly run operations in the specified context.
*
* @author Jeff Fischer
*/
public class IdentityExecutionUtils {
private static final Log LOG = LogFactory.getLog(IdentityExecutionUtils.class);
public static <T, G extends Throwable> T runOperationByIdentifier(IdentityOperation<T, G> operation, Site site, Site profile, Catalog catalog,
PlatformTransactionManager transactionManager) throws G {
IdentityUtilContext context = new IdentityUtilContext();
context.setIdentifier(site);
IdentityUtilContext.setUtilContext(context);
BroadleafRequestContext brc = BroadleafRequestContext.getBroadleafRequestContext();
Site previousSite = brc.getSite();
Catalog previousCatalog = brc.getCurrentCatalog();
Site previousProfile = brc.getCurrentProfile();
boolean isNew = initRequestContext(site, profile, catalog);
activateSession();
TransactionContainer container = null;
if (transactionManager != null) {
container = establishTransaction(transactionManager);
}
boolean isError = false;
try {
return operation.execute();
} catch (RuntimeException e) {
isError = true;
throw e;
} finally {
if (container != null) {
finalizeTransaction(transactionManager, container, isError);
}
IdentityUtilContext.setUtilContext(null);
if (isNew) {
BroadleafRequestContext.setBroadleafRequestContext(null);
}
BroadleafRequestContext.getBroadleafRequestContext().setSite(previousSite);
BroadleafRequestContext.getBroadleafRequestContext().setCurrentCatalog(previousCatalog);
BroadleafRequestContext.getBroadleafRequestContext().setCurrentProfile(previousProfile);
}
}
public static <T, G extends Throwable> T runOperationByIdentifier(IdentityOperation<T, G> operation, Site site, Catalog catalog) throws G {
return runOperationByIdentifier(operation, site, null, catalog, null);
}
public static <T, G extends Throwable> T runOperationByIdentifier(IdentityOperation<T, G> operation, Site site, Site profile, Catalog catalog) throws G {
return runOperationByIdentifier(operation, site, profile, catalog, null);
}
public static <T, G extends Throwable> T runOperationByIdentifier(IdentityOperation<T, G> operation, Site site) throws G {
return runOperationByIdentifier(operation, site, null, null, null);
}
public static <T, G extends Throwable> T runOperationByIdentifier(IdentityOperation<T, G> operation, Site site, Site profile) throws G {
return runOperationByIdentifier(operation, site, profile, null);
}
public static <T, G extends Throwable> T runOperationAndIgnoreIdentifier(IdentityOperation<T, G> operation) throws G {
return runOperationAndIgnoreIdentifier(operation, null);
}
public static <T, G extends Throwable> T runOperationAndIgnoreIdentifier(IdentityOperation<T, G> operation,
PlatformTransactionManager transactionManager) throws G {
BroadleafRequestContext brc = BroadleafRequestContext.getBroadleafRequestContext();
Site previousSite = brc.getSite();
Catalog previousCatalog = brc.getCurrentCatalog();
Site previousProfile = brc.getCurrentProfile();
boolean isNew = initRequestContext(null, null, null);
boolean isIgnoringSite = BroadleafRequestContext.getBroadleafRequestContext().getIgnoreSite();
BroadleafRequestContext.getBroadleafRequestContext().setIgnoreSite(true);
activateSession();
TransactionContainer container = null;
if (transactionManager != null) {
container = establishTransaction(transactionManager);
}
boolean isError = false;
try {
return operation.execute();
} catch (RuntimeException e) {
isError = true;
throw e;
} finally {
if (container != null) {
finalizeTransaction(transactionManager, container, isError);
}
if (isNew) {
BroadleafRequestContext.setBroadleafRequestContext(null);
}
BroadleafRequestContext.getBroadleafRequestContext().setIgnoreSite(isIgnoringSite);
BroadleafRequestContext.getBroadleafRequestContext().setSite(previousSite);
BroadleafRequestContext.getBroadleafRequestContext().setCurrentCatalog(previousCatalog);
BroadleafRequestContext.getBroadleafRequestContext().setCurrentProfile(previousProfile);
}
}
private static boolean initRequestContext(Site site, Site profile, Catalog catalog) {
boolean isNew = false;
BroadleafRequestContext requestContext = BroadleafRequestContext.getBroadleafRequestContext();
if (requestContext == null) {
requestContext = new BroadleafRequestContext();
BroadleafRequestContext.setBroadleafRequestContext(requestContext);
isNew = true;
}
requestContext.setSite(site);
requestContext.setCurrentCatalog(catalog);
requestContext.setCurrentProfile(profile);
if (site != null) {
requestContext.setIgnoreSite(false);
}
return isNew;
}
private static void activateSession() {
Map<Object, Object> resourceMap = TransactionSynchronizationManager.getResourceMap();
for (Map.Entry<Object, Object> entry : resourceMap.entrySet()) {
if (entry.getKey() instanceof EntityManagerFactory && entry.getValue() instanceof EntityManagerHolder) {
((HibernateEntityManager) ((EntityManagerHolder) entry.getValue()).getEntityManager()).getSession();
}
}
}
private static void finalizeTransaction(PlatformTransactionManager transactionManager, TransactionContainer
container, boolean error) {
TransactionUtils.finalizeTransaction(container.status, transactionManager, error);
for (Map.Entry<Object, Object> entry : container.usedResources.entrySet()) {
if (!TransactionSynchronizationManager.hasResource(entry.getKey())) {
TransactionSynchronizationManager.bindResource(entry.getKey(), entry.getValue());
}
}
}
private static TransactionContainer establishTransaction(PlatformTransactionManager transactionManager) {
Map<Object, Object> usedResources = new HashMap<Object, Object>();
Map<Object, Object> resources = TransactionSynchronizationManager.getResourceMap();
for (Map.Entry<Object, Object> entry : resources.entrySet()) {
if ((entry.getKey() instanceof EntityManagerFactory || entry.getKey() instanceof DataSource) &&
TransactionSynchronizationManager.hasResource(entry.getKey())) {
usedResources.put(entry.getKey(), entry.getValue());
}
}
for (Map.Entry<Object, Object> entry : usedResources.entrySet()) {
TransactionSynchronizationManager.unbindResource(entry.getKey());
}
TransactionStatus status;
try {
status = TransactionUtils.createTransaction(TransactionDefinition.PROPAGATION_REQUIRES_NEW,
transactionManager, false);
} catch (RuntimeException e) {
throw e;
}
return new TransactionContainer(status, usedResources);
}
private static class TransactionContainer {
TransactionStatus status;
Map<Object, Object> usedResources;
private TransactionContainer(TransactionStatus status, Map<Object, Object> usedResources) {
this.status = status;
this.usedResources = usedResources;
}
public TransactionStatus getStatus() {
return status;
}
public void setStatus(TransactionStatus status) {
this.status = status;
}
public Map<Object, Object> getUsedResources() {
return usedResources;
}
public void setUsedResources(Map<Object, Object> usedResources) {
this.usedResources = usedResources;
}
}
}