org.kepler.util.AuthNamespace.java Source code

Java tutorial

Introduction

Here is the source code for org.kepler.util.AuthNamespace.java

Source

/*
 * Copyright (c) 2010 The Regents of the University of California.
 * All rights reserved.
 *
 * '$Author: crawl $'
 * '$Date: 2013-02-21 11:13:44 -0800 (Thu, 21 Feb 2013) $' 
 * '$Revision: 31472 $'
 * 
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the above
 * copyright notice and the following two paragraphs appear in all copies
 * of this software.
 *
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
 * ENHANCEMENTS, OR MODIFICATIONS.
 *
 */

/**
 * 
 */
package org.kepler.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.Hashtable;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
import java.util.Vector;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.kepler.build.modules.Module;
import org.kepler.build.project.ProjectLocator;
import org.kepler.configuration.ConfigurationManager;
import org.kepler.configuration.ConfigurationProperty;
import org.kepler.objectmanager.lsid.LSIDGenerator;

/**
 * This class contains an Authority and a Namespace to be used for uniquely
 * generating KeplerLSIDs.
 * 
 * @author Aaron Schultz
 */
public class AuthNamespace {
    private static final Log log = LogFactory.getLog(AuthNamespace.class.getName());
    private static final boolean isDebugging = log.isDebugEnabled();

    /**
     * The authority that assigned the namespace to this Kepler instance.
     */
    private String _authority;

    /**
     * The namespace that uniquely identifies this Kepler instance along with
     * the authority
     */
    private String _namespace;

    /**
     * The list of configured authorities to try to get a unique authNamespace
     * from. This is populated from the config.xml file.
     */
    private Vector<String> _configuredAuthorities;

    /**
     * The filename where the authority and namesapce are stored. Store the
     * AuthorizedNamespace file in the KEPLER directory, not the cache
     * directory.
     */
    private String _anFileName;

    private boolean _initialized = false;

    private final String _saveFileName = "AuthorizedNamespace";

    /** singleton instance **/
    private static AuthNamespace instance = null;

    /**
     * Constructor.
     */
    protected AuthNamespace() {

        File persistentDir = DotKeplerManager.getInstance().getPersistentModuleDirectory("core");
        _anFileName = persistentDir.toString();
        if (!_anFileName.endsWith(File.separator)) {
            _anFileName += File.separator;
        }
        _anFileName += _saveFileName;
        if (isDebugging)
            log.debug(_anFileName);
        _configuredAuthorities = new Vector<String>(1);

        // make sure the userDirPath exists
        new File(DotKeplerManager.getDotKeplerPath()).mkdirs();

    }

    /**
     * @return the Authority
     */
    public String getAuthority() {
        return _authority;
    }

    private void setAuthority(String authority) throws Exception {
        int ind;
        if (authority.startsWith("http://")) {
            authority = authority.substring(7);
        } else if (authority.startsWith("https://")) {
            authority = authority.substring(8);
        } else if ((ind = authority.indexOf("://")) >= 0) {
            authority = authority.substring(ind + 3);
        }
        if (authority.indexOf(':') >= 0) {
            throw new Exception("authority can not contain a colon");
        }
        _authority = authority;
    }

    /**
     * @return the Namespace
     */
    public String getNamespace() {
        return _namespace;
    }

    private void setNamespace(String namespace) throws Exception {
        if (namespace.indexOf(':') >= 0) {
            throw new Exception("namespace can not contain a colon");
        }
        _namespace = namespace;
    }

    public String getAuthNamespace() {
        return getAuthority() + ":" + getNamespace();
    }

    /**
     * The purpose here is to return a unique string based on the authNamespace
     * string that can be used for including in the name of Instance specific
     * strings such as the .kepler directory name. This is a one way operation
     * such that the HashValue can be obtained from the AuthNamespace string but
     * probably not the other way around.
     * 
     */
    public String getFileNameForm() {
        String filenameFriendly = getAuthNamespace();
        filenameFriendly = filenameFriendly.replaceAll("/", ".");
        filenameFriendly = filenameFriendly.replaceAll(":", ".");
        filenameFriendly = filenameFriendly.replaceAll("\\.\\.", ".");
        // log.debug(filenameFriendly);
        return filenameFriendly;
    }

