org.intermine.web.struts.InitialiserPlugin.java Source code

Java tutorial

Introduction

Here is the source code for org.intermine.web.struts.InitialiserPlugin.java

Source

package org.intermine.web.struts;

/*
 * Copyright (C) 2002-2013 FlyMine
 *
 * This code may be freely distributed and modified under the
 * terms of the GNU Lesser General Public Licence.  This should
 * be distributed with the code.  See the LICENSE file for more
 * information or http://www.gnu.org/copyleft/lesser.html.
 *
 */

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Pattern;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;

import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.apache.struts.action.ActionServlet;
import org.apache.struts.action.PlugIn;
import org.apache.struts.config.ModuleConfig;
import org.apache.tools.ant.BuildException;
import org.intermine.api.InterMineAPI;
import org.intermine.api.LinkRedirectManager;
import org.intermine.api.bag.BagQueryConfig;
import org.intermine.api.bag.BagQueryHelper;
import org.intermine.api.config.ClassKeyHelper;
import org.intermine.api.profile.BagState;
import org.intermine.api.profile.Profile;
import org.intermine.api.profile.ProfileManager;
import org.intermine.api.profile.TagManager;
import org.intermine.api.profile.UserNotFoundException;
import org.intermine.api.query.MainHelper;
import org.intermine.api.search.GlobalRepository;
import org.intermine.api.search.SearchRepository;
import org.intermine.api.tag.TagNames;
import org.intermine.api.tracker.Tracker;
import org.intermine.api.tracker.TrackerDelegate;
import org.intermine.api.tracker.util.TrackerUtil;
import org.intermine.metadata.ClassDescriptor;
import org.intermine.metadata.FieldDescriptor;
import org.intermine.metadata.Model;
import org.intermine.model.InterMineObject;
import org.intermine.model.userprofile.Tag;
import org.intermine.model.userprofile.UserProfile;
import org.intermine.modelproduction.MetadataManager;
import org.intermine.objectstore.ObjectStore;
import org.intermine.objectstore.ObjectStoreException;
import org.intermine.objectstore.ObjectStoreFactory;
import org.intermine.objectstore.ObjectStoreSummary;
import org.intermine.objectstore.ObjectStoreWriter;
import org.intermine.objectstore.ObjectStoreWriterFactory;
import org.intermine.objectstore.intermine.ObjectStoreInterMineImpl;
import org.intermine.objectstore.intermine.ObjectStoreWriterInterMineImpl;
import org.intermine.sql.Database;
import org.intermine.sql.DatabaseUtil;
import org.intermine.util.PropertiesUtil;
import org.intermine.util.TypeUtil;
import org.intermine.web.autocompletion.AutoCompleter;
import org.intermine.web.context.InterMineContext;
import org.intermine.web.logic.Constants;
import org.intermine.web.logic.aspects.Aspect;
import org.intermine.web.logic.aspects.AspectBinding;
import org.intermine.web.logic.config.FieldConfig;
import org.intermine.web.logic.config.FieldConfigHelper;
import org.intermine.web.logic.config.WebConfig;
import org.intermine.web.logic.session.SessionMethods;
import org.intermine.webservice.server.query.result.XMLValidator;
import org.jfree.util.Log;

/**
 * Initialiser for the InterMine web application.
 * Anything that needs global initialisation goes here.
 *
 * @author Andrew Varley
 * @author Thomas Riley
 */
public class InitialiserPlugin implements PlugIn {
    private static final Logger LOG = Logger.getLogger(InitialiserPlugin.class);

    ProfileManager profileManager;
    TrackerDelegate trackerDelegate;
    ObjectStore os;
    Map<String, String> blockingErrorKeys;
    /** The list of tags that mark something as public */
    public static final List<String> PUBLIC_TAG_LIST = Arrays.asList(TagNames.IM_PUBLIC);

