/*******************************************************************************
* Copyright 2013 Geoscience Australia
*
* 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 au.gov.ga.earthsci.seeder;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.xml.parsers.DocumentBuilder;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.RegistryFactory;
import org.eclipse.e4.core.contexts.ContextInjectionFactory;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import au.gov.ga.earthsci.common.persistence.Exportable;
import au.gov.ga.earthsci.common.persistence.Persistent;
import au.gov.ga.earthsci.common.persistence.Persister;
import au.gov.ga.earthsci.common.util.ConfigurationUtil;
import au.gov.ga.earthsci.common.util.ExtensionPointHelper;
import au.gov.ga.earthsci.common.util.XmlUtil;
/**
* Manages a collection of seeders, defined by the
* {@value #SEEDERS_EXTENSION_POINT_ID} extension point.
*
* @author Michael de Hoog (michael.dehoog@ga.gov.au)
*/
@Singleton
public class SeederManager
{
public static final String SEEDERS_EXTENSION_POINT_ID = "au.gov.ga.earthsci.seeders"; //$NON-NLS-1$
private static final Logger logger = LoggerFactory.getLogger(SeederManager.class);
@Inject
private static IEclipseContext context;
private static final Map<String, Class<ISeeder>> seeders = new HashMap<String, Class<ISeeder>>();
private static final Map<Class<ISeeder>, ISeeder> seederCache = new HashMap<Class<ISeeder>, ISeeder>();
private static final String seederFilename = "seeder.xml"; //$NON-NLS-1$
private static final File seederFile = ConfigurationUtil.getWorkspaceFile(seederFilename);
private static final Persister persister;
private static SeederHistory history = new SeederHistory();
static
{
persister = new Persister();
persister.setIgnoreMissing(true);
persister.setIgnoreNulls(true);
persister.registerNamedExportable(SeederHistory.class, "History"); //$NON-NLS-1$
persister.registerNamedExportable(SeederRevision.class, "Seeder"); //$NON-NLS-1$
IConfigurationElement[] config =
RegistryFactory.getRegistry().getConfigurationElementsFor(SEEDERS_EXTENSION_POINT_ID);
for (IConfigurationElement element : config)
{
try
{
String elementName = element.getAttribute("element"); //$NON-NLS-1$
if (seeders.containsKey(elementName))
{
throw new Exception("Element name '" + elementName + "' already exists in the seeder registry"); //$NON-NLS-1$ //$NON-NLS-2$
}
@SuppressWarnings("unchecked")
Class<ISeeder> seederClass =
(Class<ISeeder>) ExtensionPointHelper.getClassForProperty(element, "class"); //$NON-NLS-1$
seeders.put(elementName, seederClass);
}
catch (Exception e)
{
logger.error("Error processing intent filter", e); //$NON-NLS-1$
}
}
}
public static void addSeeder(String elementName, Class<ISeeder> seederClass)
{
seeders.put(elementName, seederClass);
}
public static void removeSeeder(String elementName)
{
seeders.remove(elementName);
}
public static void seed(Document document, URL url)
{
int lastRevision = history.getRevision(url);
int maxRevision = -1;
Element documentElement = document.getDocumentElement();
Element[] children = XmlUtil.getElements(documentElement);
for (Element child : children)
{
int revision = -1;
try
{
String revisionString = child.getAttribute("revision"); //$NON-NLS-1$
revision = Integer.parseInt(revisionString);
}
catch (Exception e)
{
//ignore
}
//if newer than last (or revision attribute doesn't exist), then apply this revision
if (revision > lastRevision || revision < 0)
{
Class<ISeeder> seederClass = seeders.get(child.getNodeName());
if (seederClass != null)
{
ISeeder seeder = seederCache.get(seederClass);
if (seeder == null)
{
seeder = ContextInjectionFactory.make(seederClass, context);
seederCache.put(seederClass, seeder);
}
seeder.seed(child, url);
maxRevision = Math.max(maxRevision, revision);
}
else
{
logger.error("No seeder found for element: " + child.getNodeName()); //$NON-NLS-1$
}
}
}
if (maxRevision > lastRevision)
{
history.setRevision(url, maxRevision);
}
}
@PostConstruct
public void loadSeederFile()
{
try
{
if (seederFile.exists())
{
Document document = XmlUtil.createDocumentBuilder().parse(seederFile);
Element parent = document.getDocumentElement();
Element element = XmlUtil.getFirstChildElement(parent);
history = (SeederHistory) persister.load(element, seederFile.toURI());
}
}
catch (Exception e)
{
logger.warn("Error loading seeder configuration file", e); //$NON-NLS-1$
}
}
@PreDestroy
protected void saveSeederFile()
{
OutputStream os = null;
try
{
DocumentBuilder documentBuilder = XmlUtil.createDocumentBuilder();
Document document = documentBuilder.newDocument();
Element element = document.createElement("Seeder"); //$NON-NLS-1$
document.appendChild(element);
persister.save(history, element, seederFile.toURI());
os = new FileOutputStream(seederFile);
XmlUtil.saveDocumentToFormattedStream(document, os);
}
catch (Exception e)
{
logger.error("Error saving seeder configuration file", e); //$NON-NLS-1$
}
finally
{
if (os != null)
{
try
{
os.close();
}
catch (IOException e)
{
}
}
}
}
@Exportable
private static class SeederHistory
{
private final Map<URL, SeederRevision> map = new HashMap<URL, SeederRevision>();
@Persistent
public SeederRevision[] getRevisions()
{
Collection<SeederRevision> values = map.values();
return values.toArray(new SeederRevision[values.size()]);
}
//used by persistence mechanism
@SuppressWarnings("unused")
public void setRevisions(SeederRevision[] revisions)
{
map.clear();
if (revisions != null)
{
for (SeederRevision revision : revisions)
{
map.put(revision.url, revision);
}
}
}
public int getRevision(URL url)
{
SeederRevision revision = map.get(url);
return revision != null ? revision.revision : -1;
}
public void setRevision(URL url, int revision)
{
SeederRevision value = new SeederRevision();
value.url = url;
value.revision = revision;
map.put(url, value);
}
}
@Exportable
private static class SeederRevision
{
@Persistent(attribute = true)
public URL url;
@Persistent(attribute = true)
public int revision;
}
}