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}