    /**
     * Init method called at Servlet initialisation
     *
     * @param servlet ActionServlet that is managing all the modules
     * in this web application
     * @param config ModuleConfig for the module with which this
     * plug-in is associated
     *
     * @throws ServletException if this <code>PlugIn</code> cannot
     * be successfully initialized
     */
    public void init(ActionServlet servlet, ModuleConfig config) throws ServletException {

        // NOTE throwing exceptions other than a ServletException from this class causes the
        // webapp to fail to deploy with no error message.

        final ServletContext servletContext = servlet.getServletContext();
        initBlockingErrors(servletContext);

        // initialise properties
        Properties webProperties = loadWebProperties(servletContext);

        // read in additional webapp specific information and put in servletContext
        loadAspectsConfig(servletContext);
        loadClassDescriptions(servletContext);
        loadOpenIDProviders(servletContext);

        // get link redirector
        LinkRedirectManager redirect = getLinkRedirector(webProperties);

        // set up core InterMine application
        os = getProductionObjectStore(webProperties);
        WebConfig webConfig = null;
        if (os != null) {
            webConfig = loadWebConfig(servletContext, os);
        }

        final ObjectStoreWriter userprofileOSW = getUserprofileWriter(webProperties);

        if (userprofileOSW != null) {
            //verify all table mapping classes exist in the userprofile db
            if (!verifyTablesExist(userprofileOSW)) {
                return;
            }
            if (!verifySuperUserExist(userprofileOSW)) {
                return;
            }
            //verify if intermine_state exists in the savedbag table and if it has the right type
            if (!verifyListTables(userprofileOSW)) {
                return;
            }
            //verify if we the webapp needs to upgrade the lists
            verifyListUpgrade(userprofileOSW);
        }

        final ObjectStoreSummary oss = summariseObjectStore(servletContext);

        if (webProperties != null && userprofileOSW != null) {
            trackerDelegate = initTrackers(webProperties, userprofileOSW);
        }

        if (os != null && webProperties != null) {
            final Map<String, List<FieldDescriptor>> classKeys = loadClassKeys(os.getModel());
            final BagQueryConfig bagQueryConfig = loadBagQueries(servletContext, os, webProperties);

            if (userprofileOSW != null) {
                final InterMineAPI im;
                try {
                    im = new InterMineAPI(os, userprofileOSW, classKeys, bagQueryConfig, oss, trackerDelegate,
                            redirect);
                } catch (UserNotFoundException unfe) {
                    blockingErrorKeys.put("errors.init.superuser", null);
                    return;
                }
                SessionMethods.setInterMineAPI(servletContext, im);

                InterMineContext.initilise(im, webProperties, webConfig);

                // need a global reference to ProfileManager so it can be closed cleanly on destroy
                profileManager = im.getProfileManager();

                //verify superuser setted in the db matches with the user in the properties file
                final Profile superProfile = profileManager.getSuperuserProfile();
                if (!superProfile.getUsername()
                        .equals(PropertiesUtil.getProperties().getProperty("superuser.account").trim())) {
                    blockingErrorKeys.put("errors.init.superuser", null);
                }

                // index global webSearchables
                SearchRepository searchRepository = new GlobalRepository(superProfile);
                List<String> users = profileManager.getSuperUsers();
                for (String su : users) {
                    new GlobalRepository(profileManager.getProfile(su));
                }
                SessionMethods.setGlobalSearchRepository(servletContext, searchRepository);

                servletContext.setAttribute(Constants.GRAPH_CACHE, new HashMap<String, String>());

                loadAutoCompleter(servletContext, os);

                cleanTags(im.getTagManager());

                if (webConfig != null) {
                    Map<String, Boolean> keylessClasses = new HashMap<String, Boolean>();
                    for (ClassDescriptor cld : os.getModel().getClassDescriptors()) {
                        boolean keyless = true;
                        for (FieldConfig fc : FieldConfigHelper.getClassFieldConfigs(webConfig, cld)) {
                            if ((fc.getDisplayer() == null) && fc.getShowInSummary()) {
                                keyless = false;
                                break;
                            }
                        }
                        if (keyless) {
                            keylessClasses.put(TypeUtil.unqualifiedName(cld.getName()), Boolean.TRUE);
                        }
                    }
                    servletContext.setAttribute(Constants.KEYLESS_CLASSES_MAP, keylessClasses);
                }

                if (oss != null) {
                    setupClassSummaryInformation(servletContext, oss, os.getModel());
                }

                doRegistration(webProperties);
            }
        }
    }

    private void initBlockingErrors(ServletContext servletContext) {
        blockingErrorKeys = new HashMap<String, String>();
        SessionMethods.setErrorOnInitialiser(servletContext, blockingErrorKeys);
    }

    private void doRegistration(Properties webProperties) {
        Registrar reg = new Registrar(webProperties);
        reg.start();
    }

    private ObjectStore getProductionObjectStore(Properties webProperties) {
        String osAlias = (String) webProperties.get("webapp.os.alias");
        try {
            os = ObjectStoreFactory.getObjectStore(osAlias);
        } catch (Exception e) {
            LOG.error("Unable to create ObjectStore - " + osAlias + " " + e.getMessage(), e);
            blockingErrorKeys.put("errors.init.objectstoreconnection", e.getMessage());
        }
        return os;
    }

