mirror of
https://github.com/oliverschmidt/contiki.git
synced 2024-08-16 07:28:57 +00:00
396 lines
13 KiB
Java
396 lines
13 KiB
Java
/*
|
|
* Copyright (c) 2006, 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: CoreComm.java,v 1.11 2008/02/12 15:03:02 fros4943 Exp $
|
|
*/
|
|
|
|
package se.sics.cooja;
|
|
|
|
import java.io.*;
|
|
import java.lang.reflect.*;
|
|
import java.net.*;
|
|
import java.util.Vector;
|
|
|
|
import se.sics.cooja.MoteType.MoteTypeCreationException;
|
|
import se.sics.cooja.dialogs.MessageList;
|
|
|
|
/**
|
|
* The purpose of corecomm's is communicating with a compiled Contiki system
|
|
* using Java Native Interface (JNI). Each implemented class (named
|
|
* Lib[number]), loads a shared library which belongs to one mote type. The
|
|
* reason for this somewhat strange design is that once loaded, a native library
|
|
* cannot be unloaded in Java (in the current versions available). Therefore if
|
|
* we wish to load several libraries, the names and associated native functions
|
|
* must have unique names. And those names are defined via the calling class in
|
|
* JNI. For example, the corresponding function for a native tick method in
|
|
* class Lib1 will be named Java_se_sics_cooja_corecomm_Lib1_tick. When creating
|
|
* a new mote type, the main Contiki source file is generated with function
|
|
* names compatible with the next available corecomm class. This also implies
|
|
* that even if a mote type is deleted, a new one cannot be created using the
|
|
* same corecomm class without restarting the JVM and thus the entire
|
|
* simulation.
|
|
*
|
|
* Each implemented CoreComm class needs read access to the following core
|
|
* variables:
|
|
* <ul>
|
|
* <li>referenceVar
|
|
* </ul>
|
|
* and the following native functions:
|
|
* <ul>
|
|
* <li>tick()
|
|
* <li>init()
|
|
* <li>getReferenceAbsAddr()
|
|
* <li>getMemory(int start, int length, byte[] mem)
|
|
* <li>setMemory(int start, int length, byte[] mem)
|
|
*
|
|
* @author Fredrik Osterlind
|
|
*/
|
|
public abstract class CoreComm {
|
|
|
|
// Static pointers to current libraries
|
|
private final static Vector<CoreComm> coreComms = new Vector<CoreComm>();
|
|
|
|
private final static Vector<File> coreCommFiles = new Vector<File>();
|
|
|
|
private static int fileCounter = 1;
|
|
|
|
/**
|
|
* Has any library been loaded? Since libraries can't be unloaded the entire
|
|
* simulator may have to be restarted.
|
|
*
|
|
* @return True if any library has been loaded this session
|
|
*/
|
|
public static boolean hasLibraryBeenLoaded() {
|
|
return coreComms.size() > 0;
|
|
}
|
|
|
|
/**
|
|
* Has given library file already been loaded during this session? A loaded
|
|
* library can be removed, but not unloaded during one session. And a new
|
|
* library file, named the same as an earlier loaded and removed file, can't
|
|
* be loaded either.
|
|
*
|
|
* @param libraryFile
|
|
* Library file
|
|
* @return True if a library has already been loaded from the given file's
|
|
* filename
|
|
*/
|
|
public static boolean hasLibraryFileBeenLoaded(File libraryFile) {
|
|
for (File loadedFile : coreCommFiles) {
|
|
if (loadedFile != null
|
|
&& loadedFile.getName().equals(libraryFile.getName())) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Get the class name of next free core communicator class. If null is
|
|
* returned, no classes are available.
|
|
*
|
|
* @return Class name
|
|
*/
|
|
public static String getAvailableClassName() {
|
|
return "Lib" + fileCounter;
|
|
}
|
|
|
|
/**
|
|
* Generates new source file by reading default source template and replacing
|
|
* the class name field.
|
|
*
|
|
* @param className
|
|
* Java class name (without extension)
|
|
* @throws MoteTypeCreationException
|
|
* If error occurs
|
|
*/
|
|
public static void generateLibSourceFile(String className)
|
|
throws MoteTypeCreationException {
|
|
BufferedWriter sourceFileWriter = null;
|
|
BufferedReader templateFileReader = null;
|
|
String destFilename = null;
|
|
|
|
try {
|
|
Reader reader;
|
|
String mainTemplate = GUI
|
|
.getExternalToolsSetting("CORECOMM_TEMPLATE_FILENAME");
|
|
|
|
if ((new File(mainTemplate)).exists()) {
|
|
reader = new FileReader(mainTemplate);
|
|
} else {
|
|
InputStream input = CoreComm.class
|
|
.getResourceAsStream('/' + mainTemplate);
|
|
if (input == null) {
|
|
throw new FileNotFoundException(mainTemplate + " not found");
|
|
}
|
|
reader = new InputStreamReader(input);
|
|
}
|
|
|
|
templateFileReader = new BufferedReader(reader);
|
|
destFilename = className + ".java";
|
|
|
|
File dir = new File("se/sics/cooja/corecomm");
|
|
if (!dir.exists()) {
|
|
dir.mkdirs();
|
|
}
|
|
|
|
sourceFileWriter = new BufferedWriter(new OutputStreamWriter(
|
|
new FileOutputStream("se/sics/cooja/corecomm/" + destFilename)));
|
|
|
|
// Replace special fields in template
|
|
String line;
|
|
while ((line = templateFileReader.readLine()) != null) {
|
|
line = line.replaceFirst("\\[CLASSNAME\\]", className);
|
|
sourceFileWriter.write(line + "\n");
|
|
}
|
|
|
|
sourceFileWriter.close();
|
|
templateFileReader.close();
|
|
} catch (Exception e) {
|
|
try {
|
|
if (sourceFileWriter != null) {
|
|
sourceFileWriter.close();
|
|
}
|
|
if (templateFileReader != null) {
|
|
templateFileReader.close();
|
|
}
|
|
} catch (Exception e2) {
|
|
}
|
|
|
|
throw (MoteTypeCreationException) new MoteTypeCreationException(
|
|
"Could not generate corecomm source file: " + className + ".java")
|
|
.initCause(e);
|
|
}
|
|
|
|
File genFile = new File("se/sics/cooja/corecomm/" + destFilename);
|
|
if (genFile.exists()) {
|
|
return;
|
|
}
|
|
|
|
throw new MoteTypeCreationException(
|
|
"Could not generate corecomm source file: " + className + ".java");
|
|
}
|
|
|
|
/**
|
|
* Compiles Java class.
|
|
*
|
|
* @param className
|
|
* Java class name (without extension)
|
|
* @throws MoteTypeCreationException
|
|
* If Java class compilation error occurs
|
|
*/
|
|
private static void compileSourceFile(String className)
|
|
throws MoteTypeCreationException {
|
|
MessageList compilationOutput = new MessageList();
|
|
OutputStream compilationStandardStream = compilationOutput
|
|
.getInputStream(MessageList.NORMAL);
|
|
OutputStream compilationErrorStream = compilationOutput
|
|
.getInputStream(MessageList.ERROR);
|
|
|
|
File classFile = new File("se/sics/cooja/corecomm/" + className + ".class");
|
|
|
|
try {
|
|
int b;
|
|
String[] cmd = new String[] { GUI.getExternalToolsSetting("PATH_JAVAC"),
|
|
"-version", "se/sics/cooja/corecomm/" + className + ".java" };
|
|
|
|
Process p = Runtime.getRuntime().exec(cmd, null, null);
|
|
InputStream outputStream = p.getInputStream();
|
|
InputStream errorStream = p.getErrorStream();
|
|
while ((b = outputStream.read()) >= 0) {
|
|
compilationStandardStream.write(b);
|
|
}
|
|
while ((b = errorStream.read()) >= 0) {
|
|
compilationErrorStream.write(b);
|
|
}
|
|
p.waitFor();
|
|
|
|
if (classFile.exists()) {
|
|
return;
|
|
}
|
|
|
|
// Try including cooja.jar
|
|
cmd = new String[] {
|
|
GUI.getExternalToolsSetting("PATH_JAVAC"),
|
|
"-version",
|
|
"se/sics/cooja/corecomm/" + className + ".java",
|
|
"-cp",
|
|
GUI.getExternalToolsSetting("PATH_CONTIKI")
|
|
+ "/tools/cooja/dist/cooja.jar" };
|
|
|
|
p = Runtime.getRuntime().exec(cmd, null, null);
|
|
|
|
outputStream = p.getInputStream();
|
|
errorStream = p.getErrorStream();
|
|
while ((b = outputStream.read()) >= 0) {
|
|
compilationStandardStream.write(b);
|
|
}
|
|
while ((b = errorStream.read()) >= 0) {
|
|
compilationErrorStream.write(b);
|
|
}
|
|
p.waitFor();
|
|
|
|
if (classFile.exists()) {
|
|
return;
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
MoteTypeCreationException exception = (MoteTypeCreationException) new MoteTypeCreationException(
|
|
"Could not compile corecomm source file: " + className + ".java")
|
|
.initCause(e);
|
|
exception.setCompilationOutput(compilationOutput);
|
|
throw exception;
|
|
} catch (InterruptedException e) {
|
|
MoteTypeCreationException exception = (MoteTypeCreationException) new MoteTypeCreationException(
|
|
"Could not compile corecomm source file: " + className + ".java")
|
|
.initCause(e);
|
|
exception.setCompilationOutput(compilationOutput);
|
|
throw exception;
|
|
}
|
|
|
|
MoteTypeCreationException exception = new MoteTypeCreationException(
|
|
"Could not compile corecomm source file: " + className + ".java");
|
|
exception.setCompilationOutput(compilationOutput);
|
|
throw exception;
|
|
}
|
|
|
|
/**
|
|
* Loads given Java class file from disk.
|
|
*
|
|
* @param classFile
|
|
* Java class (without extension)
|
|
* @return Loaded class
|
|
* @throws MoteTypeCreationException
|
|
* If error occurs
|
|
*/
|
|
private static Class loadClassFile(String className)
|
|
throws MoteTypeCreationException {
|
|
Class loadedClass = null;
|
|
try {
|
|
ClassLoader urlClassLoader = new URLClassLoader(
|
|
new URL[] { new File(".").toURI().toURL() },
|
|
CoreComm.class.getClassLoader());
|
|
loadedClass = urlClassLoader.loadClass("se.sics.cooja.corecomm."
|
|
+ className);
|
|
|
|
} catch (MalformedURLException e) {
|
|
throw (MoteTypeCreationException) new MoteTypeCreationException(
|
|
"Could not load corecomm class file: " + className + ".class")
|
|
.initCause(e);
|
|
} catch (ClassNotFoundException e) {
|
|
throw (MoteTypeCreationException) new MoteTypeCreationException(
|
|
"Could not load corecomm class file: " + className + ".class")
|
|
.initCause(e);
|
|
}
|
|
if (loadedClass == null) {
|
|
throw new MoteTypeCreationException(
|
|
"Could not load corecomm class file: " + className + ".class");
|
|
}
|
|
|
|
return loadedClass;
|
|
}
|
|
|
|
/**
|
|
* Create and return an instance of the core communicator identified by
|
|
* className. This core communicator will load the native library libFile.
|
|
*
|
|
* @param className
|
|
* Class name of core communicator
|
|
* @param libFile
|
|
* Native library file
|
|
* @return Core Communicator
|
|
*/
|
|
public static CoreComm createCoreComm(String className, File libFile)
|
|
throws MoteTypeCreationException {
|
|
generateLibSourceFile(className);
|
|
|
|
compileSourceFile(className);
|
|
|
|
Class newCoreCommClass = loadClassFile(className);
|
|
|
|
try {
|
|
Constructor constr = newCoreCommClass
|
|
.getConstructor(new Class[] { File.class });
|
|
CoreComm newCoreComm = (CoreComm) constr
|
|
.newInstance(new Object[] { libFile });
|
|
|
|
coreComms.add(newCoreComm);
|
|
coreCommFiles.add(libFile);
|
|
fileCounter++;
|
|
|
|
return newCoreComm;
|
|
} catch (Exception e) {
|
|
throw (MoteTypeCreationException) new MoteTypeCreationException(
|
|
"Error when creating corecomm instance: " + className).initCause(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ticks a mote once. This should not be used directly, but instead via
|
|
* Mote.tick().
|
|
*/
|
|
public abstract void tick();
|
|
|
|
/**
|
|
* Initializes a mote by running a startup script in the core. (Should only be
|
|
* run once, at the same time as the library is loaded)
|
|
*/
|
|
protected abstract void init();
|
|
|
|
/**
|
|
* Returns absolute memory location of the core variable referenceVar. Used to
|
|
* get offset between relative and absolute memory addresses.
|
|
*
|
|
* @return Absolute memory address
|
|
*/
|
|
public abstract int getReferenceAbsAddr();
|
|
|
|
/**
|
|
* Fills an byte array with memory segment identified by start and length.
|
|
*
|
|
* @param start
|
|
* Start address of segment
|
|
* @param length
|
|
* Length of segment
|
|
* @param mem
|
|
* Array to fill with memory segment
|
|
*/
|
|
public abstract void getMemory(int start, int length, byte[] mem);
|
|
|
|
/**
|
|
* Overwrites a memory segment identified by start and length.
|
|
*
|
|
* @param start
|
|
* Start address of segment
|
|
* @param length
|
|
* Length of segment
|
|
* @param mem
|
|
* New memory segment data
|
|
*/
|
|
public abstract void setMemory(int start, int length, byte[] mem);
|
|
|
|
}
|