/******************************************************************************* * Copyright (c) 2009 the CHISEL group and contributors. * 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: * Del Myers - initial API and implementation *******************************************************************************/ package ca.uvic.chisel.javasketch.data.model.imple.internal; import java.io.IOException; import java.sql.Connection; import java.sql.Date; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Timer; import java.util.TreeMap; import ca.uvic.chisel.hsqldb.server.IDataPortal; import ca.uvic.chisel.javasketch.data.SketchDataPlugin; import ca.uvic.chisel.javasketch.data.internal.DataUtils; import ca.uvic.chisel.javasketch.data.internal.IDataTriggerListener; import ca.uvic.chisel.javasketch.data.internal.WriteDataUtils; import ca.uvic.chisel.javasketch.data.model.IActivation; import ca.uvic.chisel.javasketch.data.model.IThread; import ca.uvic.chisel.javasketch.data.model.ITrace; import ca.uvic.chisel.javasketch.data.model.ITraceClass; import ca.uvic.chisel.javasketch.data.model.ITraceEventListener; import ca.uvic.chisel.javasketch.data.model.ITraceMetaEvent; import ca.uvic.chisel.javasketch.data.model.ITraceModel; import ca.uvic.chisel.javasketch.data.model.ITraceModelProxy; /** * @author Del Myers * */ public class TraceImpl extends TraceModelIDImpl implements ITrace, IDataTriggerListener { private WriteDataUtils dataUtils; private TreeMap<String, ITraceClass> classes; private List<IThread> threads; private List<ITraceMetaEvent> events; private TreeMap<String, ITraceModel> modelCache; private boolean classesDirty; private boolean threadsDirty; private boolean disposed; private String launchID; private Date launchTime; private static final Timer EVENT_THREAD = new Timer("Trace Model Events", true); private static final EventAgrigatorTask EVENT_AGGRIGATOR; static { EVENT_AGGRIGATOR = new EventAgrigatorTask(); EVENT_THREAD.schedule(EVENT_AGGRIGATOR, 1000, 2000); } private TraceImpl(String launchID, java.util.Date launchTime, WriteDataUtils utils) throws SQLException { this.dataUtils = utils; this.launchID = launchID; this.launchTime = new Date(launchTime.getTime()); classes = new TreeMap<String, ITraceClass>(); threads = new ArrayList<IThread>(); modelCache = new TreeMap<String, ITraceModel>(); load(); dataUtils.addTriggerListener(this); classesDirty = true; threadsDirty = true; register(this); } private TraceImpl(IDataPortal portal) { } /** * Checks to see if a trace with the given launch id and time exists for the * given data portal. * @param launchID * @param date * @param portal * @return */ public static boolean exists(String launchID, java.util.Date date, IDataPortal portal) { try { PreparedStatement s = portal.prepareStatement("Select * from Trace"); ResultSet results = s.executeQuery(); if (results.next()) { return (results.getString("launch_id") != null) && (results.getString("time") != null); } else { return false; } } catch (SQLException e) { return false; } } /** * Loads the trace with the given launch id and time from the given data portal. * If no such trace can be loaded, then null will be returned. * @param launchID * @param date * @param portal * @return */ public static ITrace load(String launchID, java.util.Date date, IDataPortal portal) { if (!exists(launchID, date, portal)) { return null; } try { WriteDataUtils utils = new WriteDataUtils(portal); TraceImpl trace = new TraceImpl(launchID, date, utils); return trace; } catch (SQLException e) { return null; } } /** * Creates a new trace for the given launch ID and time on the given data portal. All * previous data will be deleted. * @param launchID * @param date * @param portal * @return */ public static ITrace create(String launchID, java.util.Date date, IDataPortal portal) { TraceImpl trace = null; try { WriteDataUtils utils = new WriteDataUtils(portal); utils.initializeDB(launchID, date); trace = new TraceImpl(launchID, date, utils); } catch (SQLException e) { return null; } catch (IOException e) { return null; } return trace; } /** * */ public Connection getConnection() { try { return dataUtils.getConnection(); } catch (SQLException e) { } return null; } /* (non-Javadoc) * @see ca.uvic.chisel.javasketch.data.model.ITrace#forName(java.lang.String) */ public synchronized ITraceClass forName(String name) { if (disposed) { return null; } ITraceClass clazz = null; try { clazz = classes.get(name); if (clazz == null) { ResultSet results = dataUtils.findTraceClass(name); if (results != null) { clazz = new TraceClassImpl(this, name); classes.put(name, clazz); } } } catch (SQLException e) { SketchDataPlugin.getDefault().log(e); } return clazz; } /* (non-Javadoc) * @see ca.uvic.chisel.javasketch.data.model.ITrace#getClasses() */ public synchronized Collection<ITraceClass> getClasses() { if (disposed) { return Collections.emptyList(); } try { if (classesDirty) { ResultSet results = dataUtils.getTraceClasses(); while (results.next()) { try { String name = results.getString("type_name"); if (!classes.containsKey(name)) { ITraceClass clazz = new TraceClassImpl(this, name); classes.put(name, clazz); } } catch (SQLException e) { SketchDataPlugin.getDefault().log(e); } } classesDirty = false; } } catch (SQLException e) {} return Collections.unmodifiableCollection(classes.values()); } /* (non-Javadoc) * @see ca.uvic.chisel.javasketch.data.model.ITrace#getLaunchID() */ public String getLaunchID() { return launchID; } /* (non-Javadoc) * @see ca.uvic.chisel.javasketch.data.model.ITrace#getThreads() */ public synchronized Collection<IThread> getThreads() { if (disposed) { return Collections.emptyList(); } try { if (threadsDirty) { threads = new ArrayList<IThread>(); ResultSet results = getDataUtils().getThreads(); while (results.next()) { try { IThread thread = new ThreadImpl(this, results); threads.add(thread); } catch (SQLException e) { SketchDataPlugin.getDefault().log(e); } } threadsDirty = false; } } catch (SQLException e) { SketchDataPlugin.getDefault().log(e); } return Collections.unmodifiableCollection(threads); } /* (non-Javadoc) * @see ca.uvic.chisel.javasketch.data.model.ITrace#getTime() */ public Date getTraceTime() { return launchTime; } /* (non-Javadoc) * @see ca.uvic.chisel.javasketch.data.model.ITraceModel#getTrace() */ public ITrace getTrace() { return this; } public DataUtils getDataUtils() { try { } catch (IllegalStateException e) { SketchDataPlugin.getDefault().log(e); } return dataUtils; } /* (non-Javadoc) * @see ca.uvic.chisel.javasketch.data.model.imple.internal.TraceModelImpl#load() */ @Override public synchronized void load() { if (disposed) { return; } try { ResultSet results = getDataUtils().getTrace(); loadFromResults(results); } catch (SQLException e) { isValid = false; dispose(); SketchDataPlugin.getDefault().log(e); } } /* (non-Javadoc) * @see ca.uvic.chisel.javasketch.data.model.imple.internal.TraceModelImpl#unload() */ @Override public void unload() { for (ITraceClass c : classes.values()) { ((TraceClassImpl)c).unload(); } for (IThread t : threads) { ((ThreadImpl)t).unload(); } super.unload(); } /** * @param traceModelBase */ public synchronized void register(TraceModelImpl traceModel) { String identifier = traceModel.getIdentifier().toUpperCase(); modelCache.put(identifier, traceModel); } /* (non-Javadoc) * @see ca.uvic.chisel.javasketch.data.model.imple.internal.TraceModelImpl#getModelID() */ @Override public long getModelID() { return 0; } /* (non-Javadoc) * @see ca.uvic.chisel.javasketch.data.model.ITraceModel#getIdentifier() */ public String getIdentifier() { return "[TRACE]," + getModelID(); } public ITraceModel findElement(String identifier) { if (disposed) { return null; } try { if (identifier == null) return null; identifier = identifier.toUpperCase(); } catch (IllegalStateException e) { return null; } return modelCache.get(identifier); } /* (non-Javadoc) * @see ca.uvic.chisel.javasketch.data.model.ITrace#getEvents() */ public synchronized List<ITraceMetaEvent> getEvents() { if (disposed) { return Collections.emptyList(); } try { if (events != null) { return Collections.unmodifiableList(events); } else { events = new ArrayList<ITraceMetaEvent>(); try { Statement eventsStatement = getConnection().createStatement(); ResultSet results = eventsStatement.executeQuery("SELECT * FROM Event ORDER BY time"); while (results.next()) { events.add(new TraceEventImpl(this, results)); } } catch (SQLException e) { SketchDataPlugin.getDefault().log(e); } } } catch (IllegalStateException e) {} return Collections.unmodifiableList(events); } /** * Clears all the data in this trace. */ private synchronized void clear() { classes.clear(); modelCache.clear(); if (events != null) { events.clear(); events = null; } } /* (non-Javadoc) * @see ca.uvic.chisel.javasketch.data.internal.IDataTriggerListener#rowAdded(java.lang.String[], java.lang.Object[]) */ public void rowAdded(String tableName, Object[] row) { if (!disposed) { EVENT_AGGRIGATOR.aggrigate(this, row); } } /* (non-Javadoc) * @see ca.uvic.chisel.javasketch.data.model.imple.internal.TraceModelImpl#invalidate() */ @Override public void invalidate() { dispose(); synchronized (this) { for (ITraceModel element : modelCache.values()) { if (element instanceof TraceModelImpl && element != this) { ((TraceModelImpl)element).invalidate(); } } dataUtils.reset(); clear(); isValid = false; } } public synchronized void initialize() throws SQLException, IOException { invalidate(); unload(); dataUtils.initializeDB(launchID, launchTime); dataUtils.addTriggerListener(this); disposed = false; load(); } public void addListener(ITraceEventListener listener) { if (!disposed && isValid()) { EVENT_AGGRIGATOR.addListener(this, listener); } } public void removeListener(ITraceEventListener listener) { if (!disposed) { EVENT_AGGRIGATOR.removeListener(this, listener); } } synchronized void classesChanged() { classesDirty = true; } synchronized void threadsChanged() { threadsDirty = true; } /** * @param className */ void methodsChanged(String className) { TraceClassImpl type = (TraceClassImpl) forName(className); type.setDirty(true); } /** * @param activation */ void activationChanged(IActivation activation) { ((ActivationImpl)activation).setDirty(true); } /** * */ public void dispose() { EVENT_AGGRIGATOR.removeListeners(this); dataUtils.removeTriggerListener(this); this.disposed = true; } /* (non-Javadoc) * @see ca.uvic.chisel.javasketch.data.model.ITrace#getDataTime() */ public Date getDataTime() { if (disposed) { return new Date(0); } try { Timestamp stamp = getDate("data_time"); if (stamp != null) { return new Date(stamp.getTime()); } } catch (IllegalStateException e) {} return null; } /* (non-Javadoc) * @see ca.uvic.chisel.javasketch.data.model.ITraceModel#getKind() */ public int getKind() { return TRACE; } /* (non-Javadoc) * @see ca.uvic.chisel.javasketch.data.model.ITrace#getElement(java.lang.String) */ public ITraceModelProxy getElement(String identifier) { return TraceModelProxy.forIdentifier(this, identifier); } /* (non-Javadoc) * @see ca.uvic.chisel.javasketch.data.model.imple.internal.TraceModelImpl#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (obj == null || !getClass().equals(obj.getClass())) { return false; } TraceImpl that = (TraceImpl) obj; String id = getIdentity(); if (id == null) { return false; } return id.equals(that.getIdentity()); } /* (non-Javadoc) * @see ca.uvic.chisel.javasketch.data.model.imple.internal.TraceModelImpl#hashCode() */ @Override public int hashCode() { return getIdentity().hashCode(); } private String getIdentity() { return launchID + "@" + launchTime; } }