    private void loadAspectsConfig(ServletContext servletContext) {
        InputStream xmlInputStream = servletContext.getResourceAsStream("/WEB-INF/aspects.xml");
        InputStream xmlInputStreamForValidate = servletContext.getResourceAsStream("/WEB-INF/aspects.xml");
        if (xmlInputStream == null) {
            LOG.info("Unable to find /WEB-INF/aspects.xml, there will be no aspects");
            SessionMethods.setAspects(servletContext, Collections.EMPTY_MAP);
            SessionMethods.setCategories(servletContext, Collections.EMPTY_SET);
        } else {
            StringWriter writer = new StringWriter();
            try {
                IOUtils.copy(xmlInputStreamForValidate, writer);
            } catch (IOException ioe) {
                LOG.error("Problems converting xmlInputStream into a String ", ioe);
                blockingErrorKeys.put("errors.init.aspects.generic", ioe.getMessage());
                return;
            }
            String xml = writer.toString();
            String xmlSchemaUrl = "";
            try {
                xmlSchemaUrl = servletContext.getResource("/WEB-INF/aspects.xsd").toString();
            } catch (MalformedURLException mue) {
                LOG.warn("Problems retrieving url fo aspects.xsd ", mue);
            }
            Map<String, Aspect> aspects;
            if (validateXML(xml, xmlSchemaUrl, "errors.init.aspects.validation")) {
                try {
                    aspects = AspectBinding.unmarhsal(xmlInputStream);
                } catch (Exception e) {
                    LOG.error("problem while reading aspect configuration file", e);
                    blockingErrorKeys.put("errors.init.aspects", e.getMessage());
                    return;
                }
                SessionMethods.setAspects(servletContext, aspects);
                SessionMethods.setCategories(servletContext, Collections.unmodifiableSet(aspects.keySet()));
            }
        }
    }

    private boolean validateXML(String xml, String schemaUrl, String errorCode) {
        /*        Source xmlFile = new StreamSource(xmlInputStream);
                SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
                try {
        Schema schema = schemaFactory.newSchema(new StreamSource(xsdInputStream));
        Validator validator = schema.newValidator();
        try {
            validator.validate(xmlFile);
            return true;
        } catch (SAXException se) {
            LOG.error(xmlFile.getSystemId() + " is NOT valid");
            blockingErrorKeys.put(errorCode, se.getMessage());
        } catch (IOException ioe) {
            LOG.error("Problems find file ", ioe);
        }
                } catch (SAXException se) {
        LOG.error("Problems parsing xsd file", se);
                }
                return false;*/
        XMLValidator validator = new XMLValidator();
        validator.validate(xml, schemaUrl);
        if (validator.getErrorsAndWarnings().size() == 0) {
            return true;
        }
        blockingErrorKeys.put(errorCode, validator.getErrorsAndWarnings().get(0));
        return false;
    }

    private void loadAutoCompleter(ServletContext servletContext, ObjectStore os) throws ServletException {
        if (os instanceof ObjectStoreInterMineImpl) {
            Database db = ((ObjectStoreInterMineImpl) os).getDatabase();
            try {
                InputStream is = MetadataManager.retrieveBLOBInputStream(db, MetadataManager.AUTOCOMPLETE_INDEX);
                AutoCompleter ac;

                if (is != null) {
                    ac = new AutoCompleter(is);
                    SessionMethods.setAutoCompleter(servletContext, ac);
                } else {
                    ac = null;
                    LOG.warn("No AutoCompleter index found in database.");
                }
            } catch (SQLException e) {
                LOG.error("Problem with database", e);
                throw new ServletException("Problem with database", e);
            }
        }
    }

