/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.communication.rpc.internal;
import java.lang.ref.WeakReference;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import org.apache.commons.logging.LogFactory;
import org.osgi.framework.BundleContext;
import de.rcenvironment.core.communication.api.CommunicationService;
import de.rcenvironment.core.communication.common.InstanceNodeSessionId;
import de.rcenvironment.core.communication.rpc.api.CallbackProxyService;
import de.rcenvironment.core.communication.rpc.api.CallbackService;
import de.rcenvironment.core.communication.rpc.api.RemotableCallbackService;
import de.rcenvironment.core.toolkitbridge.transitional.ConcurrencyUtils;
import de.rcenvironment.core.utils.common.ServiceUtils;
import de.rcenvironment.core.utils.common.rpc.RemoteOperationException;
import de.rcenvironment.toolkit.modules.concurrency.api.TaskDescription;
/**
* Background task which sets the time to live (TTL) for call back objects and call back proxy objects which are held by
* {@link CallbackService} and {@link CallbackProxyService}. Additionally, it removes objects and proxies which time to live is expired.
*
* @author Doreen Seider
* @author Robert Mischke
*/
public class CleanJob {
/** Represents 10 min. */
public static final int TTL_MSEC = 600000;
/** Represents 8 min. */
public static final int UPDATE_INTERVAL_MSEC = 480000;
/** Key for the service to call contained in the job context. */
public static final String SERVICE = "de.rcenvironment.rce.communication.callback.service";
/** Key for the weak object references map contained in the job context. */
public static final String WEAK_MAP = "de.rcenvironment.rce.communication.callback.weak";
/** Key for the TTL map contained in the job context. */
public static final String TTL_MAP = "de.rcenvironment.rce.communication.callback.ttl";
/** Key for the platforms (to call) map contained in the job context. */
public static final String PLATFORMS_MAP = "de.rcenvironment.rce.communication.callback.platforms";
private static CommunicationService communicationService;
private static Map<String, ScheduledFuture<?>> scheduleAtFixedRate = new HashMap<String, ScheduledFuture<?>>();
/** Only called by OSGi. */
@Deprecated
public CleanJob() {}
protected void activate(BundleContext bundleContext) {}
protected void bindCommunicationService(CommunicationService newCommunicationService) {
communicationService = newCommunicationService;
}
protected void unbindCommunicationService(CommunicationService oldCommunicationService) {
communicationService = ServiceUtils.createFailingServiceProxy(CommunicationService.class);
}
/**
* Cleans callback objects and callback proxies.
*
* @author Doreen Seider
*/
protected static class CleanRunnable implements Runnable {
private final Class<?> iface;
private final Map<String, WeakReference<Object>> objects;
private final Map<String, InstanceNodeSessionId> nodes;
private final Map<String, Long> ttls;
protected CleanRunnable(Class<?> iface, Map<String, WeakReference<Object>> objects,
Map<String, Long> ttls, Map<String, InstanceNodeSessionId> nodes) {
this.iface = iface;
this.objects = objects;
this.nodes = nodes;
this.ttls = ttls;
}
@Override
@TaskDescription("Communication Layer: Purge old callback objects/proxies and renew TTL for remaining")
public void run() {
// remove all unreferenced and expired objects and renew TTL for all remaining objects
synchronized (objects) {
for (Iterator<String> iterator = objects.keySet().iterator(); iterator.hasNext();) {
String id = iterator.next();
if (objects.get(id).get() == null || new Date(ttls.get(id)).before(new Date())) {
iterator.remove();
ttls.remove(id);
nodes.remove(id);
} else {
try {
if (iface == CallbackProxyService.class) {
RemotableCallbackService remoteService =
(RemotableCallbackService) communicationService.getRemotableService(RemotableCallbackService.class,
nodes.get(id));
remoteService.setTTL(id, ttls.get(id));
} else if (iface == CallbackService.class) {
// this code path doesn't seem to be used anymore; replacing with exception to test - misc_ro, Oct 2015
throw new RemoteOperationException("Unexpected callback code path used");
// CallbackProxyService service = (CallbackProxyService) communicationService
// .getService(CallbackProxyService.class, nodes.get(id), context);
// service.setTTL(id, new Date(System.currentTimeMillis() + CleanJob.TTL_MSEC).getTime());
}
} catch (RemoteOperationException | RuntimeException e) {
// temporary fix for remote call failures;
// see https://www.sistec.dlr.de/mantis/view.php?id=6542
LogFactory.getLog(getClass()).debug("Failed to update TTL for id " + id + " via " + iface.getSimpleName()
+ " @ " + nodes.get(id));
}
}
}
}
}
}
/**
* Schedules a clean job.
*
* @param iface The service's iface to call for TTL update.
* @param objects The referenced objects.
* @param ttls The TTLs of the references objects.
* @param platforms The platforms to call for TTL update.
*/
@SuppressWarnings({ "rawtypes" })
public static void scheduleJob(Class iface,
Map<String, WeakReference<Object>> objects,
Map<String, Long> ttls,
Map<String, InstanceNodeSessionId> platforms) {
synchronized (CleanJob.class) {
if (!scheduleAtFixedRate.containsKey(iface.getCanonicalName())) {
CleanRunnable runnable = new CleanRunnable(iface, objects, ttls, platforms);
scheduleAtFixedRate.put(iface.getCanonicalName(),
ConcurrencyUtils.getAsyncTaskService().scheduleAtFixedRate(runnable, CleanJob.UPDATE_INTERVAL_MSEC));
}
}
}
/**
* Unschedules a clean job.
*
* @param iface The service's iface to call for TTL update.
*/
@SuppressWarnings({ "rawtypes" })
public static void unscheduleJob(Class iface) {
synchronized (CleanJob.class) {
if (scheduleAtFixedRate.containsKey(iface.getCanonicalName())) {
boolean cancelled = scheduleAtFixedRate.get(iface.getCanonicalName()).cancel(true);
if (!cancelled) {
LogFactory.getLog(CleanJob.class).warn("Clean job triggered by " + iface.getCanonicalName()
+ " could not be cancelled. Probably, it was already done.");
}
}
}
}
}