added support for multi-class application motes

This commit is contained in:
nifi 2010-01-24 20:06:16 +00:00
parent 9f6a069f96
commit 7922108cbd
2 changed files with 459 additions and 104 deletions

View file

@ -0,0 +1,297 @@
/*
* Copyright (c) 2010, Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: ImportAppMoteDialog.java,v 1.1 2010/01/24 20:06:16 nifi Exp $
*/
package se.sics.cooja.dialogs;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileNotFoundException;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.filechooser.FileFilter;
import se.sics.cooja.GUI;
import se.sics.cooja.Mote;
import se.sics.cooja.Simulation;
import se.sics.cooja.motes.ImportAppMoteType;
/**
* Java Application Mote Type compile dialog.
*
* @author Niclas Finne
*/
public class ImportAppMoteDialog extends JDialog {
private static final long serialVersionUID = 1323772528826214008L;
protected final static Dimension LABEL_DIMENSION = new Dimension(170, 25);
private static String lastPath;
private static String lastFile;
private JTextField descriptionField;
private JTextField pathField;
private JTextField classField;
private JButton cancelButton;
private JButton createButton;
private boolean hasSelected = false;
public ImportAppMoteDialog(Container parent, final Simulation simulation, final ImportAppMoteType moteType) {
super((Window)parent, "Create Mote Type: Application Mote", ModalityType.APPLICATION_MODAL);
JPanel mainPanel = new JPanel(new BorderLayout());
JPanel topPanel = new JPanel();
topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.Y_AXIS));
topPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
descriptionField = new JTextField(40);
if (moteType.getDescription() != null) {
descriptionField.setText(moteType.getDescription());
} else {
descriptionField.setText("[enter mote type description]");
}
topPanel.add(createPanel("Description", descriptionField));
pathField = new JTextField(40);
File moteClassPath = moteType.getMoteClassPath();
if (moteClassPath != null) {
pathField.setText(moteClassPath.getPath());
} else if (lastPath != null) {
pathField.setText(lastPath);
}
topPanel.add(createPanel("Java Class Path:", pathField));
classField = new JTextField(40);
String moteClass = moteType.getMoteClassName();
if (moteClass != null && moteClass.length() > 0) {
classField.setText(moteClass);
} else if (lastFile != null) {
classField.setText(lastFile);
}
JButton browseButton = new JButton("Browse");
browseButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JFileChooser fc = new JFileChooser();
String path = pathField.getText();
String name = classField.getText();
if (name.indexOf('/') >= 0 || name.indexOf(File.separatorChar) >= 0) {
// Already full path
fc.setSelectedFile(new File(name));
} else if (name.length() > 0) {
fc.setSelectedFile(new File(new File(path), name.replace(".", "/") + ".class"));
} else {
File fp = simulation.getGUI()
.restorePortablePath(new File(GUI.getExternalToolsSetting("IMPORT_APP_LAST",
"mymote.class")));
if (path.length() > 0 && !fp.getAbsolutePath().startsWith(path)) {
fc.setCurrentDirectory(new File(path));
} else {
fc.setSelectedFile(fp);
}
}
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
fc.addChoosableFileFilter(new FileFilter() {
public boolean accept(File f) {
if (f.isDirectory()) {
return true;
}
String filename = f.getName();
if (filename != null && filename.endsWith(".class")) {
return true;
}
return false;
}
public String getDescription() {
return "Application Mote Java Class";
}
});
fc.setDialogTitle("Select Application Mote Java Class");
if (fc.showOpenDialog(ImportAppMoteDialog.this) == JFileChooser.APPROVE_OPTION) {
File fp = fc.getSelectedFile();
if (fp != null) {
trySetClass(simulation, moteType, fp);
}
}
}
});
topPanel.add(createPanel("Application Mote Java Class:", classField, browseButton));
ActionListener cancelAction = new ActionListener() {
public void actionPerformed(ActionEvent e) {
setVisible(false);
dispose();
}
};
cancelButton = new JButton("Cancel");
cancelButton.addActionListener(cancelAction);
KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
getRootPane().registerKeyboardAction(cancelAction, stroke, JComponent.WHEN_IN_FOCUSED_WINDOW);
createButton = new JButton("Create");
createButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String className = classField.getText();
if (className.length() == 0) {
JOptionPane.showMessageDialog(ImportAppMoteDialog.this,
"No class specified", "Failed to load class", JOptionPane.ERROR_MESSAGE);
return;
}
File classFile;
if (className.indexOf('/') >= 0 || className.indexOf(File.separatorChar) >= 0) {
classFile = new File(className);
} else {
classFile = new File(new File(pathField.getText()),
className.replace(".", "/") + ".class");
}
if (trySetClass(simulation, moteType, classFile)) {
moteType.setDescription(descriptionField.getText());
String path = pathField.getText();
if (path.length() > 0) {
moteType.setMoteClassPath(new File(path));
lastPath = path;
} else {
moteType.setMoteClassPath(null);
lastPath = null;
}
moteType.setMoteClassName(classField.getText());
lastFile = classField.getText();
hasSelected = true;
ImportAppMoteDialog.this.setVisible(false);
ImportAppMoteDialog.this.dispose();
}
}
});
getRootPane().setDefaultButton(createButton);
JPanel buttonPanel = new JPanel();
buttonPanel.add(createButton);
buttonPanel.add(cancelButton);
/* Build panel */
mainPanel.add(BorderLayout.NORTH, topPanel);
mainPanel.add(buttonPanel, BorderLayout.SOUTH);
setContentPane(mainPanel);
setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
cancelButton.doClick();
}
});
descriptionField.requestFocus();
descriptionField.selectAll();
pack();
setLocationRelativeTo(parent);
}
private boolean trySetClass(Simulation simulation, ImportAppMoteType moteType, File classFile) {
try {
ImportAppMoteType.TestLoader loader = moteType.createTestLoader(classFile);
if (!loader.isTestSubclass(Mote.class)) {
JOptionPane.showMessageDialog(ImportAppMoteDialog.this,
"Class '" + classFile + "'\n is not of type Mote", "Failed to load class",
JOptionPane.ERROR_MESSAGE);
} else {
pathField.setText(loader.getTestClassPath().getPath());
classField.setText(loader.getTestClassName());
GUI.setExternalToolsSetting("IMPORT_APP_LAST",
simulation.getGUI().createPortablePath(classFile).getPath());
return true;
}
} catch (FileNotFoundException e1) {
JOptionPane.showMessageDialog(ImportAppMoteDialog.this,
"Could not find class '" + classFile + "'", "Failed to load class",
JOptionPane.ERROR_MESSAGE);
} catch (Exception e1) {
e1.printStackTrace();
JOptionPane.showMessageDialog(ImportAppMoteDialog.this,
"Could not load class '" + classFile + "':\n" + e1, "Failed to load class",
JOptionPane.ERROR_MESSAGE);
} catch (LinkageError e1) {
e1.printStackTrace();
JOptionPane.showMessageDialog(ImportAppMoteDialog.this,
"Could not load class '" + classFile + "':\n" + e1, "Failed to load class",
JOptionPane.ERROR_MESSAGE);
}
return false;
}
private Component createPanel(String title, JComponent field1) {
return createPanel(title, field1, null);
}
private Component createPanel(String title, JComponent field1, JComponent field2) {
Box panel = Box.createHorizontalBox();
JLabel label = new JLabel(title);
label.setPreferredSize(LABEL_DIMENSION);
panel.add(label);
panel.add(field1);
if (field2 != null) {
panel.add(field2);
}
return panel;
}
public boolean waitForUserResponse() {
setVisible(true);
return hasSelected;
}
}