    /**
     * Object and widget display configuration
     */
    private WebConfig loadWebConfig(ServletContext servletContext, ObjectStore os) {
        WebConfig retval = null;
        InputStream xmlInputStream = servletContext.getResourceAsStream("/WEB-INF/webconfig-model.xml");
        InputStream xmlInputStreamForValidation = servletContext
                .getResourceAsStream("/WEB-INF/webconfig-model.xml");
        if (xmlInputStream == null) {
            LOG.error("Unable to find /WEB-INF/webconfig-model.xml.");
            blockingErrorKeys.put("errors.init.webconfig.notfound", null);
        } else {
            StringWriter writer = new StringWriter();
            try {
                IOUtils.copy(xmlInputStreamForValidation, writer);
            } catch (IOException ioe) {
                LOG.error("Problems converting xmlInputStream into a String ", ioe);
                blockingErrorKeys.put("errors.init.webconfig.generic", ioe.getMessage());
            }
            String xml = writer.toString();
            String xmlSchemaUrl = "";
            try {
                xmlSchemaUrl = servletContext.getResource("/WEB-INF/webconfig-model.xsd").toString();
            } catch (MalformedURLException mue) {
                LOG.warn("Problems retrieving url fo aspects.xsd ", mue);
            }
            if (validateXML(xml, xmlSchemaUrl, "errors.init.webconfig.validation")) {
                try {
                    retval = WebConfig.parse(servletContext, os.getModel());
                    String validationMessage = retval.validateWidgetsConfig(os.getModel());
                    if (validationMessage.isEmpty()) {
                        SessionMethods.setWebConfig(servletContext, retval);
                    } else {
                        blockingErrorKeys.put("errors.init.webconfig.validation", validationMessage);
                    }
                } catch (FileNotFoundException fnf) {
                    LOG.error("Problem to find the webconfig-model.xml file.", fnf);
                    blockingErrorKeys.put("errors.init.webconfig.notfound", null);
                } catch (ClassNotFoundException cnf) {
                    LOG.error("Classes mentioned in the webconfig-model.xml" + " file aren't in the Model", cnf);
                    blockingErrorKeys.put("errors.init.webconfig.classnotfound", cnf.getMessage());
                } catch (Exception e) {
                    LOG.error("Problem to parse the webconfig-model.xml file", e);
                    blockingErrorKeys.put("errors.init.webconfig.parsing", e.getMessage());
                }
            }
        }
        return retval;
    }

    /**
     *  Load user-friendly class description
     */
    private void loadClassDescriptions(ServletContext servletContext) {
        Properties classDescriptions = new Properties();
        try {
            classDescriptions.load(servletContext.getResourceAsStream("/WEB-INF/classDescriptions.properties"));
        } catch (Exception e) {
            LOG.error("Error loading class descriptions", e);
            blockingErrorKeys.put("errors.init.classDescriptions", null);
        }
        servletContext.setAttribute("classDescriptions", classDescriptions);
    }

    /**
     * Load keys that describe how objects should be uniquely identified
     */
    private Map<String, List<FieldDescriptor>> loadClassKeys(Model model) {
        Properties classKeyProps = new Properties();
        try {
            classKeyProps
                    .load(InitialiserPlugin.class.getClassLoader().getResourceAsStream("class_keys.properties"));
        } catch (Exception e) {
            LOG.error("Error loading class descriptions", e);
            blockingErrorKeys.put("errors.init.classkeys", null);
        }
        Map<String, List<FieldDescriptor>> classKeys = ClassKeyHelper.readKeys(model, classKeyProps);
        return classKeys;
    }

    /**
     * Load keys that describe how objects should be uniquely identified
     */
    private BagQueryConfig loadBagQueries(ServletContext servletContext, ObjectStore os, Properties webProperties) {
        BagQueryConfig bagQueryConfig = null;
        InputStream is = servletContext.getResourceAsStream("/WEB-INF/bag-queries.xml");
        if (is != null) {
            try {
                bagQueryConfig = BagQueryHelper.readBagQueryConfig(os.getModel(), is);
            } catch (Exception e) {
                Log.error("Error loading class bag queries. ", e);
                blockingErrorKeys.put("errors.init.bagqueries", e.getMessage());
            }
            InputStream isBag = getClass().getClassLoader().getResourceAsStream("extraBag.properties");
            Properties bagProperties = new Properties();
            if (isBag != null) {
                try {
                    bagProperties.load(isBag);
                    bagQueryConfig.setConnectField(bagProperties.getProperty("extraBag.connectField"));
                    bagQueryConfig.setExtraConstraintClassName(bagProperties.getProperty("extraBag.className"));
                    bagQueryConfig.setConstrainField(bagProperties.getProperty("extraBag.constrainField"));
                } catch (IOException e) {
                    Log.error("Error loading extraBag.properties. ", e);
                    blockingErrorKeys.put("errors.init.extrabagloading", null);
                }
            } else {
                LOG.error("Could not find extraBag.properties file");
                blockingErrorKeys.put("errors.init.extrabag", null);
            }
        } else {
            // can used defaults so just log a warning
            LOG.warn("No custom bag queries found - using default query");
        }
        return bagQueryConfig;
    }

