Commit 52dfb53d authored by David Johnson's avatar David Johnson
Browse files

This commit contains several things:

  * the google maps interface to the applet
  * a new control panel
  * a cache for dynamic web content that evicts if memory runs low
  * (some) comment cleanup
  * updated copyrights
parent b79d0dcc
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2006-2007 University of Utah and the Flux Group.
* All rights reserved.
*/
public class CollapsablePanel extends javax.swing.JPanel {
String title;
public CollapsablePanel() {
initComponents();
}
public CollapsablePanel(String title) {
this.title = title;
initComponents();
}
public void setTitle(String title) {
this.title = title;
}
public String getTitle() {
return this.title;
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
private void initComponents() {
setLayout(new java.awt.GridBagLayout());
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables
}
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2006-2007 University of Utah and the Flux Group.
* All rights reserved.
*/
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class CollapsablePanelContainer extends javax.swing.JPanel {
// title :: panel
Hashtable childPanels;
// title :: GridBagConstraints
Hashtable childConstraints;
// title :: state (false == uncollapsed)
Hashtable childPanelState;
// title :: int (index)
Hashtable childPanelIndex;
// title :: JButton
Hashtable childPanelButtons;
public CollapsablePanelContainer() {
childPanels = new Hashtable();
childConstraints = new Hashtable();
childPanelState = new Hashtable();
childPanelIndex = new Hashtable();
childPanelButtons = new Hashtable();
initComponents();
this.removeAll();
}
public void add(JComponent c,Object la) {
if (c instanceof CollapsablePanel) {
appendChildPanel(((CollapsablePanel)c).getTitle(),c,la);
}
else {
appendChildPanel("unk",c,la);
}
}
void appendChildPanel(String title,JComponent ccp,Object layoutArg) {
Set cpNames = childPanels.keySet();
if (cpNames == null || cpNames.contains(title)) {
return;
}
int y = childPanels.size() * 2;
childPanelIndex.put(title,new Integer(y));
childPanels.put(title,ccp);
childPanelState.put(title,new Boolean(false));
GridBagConstraints gc = new GridBagConstraints();
gc.anchor = GridBagConstraints.NORTHWEST;
gc.gridx = 0;
gc.gridy = y;
//gc.weighty = 1.0;
JButton ocButton = new javax.swing.JButton();
//ocButton.setFont(new java.awt.Font("Dialog", 1, 10));
ocButton.setIcon(new ImageIcon(getClass().getResource("/open.gif")));
ocButton.setBorderPainted(false);
ocButton.setMargin(new java.awt.Insets(0, 0, 0, 0));
childPanelButtons.put(title,ocButton);
final CollapsablePanelContainer cpc = this;
final String lTitle = title;
ocButton.addActionListener(new ActionListener() {
final String myTitle = lTitle;
final CollapsablePanelContainer myCPC = cpc;
public void actionPerformed(ActionEvent evt) {
myCPC.invertState(myTitle);
}
});
super.add(ocButton,gc);
gc.gridx = 1;
gc.anchor = GridBagConstraints.NORTHWEST;
//gc.weightx = 1.0;
JLabel ocLabel = new JLabel(title);
ocLabel.setFont(new java.awt.Font("Dialog", 1, 12));
super.add(ocLabel,gc);
// Border pBorder = BorderFactory.createTitledBorder(null,"",
// javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION,
// javax.swing.border.TitledBorder.DEFAULT_POSITION,
// new java.awt.Font("Dialog", 0, 10));
Border pBorder = BorderFactory.createLineBorder(Color.GRAY,1);
ccp.setBorder(pBorder);
gc.anchor = GridBagConstraints.NORTHWEST;
gc.gridy = y + 1;
gc.gridx = 0;
gc.gridwidth = 2;
if (layoutArg instanceof GridBagConstraints) {
GridBagConstraints igc = (GridBagConstraints)layoutArg;
// just need to set the grid args; leave the rest intact.
//igc.anchor = GridBagConstraints.NORTHWEST;
//igc.fill = GridBagConstraints.BOTH;
//igc.weightx = 1.0;
//igc.weighty = 1.0;
igc.gridy = y + 1;
igc.gridx = 0;
igc.gridwidth = 2;
this.childConstraints.put(title,igc);
super.add(ccp,igc);
}
else {
this.childConstraints.put(title,gc);
super.add(ccp,gc);
}
this.revalidate();
this.repaint();
}
public void invertState(String title) {
Boolean pstate = (Boolean)childPanelState.get(title);
if (pstate == null) {
return;
}
setCollapsed(title,!pstate.booleanValue());
}
public void setCollapsed(String title,boolean collapsed) {
Boolean pstate = (Boolean)childPanelState.get(title);
if (pstate == null || pstate.booleanValue() == collapsed) {
return;
}
else {
childPanelState.put(title,new Boolean(collapsed));
if (collapsed) {
JComponent oc = (JComponent)childPanels.get(title);
super.remove(oc);
JButton ocButton = (JButton)childPanelButtons.get(title);
ocButton.setIcon(new ImageIcon(getClass().getResource("/closed.gif")));
this.revalidate();
this.repaint();
}
else {
int y = ((Integer)childPanelIndex.get(title)).intValue();
GridBagConstraints gc = (GridBagConstraints)childConstraints.get(title);
// gc.anchor = GridBagConstraints.NORTHWEST;
// gc.gridx = 0;
// gc.gridy = y + 1;
// gc.gridwidth = 2;
super.add((Component)childPanels.get(title),gc);
JButton ocButton = (JButton)childPanelButtons.get(title);
ocButton.setIcon(new ImageIcon(getClass().getResource("/open.gif")));
this.revalidate();
this.repaint();
}
}
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
private void initComponents() {
jButton1 = new javax.swing.JButton();
setLayout(new java.awt.GridBagLayout());
jButton1.setFont(new java.awt.Font("Dialog", 1, 10));
jButton1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/open.gif")));
jButton1.setText("jButton1");
add(jButton1, new java.awt.GridBagConstraints());
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton jButton1;
// End of variables declaration//GEN-END:variables
}
This diff is collapsed.
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2006-2007 University of Utah and the Flux Group.
* All rights reserved.
*/
import java.util.*;
import java.util.zip.*;
import java.awt.Image;
import java.awt.image.*;
import java.net.URL;
import java.net.URLEncoder;
import java.net.URLConnection;
import java.net.MalformedURLException;
import java.io.IOException;
import java.io.*;
import java.security.MessageDigest;
/*
* DataCache does exactly what it says it does. It can retrieve dynamically-
* generated content from the web and do a diff on the raw data to see if it
* matches anything we've already retrieved. You can "preload" items (i.e.,
* add the url retrieval info, but not actually fetch), fetch on demand, set
* an evict policy (i.e., to see if you're running short on system RAM and need
* to evict before grabbing more). If objects are the same, callers are given
* refs to the one object saved.
*/
public class DataCache {
public static final int EVICT_NEVER = 1;
// eviction is based on an LRU policy, of course
public static final int EVICT_ATNEED = 2;
private static final int EVENT_TYPE_RETRIEVED = 1;
private static final int EVENT_TYPE_RETRIEVAL_FAILURE = 2;
private static final int EVENT_TYPE_EVICTED = 3;
private java.applet.Applet applet;
// URL :: CacheObject
private Hashtable cache;
// CacheObject :: Object (type determined by URLConnection.getContent())
private Hashtable objMap;
// Object (real one) :: md5(raw byte array)
private Hashtable objMD5Map;
// Object (real one) :: last access time
private Hashtable objAccessTimes;
// CacheObject :: Vector(DataCacheListener)
private Hashtable dcls;
private float minFreeThreshold;
void evictLRUObject() {
long ctime = System.currentTimeMillis();
Vector cacheObjects = null;
Object lruRealObject = null;
long currentOldestAge = Long.MIN_VALUE;
for (Enumeration e1 = objAccessTimes.keys(); e1.hasMoreElements(); ) {
Object key = e1.nextElement();
long atime = ((Long)objAccessTimes.get(key)).longValue();
if ((ctime - atime) < currentOldestAge) {
continue;
}
boolean canEvict = true;
Vector tmpCacheObjects = new Vector();
// now see if all CacheObjects pointing to the real object allow
// eviction... note that this could produce a DOS on the memory
// system if a user came along and said no eviction for all objects
// in the cache...
for (Enumeration e2 = objMap.keys(); e2.hasMoreElements(); ) {
// check through the current map to see if all CacheObjects that
// map to this object all have evict policy set:
DataCacheObject co = (DataCacheObject)e2.nextElement();
Object realObj = objMap.get(co);
if (realObj.equals(key)) {
if (co.getEvictPolicy() != EVICT_ATNEED) {
canEvict = false;
break;
}
else {
tmpCacheObjects.add(co);
}
}
}
if (canEvict) {
currentOldestAge = ctime - atime;
lruRealObject = key;
cacheObjects = tmpCacheObjects;
}
}
if (lruRealObject != null) {
// something to evict!
for (Enumeration e1 = cacheObjects.elements(); e1.hasMoreElements(); ) {
DataCacheObject dco = (DataCacheObject)e1.nextElement();
objMap.remove(dco);
notifyListeners(dco,new DataCacheEvent(this,dco),
this.EVENT_TYPE_EVICTED);
}
objMD5Map.remove(lruRealObject);
objAccessTimes.remove(lruRealObject);
}
}
public DataCache(java.applet.Applet applet) {
this.applet = applet;
this.minFreeThreshold = 0.05f;
this.cache = new Hashtable();
this.objMap = new Hashtable();
this.objMD5Map = new Hashtable();
this.objAccessTimes = new Hashtable();
this.dcls = new Hashtable();
// spin off a thread to monitor memory usage and warn/evict
// as necessary
(new MMThread(this)).start();
}
public DataCacheObject registerURL(URL u,int evict_policy) {
for (Enumeration e1 = cache.keys(); e1.hasMoreElements(); ) {
if (((URL)e1.nextElement()).equals(u)) {
debug("while registering " + u + " found in cache");
return (DataCacheObject)cache.get(u);
}
}
DataCacheObject o = new DataCacheObject(this,u,evict_policy);
cache.put(u,o);
debug("registered u");
return o;
}
private void debug(String s) {
System.out.println("DEBUG: DataCache: " + s);
}
Object getDone(DataCacheObject o,Exception ex,
Object content,byte[] rawContent) {
Object retval = null;
DataCacheEvent ce = new DataCacheEvent(this,o);
Exception lex = ex;
// now check the cache and see if there's an identical object in there.
if (ex == null) {
byte[] newDigest = null;
try {
MessageDigest di = MessageDigest.getInstance("MD5");
newDigest = di.digest(rawContent);
debug("computed digest for " + o.getURL() + " to be " + newDigest.toString());
Object realObj = null;
for (Enumeration e1 = objMD5Map.keys(); e1.hasMoreElements(); ) {
Object key = e1.nextElement();
byte[] digest = (byte[])objMD5Map.get(key);
if (digest.length == newDigest.length) {
boolean eq = true;
for (int i = 0; i < digest.length; ++i) {
if (digest[i] != newDigest[i]) {
eq = false;
break;
}
}
if (eq) {
realObj = key;
break;
}
}
}
if (realObj != null) {
// need to associate this cacheobject with the
// backing object
this.objMap.put(o,realObj);
debug("getDone: using cached object " +
((realObj != null)?realObj.getClass().toString():"") +
" for " + o.getURL());
}
else {
// need to save this object:
realObj = content;
this.objMap.put(o,realObj);
this.objMD5Map.put(realObj,newDigest);
debug("getDone: using new object " +
((realObj != null)?realObj.getClass().toString():"") +
" for " + o.getURL());
}
this.objAccessTimes.put(realObj,new Long(System.currentTimeMillis()));
retval = realObj;
}
catch (Exception ex2) {
ex2.printStackTrace();
lex = ex2;
}
}
ce.setException(lex);
notifyListeners(o,ce,
(lex == null)?EVENT_TYPE_RETRIEVED:EVENT_TYPE_RETRIEVAL_FAILURE);
return retval;
}
public Object getURL(DataCacheObject o) {
Object retval = null;
if ((retval = this.objMap.get(o)) != null) {
this.objAccessTimes.put(retval,new Long(System.currentTimeMillis()));
debug("getURL: cache hit");
return retval;
}
else {
Object contentObj = null;
byte[] raw = null;
Exception rete = null;
try {
URLConnection uconn = o.getURL().openConnection();
debug("getURL: content type = '" + uconn.getContentType() + "'");
contentObj = uconn.getContent();
// debug("getURL: first contentObj = " +
// ((contentObj != null)?contentObj.getClass().toString():""));
InputStream is = uconn.getInputStream();
//contentObj = uconn.getContent();
byte[] tmpraw;
raw = new byte[1024];
int rc = 0;
int total = 0;
while ((rc = is.read(raw,total,raw.length - total)) != -1) {
total += rc;
if (total == raw.length) {
tmpraw = new byte[raw.length + 1024];
System.arraycopy(raw,0,tmpraw,0,raw.length);
raw = tmpraw;
}
}
tmpraw = new byte[total];
System.arraycopy(raw,0,tmpraw,0,total);
raw = tmpraw;
}
catch (Exception ex) {
ex.printStackTrace();
raw = null;
contentObj = null;
rete = ex;
}
// now, some trickery... we have to actually create the image from
// the content source... a silly sun! they're so silly.
if (contentObj != null &&
contentObj instanceof java.awt.image.ImageProducer) {
contentObj = java.awt.Toolkit.getDefaultToolkit().createImage(raw);
// debug("getURL: second contentObj = " +
// ((contentObj != null)?contentObj.getClass().toString():""));
// java.awt.image.ImageObserver io = new java.awt.image.ImageObserver() {
// public boolean imageUpdate(Image img,int iflags,int x,int y,
// int w,int h) {
// System.out.println("DEBUG: w="+w+",h="+h+" for img " + img);
// if (w > 1 && h > 1) {
// return false;
// }
// return true;
// }
// };
// ((java.awt.Image)contentObj).getHeight(io);
}
debug("getURL: downloaded object (" +
((contentObj != null)?contentObj.getClass().toString():"") +
")" + " for " + o.getURL());
return getDone(o,rete,contentObj,raw);
}
}
public void preloadURL(DataCacheObject o) {
// first check cache --- if it's there, signal the dcl
// before returning
Object retval = this.objMap.get(o);
if (retval != null) {
// update timestamp? no. only usage.
notifyListeners(o,new DataCacheEvent(this,o),
EVENT_TYPE_RETRIEVED);
return;
}
else {
final DataCache odc = this;
final DataCacheObject oco = o;
Thread gT = new Thread() {
private final DataCache dc = odc;
private final DataCacheObject co = oco;
public void run() {
odc.getURL(co);
}
};
}
}
public void addCacheListener(DataCacheObject o,DataCacheListener dcl) {
Vector dclList = null;
if ((dclList = (Vector)dcls.get(o)) == null) {
dclList = new Vector();
dcls.put(o,dclList);
}
if (!dclList.contains(dcl)) {
dclList.add(dcl);
}
}
public void removeCacheListener(DataCacheObject o,DataCacheListener dcl) {
Vector dclList = null;
if ((dclList = (Vector)dcls.get(o)) == null) {
dclList.remove(dcl);
}
}
void notifyListeners(DataCacheObject o,DataCacheEvent e,int evtType) {
Vector dclList = (Vector)dcls.get(o);
if (dclList != null) {
for (Enumeration e1 = dclList.elements(); e1.hasMoreElements(); ) {
switch(evtType) {
case EVENT_TYPE_RETRIEVED:
((DataCacheListener)e1.nextElement()).dataRetrieved(e);
break;
case EVENT_TYPE_RETRIEVAL_FAILURE:
((DataCacheListener)e1.nextElement()).dataRetrievalFailure(e);
break;
case EVENT_TYPE_EVICTED:
((DataCacheListener)e1.nextElement()).dataEvicted(e);
break;
default:
e1.nextElement();
break;
}
}
}
}
public void setMinFreeThreshold(float mft) {
this.minFreeThreshold = mft;
}
</