declare javaobj j; j = _new_ javaobj("somejavaclass");
character1
|
|
1Java string data types are mapped to SAS character data types as UTF-8 strings. |
dhash
and shash
classes
from the DATA step.
/* Java code */ import java.util.*; public class dhash { private Hashtable table; public dhash() { table = new Hashtable (); } public void put(double key, double value) { table.put(new Double(key), new Double(value)); } public double get(double key) { Double ret = table.get(new Double(key)); return ret.doubleValue(); } } import java.util.*; public class shash { private Hashtable table; public shash() { table = new Hashtable (); } public void put(double key, String value) { table.put(new Double(key), value); } public String get(double key) { return table.get(new Double(key)); } }
/* DATA step code */ data _null_; dcl javaobj sh('shash'); dcl javaobj dh('dhash'); length d 8; length s $20; do i = 1 to 10; dh.callvoidmethod('vput', i, i * 2); end; do i = 1 to 10; sh.callvoidmethod('put', i, 'abc' || left(trim(i))); end; do i = 1 to 10; dh.calldoublemethod('get', i, d); sh.callstringmethod('get', i, s); put d= s=; end; run;
/* Java code */ import java.util.*; import java.lang.*; class jtest { public void dbl(double args[]) { for(int i = 0; i < args.length; i++) System.out.println(args[i]); } public void str(String args[]) { for(int i = 0; i < args.length; i++) System.out.println(args[i]); } }
/* DATA Step code */ data _null_; dcl javaobj j("jtest"); array s{3} $20 ("abc", "def", "ghi"); array d{10} (1:10); j.callVoidMethod("dbl", d); j.callVoidMethod("str", s); run;
/* Java code */ import java.util.*; class mVector extends Vector { public mVector() { super(); } public mVector(double d) { super((int)d); } public void addElement(String s) { addElement((Object)s); } } import java.util.*; public class mIterator { protected mVector m_v; protected Iterator iter; public mIterator(mVector v) { m_v = v; iter = v.iterator(); } public boolean hasNext() { return iter.hasNext(); } public String next() { String ret = null; ret = (String)iter.next(); return ret; } }
mVector
constructor
takes a DOUBLE argument). Overloading the constructor is necessary
because java/util/Vector
's constructor
takes an integer value, but the DATA step has no integer type.
/* DATA step code */ data _null_; length b 8; length val $200; dcl javaobj v("mVector"); v.callVoidMethod("addElement", "abc"); v.callVoidMethod("addElement", "def"); v.callVoidMethod("addElement", "ghi"); dcl javaobj iter("mIterator", v); iter.callBooleanMethod("hasNext", b); do while(b); iter.callStringMethod("next", val); put val=; iter.callBooleanMethod("hasNext", b); end; m.delete(); v.delete(); iter.delete(); run;
/* Java code */ import java.awt.*; import java.util.*; import java.awt.event.*; class colorsUI extends Frame { private Button red; private Button blue; private Button green; private Button quit; private Vector list; private boolean d; private colorsButtonListener cbl; public colorsUI() { d = false; list = new Vector(); cbl = new colorsButtonListener(); setBackground(Color.lightGray); setSize(320,100); setTitle("New Frame"); setVisible(true); setLayout(new FlowLayout(FlowLayout.CENTER, 10, 15)); addWindowListener(new colorsUIListener()); red = new Button("Red"); red.setBackground(Color.red); red.addActionListener(cbl); blue = new Button("Blue"); blue.setBackground(Color.blue); blue.addActionListener(cbl); green = new Button("Green"); green.setBackground(Color.green); green.addActionListener(cbl); quit = new Button("Quit"); quit.setBackground(Color.yellow); quit.addActionListener(cbl); this.add(red); this.add(blue); this.add(green); this.add(quit); show(); } public synchronized void enqueue(Object o) { synchronized(list) { list.addElement(o); notify(); } } public synchronized Object dequeue() { try { while(list.isEmpty()) wait(); if (d) return null; synchronized(list) { Object ret = list.elementAt(0); list.removeElementAt(0); return ret; } } catch(Exception e) { return null; } } public String getNext() { return (String)dequeue(); } public boolean done() { return d; } class colorsButtonListener implements ActionListener { public void actionPerformed(ActionEvent e) { Button b; String l; b = (Button)e.getSource(); l = b.getLabel(); if ( l.equals("Quit") ) { d = true; hide(); l = ""; } enqueue(l); } } class colorsUIListener extends WindowAdapter { public void windowClosing(WindowEvent e) { Window w; w = e.getWindow(); d = true; enqueue(""); w.hide(); } } public static void main(String s[]) { colorsUI cui; cui = new colorsUI(); } }
/* DATA step code */ data colors; length s $10; length done 8; drop done; if (_n_ = 1) then do; /* Declare and instantiate colors object (from colorsUI.class) */ dcl javaobj j("colorsUI"); end; /* * colorsUI.class will display a simple UI and maintain a * queue to hold color choices. */ /* Loop until user hits quit button */ do while (1); j.callBooleanMethod("done", done); if (done) then leave; else do; /* Get next color back from queue */ j.callStringMethod("getNext", s); if s ne "" then output; end; end; run; proc print data=colors; run; quit;
colorsUI
class is instantiated
and the user interface is displayed. You enter a loop that is terminated
when you click Quit. This action is communicated
to the DATA step through the Done variable. While looping, the DATA
step retrieves the values from the Java class's queue and writes the
values successively to the output data set.
x
, which resides in
a folder or directory, y
. You call
the methods in this class by using the Java object with the classpath
that includes the y
folder.
/* Java code */ package com.sas; public class x { public void m() { System.out.println("method m in y folder"); } public void m2() { System.out.println("method m2 in y folder"); } }
/* DATA step code */ data _null_; dcl javaobj j('com/sas/x'); j.callvoidmethod('m'); j.callvoidmethod('m2'); run;
/* Java code */ package com.sas; public class z { public void m() { System.out.println("method m in y folder"); } public void m2() { System.out.println("method m2 in y folder"); } }
y
by
changing the classpath, but this requires restarting SAS. The following
method allows for more dynamic control of how classes are loaded.
m
and m2
.
/* Java code */ import com.sas.x; public class apiImpl implements apiInterface { private x x; public apiImpl() { x = new x(); } public void m() { x.m(); } public void m2() { x.m2(); } }
apiClassLoader
custom
class loader is provided later in this section.
/* Java code */ public class api { /* Load classes from the z folder */ static ClassLoader customLoader = new apiClassLoader("C:\\z"); static String API_IMPL = "apiImpl"; apiInterface cp = null; public api() { cp = load(); } public void m() { cp.m(); } public void m2() { cp.m2(); } private static apiInterface load() { try { Class aClass = customLoader.loadClass(API_IMPL); return (apiInterface) aClass.newInstance(); } catch (Exception e) { e.printStackTrace(); return null; } } }
api
Java
object instance class. The Java object instantiates the api
class,
which creates a custom class loader to load classes from the z
folder.
The api
class calls the custom loader
and returns an instance of the apiImpl
interface
implementation class to the Java object. When methods are called through
the Java object, the api
class delegates
them to the implementation class.
/* DATA step code */ data _null_; dcl javaobj j('api'); j.callvoidmethod('m'); j.callvoidmethod('m2'); run;
ClassLoader
constructor.static ClassLoader customLoader = new apiClassLoader("C:\\z;C:\\temp\some.jar");
import java.io.*; import java.util.*; import java.util.jar.*; import java.util.zip.*; public class apiClassLoader extends ClassLoader { //class repository where findClass performs its search private List classRepository; public apiClassLoader(String loadPath) { super(apiClassLoader.class.getClassLoader()); initLoader(loadPath); } public apiClassLoader(ClassLoader parent,String loadPath) { super(parent); initLoader(loadPath); } /** * This method will look for the class in the class repository. If * the method cannot find the class, the method will delegate to its parent * class loader. * * @param className A String specifying the class to be loaded * @return A Class object loaded by the apiClassLoader * @throws ClassNotFoundException if the method is unable to load the class */ public Class loadClass(String name) throws ClassNotFoundException { // Check if the class is already loaded Class loadedClass = findLoadedClass(name); // Search for class in local repository before delegating if (loadedClass == null) { loadedClass = myFindClass(name); } // If class not found, delegate to parent if (loadedClass == null) { loadedClass = this.getClass().getClassLoader().loadClass(name); } return loadedClass; } private Class myFindClass(String className) throws ClassNotFoundException { byte[] classBytes = loadFromCustomRepository(className); if(classBytes != null) { return defineClass(className,classBytes,0,classBytes.length); } return null; } /** * This method loads binary class file data from the classRepository. */ private byte[] loadFromCustomRepository(String classFileName) throws ClassNotFoundException { Iterator dirs = classRepository.iterator(); byte[] classBytes = null; while (dirs.hasNext()) { String dir = (String) dirs.next(); if (dir.endsWith(".jar")) { // Look for class in jar String jclassFileName = classFileName; jclassFileName = jclassFileName.replace('.', '/'); jclassFileName += ".class"; try { JarFile j = new JarFile(dir); for (Enumeration e = j.entries(); e.hasMoreElements() ;) { Object n = e.nextElement(); if (jclassFileName.equals(n.toString())) { ZipEntry zipEntry = j.getEntry(jclassFileName); if (zipEntry == null) { return null; } else { // read file InputStream is = j.getInputStream(zipEntry); classBytes = new byte[is.available()]; is.read(classBytes); break; } } } } catch (Exception e) { System.out.println("jar file exception"); return null; } } else { // Look for class in directory String fclassFileName = classFileName; fclassFileName = fclassFileName.replace('.', File.separatorChar); fclassFileName += ".class"; try { File file = new File(dir,fclassFileName); if(file.exists()) { //read file InputStream is = new FileInputStream(file); classBytes = new byte[is.available()]; is.read(classBytes); break; } } catch(IOException ex) { System.out.println("IOException raised while reading class file data"); ex.printStackTrace(); return null; } } } return classBytes; } private void initLoader(String loadPath) { /* * loadPath is passed in as a string of directories/jar files * separated by the File.pathSeparator */ classRepository = new ArrayList(); if((loadPath != null) && !(loadPath.equals(""))) { StringTokenizer tokenizer = new StringTokenizer(loadPath,File.pathSeparator); while(tokenizer.hasMoreTokens()) { classRepository.add(tokenizer.nextToken()); } } } }