001package co.codewizards.cloudstore.local; 002 003import static co.codewizards.cloudstore.core.util.Util.*; 004 005import java.io.File; 006import java.io.IOException; 007import java.lang.reflect.Proxy; 008import java.util.ArrayList; 009import java.util.Collections; 010import java.util.HashMap; 011import java.util.HashSet; 012import java.util.List; 013import java.util.Map; 014import java.util.Set; 015import java.util.concurrent.CopyOnWriteArrayList; 016 017import org.slf4j.Logger; 018import org.slf4j.LoggerFactory; 019 020import co.codewizards.cloudstore.core.repo.local.FileAlreadyRepositoryException; 021import co.codewizards.cloudstore.core.repo.local.LocalRepoManager; 022import co.codewizards.cloudstore.core.repo.local.LocalRepoManagerCloseEvent; 023import co.codewizards.cloudstore.core.repo.local.LocalRepoManagerCloseListener; 024import co.codewizards.cloudstore.core.repo.local.LocalRepoManagerException; 025import co.codewizards.cloudstore.core.repo.local.LocalRepoManagerFactory; 026 027/** 028 * Registry of {@link LocalRepoManager}s. 029 * <p> 030 * There is one single instance of this registry. It serves as the central point to obtain 031 * {@code LocalRepoManager}s. 032 * @author Marco หงุ่ยตระกูล-Schulze - marco at codewizards dot co 033 */ 034public class LocalRepoManagerFactoryImpl implements LocalRepoManagerFactory 035{ 036 private static final Logger logger = LoggerFactory.getLogger(LocalRepoManagerFactoryImpl.class); 037 038 private Map<File, LocalRepoManagerImpl> localRoot2LocalRepoManagerImpl = new HashMap<File, LocalRepoManagerImpl>(); 039 private Set<LocalRepoManagerImpl> nonReOpenableLocalRepoManagerImpls = new HashSet<LocalRepoManagerImpl>(); 040 041 private List<LocalRepoManagerCloseListener> localRepoManagerCloseListeners = new CopyOnWriteArrayList<LocalRepoManagerCloseListener>(); 042 043 private LocalRepoManagerCloseListener localRepoManagerCloseListener = new LocalRepoManagerCloseListener() { 044 @Override 045 public void preClose(LocalRepoManagerCloseEvent event) { 046 if (!event.isBackend()) 047 throw new IllegalStateException("Why are we notified by the proxy?!?"); 048 049 preLocalRepoManagerBackendClose(event.getLocalRepoManager()); 050 } 051 @Override 052 public void postClose(LocalRepoManagerCloseEvent event) { 053 if (!event.isBackend()) 054 throw new IllegalStateException("Why are we notified by the proxy?!?"); 055 056 postLocalRepoManagerBackendClose((LocalRepoManagerImpl) event.getLocalRepoManager()); 057 } 058 }; 059 060 @Override 061 public synchronized Set<File> getLocalRoots() { 062 return Collections.unmodifiableSet(new HashSet<File>(localRoot2LocalRepoManagerImpl.keySet())); 063 } 064 065 @Override 066 public synchronized LocalRepoManager createLocalRepoManagerForExistingRepository(File localRoot) throws LocalRepoManagerException { 067 localRoot = canonicalize(localRoot); 068 069 LocalRepoManagerImpl localRepoManagerImpl = localRoot2LocalRepoManagerImpl.get(localRoot); 070 if (localRepoManagerImpl != null && !localRepoManagerImpl.open()) { 071 localRoot2LocalRepoManagerImpl.remove(localRoot); 072 nonReOpenableLocalRepoManagerImpls.add(localRepoManagerImpl); 073 while (localRepoManagerImpl.isOpen()) { 074 logger.info("createLocalRepoManagerForExistingRepository: Existing LocalRepoManagerImpl is currently closing and could not be re-opened. Waiting for it to be completely closed."); 075 try { Thread.sleep(100); } catch (InterruptedException x) { doNothing(); } 076 } 077 localRepoManagerImpl = null; 078 } 079 080 if (localRepoManagerImpl == null) { 081 localRepoManagerImpl = new LocalRepoManagerImpl(localRoot, false); 082 if (!localRepoManagerImpl.open()) 083 throw new IllegalStateException("localRepoManagerImpl.open() of *new* instance returned false!"); 084 085 enlist(localRepoManagerImpl); 086 } 087 return createProxy(localRepoManagerImpl); 088 } 089 090 @Override 091 public synchronized LocalRepoManager createLocalRepoManagerForNewRepository(File localRoot) throws LocalRepoManagerException { 092 localRoot = canonicalize(localRoot); 093 094 LocalRepoManagerImpl localRepoManagerImpl = localRoot2LocalRepoManagerImpl.get(localRoot); 095 if (localRepoManagerImpl != null) { 096 throw new FileAlreadyRepositoryException(localRoot); 097 } 098 099 localRepoManagerImpl = new LocalRepoManagerImpl(localRoot, true); 100 if (!localRepoManagerImpl.open()) 101 throw new IllegalStateException("localRepoManagerImpl.open() of *new* instance returned false!"); 102 103 enlist(localRepoManagerImpl); 104 return createProxy(localRepoManagerImpl); 105 } 106 107 private LocalRepoManager createProxy(LocalRepoManagerImpl localRepoManagerImpl) { 108 return (LocalRepoManager) Proxy.newProxyInstance( 109 this.getClass().getClassLoader(), 110 new Class<?>[] { LocalRepoManager.class }, 111 new LocalRepoManagerInvocationHandler(localRepoManagerImpl)); 112 } 113 114 @Override 115 public synchronized void close() { 116 for (LocalRepoManagerImpl localRepoManagerImpl : new ArrayList<LocalRepoManagerImpl>(localRoot2LocalRepoManagerImpl.values())) { 117 localRepoManagerImpl.close(); 118 } 119 } 120 121 @Override 122 public void addLocalRepoManagerCloseListener(LocalRepoManagerCloseListener listener) { 123 localRepoManagerCloseListeners.add(listener); 124 } 125 126 @Override 127 public void removeLocalRepoManagerCloseListener(LocalRepoManagerCloseListener listener) { 128 localRepoManagerCloseListeners.remove(listener); 129 } 130 131 private void enlist(LocalRepoManagerImpl localRepoManager) { 132 localRoot2LocalRepoManagerImpl.put(localRepoManager.getLocalRoot(), localRepoManager); 133 localRepoManager.addLocalRepoManagerCloseListener(localRepoManagerCloseListener); 134 } 135 136 private File canonicalize(File localRoot) { 137 assertNotNull("localRoot", localRoot); 138 try { 139 localRoot = localRoot.getCanonicalFile(); 140 } catch (IOException e) { 141 throw new RuntimeException(e); 142 } 143 return localRoot; 144 } 145 146 private void preLocalRepoManagerBackendClose(LocalRepoManager localRepoManager) { 147 LocalRepoManagerCloseEvent event = new LocalRepoManagerCloseEvent(this, localRepoManager, true); 148 for (LocalRepoManagerCloseListener listener : localRepoManagerCloseListeners) { 149 listener.preClose(event); 150 } 151 } 152 153 private void postLocalRepoManagerBackendClose(LocalRepoManagerImpl localRepoManager) { 154 assertNotNull("localRepoManager", localRepoManager); 155 synchronized (this) { 156 LocalRepoManagerImpl localRepoManager2 = localRoot2LocalRepoManagerImpl.remove(localRepoManager.getLocalRoot()); 157 if (localRepoManager != localRepoManager2) { 158 if (nonReOpenableLocalRepoManagerImpls.remove(localRepoManager)) 159 logger.info("localRepoManager[{}] could not be re-opened and was unlisted before.", localRepoManager.id); 160 else 161 throw new IllegalStateException(String.format("localRepoManager[%s] is unknown!", localRepoManager.id)); 162 163 localRoot2LocalRepoManagerImpl.put(localRepoManager2.getLocalRoot(), localRepoManager2); // re-add! 164 } 165 } 166 LocalRepoManagerCloseEvent event = new LocalRepoManagerCloseEvent(this, localRepoManager, true); 167 for (LocalRepoManagerCloseListener listener : localRepoManagerCloseListeners) { 168 listener.postClose(event); 169 } 170 } 171 172 private static final void doNothing() { } 173}