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.persistence.PersistenceProviderFactory; 035import org.nuxeo.ecm.core.uidgen.AbstractUIDSequencer; 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.getService(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 void initSequence(String key, long id) { 141 while (getNextLong(key) < id) { 142 continue; 143 } 144 } 145 146 @Override 147 public long getNextLong(final String key) { 148 149 SeqRunner runner = new SeqRunner(key); 150 151 Future<?> future = tpe.submit(runner); 152 153 try { 154 future.get(); 155 } catch (InterruptedException | ExecutionException e) { // deals with interrupt below 156 ExceptionUtils.checkInterrupt(e); 157 throw new NuxeoException(e); 158 } 159 160 return runner.getResult(); 161 162 } 163 164 @SuppressWarnings("boxing") 165 protected int doGetNext(final String key) { 166 return getOrCreatePersistenceProvider().run(true, new RunCallback<Integer>() { 167 @Override 168 public Integer runWith(EntityManager em) { 169 return getNext(em, key); 170 } 171 }); 172 } 173 174 protected int getNext(EntityManager em, String key) { 175 UIDSequenceBean seq; 176 try { 177 seq = (UIDSequenceBean) em.createNamedQuery("UIDSequence.findByKey").setParameter("key", key).getSingleResult(); 178 // createQuery("FROM UIDSequenceBean seq WHERE seq.key = :key") 179 } catch (NoResultException e) { 180 seq = new UIDSequenceBean(key); 181 em.persist(seq); 182 } 183 return seq.nextIndex(); 184 } 185 186}