/* * (C) Copyright 2006-2010 Nuxeo SA (http://nuxeo.com/) and others. * * 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. * * Contributors: * bstefanescu */ package org.nuxeo.ecm.platform.uidgen; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import javax.persistence.EntityManager; import javax.persistence.NoResultException; import org.nuxeo.common.utils.ExceptionUtils; import org.nuxeo.ecm.core.api.NuxeoException; import org.nuxeo.ecm.core.persistence.PersistenceProvider; import org.nuxeo.ecm.core.persistence.PersistenceProvider.RunCallback; import org.nuxeo.ecm.core.uidgen.AbstractUIDSequencer; import org.nuxeo.ecm.core.persistence.PersistenceProviderFactory; import org.nuxeo.runtime.api.Framework; import org.nuxeo.runtime.transaction.TransactionHelper; /** * This implementation uses a static persistence provider to be able to instantiate this class without passing by * Framework.getService -> this is to avoid potential problems do to sequencer factories. Anyway sequencer factories * should be removed (I don't think they are really needed). * * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> */ public class JPAUIDSequencerImpl extends AbstractUIDSequencer { private static volatile PersistenceProvider persistenceProvider; protected ThreadPoolExecutor tpe = new ThreadPoolExecutor(1, 2, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(1000)); public JPAUIDSequencerImpl() { } @Override public void init() { // NOP } /** * Must be called when the service is no longer needed */ @Override public void dispose() { deactivatePersistenceProvider(); tpe.shutdownNow(); } protected PersistenceProvider getOrCreatePersistenceProvider() { if (persistenceProvider == null) { synchronized (JPAUIDSequencerImpl.class) { if (persistenceProvider == null) { activatePersistenceProvider(); } } } return persistenceProvider; } protected static void activatePersistenceProvider() { Thread thread = Thread.currentThread(); ClassLoader last = thread.getContextClassLoader(); try { thread.setContextClassLoader(PersistenceProvider.class.getClassLoader()); PersistenceProviderFactory persistenceProviderFactory = Framework.getLocalService(PersistenceProviderFactory.class); persistenceProvider = persistenceProviderFactory.newProvider("NXUIDSequencer"); persistenceProvider.openPersistenceUnit(); } finally { thread.setContextClassLoader(last); } } private static void deactivatePersistenceProvider() { if (persistenceProvider != null) { synchronized (JPAUIDSequencerImpl.class) { if (persistenceProvider != null) { persistenceProvider.closePersistenceUnit(); persistenceProvider = null; } } } } protected class SeqRunner implements Runnable { protected final String key; protected int result; protected boolean completed = false; public SeqRunner(final String key) { this.key = key; } @Override public void run() { TransactionHelper.startTransaction(); try { result = doGetNext(key); completed = true; } finally { TransactionHelper.commitOrRollbackTransaction(); } } public int getResult() { return result; } public boolean isCompleted() { return completed; } } @Override public int getNext(final String key) { SeqRunner runner = new SeqRunner(key); Future<?> future = tpe.submit(runner); try { future.get(); } catch (InterruptedException | ExecutionException e) { // deals with interrupt below ExceptionUtils.checkInterrupt(e); throw new NuxeoException(e); } return runner.getResult(); } @SuppressWarnings("boxing") protected int doGetNext(final String key) { return getOrCreatePersistenceProvider().run(true, new RunCallback<Integer>() { @Override public Integer runWith(EntityManager em) { return getNext(em, key); } }); } protected int getNext(EntityManager em, String key) { UIDSequenceBean seq; try { seq = (UIDSequenceBean) em.createNamedQuery("UIDSequence.findByKey").setParameter("key", key).getSingleResult(); // createQuery("FROM UIDSequenceBean seq WHERE seq.key = :key") } catch (NoResultException e) { seq = new UIDSequenceBean(key); em.persist(seq); } return seq.nextIndex(); } }