    /**
     * Set the unique Authority and Namespace for this Kepler Instance.
     */
    public void initialize() {
        log.debug("initialize()");
        if (_initialized)
            return;
        _initialized = true; // never do this twice.

        /**
         * Configure the list of authNamespace services that should be used to
         * get a unique namespace from.
         */
        readAuthNamespaceServicesConfiguration();

        /*
         * If the AuthNamespace file does not exist, create it.
         */
        File ianFile = new File(_anFileName);
        if (!ianFile.exists()) {
            generateAuthNamespace();
        }

        refreshAuthNamespace();

        if (getAuthority() == "uuid") {
            /*
             * TODO warn the user they are working with a uuid and ask if they'd
             * like to verify a new namespace from an authority.
             * 
             * This should be farmed out to a class in the gui module. If there
             * is no gui around then an alternate path should be created for
             * doing this.
             */
        }
    }

    /**
     * Don't use this method.  Only if the LSID_GENERATOR table is corrupt
     * or deleted do we want to get a new Authorized Namespace.
     */
    public void getNewAuthorizedNamespace() {
        File anFile = new File(_anFileName);
        anFile.delete();
        generateAuthNamespace();
        refreshAuthNamespace();
    }

    /**
     * This initializer method is only to be used for JUnit testing.
     */
    public void initializeForTesting(String authority, String namespace) {
        log.debug("initializeForTests()");
        if (_initialized)
            return;
        _initialized = true;

        // use a different file for testing
        _anFileName = ProjectLocator.getProjectDir().toString();
        if (!_anFileName.endsWith(File.separator)) {
            _anFileName += File.separator;
        }
        _anFileName += "AuthorizedNamespaceTesting";
        if (isDebugging)
            log.debug(_anFileName);

        // use a generic authority and namespace for testing
        _authority = authority;
        _namespace = namespace;

        try {
            writeAuthorizedNamespace();
        } catch (Exception e) {
            System.out.println(e.toString());
            e.printStackTrace();
        }
        _initialized = true;
    }

    /**
     * Populate the configuredAuthorities list from the config.xml file.
     */
    private void readAuthNamespaceServicesConfiguration() {
        ConfigurationManager cm = ConfigurationManager.getInstance();
        Module cmMod = ConfigurationManager.getModule("configuration-manager");
        String path = "authNamespaceServices.authNamespaceService";
        List<ConfigurationProperty> anProps = cm.getProperties(cmMod, path);
        Hashtable<Integer, String> addys = new Hashtable<Integer, String>();
        for (ConfigurationProperty anProp : anProps) {
            ConfigurationProperty ordProp = anProp.getProperty("ordering");
            ConfigurationProperty urlProp = anProp.getProperty("url");

            String ordStr = ordProp.getValue();
            Integer ordInt = Integer.parseInt(ordStr);
            String urlStr = urlProp.getValue();

            addys.put(ordInt, urlStr);
        }
        for (int i = 1; i <= addys.size(); i++) {
            Integer I = new Integer(i);
            String addy = addys.get(I);
            if (isDebugging)
                log.debug(addy);
            _configuredAuthorities.add(addy);
        }
    }

    /**
     * Try to query the configured authorities to get an assigned namespace.
     * Otherwise generate a uuid as the namespace.
     */
    private void generateAuthNamespace() {
        boolean verified = false;

        /*
         * First, try to connect to a server to get a verified namespace.
         */
        try {

            for (int i = 0; i < _configuredAuthorities.size(); i++) {
                String currentAuthority = _configuredAuthorities.get(i);
                verified = queryAuthorizedNamespace(currentAuthority);
                if (_authority == null || _namespace == null) {
                    verified = false;
                }
                if (verified) {
                    log.info("A unique namespace, " + _namespace + ", was successfully retrieved from authority "
                            + _authority);
                    break;
                }
            }

        } catch (Exception e1) {
            log.error(e1.toString());
            e1.printStackTrace();
        }

        /*
         * If a verified namespace cannot be retrieved, generate one.
         */
        if (!verified) {
            /*
             * Generate a probabilistically unique random ID for the namespace
             * and set uuid as the authority.
             */
            log.warn("Generating UUID based AuthNamespace");

            try {
                setAuthority("uuid");
                setNamespace(UUID.randomUUID().toString());

                writeAuthorizedNamespace();
            } catch (Exception e) {
                log.error("Unable to write InstanceAuthNamespace file: " + _anFileName);
                e.printStackTrace();
            }
        }
    }

