All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.javamoney.moneta.internal.loader.DefaultLoaderService Maven / Gradle / Ivy

/**
 * Copyright (c) 2012, 2014, Credit Suisse (Anatole Tresch), Werner Keil and others by the @author tag.
 *
 * 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.javamoney.moneta.internal.loader;

import org.javamoney.moneta.spi.LoaderService;

import javax.money.spi.Bootstrap;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * This class provides a mechanism to register resources, that may be updated
 * regularly. The implementation, based on the {@link UpdatePolicy}
 * loads/updates the resources from arbitrary locations and stores it to the
 * format file cache. Default loading tasks can be configured within the javamoney.properties
 * file, @see org.javamoney.moneta.loader.format.LoaderConfigurator .
 * 

* * @author Anatole Tresch */ public class DefaultLoaderService implements LoaderService { /** * Logger used. */ private static final Logger LOG = Logger.getLogger(DefaultLoaderService.class.getName()); /** * The data resources managed by this instance. */ private final Map resources = new ConcurrentHashMap<>(); /** * The registered {@link LoaderListener} instances. */ private final Map> listenersMap = new ConcurrentHashMap<>(); /** * The local resource cache, to allow keeping current data on the local * system. */ private static final ResourceCache CACHE = loadResourceCache(); /** * The thread pool used for loading of data, triggered by the timer. */ private final ExecutorService executors = Executors.newCachedThreadPool(); /** * The timer used for schedules. */ private volatile Timer timer; /** * Constructor, initializing from config. */ public DefaultLoaderService() { initialize(); } /** * This method reads initial loads from the javamoney.properties and installs the according timers. */ protected void initialize() { // Cancel any running tasks Timer oldTimer = timer; timer = new Timer(); if (oldTimer!=null) { oldTimer.cancel(); } // (re)initialize LoaderConfigurator configurator = new LoaderConfigurator(this); configurator.load(); } /** * Loads the cache to be used. * * @return the cache to be used, not null. */ private static ResourceCache loadResourceCache() { try { ResourceCache cache = Bootstrap.getService(ResourceCache.class); if (cache == null) { cache = new DefaultResourceCache(); } return cache; } catch (Exception e) { LOG.log(Level.SEVERE, "Error loading ResourceCache instance.", e); return new DefaultResourceCache(); } } /** * Get the resource cache loaded. * * @return the resource cache, not null. */ static ResourceCache getResourceCache() { return DefaultLoaderService.CACHE; } /** * Removes a resource managed. * * @param resourceId the resource id. */ public void unload(String resourceId) { LoadableResource res = this.resources.get(resourceId); if (res!=null) { res.unload(); } } /* * (non-Javadoc) * * @see * LoaderService#registerData(java.lang.String, * LoaderService.UpdatePolicy, java.util.Map, * java.net.URL, java.net.URL[]) */ @Override public void registerData(String resourceId, UpdatePolicy updatePolicy, Map properties, LoaderListener loaderListener, URI backupResource, URI... resourceLocations) { if (resources.containsKey(resourceId)) { throw new IllegalArgumentException("Resource : " + resourceId + " already registered."); } LoadableResource res = new LoadableResource(resourceId, CACHE, updatePolicy, properties, backupResource, resourceLocations); this.resources.put(resourceId, res); if (loaderListener != null) { this.addLoaderListener(loaderListener, resourceId); } switch (updatePolicy) { case NEVER: loadDataLocal(resourceId); break; case ONSTARTUP: loadDataAsync(resourceId); break; case SCHEDULED: addScheduledLoad(res); break; case LAZY: default: break; } } /* * (non-Javadoc) * * @see * LoaderService#registerAndLoadData(java.lang.String, * LoaderService.UpdatePolicy, java.util.Map, * java.net.URL, java.net.URL[]) */ @Override public void registerAndLoadData(String resourceId, UpdatePolicy updatePolicy, Map properties, LoaderListener loaderListener, URI backupResource, URI... resourceLocations) { if (resources.containsKey(resourceId)) { throw new IllegalArgumentException("Resource : " + resourceId + " already registered."); } LoadableResource res = new LoadableResource(resourceId, CACHE, updatePolicy, properties, backupResource, resourceLocations); this.resources.put(resourceId, res); if (loaderListener != null) { this.addLoaderListener(loaderListener, resourceId); } switch (updatePolicy) { case SCHEDULED: addScheduledLoad(res); break; case LAZY: default: break; } loadData(resourceId); } /* * (non-Javadoc) * * @see * LoaderService#getUpdateConfiguration(java.lang * .String) */ @Override public Map getUpdateConfiguration(String resourceId) { LoadableResource load = this.resources.get(resourceId); if (load!=null) { return load.getProperties(); } return null; } /* * (non-Javadoc) * * @see * LoaderService#isResourceRegistered(java.lang.String) */ @Override public boolean isResourceRegistered(String dataId) { return this.resources.containsKey(dataId); } /* * (non-Javadoc) * * @see LoaderService#getResourceIds() */ @Override public Set getResourceIds() { return this.resources.keySet(); } /* * (non-Javadoc) * * @see LoaderService#getData(java.lang.String) */ @Override public InputStream getData(String resourceId) throws IOException { LoadableResource load = this.resources.get(resourceId); if (load!=null) { load.getDataStream(); } throw new IllegalArgumentException("No such resource: " + resourceId); } /* * (non-Javadoc) * * @see LoaderService#loadData(java.lang.String) */ @Override public boolean loadData(String resourceId) { return loadDataSynch(resourceId); } /* * (non-Javadoc) * * @see * LoaderService#loadDataAsync(java.lang.String) */ @Override public Future loadDataAsync(final String resourceId) { return executors.submit(new Callable() { @Override public Boolean call() { return loadDataSynch(resourceId); } }); } /* * (non-Javadoc) * * @see * LoaderService#loadDataLocal(java.lang.String) */ @Override public boolean loadDataLocal(String resourceId) { LoadableResource load = this.resources.get(resourceId); if (load!=null) { try { if (load.loadFallback()) { triggerListeners(resourceId, load.getDataStream()); return true; } } catch (Exception e) { LOG.log(Level.SEVERE, "Failed to load resource locally: " + resourceId, e); } } else { throw new IllegalArgumentException("No such resource: " + resourceId); } return false; } /** * Reload data for a resource synchronously. * * @param resourceId the resource id, not null. * @return true, if loading succeeded. */ private boolean loadDataSynch(String resourceId) { LoadableResource load = this.resources.get(resourceId); if (load!=null) { try { if (load.load()) { triggerListeners(resourceId, load.getDataStream()); return true; } } catch (Exception e) { LOG.log(Level.SEVERE, "Failed to load resource: " + resourceId, e); } } else { throw new IllegalArgumentException("No such resource: " + resourceId); } return false; } /* * (non-Javadoc) * * @see LoaderService#resetData(java.lang.String) */ @Override public void resetData(String dataId) throws IOException { LoadableResource load = this.resources.get(dataId); if (load == null) { throw new IllegalArgumentException("No such resource: " + dataId); } if (load.resetToFallback()) { triggerListeners(dataId, load.getDataStream()); } } /** * Trigger the listeners registered for the given dataId. * * @param dataId the data id, not null. * @param is the InputStream, containing the latest data. */ @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter") private void triggerListeners(String dataId, InputStream is) { List listeners = getListeners(""); synchronized (listeners) { for (LoaderListener ll : listeners) { try { ll.newDataLoaded(dataId, is); } catch (Exception e) { LOG.log(Level.SEVERE, "Error calling LoadListener: " + ll, e); } } } if (!(dataId==null || dataId.isEmpty())) { listeners = getListeners(dataId); synchronized (listeners) { for (LoaderListener ll : listeners) { try { ll.newDataLoaded(dataId, is); } catch (Exception e) { LOG.log(Level.SEVERE, "Error calling LoadListener: " + ll, e); } } } } } /* * (non-Javadoc) * * @see * LoaderService#addLoaderListener(org.javamoney * .moneta.spi.LoaderService.LoaderListener, java.lang.String[]) */ @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter") @Override public void addLoaderListener(LoaderListener l, String... dataIds) { if (dataIds.length == 0) { List listeners = getListeners(""); synchronized (listeners) { listeners.add(l); } } else { for (String dataId : dataIds) { List listeners = getListeners(dataId); synchronized (listeners) { listeners.add(l); } } } } /** * Evaluate the {@link LoaderListener} instances, listening fo a dataId * given. * * @param dataId The dataId, not null * @return the according listeners */ private List getListeners(String dataId) { if (dataId==null) { dataId = ""; } List listeners = this.listenersMap.get(dataId); if (listeners==null) { synchronized (listenersMap) { listeners = this.listenersMap.get(dataId); if (listeners==null) { listeners = Collections.synchronizedList(new ArrayList()); this.listenersMap.put(dataId, listeners); } } } return listeners; } /* * (non-Javadoc) * * @see * LoaderService#removeLoaderListener(org.javamoney * .moneta.spi.LoaderService.LoaderListener, java.lang.String[]) */ @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter") @Override public void removeLoaderListener(LoaderListener l, String... dataIds) { if (dataIds.length == 0) { List listeners = getListeners(""); synchronized (listeners) { listeners.remove(l); } } else { for (String dataId : dataIds) { List listeners = getListeners(dataId); synchronized (listeners) { listeners.remove(l); } } } } /* * (non-Javadoc) * * @see * LoaderService#getUpdatePolicy(java.lang.String) */ @Override public UpdatePolicy getUpdatePolicy(String resourceId) { LoadableResource load = this.resources.get(resourceId); if (load == null) { throw new IllegalArgumentException("No such resource: " + resourceId); } return load.getUpdatePolicy(); } /** * Create the schedule for the given {@link LoadableResource}. * * @param load the load item to be managed, not null. */ private void addScheduledLoad(final LoadableResource load) { Objects.requireNonNull(load); TimerTask task = new TimerTask() { @Override public void run() { try { load.load(); } catch (Exception e) { LOG.log(Level.SEVERE, "Failed to update remote resource: " + load.getResourceId(), e); } } }; Map props = load.getProperties(); if (props!=null) { String value = props.get("period"); long periodMS = parseDuration(value); value = props.get("delay"); long delayMS = parseDuration(value); if (periodMS > 0) { timer.scheduleAtFixedRate(task, delayMS, periodMS); } else { value = props.get("at"); if (value!=null) { List dates = parseDates(value); for(GregorianCalendar date:dates){ timer.schedule(task, date.getTime(), 3_600_000 * 24 /* daily */); } } } } } /** * Parse the dates of type HH:mm:ss:nnn, whereas minutes and smaller are * optional. * * @param value the input text * @return the parsed */ private List parseDates(String value) { String[] parts = value.split(","); List result = new ArrayList<>(); for (String part : parts) { if (part.isEmpty()) { continue; } String[] subparts = part.split(":"); GregorianCalendar cal = new GregorianCalendar(); for (int i = 0; i < subparts.length; i++) { switch (i) { case 0: cal.set(GregorianCalendar.HOUR_OF_DAY, Integer.parseInt(subparts[i])); break; case 1: cal.set(GregorianCalendar.MINUTE, Integer.parseInt(subparts[i])); break; case 2: cal.set(GregorianCalendar.SECOND, Integer.parseInt(subparts[i])); break; case 3: cal.set(GregorianCalendar.MILLISECOND, Integer.parseInt(subparts[i])); break; } } result.add(cal); } return result; } /** * Parse a duration of the form HH:mm:ss:nnn, whereas only hours are non * optional. * * @param value the input value * @return the duration in ms. */ protected long parseDuration(String value) { long periodMS = 0L; if (value!=null) { String[] parts = value.split(":"); for (int i = 0; i < parts.length; i++) { switch (i) { case 0: // hours periodMS += (Integer.parseInt(parts[i])) * 3600000L; break; case 1: // minutes periodMS += (Integer.parseInt(parts[i])) * 60000L; break; case 2: // seconds periodMS += (Integer.parseInt(parts[i])) * 1000L; break; case 3: // ms periodMS += (Integer.parseInt(parts[i])); break; default: break; } } } return periodMS; } @Override public String toString() { return "DefaultLoaderService [resources=" + resources + ']'; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy