package edu.tufts.vue.dsm.impl; import java.io.File; import java.util.Properties; import org.apache.log4j.Level; /** * This exists to provide an org.osid.provider.ProviderControlManager with the ability to change the * reported value of the "root" property from osid.properties dynamically at runtime w/out having to * re-write a bunch of the low-level installer code (which currently exist only in jar files, * and is not part of our source tree -- see TuftsOsidProivder.jar and MIT-OTS-NO_PROVIDER.jar). * * We need to do this because the directory /Library on Mac OS X changed to be default unwriteable * (at least as of Lion, possibly prior). * * We override getConfiguration("root") to return our desired value. This also means we could, * if we wish in the future, make it a per-user directory opposed to a system-wide * shared directory. * * Note that this class MUST be have a base-name of "ProviderControlManager", as this will be * instanced, initialized and returned by a call to edu.mit.osidimpl.OsidLoader.getManager(...) in * VueOsidFactory. * * We override the ProviderControlManager implementation that's been in use for years: * @see edu.mit.osidimpl.provider.repository.ProviderControlManager * * S.Fraize 2012 June * * @see http://stuff.mit.edu/afs/athena/project/okidev/okiproject/impl/seussville/src/edu/mit/osidimpl/manager * @see http://stuff.mit.edu/afs/athena/project/okidev/okiproject/impl/seussville/src/edu/mit/osidimpl/provider */ public class ProviderControlManager extends edu.mit.osidimpl.provider.repository.ProviderControlManager { public static final org.apache.log4j.Logger Log = org.apache.log4j.Logger.getLogger(VuePCM.class); private static final String OldLibraryDir = "/Library/OsidProviders"; private static final String NewLibraryDir = "/Library/Caches/OsidProviders"; // mac only private String runtimeRoot = null; private static boolean DEBUG = false; //private void trace(String s) { Log.info(System.identityHashCode(this) + ": " + s); } private void trace(String s) { Log.info(s); } @Override public void assignConfiguration(java.util.Properties conf) throws org.osid.OsidException { if (DEBUG) trace("assignConfiguration " + conf + " ..."); // This will load the configuration, at which point getConfiguration(someKey) will work, // and then call initialize: super.assignConfiguration(conf); if (DEBUG) trace("assignConfiguration done."); } // Note that in super.initialize, "root" is pulled from config, and then // an installer is instanced: // this.installer = new edu.mit.osidimpl.provider.installer.firstcut.Installer(this.rootDir); // It ALSO initializes a classloader with getClass().getClassLoader(), which means // the class loader COULD be different from the one we were getting out of the // jar file, tho probably not -- at deploy time, it's all coming from one archive... @Override protected void initialize() throws org.osid.OsidException { trace("initialize..."); if (tufts.Util.isMacPlatform()) { try { runtimeRoot = checkForRootDirRuntimeConfiguration(); if (runtimeRoot != null) Log.info("Runtime configured OSID provider install dir: " + runtimeRoot); } catch (Throwable t) { Log.warn("checking for runtime root", t); } } super.initialize(); final String installRoot = runtimeRoot == null ? getActiveRoot() : runtimeRoot; final File installDir = new File(installRoot); if (runtimeRoot == null) { // will just be whatever was in osid.properties: Log.info("Configured OSID provider install dir: " + installDir); } // Make sure the shared install dir is readable & writeable to all users. // (skip this if we move to a per-user home directory based provider install dir) try { // Make readable & writeable to everyone [these only since Java 1.6] //installDir.setReadable(true, false); //installDir.setWritable(true, false); final Object[] args = { Boolean.TRUE, Boolean.FALSE }; tufts.Util.execute(installDir, "java.io.File", "setReadable", args); tufts.Util.execute(installDir, "java.io.File", "setWritable", args); } catch (Throwable t) { Log.warn("Couldn't set modes on install dir: " + installDir + "; " + t); } if (DEBUG) trace("initialize done."); } @Override protected void loadConfiguration() throws org.osid.OsidException { trace("loadConfiguration..."); super.loadConfiguration(); if (DEBUG) trace("loadConfiguration done."); } /** this isn't called on startup, but can be called later if updating or adding repositories */ @Override protected void initializeRepository() throws org.osid.provider.ProviderException { trace("initializeRepository..."); super.initializeRepository(); if (DEBUG) trace("initializeRepository done."); } // Note that getConfiguration in edu.mit.osidimpl.OsidLoader is private. // Fortunately, in a parent class it's protected. @Override protected String getConfiguration(final String key) { String value = super.getConfiguration(key); if (runtimeRoot != null && "root".equalsIgnoreCase(key)) { trace("getConfig " + key + " = " + value); value = runtimeRoot; trace(" override " + key + " = " + value); } else { if (DEBUG) trace("getConfig " + key + " = " + value); } return value; } String getActiveRoot() { return getConfiguration("root"); } private String checkForRootDirRuntimeConfiguration() { final File oldDir = new File(OldLibraryDir); final File newDir = new File(NewLibraryDir); if (oldDir.exists() && oldDir.isDirectory() && oldDir.canRead()) { final String[] oldContents = oldDir.list(); Log.info("Found old OsidProviders dir: " + oldDir + " contains " + oldContents.length + " files; canWrite=" + oldDir.canWrite()); if (DEBUG) tufts.Util.dumpArray(oldContents); if (newDir.exists() && newDir.isDirectory() && newDir.canRead()) { // In case an OLD version of VUE was later run which re-created the original dir // somehow, prioritize the new directory if it has more contents than the old, // or, of course, if the old isn't writeable. final String newContents[] = newDir.list(); Log.info("Found new OsidProviders dir: " + newDir + " contains " + newContents.length + " files"); if (oldDir.canWrite() && newContents.length > oldContents.length) { Log.info("prioritizing new dir over old based on contents: " + NewLibraryDir); return NewLibraryDir; } } if (oldDir.canWrite() && oldContents.length > 0) { Log.info("old dir still writeable, keep using: " + OldLibraryDir); // Leave it be -- keep using old location. Even if the user upgrades to a new // version of OS X where /Library is no longer writeable, /Library/OsidProviders // should already be there -- "grandfathered" in. // Note: did sharing /Library/OsidProviders amonst users ever actually work? It // only could have if when it was created initially it was made readable and // writeable to all, and I don't see any code for that in the Installer... If // that's the case, this is still a problem going forward, and we should // consider putting this in the users home .vue_2 config directory... return null; } else if (oldContents.length > 0) Log.warn("Old providers dir exists, but we cannot write to it: " + oldContents.length + " providers may need re-download for " + NewLibraryDir); } // Note that our installer, edu.mit.osidimpl.provider.installer.firstcut.Installer, // will do File.mkdirs() to create the provider install location. return NewLibraryDir; } private void inspectProperties() { try { // ANY class should do using an absolute (starts with '/') resource name... final java.io.InputStream in = VueOsidFactory.class.getResourceAsStream("/osid.properties"); Log.debug("Got stream for inspecting osid.properties: " + in); if (in != null) { final Properties osidProps = new Properties(); osidProps.load(in); //Log.debug("Got osid.properties: " + osidProps); tufts.Util.dump(osidProps.entrySet()); } } catch (Exception e) { Log.warn(e); } //Log.debug("root=" + VueResources.getString("root")); } // /** // * Note that the values of OldLibraryDir and NewLibraryDir depend upon the past (and future) values of the // * property "root" from osid.properties. Worst case: if we cannot move the old providers dir to the new // * location, the ProviderInstallationManager, when it pulls the value of "root", will create a new empty // * providers dir, and the user will have to re-update / re-download their providers. // * // * ALSO note that this is all much more complicated than if we had access to the ProviderInstallationManger // * SOURCE. We have one property from osid.properties that it is pulling ("root"), where providers will // * both be found and installed. This property will be same for all platforms (tho technically we could // * tweak our build process to include different osid.properties files for different platforms), and // * everything must already be in place before the property is polled. // * // */ // private static void checkAndUpdateProviderInstallLocation() // { // if (true) { // // TODO: Maybe only char osid.properties root for MAC platform in a mac-only config file? // // Oh, no, can't work -- I doubt the opaque osid load code makes use of a localization scheme. // // SO PROBLEM: changing osid.properties:root will affect ALL platforms... And we can't // // just punt is /Library/Caches probably only exists on the mac. // try { // //final java.io.InputStream in = edu.mit.osidimpl.OsidLoader.class.getResourceAsStream("osid.properties"); // //final java.io.InputStream in = tufts.vue.VUE.getResourceAsStream("osid.properties"); // //final java.io.InputStream in = tufts.Util.class.getResourceAsStream("/osid.properties"); // // ANY class should do using an absolute (starts with '/') resource name... // final java.io.InputStream in = VueOsidFactory.class.getResourceAsStream("/osid.properties"); // Log.debug("Got stream for inspecting osid.properties: " + in); // if (in != null) { // final Properties osidProps = new Properties(); // osidProps.load(in); // //Log.debug("Got osid.properties: " + osidProps); // tufts.Util.dump(osidProps.entrySet()); // } // } catch (Exception e) { // Log.warn(e); // } // //Log.debug("root=" + VueResources.getString("root")); // } // final String OldLibraryDir = "/Library/OsidProviders"; // final String NewLibraryParentDir = "/Library/Caches"; // final String NewLibraryDir = NewLibraryParentDir + "/OsidProviders"; // final File oldDir = new File(OldLibraryDir); // final File newDir = new File(NewLibraryDir); // boolean foundOld = false; // boolean foundNew = false; // if (oldDir.exists()) { // foundOld = true; // Log.info("Found old OsidProviders dir: " + oldDir); // tufts.Util.dumpArray(oldDir.listFiles()); // } // if (newDir.exists()) { // foundNew = true; // Log.info("Found new OsidProviders dir: " + newDir); // if (foundOld) // Log.warn("Found BOTH old and new OsidProviders directories -- use of any old providers may require data source update(s) / re-download(s)."); // } // if (foundOld && !foundNew) { // Log.info(" moving " + OldLibraryDir + " to " + NewLibraryDir + "..."); // final File newParent = new File(NewLibraryParentDir); // if (!newParent.exists()) { // // shouldn't happen on mac (/Library/Caches should always exist), but // // will probably happen on Windows & Linux. // try { // if (newParent.mkdirs()) // Log.info("Created " + newParent); // else // Log.warn("Failed to create " + newParent); // } catch (Exception e) { // Log.warn("Failed to create " + newParent, e); // } // } else { // Log.info(newParent + " already exists."); // } // Exception ex = null; // boolean success = false; // try { // success = oldDir.renameTo(newDir); // } catch (Exception e) { // ex = e; // } // if (success) { // Log.info(" moved " + OldLibraryDir + " to " + NewLibraryDir); // } else { // Log.warn("move of " + oldDir + " to " + newDir + ": FAILED"); // if (ex != null) // Log.warn(ex); // } // } // } } /** for log tagging to differentiate from classes with same name in other impl dirs */ class VuePCM {}