/*==========================================================================*\
| $Id: FeatureProvider.java,v 1.9 2011/04/25 19:08:23 stedwar2 Exp $
|*-------------------------------------------------------------------------*|
| Copyright (C) 2006-2008 Virginia Tech
|
| This file is part of Web-CAT.
|
| Web-CAT is free software; you can redistribute it and/or modify
| it under the terms of the GNU Affero General Public License as published
| by the Free Software Foundation; either version 3 of the License, or
| (at your option) any later version.
|
| Web-CAT is distributed in the hope that it will be useful,
| but WITHOUT ANY WARRANTY; without even the implied warranty of
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
| GNU General Public License for more details.
|
| You should have received a copy of the GNU Affero General Public License
| along with Web-CAT; if not, see <http://www.gnu.org/licenses/>.
\*==========================================================================*/
package net.sf.webcat;
import java.io.*;
import java.net.URL;
import java.util.*;
// -------------------------------------------------------------------------
/**
* Represents the provider of a subsystem, including the provider's web
* site for obtaining dynamic updates.
*
* @author stedwar2
* @author Last changed by $Author: stedwar2 $
* @version $Revision: 1.9 $, $Date: 2011/04/25 19:08:23 $
*/
public class FeatureProvider
{
//~ Constructors ..........................................................
// ----------------------------------------------------------
/**
* Creates a new subsystem provider. This constructor is private, since
* other classes should use the factory method
* {@link #getProvider(String)}.
* @param url the URL identifying this provider, which should point to
* an update.properties file
*/
private FeatureProvider( URL url, InputStream stream )
{
this.url = url;
subsystems = new HashMap<String, FeatureDescriptor>();
renamedSubsystems = new HashMap<String, String>();
plugins = new HashMap<String, FeatureDescriptor>();
renamedPlugins = new HashMap<String, String>();
loadProperties( stream );
}
// ----------------------------------------------------------
/**
* Get the subsystem provider associated with the specified URL.
* @param url the URL for the provider
* @return the subsystem provider, or null if none exists
* @throws IOException
*/
public static FeatureProvider getProvider( String url ) throws IOException
{
FeatureProvider provider = null;
URL realURL = baseURLFor( url );
if ( realURL != null )
{
provider = providerRegistry.get( realURL );
if ( provider == null )
{
InputStream stream =
new URL( realURL, UPDATE_PROPERTIES_FILE ).openStream();
provider = new FeatureProvider( realURL, stream );
stream.close();
if ( provider != null )
{
providerRegistry.put( realURL, provider );
}
}
}
return provider;
}
// ----------------------------------------------------------
/**
* Get the collection of all registered providers.
* @return the collection of providers
*/
public static Collection<FeatureProvider> providers()
{
return providerRegistry.values();
}
// ----------------------------------------------------------
/**
* Gets the current version of a feature provider and clears the old one from
* the registry.
*/
public static void refreshProviderRegistry()
{
for(URL realURL : providerRegistry.keySet())
{
if ( realURL != null )
{
FeatureProvider provider = null;
provider = providerRegistry.get( realURL );
if ( provider == null )
{
try
{
InputStream stream = new URL( realURL, UPDATE_PROPERTIES_FILE ).openStream();
provider = new FeatureProvider( realURL, stream );
stream.close();
}
catch (IOException e)
{
provider = null;
}
if ( provider != null )
{
providerRegistry.put( realURL, provider );
}
}
}
}
}
//~ Public Methods ........................................................
// ----------------------------------------------------------
/**
* Get this provider's name, e.g. "Virginia Tech Computer Science".
* If the provider does not define a name in its update.properties file,
* then the URL will be used as the provider's name instead.
* @return the provider's name as a string
*/
public String name()
{
return name;
}
// ----------------------------------------------------------
/**
* Get this provider's base URL.
* @return the base URL
*/
public URL url()
{
return url;
}
// ----------------------------------------------------------
/**
* Get the descriptor for a feature provided by this provider.
* @param featureName the name of the subsystem or plug-in
* @return the provider's descriptor for the given feature, or null
* if there is none
*/
public FeatureDescriptor descriptor( String featureName )
{
FeatureDescriptor result = subsystemDescriptor( featureName );
if ( result == null )
{
result = pluginDescriptor( featureName );
}
return result;
}
// ----------------------------------------------------------
/**
* Get the descriptor for a subsystem provided by this provider.
* @param subsystemName the name of the subsystem
* @return the provider's descriptor for the given subsystem, or null
* if there is none
*/
public FeatureDescriptor subsystemDescriptor( String subsystemName )
{
String newName = renamedSubsystems.get( subsystemName );
if ( newName != null )
{
subsystemName = newName;
}
return subsystems.get( subsystemName );
}
// ----------------------------------------------------------
/**
* Get the descriptor for a subsystem provided by this provider.
* @param pluginName the name of the subsystem
* @return the provider's descriptor for the given subsystem, or null
* if there is none
*/
public FeatureDescriptor pluginDescriptor( String pluginName )
{
String newName = renamedPlugins.get( pluginName );
if ( newName != null )
{
pluginName = newName;
}
return plugins.get( pluginName );
}
// ----------------------------------------------------------
/**
* Get the collection of subsystems provided by this provider.
* @return the provided subsystems as a collection of descriptors
*/
public Collection<FeatureDescriptor> subsystems()
{
return subsystems.values();
}
// ----------------------------------------------------------
/**
* Get the collection of plugins provided by this provider.
* @return the provided plugins as a collection of descriptors
*/
public Collection<FeatureDescriptor> plugins()
{
return plugins.values();
}
// ----------------------------------------------------------
/**
* Reload this provider's update.properties file and refresh the
* set of features it provides.
*/
public void refresh()
{
subsystems = new HashMap<String, FeatureDescriptor>();
plugins = new HashMap<String, FeatureDescriptor>();
try
{
InputStream stream =
new URL( url, UPDATE_PROPERTIES_FILE ).openStream();
loadProperties( stream );
stream.close();
}
catch ( IOException e )
{
WCUpdater.logError(getClass(), "exception reading from provider at " + url, e );
}
}
//~ Private Methods .......................................................
// ----------------------------------------------------------
/**
* Attempt to load the properties settings for this provider.
*/
private void loadProperties( InputStream stream )
{
properties = new Properties();
try
{
properties.load( stream );
}
catch ( IOException e )
{
WCUpdater.logError(getClass(), "exception reading from provider at " + url, e );
}
name = properties.getProperty( "provider.name", url.toString() );
for ( Enumeration<?> e = properties.keys(); e.hasMoreElements(); )
{
String key = (String)e.nextElement();
if ( key.startsWith( FeatureDescriptor.SUBSYSTEM_NAME_PREFIX )
&& key.indexOf( '.',
FeatureDescriptor.SUBSYSTEM_NAME_PREFIX.length() ) == -1 )
{
String originalName = key.substring(
FeatureDescriptor.SUBSYSTEM_NAME_PREFIX.length() );
String subsystemName = originalName;
String newName = subsystemName;
while ( newName != null )
{
subsystemName = newName;
newName = properties.getProperty(
subsystemName + FeatureDescriptor.RENAME_SUFFIX );
}
if ( !originalName.equals( subsystemName ) )
{
renamedSubsystems.put( originalName, subsystemName );
}
else if ( subsystems.get( subsystemName ) == null )
{
subsystems.put(
subsystemName,
new FeatureDescriptor(
subsystemName, properties, false ) );
}
}
else if ( key.startsWith( FeatureDescriptor.PLUGIN_NAME_PREFIX )
&& key.indexOf( '.',
FeatureDescriptor.PLUGIN_NAME_PREFIX.length() ) == -1 )
{
String originalName = key.substring(
FeatureDescriptor.PLUGIN_NAME_PREFIX.length() );
String pluginName = originalName;
String newName = pluginName;
while ( newName != null )
{
pluginName = newName;
newName = properties.getProperty(
pluginName + FeatureDescriptor.RENAME_SUFFIX );
}
if ( !originalName.equals( pluginName ) )
{
renamedPlugins.put( originalName, pluginName );
}
else if ( plugins.get( pluginName ) == null )
{
plugins.put(
pluginName,
new FeatureDescriptor(
pluginName, properties, true ) );
}
}
}
}
// ----------------------------------------------------------
/**
* Get the "canonical" base URL for a given provider, given a
* publicly listed URL. The canonical base URL is the URL to the
* directory containing the update.properties file.
* @return the canonical URL to the provider's base directory
*/
private static URL baseURLFor( String url )
{
URL result = null;
String updateUrl = url;
if ( updateUrl != null )
{
if ( updateUrl.endsWith( UPDATE_PROPERTIES_FILE ) )
{
updateUrl = updateUrl.substring(
0, updateUrl.length() - UPDATE_PROPERTIES_FILE.length() );
}
else
{
if ( !updateUrl.endsWith( "/" ) )
{
updateUrl = updateUrl + "/";
}
}
try
{
result = new URL( updateUrl );
}
catch (java.net.MalformedURLException e)
{
WCUpdater.logInfo("FeatureProvider: ERROR: Malformed "
+ "feature provider URL: " + updateUrl );
}
}
return result;
}
//~ Instance/static variables .............................................
private URL url;
private String name;
private Properties properties;
private Map<String, FeatureDescriptor> subsystems;
private Map<String, String> renamedSubsystems;
private Map<String, FeatureDescriptor> plugins;
private Map<String, String> renamedPlugins;
private static Map<URL, FeatureProvider> providerRegistry =
new HashMap<URL, FeatureProvider>();
private static final String UPDATE_PROPERTIES_FILE = "update.properties";
}