    /**
     * Update the origins and lastState maps if there are any new properties in the
     * current state, or if any of the properties we know about has a new value.
     *
     * @param lastState The way things looked last time we were here.
     * @param origins The places things come from.
     * @param currentSource What to record if a property has just appeared or changed.
     * @param currentState The way things look now.
     */
    private void updateOrigins(Map<String, String> lastState, Map<String, List<String>> origins,
            String currentSource, Properties currentState) {
        for (Entry<Object, Object> pair : currentState.entrySet()) {
            if (!origins.containsKey(pair.getKey())) {
                origins.put(String.valueOf(pair.getKey()), new ArrayList<String>());
            }
            if (!lastState.containsKey(pair.getKey())
                    || !lastState.get(pair.getKey()).equals(((String) pair.getValue()).trim())) {
                origins.get(pair.getKey()).add(currentSource);
            }
            lastState.put((String) pair.getKey(), ((String) pair.getValue()).trim());
        }
    }

    /**
     * Read in the webapp configuration properties
     */
    private Properties loadWebProperties(ServletContext servletContext) {
        Map<String, String> lastState = new HashMap<String, String>();
        Map<String, List<String>> origins = new TreeMap<String, List<String>>();
        Properties webProperties = new Properties();
        InputStream globalPropertiesStream = servletContext.getResourceAsStream("/WEB-INF/global.web.properties");
        try {
            webProperties.load(globalPropertiesStream);
        } catch (Exception e) {
            LOG.error("Unable to find global.web.properties", e);
            blockingErrorKeys.put("errors.init.globalweb", null);
            return webProperties;
        }
        updateOrigins(lastState, origins, "/WEB-INF/global.web.properties", webProperties);

        LOG.info("Looking for extra property files");
        Pattern pattern = Pattern.compile("/WEB-INF/(?!global)\\w+\\.web\\.properties$");
        ResourceFinder finder = new ResourceFinder(servletContext);

        Collection<String> otherResources = finder.findResourcesMatching(pattern);
        for (String resource : otherResources) {
            LOG.info("Loading extra resources from " + resource);
            InputStream otherResourceStream = servletContext.getResourceAsStream(resource);
            try {
                webProperties.load(otherResourceStream);
            } catch (Exception e) {
                LOG.error("Unable to load " + resource, e);
                blockingErrorKeys.put("errors.init.globalweb", null);
                return webProperties;
            }
            updateOrigins(lastState, origins, resource, webProperties);
        }

        // Load these last, as they always take precedence.
        InputStream modelPropertiesStream = servletContext.getResourceAsStream("/WEB-INF/web.properties");
        if (modelPropertiesStream == null) {
            // there are no model specific properties
        } else {
            try {
                webProperties.load(modelPropertiesStream);
            } catch (Exception e) {
                LOG.error("Unable to load web.properties", e);
                blockingErrorKeys.put("errors.init.webproperties", null);
                return webProperties;
            }
            updateOrigins(lastState, origins, "/WEB-INF/web.properties", webProperties);
        }
        SessionMethods.setPropertiesOrigins(servletContext, origins);
        Properties trimProperties = trimProperties(webProperties);
        SessionMethods.setWebProperties(servletContext, trimProperties);
        MainHelper.loadHelpers(trimProperties);
        return trimProperties;
    }

    private Properties trimProperties(Properties webProperties) {
        Properties trimProperties = new Properties();
        for (Entry<Object, Object> property : webProperties.entrySet()) {
            trimProperties.put(property.getKey(), ((String) property.getValue()).trim());
        }
        return trimProperties;
    }

    private void loadOpenIDProviders(ServletContext context) {
        Set<String> providers = new HashSet<String>();
        Properties providerProps = new Properties();

        InputStream is = getClass().getClassLoader().getResourceAsStream("openid-providers.properties");
        if (is == null) {
            LOG.info("couldn't find openid providers, using system class-loader");
            is = ClassLoader.getSystemClassLoader().getResourceAsStream("openid-properties.properties");
        }
        if (is != null) {
            try {
                providerProps.load(is);
            } catch (IOException e) {
                LOG.error("Could not load openid-providers.properties", e);
                blockingErrorKeys.put("errors.init.openidprovidersloading", null);
                return;
            }
        } else {
            LOG.error("Could not find openid-providers.properties");
            blockingErrorKeys.put("errors.init.openidproviders", null);
            return;
        }

        for (Object key : providerProps.keySet()) {
            String keyString = (String) key;
            if (!keyString.endsWith(".alias")) {
                providers.add(keyString);
                LOG.info("Added " + keyString);
            }
        }

        SessionMethods.setOpenIdProviders(context, providers);
    }

