/* * Copyright 2000-2001,2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jetspeed.services.urlmanager; // Java classes import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.PrintWriter; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Vector; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import org.apache.jetspeed.services.logging.JetspeedLogFactoryService; import org.apache.jetspeed.services.logging.JetspeedLogger; import org.apache.jetspeed.services.resources.JetspeedResources; import org.apache.turbine.services.TurbineBaseService; /** * <p> * This implementation of the URLManagerService is backed by a simple map * persisted on disk in a properties file * </p> * Added: Support for proxies. <br> * Example: (Set in <code>JetspeedResources.properties</code>)<br> * <code>services.URLManager.proxy.http.host=myproxy.mydomain</code><br> * <code>services.URLManager.proxy.http.port=81</code><br> * * @see URLManagerService * @author <a href="mailto:raphael@apache.org">Rapha�l Luta</a> * @author <a href="mailto:sgala@hisitech.com">Santiago Gala</a> * @version $Id: JetspeedURLManagerService.java,v 1.16 2004/02/23 03:30:47 jford * Exp $ */ public class JetspeedURLManagerService extends TurbineBaseService implements URLManagerService { /** * Static initialization of the logger for this class */ private static final JetspeedLogger logger = JetspeedLogFactoryService .getLogger(JetspeedURLManagerService.class.getName()); /** * Map used to store all URL Information. */ private Map urls = new HashMap(); /** * Path to the properties file used for persisting the data */ private String path = null; /** * Hashtable to store proxy configuration in */ private final Hashtable proxies = new Hashtable(); /** * Late init. Don't return control until early init says we're done. */ @Override public void init() { while (!getInit()) { try { Thread.sleep(500); } catch (InterruptedException ie) { logger.info("URLManager service: Waiting for init()..."); } } } /** * Called during Turbine.init() * * @param config * A ServletConfig. */ @Override public synchronized void init(ServletConfig config) { // We have already been initialized... if (getInit()) { return; } try { logger.info("JetspeedURLManagerService early init()....starting!"); // Proxy Settings are stored as // 'services.URLManager.proxy.<protocol>.port' and as // 'services.URLManager.proxy.<protocol>.port' in // JetspeedResource.properties. // Get a list of settings and store them in the hashtable String prefix = "services." + URLManagerService.SERVICE_NAME + ".proxy."; Iterator resourceKeys = JetspeedResources.getKeys(prefix); String key, hashKey; Object hashValue = null; while (resourceKeys.hasNext()) { key = (String) resourceKeys.next(); hashKey = key.substring(prefix.length()).toLowerCase(); if (hashKey.endsWith(".host")) { hashValue = JetspeedResources.getString(key); proxies.put(hashKey, hashValue); } else if (hashKey.endsWith(".port")) { hashValue = new Integer(JetspeedResources.getInt(key)); proxies.put(hashKey, hashValue); } } path = JetspeedResources.getString("services." + URLManagerService.SERVICE_NAME + ".url"); if (path == null) { String tempdir = new String("WEB-INF/conf/datasources.properties"); String ps = System.getProperty("file.separator"); try { ServletContext sc = config.getServletContext(); tempdir = sc.getAttribute("javax.servlet.context.tempdir").toString() + ps + "jetspeed" + ps + "conf" + ps + "datasources.properties"; logger .debug("URLMangler: will create file in servlet temp directory " + tempdir); } catch (Exception e) { logger .debug("URLMangler: problems creating file in servlet temp directory " + " falling back to WEB-INF/conf : " + e); } path = tempdir; } else { logger.debug("URLMangler: will create file in user configured " + path); path = config.getServletContext().getRealPath(path); // should test for writability here and fallback to servlet tmp // directory on failure } load(); logger.info("JetspeedURLManagerService early init()....finished!"); } catch (Throwable t) { logger.error("Cannot initialize JetspeedURLManagerService!", t); } setInit(true); } /** * Called during Turbine destroy(). Persist the Manager state to disk */ @Override public void shutdown() { save(); } /** * Registers a new URL record. If the url is already registered in the system, * doesn't modify the current record. * * @param url * the url to register */ public void register(String url) { if (url != null) { URLInfo info = getInfo(url); if (info == null) { register(new URLInfo(url, URLManagerService.STATUS_OK)); } } } /** * Registers a new URL record. If the url is already registered in the system, * updates the status of this URL info record * * @param url * the url to register * @param status * the status of this url */ public void register(String url, int status) { if (url != null) { URLInfo info = getInfo(url); if (info == null) { register(new URLInfo(url, status)); } else { info.setStatus(status); } } } /** * Registers a new URL record. If the url is already registered in the system, * updates both the status and the message of this URL info record * * @param url * the url to register * @param status * the status of this url * @param message * a descriptive message of the status */ public void register(String url, int status, String message) { if (url != null) { URLInfo info = getInfo(url); if (info == null) { register(new URLInfo(url, status, message)); } else { info.setStatus(status); info.setMessage(message); } } } /** * Register or replace an URL record. All records are keyed to the imutable * URL of URLInfo. * * @param info * the info record to store */ public void register(URLInfo info) { if (info != null) { synchronized (urls) { if (getInfo(info.getURL()) == null) { urls.put(info.getURL().intern(), info); } } } } /** * Unregister an URL from the repository * * @param url * the url to remove */ public void unregister(String url) { if (url != null) { synchronized (urls) { urls.remove(url.intern()); } } } /** * Get the information record stored in the database about an URL. * * @param url * the url whose record is sought * @return the description record found in the repository or null. */ public URLInfo getInfo(String url) { URLInfo info = null; if (url != null) { synchronized (urls) { info = (URLInfo) urls.get(url.intern()); } } return info; } /** * Test whether the URL is currently believed to be OK by this repository. * * @param url * the url to be tested * @return false is the url is known by this repository and has a status * indicating an error, true otherwise. */ public boolean isOK(String url) { URLInfo info = getInfo(url); // we don't know this URL, play it safe and say it's good if (info == null) { return true; } return (info.getStatus() == URLManagerService.STATUS_OK); } /** * List of the current known URLs in the repository * * @return a List of URL strings known to this repository */ public List list() { synchronized (urls) { return new Vector(urls.keySet()); } } /** * List of the current known URLs in the repository which have the given * status. * * @param status * the status to be retrieved. May be * {@link URLManagerService#STATUS_ANY} to indicate any status * @return a List of URL strings known to this repository with this status */ public List list(int status) { Vector result = new Vector(); synchronized (urls) { Iterator i = urls.entrySet().iterator(); while (i.hasNext()) { Map.Entry entry = (Map.Entry) i.next(); URLInfo info = (URLInfo) entry.getValue(); if ((info.getStatus() & status) != 0) { result.addElement(entry.getKey()); } } } return result; } /** * Load the persisted state of the repository from disk */ private synchronized void load() { Map store = new HashMap(); // Configuration config = null; logger.info("Restoring the URLs from disk: " + path); try { /*- config = new Configuration( path ); int count = 1; String url = null; while ( ( url = ( config .getString("entry."+count+".url") ) ) != null ) { //Intern the url to ensure we can use "==" to compare //and synchronize on it url = url.intern(); int status = config.getInteger("entry."+count+".status", URLManagerService.STATUS_OK ); if( store.get( url ) == null ) store.put( url, new URLInfo( url, status ) ); count++; } logger.info( "URLManager loaded " + count + " urls" ); */ } catch (Exception e) { logger.error("Could not restore URLManager state", e); return; } finally { // set the loaded store as the new store this.urls = store; } } /** * Persist the state of the repository on disk in a properties file */ private synchronized void save() { PrintWriter pw = null; try { File propfile = new File(path); // FileWriter doesn't always do this propfile.getParentFile().mkdirs(); propfile.createNewFile(); pw = new PrintWriter(new BufferedWriter(new FileWriter(propfile))); synchronized (urls) { Iterator i = urls.values().iterator(); int entryNum = 1; while (i.hasNext()) { URLInfo info = (URLInfo) i.next(); pw.print("entry."); pw.print(entryNum); pw.print(".url="); writeEscaped(pw, info.getURL()); pw.println(""); pw.print("entry."); pw.print(entryNum); pw.print(".status="); pw.print(info.getStatus()); pw.println(""); entryNum++; } } } catch (Throwable t) { logger.error("Impossible to save URLManager state to " + path, t); } finally { if (pw != null) { pw.close(); } } } /** * Return the port of a proxy * * @param protocol * The protocol that the proxy supports, e.g. 'http' * @return The port number (1-65535), or -1 if no port was specified (= use * default) */ public int getProxyPort(String protocol) { Integer proxyPort = (Integer) proxies.get((protocol + ".port").toLowerCase()); if (proxyPort != null) { return proxyPort.intValue(); } else { return -1; } } /** * Return a proxy's hostname * * @param protocol * The protocol that the proxy supports, e.g. 'http' * @return The hostname of the proxy, or <code>null</code> if no proxy is * specified for this protocol */ public String getProxyHost(String protocol) { String proxyHost = (String) proxies.get((protocol + ".host").toLowerCase()); return proxyHost; } /** * <p> * Escape values when saving. Appends a String to a StringBuffer, escaping * commas. * </p> * <p> * We assume that commas are unescaped. * </p> * * @param sink * a StringBuffer to write output * @param element * a value to be written */ protected void writeEscaped(PrintWriter sink, String element) { int upTo = element.indexOf(","); if (upTo == -1) { sink.print(element); return; } sink.print(element.substring(0, upTo)); sink.print("\\,"); writeEscaped(sink, element.substring(upTo + 1, element.length())); return; } }