package org.jactr.eclipse.runtime.ui.log2.live; /* * default logging */ import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.locks.ReentrantLock; import javolution.util.FastList; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.jactr.eclipse.runtime.log2.ILogSessionDataStream; import org.jactr.eclipse.runtime.log2.LogData; import org.jactr.eclipse.runtime.session.stream.ILiveSessionDataStream; import org.jactr.eclipse.runtime.session.stream.ILiveSessionDataStreamListener; import org.jactr.eclipse.ui.concurrent.QueueingUIJob; public class LiveLogDataContentProvider implements IStructuredContentProvider { /** * Logger definition */ static private final transient Log LOGGER = LogFactory .getLog(LiveLogDataContentProvider.class); private final static long MINIMUM_RESPONSIVENESS = 300; // ms private TableViewer _viewer; private ILogSessionDataStream _logData; private ILiveSessionDataStreamListener<LogData> _liveListener; private ReentrantLock _lock = new ReentrantLock(); private FastList<LogData> _pendingRemovals; private FastList<LogData> _pendingAdds; private FastList<LogData> _pendingUpdates; private Set<String> _knownColumns; private QueueingUIJob _updater; private List<ColumnListener> _columnListeners = new LinkedList<ColumnListener>(); public LiveLogDataContentProvider(TableViewer viewer) { _viewer = viewer; _knownColumns = new TreeSet<String>(); _pendingRemovals = FastList.newInstance(); _pendingAdds = FastList.newInstance(); _pendingUpdates = FastList.newInstance(); _liveListener = new ILiveSessionDataStreamListener<LogData>() { public void dataChanged(ILiveSessionDataStream stream, Collection<LogData> added, Collection<LogData> modified, Collection<LogData> removed) { /* * we queue up the remove and adds */ _lock.lock(); try { _pendingRemovals.addAll(removed); _pendingAdds.addAll(added); _pendingUpdates.addAll(modified); } finally { _lock.unlock(); } _updater.queue(MINIMUM_RESPONSIVENESS); } }; _updater = new QueueingUIJob("Log Populator") { @Override public IStatus runInUIThread(IProgressMonitor monitor) { processChanges(monitor); return Status.OK_STATUS; } }; // hide it from the user _updater.setSystem(true); } public void dispose() { FastList.recycle(_pendingAdds); FastList.recycle(_pendingRemovals); FastList.recycle(_pendingUpdates); } @SuppressWarnings("unchecked") public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { if (newInput != null) { _logData = (ILogSessionDataStream) newInput; if (_logData instanceof ILiveSessionDataStream) ((ILiveSessionDataStream) _logData).addListener(_liveListener, // new SWTExecutor()); //this may have been killing our performance.. null); } else /* * remove */ if (_logData instanceof ILiveSessionDataStream) ((ILiveSessionDataStream) _logData).removeListener(_liveListener); } public Object[] getElements(Object inputElement) { FastList<LogData> data = FastList.newInstance(); try { _logData.getData(_logData.getStartTime(), _logData.getEndTime(), data); return data.toArray(); } finally { FastList.recycle(data); } } private void snagData(Collection<LogData> source, Collection<LogData> dest) { _lock.lock(); try { dest.addAll(source); source.clear(); } finally { _lock.unlock(); } } protected void processChanges(IProgressMonitor monitor) { FastList<LogData> pending = FastList.newInstance(); LogData lastData = null; try { int total = _pendingAdds.size() + _pendingRemovals.size() + _pendingUpdates.size(); monitor.beginTask("Updating log", total); snagData(_pendingAdds, pending); if (pending.size() > 0) { monitor.subTask(String.format("Adding %d rows", pending.size())); lastData = pending.getLast(); verifyColumns(lastData); _viewer.add(pending.toArray()); monitor.worked(pending.size()); pending.clear(); } snagData(_pendingUpdates, pending); if (pending.size() > 0) { monitor.subTask(String.format("Updating %d rows", pending.size())); _viewer.update(pending.toArray(), null); monitor.worked(pending.size()); pending.clear(); } snagData(_pendingRemovals, pending); if (pending.size() > 0) { monitor.subTask(String.format("Removing %d rows", pending.size())); _viewer.remove(pending.toArray()); monitor.worked(pending.size()); } } finally { FastList.recycle(pending); if (lastData != null) { Table table = _viewer.getTable(); int item = table.getItemCount() - 1; // disable automatic selection for performance reasons // table.select(item); // table.showSelection(); table.showItem(table.getItem(item)); } monitor.done(); } } /** * make sure we have columns for everyone * * @param data */ protected void verifyColumns(LogData data) { if (_knownColumns.size() == 0) for (TableColumn column : _viewer.getTable().getColumns()) _knownColumns.add(column.getText()); Set<String> streamsInData = data.getStreamNames(); for (String stream : streamsInData) if (!_knownColumns.contains(stream)) { /* * new column */ Table table = _viewer.getTable(); TableColumn column = new TableColumn(table, SWT.LEFT); column.setText(stream); _knownColumns.add(stream); for(ColumnListener listener: _columnListeners) listener.added(column); } } public void addColumnListener(ColumnListener listener) { _columnListeners.add(listener); } public void removeColumnListener(ColumnListener listener) { _columnListeners.remove(listener); } }