package net.sourceforge.tagsea.instrumentation; import java.io.IOException; import java.text.DateFormat; import java.util.Date; import java.util.Locale; import java.util.Timer; import java.util.TimerTask; import java.util.TreeSet; import net.sourceforge.tagsea.TagSEAPlugin; import net.sourceforge.tagsea.core.ITag; import net.sourceforge.tagsea.core.ITagChangeEvent; import net.sourceforge.tagsea.core.ITagChangeListener; import net.sourceforge.tagsea.core.ITagSEAOperationStateListener; import net.sourceforge.tagsea.core.IWaypoint; import net.sourceforge.tagsea.core.IWaypointChangeEvent; import net.sourceforge.tagsea.core.IWaypointChangeListener; import net.sourceforge.tagsea.core.TagDelta; import net.sourceforge.tagsea.core.TagSEAOperation; import net.sourceforge.tagsea.core.WaypointDelta; import net.sourceforge.tagsea.core.ui.internal.ITagSEAUIListener; import net.sourceforge.tagsea.core.ui.internal.TagSEAUI; import net.sourceforge.tagsea.core.ui.internal.TagSEAUIEvent; import net.sourceforge.tagsea.instrumentation.network.NetworkSendJob; import net.sourceforge.tagsea.logging.DeletedTagEvent; import net.sourceforge.tagsea.logging.DeletedWaypointEvent; import net.sourceforge.tagsea.logging.FiltersChangedEvent; import net.sourceforge.tagsea.logging.JobEvent; import net.sourceforge.tagsea.logging.JobState; import net.sourceforge.tagsea.logging.LoggingFactory; import net.sourceforge.tagsea.logging.ModelEvent; import net.sourceforge.tagsea.logging.NavigateEvent; import net.sourceforge.tagsea.logging.NewTagEvent; import net.sourceforge.tagsea.logging.NewWaypointEvent; import net.sourceforge.tagsea.logging.TagNameChangeEvent; import net.sourceforge.tagsea.logging.TagWaypointsChangedEvent; import net.sourceforge.tagsea.logging.TaskNavigateEvent; import net.sourceforge.tagsea.logging.UIEvent; import net.sourceforge.tagsea.logging.ViewEvent; import net.sourceforge.tagsea.logging.ViewEventType; import net.sourceforge.tagsea.logging.WaypointAttribute; import net.sourceforge.tagsea.logging.WaypointChangeEvent; import net.sourceforge.tagsea.logging.WaypointState; import net.sourceforge.tagsea.logging.WaypointTagNameChangeEvent; import net.sourceforge.tagsea.logging.WaypointTagsChangedEvent; import org.eclipse.core.resources.ISaveContext; import org.eclipse.core.resources.ISaveParticipant; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IViewPart; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.eclipse.ui.views.markers.internal.TaskMarker; import org.osgi.framework.BundleContext; /** * The activator class controls the plug-in life cycle */ public class TagSEAInstrumentationPlugin extends AbstractUIPlugin implements ISaveParticipant { // The plug-in ID public static final String PLUGIN_ID = "net.sourceforge.tagsea.instrumentation"; private TagSEAModelListener modelListener; private TagSEAUIListener uiListener; private Timer uploadTimer; private boolean logging; // The shared instance private static TagSEAInstrumentationPlugin plugin; private LogSink sink; /** * Periodic task for uploading TagSEA logs. * @author Del Myers * */ private final class UploadTask extends TimerTask { @Override public void run() { TagSEAPlugin.run(new NetworkSendJob(), true); Date date = InstrumentationPreferences.getNextSendDate(); //getLog().log(new Status(Status.INFO, PLUGIN_ID, "Scheduling send for " + date.toString())); if (date != null) { uploadTimer.schedule(new UploadTask(), date); } } } private static class TagSEAModelListener implements IWaypointChangeListener, ITagChangeListener, ITagSEAOperationStateListener { /* (non-Javadoc) * @see net.sourceforge.tagsea.core.IWaypointChangeListener#waypointsChanged(net.sourceforge.tagsea.core.WaypointDelta) */ public void waypointsChanged(WaypointDelta delta) { for (IWaypointChangeEvent event : delta.getChanges()) { TagSEAInstrumentationPlugin.getDefault().logModelEvent(event); } } /* (non-Javadoc) * @see net.sourceforge.tagsea.core.ITagChangeListener#tagsChanged(net.sourceforge.tagsea.core.TagDelta) */ public void tagsChanged(TagDelta delta) { for (ITagChangeEvent event : delta.getEvents()) { TagSEAInstrumentationPlugin.getDefault().logModelEvent(event); } } /* (non-Javadoc) * @see net.sourceforge.tagsea.core.ITagSEAOperationStateListener#stateChanged(net.sourceforge.tagsea.core.TagSEAOperation) */ public void stateChanged(TagSEAOperation operation) { TagSEAInstrumentationPlugin.getDefault().logOperationState(operation); } } private static class TagSEAUIListener implements ITagSEAUIListener { public void eventPerformed(TagSEAUIEvent event) { TagSEAInstrumentationPlugin.getDefault().logUIEvent(event); } } /** * Listens to preferences to start/stop listening to model events and logging. * @author Del Myers * */ private class PreferenceListener implements IPreferenceChangeListener { public void preferenceChange(PreferenceChangeEvent event) { if (InstrumentationPreferences.MONITORING.equals(event.getKey())) { if (InstrumentationPreferences.isMonitoringEnabled()) { initializeLogging(); } else { quitLogging(); } } else if (InstrumentationPreferences.SEND_INTERVAL.equals(event.getKey())) { if (InstrumentationPreferences.isUploadEnabled()) { startUploadTimer(); } else { stopUploadTimer(); } } } } /** * The constructor */ public TagSEAInstrumentationPlugin() { plugin = this; this.sink = new LogSink(); } /* * (non-Javadoc) * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) */ public void start(BundleContext context) throws Exception { super.start(context); Date today = DateUtils.today(); Date tomorrow = new Date(today.getTime() + DateUtils.ONE_DAY); //save waypoints once a day. Timer timer = new Timer("Logging Waypoint State"); timer.schedule(new TimerTask(){ @Override public void run() { try { sink.saveWaypoints(); } catch (IOException e) { log(e); } }}, tomorrow, DateUtils.ONE_DAY); } /** * Saves a log of the current state of the waypoints in the user's workspace. * */ @SuppressWarnings("unchecked") /** * Returns the shared instance * * @return the shared instance */ public static TagSEAInstrumentationPlugin getDefault() { return plugin; } public void log(Exception e) { if (e instanceof CoreException) { getLog().log(((CoreException)e).getStatus()); } else { String message = e.getMessage(); if (message == null) { message = ""; } Status status = new Status( Status.ERROR, PLUGIN_ID, Status.ERROR, message, e ); getLog().log(status); } } /** * @param marker */ @SuppressWarnings("restriction") public void logTaskEvent(TaskMarker marker) { TaskNavigateEvent event = LoggingFactory.eINSTANCE.createTaskNavigateEvent(); event.setTime(System.currentTimeMillis()); event.setLine(marker.getLine()); event.setResource(marker.getResource().getFullPath().toPortableString()); event.setDescription(marker.getDescription()); sink.consumeEvent(event); } /** * @param event */ @SuppressWarnings("unchecked") public void logUIEvent(TagSEAUIEvent event) { //System.err.print("U"); UIEvent uievent = null; switch (event.type) { case TagSEAUIEvent.NAVIGATE: NavigateEvent ne = LoggingFactory.eINSTANCE.createNavigateEvent(); ne.setTime(event.time); ne.setWaypoint(createWaypointState(event.waypoint)); uievent = ne; break; case TagSEAUIEvent.FILTER: FiltersChangedEvent fe = LoggingFactory.eINSTANCE.createFiltersChangedEvent(); fe.setTime(event.time); String detail = event.detail; if (detail != null) { String[] names = detail.split(","); String[] waypointTypes = TagSEAPlugin.getDefault().getWaypointTypes(); TreeSet<String> hiddenTypes = new TreeSet<String>(); for (String name : names) { String value = name.trim(); fe.getHidden().add(value); hiddenTypes.add(value); } for (String type : waypointTypes) { if (!hiddenTypes.contains(type)) { fe.getVisible().add(type); } } } uievent = fe; break; case TagSEAUIEvent.VIEW: ViewEvent ve = LoggingFactory.eINSTANCE.createViewEvent(); ve.setFilterString(""); ve.setTime(event.time); if (event.viewId != null) { ve.setViewid(event.viewId); } switch (event.kind) { case TagSEAUIEvent.VIEW_ACTIVATED: ve.setType(ViewEventType.ACTIVATED_LITERAL); break; case TagSEAUIEvent.VIEW_DEACTIVATED: ve.setType(ViewEventType.DEACTIVATED_LITERAL); break; case TagSEAUIEvent.VIEW_CLOSED: ve.setType(ViewEventType.CLOSED_LITERAL); break; case TagSEAUIEvent.VIEW_OPENED: ve.setType(ViewEventType.OPENED_LITERAL); break; case TagSEAUIEvent.VIEW_HIDDEN: ve.setType(ViewEventType.HIDDEN_LITERAL); break; case TagSEAUIEvent.VIEW_TOP: ve.setType(ViewEventType.TOP_LITERAL); break; case TagSEAUIEvent.VIEW_FILTERED: ve.setType(ViewEventType.FILTERED_LITERAL); ve.setFilterString(event.detail); break; case TagSEAUIEvent.VIEW_HIERARCHY: ve.setType(ViewEventType.HIERARCHY_LITERAL); ve.setHierarchyOn(Boolean.parseBoolean(event.detail)); } if (event.detail != null) { ve.setFilterString(event.detail); } uievent = ve; break; } if (uievent != null) { sink.consumeEvent(uievent); } } @SuppressWarnings("unchecked") public void logModelEvent(IWaypointChangeEvent event) { //System.err.print("W"); ModelEvent ev = null; switch (event.getType()) { case IWaypointChangeEvent.CHANGE: WaypointChangeEvent wce = LoggingFactory.eINSTANCE.createWaypointChangeEvent(); wce.setTime(System.currentTimeMillis()); wce.setWaypoint(createWaypointState(event.getWaypoint())); for (String attr : event.getChangedAttributes()) { WaypointAttribute na = LoggingFactory.eINSTANCE.createWaypointAttribute(); WaypointAttribute oa = LoggingFactory.eINSTANCE.createWaypointAttribute(); na.setName(attr); oa.setName(attr); na.setValue(getValueAsString(event.getNewValue(attr))); oa.setValue(getValueAsString(event.getOldValue(attr))); } ev = wce; break; case IWaypointChangeEvent.NEW: NewWaypointEvent ne = LoggingFactory.eINSTANCE.createNewWaypointEvent(); ne.setTime(System.currentTimeMillis()); ne.setWaypoint(createWaypointState(event.getWaypoint())); ev = ne; break; case IWaypointChangeEvent.DELETE: DeletedWaypointEvent de = LoggingFactory.eINSTANCE.createDeletedWaypointEvent(); de.setTime(System.currentTimeMillis()); de.setWaypoint(createWaypointState(event.getWaypoint())); ev = de; break; case IWaypointChangeEvent.TAG_NAME_CHANGED: WaypointTagNameChangeEvent tne = LoggingFactory.eINSTANCE.createWaypointTagNameChangeEvent(); tne.setTime(System.currentTimeMillis()); tne.setWaypoint(createWaypointState(event.getWaypoint())); tne.setNewTagName(tne.getNewTagName()); tne.setOldTagName(event.getOldTagName()); ev = tne; break; case IWaypointChangeEvent.TAGS_CHANGED: WaypointTagsChangedEvent tce = LoggingFactory.eINSTANCE.createWaypointTagsChangedEvent(); tce.setTime(System.currentTimeMillis()); tce.setWaypoint(createWaypointState(event.getWaypoint())); for (String t : event.getOldTags()) { tce.getOldTags().add(t); } ev = tce; break; } if (ev != null) { sink.consumeEvent(ev); } } @SuppressWarnings("unchecked") public void logModelEvent(ITagChangeEvent event) { //System.err.print("T"); ModelEvent me = null; switch (event.getType()) { case ITagChangeEvent.NEW: NewTagEvent nte = LoggingFactory.eINSTANCE.createNewTagEvent(); nte.setTime(System.currentTimeMillis()); nte.setTagName(event.getTag().getName()); me = nte; break; case ITagChangeEvent.DELETED: DeletedTagEvent dte = LoggingFactory.eINSTANCE.createDeletedTagEvent(); dte.setTime(System.currentTimeMillis()); dte.setTagName(event.getTag().getName()); me = dte; break; case ITagChangeEvent.NAME: TagNameChangeEvent tne = LoggingFactory.eINSTANCE.createTagNameChangeEvent(); tne.setTime(System.currentTimeMillis()); tne.setOldName(event.getOldName()); tne.setTagName(event.getNewName()); me = tne; break; case ITagChangeEvent.WAYPOINTS: TagWaypointsChangedEvent twe = LoggingFactory.eINSTANCE.createTagWaypointsChangedEvent(); for (IWaypoint wp : event.getOldWaypoints()) { twe.getOldWaypoint().add(createWaypointState(wp)); } for (IWaypoint wp : event.getNewWaypoints()) { twe.getNewWaypoint().add(createWaypointState(wp)); } twe.setTime(System.currentTimeMillis()); twe.setTagName(event.getTag().getName()); me = twe; break; } if (me != null) { sink.consumeEvent(me); } } /** * Logs the state of the given operation. * @param op */ @SuppressWarnings("unchecked") public void logOperationState(TagSEAOperation op) { //System.err.print("O"); JobEvent je = LoggingFactory.eINSTANCE.createJobEvent(); je.setName(op.getName()); je.setTime(System.currentTimeMillis()); switch (op.getState()) { case CREATED: je.setState(JobState.CREATED_LITERAL); break; case QUEUED: je.setState(JobState.QUEUED_LITERAL); break; case WAITING: je.setState(JobState.QUEUED_LITERAL); break; case QUITING: je.setState(JobState.QUITING_LITERAL); break; case RUNNING: je.setState(JobState.RUNNING_LITERAL); break; case DONE: je.setState(JobState.DONE_LITERAL); break; } sink.consumeEvent(je); } @SuppressWarnings("unchecked") static WaypointState createWaypointState(IWaypoint waypoint) { WaypointState state = LoggingFactory.eINSTANCE.createWaypointState(); state.setWaypointType(waypoint.getType()); for (String attr : waypoint.getAttributes()) { WaypointAttribute attribute = LoggingFactory.eINSTANCE.createWaypointAttribute(); Object value = waypoint.getValue(attr); attribute.setName(attr); attribute.setValue(getValueAsString(value)); state.getAttributes().add(attribute); } for (ITag tag : waypoint.getTags()) { state.getTagNames().add(tag.getName()); } return state; } private static String getValueAsString(Object value) { if (value instanceof Date) { DateFormat format = DateFormat.getDateInstance(DateFormat.SHORT, Locale.CANADA); return format.format((Date)value); } else { return value.toString(); } } // @SuppressWarnings("unchecked") // public CurrentWaypoints getWaypoints(Date date) throws IOException { // IPath stateLocation = TagSEAInstrumentationPlugin.getDefault().getStateLocation(); // DateFormat formatter = DateFormat.getDateInstance(DateFormat.SHORT, Locale.CANADA); // String dateString = formatter.format(date); // String fileNameString = dateString.replace('/', '-') + ".waypointlog"; // IPath pathLocation = stateLocation.append(fileNameString); // File file = pathLocation.toFile(); // CurrentWaypoints log = null; // if (file.exists()) { // ResourceSet inputResourceSet = new ResourceSetImpl(); // inputResourceSet.getPackageRegistry().put(LoggingPackage.eNS_PREFIX, LoggingPackage.eINSTANCE); // inputResourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("waypointlog", new XMIResourceFactoryImpl()); // Resource inputResource = inputResourceSet.createResource(URI.createFileURI(pathLocation.toPortableString())); // inputResource.load(null); // log = (CurrentWaypoints) inputResource.getContents().get(0); // } else { // log = LoggingFactory.eINSTANCE.createCurrentWaypoints(); // Locale locale = Locale.getDefault(); // log.setCountry(locale.getCountry()); // log.setLanguage(locale.getLanguage()); // log.setUid(getUserId()); // log.setUname(getUserName()); // try { // log.setDate(formatter.parse(dateString)); // } catch (ParseException e) { // log(e); // } // } // return log; // } @SuppressWarnings("unchecked") void startUILog() { InstrumentationPreferences.incrementUISession(); sink.consumeEvent(LoggingFactory.eINSTANCE.createUIRecordStartEvent()); //add an event stating that we are able to listen for the task view TaskNavigateEvent tne = LoggingFactory.eINSTANCE.createTaskNavigateEvent(); tne.setLine(-1); tne.setDescription("Logging Task Navigation Events"); tne.setResource("$$&&(Logging Task Navigation Events)&&$$"); tne.setTime(System.currentTimeMillis()); sink.consumeEvent(tne); } @SuppressWarnings("unchecked") void startModelLog() { InstrumentationPreferences.incrementModelSession(); sink.consumeEvent(LoggingFactory.eINSTANCE.createModelRecordStartEvent()); } void startUploadTimer() { stopUploadTimer(); uploadTimer = new Timer(); //wait a couple of minutes, and then send to try to ensure that we get an updated //state of the waypoints. Date date = InstrumentationPreferences.getNextSendDate(); //getLog().log(new Status(Status.INFO, PLUGIN_ID, "Scheduling send for " + date.toString())); if (date != null) { uploadTimer.schedule(new UploadTask(), date ); } } void stopUploadTimer() { if (uploadTimer != null) { uploadTimer.cancel(); uploadTimer = null; } } /** * @return */ public String getUserName() { return InstrumentationPreferences.getFirstName() + " " + InstrumentationPreferences.getLastName(); } /** * @return */ public int getUserId() { return InstrumentationPreferences.getUID(); } synchronized void initializeLogging() { if (logging) return; if (modelListener == null) modelListener = new TagSEAModelListener(); //@tag tagsea.instrumentation.2008.january : not needed in jauary 2008 study //if (uiListener == null) uiListener = new TagSEAUIListener(); if (!InstrumentationPreferences.isRegistered()) { return; } if (InstrumentationPreferences.isMonitoringEnabled()) { startModelLog(); //@tag tagsea.instrumentation.2008.january : The UI isn't logged for the study in January 2008. //startUILog(); TagSEAPlugin.addOperationStateListener(modelListener); TagSEAPlugin.addTagChangeListener(modelListener); TagSEAPlugin.addWaypointChangeListener(modelListener); final TagSEAUI ui = ((TagSEAUI)TagSEAPlugin.getDefault().getUI()); //@tag tagsea.bug.159.fix tagsea.instrumentation.2008.january : log what views are open at start up. Not needed in January 2008. //Display.getDefault().syncExec(new Runnable(){ // public void run() { // IViewPart view = ui.getTagsView(); // int state = TagSEAUIEvent.VIEW_CLOSED; // if (view == null) { // state = TagSEAUIEvent.VIEW_CLOSED; // } else { // IWorkbenchPart active = view.getSite().getPage().getActivePart(); // if (active == view) { // state = TagSEAUIEvent.VIEW_ACTIVATED; // } else { // IViewPart[] stack = view.getSite().getPage().getViewStack(view); // if (stack == null) { // state = TagSEAUIEvent.VIEW_CLOSED; // } if (stack[0] != view) { // state = TagSEAUIEvent.VIEW_HIDDEN; // } else { // state = TagSEAUIEvent.VIEW_TOP; // } // } // } // TagSEAUIEvent event = TagSEAUIEvent.createViewEvent(state, TagsView.ID, null); // logUIEvent(event); // view = ui.getWaypointView(); // state = TagSEAUIEvent.VIEW_CLOSED; // if (view == null) { // state = TagSEAUIEvent.VIEW_CLOSED; // } else { // IWorkbenchPart active = view.getSite().getPage().getActivePart(); // if (active == view) { // state = TagSEAUIEvent.VIEW_ACTIVATED; // } else { // IViewPart[] stack = view.getSite().getPage().getViewStack(view); // if (stack == null) { // state = TagSEAUIEvent.VIEW_CLOSED; // } if (stack[0] != view) { // state = TagSEAUIEvent.VIEW_HIDDEN; // } else { // state = TagSEAUIEvent.VIEW_TOP; // } // } // } // event = TagSEAUIEvent.createViewEvent(state, WaypointView.ID, null); // logUIEvent(event); // } // }); //ui.addUIEventListener(uiListener); logging = true; } } synchronized void quitLogging() { if (!logging) return; if (modelListener != null) { TagSEAPlugin.removeWaypointChangeListener(modelListener); TagSEAPlugin.removeTagChangeListener(modelListener); TagSEAPlugin.removeOperationStateListener(modelListener); } //@tag tagsea.instrumentation.2008.january : not needed in Jauary 2008 study. // if (uiListener != null) { // TagSEAUI ui = ((TagSEAUI)TagSEAPlugin.getDefault().getUI()); // ui.removeUIEventListener(uiListener); // } this.logging = false; } public void doneSaving(ISaveContext context) { } public void prepareToSave(ISaveContext context) throws CoreException { } public void rollback(ISaveContext context) { } public void saving(ISaveContext context) throws CoreException { try { switch (context.getKind()) { case ISaveContext.FULL_SAVE: sink.saveLogs(); sink.saveWaypoints(); break; case ISaveContext.SNAPSHOT: sink.saveToday(); sink.saveWaypoints(); break; } } catch (IOException e) { log(e); } } void startLogging() { if (InstrumentationPreferences.isUploadEnabled()) { //TagSEAInstrumentationPlugin.getDefault().getLog().log(new Status(Status.INFO, TagSEAInstrumentationPlugin.PLUGIN_ID, "Started upload timer.")); startUploadTimer(); } InstrumentationPreferences.addPreferenceChangeListener(new PreferenceListener()); try { ResourcesPlugin.getWorkspace().addSaveParticipant(this, this); } catch (CoreException e) { TagSEAInstrumentationPlugin.getDefault().log(e); } } }