/* Copyright (2006-2012) Schibsted ASA
* This file is part of Possom.
*
* Possom is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Possom 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Possom. If not, see <http://www.gnu.org/licenses/>.
*/
package no.sesat.search.site.config;
import java.io.Serializable;
import no.sesat.commons.ioc.BaseContext;
import no.sesat.search.site.Site;
import no.sesat.search.site.SiteContext;
import no.sesat.search.site.SiteKeyedFactory;
import org.apache.log4j.Logger;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* SiteConfiguration properties.
*
*
* @version $Id$
*/
public final class SiteConfiguration implements SiteKeyedFactory,Serializable {
/**
* The key name used to identify an instance of SiteConfiguration.
*/
public static final String NAME_KEY = "SiteConfiguration";
/**
* The property key to the site's default locale to use.
*/
public static final String SITE_LOCALE_DEFAULT = "site.locale.default";
/**
* The property key where publishing content ("/pub/") is imported from. Related to SiteLocatorFilter.PUBLISH_DIR
*/
public static final String PUBLISH_SYSTEM_URL = "publishing.system.baseURL";
/**
* The property key for the host serving the publishing content, use as host-header in importing requests.
*/
public static final String PUBLISH_PHYSICAL_HOST = "publishing.system.physicalHost";
private static final String SITE_LOCALE_SUPPORTED = "site.locale.supported";
/**
* Property key to find out if this Site is a sitesearch*
*/
public static final String IS_SITESEARCH_KEY = "site.issitesearch";
/**
* The Property key for the default tab/vertical for the site.
*/
public static final String DEFAULTTAB_KEY = "site.defaultTab";
/**
* The property key for the allow list of ipaddresses.
* If not empty the client ipaddress must exist in the list to be able to use the skin.
*/
public static final String ALLOW_LIST = "site.allow";
/**
* The property key for the disallow list of ipaddresses.
* The client ipaddress must not exist in the list to be able to use the skin.
*/
public static final String DISALLOW_LIST = "site.disallow";
public interface Context extends BaseContext, PropertiesContext, SiteContext {}
private final Properties properties = new Properties();
private final Site site;
private static final Map<Site, SiteConfiguration> INSTANCES = new HashMap<Site, SiteConfiguration>();
private static final ReentrantReadWriteLock INSTANCES_LOCK = new ReentrantReadWriteLock();
private static final Logger LOG = Logger.getLogger(SiteConfiguration.class);
/** No-argument constructor for deserialization. */
private SiteConfiguration() {
site = null;
}
private SiteConfiguration(final Context cxt) {
try {
INSTANCES_LOCK.writeLock().lock();
LOG.trace("SiteConfiguration(cxt)");
site = cxt.getSite();
cxt.newPropertiesLoader(cxt, Site.CONFIGURATION_FILE, properties).abut();
INSTANCES.put(cxt.getSite(), this);
}catch(ResourceLoadException rle){
LOG.fatal("BROKEN SITE HIERARCHY." + rle.getMessage());
throw new VirtualMachineError(rle.getMessage()){};
} finally {
INSTANCES_LOCK.writeLock().unlock();
}
}
/**
* Get all the properties. A defensive copy of the map is returned.
* Is a relatively expensive method to use.
*
* @return defensive copy of the map is returned.
*/
public Properties getProperties() {
// new Properties(properties) does not work.
// TOO SLOW (especially on a cold container) return (Properties) properties.clone();
// alternative approach that breaks synchronisation into smaller blocks
final Properties copy = new Properties();
copy.putAll(properties);
return copy;
}
/**
* Get a property
* @param key the property key
* @return the property value (or null if key doesn't exist in properties map)
*/
public String getProperty(final String key) {
assert null != key : "Expecting a value for a null key!?";
final String result = properties.getProperty(key);
//assert null != result && key.length() > 0 : "Couldn't find " + key + " in " + properties;
return result;
}
/**
* Find the correct instance handling this Site.
* We need to use a Context instead of the Site directly so we can handle different styles of loading resources.
* @param cxt the context which defines which SiteConfiguration is applicable
* @return the applicable SiteConfiguration
*/
public static SiteConfiguration instanceOf(final Context cxt) {
final Site site = cxt.getSite();
assert null != site : "valueOf(cxt) got null site";
SiteConfiguration instance = null;
try {
INSTANCES_LOCK.readLock().lock();
instance = INSTANCES.get(site);
} finally {
INSTANCES_LOCK.readLock().unlock();
}
if (instance == null) {
instance = new SiteConfiguration(cxt);
}
return instance;
}
/**
* Utility wrapper to the instanceOf(Context).
* <b>Makes the presumption we will be using the UrlResourceLoader to load the resource.</b>
* <b>Therefore can only be used within a running container, eg tomcat.</b>
* @param site the site that the SiteConfiguration is applicable to
* @return the applicable SiteConfiguration.
*/
public static SiteConfiguration instanceOf(final Site site) {
// SiteConfiguration.Context for this site & UrlResourceLoader.
final SiteConfiguration stc = SiteConfiguration.instanceOf(new SiteConfiguration.Context() {
@Override
public Site getSite() {
return site;
}
public boolean doesResourceExist(final String resource) {
return UrlResourceLoader.doesUrlExist(UrlResourceLoader.getURL(resource, site));
}
@Override
public PropertiesLoader newPropertiesLoader(
final SiteContext siteCxt,
final String resource,
final Properties properties) {
return UrlResourceLoader.newPropertiesLoader(this, resource, properties);
}
});
return stc;
}
@Override
public boolean remove(final Site site) {
try {
INSTANCES_LOCK.writeLock().lock();
return null != INSTANCES.remove(site);
} finally {
INSTANCES_LOCK.writeLock().unlock();
}
}
/** Does the site belonging to this SiteConfiguration support the provided locale.
* The default site "generic.sesam" supports all locales.
* Otherwise the list of locales found in SITE_LOCALE_SUPPORTED is checked.
* @param locale the locale to check
* @return true if the locale is supported.
*/
public boolean isSiteLocaleSupported(final Locale locale) {
if (Site.DEFAULT.getName().equals(site.getName())) {
// the DEFAULT site supports all Locales !
return true;
}
final String supportedLocales = getProperty(SITE_LOCALE_SUPPORTED);
if (null != supportedLocales) {
final String[] locales = supportedLocales.split(",");
for (String l : locales) {
if (locale.toString().equals(l)) {
return true;
}
}
}
return false;
}
}