| /******************************************************************************* |
| * 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.ofbiz.service; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.WeakHashMap; |
| |
| import javax.transaction.RollbackException; |
| import javax.transaction.Status; |
| import javax.transaction.Synchronization; |
| import javax.transaction.SystemException; |
| import javax.transaction.Transaction; |
| |
| import org.apache.ofbiz.base.util.Debug; |
| import org.apache.ofbiz.entity.transaction.GenericTransactionException; |
| import org.apache.ofbiz.entity.transaction.TransactionFactoryLoader; |
| import org.apache.ofbiz.entity.transaction.TransactionUtil; |
| |
| /** |
| * This class is used to execute services when a transaction is either |
| * committed or rolled back. It should generally be accessed via |
| * LocalDispatcher's addCommitService and addRollbackService methods |
| * or by using the service ECA event attribute values global-commit, |
| * global-rollback or global-commit-post-run |
| * |
| */ |
| public class ServiceSynchronization implements Synchronization { |
| |
| public static final String MODULE = ServiceSynchronization.class.getName(); |
| |
| private static Map<Transaction, ServiceSynchronization> syncingleton = new WeakHashMap<Transaction, ServiceSynchronization>(); |
| private List<ServiceExecution> services = new ArrayList<ServiceExecution>(); |
| |
| public static void registerCommitService(DispatchContext dctx, String serviceName, String runAsUser, Map<String, ? extends Object> context, boolean async, boolean persist) throws GenericServiceException { |
| ServiceSynchronization sync = ServiceSynchronization.getInstance(); |
| if (sync != null) { |
| sync.services.add(new ServiceExecution(dctx, serviceName, runAsUser, context, async, persist, false)); |
| } |
| } |
| |
| public static void registerRollbackService(DispatchContext dctx, String serviceName, String runAsUser, Map<String, ? extends Object> context, boolean async, boolean persist) throws GenericServiceException { |
| ServiceSynchronization sync = ServiceSynchronization.getInstance(); |
| if (sync != null) { |
| sync.services.add(new ServiceExecution(dctx, serviceName, runAsUser, context, async, persist, true)); |
| } |
| } |
| |
| protected static ServiceSynchronization getInstance() throws GenericServiceException { |
| ServiceSynchronization sync = null; |
| try { |
| Transaction transaction = TransactionFactoryLoader.getInstance().getTransactionManager().getTransaction(); |
| synchronized (transaction) { |
| sync = syncingleton.get(transaction); |
| if (sync == null) { |
| sync = new ServiceSynchronization(); |
| transaction.registerSynchronization(sync); |
| syncingleton.put(transaction, sync); |
| } |
| } |
| } catch (SystemException e) { |
| throw new GenericServiceException(e.getMessage(), e); |
| } catch (IllegalStateException e) { |
| throw new GenericServiceException(e.getMessage(), e); |
| } catch (RollbackException e) { |
| throw new GenericServiceException(e.getMessage(), e); |
| } |
| return sync; |
| } |
| |
| @Override |
| public void afterCompletion(int status) { |
| for (ServiceExecution serviceExec : this.services) { |
| serviceExec.runService(status); |
| } |
| } |
| |
| @Override |
| public void beforeCompletion() { |
| |
| } |
| |
| static class ServiceExecution { |
| protected DispatchContext dctx = null; |
| protected String serviceName; |
| protected String runAsUser = null; |
| protected Map<String, ? extends Object> context = null; |
| protected boolean rollback = false; |
| protected boolean persist = true; |
| protected boolean async = false; |
| |
| ServiceExecution(DispatchContext dctx, String serviceName, String runAsUser, Map<String, ? extends Object> context, boolean async, boolean persist, boolean rollback) { |
| this.dctx = dctx; |
| this.serviceName = serviceName; |
| this.runAsUser = runAsUser; |
| this.context = context; |
| this.async = async; |
| this.persist = persist; |
| this.rollback = rollback; |
| } |
| |
| protected void runService(int status) { |
| if ((status == Status.STATUS_COMMITTED && !rollback) || (status == Status.STATUS_ROLLEDBACK && rollback)) { |
| Thread thread = new Thread() { |
| @Override |
| public void run() { |
| String msgPrefix = null; |
| if (rollback) { |
| msgPrefix = "[Rollback] "; |
| } else { |
| msgPrefix = "[Commit] "; |
| } |
| |
| boolean beganTx; |
| try { |
| // begin the new tx |
| beganTx = TransactionUtil.begin(); |
| // configure and run the service |
| try { |
| // obtain the model and get the valid context |
| ModelService model = dctx.getModelService(serviceName); |
| Map<String, Object> thisContext; |
| if (model.validate) { |
| thisContext = model.makeValid(context, ModelService.IN_PARAM); |
| } else { |
| thisContext = new HashMap<String, Object>(); |
| thisContext.putAll(context); |
| } |
| |
| // set the userLogin object |
| thisContext.put("userLogin", ServiceUtil.getUserLogin(dctx, thisContext, runAsUser)); |
| if (async) { |
| if (Debug.infoOn()) Debug.logInfo(msgPrefix + "Invoking [" + serviceName + "] via runAsync", MODULE); |
| dctx.getDispatcher().runAsync(serviceName, thisContext, persist); |
| } else { |
| if (Debug.infoOn()) Debug.logInfo(msgPrefix + "Invoking [" + serviceName + "] via runSyncIgnore", MODULE); |
| dctx.getDispatcher().runSyncIgnore(serviceName, thisContext); |
| } |
| } catch (Throwable t) { |
| Debug.logError(t, "Problem calling " + msgPrefix + "service : " + serviceName + " / " + context, MODULE); |
| try { |
| TransactionUtil.rollback(beganTx, t.getMessage(), t); |
| } catch (GenericTransactionException e) { |
| Debug.logError(e, MODULE); |
| } |
| |
| } finally { |
| // commit the transaction |
| try { |
| TransactionUtil.commit(beganTx); |
| } catch (GenericTransactionException e) { |
| Debug.logError(e, MODULE); |
| } |
| } |
| } catch (GenericTransactionException e) { |
| Debug.logError(e, MODULE); |
| } |
| |
| } |
| }; |
| thread.start(); |
| } |
| } |
| } |
| |
| } |