001/*
002 * (C) Copyright 2006-2010 Nuxeo SAS (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl-2.1.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     bstefanescu
016 */
017package org.nuxeo.ecm.platform.uidgen;
018
019import java.util.concurrent.ExecutionException;
020import java.util.concurrent.Future;
021import java.util.concurrent.LinkedBlockingQueue;
022import java.util.concurrent.ThreadPoolExecutor;
023import java.util.concurrent.TimeUnit;
024
025import javax.persistence.EntityManager;
026import javax.persistence.NoResultException;
027
028import org.nuxeo.common.utils.ExceptionUtils;
029import org.nuxeo.ecm.core.api.NuxeoException;
030import org.nuxeo.ecm.core.persistence.PersistenceProvider;
031import org.nuxeo.ecm.core.persistence.PersistenceProvider.RunCallback;
032import org.nuxeo.ecm.core.uidgen.AbstractUIDSequencer;
033import org.nuxeo.ecm.core.persistence.PersistenceProviderFactory;
034import org.nuxeo.runtime.api.Framework;
035import org.nuxeo.runtime.transaction.TransactionHelper;
036
037/**
038 * This implementation uses a static persistence provider to be able to instantiate this class without passing by
039 * Framework.getService -> this is to avoid potential problems do to sequencer factories. Anyway sequencer factories
040 * should be removed (I don't think they are really needed).
041 *
042 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
043 */
044public class JPAUIDSequencerImpl extends AbstractUIDSequencer {
045
046    private static volatile PersistenceProvider persistenceProvider;
047
048    protected ThreadPoolExecutor tpe = new ThreadPoolExecutor(1, 2, 10, TimeUnit.SECONDS,
049            new LinkedBlockingQueue<Runnable>(1000));
050
051    public JPAUIDSequencerImpl() {
052    }
053
054    @Override
055    public void init() {
056        // NOP
057    }
058
059    /**
060     * Must be called when the service is no longer needed
061     */
062    @Override
063    public void dispose() {
064        deactivatePersistenceProvider();
065        tpe.shutdownNow();
066    }
067
068    protected PersistenceProvider getOrCreatePersistenceProvider() {
069        if (persistenceProvider == null) {
070            synchronized (JPAUIDSequencerImpl.class) {
071                if (persistenceProvider == null) {
072                    activatePersistenceProvider();
073                }
074            }
075        }
076        return persistenceProvider;
077    }
078
079    protected static void activatePersistenceProvider() {
080        Thread thread = Thread.currentThread();
081        ClassLoader last = thread.getContextClassLoader();
082        try {
083            thread.setContextClassLoader(PersistenceProvider.class.getClassLoader());
084            PersistenceProviderFactory persistenceProviderFactory = Framework.getLocalService(PersistenceProviderFactory.class);
085            persistenceProvider = persistenceProviderFactory.newProvider("NXUIDSequencer");
086            persistenceProvider.openPersistenceUnit();
087        } finally {
088            thread.setContextClassLoader(last);
089        }
090    }
091
092    private static void deactivatePersistenceProvider() {
093        if (persistenceProvider != null) {
094            synchronized (JPAUIDSequencerImpl.class) {
095                if (persistenceProvider != null) {
096                    persistenceProvider.closePersistenceUnit();
097                    persistenceProvider = null;
098                }
099            }
100        }
101    }
102
103    protected class SeqRunner implements Runnable {
104
105        protected final String key;
106
107        protected int result;
108
109        protected boolean completed = false;
110
111        public SeqRunner(final String key) {
112            this.key = key;
113        }
114
115        @Override
116        public void run() {
117            TransactionHelper.startTransaction();
118            try {
119                result = doGetNext(key);
120                completed = true;
121            } finally {
122                TransactionHelper.commitOrRollbackTransaction();
123            }
124
125        }
126
127        public int getResult() {
128            return result;
129        }
130
131        public boolean isCompleted() {
132            return completed;
133        }
134
135    }
136
137    @Override
138    public int getNext(final String key) {
139
140        SeqRunner runner = new SeqRunner(key);
141
142        Future<?> future = tpe.submit(runner);
143
144        try {
145            future.get();
146        } catch (InterruptedException | ExecutionException e) { // deals with interrupt below
147            ExceptionUtils.checkInterrupt(e);
148            throw new NuxeoException(e);
149        }
150
151        return runner.getResult();
152
153    }
154
155    @SuppressWarnings("boxing")
156    protected int doGetNext(final String key) {
157        return getOrCreatePersistenceProvider().run(true, new RunCallback<Integer>() {
158            @Override
159            public Integer runWith(EntityManager em) {
160                return getNext(em, key);
161            }
162        });
163    }
164
165    protected int getNext(EntityManager em, String key) {
166        UIDSequenceBean seq;
167        try {
168            seq = (UIDSequenceBean) em.createNamedQuery("UIDSequence.findByKey").setParameter("key", key).getSingleResult();
169            // createQuery("FROM UIDSequenceBean seq WHERE seq.key = :key")
170        } catch (NoResultException e) {
171            seq = new UIDSequenceBean(key);
172            em.persist(seq);
173        }
174        return seq.nextIndex();
175    }
176
177}