/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache Jetspeed" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache" or
* "Apache Jetspeed", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
*
This is an implementation of the RegistryService
* based on the Castor XML serialization mechanisms
This registry aggregates multiple RegistryFragment to store the regsistry * entries
* *This service expects the following properties to be set for correct operation: *
Service framework
*/
public synchronized void init(ServletConfig conf) throws
InitializationException
{
//Ensure that the servlet service is initialized
TurbineServices.getInstance().initService(ServletService.SERVICE_NAME,
conf);
ResourceService serviceConf = ((TurbineServices)
TurbineServices.getInstance())
.getResources(RegistryService.SERVICE_NAME);
String mapFile = null;
Vector names = new Vector();
int refreshRate = 0;
// read the configuration keys
try
{
directory = serviceConf.getString("directory");
mapFile = serviceConf.getString("mapping", DEFAULT_MAPPING);
extension = serviceConf.getString("extension",
DEFAULT_EXTENSION);
refreshRate = serviceConf.getInt("refreshRate",
DEFAULT_REFRESH);
verbose = serviceConf.getInt("verbose", DEFAULT_VERBOSE);
mapFile = TurbineServlet.getRealPath(mapFile);
directory = TurbineServlet.getRealPath(directory);
}
catch (Throwable t)
{
throw new InitializationException("Unable to initialize
CastorRegistryService, missing config keys");
}
// build the map of default fragments, eahc registry must be
associated
// with at least one fragment
try
{
ResourceService defaults = serviceConf.getResources("default");
Iterator i = defaults.getKeys();
while (i.hasNext())
{
String name = (String) i.next();
String fragmentFileName = defaults.getString(name);
String absFileName = new File(directory, fragmentFileName +
extension).getAbsolutePath();
// add this name in the list of available registries
names.add(name);
// store the default file mapping
this.defaults.put(name, absFileName);
}
}
catch (Exception e)
{
Log.error("RegistryService: Registry init error", e);
throw new InitializationException("Unable to initialize
CastorRegistryService, invalid registries definition");
}
// create the serializer output format
this.format = new OutputFormat();
format.setIndenting(true);
format.setIndent(4);
// test the mapping file and create the mapping object
if (mapFile != null)
{
File map = new File(mapFile);
if (map.exists() && map.isFile() && map.canRead())
{
try
{
mapping = new Mapping();
InputSource is = new InputSource(new FileReader(map));
is.setSystemId(mapFile);
mapping.loadMapping(is);
}
catch (Exception e)
{
Log.error("RegistryService: Error in mapping creation",
e);
throw new InitializationException("Error in mapping",
e);
}
}
else
{
throw new InitializationException("Mapping not found or not
a file or unreadable: " + mapFile);
}
}
// Set directory watcher if directory exists
File base = new File(directory);
File[] files = null;
if (base.exists() && base.isDirectory() && base.canRead())
{
this.watcher = new RegistryWatcher();
this.watcher.setSubscriber(this);
this.watcher.setFilter(new ExtFileFilter(extension));
if (refreshRate == 0)
{
this.watcher.setDone();
}
else
{
this.watcher.setRefreshRate(refreshRate);
}
// changing the base will trigger a synchronous loading of the
fragments
this.watcher.changeBase(base);
}
//Mark that we are done
setInit(true);
// load the registries
Enumeration en = names.elements();
while (en.hasMoreElements())
{
String name = (String) en.nextElement();
Registry registry = (Registry) registries.get(name);
if (registry == null)
{
String registryClass = null;
try
{
registryClass =
"org.apache.jetspeed.om.registry.base.Base"
+ name
+ "Registry";
registry = (Registry)
Class.forName(registryClass).newInstance();
}
catch (Exception e)
{
if (Log.getLogger().isWarnEnabled())
{
Log.warn("RegistryService: Class "
+ registryClass
+ " not found, reverting to default
Registry");
}
registry = new BaseRegistry();
}
registries.put(name, registry);
}
refresh(name);
}
// Start the directory watcher thread and rely on its refresh
process
// to completely load all registries
if (this.watcher != null)
{
this.watcher.start();
}
if (Log.getLogger().isDebugEnabled())
{
Log.debug("RegistryService: early init()....end!,
this.getInit()= " + getInit());
}
}
/** Late init method from Turbine Service model */
public void init() throws InitializationException
{
if (Log.getLogger().isDebugEnabled())
{
Log.debug("RegistryService: Late init called");
}
while (!getInit())
{
//Not yet...
try
{
Thread.sleep(500);
if ((verbose > 2) && Log.getLogger().isDebugEnabled())
{
Log.debug("RegistryService: Waiting for init of
Registry...");
}
}
catch (InterruptedException ie)
{
Log.error(ie);
}
}
if (Log.getLogger().isDebugEnabled())
{
Log.debug("RegistryService: We are done");
}
}
/**
* This is the shutdown method called by the
* Turbine Service framework
*/
public void shutdown()
{
this.watcher.setDone();
Iterator i = fragments.keySet().iterator();
while (i.hasNext())
{
saveFragment((String) i.next());
}
}
// FileRegistry interface
/** Refresh the state of the registry implementation. Should be called
* whenever the underlying fragments are modified
*/
public void refresh()
{
synchronized (watcher)
{
Enumeration en = getNames();
while (en.hasMoreElements())
{
refresh((String) en.nextElement());
}
}
}
/**
* @return a Map of all fragments keyed by file names
*/
public Map getFragmentMap()
{
return (Map) fragments.clone();
}
/**
* Load and unmarshal a RegistryFragment from the file
* @param file the absolute file path storing this fragment
*/
public void loadFragment(String file)
{
try
{
beingProcessed.put(file, file);
(new FragmentLoaderThread(file)).start();
}
catch (Throwable t)
{
Log.error("RegistryService: Could not unmarshal: " + file, t);
}
}
/**
* Read and unmarshal a fragment in memory
* @param name the name of this fragment
* @param reader the reader to use for creating this fragment
* @param persistent whether this fragment should be persisted on disk
in
* the registry
*/
public void createFragment(String name, Reader reader, boolean
persistent)
{
String file = null;
try
{
file = new File(directory, name + extension).getCanonicalPath();
Unmarshaller unmarshaller = new Unmarshaller(this.mapping);
RegistryFragment fragment = (RegistryFragment)
unmarshaller.unmarshal(reader);
fragment.setChanged(true);
updateFragment(file, fragment);
if (persistent)
{
saveFragment(file);
}
}
catch (Throwable t)
{
Log.error("RegistryService: Could not create fragment: " + file,
t);
}
finally
{
try
{
reader.close();
}
catch (Exception e)
{
Log.error(e); // At least log the exception.
}
}
}
/**
* Marshal and save a RegistryFragment to disk
* @param file the absolute file path storing this fragment
*/
public void saveFragment(String file)
{
OutputStreamWriter writer = null;
String encoding = new String("UTF-8");
RegistryFragment fragment = (RegistryFragment) fragments.get(file);
if (fragment != null)
{
try
{
writer = new OutputStreamWriter(new FileOutputStream(file),
encoding);
format.setEncoding(encoding);
Serializer serializer = new XMLSerializer(writer, format);
Marshaller marshaller = new
Marshaller(serializer.asDocumentHandler());
marshaller.setMapping(this.mapping);
marshaller.marshal(fragment);
}
catch (Throwable t)
{
Log.error("RegistryService: Could not marshal: " + file, t);
}
finally
{
try
{
writer.close();
}
catch (Exception e)
{
Log.error(e); // At least log the exception.
}
}
}
}
/**
* Remove a fragment from storage
* @param file the absolute file path storing this fragment
*/
public void removeFragment(String file)
{
RegistryFragment fragment = (RegistryFragment) fragments.get(file);
if (fragment != null)
{
synchronized (entryIndex)
{
// clear the entry index
Iterator i = entryIndex.keySet().iterator();
while (i.hasNext())
{
if (file.equals(entryIndex.get(i.next())))
{
i.remove();
}
}
// make sure the keys & entries are freed for this fragment
// only the entries not replaced by the next registry
refresh will
// stay in memory
fragment.clear();
// remove the actual fragment from memory
fragments.remove(file);
}
}
}
// Implementation specific methods
/**
* Updates a fragment in storage and the associated entryIndex
*/
protected void updateFragment(String name, RegistryFragment fragment)
{
synchronized (entryIndex)
{
// remove the old keys
Iterator i = entryIndex.keySet().iterator();
while (i.hasNext())
{
if (name.equals(entryIndex.get(i.next())))
{
i.remove();
}
}
// store the new fragment
fragments.put(name, fragment);
// recreate the index entries (only this fragment)
Enumeration enum = fragment.keys();
while (enum.hasMoreElements())
{
String strReg = (String) enum.nextElement();
Vector v = fragment.getEntries(strReg);
for (int counter = 0; counter < v.size(); counter++)
{
RegistryEntry str = (RegistryEntry)
v.elementAt(counter);
entryIndex.put(str.getName(), name);
}
}
}
}
/**
* Scan all the registry fragments for new entries relevant to
* this registry and update its definition.
*
* @param regName the name of the Registry to refresh
*/
protected void refresh(String regName)
{
if (Log.getLogger().isDebugEnabled())
{
Log.debug("RegistryService: Updating the " + regName + "
registry");
}
int count = 0;
int counDeleted = 0;
LocalRegistry registry = (LocalRegistry) get(regName);
Vector toDelete = new Vector();
Iterator i = registry.listEntryNames();
while (i.hasNext())
{
toDelete.add(i.next());
}
if (registry == null)
{
Log.error("RegistryService: Null " + name + " registry in
refresh");
return;
}
// for each fragment...
Enumeration en = fragments.keys();
while (en.hasMoreElements())
{
String location = (String) en.nextElement();
RegistryFragment fragment = (RegistryFragment)
fragments.get(location);
int fragCount = 0;
if (!fragment.hasChanged())
{
if ((verbose > 2) && Log.getLogger().isDebugEnabled())
{
Log.debug("RegistryService: Skipping fragment " +
location);
}
//remove this fragment entries from the delete list
Vector entries = fragment.getEntries(regName);
i = entries.iterator();
while (i.hasNext())
{
toDelete.remove(((RegistryEntry) i.next()).getName());
}
continue;
}
//the fragment has some changes, iterate over its entries...
Vector entries = fragment.getEntries(regName);
//... if it has entries related to this regsistry,
if (entries != null)
{
// for all these entries
Enumeration en2 = entries.elements();
while (en2.hasMoreElements())
{
RegistryEntry entry = (RegistryEntry) en2.nextElement();
// update or add the entry in the registry
try
{
if (registry.hasEntry(entry.getName()))
{
if
(registry.getEntry(entry.getName()).equals(entry))
{
if ((verbose > 2) &&
Log.getLogger().isDebugEnabled())
{
Log.debug("RegistryService: No changes
to entry " + entry.getName());
}
}
else
{
if ((verbose > 1) &&
Log.getLogger().isDebugEnabled())
{
Log.debug("RegistryService: Updating
entry " + entry.getName()
+ " of class " + entry.getClass() +
" to registry " + name);
}
registry.setLocalEntry(entry);
// Initialize the entry index
this.entryIndex.put(entry.getName(),
location);
++fragCount;
}
}
else
{
registry.addLocalEntry(entry);
// Initialize the entry index
this.entryIndex.put(entry.getName(), location);
++fragCount;
if ((verbose > 1) &&
Log.getLogger().isDebugEnabled())
{
Log.debug("RegistryService: Adding entry " +
entry.getName() + " of class "
+ entry.getClass() + " to registry " +
name);
}
}
}
catch (RegistryException e)
{
Log.error("RegistryService: RegistryException while
adding " + entry.getName() + "from " + location, e);
}
//remove this entry from the delete list
toDelete.remove(entry.getName());
}
}
count += fragCount;
}
//now delete the entries not found in any fragment
i = toDelete.iterator();
while (i.hasNext())
{
String entryName = (String) i.next();
if ((verbose > 1) && Log.getLogger().isDebugEnabled())
{
Log.debug("RegistryService: removing entry " + entryName);
}
registry.removeLocalEntry(entryName);
}
if ((verbose > 1) && Log.getLogger().isDebugEnabled())
{
Log.debug("RegistryService: Merged " + count + " entries and
deleted " + toDelete.size() + " in " + name);
}
}
/** FileFilter implementing a file extension based filter */
class ExtFileFilter implements FileFilter
{
private String extension = null;
ExtFileFilter(String extension)
{
this.extension = extension;
}
public boolean accept(File f)
{
return f.toString().endsWith(extension);
}
}
class FragmentLoaderThread extends Thread {
private String fileNm;
public void run() {
try
{
DocumentBuilderFactory dbfactory =
DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dbfactory.newDocumentBuilder();
Document d = builder.parse(new File(fileNm));
Unmarshaller unmarshaller = new Unmarshaller(mapping);
unmarshaller.setValidation(false);
RegistryFragment fragment = (RegistryFragment)
unmarshaller.unmarshal((Node) d);
//mark this fragment as changed
fragment.setChanged(true);
// if we get here, we successfully loaded the new fragment
updateFragment(fileNm, fragment);
beingProcessed.remove(fileNm);
}
catch(Throwable t)
{
Log.error("RegistryService: Could not unmarshal: " + fileNm, t);
}
}
public FragmentLoaderThread (String fileNm) {
this.fileNm = fileNm;
}
}
}