    private LinkRedirectManager getLinkRedirector(Properties webProperties) {
        final String err = "Initialisation of link redirector failed: ";
        String linkRedirector = (String) webProperties.get("webapp.linkRedirect");
        if (linkRedirector == null) {
            return null;
        }
        Class<?> c = TypeUtil.instantiate(linkRedirector);
        Constructor<?> constr = null;
        try {
            constr = c.getConstructor(new Class[] { Properties.class });
        } catch (NoSuchMethodException e) {
            LOG.error(err, e);
            return null;
        }
        LinkRedirectManager redirector = null;
        try {
            redirector = (LinkRedirectManager) constr.newInstance(new Object[] { webProperties });
        } catch (IllegalArgumentException e) {
            LOG.error(err, e);
        } catch (InstantiationException e) {
            LOG.error(err, e);
        } catch (IllegalAccessException e) {
            LOG.error(err, e);
        } catch (InvocationTargetException e) {
            LOG.error(err, e);
        }
        return redirector;
    }

    /**
     * Summarize the ObjectStore to get class counts
     */
    private ObjectStoreSummary summariseObjectStore(ServletContext servletContext) {
        Properties objectStoreSummaryProperties = new Properties();
        InputStream objectStoreSummaryPropertiesStream = servletContext
                .getResourceAsStream("/WEB-INF/objectstoresummary.properties");
        if (objectStoreSummaryPropertiesStream == null) {
            // there are no model specific properties
            LOG.error("Unable to find objectstoresummary.properties");
            blockingErrorKeys.put("errors.init.objectstoresummary", null);
        }
        try {
            objectStoreSummaryProperties.load(objectStoreSummaryPropertiesStream);
        } catch (Exception e) {
            LOG.error("Unable to read objectstoresummary.properties", e);
            blockingErrorKeys.put("errors.init.objectstoresummary.loading", null);
        }

        final ObjectStoreSummary oss = new ObjectStoreSummary(objectStoreSummaryProperties);
        return oss;
    }

    private void setupClassSummaryInformation(ServletContext servletContext, ObjectStoreSummary oss,
            final Model model) {
        Map<String, String> classes = new LinkedHashMap<String, String>();
        Map<String, Integer> classCounts = new LinkedHashMap<String, Integer>();

        for (String className : new TreeSet<String>(model.getClassNames())) {
            if (!className.equals(InterMineObject.class.getName())) {
                classes.put(className, TypeUtil.unqualifiedName(className));
            }
            try {
                classCounts.put(className, new Integer(oss.getClassCount(className)));
            } catch (Exception e) {
                LOG.error("Unable to get class count for " + className, e);
                blockingErrorKeys.put("errors.init.objectstoresummary.classcount", e.getMessage());
            }
        }
        servletContext.setAttribute("classes", classes);
        servletContext.setAttribute("classCounts", classCounts);
        // Build subclass lists for JSPs
        Map<String, List<String>> subclassesMap = new LinkedHashMap<String, List<String>>();
        for (ClassDescriptor cld : model.getClassDescriptors()) {
            ArrayList<String> subclasses = new ArrayList<String>();
            for (String thisClassName : new TreeSet<String>(getChildren(cld))) {
                if (classCounts.get(thisClassName).intValue() > 0) {
                    subclasses.add(TypeUtil.unqualifiedName(thisClassName));
                }
            }
            subclassesMap.put(TypeUtil.unqualifiedName(cld.getName()), subclasses);
        }
        servletContext.setAttribute(Constants.SUBCLASSES, subclassesMap);
    }

    private ObjectStoreWriter getUserprofileWriter(Properties webProperties) {
        ObjectStoreWriter userprofileOSW = null;
        try {
            String userProfileAlias = (String) webProperties.get("webapp.userprofile.os.alias");
            userprofileOSW = ObjectStoreWriterFactory.getObjectStoreWriter(userProfileAlias);
        } catch (ObjectStoreException e) {
            LOG.error("Unable to create userprofile - " + e.getMessage(), e);
            blockingErrorKeys.put("errors.init.userprofileconnection", e.getMessage());
            return userprofileOSW;
        }

        applyUserProfileUpgrades(userprofileOSW, blockingErrorKeys);
        return userprofileOSW;
    }

    private void applyUserProfileUpgrades(ObjectStoreWriter osw, Map<String, String> blockingErrorKeys) {
        Connection con = null;
        try {
            con = ((ObjectStoreInterMineImpl) osw).getConnection();
            DatabaseUtil.addColumn(con, "userprofile", "apikey", DatabaseUtil.Type.text);
            if (!DatabaseUtil.columnExists(con, "userprofile", "localaccount")) {
                DatabaseUtil.addColumn(con, "userprofile", "localaccount", DatabaseUtil.Type.boolean_type);
                DatabaseUtil.updateColumnValue(con, "userprofile", "localaccount", true);
            }
            if (!DatabaseUtil.columnExists(con, "userprofile", "superuser")) {
                DatabaseUtil.addColumn(con, "userprofile", "superuser", DatabaseUtil.Type.boolean_type);
                DatabaseUtil.updateColumnValue(con, "userprofile", "superuser", false);
            }
        } catch (SQLException sqle) {
            LOG.error("Problem retrieving connection", sqle);
            blockingErrorKeys.put("errors.init.userprofileconnection", sqle.getMessage());
        } finally {
            ((ObjectStoreInterMineImpl) osw).releaseConnection(con);
        }
    }

