001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 */
021package org.biojava.nbio.structure.ecod;
022
023import java.io.IOException;
024import java.lang.ref.SoftReference;
025import java.util.Collections;
026import java.util.HashMap;
027import java.util.Iterator;
028import java.util.Map;
029import java.util.Map.Entry;
030
031import org.biojava.nbio.structure.align.util.UserConfiguration;
032import org.biojava.nbio.structure.cath.CathFactory;
033import org.biojava.nbio.structure.scop.ScopFactory;
034import org.slf4j.Logger;
035import org.slf4j.LoggerFactory;
036
037/**
038 * Controls global {@link EcodDatabase EcodDatabases} being used.
039 * Implements a multiton pattern through {@link #getEcodDatabase(String)},
040 * and a singleton pattern through {@link #getEcodDatabase()}.
041 * @author Spencer Bliven
042 * @see ScopFactory
043 * @see CathFactory
044 * @see EcodInstallation
045 */
046public class EcodFactory {
047
048        private static final Logger logger = LoggerFactory.getLogger(EcodFactory.class);
049        
050        public static final String DEFAULT_VERSION = EcodInstallation.DEFAULT_VERSION;
051
052        private static Map<String, SoftReference<EcodDatabase>> versionedEcodDBs =
053                        Collections.synchronizedMap(new HashMap<String, SoftReference<EcodDatabase>>());
054        private static String defaultVersion = EcodInstallation.DEFAULT_VERSION;
055
056        /**
057         * Returns the (singleton) database for the current default version
058         */
059        public static EcodDatabase getEcodDatabase() {
060                return getEcodDatabase(defaultVersion);
061        }
062
063        public static EcodDatabase getEcodDatabase(String version) {
064                if( version == null )
065                        version = defaultVersion;
066
067                logger.trace("Waiting for EcodFactory lock to get version "+version);
068                synchronized(versionedEcodDBs) {
069                        logger.trace("Got EcodFactory lock to get version "+version);
070
071                        releaseReferences();
072
073                        SoftReference<EcodDatabase> ecodRef = versionedEcodDBs.get(version.toLowerCase());
074                        EcodDatabase ecod = null;
075                        if(ecodRef != null) {
076                                ecod = ecodRef.get();
077                        }
078                        if( ecod == null ) {
079                                logger.debug("Creating new {}, version {}",EcodInstallation.class.getSimpleName(),version);
080                                String cacheDir = new UserConfiguration().getCacheFilePath();
081                                ecod = new EcodInstallation(cacheDir, version);
082                                versionedEcodDBs.put(version.toLowerCase(), new SoftReference<EcodDatabase>(ecod));
083
084                                // If the parsed version differed from that requested, add that too
085                                // Note that getVersion() may trigger file parsing
086                                try {
087                                        if( ! versionedEcodDBs.containsKey(ecod.getVersion().toLowerCase()) ) {
088                                                versionedEcodDBs.put(ecod.getVersion().toLowerCase(),new SoftReference<EcodDatabase>(ecod));
089                                        }
090                                } catch (IOException e) {
091                                        // For parsing errors, just use the requested version
092                                }
093                        }
094                        logger.trace("Releasing EcodFactory lock after getting version "+version);
095
096                        return ecod;
097                }
098        }
099
100        /**
101         * removes SoftReferences which have already been garbage collected
102         */
103        private static void releaseReferences() {
104                synchronized(versionedEcodDBs) {
105                        Iterator<Entry<String, SoftReference<EcodDatabase>>> it = versionedEcodDBs.entrySet().iterator();
106                        while(it.hasNext()) {
107                                Entry<String, SoftReference<EcodDatabase>> entry = it.next();
108                                SoftReference<EcodDatabase> ref = entry.getValue();
109                                if(ref.get() == null) {
110                                        logger.debug("Removed version {} from EcodFactory to save memory.",entry.getKey());
111                                        it.remove();
112                                }
113                        }
114                }
115        }
116
117        /**
118         * Updates the default version
119         * @param version
120         */
121        public static void setEcodDatabase(String version) {
122                getEcodDatabase(version);
123                defaultVersion = version;
124        }
125
126        /** Can't instantiate */
127        private EcodFactory() {}
128
129}