    /**
     * Read in the Authority and Namespace from the AuthorizedNamespace file.
     */
    public void refreshAuthNamespace() {
        if (isDebugging)
            log.debug("refreshAuthNamespace()");

        try {
            InputStream is = null;
            ObjectInput oi = null;
            try {
                is = new FileInputStream(_anFileName);
                oi = new ObjectInputStream(is);
                Object newObj = oi.readObject();

                String theString = (String) newObj;
                int firstColon = theString.indexOf(':');
                setAuthority(theString.substring(0, firstColon));
                setNamespace(theString.substring(firstColon + 1));
            } finally {
                if (oi != null) {
                    oi.close();
                }
                if (is != null) {
                    is.close();
                }
            }
        } catch (FileNotFoundException e) {
            log.error(_saveFileName + " file was not found: " + _anFileName);
            e.printStackTrace();
        } catch (IOException e) {
            log.error("Unable to create ObjectInputStream");
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            log.error("Unable to read from ObjectInput");
            e.printStackTrace();
        } catch (Exception e) {
            log.error(e.toString());
            e.printStackTrace();
        }

        if (isDebugging) {
            log.debug("Instance Authority: " + getAuthority());
            log.debug("Instance Namespace: " + getNamespace());
            log.debug("Instance AuthNamespace: " + getAuthNamespace());
            log.debug("Instance Hash Value: " + getFileNameForm());
        }

    }

    /**
     * Serialize the authority:namespace String to the AuthorizedNamespace file.
     */
    private void writeAuthorizedNamespace() throws Exception {
        if (isDebugging)
            log.debug("writeAuthorizedNamespace()");

        File ianFile = new File(_anFileName);
        if (!ianFile.exists()) {
            ianFile.delete();
        }
        String authNamespace = getAuthNamespace();
        if (authNamespace == null) {
            throw new Exception("authNamespace is null");
        }
        OutputStream os = new FileOutputStream(_anFileName);
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(os);
            oos.writeObject(authNamespace);
            oos.flush();
        } finally {
            if (oos != null) {
                oos.close();
            }
        }

        try {
            // Every time we write a new AuthNamespace make sure there is
            // at least one row added to the LSID_GENERATOR table for the
            // new Authority and Namespace, this is how LSIDGenerator
            // can tell if the LSID_GENERATOR table has been deleted since
            // the last time it accessed it.
            LSIDGenerator.insertRow(getAuthority(), getNamespace(), 0, 1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
      * Return a unique namespace from the provided authority string.
      * 
     * @param authority
     * @return boolean true if success
     * @throws Exception
     */
    private boolean queryAuthorizedNamespace(String authority) throws Exception {
        if (isDebugging)
            log.debug("queryAuthorizedNamespace( " + authority + ")");

        // thwart malicious robots
        String data = URLEncoder.encode("username", "UTF-8") + "=" + URLEncoder.encode("kepler", "UTF-8");
        data += "&" + URLEncoder.encode("password", "UTF-8") + "=" + URLEncoder.encode("kepler", "UTF-8");

        try {
            // Send the request
            URL url = new URL(authority);
            URLConnection conn = url.openConnection();
            conn.setDoOutput(true);
            OutputStream os = conn.getOutputStream();
            OutputStreamWriter wr = new OutputStreamWriter(os);
            wr.write(data);
            wr.flush();

            // Get the response as a set of Properties
            BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String line;
            Properties props = new Properties();
            while ((line = rd.readLine()) != null) {
                if (isDebugging)
                    log.debug(line);
                line = line.trim();
                if (line.equals(""))
                    continue;
                int equalsIndex = line.indexOf("=");
                if (equalsIndex > 0) {
                    String key = line.substring(0, equalsIndex).trim();
                    String value = line.substring(equalsIndex + 1).trim();
                    props.setProperty(key, value);
                }
            }
            wr.close();
            rd.close();

            // Check to see if there was an error
            String errorString = props.getProperty("error");
            if (errorString != null) {
                log.warn(errorString);
            } else {
                // Check and set the returned namespace
                String newNamespace = props.getProperty("namespace");
                if (newNamespace != null && !newNamespace.equals("")) {
                    setAuthority(authority);
                    setNamespace(newNamespace);

                    try {
                        writeAuthorizedNamespace();
                        return true;
                    } catch (Exception e) {
                        log.error("Unable to write " + _saveFileName + " file: " + _anFileName);
                        e.printStackTrace();
                    }
                }
            }
        } catch (Exception e) {
            System.out.println(e.toString());
        }

        return false;
    }

    /**
     * Method for getting an instance of this singleton class.
     */
    public static AuthNamespace getInstance() {
        if (instance == null) {
            instance = new AuthNamespace();
            instance.initialize();
        }
        return instance;
    }
}