/*******************************************************************************
* Copyright 2012 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.worldwind.common.layers;
import gov.nasa.worldwind.avlist.AVList;
import gov.nasa.worldwind.avlist.AVListImpl;
import gov.nasa.worldwind.layers.Layer;
import gov.nasa.worldwind.util.WWXML;
import java.util.HashSet;
import java.util.Set;
import javax.xml.xpath.XPath;
import org.w3c.dom.Element;
import au.gov.ga.earthsci.worldwind.common.util.AVKeyMore;
/**
* Provides timed expiration functionality to World Wind layers. Allows the
* ability to define a layer that will expire every x timespans, which will mark
* all textures as expired and re-download them. Useful for layers that update
* every few hours, such as weather layers.
*
* @author Michael de Hoog (michael.dehoog@ga.gov.au)
*/
public class TimedExpirationHandler
{
protected final static String DATE_TIME_PATTERN = "dd MM yyyy HH:mm:ss z";
private final static ExpiryUpdater updater = new ExpiryUpdater();
/**
* Read expiration params from XML into the AVList
*
* @param domElement
* XML element
* @param params
* @return params
*/
public static AVList getExpirationParams(Element domElement, AVList params)
{
if (params == null)
params = new AVListImpl();
XPath xpath = WWXML.makeXPath();
WWXML.checkAndSetLongParam(domElement, params, AVKeyMore.EXPIRY_START_TIME,
"TimedExpiry/Start", xpath);
WWXML.checkAndSetDateTimeParam(domElement, params, AVKeyMore.EXPIRY_START_TIME,
"TimedExpiry/StartDate", DATE_TIME_PATTERN, xpath);
WWXML.checkAndSetTimeParam(domElement, params, AVKeyMore.EXPIRY_TIMESPAN,
"TimedExpiry/Time", xpath);
return params;
}
/**
* Register a layer with TimedExpiration parameters.
*
* @param layer
* @param params
*/
public static void registerLayer(Layer layer, AVList params)
{
long start = 0, time = 0;
Long l = (Long) params.getValue(AVKeyMore.EXPIRY_START_TIME);
if (l != null)
start = l;
l = (Long) params.getValue(AVKeyMore.EXPIRY_TIMESPAN);
if (l != null)
time = l;
if (time <= 0)
return;
layer.setValue(AVKeyMore.EXPIRY_START_TIME, start);
layer.setValue(AVKeyMore.EXPIRY_TIMESPAN, time);
updater.registerLayer(layer);
}
private static void updateExpiry(Layer layer, long current)
{
Long start = (Long) layer.getValue(AVKeyMore.EXPIRY_START_TIME);
Long time = (Long) layer.getValue(AVKeyMore.EXPIRY_TIMESPAN);
if (start == null || time == null || time <= 0)
return;
long diff = current - start;
//if start time hasn't come yet, don't set the expiry
if (diff <= 0)
return;
long spans = diff / time; //long division
long last = spans * time + start;
layer.setExpiryTime(last);
}
private static class ExpiryUpdater
{
private Set<Layer> layers = new HashSet<Layer>();
private boolean threadStarted = false;
public void registerLayer(Layer layer)
{
synchronized (layers)
{
startThreadIfRequired();
layers.add(layer);
}
}
private void startThreadIfRequired()
{
if (!threadStarted)
{
startUpdaterThread();
threadStarted = true;
}
}
private void startUpdaterThread()
{
Runnable runnable = new Runnable()
{
@Override
public void run()
{
while (true)
{
try
{
Thread.sleep(1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
synchronized (layers)
{
long current = System.currentTimeMillis();
for (Layer layer : layers)
{
if (layer.isEnabled())
updateExpiry(layer, current);
}
}
}
}
};
Thread thread = new Thread(runnable);
thread.setDaemon(true);
thread.setName("Layer timed expiry updater");
thread.start();
}
}
}