    private boolean verifySuperUserExist(ObjectStoreWriter uosw) {
        UserProfile superuserProfile = getSuperUser(uosw);
        if (superuserProfile != null) {
            superuserProfile.setSuperuser(true);
            try {
                uosw.store(superuserProfile);
            } catch (ObjectStoreException e) {
                throw new RuntimeException("Unable to set the flag to the user profile", e);
            }
            return true;
        }
        blockingErrorKeys.put("errors.init.superusernotexist", null);
        return false;
    }

    private UserProfile getSuperUser(ObjectStoreWriter uosw) {
        String superuser = PropertiesUtil.getProperties().getProperty("superuser.account");
        UserProfile superuserProfile = new UserProfile();
        superuserProfile.setUsername(superuser);
        Set<String> fieldNames = new HashSet<String>();
        fieldNames.add("username");
        try {
            superuserProfile = (UserProfile) uosw.getObjectByExample(superuserProfile, fieldNames);
        } catch (ObjectStoreException e) {
            throw new RuntimeException("Unable to load user profile", e);
        }
        return superuserProfile;
    }

    /**
     * Destroy method called at Servlet destroy. Close connection pool
     */
    public void destroy() {
        if (profileManager != null) {
            ((ObjectStoreWriterInterMineImpl) profileManager.getProfileObjectStoreWriter()).getDatabase()
                    .shutdown();
        }
        ((ObjectStoreInterMineImpl) os).getDatabase().shutdown();
    }

    /**
     * Remove class tags from the user profile that refer to classes that non longer exist
     * @param tagManager tag manager
     */
    protected static void cleanTags(TagManager tagManager) {
        List<Tag> classTags = tagManager.getTags(null, null, "class", null);

        for (Tag tag : classTags) {
            // check that class exists
            try {
                Class.forName(tag.getObjectIdentifier());
            } catch (ClassNotFoundException e) {
                tagManager.deleteTag(tag);
            }
        }
    }

    /**
     * Get the names of the type of this ClassDescriptor and all its descendants
     * @param cld the ClassDescriptor
     * @return a Set of class names
     */
    protected static Set<String> getChildren(ClassDescriptor cld) {
        Set<String> children = new HashSet<String>();
        getChildren(cld, children);
        return children;
    }

    /**
     * Add the names of the descendents of a ClassDescriptor to a Set
     * @param cld the ClassDescriptor
     * @param children the Set of child names
     */
    protected static void getChildren(ClassDescriptor cld, Set<String> children) {
        for (ClassDescriptor child : cld.getSubDescriptors()) {
            children.add(child.getName());
            getChildren(child, children);
        }
    }

    private TrackerDelegate initTrackers(Properties webProperties, ObjectStoreWriter userprofileOSW) {
        if (!verifyTrackTables(userprofileOSW.getObjectStore())) {
            blockingErrorKeys.put("errors.init.tracktable.runAnt", null);
        }
        return getTrackerDelegate(webProperties, userprofileOSW);
    }

    private boolean verifyTrackTables(ObjectStore uos) {
        Connection con = null;
        try {
            con = ((ObjectStoreInterMineImpl) uos).getConnection();
            if (DatabaseUtil.tableExists(con, TrackerUtil.TEMPLATE_TRACKER_TABLE)) {
                ResultSet res = con.getMetaData().getColumns(null, null, TrackerUtil.TEMPLATE_TRACKER_TABLE,
                        "timestamp");

                while (res.next()) {
                    if (res.getString(3).equals(TrackerUtil.TEMPLATE_TRACKER_TABLE)
                            && "timestamp".equals(res.getString(4)) && res.getInt(5) == Types.TIMESTAMP) {
                        return true;
                    }
                    return false;
                }
            }
        } catch (SQLException sqle) {
            LOG.error("Probelm retriving connection", sqle);
        } finally {
            ((ObjectStoreInterMineImpl) uos).releaseConnection(con);
        }
        return true;
    }

    /**
     * Returns the tracker manager of all trackers defined into the webapp configuration properties
     * @param webProperties the webapp configuration properties where the trackers are defined
     * @param userprofileOSW the object store writer to retrieve the database connection
     * @return TrackerManager the trackers manager
     */
    private TrackerDelegate getTrackerDelegate(Properties webProperties, ObjectStoreWriter userprofileOSW) {
        Map<String, Tracker> trackers = new HashMap<String, Tracker>();
        String trackerList = (String) webProperties.get("webapp.trackers");
        LOG.warn("initializeTrackers: trackerList is" + trackerList);
        if (trackerList != null) {
            String[] trackerClassNames = trackerList.split(",");
            TrackerDelegate td = new TrackerDelegate(trackerClassNames, userprofileOSW);
            return td;
        }
        return null;
    }

    private boolean verifyTablesExist(ObjectStore uos) {
        Connection con = null;
        Set<ClassDescriptor> classDescritpors = uos.getModel().getClassDescriptors();
        try {
            con = ((ObjectStoreInterMineImpl) uos).getConnection();
            for (ClassDescriptor cd : classDescritpors) {
                if (!cd.isInterface()) {
                    String tableNameToVerify = cd.getSimpleName().toLowerCase();
                    if (!DatabaseUtil.tableExists(con, tableNameToVerify)) {
                        LOG.error(
                                "In the userprofile database, the table " + tableNameToVerify + " doesn't exist.");
                        blockingErrorKeys.put("errors.init.tablesNotExisting", tableNameToVerify);
                        return false;
                    }
                }
            }
        } catch (SQLException sqle) {
            LOG.error("Probelm retrieving connection", sqle);
        } finally {
            ((ObjectStoreInterMineImpl) uos).releaseConnection(con);
        }
        return true;
    }

    private boolean verifyListTables(ObjectStore uos) {
        Connection con = null;
        try {
            con = ((ObjectStoreInterMineImpl) uos).getConnection();
            if (!DatabaseUtil.tableExists(con, "bagvalues")) {
                blockingErrorKeys.put("errors.init.savedbagtable.runLoadBagValuesTableAnt", null);
                return false;
            } else {
                if (!DatabaseUtil.columnExists(con, "bagvalues", "extra")
                        || DatabaseUtil.columnExists(con, "savedbag", "intermine_current")) {
                    blockingErrorKeys.put("errors.init.savedbagtable.runListTablesAnt", null);
                    return false;
                }
            }
        } catch (SQLException sqle) {
            LOG.error("Probelm retrieving connection", sqle);
        } finally {
            ((ObjectStoreInterMineImpl) uos).releaseConnection(con);
        }
        return true;
    }

    /**
     * Verify if we need to upgrade the list
     */
    private void verifyListUpgrade(ObjectStore uosw) {
        try {
            boolean listUpgrade = false;
            String productionSerialNumber = MetadataManager.retrieve(((ObjectStoreInterMineImpl) os).getDatabase(),
                    MetadataManager.SERIAL_NUMBER);
            String userprofileSerialNumber = MetadataManager
                    .retrieve(((ObjectStoreInterMineImpl) uosw).getDatabase(), MetadataManager.SERIAL_NUMBER);
            LOG.info("Production database has serialNumber \"" + productionSerialNumber + "\"");
            LOG.info("Userprofile database has serialNumber \"" + userprofileSerialNumber + "\"");
            if (productionSerialNumber != null) {
                if (userprofileSerialNumber == null || !userprofileSerialNumber.equals(productionSerialNumber)) {
                    listUpgrade = true;
                }
            }
            if (productionSerialNumber == null && userprofileSerialNumber != null) {
                listUpgrade = true;
            }
            if (listUpgrade) {
                LOG.warn("Serial numbers not equal: list upgrate needed");
                //set current attribute to false
                Connection conn = null;
                try {
                    conn = ((ObjectStoreInterMineImpl) uosw).getDatabase().getConnection();
                    if (DatabaseUtil.columnExists(conn, "savedbag", "intermine_state")) {
                        DatabaseUtil.updateColumnValue(((ObjectStoreInterMineImpl) uosw).getDatabase(), "savedbag",
                                "intermine_state", BagState.NOT_CURRENT.toString());
                    }
                } catch (SQLException sqle) {
                    throw new BuildException("Problems connecting bagvalues table", sqle);
                } finally {
                    try {
                        if (conn != null) {
                            conn.close();
                        }
                    } catch (SQLException sqle) {
                    }
                }
                // update the userprofileSerialNumber
                MetadataManager.store(((ObjectStoreInterMineImpl) uosw).getDatabase(),
                        MetadataManager.SERIAL_NUMBER, productionSerialNumber);
            }
        } catch (SQLException sqle) {
            throw new IllegalStateException("Error verifying list upgrading", sqle);
        }
    }
}