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