org.phoebus.applications.eslog.archivedjmslog.MergedModel Maven / Gradle / Ivy
The newest version!
package org.phoebus.applications.eslog.archivedjmslog;
import java.lang.reflect.Array;
import java.lang.reflect.ParameterizedType;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.TemporalAmount;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import org.phoebus.applications.eslog.Activator;
import org.phoebus.util.time.TimeParser;
import org.phoebus.util.time.TimeRelativeInterval;
import org.phoebus.util.time.TimestampFormats;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
/** Merges archived data with live data from JMS. */
public class MergedModel
implements ArchiveModelListener, LiveModelListener
{
protected ArchiveModel archive;
protected LiveModel live;
protected ObservableList messages = FXCollections.observableArrayList();
protected List messagesChangedListeners = new LinkedList<>();
protected List> timeChangedListeners = new LinkedList<>();
protected TimeRelativeInterval time_range = TimeRelativeInterval
.startsAt(Duration.ofHours(8));
protected ScheduledExecutorService expireService;
protected PropertyFilter[] filters;
private Class parameterType;
@SuppressWarnings("unchecked")
public MergedModel(ArchiveModel archive, LiveModel live)
{
this.archive = archive;
if (null != this.archive)
{
this.archive.addListener(this);
}
this.live = live;
if (null != this.live)
{
this.live.addListener(this);
}
this.expireService = Executors.newSingleThreadScheduledExecutor();
this.expireService.scheduleAtFixedRate(this::expireMessages, 1, 1,
TimeUnit.MINUTES);
this.parameterType = ((Class) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0]);
}
public void addChangeListener(final Runnable r)
{
synchronized (this.messagesChangedListeners)
{
this.messagesChangedListeners.add(r);
}
}
public void addTimeChangeListener(final Consumer c)
{
synchronized (this.timeChangedListeners)
{
this.timeChangedListeners.add(c);
}
}
protected void expireMessages()
{
Instant cutoff;
try
{
cutoff = this.time_range.toAbsoluteInterval().getStart();
}
catch (IllegalArgumentException e)
{
// the error has been logged, and there is nothing more we can do
// about it.
return;
}
Activator.logger
.finer(String.format("Expiring from %s", cutoff.toString())); //$NON-NLS-1$
long expired = 0L;
synchronized (this.messages)
{
Iterator i = this.messages.iterator();
while (i.hasNext())
{
LogMessage msg = i.next();
if (cutoff.compareTo(msg.getTime()) < 0)
{
// the first message with a timestamp > cutoff.
// no need to look any further.
break;
}
i.remove();
++expired;
}
}
if (expired > 0)
{
Activator.logger
.fine(String.format("%d messages expired.", expired)); //$NON-NLS-1$
}
}
public PropertyFilter[] getFilters()
{
return this.filters;
}
protected void getFromArchive(Instant from, Instant to)
{
this.archive.refresh(from, to);
}
@SuppressWarnings("unchecked")
public T[] getMessages()
{
synchronized (this.messages)
{
return this.messages.toArray((T[]) Array
.newInstance(this.parameterType, this.messages.size()));
}
}
public TimeRelativeInterval getTimerange()
{
return this.time_range;
}
public String[] getTimerangeText()
{
final var start = this.time_range.isStartAbsolute()
? TimestampFormats.MILLI_FORMAT
.format(this.time_range.getAbsoluteStart().get())
: TimeParser.format(this.time_range.getRelativeStart().get());
final var end = this.time_range.isEndAbsolute()
? TimestampFormats.MILLI_FORMAT
.format(this.time_range.getAbsoluteEnd().get())
: TimeParser.format(this.time_range.getRelativeEnd().get());
return new String[] { start, end };
}
public boolean isNowMode()
{
return (!this.time_range.isEndAbsolute())
&& Duration.ZERO.equals(this.time_range.getRelativeEnd().get());
}
@Override
public void messagesRetrieved(ArchiveModel model)
{
this.messages.addAll(Arrays.asList(model.getMessages()));
notifyListeners();
}
@Override
public void newMessage(T msg)
{
// ignore the message if we are not in "NOW" mode.
if (!isNowMode())
{
return;
}
synchronized (this.messages)
{
this.messages.add(msg);
}
notifyListeners();
}
protected void notifyListeners()
{
synchronized (this.messagesChangedListeners)
{
this.messagesChangedListeners.forEach(Runnable::run);
}
}
protected void notifyTimeChangedListeners()
{
synchronized (this.timeChangedListeners)
{
this.timeChangedListeners.forEach(l -> l.accept(this.time_range));
}
}
/**
* Define the filters to use when receiving messages.
*
* The model will be updates from the archive.
*
* @param filters
* The new filter definitions.
*/
public void setFilters(PropertyFilter[] filters)
{
this.filters = filters;
if (null != this.live)
{
this.live.setFilters(filters);
}
if (null != this.archive)
{
this.archive.setFilters(filters);
}
updateFromArchive();
}
/**
* Define the time interval to represent in the model.
*
* @param start_spec
* The start time.
* @param end_spec
* The end time. Set to value RelativeTime#NOW to enable the
* reception of live messages via JMS.
*/
public void setTimerange(final String start_spec, final String end_spec)
throws IllegalArgumentException
{
Activator.checkParameterString(start_spec, "start_spec"); //$NON-NLS-1$
Activator.checkParameterString(end_spec, "end_spec"); //$NON-NLS-1$
// Instant, or Duration/Period (both derived from TemporalAmount)
final var start = TimeParser.parseInstantOrTemporalAmount(start_spec);
Instant start_instant = null;
TemporalAmount start_amount = null;
if (start instanceof Instant)
start_instant = (Instant) start;
else
start_amount = (TemporalAmount) start;
final var end = TimeParser.parseInstantOrTemporalAmount(end_spec);
Instant end_instant = null;
TemporalAmount end_amount = null;
if (end instanceof Instant)
end_instant = (Instant) end;
else
end_amount = (TemporalAmount) end;
if ((null != start_instant) && (null != end_instant))
this.time_range = TimeRelativeInterval.of(start_instant,
end_instant);
if ((null != start_instant) && (null == end_instant))
this.time_range = TimeRelativeInterval.of(start_instant,
end_amount);
if ((null == start_instant) && (null != end_instant))
this.time_range = TimeRelativeInterval.of(start_amount,
end_instant);
if ((null == start_instant) && (null == end_instant))
this.time_range = TimeRelativeInterval.of(start_amount, end_amount);
notifyTimeChangedListeners();
updateFromArchive();
}
public void setTimerange(final TimeRelativeInterval interval)
{
Activator.checkParameter(interval, "interval"); //$NON-NLS-1$
this.time_range = interval;
notifyTimeChangedListeners();
updateFromArchive();
}
/**
* Trigger an update from the archive.
*/
public void updateFromArchive()
{
this.messages.clear();
if (null != this.live)
{
if (isNowMode())
{
this.live.start();
}
else
{
this.live.stop();
}
}
if (null != this.archive)
{
this.archive.refresh(this.time_range.toAbsoluteInterval());
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy