package com.revolsys.record.io; import java.io.File; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiFunction; import java.util.function.Function; import com.revolsys.io.FileUtil; import com.revolsys.io.connection.AbstractConnectionRegistryManager; import com.revolsys.io.file.Paths; import com.revolsys.record.schema.RecordStore; import com.revolsys.spring.resource.FileSystemResource; import com.revolsys.spring.resource.Resource; import com.revolsys.util.JavaBeanUtil; import com.revolsys.util.OS; import com.revolsys.util.Property; public class RecordStoreConnectionManager extends AbstractConnectionRegistryManager<RecordStoreConnectionRegistry, RecordStoreConnection> { private static final RecordStoreConnectionManager INSTANCE; // TODO make this garbage collectable with reference counting. private static Map<Map<String, Object>, RecordStore> recordStoreByConfig = new HashMap<>(); private static Map<Map<String, Object>, AtomicInteger> recordStoreCounts = new HashMap<>(); static { INSTANCE = new RecordStoreConnectionManager(); final File appsDirectory = OS.getApplicationDataDirectory(); final File oldDirectory = new File(appsDirectory, "com.revolsys.gis/Data Stores"); if (oldDirectory.exists()) { oldDirectory.renameTo(new File(appsDirectory, "com.revolsys.gis/Record Stores")); } final File recordStoresDirectory = OS .getApplicationDataDirectory("com.revolsys.gis/Record Stores"); INSTANCE.addConnectionRegistry("User", new FileSystemResource(recordStoresDirectory)); } private static BiFunction<RecordStoreConnection, Throwable, Boolean> invalidRecordStoreFunction; private static Function<String, RecordStore> missingRecordStoreFunction; public static RecordStoreConnectionManager get() { return INSTANCE; } public static BiFunction<RecordStoreConnection, Throwable, Boolean> getInvalidRecordStoreFunction() { return RecordStoreConnectionManager.invalidRecordStoreFunction; } public static <V extends RecordStore> V getRecordStore(final File file) { final Map<String, String> connectionProperties = Collections.singletonMap("url", FileUtil.toUrlString(file)); final Map<String, Object> config = Collections.<String, Object> singletonMap("connection", connectionProperties); return getRecordStore(config); } /** * Get an initialized record store. * @param connectionProperties * @return */ @SuppressWarnings("unchecked") public static <T extends RecordStore> T getRecordStore( final Map<String, ? extends Object> config) { @SuppressWarnings("rawtypes") final Map<String, Object> configClone = (Map)JavaBeanUtil.clone(config); synchronized (recordStoreByConfig) { RecordStore recordStore = recordStoreByConfig.get(configClone); if (recordStore != null && recordStore.isClosed()) { recordStoreByConfig.remove(configClone); recordStoreCounts.remove(configClone); recordStore = null; } if (recordStore == null) { final Map<String, ? extends Object> connectionProperties = (Map<String, ? extends Object>)configClone .get("connection"); final String name = (String)connectionProperties.get("name"); if (Property.hasValue(name)) { recordStore = getRecordStore(name); if (recordStore == null) { // TODO give option to add return null; } } else { recordStore = RecordStore.newRecordStore(connectionProperties); if (recordStore == null) { return null; } else { recordStore.setProperties(config); recordStore.initialize(); } } recordStoreByConfig.put(configClone, recordStore); recordStoreCounts.put(configClone, new AtomicInteger(1)); } else { final AtomicInteger count = recordStoreCounts.get(configClone); count.incrementAndGet(); } return (T)recordStore; } } public static <V extends RecordStore> V getRecordStore(final Path path) { final Map<String, String> connectionProperties = Collections.singletonMap("url", Paths.toUrlString(path)); final Map<String, Object> config = Collections.<String, Object> singletonMap("connection", connectionProperties); return getRecordStore(config); } public static RecordStore getRecordStore(final String name) { final RecordStoreConnectionManager connectionManager = get(); final List<RecordStoreConnectionRegistry> registries = new ArrayList<>(); registries.addAll(connectionManager.getConnectionRegistries()); final RecordStoreConnectionRegistry threadRegistry = RecordStoreConnectionRegistry .getForThread(); if (threadRegistry != null) { registries.add(threadRegistry); } Collections.reverse(registries); for (final RecordStoreConnectionRegistry registry : registries) { final RecordStoreConnection recordStoreConnection = registry.getConnection(name); if (recordStoreConnection != null) { return recordStoreConnection.getRecordStore(); } } if (missingRecordStoreFunction == null) { return null; } else { return missingRecordStoreFunction.apply(name); } } @SuppressWarnings("unchecked") public static void releaseRecordStore(final Map<String, ? extends Object> config) { @SuppressWarnings("rawtypes") final Map<String, Object> configClone = (Map)JavaBeanUtil.clone(config); synchronized (recordStoreByConfig) { final RecordStore recordStore = recordStoreByConfig.get(configClone); if (recordStore != null) { final AtomicInteger count = recordStoreCounts.get(configClone); if (count.decrementAndGet() == 0) { final Map<String, ? extends Object> connectionProperties = (Map<String, ? extends Object>)configClone .get("connection"); final String name = (String)connectionProperties.get("name"); if (!Property.hasValue(name)) { // TODO release for connections from connection registries recordStore.close(); } recordStoreByConfig.remove(configClone); recordStoreCounts.remove(configClone); } } } } public static void setInvalidRecordStoreFunction( final BiFunction<RecordStoreConnection, Throwable, Boolean> invalidRecordStoreFunction) { RecordStoreConnectionManager.invalidRecordStoreFunction = invalidRecordStoreFunction; } public static void setMissingRecordStoreFunction( final Function<String, RecordStore> missingRecordStoreFunction) { RecordStoreConnectionManager.missingRecordStoreFunction = missingRecordStoreFunction; } public RecordStoreConnectionManager() { super("Record Stores"); } public RecordStoreConnectionRegistry addConnectionRegistry(final String name, final boolean visible) { final RecordStoreConnectionRegistry registry = new RecordStoreConnectionRegistry(this, name, visible); addConnectionRegistry(registry); return registry; } public RecordStoreConnectionRegistry addConnectionRegistry(final String name, final Resource recordStoresDirectory) { final RecordStoreConnectionRegistry registry = new RecordStoreConnectionRegistry(this, name, recordStoresDirectory); addConnectionRegistry(registry); return registry; } @Override public String getIconName() { return "folder:database"; } }