View file

@ -33,23 +33,20 @@ package se.sics.cooja.motes;
import java.awt.Container; import java.awt.Container;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Random;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileFilter;
import org.apache.log4j.Logger;
import org.jdom.Element; import org.jdom.Element;
import se.sics.cooja.AbstractionLevelDescription; import se.sics.cooja.AbstractionLevelDescription;
import se.sics.cooja.ClassDescription; import se.sics.cooja.ClassDescription;
import se.sics.cooja.GUI;
import se.sics.cooja.Mote; import se.sics.cooja.Mote;
import se.sics.cooja.MoteType; import se.sics.cooja.MoteType;
import se.sics.cooja.Simulation; import se.sics.cooja.Simulation;
import se.sics.cooja.dialogs.ImportAppMoteDialog;
import se.sics.cooja.util.ArrayUtils; import se.sics.cooja.util.ArrayUtils;
/** /**
@ -58,11 +55,11 @@ import se.sics.cooja.util.ArrayUtils;
@ClassDescription("Imported App Mote Type") @ClassDescription("Imported App Mote Type")
@AbstractionLevelDescription("Application level") @AbstractionLevelDescription("Application level")
public class ImportAppMoteType extends AbstractApplicationMoteType { public class ImportAppMoteType extends AbstractApplicationMoteType {
private static Logger logger = Logger.getLogger(ImportAppMoteType.class);
private Simulation simulation; private Simulation simulation;
private File moteClassFile = null; private File moteClassPath = null;
private String moteClassName = null;
private Class<? extends AbstractApplicationMote> moteClass = null; private Class<? extends AbstractApplicationMote> moteClass = null;
private Constructor<? extends AbstractApplicationMote> moteConstructor = null; private Constructor<? extends AbstractApplicationMote> moteConstructor = null;
@ -75,36 +72,20 @@ public class ImportAppMoteType extends AbstractApplicationMoteType {
setDescription("Imported App Mote Type #" + identifier); setDescription("Imported App Mote Type #" + identifier);
} }
private class TestClassLoader extends ClassLoader {
private int id; /* DEBUG */
private File file;
public TestClassLoader(File f) {
id = new Random().nextInt();
file = f;
}
public TestClassLoader(ClassLoader parent, File f) {
super(parent);
id = new Random().nextInt();
file = f;
}
public Class<?> findClass(String name) {
byte[] data = loadClassData(name);
return defineClass(data, 0, data.length);
}
private byte[] loadClassData(String name) {
return ArrayUtils.readFromFile(file);
}
public String toString() {
return "ImportAppMoteType classloader #" + id;
}
}
public Collection<Element> getConfigXML() { public Collection<Element> getConfigXML() {
ArrayList<Element> config = new ArrayList<Element>(); Collection<Element> config = super.getConfigXML();
Element element = new Element("moteclass"); if (moteClassPath != null) {
element.setText(simulation.getGUI().createPortablePath(moteClassFile).getPath()); Element element = new Element("motepath");
config.add(element); element.setText(simulation.getGUI().createPortablePath(moteClassPath).getPath());
config.add(element);
}
if (moteClassName != null) {
Element element = new Element("moteclass");
element.setText(moteClassName);
config.add(element);
}
return config; return config;
} }
@ -117,11 +98,13 @@ public class ImportAppMoteType extends AbstractApplicationMoteType {
for (Element element : configXML) { for (Element element : configXML) {
String name = element.getName(); String name = element.getName();
if (name.equals("moteclass")) { if (name.equals("moteclass")) {
moteClassFile = simulation.getGUI().restorePortablePath(new File(element.getText())); moteClassName = element.getText();
} else if (name.equals("motepath")) {
moteClassPath = simulation.getGUI().restorePortablePath(new File(element.getText()));
} }
} }
return configureAndInit(GUI.getTopParentContainer(), simulation, visAvailable); return super.setConfigXML(simulation, configXML, visAvailable);
} }
public boolean configureAndInit(Container parentContainer, public boolean configureAndInit(Container parentContainer,
@ -133,86 +116,56 @@ public class ImportAppMoteType extends AbstractApplicationMoteType {
return false; return false;
} }
boolean updateSuggestion = false;
if (visAvailable) { if (visAvailable) {
/* Select mote class file */ /* Select mote class file */
JFileChooser fileChooser = new JFileChooser(); ImportAppMoteDialog dialog = new ImportAppMoteDialog(parentContainer, simulation, this);
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); if (!dialog.waitForUserResponse()) {
fileChooser.setDialogTitle("Select application class file");
if (moteClassFile != null) {
fileChooser.setSelectedFile(moteClassFile);
} else {
updateSuggestion = true;
fileChooser.setSelectedFile(new File(GUI.getExternalToolsSetting("IMPORT_APP_LAST", "mymote.class")));
}
fileChooser.setFileFilter(new FileFilter() {
public boolean accept(File file) {
if (file.isDirectory()) {
return true;
}
if (file.getName().endsWith(".class")) {
return true;
}
return false;
}
public String getDescription() {
return "Java class extending " + AbstractApplicationMoteType.class.getName();
}
public String toString() {
return ".class";
}
});
int reply = fileChooser.showOpenDialog(GUI.getTopParentContainer());
if (reply != JFileChooser.APPROVE_OPTION) {
return false; return false;
} }
moteClassFile = fileChooser.getSelectedFile();
} }
if (moteClassName == null) {
if (moteClassFile == null) {
throw new MoteTypeCreationException("Unknown mote class file"); throw new MoteTypeCreationException("Unknown mote class file");
} }
if (!moteClassFile.exists()) {
throw new MoteTypeCreationException("Mote class file does not exist");
}
if (updateSuggestion) {
GUI.setExternalToolsSetting("IMPORT_APP_LAST", moteClassFile.getPath());
}
/* Load class */
TestClassLoader loader = new TestClassLoader(moteClassFile);
try { try {
moteClass = if (moteClassPath == null) {
(Class<? extends AbstractApplicationMote>) // No class path. Check if path is available in the class name.
loader.loadClass("ignored class name"); convertPathToClass();
moteConstructor = }
(Constructor<? extends AbstractApplicationMote>)
moteClass.getConstructor(new Class[] { MoteType.class, Simulation.class }); ClassLoader parentLoader = getParentClassLoader();
ClassLoader loader;
if (moteClassPath != null) {
/* Load class */
loader = new URLClassLoader(new java.net.URL[] { moteClassPath.toURI().toURL() },
parentLoader);
} else {
loader = parentLoader;
}
moteClass = loader.loadClass(moteClassName).asSubclass(AbstractApplicationMote.class);
moteConstructor = moteClass.getConstructor(new Class[] { MoteType.class, Simulation.class });
} catch (Exception e) { } catch (Exception e) {
throw (MoteTypeCreationException) throw createError(e);
new MoteTypeCreationException("Error when loading class from: " + moteClassFile.getPath()).initCause(e); } catch(LinkageError e) {
} catch (Throwable t) { throw createError(e);
throw (MoteTypeCreationException)
new MoteTypeCreationException("Error when loading class from: " + moteClassFile.getPath()).initCause(t);
} }
/* XXX Extending GUI's project class loader. Nasty. */ if (getDescription() == null || getDescription().length() == 0) {
if (simulation.getGUI().projectDirClassLoader == null) { setDescription("Imported Mote Type #" + moteClassName);
simulation.getGUI().projectDirClassLoader =
new TestClassLoader(ClassLoader.getSystemClassLoader(), moteClassFile);
} else {
simulation.getGUI().projectDirClassLoader =
new TestClassLoader(simulation.getGUI().projectDirClassLoader, moteClassFile);
} }
logger.info(moteClass.getClassLoader());
setDescription("Imported Mote Type #" + moteClassFile.getName());
return true; return true;
} }
private MoteTypeCreationException createError(Throwable e) {
MoteTypeCreationException mte =
new MoteTypeCreationException("Error when loading class from: "
+ (moteClassPath != null ? moteClassPath.getAbsolutePath() : "") + " "
+ moteClassName);
mte.initCause(e);
return mte;
}
public Mote generateMote(Simulation simulation) { public Mote generateMote(Simulation simulation) {
try { try {
return moteConstructor.newInstance(ImportAppMoteType.this, simulation); return moteConstructor.newInstance(ImportAppMoteType.this, simulation);
@ -220,4 +173,109 @@ public class ImportAppMoteType extends AbstractApplicationMoteType {
throw (RuntimeException) new RuntimeException("Error when generating mote").initCause(e); throw (RuntimeException) new RuntimeException("Error when generating mote").initCause(e);
} }
} }
public void setMoteClassPath(File moteClassPath) {
this.moteClassPath = moteClassPath;
}
public File getMoteClassPath() {
return moteClassPath;
}
public void setMoteClassName(String moteClassName) {
this.moteClassName = moteClassName;
}
public String getMoteClassName() {
return moteClassName;
}
private void convertPathToClass() {
if (moteClassName.indexOf('/') < 0 && moteClassName.indexOf(File.separatorChar) < 0) {
// No conversion possible
return;
}
File moteClassFile = new File(moteClassName);
if (moteClassFile.canRead()) {
try {
TestLoader test = createTestLoader(moteClassFile);
// Successfully found the class
moteClassPath = test.getTestClassPath();
moteClassName = test.getTestClassName();
} catch (Exception e) {
// Ignore
} catch (LinkageError e) {
// Ignore
}
}
}
private ClassLoader getParentClassLoader() {
if (simulation.getGUI().projectDirClassLoader == null) {
return ClassLoader.getSystemClassLoader();
} else {
return simulation.getGUI().projectDirClassLoader;
}
}
public TestLoader createTestLoader(File classFile) throws IOException {
classFile = classFile.getCanonicalFile();
ArrayList<URL> list = new ArrayList<URL>();
for(File parent = classFile.getParentFile();
parent != null;
parent = parent.getParentFile()) {
list.add(parent.toURI().toURL());
}
return new TestLoader(list.toArray(new URL[list.size()]),
getParentClassLoader(), classFile);
}
public static class TestLoader extends URLClassLoader {
private final File classFile;
private File classPath;
private Class<?> testClass;
private TestLoader(URL[] classpath, ClassLoader parentClassLoader, File classFile)
throws IOException
{
super(classpath, parentClassLoader);
this.classFile = classFile.getCanonicalFile();
byte[] data = ArrayUtils.readFromFile(classFile);
if (data == null) {
throw new FileNotFoundException(classFile.getAbsolutePath());
}
this.testClass = defineClass(null, data, 0, data.length);
}
public File getTestClassFile() {
return classFile;
}
public boolean isTestSubclass(Class<?> type) {
return type.isAssignableFrom(testClass);
}
public Class<?> getTestClass() {
return testClass;
}
public String getTestClassName() {
return testClass.getCanonicalName();
}
public File getTestClassPath() {
if (classPath == null) {
String name = testClass.getCanonicalName();
int index = name.indexOf('.');
classPath = classFile.getParentFile();
while(index >= 0) {
classPath = classPath.getParentFile();
index = name.indexOf('.', index + 1);
}
}
return classPath;
}
}
} }