/*******************************************************************************
* Copyright (c) 2007, 2014 compeople AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* compeople AG - initial API and implementation
*******************************************************************************/
package org.eclipse.riena.monitor.client;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.osgi.service.log.LogService;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExecutableExtension;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.equinox.log.Logger;
import org.eclipse.riena.core.Log4r;
import org.eclipse.riena.core.util.Literal;
import org.eclipse.riena.core.util.Millis;
import org.eclipse.riena.core.util.PropertiesUtils;
import org.eclipse.riena.core.wire.InjectService;
import org.eclipse.riena.internal.monitor.client.Activator;
import org.eclipse.riena.monitor.common.Collectible;
import org.eclipse.riena.monitor.common.IReceiver;
/**
* This simple sender implements {@code ISender} that uses riena�s (remote)
* services to communicate with the �server�.
* <p>
* The simple sender expects the following configuration that can be passed with
* its definition in an extension:
* <ul>
* <li>retryTime - defines the time in minutes that will be waited for a retry
* when a send has failed. (default value is 15 minutes if not defined)</li>
* </ul>
* Periods of time can be specified as a string conforming to
* {@link Millis#valueOf(String)}.<br>
* Example extension:
*
* <pre>
* <extension point="org.eclipse.riena.monitor.client.sender">
* <sender
* name="SimpleSender"
* class="org.eclipse.riena.monitor.client.SimpleSender:retryTime=20 m">
* </sender>
* </extension>
* </pre>
*/
public class SimpleSender implements ISender, IExecutableExtension {
private IStore store;
private IReceiver receiver;
private boolean started;
private long retryTime;
private final Map<String, Sender> senders = new HashMap<String, Sender>();
private static final String RETRY_TIME = "retryTime"; //$NON-NLS-1$
private static final String RETRY_TIME_DEFAULT = "15 m"; //$NON-NLS-1$
private static final Logger LOGGER = Log4r.getLogger(Activator.getDefault(), SimpleSender.class);
public void setInitializationData(final IConfigurationElement config, final String propertyName, final Object data)
throws CoreException {
Map<String, String> properties = null;
try {
properties = PropertiesUtils.asMap(data, Literal.map(RETRY_TIME, RETRY_TIME_DEFAULT));
retryTime = Millis.valueOf(properties.get(RETRY_TIME));
Assert.isLegal(retryTime > 0, "retryTime must be greater than 0."); //$NON-NLS-1$
} catch (final IllegalArgumentException e) {
throw configurationException("Bad configuration.", e); //$NON-NLS-1$
}
}
private CoreException configurationException(final String message, final Exception e) {
return new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, message, e));
}
@InjectService(useRanking = true)
public void bind(final IReceiver receiver) {
this.receiver = receiver;
}
public void unbind(final IReceiver receiver) {
this.receiver = null;
}
public void start(final IStore store, final Collection<Category> categories) {
if (started) {
return;
}
Assert.isNotNull(store, "store must not be null"); //$NON-NLS-1$
Assert.isNotNull(categories, "categories must not be null"); //$NON-NLS-1$
this.store = store;
started = true;
for (final Category category : categories) {
final Sender sender = new Sender(category.getName());
senders.put(category.getName(), sender);
// check if there are remaining {@code Collectible}s, if so trigger transfer in the background with a slight delay
sender.tryIt(Millis.seconds(5));
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.riena.monitor.client.ISender#stop()
*/
public void stop() {
if (!started) {
return;
}
started = false;
for (final Sender sender : senders.values()) {
sender.cancel();
}
senders.clear();
}
public synchronized void triggerTransfer(final String categoryName) {
if (!started) {
return;
}
final Sender sender = senders.get(categoryName);
if (sender == null) {
return;
}
sender.tryIt(0);
}
private final class Sender extends Job {
private final String category;
private boolean retrying;
private Sender(final String category) {
super("SimpleSender"); //$NON-NLS-1$
this.category = category;
}
private void tryIt(final long delay) {
if (retrying) {
LOGGER.log(LogService.LOG_DEBUG, "Sender(" + category + ") retry already scheduled."); //$NON-NLS-1$ //$NON-NLS-2$
return;
}
schedule(delay);
}
/*
* (non-Javadoc)
*
* @seeorg.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.
* IProgressMonitor)
*/
@Override
protected IStatus run(final IProgressMonitor monitor) {
LOGGER.log(LogService.LOG_DEBUG, "Sender(" + category + ") started with" + (retrying ? "" : "out") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ " retry"); //$NON-NLS-1$
if (receiver == null) {
LOGGER.log(LogService.LOG_DEBUG, "Sender(" + category + ") ended (no receiver)"); //$NON-NLS-1$ //$NON-NLS-2$
return Status.OK_STATUS;
}
final List<Collectible<?>> transferables = store.retrieveTransferables(category);
if (transferables.size() == 0) {
LOGGER.log(LogService.LOG_DEBUG, "Sender(" + category + ") ended (nothing to send)"); //$NON-NLS-1$ //$NON-NLS-2$
return Status.OK_STATUS;
}
transfer(transferables);
LOGGER.log(LogService.LOG_DEBUG, "Sender(" + category + ") ended"); //$NON-NLS-1$ //$NON-NLS-2$
return Status.OK_STATUS;
}
/**
*
*/
private void transfer(final List<Collectible<?>> transferables) {
LOGGER.log(LogService.LOG_DEBUG, "sender transfer " + transferables.size() + " transferables:"); //$NON-NLS-1$ //$NON-NLS-2$
for (final Collectible<?> transferable : transferables) {
LOGGER.log(LogService.LOG_DEBUG, " - " + transferable.toLogString()); //$NON-NLS-1$
}
try {
if (receiver.take(System.currentTimeMillis(), transferables)) {
store.commitTransferred(transferables);
retrying = false;
} else {
throw new RuntimeException("Retry sending later because receiver rejected it."); //$NON-NLS-1$
}
} catch (final Throwable t) {
LOGGER.log(LogService.LOG_DEBUG, "sending failed with: " + condense(t)); //$NON-NLS-1$
LOGGER.log(LogService.LOG_DEBUG, "retrying in " + retryTime + " milli seconds"); //$NON-NLS-1$ //$NON-NLS-2$
retrying = true;
schedule(retryTime);
}
}
private final static String CAUSED_BY = " Caused by: "; //$NON-NLS-1$
private String condense(Throwable throwable) {
final StringBuilder bob = new StringBuilder();
do {
bob.append(throwable.toString()).append(CAUSED_BY);
throwable = throwable.getCause();
} while (throwable != null);
bob.setLength(bob.length() - CAUSED_BY.length());
return bob.toString();
}
}
}