DBVIEW
src/org/dbview/input_addons/ProfilesRepository.java
00001 /*
00002         DbView - Graph Visualization
00003     Copyright (C) 2012  Denis BEURIVE
00004 
00005     This program is free software: you can redistribute it and/or modify
00006     it under the terms of the GNU General Public License as published by
00007     the Free Software Foundation, either version 3 of the License, or
00008     (at your option) any later version.
00009 
00010     This program is distributed in the hope that it will be useful,
00011     but WITHOUT ANY WARRANTY; without even the implied warranty of
00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013     GNU General Public License for more details.
00014 
00015     You should have received a copy of the GNU General Public License
00016     along with this program.  If not, see <http://www.gnu.org/licenses/>.
00017 */
00018 
00019 package org.dbview.input_addons;
00020 
00021 import org.jdom.Element;
00022 import org.jdom.Document;
00023 import org.jdom.output.XMLOutputter;
00024 import java.net.URISyntaxException;
00025 import org.jdom.input.SAXBuilder;
00026 import java.io.File;
00027 import org.dbview.conf.*;
00028 import org.dbview.utils.*;
00029 import java.net.URL;
00030 import java.io.FileWriter;
00031 import org.jdom.xpath.XPath;
00032 import org.jdom.JDOMException;
00033 import org.jdom.Attribute;
00034 import java.util.ArrayList;
00035 import java.util.List;
00036 
00037 /**
00038  * <p>This class implements the profile's repository.</p>
00039  * <p>A profile is a set of value used by input add-ons to connect to their associated data source (Mysql...).</p>
00040  * @author Denis BEURIVE
00041  */
00042 public class ProfilesRepository
00043 {
00044     /**
00045      * XML representation of the list of profiles.
00046      */
00047     private static Document __profiles = null;
00048 
00049     /**
00050      * File that contains the XML description of the list of profiles.
00051      */
00052     private static File __repository   = null;
00053 
00054     /**
00055      * Initialize the repository.
00056      * @throws Exception
00057      */
00058     public static void init() throws Exception
00059     {
00060         if (null != ProfilesRepository.__profiles) { return; }
00061         // { throw new Exception("Unexpected error : The profile repository has already been initialized. It should not be initialized twice."); }
00062 
00063         // Build the path to the repository.
00064         ProfilesRepository.__initFile();
00065 
00066         // Shall we create an empty repository?
00067         if (! ProfilesRepository.__repository.exists())
00068         {
00069             Element root = new Element("profiles");
00070             root.setAttribute("created", org.dbview.utils.Date.now());
00071             Document doc = new Document(root);
00072             ProfilesRepository.__setUpdateDateTimeToNow(doc);
00073             ProfilesRepository.__write(doc);
00074         }
00075 
00076         // Load the repository.
00077         if (! ProfilesRepository.__repository .isFile())  { throw new Exception("Unexpected error : The entry that represents the profiles' repository \"" + ProfilesRepository.__repository.getPath() + "\" is not a regular file!"); }
00078         if (! ProfilesRepository.__repository .canRead()) { throw new Exception("Unexpected error : The entry that represents the profiles' repository \"" + ProfilesRepository.__repository.getPath() + "\" is not readable!"); }
00079         SAXBuilder builder = new SAXBuilder();
00080         ProfilesRepository.__profiles = builder.build(ProfilesRepository.__repository);
00081     }
00082 
00083     /**
00084      * Add a new profile to the repository.
00085      * @param in_profile Profile to add.
00086      * @throws ProfilesRepositoryException
00087      * @throws Exception
00088      */
00089     public static void add(AbstractConfiguration in_profile) throws ProfilesRepositoryException, JDOMException, Exception
00090     {
00091         ProfilesRepository.__add(in_profile.toXml());
00092     }
00093 
00094     /**
00095      * Given a profile, this method removes the associated profile from the repository.
00096      * @param in_profile Profile to remove.
00097      * @throws ProfilesRepositoryException
00098      * @throws JDOMException
00099      * @throws Exception
00100      */
00101     public static void remove(AbstractConfiguration in_profile) throws ProfilesRepositoryException, JDOMException, Exception
00102     {
00103         ProfilesRepository.__remove(in_profile.toXml());
00104     }
00105 
00106     /**
00107      * Given a profit's name, this method removes the associated profile from the repository.
00108      * @param in_name The name of the profile to remove.
00109      * @throws ProfilesRepositoryException
00110      * @throws JDOMException
00111      * @throws Exception
00112      */
00113     public static void remove(String in_name) throws ProfilesRepositoryException, JDOMException, Exception
00114     {
00115         if (null == ProfilesRepository.__profiles) { ProfilesRepository.init(); }
00116         Element conf = ProfilesRepository.__getProfileByName(in_name);
00117         if (null == conf) { throw new ProfilesRepositoryException("The name \"" + in_name + "\" is not associated to any profile."); }
00118         if (! ProfilesRepository.__profiles.getRootElement().removeContent(conf))
00119         { throw new ProfilesRepositoryException("Could not remove profile \"" + in_name + "\"."); }
00120         // Write to disk.
00121         ProfilesRepository.__setUpdateDateTimeToNow(null);
00122         ProfilesRepository.__write(null);
00123     }
00124 
00125     /**
00126      * Update a given profile.
00127      * @remark The removal uses the name of the profile
00128      * @param in_profile New profile.
00129      * @throws ProfilesRepositoryException
00130      * @throws JDOMException
00131      * @throws Exception
00132      */
00133     public static void update(AbstractConfiguration in_profile) throws ProfilesRepositoryException, JDOMException, Exception
00134     {
00135         ProfilesRepository.__update(in_profile.toXml());
00136     }
00137 
00138     /**
00139      * Return a literal representation of the profiles' repository.
00140      * @return Return a literal representation of the profiles' repository.
00141      */
00142     public static String print() throws Exception
00143     {
00144         if (null == ProfilesRepository.__profiles) { ProfilesRepository.init(); }
00145         return org.dbview.utils.Jdom.print(ProfilesRepository.__profiles);
00146     }
00147 
00148     /**
00149      * Return an instance of a given profile's configuration's holder.
00150      * @remark The returned instance is initialized using the XML representation extracted from the repository.
00151      * @param in_name Name of the profile.
00152      * @return If the requested profile exists, then the method returns an instance of the configuration's holder.
00153      *         Otherwise, it returns the value null.
00154      */
00155     public static AbstractConfiguration getProfileInstance(String in_name) throws ConfigurationException, JDOMException, Exception
00156     {
00157         if (null == ProfilesRepository.__profiles) { ProfilesRepository.init(); }
00158         Element profile = ProfilesRepository.__getProfileByName(in_name);
00159         if (null == profile) { return null; }
00160         String target = ProfilesRepository.__getProfileTarget(profile);
00161         InputCatalog a = new InputCatalog();
00162         AbstractConfiguration conf = (AbstractConfiguration)a.getAdaptor(target, "Configuration");
00163         conf.fromXml(profile, null, null);
00164         return conf;
00165     }
00166 
00167     /**
00168      * This method returns the list of all profiles' names stored in the profiles' repository.
00169      * @return The method returns the list of all profiles stored in the profiles' repository.
00170      * @throws JDOMException
00171      */
00172     public static ArrayList<String> getProfilesNames() throws JDOMException
00173     {
00174         ArrayList<String> profiles = new ArrayList<String>();
00175         XPath xpa = XPath.newInstance("configuration");
00176         @SuppressWarnings("unchecked")
00177         List<Element> list = (List<Element>)xpa.selectNodes(ProfilesRepository.__profiles.getRootElement());
00178 
00179         for (int i=0; i<list.size(); i++)
00180         {
00181             Element e = list.get(i);
00182             profiles.add(e.getAttributeValue("name"));
00183         }
00184 
00185         return profiles;
00186     }
00187 
00188     /**
00189      * This method tries to locate a given profile given its name.
00190      * @param in_name Name of the profile to locate.
00191      * @return If the profile, identified by the given name exists, then it is returned.
00192      *         Otherwise, the method returns the value null.
00193      * @throws JDOMException
00194      */
00195     private static Element __getProfileByName(String in_name) throws JDOMException
00196     {
00197         XPath xpa = XPath.newInstance("configuration[@name='" + in_name + "']");
00198         return (Element)xpa.selectSingleNode(ProfilesRepository.__profiles.getRootElement());
00199     }
00200 
00201     /**
00202      * Given a profile, this method returns its name.
00203      * @param in_profile Profile.
00204      * @return The method returns the profile's name.
00205      * @throws Exception
00206      */
00207     private static String __getProfileName(Element in_profile) throws Exception
00208     {
00209         Attribute attr = in_profile.getAttribute("name");
00210         if (null == attr) { throw new Exception("Unexpected error : Found a profile with no name!"); }
00211         return attr.getValue();
00212     }
00213 
00214     /**
00215      * Given a profile, this method returns the name of the associated input add-on.
00216      * @param in_profile Profile.
00217      * @return The method returns the name of the associated input add-on.
00218      * @throws Exception
00219      */
00220     private static String __getProfileTarget(Element in_profile) throws Exception
00221     {
00222         Attribute attr = in_profile.getAttribute("target");
00223         if (null == attr) { throw new Exception("Unexpected error : Found a profile with no target name!"); }
00224         return attr.getValue();
00225     }
00226 
00227     /**
00228      * This method creates an instance of class "File" that points to the profiles' repository.
00229      * @throws URISyntaxException
00230      * @throws Exception
00231      */
00232     private static void __initFile() throws URISyntaxException, Exception
00233     {
00234         URL resources_url = JavaVm.locateResource(Conf.get("repositoriesDirName"));
00235         if (null == resources_url) { throw new Exception("Unexpected error : Can not locate the directory where the profile repository is kept (\"" + Conf.get("repositoriesDirName") + "\")."); }
00236         ProfilesRepository.__repository = new File(resources_url.toURI().getPath(), Conf.get("profilesRepository"));
00237     }
00238 
00239     /**
00240      * This method writes the profiles to the file that contains the repository.
00241      * @param in_doc XML document to write (can be null!).
00242      * @warning The argument <i>in_doc</i> can be null.
00243      * @throws URISyntaxException
00244      * @throws Exception
00245      */
00246     private static void __write(Document in_doc) throws URISyntaxException, Exception
00247     {
00248         if (null == ProfilesRepository.__repository) { ProfilesRepository.__initFile(); }
00249         XMLOutputter outputter = new XMLOutputter();
00250         outputter.setFormat(org.jdom.output.Format.getPrettyFormat());
00251         FileWriter fw = new FileWriter(ProfilesRepository.__repository);
00252         in_doc = null == in_doc ? ProfilesRepository.__profiles : in_doc;
00253         fw.write(outputter.outputString(in_doc));
00254         fw.close();
00255     }
00256 
00257     /**
00258      * This method sets the value of the (XML) attribute "updated" to the current date ant time.
00259      * @param in_doc XML document to update (can be null!).
00260      * @warning The argument <i>in_doc</i> can be null.
00261      */
00262     private static void __setUpdateDateTimeToNow(Document in_doc)
00263     {
00264         in_doc = null == in_doc ? ProfilesRepository.__profiles : in_doc;
00265         in_doc.getRootElement().setAttribute("updated", org.dbview.utils.Date.now());
00266     }
00267 
00268     /**
00269      * Add a new profile to the repository.
00270      * @param in_profile Profile to add.
00271      * @throws ProfilesRepositoryException
00272      * @throws Exception
00273      */
00274     private static void __add(Element in_profile) throws ProfilesRepositoryException, JDOMException, Exception
00275     {
00276         if (null == ProfilesRepository.__profiles) { ProfilesRepository.init(); }
00277         String profile_name = ProfilesRepository.__getProfileName(in_profile);
00278 
00279         // Make sure that the profile does not already exist.
00280         Element conf = ProfilesRepository.__getProfileByName(profile_name);
00281         if (null != conf) { throw new ProfilesRepositoryException("The name \"" + profile_name + "\" is already associated to a profile."); }
00282 
00283         // Add the new profile.
00284         ProfilesRepository.__profiles.getRootElement().addContent(in_profile);
00285 
00286         // Write to disk.
00287         ProfilesRepository.__setUpdateDateTimeToNow(null);
00288         ProfilesRepository.__write(null);
00289     }
00290 
00291     /**
00292      * Update a given profile.
00293      * @remark The removal uses the name of the profile
00294      * @param in_profile New profile.
00295      * @throws ProfilesRepositoryException
00296      * @throws JDOMException
00297      * @throws Exception
00298      */
00299     private static void __update(Element in_profile) throws ProfilesRepositoryException, JDOMException, Exception
00300     {
00301         // Delete the profile (the removal uses the name of the profile).
00302         ProfilesRepository.__remove(in_profile);
00303 
00304         // Add the profile.
00305         ProfilesRepository.__add(in_profile);
00306     }
00307 
00308     /**
00309      * Given a profile, this method removes the profile from the repository.
00310      * @param in_profile The profile to remove.
00311      * @throws ProfilesRepositoryException
00312      * @throws JDOMException
00313      * @throws Exception
00314      */
00315     private static void __remove(Element in_profile) throws ProfilesRepositoryException, JDOMException, Exception
00316     {
00317         if (null == ProfilesRepository.__profiles) { ProfilesRepository.init(); }
00318         String profile_name = ProfilesRepository.__getProfileName(in_profile);
00319         ProfilesRepository.remove(profile_name);
00320     }
00321 }