001/* 002 * Copyright (c) 2012, 2014, Credit Suisse (Anatole Tresch), Werner Keil and others by the @author tag. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 005 * use this file except in compliance with the License. You may obtain a copy of 006 * the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 013 * License for the specific language governing permissions and limitations under 014 * the License. 015 */ 016package org.javamoney.moneta.spi.loader; 017 018import org.javamoney.moneta.spi.LoaderService; 019 020import javax.money.spi.Bootstrap; 021import java.io.IOException; 022import java.io.InputStream; 023import java.net.URI; 024import java.util.List; 025import java.util.Map; 026import java.util.Set; 027import java.util.Timer; 028import java.util.concurrent.*; 029import java.util.logging.Level; 030import java.util.logging.Logger; 031 032import org.javamoney.moneta.spi.LoadDataInformation; 033import org.javamoney.moneta.spi.LoadDataInformationBuilder; 034 035 036/** 037 * This class provides a mechanism to register resources, that may be updated 038 * regularly. The implementation, based on the {@link UpdatePolicy} 039 * loads/updates the resources from arbitrary locations and stores it to the 040 * format file cache. Default loading tasks can be configured within the javamoney.properties 041 * file, @see org.javamoney.moneta.loader.format.LoaderConfigurator . 042 * 043 * 044 * @author Anatole Tresch 045 */ 046public class DefaultLoaderService implements LoaderService { 047 /** 048 * Logger used. 049 */ 050 private static final Logger LOG = Logger.getLogger(DefaultLoaderService.class.getName()); 051 /** 052 * The data resources managed by this instance. 053 */ 054 private final Map<String, LoadableResource> resources = new ConcurrentHashMap<>(); 055 /** 056 * The registered {@link LoaderListener} instances. 057 */ 058 private final DefaultLoaderListener listener = new DefaultLoaderListener(); 059 060 /** 061 * The local resource cache, to allow keeping current data on the local 062 * system. 063 */ 064 private static final ResourceCache CACHE = loadResourceCache(); 065 /** 066 * The thread pool used for loading of data, triggered by the timer. 067 */ 068 private final ExecutorService executors = Executors.newCachedThreadPool(DaemonThreadFactory.INSTANCE); 069 070 private DefaultLoaderServiceFacade defaultLoaderServiceFacade; 071 072 /** 073 * The timer used for schedules. 074 */ 075 private volatile Timer timer; 076 077 /** 078 * Constructor, initializing from config. 079 */ 080 public DefaultLoaderService() { 081 initialize(); 082 } 083 084 /** 085 * This method reads initial loads from the javamoney.properties and installs the according timers. 086 */ 087 @Deprecated 088 protected void initialize() { 089 // Cancel any running tasks 090 Timer oldTimer = timer; 091 timer = new Timer(); 092 if (oldTimer!=null) { 093 oldTimer.cancel(); 094 } 095 // (re)initialize 096 LoaderConfigurator configurator = new LoaderConfigurator(this); 097 defaultLoaderServiceFacade = new DefaultLoaderServiceFacade(timer, listener, resources); 098 configurator.load(); 099 } 100 101 /** 102 * Loads the cache to be used. 103 * 104 * @return the cache to be used, not null. 105 */ 106 private static ResourceCache loadResourceCache() { 107 try { 108 ResourceCache cache = Bootstrap.getService(ResourceCache.class); 109 if (cache == null) { 110 cache = new DefaultResourceCache(); 111 } 112 return cache; 113 } catch (Exception e) { 114 LOG.log(Level.SEVERE, "Error loading ResourceCache instance.", e); 115 return new DefaultResourceCache(); 116 } 117 } 118 119 /** 120 * Get the resource cache loaded. 121 * 122 * @return the resource cache, not null. 123 */ 124 static ResourceCache getResourceCache() { 125 return DefaultLoaderService.CACHE; 126 } 127 128 /** 129 * Removes a resource managed. 130 * 131 * @param resourceId the resource id. 132 */ 133 public void unload(String resourceId) { 134 LoadableResource res = this.resources.get(resourceId); 135 if (res!=null) { 136 res.unload(); 137 } 138 } 139 140 /* 141 * (non-Javadoc) 142 * 143 * @see 144 * LoaderService#registerData(java.lang.String, 145 * LoaderService.UpdatePolicy, java.util.Map, 146 * java.net.URL, java.net.URL[]) 147 */ 148 @Override 149 public void registerData(LoadDataInformation loadDataInformation) { 150 151 if (resources.containsKey(loadDataInformation.getResourceId())) { 152 throw new IllegalArgumentException("Resource : " + loadDataInformation.getResourceId() + " already registered."); 153 } 154 155 LoadableResource resource = new LoadableResourceBuilder() 156 .withCache(CACHE).withLoadDataInformation(loadDataInformation) 157 .build(); 158 this.resources.put(loadDataInformation.getResourceId(), resource); 159 160 if (loadDataInformation.getLoaderListener() != null) { 161 this.addLoaderListener(loadDataInformation.getLoaderListener(), loadDataInformation.getResourceId()); 162 } 163 164 if(loadDataInformation.isStartRemote()) { 165 defaultLoaderServiceFacade.loadDataRemote(loadDataInformation.getResourceId(), resources); 166 } 167 switch (loadDataInformation.getUpdatePolicy()) { 168 case NEVER: 169 loadDataLocal(loadDataInformation.getResourceId()); 170 break; 171 case ONSTARTUP: 172 loadDataAsync(loadDataInformation.getResourceId()); 173 break; 174 case SCHEDULED: 175 defaultLoaderServiceFacade.scheduledData(resource); 176 break; 177 case LAZY: 178 default: 179 break; 180 } 181 } 182 183 @Override 184 public void registerAndLoadData(LoadDataInformation loadDataInformation) { 185 registerData(loadDataInformation); 186 loadData(loadDataInformation.getResourceId()); 187 } 188 189 @Override 190 public void registerAndLoadData(String resourceId, UpdatePolicy updatePolicy, Map<String, String> properties, LoaderListener loaderListener, URI backupResource, URI... resourceLocations) { 191 registerAndLoadData(new LoadDataInformationBuilder() 192 .withResourceId(resourceId) 193 .withUpdatePolicy(updatePolicy) 194 .withProperties(properties) 195 .withLoaderListener(loaderListener) 196 .withBackupResource(backupResource) 197 .withResourceLocations(resourceLocations) 198 .build()); 199 } 200 201 @Override 202 public void registerData(String resourceId, UpdatePolicy updatePolicy, Map<String, String> properties, LoaderListener loaderListener, URI backupResource, URI... resourceLocations) { 203 if (resources.containsKey(resourceId)) { 204 throw new IllegalArgumentException("Resource : " + resourceId + " already registered."); 205 } 206 LoadDataInformation loadInfo = new LoadDataInformationBuilder() 207 .withResourceId(resourceId) 208 .withUpdatePolicy(updatePolicy) 209 .withProperties(properties) 210 .withLoaderListener(loaderListener) 211 .withBackupResource(backupResource) 212 .withResourceLocations(resourceLocations) 213 .build(); 214 215 LoadableResource resource = new LoadableResourceBuilder() 216 .withCache(CACHE).withLoadDataInformation(loadInfo) 217 .build(); 218 this.resources.put(loadInfo.getResourceId(), resource); 219 220 if (loadInfo.getLoaderListener() != null) { 221 this.addLoaderListener(loadInfo.getLoaderListener(), loadInfo.getResourceId()); 222 } 223 224 switch (loadInfo.getUpdatePolicy()) { 225 case SCHEDULED: 226 defaultLoaderServiceFacade.scheduledData(resource); 227 break; 228 case LAZY: 229 default: 230 break; 231 } 232 loadData(resourceId); 233 } 234 235 @Override 236 public Map<String, String> getUpdateConfiguration(String resourceId) { 237 LoadableResource load = this.resources.get(resourceId); 238 if (load!=null) { 239 return load.getProperties(); 240 } 241 return null; 242 } 243 244 @Override 245 public boolean isResourceRegistered(String dataId) { 246 return this.resources.containsKey(dataId); 247 } 248 249 @Override 250 public Set<String> getResourceIds() { 251 return this.resources.keySet(); 252 } 253 254 @Override 255 public InputStream getData(String resourceId) throws IOException { 256 LoadableResource load = this.resources.get(resourceId); 257 if (load!=null) { 258 load.getDataStream(); 259 } 260 throw new IllegalArgumentException("No such resource: " + resourceId); 261 } 262 263 @Override 264 public boolean loadData(String resourceId) { 265 return defaultLoaderServiceFacade.loadData(resourceId, resources); 266 } 267 268 @Override 269 public Future<Boolean> loadDataAsync(final String resourceId) { 270 return executors.submit(new Callable<Boolean>() { 271 @Override 272 public Boolean call() throws Exception { 273 return defaultLoaderServiceFacade.loadData(resourceId, resources); 274 } 275 }); 276 } 277 278 @Override 279 public boolean loadDataLocal(String resourceId) { 280 return defaultLoaderServiceFacade.loadDataLocal(resourceId); 281 } 282 283 284 @Override 285 public void resetData(String resourceId) throws IOException { 286 LoadableResource load = this.resources.get(resourceId); 287 if (load == null) { 288 throw new IllegalArgumentException("No such resource: " + resourceId); 289 } 290 if (load.resetToFallback()) { 291 listener.trigger(resourceId, load.getDataStream()); 292 } 293 } 294 295 @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter") 296 @Override 297 public void addLoaderListener(LoaderListener l, String... dataIds) { 298 if (dataIds.length == 0) { 299 List<LoaderListener> listeners = listener.getListeners(""); 300 synchronized (listeners) { 301 listeners.add(l); 302 } 303 } else { 304 for (String dataId : dataIds) { 305 List<LoaderListener> listeners = listener.getListeners(dataId); 306 synchronized (listeners) { 307 listeners.add(l); 308 } 309 } 310 } 311 } 312 313 @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter") 314 @Override 315 public void removeLoaderListener(LoaderListener l, String... dataIds) { 316 if (dataIds.length == 0) { 317 List<LoaderListener> listeners = listener.getListeners(""); 318 synchronized (listeners) { 319 listeners.remove(l); 320 } 321 } else { 322 for (String dataId : dataIds) { 323 List<LoaderListener> listeners = listener.getListeners(dataId); 324 synchronized (listeners) { 325 listeners.remove(l); 326 } 327 } 328 } 329 } 330 331 @Override 332 public UpdatePolicy getUpdatePolicy(String resourceId) { 333 LoadableResource load = this.resources.get(resourceId); 334 if (load == null) { 335 throw new IllegalArgumentException("No such resource: " + resourceId); 336 } 337 return load.getUpdatePolicy(); 338 } 339 340 @Override 341 public String toString() { 342 return "DefaultLoaderService [resources=" + resources + ']'; 343 } 344 345 346}