-

Vitro Version 1.4 Installation Guide

+

Vitro Version 1.5. Installation Guide

- November 15, 2011 + January 6, 2013
@@ -38,8 +38,13 @@ If you are upgrading a previous installation of Vitro, you may need to adjust your procedure accordingly.

-
-

Introduction to the Vitro installation

+

+ Other servlet containers: If you want to use a servlet container + other than Tomcat, please consult + instructions for other servlet containers in this directory. +

+ +

Introduction to the Vitro installation

Before beginning the installation, @@ -54,8 +59,8 @@

This is created when you checkout the Vitro source code (see installation step III, below). - This is where you will create your deploy.properties file - (see installation step V, below), + This is where you will create your build.properties file + (see installation step IV, below), and where you will make any modifications to the Vitro theme or code. You can create this wherever you choose.

@@ -68,18 +73,20 @@ but you won’t need to look at it or change it. If you need to change Vitro, make the changes in the distribution directory, and run the build script again. Tell the build script where to find Tomcat by setting tomcat.home - in the deploy.properties file (see installation step V, + in the build.properties file (see installation step IV, below).

The Vitro home directory

- Vitro will use this area to store some of the data it uses. Uploaded - image files are stored here, and the search index is stored here also. + This directory contains the runtime configuration properties for Vitro. + Vitro will also use this area to store some of the data it uses. Uploaded + image files are stored here, and the Solr home directory is stored here also. You can create this wherever you choose. Tell Vitro where to find the - home directory by setting vitro.home.directory - in the - deploy.properties file (see installation step V, - below). You must create this directory before starting Vitro, and you + home directory by setting vitro.home in the + build.properties file (see installation step IV, + below). You must create this directory before starting Vitro, + you must create the runtime.properties file in this directory + (see Step V, below), and you must ensure that Tomcat has permission to read and write to this directory when it runs.

@@ -176,7 +183,10 @@ Check out the Vitro Source Code
  • - Specify deployment properties + Specify build properties +
  • +
  • + Specify runtime properties
  • Compile and deploy @@ -275,12 +285,91 @@ If you want the very latest Vitro source, you can use subversion to check it out from SourceForge:
             svn checkout svn://svn.code.sf.net/p/vivo/vitro/code/trunk [Vitro_distribution_dir]

    -

    IV. Specify deployment properties

    + +

    IV. Specify build properties

    In the webapp/config directory of the Vitro distribution, - copy the file example.deploy.properties - to a file named simply deploy.properties. This file sets - several properties used in compilation and deployment. + copy the file example.build.properties + to a file named simply build.properties. + Edit the file to suit your installation, as described in the following table. +

    +

    + These properties are used in compilation and deployment. + They will be incorporated into Vitro when it is compiled in + Step VI. If you want to change these properties at + a later date, you will need to stop Tomcat, repeat Step VI, + and restart Tomcat. +

    +

    + Windows: + For those installing on Windows operating + system, include the windows drive and use the forward slash "/" and not + the back slash "\" in the directory locations, e.g. c:/tomcat. +

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Directory where tomcat is installed. +
    + tomcat.home + + /usr/local/tomcat +
    + Name of your Vitro application. +
    + webapp.name + + vitro +
    + Directory where the Vitro application will store + the data that it creates. This includes uploaded files (usually images) + and the Solr search index. Be sure this directory exists and is + writable by the user who the Tomcat service is running as. +
    + vitro.home + + /usr/local/vitro/home +
    + +

    V. Specify runtime properties

    +

    + In Step IV, you defined the location of the Vitro home directory, + by specifying vitro.home in the build.properties file. + Create that directory now. +

    +

    + In the webapp/config subdirectory of the Vitro distribution, you will find a file called + example.runtime.properties. Copy this to the Vitro home directory you have created, + renaming the copy to runtime.properties. + Edit the file to suit your installation, as described in the following table. +

    +

    + These properties are loaded when Vitro starts up. If you want to change these + properties at a later date, you will need to restart Tomcat for them to take + effect. You will not need to repeat Step VI.

    Windows: @@ -329,32 +418,6 @@ http://vitro.mydomain.edu/individual/ - - - Directory where tomcat is installed. - - - - - tomcat.home - - - /usr/local/tomcat - - - - - Name of your Vitro application. - - - - - webapp.name - - - vitro - - URL of Solr context used in local Vitro search. @@ -372,53 +435,6 @@ http://localhost:8080/vitrosolr - - - Restricts access to the Solr search platform. - The value is a regular expression. When a request is - made to Solr, the IP address of the requestor must the expression, - or the request will be rejected. -
    - Examples: -

      -
    • - vitro.local.solr.ipaddress.mask = 127\.0\.0\.1 -
    • -
    • - vitro.local.solr.ipaddress.mask = - 127\.0\.0\.1|0:0:0:0:0:0:0:1 -
    • -
    • - vitro.local.solr.ipaddress.mask = 169.254.* -
    • -
    - - - - - - vitro.local.solr.ipaddress.mask - - - 127\.0\.0\.1|[0:]+:1 - - - - - Directory where the Vitro application will store - the data that it creates. This includes uploaded files (usually images) - and the Solr search index. Be sure this directory exists and is - writable by the user who the Tomcat service is running as. - - - - - vitro.home.directory - - - /usr/local/vitro/data - - Specify an SMTP host that the application will @@ -641,7 +657,7 @@ -

    V. Compile and deploy

    +

    VI. Compile and deploy

    At the command line, change to the webapp directory inside the Vitro distribution directory. Then type: @@ -650,7 +666,7 @@

    to build Vitro and deploy to Tomcat's webapps directory.

    -

    VI. Configure Tomcat

    +

    VII. Configure Tomcat

    Set JVM parameters

    Vitro may require more memory than that allocated to Tomcat by @@ -686,7 +702,7 @@ accepting percent-encoded UTF-8.

    - Edit Tomcat's conf/server.xml and add the following attribute to each of the + Edit Tomcat's conf/server.xml and add the following attribute to each of the Connector elements: URIEncoding="UTF-8".

    @@ -699,6 +715,9 @@
                         </Server>
                 

    + Note: some versions of Tomcat already include this attribute as the default. +

    +

    Take care when creating Context elements

    Each of the webapps in the Vitro distribution (Vitro and Solr) includes a "context fragment" file, @@ -710,22 +729,35 @@ deployment parameters from the overridden context fragment.

    - See Section X below, + See Section XI below, for an example of overriding the Vitro context fragment.

    -

    VII. Start Tomcat

    +

    VIII. Start Tomcat

    Most Tomcat installations can be started by running startup.sh or startup.bat in Tomcat's bin directory. Point your - browser to "http://localhost:8080/vitro/" to test the application. If - Tomcat does not start up, or the Vitro application is not visible, check - the files in Tomcat's logs directory. Error messages are commonly found + browser to "http://localhost:8080/vitro/" to test the application. +

    +

    + On start up Vitro will run some diagnostic tests. If a + problem is detected, the normal Vitro pages will redirect + to a startup status page describing the problem. You + can stop tomcat, attempt to fix the problem and + proceeded from Step VI. The + startup status page may offer a continue link which + will allow you to use VIVO in spite of the problems. +

    +

    + If Tomcat does not start up, or the Vitro application is not visible, check + the files in Tomcat's logs directory. Error messages are commonly found in catalina.out or localhost.log

    -

    VIII. Log in and add RDF data

    + +

    +

    IX. Log in and add RDF data

    If the startup was successful, you will see a welcome message informing you that you have successfully installed Vitro. Click the "Log @@ -759,7 +791,7 @@ the page will show a simple index of the knowledge base.

    -

    IX. Set the Contact Email Address (if using +

    X. Set the Contact Email Address (if using "Contact Us" form)

    If you have configured your application to use the "Contact Us" @@ -781,7 +813,7 @@ provide an email address in this step, your users will receive a java error in the interface.

    -

    X. Set up Apache Tomcat Connector

    +

    XI. Set up Apache Tomcat Connector

    It is recommended that a Tomcat Connector such as mod_jk be used to ensure that the site address does not include the port number (e.g. @@ -832,16 +864,11 @@ cookies="true" > <Manager pathname="" /> - - <Environment type="java.lang.String" override="false" - name="path.configuration" - value="deploy.properties" - /> </Context> ... -

    XI. Using an External Authentication System +

    XII. Using an External Authentication System with Vitro

    @@ -878,7 +905,7 @@

    Configuring Vitro

    To enable external authentication, Vitro requires three values in - the deploy.properties + the runtime.properties file.

      @@ -893,7 +920,7 @@

      You need to tell Vitro the name of that HTTP header. Insert a - line like this in the deploy.properties file: + line like this in the runtime.properties file:

      externalAuth.netIdHeaderName = [the header name]

      @@ -907,7 +934,7 @@ the Vitro login form. You need to tell Vitro what text should appear in that button.

      - Put a line like this in the deploy.properties file: + Put a line like this in the runtime.properties file: externalAuth.buttonText = [the text for your login button] For example:

      externalAuth.buttonText = Log in using BearCat Shibboleth
      @@ -924,7 +951,7 @@ data model for a person with a property that matches the User’s network ID (the value of the property must be either a String literal or an untyped literal). You need to tell Vitro what property should be used - for matching. Insert a line like this in the deploy.properties file: + for matching. Insert a line like this in the runtime.properties file:

      selfEditing.idMatchingProperty = [the URI of the property]

      @@ -933,40 +960,38 @@

      selfEditing.idMatchingProperty = http://vitro.mydomain.edu/ns#networkId
    -

    XII. Was the installation successful?

    +

    XIII. Was the installation successful?

    If you have completed the previous steps, you have good indications that the installation was successful.

    • - Step VII showed that Tomcat recognized the webapp, and that the + Step VIII showed that Tomcat recognized the webapp, and that the webapp was able to present the initial page.
    • - Step VIII verified that you can log in to the administrator + Step IX verified that you can log in to the administrator account.

    - Step VII also shows that the Vitro self-tests ran successfully. + Step VIII also shows that the Vitro self-tests ran successfully. When Tomcat starts the Vitro webapp, it runs several tests. If any of these tests produce warnings or error message, you would see them instead of the Vitro home page.

    Among other things, the self-tests check -

    -
      -
    • The Vitro home directory exists and Vitro can write to it.
    • -
    • Vitro can connect to the database.
    • -
    • Vitro can connect to the Solr search application.
    • -
    -

    +

      +
    • The Vitro home directory exists and Vitro can write to it.
    • +
    • Vitro can connect to the database.
    • +
    • Vitro can connect to the Solr search application.
    • +
    If you saw the Vitro home page in Step VII, you know that your installation passed all of these tests.

    -

    XIII. Review the Vitro Terms of Use

    +

    XIV. Review the Vitro Terms of Use

    Vitro comes with a "Terms of Use" statement linked from the footer. The "Site Name" you assign in the "Site Information" form under the Site Admin @@ -992,7 +1017,7 @@

    diff --git a/opensocial/build_orng.xml b/opensocial/build_orng.xml index de883712c..02ceabc85 100644 --- a/opensocial/build_orng.xml +++ b/opensocial/build_orng.xml @@ -30,40 +30,53 @@ deploy - Configure the application and deploy directly into the Tomcat webapps target: properties - - - - - - - - - - - - - - - - - --> - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - + - - - - - - - - - + @@ -83,7 +96,7 @@ deploy - Configure the application and deploy directly into the Tomcat webapps - + @@ -145,5 +158,4 @@ deploy - Configure the application and deploy directly into the Tomcat webapps - \ No newline at end of file diff --git a/solr/template.context.xml b/solr/template.context.xml index 6d1f87578..8244079c8 100644 --- a/solr/template.context.xml +++ b/solr/template.context.xml @@ -1,11 +1,4 @@ - - - - - - - + + + - - - - + + + + - - - - - + + + + + + - - + + + + + + - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - -all - Runs "clean", then "deploy". -clean - Delete all artifacts so the next build will be from scratch. -compile - Compile the Java source files. -orng - Configure and deploy the ORNG Shindig application. -test - Compile and run the JUnit tests. -war - Create a WAR file to be deployed in a servlet container. -deploy - Deploy the application directly into the Tomcat webapps directory. - - - - - - - - - + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - + + + + + + - + - + - - - - - - + + + + - - - - - - - + + + + + - - + + + + + + + + + + + + + + + + + + + - + + + + + - - + + - - + + - - + + + + + + + + + + + + + + + @@ -227,112 +237,160 @@ deploy - Deploy the application directly into the Tomcat webapps directory. - - - - - + - ${revisionInfo.timestamp} + ${revisionInfo.timestamp} - - - + + - - - - + + + + + + - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + +all - Runs "clean", then "deploy". +clean - Delete all artifacts so the next build will be from scratch. +compile - Compile the Java source files. +orng - Configure and deploy the ORNG Shindig application. +test - Compile and run the JUnit tests. +distribute - Create WAR files to be deployed in a servlet container. +deploy - Deploy the application directly into the Tomcat webapps directory. + + + + + + - + @@ -393,13 +451,16 @@ deploy - Deploy the application directly into the Tomcat webapps directory. - + - + + \ No newline at end of file diff --git a/webapp/config/example.build.properties b/webapp/config/example.build.properties new file mode 100644 index 000000000..cd1a293ff --- /dev/null +++ b/webapp/config/example.build.properties @@ -0,0 +1,29 @@ +# ----------------------------------------------------------------------------- +# +# Vitro build properties +# +# This file is provided as example.build.properties. +# +# Save a copy of this file as build.properties, and edit the properties as +# needed for your installation. +# +# ----------------------------------------------------------------------------- + +# +# The base install directory for your Tomcat server. The Vitro application +# will be deployed in the /webapps directory below this base. +# +tomcat.home = /usr/local/tomcat + +# +# The name of the Vitro application. This will be used as the name of the +# subdirectory within your Tomcat server's /webapps directory. It also appears +# in the URL for the application. For example, http://my.vitro.server/vitro +# +webapp.name = vitro + +# +# The location where the Vitro application will store the data that it creates. +# This includes uploaded files (usually images) and the search index. +# +vitro.home = /usr/local/vitro/home diff --git a/webapp/config/example.deploy.properties b/webapp/config/example.runtime.properties similarity index 70% rename from webapp/config/example.deploy.properties rename to webapp/config/example.runtime.properties index 9b66f1385..691f98139 100644 --- a/webapp/config/example.deploy.properties +++ b/webapp/config/example.runtime.properties @@ -1,11 +1,11 @@ # ----------------------------------------------------------------------------- # -# Vitro deployment properties +# Vitro runtime properties # -# This file is provided as example.deploy.properties. +# This file is provided as example.runtime.properties. # -# Save a copy of this file as deploy.properties, and edit the properties as -# needed for your deployment. +# Save a copy of this file as runtime.properties in your Vitro home directory, +# and edit the properties as needed for your installation. # # ----------------------------------------------------------------------------- @@ -22,19 +22,6 @@ # Vitro.defaultNamespace = http://vivo.mydomain.edu/individual/ -# -# The base install directory for your Tomcat server. The Vitro application -# will be deployed in the /webapps directory below this base. -# -tomcat.home = /usr/local/tomcat - -# -# The name of the Vitro application. This will be used as the name of the -# subdirectory within your Tomcat server's /webapps directory. It also appears -# in the URL for the application. For example, http://my.vitro.server/vitro -# -webapp.name = vitro - # # URL of Solr context used in local Vitro search. This will usually consist of: # scheme + server_name + port + vitro_webapp_name + "solr" @@ -45,28 +32,6 @@ webapp.name = vitro # vitro.local.solr.url = http://localhost:8080/vitrosolr vitro.local.solr.url = http://localhost:8080/vitrosolr -# -# Restricts access to the Solr search platform. The value is a regular expression. -# When a request is made to Solr, the IP address of the requestor must match the -# regular expression, or the request will be rejected. -# -# NOTE: don't leave a space on the end of the line, unless you want it to be -# part of the expression. -# Examples: -# vitro.local.solr.ipaddress.mask = 127\.0\.0\.1 -# vitro.local.solr.ipaddress.mask = 127\.0\.0\.1|0:0:0:0:0:0:0:1 -# vitro.local.solr.ipaddress.mask = 169\.254\..* -# If this line is removed, your Solr server will respond to requests from any -# location. This may be useful for experimenting but would likely be considered -# a security problem in a production environment. -vitro.local.solr.ipaddress.mask = 127\.0\.0\.1|[0:]+:1 - -# -# The location where the Vitro application will store the data that it creates. -# This includes uploaded files (usually images) and the search index. -# -vitro.home.directory = /usr/local/vitro/data - # # Email parameters which VIVO can use to send mail. If these are left empty, # the "Contact Us" form will be disabled and users will not be notified of diff --git a/webapp/context.xml b/webapp/context.xml index 364c41b35..b0cd304f7 100644 --- a/webapp/context.xml +++ b/webapp/context.xml @@ -1,20 +1,5 @@ - - - - - diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/RootUserPolicy.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/RootUserPolicy.java index 03234a2a1..9c635a6cf 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/RootUserPolicy.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/auth/policy/RootUserPolicy.java @@ -120,7 +120,7 @@ public class RootUserPolicy implements PolicyIface { PROPERTY_ROOT_USER_EMAIL); if (email == null) { throw new IllegalStateException( - "deploy.properties must contain a value for '" + "runtime.properties must contain a value for '" + PROPERTY_ROOT_USER_EMAIL + "'"); } else { return email; @@ -177,7 +177,7 @@ public class RootUserPolicy implements PolicyIface { private void complainAboutMultipleRootUsers() { for (String other : otherRootUsers) { - ss.warning(this, "deploy.properties specifies '" + ss.warning(this, "runtime.properties specifies '" + configuredRootUser + "' as the value for '" + PROPERTY_ROOT_USER_EMAIL + "', but the system also contains this root user: " @@ -189,7 +189,7 @@ public class RootUserPolicy implements PolicyIface { private void complainAboutWrongRootUsers() { for (String other : otherRootUsers) { - ss.warning(this, "deploy.properties specifies '" + ss.warning(this, "runtime.properties specifies '" + configuredRootUser + "' as the value for '" + PROPERTY_ROOT_USER_EMAIL + "', but the system contains this root user instead: " diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationProperties.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationProperties.java index 0109c9286..74112fc15 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationProperties.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationProperties.java @@ -20,8 +20,8 @@ import org.apache.commons.logging.LogFactory; * are attached to the servlet context. * * The customary behavior is for ConfigurationPropertiesSetup to create a - * ConfigurationPropertiesImpl, which will parse the deploy.properties file for - * these properties. + * ConfigurationPropertiesImpl, which will obtain the properties from the + * build.properties file and the runtime.properties file. */ public abstract class ConfigurationProperties { private static final Log log = LogFactory diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesImpl.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesImpl.java index f0440b5de..1361d350a 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesImpl.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesImpl.java @@ -9,13 +9,16 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Properties; +import java.util.TreeMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * The basic implementation of ConfigurationProperties. It loads the - * configuration properties from a properties file and stores them in a map. + * configuration properties from a properties file and stores them in a map. It + * also permits the caller to supply a map of "preemptive" properties that will + * be included and will override any matching properties from the file. * * Leading and trailing white space are trimmed from the property values. * @@ -27,23 +30,25 @@ public class ConfigurationPropertiesImpl extends ConfigurationProperties { private final Map propertyMap; - public ConfigurationPropertiesImpl(InputStream stream) { + public ConfigurationPropertiesImpl(InputStream stream, + Map preemptiveProperties) throws IOException { Properties props = loadFromPropertiesFile(stream); Map map = copyPropertiesToMap(props); - trimWhiteSpaceFromValues(map); - this.propertyMap = Collections.unmodifiableMap(map); + if (preemptiveProperties != null) { + map.putAll(preemptiveProperties); + } + + trimWhiteSpaceFromValues(map); + + this.propertyMap = Collections.unmodifiableMap(map); log.debug("Configuration properties are: " + map); } - private Properties loadFromPropertiesFile(InputStream stream) { + private Properties loadFromPropertiesFile(InputStream stream) + throws IOException { Properties props = new Properties(); - try { - props.load(stream); - } catch (IOException e) { - throw new IllegalStateException( - "Failed to parse the configuration properties file.", e); - } + props.load(stream); return props; } @@ -84,7 +89,8 @@ public class ConfigurationPropertiesImpl extends ConfigurationProperties { @Override public String toString() { - return "ConfigurationPropertiesImpl[propertyMap=" + propertyMap + "]"; + return "ConfigurationPropertiesImpl[propertyMap=" + + new TreeMap(propertyMap) + "]"; } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSetup.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSetup.java index 82318a4de..b5b5da3fa 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSetup.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSetup.java @@ -4,11 +4,13 @@ package edu.cornell.mannlib.vitro.webapp.config; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Properties; -import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.servlet.ServletContext; @@ -21,61 +23,45 @@ import org.apache.commons.logging.LogFactory; import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus; /** - * Reads the configuration properties from a file and stores them in the servlet + * Locates the runtime configuration properties and stores them in the servlet * context. * * This must be invoked before any listener that requires configuration * properties. * - * The path to the file can be specified by an Environment name in the Context, - * like this: + * The properties file must be called 'runtime.properties' in the Vitro home + * directory. The path to the Vitro home directory can be specifed by an JNDI + * value, or by a System property, or by a property in + * WEB-INF/resources/build.properties, in that order. If the Vitro home + * directory is specified in more than one way, a warning is issued and the + * first value is used. * - *
    + * If the Vitro home directory cannot be located, or if it does not contain a
    + * file called 'runtime.properties', or if the file cannot be loaded, a fatal
    + * error is registered to abort the startup.
      * 
    - * 
    - *     
    - * 
    - * 
    - * 
    - * - * We look in this environment variable to find the path to the properties file. - * If there is no such environment variable, the default path is used. - * - * Once the path has been determined, we will use it to look for a resource in - * the classpath. So if the path is "deploy.properties", it might be found in - * "tomcat/webapps/vivo/WEB-INF/classes/deploy.properties". Of course, it might - * also be found in any other portion of the classpath as well. - * - * If we can't find the resource in the classpath, we will use it to look for an - * external file. So, one might reasonably set this value to something like - * "/usr/local/vitro/stuff/my.deploy.properties". - * - * If neither a resource nor an external file can be found, we throw an - * exception and set AbortStartup. + * The ConfigurationProperties bean is created from the key/value pairs found in + * 'runtime.properties', and is stored in the servlet context. The value that + * was determined for 'vitro.home' is also included in the bean. */ public class ConfigurationPropertiesSetup implements ServletContextListener { private static final Log log = LogFactory .getLog(ConfigurationPropertiesSetup.class); - /** - * The JNDI naming context where Tomcat stores environment attributes. - */ - static final String JNDI_BASE = "java:comp/env"; + /** JNDI path that defines the Vitro home directory */ + private static final String VHD_JNDI_PATH = "java:comp/env/vitro/home"; - /** - * The name of the JNDI environment mapping for the path to the - * configuration file (or resource). - */ - static final String PATH_CONFIGURATION = "path.configuration"; + /** System property that defines the Vitro home directory */ + private static final String VHD_SYSTEM_PROPERTY = "vitro.home"; - /** - * If we don't find the path to the config properties from a JNDI mapping, - * use this. Not final, so we can jigger it for unit tests. - */ - private static String DEFAULT_CONFIG_PATH = "deploy.properties"; + /** build.properties property that defines the Vitro home directory */ + private static final String VHD_BUILD_PROPERTY = "vitro.home"; + + /** Configuration property to store the Vitro home directory */ + private static final String VHD_CONFIGURATION_PROPERTY = "vitro.home"; + + /** Name of the file that contains runtime properties. */ + private static final String FILE_RUNTIME_PROPERTIES = "runtime.properties"; @Override public void contextInitialized(ServletContextEvent sce) { @@ -85,12 +71,18 @@ public class ConfigurationPropertiesSetup implements ServletContextListener { try { InputStream stream = null; try { - stream = locatePropertiesFile(); - + File vitroHomeDir = locateVitroHomeDirectory(ctx, ss); + + File runtimePropertiesFile = locateRuntimePropertiesFile( + vitroHomeDir, ss); + stream = new FileInputStream(runtimePropertiesFile); + + Map preempts = createPreemptiveProperties( + VHD_CONFIGURATION_PROPERTY, vitroHomeDir); ConfigurationPropertiesImpl bean = new ConfigurationPropertiesImpl( - stream); + stream, preempts); + ConfigurationProperties.setBean(ctx, bean); - ss.info(this, "Loaded " + bean.getPropertyMap().size() + " properties."); } finally { @@ -102,81 +94,171 @@ public class ConfigurationPropertiesSetup implements ServletContextListener { } } } + } catch (IllegalStateException e) { + ss.fatal(this, e.getMessage(), e); } catch (Exception e) { - log.error(e, e); ss.fatal(this, e.getMessage(), e); } } - private InputStream locatePropertiesFile() { - String path = determinePathToProperties(); - log.debug("Configuration properties path is '" + path + "'"); - - if (resourceExists(path)) { - log.debug("Found configuration properties as a resource."); - return getResourceStream(path); - } - - if (externalFileExists(path)) { - log.debug("Found configuration properties as an external file."); - return getExternalFileStream(path); - } - - throw new IllegalStateException("Can't find the properties file at '" - + path + "'"); - } - /** - * If we can't find it with JNDI, use the default. + * Look in the JDNI environment, the system properties, and the + * build.properties file. + * + * If we don't find it, fail. If we find it more than once, warn and use the + * first one. + * + * Confirm that it is an existing, readable directory. */ - private String determinePathToProperties() { + private File locateVitroHomeDirectory(ServletContext ctx, StartupStatus ss) { + Map whereWasIt = new LinkedHashMap(); + getVhdFromJndi(whereWasIt); + getVhdFromSystemProperties(whereWasIt); + getVhdFromBuildProperties(ctx, whereWasIt); + + if (whereWasIt.isEmpty()) { + String message = String.format("Can't find a value " + + "for the Vitro home directory. " + + "Looked in JNDI environment at '%s'. " + + "Looked for a system property named '%s'. " + + "Looked in 'WEB-INF/resources/build.properties' " + + "for '%s'.", VHD_JNDI_PATH, VHD_SYSTEM_PROPERTY, + VHD_BUILD_PROPERTY); + throw new IllegalStateException(message); + } else if (whereWasIt.size() > 1) { + String message = String.format("Found multiple values for the " + + "Vitro home directory: " + whereWasIt.keySet()); + ss.warning(this, message); + } + String message = whereWasIt.keySet().iterator().next(); + String vhdPath = whereWasIt.values().iterator().next(); + + ss.info(this, message); + + File vhd = new File(vhdPath); + if (!vhd.exists()) { + throw new IllegalStateException("Vitro home directory '" + vhdPath + + "' does not exist."); + } + if (!vhd.isDirectory()) { + throw new IllegalStateException("Vitro home directory '" + vhdPath + + "' is not a directory."); + } + if (!vhd.canRead()) { + throw new IllegalStateException("Vitro home directory '" + vhdPath + + "' cannot be read."); + } + if (!vhd.canWrite()) { + throw new IllegalStateException( + "Can't write to Vitro home directory: '" + vhdPath + "'."); + } + + return vhd; + } + + private void getVhdFromJndi(Map whereWasIt) { try { - Context envCtx = (Context) new InitialContext().lookup(JNDI_BASE); - if (envCtx == null) { - log.debug("JNDI Lookup on '" + JNDI_BASE + "' failed."); - return DEFAULT_CONFIG_PATH; + String vhdPath = (String) new InitialContext() + .lookup(VHD_JNDI_PATH); + + if (vhdPath == null) { + log.debug("Didn't find a JNDI value at '" + VHD_JNDI_PATH + + "'."); + return; } - String configPath = (String) envCtx.lookup(PATH_CONFIGURATION); - if (configPath == null) { - log.debug("JNDI Lookup on '" + PATH_CONFIGURATION + "' failed."); - return DEFAULT_CONFIG_PATH; - } - - log.debug("deploy.property as specified by JNDI: " + configPath); - return configPath; + log.debug("'" + VHD_JNDI_PATH + "' as specified by JNDI: " + + vhdPath); + String message = String.format( + "JNDI environment '%s' was set to '%s'", VHD_JNDI_PATH, + vhdPath); + whereWasIt.put(message, vhdPath); } catch (NamingException e) { - log.warn("JNDI lookup failed. " - + "Using default path for config properties.", e); - return DEFAULT_CONFIG_PATH; + log.debug("JNDI lookup failed. " + e); } } - private boolean resourceExists(String path) { - return getResourceStream(path) != null; + private void getVhdFromSystemProperties(Map whereWasIt) { + String vhdPath = System.getProperty(VHD_SYSTEM_PROPERTY); + + if (vhdPath == null) { + log.debug("Didn't find a system property value at '" + + VHD_SYSTEM_PROPERTY + "'."); + return; + } + + log.debug("'" + VHD_SYSTEM_PROPERTY + + "' as specified by system property: " + vhdPath); + String message = String.format("System property '%s' was set to '%s'", + VHD_SYSTEM_PROPERTY, vhdPath); + whereWasIt.put(message, vhdPath); } - private InputStream getResourceStream(String path) { - return getClass().getClassLoader().getResourceAsStream(path); - } + private void getVhdFromBuildProperties(ServletContext ctx, + Map whereWasIt) { + String resourcePath = "/WEB-INF/resources/build.properties"; - private boolean externalFileExists(String path) { - File file = new File(path); - return file.isFile(); - } - - private InputStream getExternalFileStream(String path) { InputStream stream = null; - File file = new File(path); - if (file.isFile()) { - try { - stream = new FileInputStream(file); - } catch (FileNotFoundException e) { - // testing file.isFile() should have prevented this - log.error(e, e); + try { + stream = ctx.getResourceAsStream(resourcePath); + if (stream == null) { + log.debug("Didn't find a resource at '" + resourcePath + "'."); + return; + } + + Properties props = new Properties(); + props.load(stream); + String vhdPath = props.getProperty(VHD_BUILD_PROPERTY); + if (vhdPath == null) { + log.debug("'" + resourcePath + "' didn't contain a value for '" + + VHD_BUILD_PROPERTY + "'."); + return; + } + + log.debug("'" + VHD_BUILD_PROPERTY + + "' as specified by build.properties: " + vhdPath); + String message = String.format( + "In resource '%s', '%s' was set to '%s'.", resourcePath, + VHD_BUILD_PROPERTY, vhdPath); + whereWasIt.put(message, vhdPath); + } catch (IOException e) { + log.warn("Failed to load from '" + resourcePath + "'.", e); + } finally { + if (stream != null) { + try { + stream.close(); + } catch (IOException e) { + e.printStackTrace(); + } } } - return stream; + } + + private File locateRuntimePropertiesFile(File vitroHomeDir, StartupStatus ss) { + File rpf = new File(vitroHomeDir, FILE_RUNTIME_PROPERTIES); + + if (!rpf.exists()) { + throw new IllegalStateException("Did not find '" + + FILE_RUNTIME_PROPERTIES + "' in vitro home directory '" + + vitroHomeDir + "'"); + } + if (!rpf.isFile()) { + throw new IllegalStateException("'" + rpf.getPath() + + "' is not a file."); + } + if (!rpf.canRead()) { + throw new IllegalStateException("Cannot read '" + rpf.getPath() + + "'."); + } + ss.info(this, "Loading runtime properties from '" + rpf.getPath() + "'"); + return rpf; + } + + private Map createPreemptiveProperties( + String propertyVitroHome, File vitroHomeDir) { + Map map = new HashMap(); + map.put(propertyVitroHome, vitroHomeDir.getAbsolutePath()); + return map; } @Override diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSmokeTests.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSmokeTests.java index e5b1018f9..7d7a6211b 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSmokeTests.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/config/ConfigurationPropertiesSmokeTests.java @@ -32,7 +32,7 @@ public class ConfigurationPropertiesSmokeTests implements private static final Log log = LogFactory .getLog(ConfigurationPropertiesSmokeTests.class); - private static final String PROPERTY_HOME_DIRECTORY = "vitro.home.directory"; + private static final String PROPERTY_HOME_DIRECTORY = "vitro.home"; private static final String PROPERTY_DB_URL = "VitroConnection.DataSource.url"; private static final String PROPERTY_DB_USERNAME = "VitroConnection.DataSource.username"; private static final String PROPERTY_DB_PASSWORD = "VitroConnection.DataSource.password"; @@ -61,7 +61,7 @@ public class ConfigurationPropertiesSmokeTests implements ConfigurationProperties props, StartupStatus ss) { String homeDirectoryPath = props.getProperty(PROPERTY_HOME_DIRECTORY); if (homeDirectoryPath == null || homeDirectoryPath.isEmpty()) { - ss.fatal(this, "deploy.properties does not contain a value for '" + ss.fatal(this, "Can't find a value for the home directory: '" + PROPERTY_HOME_DIRECTORY + "'"); return; } @@ -97,19 +97,19 @@ public class ConfigurationPropertiesSmokeTests implements ConfigurationProperties props, StartupStatus ss) { String url = props.getProperty(PROPERTY_DB_URL); if (url == null || url.isEmpty()) { - ss.fatal(this, "deploy.properties does not contain a value for '" + ss.fatal(this, "runtime.properties does not contain a value for '" + PROPERTY_DB_URL + "'"); return; } String username = props.getProperty(PROPERTY_DB_USERNAME); if (username == null || username.isEmpty()) { - ss.fatal(this, "deploy.properties does not contain a value for '" + ss.fatal(this, "runtime.properties does not contain a value for '" + PROPERTY_DB_USERNAME + "'"); return; } String password = props.getProperty(PROPERTY_DB_PASSWORD); if (password == null || password.isEmpty()) { - ss.fatal(this, "deploy.properties does not contain a value for '" + ss.fatal(this, "runtime.properties does not contain a value for '" + PROPERTY_DB_PASSWORD + "'"); return; } @@ -259,7 +259,7 @@ public class ConfigurationPropertiesSmokeTests implements ConfigurationProperties props, StartupStatus ss) { String ns = props.getProperty(PROPERTY_DEFAULT_NAMESPACE); if (ns == null || ns.isEmpty()) { - ss.fatal(this, "deploy.properties does not contain a value for '" + ss.fatal(this, "runtime.properties does not contain a value for '" + PROPERTY_DEFAULT_NAMESPACE + "'"); return; } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/ExternalAuthHelper.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/ExternalAuthHelper.java index 74168a2a3..d1bdf0dfe 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/ExternalAuthHelper.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/ExternalAuthHelper.java @@ -117,7 +117,7 @@ public class ExternalAuthHelper { } if (externalAuthServerUrl == null) { - log.debug("deploy.properties doesn't contain a value for '" + log.debug("runtime.properties doesn't contain a value for '" + PROPERTY_EXTERNAL_AUTH_SERVER_URL + "' -- sending directly to '" + returnUrl + "'"); return returnUrl; @@ -142,7 +142,7 @@ public class ExternalAuthHelper { if (externalAuthHeaderName == null) { log.error("User asked for external authentication, " - + "but deploy.properties doesn't contain a value for '" + + "but runtime.properties doesn't contain a value for '" + PROPERTY_EXTERNAL_AUTH_ID_HEADER + "'"); return null; } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthReturn.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthReturn.java index a2e77115a..878a60b30 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthReturn.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/authenticate/LoginExternalAuthReturn.java @@ -37,7 +37,7 @@ public class LoginExternalAuthReturn extends BaseLoginServlet { * the header will contain the name of the user who just logged in. * * Deal with these possibilities: - * - The header name was not configured in deploy.properties. Complain. + * - The header name was not configured in runtime.properties. Complain. * - No username: the login failed. Complain * - User corresponds to a User acocunt. Record the login. * - User corresponds to an Individual (self-editor). diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ContactMailController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ContactMailController.java index 96243ff0f..a48d040f0 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ContactMailController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ContactMailController.java @@ -50,7 +50,7 @@ public class ContactMailController extends FreemarkerHttpServlet { private final static String TEMPLATE_ERROR = "contactForm-error.ftl"; private final static String TEMPLATE_FORM = "contactForm-form.ftl"; - private static final String PROPERTY_VITRO_HOME_DIR = "vitro.home.directory"; + private static final String PROPERTY_VITRO_HOME_DIR = "vitro.home"; private static final String EMAIL_JOURNAL_FILE_DIR = "emailJournal"; private static final String EMAIL_JOURNAL_FILE_NAME = "contactFormEmails.html"; diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/grefine/JSONReconcileServlet.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/grefine/JSONReconcileServlet.java index 1164b383d..670eeeda3 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/grefine/JSONReconcileServlet.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/grefine/JSONReconcileServlet.java @@ -201,7 +201,7 @@ public class JSONReconcileServlet extends VitroHttpServlet { viewJson.put("url", urlBuf.toString() + "/individual?uri={{id}}"); json.put("view", viewJson); - // parse defaultTypeList from deploy.properties + // parse defaultTypeList from runtime.properties if (defaultTypeList != null) { String[] splitList = defaultTypeList.split(";"); String[][] idNameArray = new String[splitList.length][splitList.length]; diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/backend/FileStorageSetup.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/backend/FileStorageSetup.java index 802508a8c..3f7aa29e0 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/backend/FileStorageSetup.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/filestorage/backend/FileStorageSetup.java @@ -35,7 +35,7 @@ public class FileStorageSetup implements ServletContextListener { * {@link ConfigurationProperties} for the vivo home directory. The file * storage base directory is in a subdirectory below this one. */ - public static final String PROPERTY_VITRO_HOME_DIR = "vitro.home.directory"; + public static final String PROPERTY_VITRO_HOME_DIR = "vitro.home"; public static final String FILE_STORAGE_SUBDIRECTORY = "uploads"; /** diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/search/solr/SolrSetup.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/solr/SolrSetup.java index 6dc2a7d8d..568f3713e 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/search/solr/SolrSetup.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/solr/SolrSetup.java @@ -89,7 +89,7 @@ public class SolrSetup implements javax.servlet.ServletContextListener{ /* setup the http connection with the solr server */ String solrServerUrlString = ConfigurationProperties.getBean(sce).getProperty("vitro.local.solr.url"); if( solrServerUrlString == null ){ - ss.fatal(this, "Could not find vitro.local.solr.url in deploy.properties. "+ + ss.fatal(this, "Could not find vitro.local.solr.url in runtime.properties. "+ "Vitro application needs a URL of a solr server that it can use to index its data. " + "It should be something like http://localhost:${port}" + context.getContextPath() + "solr" ); @@ -101,7 +101,7 @@ public class SolrSetup implements javax.servlet.ServletContextListener{ solrServerUrl = new URL(solrServerUrlString); } catch (MalformedURLException e) { ss.fatal(this, "Can't connect with the solr server. " + - "The value for vitro.local.solr.url in deploy.properties is not a valid URL: " + solrServerUrlString); + "The value for vitro.local.solr.url in runtime.properties is not a valid URL: " + solrServerUrlString); return; } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/JenaDataSourceSetupBase.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/JenaDataSourceSetupBase.java index 46958132c..fbe8e61f4 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/JenaDataSourceSetupBase.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/JenaDataSourceSetupBase.java @@ -546,7 +546,7 @@ public class JenaDataSourceSetupBase extends JenaBaseDaoCon { if ((dns != null) && (!dns.isEmpty())) { return dns; } else { - throw new IllegalStateException("deploy.properties does not " + throw new IllegalStateException("runtime.properties does not " + "contain a value for '" + VITRO_DEFAULT_NAMESPACE + "'"); } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/SolrSmokeTest.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/SolrSmokeTest.java index f0fdeadc4..76065eeee 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/SolrSmokeTest.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/SolrSmokeTest.java @@ -50,7 +50,7 @@ public class SolrSmokeTest implements ServletContextListener { .getProperty("vitro.local.solr.url", ""); if (solrUrlString.isEmpty()) { ss.fatal(this, "Can't connect to Solr search engine. " - + "deploy.properties must contain a value for " + + "runtime.properties must contain a value for " + "vitro.local.solr.url"); return; } @@ -62,7 +62,7 @@ public class SolrSmokeTest implements ServletContextListener { } catch (MalformedURLException e) { ss.fatal(this, "Can't connect to Solr search engine. " + "The value for vitro.local.solr.url " - + "in deploy.properties is not a valid URL: '" + + "in runtime.properties is not a valid URL: '" + solrUrlString + "'", e); } @@ -124,9 +124,7 @@ public class SolrSmokeTest implements ServletContextListener { int status = e.getStatusCode(); Throwable cause = e.getCause(); - if (status == HttpStatus.SC_FORBIDDEN) { - warnForbidden(); - } else if (status == SOCKET_TIMEOUT_STATUS) { + if (status == SOCKET_TIMEOUT_STATUS) { warnSocketTimeout(); } else if (status != 0) { warnBadHttpStatus(status); @@ -151,23 +149,14 @@ public class SolrSmokeTest implements ServletContextListener { ss.warning(listener, "Can't connect to the Solr search engine. " + "The socket connection has repeatedly timed out. " + "Check the value of vitro.local.solr.url in " - + "deploy.properties. Is Solr responding at that URL?"); - } - - private void warnForbidden() { - ss.warning(listener, "Can't connect to the Solr search engine. " - + "The Solr server will not accept connections from this " - + "host. Check the value of " - + "vitro.local.solr.ipaddress.mask in " - + "deploy.properties -- " - + "does it authorize access from this IP address?"); + + "runtime.properties. Is Solr responding at that URL?"); } private void warnBadHttpStatus(int status) { ss.warning(listener, "Can't connect to the Solr search engine. " + "The Solr server returned a status code of " + status + ". Check the value of vitro.local.solr.url in " - + "deploy.properties."); + + "runtime.properties."); } private void warnProtocolViolation(HttpException e) { @@ -179,7 +168,7 @@ public class SolrSmokeTest implements ServletContextListener { ss.warning(listener, "Can't connect to the Solr search engine. '" + e.getMessage() + "' is an unknown host." + "Check the value of vitro.local.solr.url in " - + "deploy.properties.", e); + + "runtime.properties.", e); } private void warnConnectionRefused(ConnectException e) { @@ -187,7 +176,7 @@ public class SolrSmokeTest implements ServletContextListener { + "The host refused the connection. " + "Is it possible that the port number is incorrect? " + "Check the value of vitro.local.solr.url in " - + "deploy.properties.", e); + + "runtime.properties.", e); } private void warnTransportError(IOException e) { diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/UpdatePermissionSetUris.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/UpdatePermissionSetUris.java index 212154ff4..f0fffdc7b 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/UpdatePermissionSetUris.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/UpdatePermissionSetUris.java @@ -182,10 +182,10 @@ public class UpdatePermissionSetUris implements ServletContextListener { Journal(ServletContext ctx) throws IOException { String homeDirectoryPath = ConfigurationProperties.getBean(ctx) - .getProperty("vitro.home.directory"); + .getProperty("vitro.home"); if (homeDirectoryPath == null) { throw new IllegalStateException( - "No value found for vitro.home.directory"); + "No value found for vitro.home"); } File homeDirectory = new File(homeDirectoryPath); confirmIsDirectory(homeDirectory); diff --git a/webapp/src/edu/ucsf/vitro/opensocial/OpenSocialSmokeTests.java b/webapp/src/edu/ucsf/vitro/opensocial/OpenSocialSmokeTests.java index b9b75917a..7ecfce485 100644 --- a/webapp/src/edu/ucsf/vitro/opensocial/OpenSocialSmokeTests.java +++ b/webapp/src/edu/ucsf/vitro/opensocial/OpenSocialSmokeTests.java @@ -70,7 +70,7 @@ public class OpenSocialSmokeTests implements ServletContextListener { configProps = ConfigurationProperties.getBean(ctx); /* - * If OpenSocial is not configured in deploy.properties, skip the tests. + * If OpenSocial is not configured in runtime.properties, skip the tests. */ if (!configurationPresent()) { ss.info(this, "The OpenSocial connection is not configured."); @@ -188,7 +188,7 @@ public class OpenSocialSmokeTests implements ServletContextListener { } /** - * Check that the Token Key file has been specified in deploy.properties, + * Check that the Token Key file has been specified in runtime.properties, * and that it actually does exist. */ private void checkTokenKeyFile() { @@ -210,14 +210,14 @@ public class OpenSocialSmokeTests implements ServletContextListener { } /** - * Get the Token Service info from deploy.properties. It must be in the form + * Get the Token Service info from runtime.properties. It must be in the form * of host:port, and may not refer to localhost. */ private void checkTokenServiceInfo() { String tsInfo = configProps.getProperty(PROPERTY_SHINDIG_TOKEN_SERVICE); if (StringUtils.isEmpty(tsInfo)) { warnings.add(new Warning("There is no value for '" - + PROPERTY_SHINDIG_TOKEN_SERVICE + "' in deploy.properties")); + + PROPERTY_SHINDIG_TOKEN_SERVICE + "' in runtime.properties")); return; } @@ -278,7 +278,7 @@ public class OpenSocialSmokeTests implements ServletContextListener { private static class NoSuchPropertyException extends Exception { NoSuchPropertyException(String key) { - super("There is no value for '" + key + "' in deploy.properties"); + super("There is no value for '" + key + "' in build.properties"); } } From dad9430c2f31c27fc7eee189d025f48a20f59b68 Mon Sep 17 00:00:00 2001 From: hudajkhan Date: Thu, 17 Jan 2013 11:53:41 -0500 Subject: [PATCH 27/43] updates to dynamic fields etc. for search, enable parameter to remove space for dynamic field --- solr/homeDirectoryTemplate/conf/schema.xml | 2 +- .../solr/documentBuilding/ContextNodeFields.java | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/solr/homeDirectoryTemplate/conf/schema.xml b/solr/homeDirectoryTemplate/conf/schema.xml index 0292cd5f8..b784f33c9 100644 --- a/solr/homeDirectoryTemplate/conf/schema.xml +++ b/solr/homeDirectoryTemplate/conf/schema.xml @@ -253,7 +253,7 @@ - + diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/search/solr/documentBuilding/ContextNodeFields.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/solr/documentBuilding/ContextNodeFields.java index bee1be521..b9d71beb6 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/search/solr/documentBuilding/ContextNodeFields.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/solr/documentBuilding/ContextNodeFields.java @@ -80,7 +80,7 @@ public class ContextNodeFields implements DocumentModifier{ * @return StringBuffer with text values to add to ALLTEXT field of solr Document. */ protected StringBuffer executeQueryForValues( Individual individual, Collection queries){ - /* execute all the queries on the list and concat the values to add to all text */ + /* execute all the queries on the list and concat the values to add to all text */ RDFService rdfService = rdfServiceFactory.getRDFService(); StringBuffer allValues = new StringBuffer(""); @@ -95,7 +95,7 @@ public class ContextNodeFields implements DocumentModifier{ ResultSet results = RDFServiceUtils.sparqlSelectQuery(subInUriQuery, rdfService); while(results.hasNext()){ valuesForQuery.append( - getTextForRow( results.nextSolution() ) ) ; + getTextForRow( results.nextSolution(), true ) ) ; } }catch(Throwable t){ @@ -111,10 +111,10 @@ public class ContextNodeFields implements DocumentModifier{ } rdfService.close(); - return allValues; + return allValues; } - protected String getTextForRow( QuerySolution row){ + protected String getTextForRow( QuerySolution row, boolean addSpace){ if( row == null ) return ""; @@ -124,7 +124,11 @@ public class ContextNodeFields implements DocumentModifier{ String name = iter.next(); RDFNode node = row.get( name ); if( node != null ){ - text.append(" ").append( node.toString() ); + if(addSpace) { + text.append(" ").append( node.toString() ); + } else { + text.append(node.toString()); + } }else{ log.debug(name + " is null"); } @@ -132,7 +136,6 @@ public class ContextNodeFields implements DocumentModifier{ return text.toString(); } - public void shutdown(){ shutdown=true; } From bb6b2fa970c9143141ea21296f885acca2f12eb2 Mon Sep 17 00:00:00 2001 From: j2blake Date: Thu, 24 Jan 2013 16:21:36 -0500 Subject: [PATCH 28/43] VIVO-12 NIHVIVO-4011 Provide config and GUI for selecting Locale --- doc/install.html | 74 ++++- webapp/config/example.runtime.properties | 43 +++ .../freemarker/FreemarkerConfiguration.java | 1 - .../mannlib/vitro/webapp/i18n/I18n.java | 2 +- ...ceBundle.java => VitroResourceBundle.java} | 10 +- .../selection/LocaleSelectionController.java | 100 ++++++ .../selection/LocaleSelectionDataGetter.java | 95 ++++++ .../i18n/selection/LocaleSelectionFilter.java | 113 +++++++ .../i18n/selection/LocaleSelectionSetup.java | 148 +++++++++ .../selection/LocaleSelectorUtilities.java | 54 +++ .../webapp/i18n/selection/SelectedLocale.java | 204 ++++++++++++ .../vitro/webapp/startup/StartupStatus.java | 2 +- .../selection/LocaleSelectionSetupTest.java | 314 ++++++++++++++++++ .../webapp/startup/StartupStatusStub.java | 140 ++++++++ webapp/themes/vitro/templates/identity.ftl | 1 + .../WEB-INF/resources/startup_listeners.txt | 3 + webapp/web/WEB-INF/web.xml | 20 ++ .../freemarker/page/partials/identity.ftl | 1 + .../page/partials/languageSelector.ftl | 31 ++ 19 files changed, 1346 insertions(+), 10 deletions(-) rename webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/{VivoResourceBundle.java => VitroResourceBundle.java} (94%) create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionController.java create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionDataGetter.java create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionFilter.java create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionSetup.java create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectorUtilities.java create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/SelectedLocale.java create mode 100644 webapp/test/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionSetupTest.java create mode 100644 webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/startup/StartupStatusStub.java create mode 100644 webapp/web/templates/freemarker/page/partials/languageSelector.ftl diff --git a/doc/install.html b/doc/install.html index 845306e91..1e4064aa9 100644 --- a/doc/install.html +++ b/doc/install.html @@ -651,10 +651,80 @@ - An absolute file path, pointing to the root directory of the Harvester utility. - You must include the final slash. + Force VIVO to use a specific language or Locale instead of those + specified by the browser. + This affects RDF data retrieved from the model, if RDFService.languageFilter is true. + This also affects the text of pages that have been modified to support multiple languages. + + + languages.forceLocale + + + en_US + + + + + + A list of supported languages or Locales that the user may choose to + use instead of the one specified by the browser. Selection images must + be available in the i18n/images directory of the theme. + This affects RDF data retrieved from the model, if RDFService.languageFilter is true. + This also affects the text of pages that have been modified to support multiple languages. + + + + + languages.selectableLocales + + + en, es, fr_FR + + + + + + For developers only. + Defeat the Freemarker template cache, so each template + is read from disk on each request. This permits developers to immediately + see the effect of changes to the template. The default is false, which + means that a cached copy of each template will be used for 60 seconds + before the disk is checked for a new version. +
    Setting this option to "true" slows down VIVO performance. + + + + + developer.defeatFreemarkerCache + + + false + + + + + + For developers only. + Defeat the cache of language-specific text strings, + so the language file is read from disk on each request. + This permits developers to immediately + see the effect of changes to the text strings. + The default is false, which means that the language file is + read when VIVO starts up, or when a new theme is selected. +
    Setting this option to "true" slows down VIVO performance. + + + + + developer.defeatI18nCache = true + + + false + + +

    VI. Compile and deploy

    diff --git a/webapp/config/example.runtime.properties b/webapp/config/example.runtime.properties index 691f98139..d0284a389 100644 --- a/webapp/config/example.runtime.properties +++ b/webapp/config/example.runtime.properties @@ -119,3 +119,46 @@ proxy.eligibleTypeList = http://www.w3.org/2002/07/owl#Thing # header supplied by the browser. Default is true if not set. # RDFService.languageFilter = true + +# +# Force VIVO to use a specific language or Locale instead of those +# specified by the browser. This affects RDF data retrieved from the model, +# if RDFService.languageFilter is true. This also affects the text of pages +# that have been modified to support multiple languages. +# +# languages.forceLocale = en_US + +# +# A list of supported languages or Locales that the user may choose to +# use instead of the one specified by the browser. Selection images must +# be available in the i18n/images directory of the theme. This affects +# RDF data retrieved from the model, if RDFService.languageFilter is true. +# This also affects the text of pages that have been modified to support +# multiple languages. +# +# This should not be used with languages.forceLocale, which will override it. +# +# languages.selectableLocales = en, es, fr + +# +# For developers only: Setting this option to "true" slows down VIVO performance. +# +# Defeat the Freemarker template cache, so each template is read from disk +# on each request. This permits developers to immediately see the effect of +# changes to the template. The default is false, which means +# that a cached copy of each template will be used for 60 seconds before +# the disk is checked for a new version. +# +# developer.defeatFreemarkerCache = true + +# +# For developers only: Setting this option to "true" slows down VIVO performance. +# +# Defeat the cache of language-specific text strings, so the language file +# is read from disk on each request. This permits developers to immediately +# see the effect of changes to the text strings. The default is +# false, which means that the language file is read when +# VIVO starts up, or when a new theme is selected. +# +# developer.defeatI18nCache = true + diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerConfiguration.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerConfiguration.java index 07a370779..6f8f46511 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerConfiguration.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerConfiguration.java @@ -5,7 +5,6 @@ package edu.cornell.mannlib.vitro.webapp.controller.freemarker; import java.io.File; import java.io.IOException; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/I18n.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/I18n.java index 00af47192..5a6e72bd8 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/I18n.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/I18n.java @@ -200,7 +200,7 @@ public class I18n { log.debug("Paths are '" + themeI18nPath + "' and '" + appI18nPath + "'"); - return VivoResourceBundle.getBundle(bundleName, ctx, appI18nPath, + return VitroResourceBundle.getBundle(bundleName, ctx, appI18nPath, themeI18nPath, this); } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/VivoResourceBundle.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/VitroResourceBundle.java similarity index 94% rename from webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/VivoResourceBundle.java rename to webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/VitroResourceBundle.java index ba314ea36..d4c9754f9 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/VivoResourceBundle.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/VitroResourceBundle.java @@ -41,8 +41,8 @@ import org.apache.commons.logging.LogFactory; * * In all_es.properties: account_email_html = @@file accountEmail_es.html */ -public class VivoResourceBundle extends ResourceBundle { - private static final Log log = LogFactory.getLog(VivoResourceBundle.class); +public class VitroResourceBundle extends ResourceBundle { + private static final Log log = LogFactory.getLog(VitroResourceBundle.class); private static final String FILE_FLAG = "@@file "; private static final String MESSAGE_FILE_NOT_FOUND = "File {1} not found for property {0}."; @@ -51,11 +51,11 @@ public class VivoResourceBundle extends ResourceBundle { // Factory method // ---------------------------------------------------------------------- - public static VivoResourceBundle getBundle(String bundleName, + public static VitroResourceBundle getBundle(String bundleName, ServletContext ctx, String appI18nPath, String themeI18nPath, Control control) { try { - return new VivoResourceBundle(bundleName, ctx, appI18nPath, + return new VitroResourceBundle(bundleName, ctx, appI18nPath, themeI18nPath, control); } catch (FileNotFoundException e) { log.debug(e); @@ -78,7 +78,7 @@ public class VivoResourceBundle extends ResourceBundle { private final Properties defaults; private final Properties properties; - private VivoResourceBundle(String bundleName, ServletContext ctx, + private VitroResourceBundle(String bundleName, ServletContext ctx, String appI18nPath, String themeI18nPath, Control control) throws IOException { this.bundleName = bundleName; diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionController.java new file mode 100644 index 000000000..523a263a2 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionController.java @@ -0,0 +1,100 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.i18n.selection; + +import java.io.IOException; +import java.util.List; +import java.util.Locale; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang.LocaleUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.cornell.mannlib.vitro.webapp.beans.DisplayMessage; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder; + +/** + * Call this at /selectLocale&selection=[locale_string] + * + * For example: /selectLocale&selection=en_US or /selectLocale&selection=es + * + * Write an error to the log (and to DisplayMessage) if the selection is not + * syntactically valid. + * + * Write a warning to the log if the selection code is not one of the selectable + * Locales from runtime.properties, or if the selection code is not recognized + * by the system. + * + * Set the new Locale in the Session using SelectedLocale and return to the + * referrer. + */ +public class LocaleSelectionController extends HttpServlet { + private static final Log log = LogFactory + .getLog(LocaleSelectionController.class); + + public static final String PARAMETER_SELECTION = "selection"; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + String referrer = req.getHeader("referer"); + + String selectedLocale = req.getParameter(PARAMETER_SELECTION); + + try { + processSelectedLocale(req, selectedLocale); + } catch (Exception e) { + log.error("Failed to process the user's Locale selection", e); + } + + if (StringUtils.isEmpty(referrer)) { + resp.sendRedirect(UrlBuilder.getHomeUrl()); + } else { + resp.sendRedirect(referrer); + } + } + + private void processSelectedLocale(HttpServletRequest req, + String selectedLocale) { + if (StringUtils.isBlank(selectedLocale)) { + log.debug("No '" + PARAMETER_SELECTION + "' parameter"); + return; + } + + Locale locale = null; + + try { + locale = LocaleUtils.toLocale(selectedLocale.trim()); + log.debug("Locale selection is " + locale); + } catch (IllegalArgumentException e) { + log.error("Failed to convert the selection to a Locale", e); + DisplayMessage.setMessage(req, + "There was a problem in the system. " + + "Your language choice was rejected."); + return; + } + + List selectables = SelectedLocale.getSelectableLocales(req); + if (!selectables.contains(locale)) { + log.warn("User selected a locale '" + locale + + "' that was not in the list: " + selectables); + } else if (!LocaleUtils.isAvailableLocale(locale)) { + log.warn("User selected an unrecognized locale: '" + locale + "'"); + } + + SelectedLocale.setSelectedLocale(req, locale); + log.debug("Setting selected locale to " + locale); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + doGet(req, resp); + } +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionDataGetter.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionDataGetter.java new file mode 100644 index 000000000..627ad04ca --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionDataGetter.java @@ -0,0 +1,95 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.i18n.selection; + +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder; +import edu.cornell.mannlib.vitro.webapp.utils.dataGetter.DataGetter; + +/** + * Get the data for the selectable Locales, so the Freemarker template can + * create a row of flag images that will select the desired locale. + * + * If there are no selectable Locales in runtime.properties, we return an empty + * map. (selectLocale?? will return false) + * + * If the Locale has been forced by runtime.properties, we do the same. + * + * If there are selectable Locales, the returned map will contain a structure + * like this: + * + *
    + * {selectLocale={
    + *   selectLocaleUrl = [the URL for the form action to select a Locale]
    + *   locales={         [a list of maps]
    + *       {               [a map for each Locale]
    + *         code =          [the code for the Locale, e.g. "en_US"]
    + *         label =         [the alt text for the Locale, e.g. "Spanish (Spain)"]
    + *         imageUrl =      [the URL of the image that represents the Locale]
    + *       }
    + *     }  
    + *   }
    + * }
    + * 
    + */ +public class LocaleSelectionDataGetter implements DataGetter { + private static final Log log = LogFactory + .getLog(LocaleSelectionDataGetter.class); + + private final VitroRequest vreq; + + public LocaleSelectionDataGetter(VitroRequest vreq) { + this.vreq = vreq; + } + + @Override + public Map getData(Map valueMap) { + List selectables = SelectedLocale.getSelectableLocales(vreq); + if (selectables.isEmpty()) { + return Collections.emptyMap(); + } + + Map result = new HashMap(); + result.put("selectLocaleUrl", UrlBuilder.getUrl("/selectLocale")); + result.put("locales", buildLocalesList(selectables)); + + Map bodyMap = new HashMap(); + bodyMap.put("selectLocale", result); + log.debug("Sending these values: " + bodyMap); + return bodyMap; + } + + private List> buildLocalesList(List selectables) { + Locale currentLocale = SelectedLocale.getCurrentLocale(vreq); + List> list = new ArrayList>(); + for (Locale locale : selectables) { + try { + list.add(buildLocaleMap(locale, currentLocale)); + } catch (FileNotFoundException e) { + log.warn("Can't show the Locale selector for '" + locale + + "': " + e); + } + } + return list; + } + + private Map buildLocaleMap(Locale locale, + Locale currentLocale) throws FileNotFoundException { + Map map = new HashMap(); + map.put("code", locale.toString()); + map.put("label", locale.getDisplayName(currentLocale)); + map.put("imageUrl", LocaleSelectorUtilities.getImageUrl(vreq, locale)); + return map; + } +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionFilter.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionFilter.java new file mode 100644 index 000000000..377638977 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionFilter.java @@ -0,0 +1,113 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.i18n.selection; + +import java.io.IOException; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.Locale; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; + +import org.apache.commons.collections.EnumerationUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Check for a Locale in the ServletContext or the Session that should override + * the Locale in the ServletRequest. + * + * If there is such a Locale, wrap the ServletRequest so it behaves as if that + * is the preferred Locale. + * + * Otherwise, just process the request as usual. + */ +public class LocaleSelectionFilter implements Filter { + private static final Log log = LogFactory + .getLog(LocaleSelectionFilter.class); + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + // Nothing to do at startup. + } + + @Override + public void destroy() { + // Nothing to do at shutdown. + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + if (request instanceof HttpServletRequest) { + HttpServletRequest hreq = (HttpServletRequest) request; + + Locale overridingLocale = SelectedLocale.getOverridingLocale(hreq); + log.debug("overriding Locale is " + overridingLocale); + + if (overridingLocale != null) { + request = new LocaleSelectionRequestWrapper(hreq, + overridingLocale); + } + } else { + log.debug("Not an HttpServletRequest."); + } + chain.doFilter(request, response); + } + + // ---------------------------------------------------------------------- + // Helper classes + // ---------------------------------------------------------------------- + + /** + * Uses the selected Locale as the preferred Locale of the request. + */ + private static class LocaleSelectionRequestWrapper extends + HttpServletRequestWrapper { + private final HttpServletRequest request; + private final Locale selectedLocale; + + public LocaleSelectionRequestWrapper(HttpServletRequest request, + Locale selectedLocale) { + super(request); + + if (request == null) { + throw new NullPointerException("request may not be null."); + } + this.request = request; + + if (selectedLocale == null) { + throw new NullPointerException( + "selectedLocale may not be null."); + } + this.selectedLocale = selectedLocale; + } + + @Override + public Locale getLocale() { + return selectedLocale; + } + + /** + * Put the selected Locale on the front of the list of acceptable + * Locales. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public Enumeration getLocales() { + List list = EnumerationUtils.toList(request.getLocales()); + list.remove(selectedLocale); + list.add(0, selectedLocale); + return Collections.enumeration(list); + } + } + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionSetup.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionSetup.java new file mode 100644 index 000000000..c89819421 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionSetup.java @@ -0,0 +1,148 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.i18n.selection; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import javax.servlet.ServletContext; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +import org.apache.commons.lang.LocaleUtils; +import org.apache.commons.lang.StringUtils; + +import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; +import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus; + +/** + * Check the ConfigurationProperties for a forced locale, or for a + * comma-separate list of selectable locales. + * + * Create the appropriate Locale objects and store them in the ServletContext. + */ +public class LocaleSelectionSetup implements ServletContextListener { + /** + * If this is set, the locale is forced. No selection will be offered to the + * user, and browser locales will be ignored. + */ + public static final String PROPERTY_FORCE_LOCALE = "languages.forceLocale"; + + /** + * This is the list of locales that the user may select. There should be a + * national flag or symbol available for each supported locale. + */ + public static final String PROPERTY_SELECTABLE_LOCALES = "languages.selectableLocales"; + + private ServletContext ctx; + private StartupStatus ss; + private ConfigurationProperties props; + + private String forceString; + private String selectableString; + + @Override + public void contextInitialized(ServletContextEvent sce) { + ctx = sce.getServletContext(); + ss = StartupStatus.getBean(ctx); + props = ConfigurationProperties.getBean(sce); + + readProperties(); + + if (isForcing() && hasSelectables()) { + warnAboutOverride(); + } + + if (isForcing()) { + forceLocale(); + } else if (hasSelectables()) { + setUpSelections(); + } else { + reportNoLocales(); + } + } + + private void readProperties() { + forceString = props.getProperty(PROPERTY_FORCE_LOCALE, ""); + selectableString = props.getProperty(PROPERTY_SELECTABLE_LOCALES, ""); + } + + private boolean isForcing() { + return StringUtils.isNotBlank(forceString); + } + + private boolean hasSelectables() { + return StringUtils.isNotBlank(selectableString); + } + + private void warnAboutOverride() { + ss.warning(this, "'" + PROPERTY_FORCE_LOCALE + "' will override '" + + PROPERTY_SELECTABLE_LOCALES + "'."); + } + + private void forceLocale() { + try { + Locale forceLocale = buildLocale(forceString); + SelectedLocale.setForcedLocale(ctx, forceLocale); + ssInfo("Setting forced locale to '" + forceLocale + "'."); + } catch (IllegalArgumentException e) { + ssWarning("Problem in '" + PROPERTY_FORCE_LOCALE + "': " + + e.getMessage()); + } + } + + private void setUpSelections() { + List locales = new ArrayList(); + for (String string : splitSelectables()) { + try { + locales.add(buildLocale(string)); + } catch (IllegalArgumentException e) { + ssWarning("Problem in '" + PROPERTY_SELECTABLE_LOCALES + "': " + + e.getMessage()); + } + } + + SelectedLocale.setSelectableLocales(ctx, locales); + ssInfo("Setting selectable locales to '" + locales + "'."); + } + + private String[] splitSelectables() { + return selectableString.split("\\s*,\\s*"); + } + + private void reportNoLocales() { + ssInfo("There is no Locale information."); + } + + private void ssInfo(String message) { + ss.info(this, message + showPropertyValues()); + } + + private void ssWarning(String message) { + ss.warning(this, message + showPropertyValues()); + } + + private String showPropertyValues() { + return " In runtime.properties, '" + PROPERTY_FORCE_LOCALE + + "' is set to '" + forceString + "', '" + + PROPERTY_SELECTABLE_LOCALES + "' is set to '" + + selectableString + "'"; + } + + private Locale buildLocale(String localeString) + throws IllegalArgumentException { + Locale locale = LocaleUtils.toLocale(localeString); + + if (!LocaleUtils.isAvailableLocale(locale)) { + ssWarning("'" + locale + "' is not a recognized locale."); + } + return locale; + } + + @Override + public void contextDestroyed(ServletContextEvent arg0) { + // Nothing to do at shutdown. + } + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectorUtilities.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectorUtilities.java new file mode 100644 index 000000000..0e5eeb492 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectorUtilities.java @@ -0,0 +1,54 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.i18n.selection; + +import java.io.FileNotFoundException; +import java.util.Locale; + +import javax.servlet.ServletContext; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; + +/** + * Some static methods for the GUI aspects of selecting a Locale. + */ +public class LocaleSelectorUtilities { + private static final Log log = LogFactory + .getLog(LocaleSelectorUtilities.class); + + /** + * Look in the current theme directory to find a selection image for this + * Locale. + * + * Images are expected at a resource path like + * /[themeDir]/i18n/images/select_locale_[locale_code].* + * + * For example, /themes/wilma/i18n/images/select_locale_en.png + * /themes/wilma/i18n/images/select_locale_en.JPEG + * /themes/wilma/i18n/images/select_locale_en.gif + * + * To create a proper URL, prepend the context path. + */ + public static String getImageUrl(VitroRequest vreq, Locale locale) + throws FileNotFoundException { + String filename = "select_locale_" + locale + "."; + + String themeDir = vreq.getAppBean().getThemeDir(); + String imageDirPath = "/" + themeDir + "i18n/images/"; + + ServletContext ctx = vreq.getSession().getServletContext(); + for (Object o : ctx.getResourcePaths(imageDirPath)) { + String resourcePath = (String) o; + if (resourcePath.contains(filename)) { + String fullPath = vreq.getContextPath() + resourcePath; + log.debug("Found image for " + locale + " at '" + fullPath + + "'"); + return fullPath; + } + } + throw new FileNotFoundException("Can't find an image for " + locale); + } +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/SelectedLocale.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/SelectedLocale.java new file mode 100644 index 000000000..7ae4586cb --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/i18n/selection/SelectedLocale.java @@ -0,0 +1,204 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.i18n.selection; + +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * A utility class for storing and retrieving Locale information. + * + * The static methods create beans and store them in the ServletContext or the + * session, where the information can be found later. + */ +public abstract class SelectedLocale { + private static final Log log = LogFactory.getLog(SelectedLocale.class); + + /** Use this attribute on both the ServletContext and the Session. */ + protected static final String ATTRIBUTE_NAME = "SELECTED_LOCALE"; + + /** + * Store the forced locale in the servlet context. Clear any selectable + * Locales. + */ + public static void setForcedLocale(ServletContext ctx, Locale forcedLocale) { + log.debug("Set forced locale: " + forcedLocale); + ctx.setAttribute(ATTRIBUTE_NAME, + new ContextSelectedLocale(forcedLocale)); + } + + /** + * Store the selected locale in the current session. + */ + public static void setSelectedLocale(HttpServletRequest req, + Locale selectedLocale) { + log.debug("Set selected locale: " + selectedLocale); + req.getSession().setAttribute(ATTRIBUTE_NAME, + new SessionSelectedLocale(selectedLocale)); + } + + /** + * Do we need to override the Locale in the current request? return the + * first of these to be found: + *
      + *
    • The forced Locale in the servlet context
    • + *
    • The selected Locale in the session
    • + *
    • null
    • + *
    + */ + public static Locale getOverridingLocale(HttpServletRequest req) { + HttpSession session = req.getSession(); + ServletContext ctx = session.getServletContext(); + + Object ctxInfo = ctx.getAttribute(ATTRIBUTE_NAME); + if (ctxInfo instanceof ContextSelectedLocale) { + Locale forcedLocale = ((ContextSelectedLocale) ctxInfo) + .getForcedLocale(); + if (forcedLocale != null) { + log.debug("Found forced locale in the context: " + forcedLocale); + return forcedLocale; + } + } + + Object sessionInfo = session.getAttribute(ATTRIBUTE_NAME); + if (sessionInfo instanceof SessionSelectedLocale) { + Locale selectedLocale = ((SessionSelectedLocale) sessionInfo) + .getSelectedLocale(); + if (selectedLocale != null) { + log.debug("Found selected locale in the session: " + + selectedLocale); + return selectedLocale; + } + } + + return null; + } + + /** + * Get the current Locale to use, which is the first of these to be found: + *
      + *
    • The forced Locale in the servlet context
    • + *
    • The selected Locale in the session
    • + *
    • The Locale from the request
    • + *
    • The default Locale for the JVM
    • + *
    + */ + public static Locale getCurrentLocale(HttpServletRequest req) { + Locale overridingLocale = getOverridingLocale(req); + + if (overridingLocale != null) { + return overridingLocale; + } + + Locale requestLocale = req.getLocale(); + if (requestLocale != null) { + log.debug("Found locale in the request: " + requestLocale); + return requestLocale; + } + + log.debug("Using default locale: " + Locale.getDefault()); + return Locale.getDefault(); + } + + /** + * Store a list of selectable Locales in the servlet context, so we can + * easily build the selection panel in the GUI. Clears any forced locale. + */ + public static void setSelectableLocales(ServletContext ctx, + List selectableLocales) { + log.debug("Setting selectable locales: " + selectableLocales); + ctx.setAttribute(ATTRIBUTE_NAME, new ContextSelectedLocale( + selectableLocales)); + } + + /** + * Get the list of selectable Locales from the servlet context. May return + * an empty list, but never returns null. + */ + public static List getSelectableLocales(HttpServletRequest req) { + ServletContext ctx = req.getSession().getServletContext(); + Object ctxInfo = ctx.getAttribute(ATTRIBUTE_NAME); + if (ctxInfo instanceof ContextSelectedLocale) { + List selectableLocales = ((ContextSelectedLocale) ctxInfo) + .getSelectableLocales(); + if (selectableLocales != null) { + log.debug("Returning selectable locales: " + selectableLocales); + return selectableLocales; + } + } + + log.debug("No selectable locales were found. Returning an empty list."); + return Collections.emptyList(); + } + + // ---------------------------------------------------------------------- + // Bean classes + // ---------------------------------------------------------------------- + + /** Holds Locale information in the ServletContext. */ + protected static class ContextSelectedLocale { + // Only one of these is populated. + private final Locale forcedLocale; + private final List selectableLocales; + + public ContextSelectedLocale(Locale forcedLocale) { + if (forcedLocale == null) { + throw new NullPointerException("forcedLocale may not be null."); + } + + this.forcedLocale = forcedLocale; + this.selectableLocales = Collections.emptyList(); + } + + public ContextSelectedLocale(List selectableLocales) { + if (selectableLocales == null) { + selectableLocales = Collections.emptyList(); + } + + this.forcedLocale = null; + this.selectableLocales = selectableLocales; + } + + public Locale getForcedLocale() { + return forcedLocale; + } + + public List getSelectableLocales() { + return selectableLocales; + } + + @Override + public String toString() { + return "ContextSelectedLocale[forced=" + forcedLocale + + ", selectable=" + selectableLocales + "]"; + } + + } + + /** Holds Locale information in the Session. */ + protected static class SessionSelectedLocale { + private final Locale selectedLocale; + + public SessionSelectedLocale(Locale selectedLocale) { + this.selectedLocale = selectedLocale; + } + + public Locale getSelectedLocale() { + return selectedLocale; + } + + @Override + public String toString() { + return "SessionSelectedLocale[" + selectedLocale + "]"; + } + + } +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/startup/StartupStatus.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/startup/StartupStatus.java index 0a0601910..6c3e4a3df 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/startup/StartupStatus.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/startup/StartupStatus.java @@ -23,7 +23,7 @@ import org.apache.commons.logging.LogFactory; public class StartupStatus { private static final Log log = LogFactory.getLog(StartupStatus.class); - private static final String ATTRIBUTE_NAME = "STARTUP_STATUS"; + protected static final String ATTRIBUTE_NAME = "STARTUP_STATUS"; // ---------------------------------------------------------------------- // static methods diff --git a/webapp/test/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionSetupTest.java b/webapp/test/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionSetupTest.java new file mode 100644 index 000000000..3709d8a43 --- /dev/null +++ b/webapp/test/edu/cornell/mannlib/vitro/webapp/i18n/selection/LocaleSelectionSetupTest.java @@ -0,0 +1,314 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.i18n.selection; + +import static edu.cornell.mannlib.vitro.webapp.i18n.selection.LocaleSelectionSetup.PROPERTY_FORCE_LOCALE; +import static edu.cornell.mannlib.vitro.webapp.i18n.selection.LocaleSelectionSetup.PROPERTY_SELECTABLE_LOCALES; +import static edu.cornell.mannlib.vitro.webapp.i18n.selection.SelectedLocale.ATTRIBUTE_NAME; +import static org.junit.Assert.fail; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +import javax.servlet.ServletContextEvent; + +import org.apache.commons.lang.LocaleUtils; +import org.apache.commons.lang.ObjectUtils; +import org.apache.log4j.Level; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import stubs.edu.cornell.mannlib.vitro.webapp.config.ConfigurationPropertiesStub; +import stubs.edu.cornell.mannlib.vitro.webapp.startup.StartupStatusStub; +import stubs.javax.servlet.ServletContextStub; +import edu.cornell.mannlib.vitro.testing.AbstractTestClass; +import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; +import edu.cornell.mannlib.vitro.webapp.i18n.selection.SelectedLocale.ContextSelectedLocale; + +/** + * TODO + */ +public class LocaleSelectionSetupTest extends AbstractTestClass { + // ---------------------------------------------------------------------- + // Infrastructure + // ---------------------------------------------------------------------- + + private LocaleSelectionSetup lss; + private ServletContextStub ctx; + private ServletContextEvent sce; + private ConfigurationPropertiesStub props; + private StartupStatusStub ss; + + private int[] expectedMessageCounts; + private Locale expectedForcedLocale; + private List expectedSelectableLocales; + + @Before + public void setup() { +// setLoggerLevel(LocaleSelectionSetup.class, Level.DEBUG); +// setLoggerLevel(StartupStatusStub.class, Level.DEBUG); + setLoggerLevel(ConfigurationProperties.class, Level.WARN); + + ctx = new ServletContextStub(); + sce = new ServletContextEvent(ctx); + + props = new ConfigurationPropertiesStub(); + props.setBean(ctx); + + ss = new StartupStatusStub(ctx); + + lss = new LocaleSelectionSetup(); + } + + @After + public void checkExpectations() { + if (expectedMessageCounts == null) { + fail("expecteMessages() was not called"); + } + + String message = compareMessageCount("info", ss.getInfoCount(), + expectedMessageCounts[0]) + + compareMessageCount("warning", ss.getWarningCount(), + expectedMessageCounts[1]) + + compareMessageCount("fatal", ss.getFatalCount(), + expectedMessageCounts[2]) + + checkForced() + + checkSelectable(); + if (!message.isEmpty()) { + fail(message); + } + } + + private String compareMessageCount(String label, int actual, int expected) { + if (expected == actual) { + return ""; + } else { + return "expecting " + expected + " " + label + + " messages, but received " + actual + "; "; + } + } + + private String checkForced() { + Locale actual = null; + Object o = ctx.getAttribute(ATTRIBUTE_NAME); + if (o instanceof ContextSelectedLocale) { + actual = ((ContextSelectedLocale) o).getForcedLocale(); + } + + Locale expected = expectedForcedLocale; + if (ObjectUtils.equals(expected, actual)) { + return ""; + } else { + return "expected forced locale of " + expectedForcedLocale + + ", but was " + actual + "; "; + } + } + + private String checkSelectable() { + List actual = Collections.emptyList(); + Object o = ctx.getAttribute(ATTRIBUTE_NAME); + if (o instanceof ContextSelectedLocale) { + actual = ((ContextSelectedLocale) o).getSelectableLocales(); + } + + List expected = expectedSelectableLocales; + if (expected == null) { + expected = Collections.emptyList(); + } + + if (ObjectUtils.equals(expected, actual)) { + return ""; + } else { + return "expected selectable locales of " + expected + ", but was " + + actual + "; "; + } + } + + // ---------------------------------------------------------------------- + // The tests + // ---------------------------------------------------------------------- + + // General functionality + + @Test + public void neitherPropertyIsSpecified() { + lss.contextInitialized(sce); + expectMessages(1, 0, 0); + } + + @Test + public void forceSuccessL() { + props.setProperty(PROPERTY_FORCE_LOCALE, "es"); + lss.contextInitialized(sce); + expectForced("es"); + expectMessages(1, 0, 0); + } + + @Test + public void forceSuccessL_C() { + props.setProperty(PROPERTY_FORCE_LOCALE, "es_ES"); + lss.contextInitialized(sce); + expectForced("es_ES"); + expectMessages(1, 0, 0); + } + + @Test + public void forceSuccessL_C_V() { + props.setProperty(PROPERTY_FORCE_LOCALE, "no_NO_NY"); + lss.contextInitialized(sce); + expectForced("no_NO_NY"); + expectMessages(1, 0, 0); + } + + @Test + public void oneSelectable() { + props.setProperty(PROPERTY_SELECTABLE_LOCALES, "fr_FR"); + lss.contextInitialized(sce); + expectSelectable("fr_FR"); + expectMessages(1, 0, 0); + } + + @Test + public void twoSelectables() { + props.setProperty(PROPERTY_SELECTABLE_LOCALES, "fr_FR, es_PE"); + lss.contextInitialized(sce); + expectSelectable("fr_FR", "es_PE"); + expectMessages(1, 0, 0); + } + + @Test + public void bothPropertiesAreSpecified() { + props.setProperty(PROPERTY_FORCE_LOCALE, "es_ES"); + props.setProperty(PROPERTY_SELECTABLE_LOCALES, "fr_FR"); + lss.contextInitialized(sce); + expectForced("es_ES"); + expectMessages(1, 1, 0); + } + + // Locale string syntax (common to both force and selectable) + + @Test + public void langaugeIsEmpty() { + props.setProperty(PROPERTY_FORCE_LOCALE, "_ES"); + lss.contextInitialized(sce); + expectMessages(0, 1, 0); + } + + @Test + public void languageWrongLength() { + props.setProperty(PROPERTY_FORCE_LOCALE, "e_ES"); + lss.contextInitialized(sce); + expectMessages(0, 1, 0); + } + + @Test + public void languageNotAlphabetic() { + props.setProperty(PROPERTY_FORCE_LOCALE, "e4_ES"); + lss.contextInitialized(sce); + expectMessages(0, 1, 0); + } + + @Test + public void languageNotLowerCase() { + props.setProperty(PROPERTY_FORCE_LOCALE, "eS_ES"); + lss.contextInitialized(sce); + expectMessages(0, 1, 0); + } + + @Test + public void countryIsEmpty() { + props.setProperty(PROPERTY_FORCE_LOCALE, "es_ _13"); + lss.contextInitialized(sce); + expectMessages(0, 1, 0); + } + + @Test + public void countryWrongLength() { + props.setProperty(PROPERTY_FORCE_LOCALE, "es_ESS"); + lss.contextInitialized(sce); + expectMessages(0, 1, 0); + } + + @Test + public void countryNotAlphabetic() { + props.setProperty(PROPERTY_FORCE_LOCALE, "es_E@"); + lss.contextInitialized(sce); + expectMessages(0, 1, 0); + } + + @Test + public void countryNotUpperCase() { + props.setProperty(PROPERTY_FORCE_LOCALE, "es_es"); + lss.contextInitialized(sce); + expectMessages(0, 1, 0); + } + + @Test + public void variantIsEmpty() { + props.setProperty(PROPERTY_FORCE_LOCALE, "es_ES_"); + lss.contextInitialized(sce); + expectMessages(0, 1, 0); + } + + @Test + public void funkyVariantIsAcceptable() { + props.setProperty(PROPERTY_FORCE_LOCALE, "es_ES_123_aa"); + lss.contextInitialized(sce); + expectForced("es_ES_123_aa"); + expectMessages(1, 1, 0); + } + + @Test + public void localeNotRecognizedProducesWarning() { + props.setProperty(PROPERTY_FORCE_LOCALE, "es_FR"); + lss.contextInitialized(sce); + expectForced("es_FR"); + expectMessages(1, 1, 0); + } + + // Syntax of selectable property + + @Test + public void emptySelectableLocaleProducesWarning() { + props.setProperty(PROPERTY_SELECTABLE_LOCALES, "es_ES, , fr_FR"); + lss.contextInitialized(sce); + expectSelectable("es_ES", "fr_FR"); + expectMessages(1, 1, 0); + } + + @Test + public void blanksAroundCommasAreIgnored() { + props.setProperty(PROPERTY_SELECTABLE_LOCALES, "es_ES,en_US \t , fr_FR"); + lss.contextInitialized(sce); + expectSelectable("es_ES", "en_US", "fr_FR"); + expectMessages(1, 0, 0); + } + + // ---------------------------------------------------------------------- + // helper methods + // ---------------------------------------------------------------------- + + private void expectMessages(int infoCount, int warningCount, int fatalCount) { + this.expectedMessageCounts = new int[] { infoCount, warningCount, + fatalCount }; + } + + private void expectForced(String localeString) { + this.expectedForcedLocale = stringToLocale(localeString); + } + + private void expectSelectable(String... strings) { + List list = new ArrayList(); + for (String string : strings) { + list.add(stringToLocale(string)); + } + this.expectedSelectableLocales = list; + } + + private Locale stringToLocale(String string) { + return LocaleUtils.toLocale(string); + } +} diff --git a/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/startup/StartupStatusStub.java b/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/startup/StartupStatusStub.java new file mode 100644 index 000000000..b7e068507 --- /dev/null +++ b/webapp/test/stubs/edu/cornell/mannlib/vitro/webapp/startup/StartupStatusStub.java @@ -0,0 +1,140 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package stubs.edu.cornell.mannlib.vitro.webapp.startup; + +import java.util.List; + +import javax.servlet.ServletContext; +import javax.servlet.ServletContextListener; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus; + +/** + * Keep track of how many messages come in. + */ +public class StartupStatusStub extends StartupStatus { + private static final Log log = LogFactory.getLog(StartupStatusStub.class); + + // ---------------------------------------------------------------------- + // Stub infrastructure + // ---------------------------------------------------------------------- + + private int infoCount = 0; + private int warningCount = 0; + private int fatalCount = 0; + + public StartupStatusStub(ServletContext ctx) { + ctx.setAttribute(ATTRIBUTE_NAME, this); + } + + public int getInfoCount() { + return infoCount; + } + + public int getWarningCount() { + return warningCount; + } + + public int getFatalCount() { + return fatalCount; + } + + // ---------------------------------------------------------------------- + // Stub methods + // ---------------------------------------------------------------------- + + @Override + public void info(ServletContextListener listener, String message) { + log.debug("INFO: " + message); + infoCount++; + } + + @Override + public void info(ServletContextListener listener, String message, + Throwable cause) { + log.debug("INFO: " + message + " " + cause); + infoCount++; + } + + @Override + public void warning(ServletContextListener listener, String message) { + log.debug("WARNING: " + message); + warningCount++; + } + + @Override + public void warning(ServletContextListener listener, String message, + Throwable cause) { + log.debug("WARNING: " + message + " " + cause); + warningCount++; + } + + @Override + public void fatal(ServletContextListener listener, String message) { + log.debug("FATAL: " + message); + fatalCount++; + } + + @Override + public void fatal(ServletContextListener listener, String message, + Throwable cause) { + log.debug("FATAL: " + message + " " + cause); + fatalCount++; + } + + // ---------------------------------------------------------------------- + // Un-implemented methods + // ---------------------------------------------------------------------- + + @Override + public void listenerNotExecuted(ServletContextListener listener) { + throw new RuntimeException( + "StartupStatusStub.listenerNotExecuted() not implemented."); + } + + @Override + public void listenerExecuted(ServletContextListener listener) { + throw new RuntimeException( + "StartupStatusStub.listenerExecuted() not implemented."); + } + + @Override + public boolean allClear() { + throw new RuntimeException( + "StartupStatusStub.allClear() not implemented."); + } + + @Override + public boolean isStartupAborted() { + throw new RuntimeException( + "StartupStatusStub.isStartupAborted() not implemented."); + } + + @Override + public List getStatusItems() { + throw new RuntimeException( + "StartupStatusStub.getStatusItems() not implemented."); + } + + @Override + public List getErrorItems() { + throw new RuntimeException( + "StartupStatusStub.getErrorItems() not implemented."); + } + + @Override + public List getWarningItems() { + throw new RuntimeException( + "StartupStatusStub.getWarningItems() not implemented."); + } + + @Override + public List getItemsForListener(ServletContextListener listener) { + throw new RuntimeException( + "StartupStatusStub.getItemsForListener() not implemented."); + } + +} diff --git a/webapp/themes/vitro/templates/identity.ftl b/webapp/themes/vitro/templates/identity.ftl index f102bd740..127c66fc6 100644 --- a/webapp/themes/vitro/templates/identity.ftl +++ b/webapp/themes/vitro/templates/identity.ftl @@ -9,6 +9,7 @@