Una forma de integración con otras aplicciones es transmitir datos en un formato común como caracteres de 8 bits sobre sockets TCP/IP. La desventaja es tener que gastar mucho tiempo en derivar un mensaje de protocolo y mapeado de varias estructuras de datos hacia y desde el formato de transmisión común para que los datos puedan ser enviados y recibidos sobre la conexión TCP/IP.
Aquí es donde pueden ayudar el "Common Object Request Broker Architecture" (CORBA) y su "Interface Definition Language" (IDL). IDL proporciona un formato común para representar un objeto que puede ser distribuido a otras aplicaciones. Las otras aplicaciones podrían incluso no entender de objetos, pero mientras puedan proporcionar un mapeado entre el formato común IDL y sus propia representación de datos, la aplicación podrá compartir los datos.
Este capítulo describe el esquema de mapeo de IDL a lenguaje Java, y cómo reemplazar el original RegistrationBean basado en contenedor controlador por su equivalente servidor CORBA. Los programas SellerBean.java y AuctionServlet.java también se modifican para interoperar con el programa CORBA RegistrationServer.
Esta sección descrbie el esquema de mapedo de Java a IDL y, cuando sea necesario, presenta problemas que debemos tener en consideración.
| Tipo de Dato Java | Formato IDL | Exception |
|---|---|---|
| byte | octet | |
| boolean | boolean | |
| char | char | DATA_CONVERSION |
| char | wchar | |
| double | double | |
| float | float | |
| int | long | |
| int | unsigned long | |
| long | long long | |
| long | unsigned long long | |
| short | short | |
| short | unsigned short | |
| java.lang.String | string | DATA_CONVERSION |
| java.lang.String | wstring | MARSHAL |
Valores sin Signo: Los tipos de datos Java: byte, short, int, y long están representados por entereros de complemento a dos de 8, 16, 32 y 64 bits. Esto significa que un valor short Java representa un rango desde -215 hasta 215 - 1 ó desde -32768 hasta 32767 inclusives. El tipo con signo equivalente IDL para un short, short, es igual en el rango, pero el tipo short IDL sin signo usa el rango desde 0 hata 215 ó desde 0 hasta 65535.
Esto significa que en el caso de short, si un valor short sin signo mayor de 32767 es pasado a un programa escrito en Java, el valor short será representado como un número negativo. Esto puede causar confusión en los límites de test para valores mayores que 32767 o menores que 0.
Tipos char IDL: El lenguaje Java usa un unicode de 16 Bits, pero los tipos char y string de IDL son carcateres de 8 bits. Podemos mapear un char Java a un char IDL de 8 bits para transmitir caracteres multi-byte si usamos un array para hacerlo. Sin embargo, el tipo de caracter ancho de IDL wchar está especialmente diseñado para lenguajes con caracteres multi-bytes y aloja el número fijo de bytes que sea necesario para contener el conjunto del lenguaje para cada una de las letras.
Cuando se mapea desde el tipo char de Java al tipo char de IDL, se puede lanzar la excepción DATA_CONVERSION si el caracter no entra en los 8 bits.
Tipos string IDL: El tipo string IDL puede ser lanzado como una secuencia de tipos char IDL, también lanza la excepción DATA_CONVERSION. El tipo wstring IDL es equivalente a una secuencua de wchars terminada por un wchar NULL.
Un tipo string y un tipo wstring de IDL pueden tener un tamaño fijo o sin máximo definido. Si intentamos mapear un java.lang.String a un string IDL de tamaño fijo y el java.lang.String es demasidado largo, se lanzará una excepción MARSHAL.
Paquetes e Interfaces Java: Las sentencias de paquete Java son equivalentes al tipo module de IDL. Este tipo puede ser anidado, lo que resulta en que las clases Java generadas se crean en subdirectorios anidados.
Por ejemplo, si un programa CORBA contiene esta sentencia de paquete:
el fichero de mapeo debería tener este mapeo a módulo IDL para ella:package registration;
module registration {
};
Si un programa CORBA contiene una herencia de paquete como esta:
su mapeo IDL de módulo será este:package registration.corba;
module registration {
module corba {
};
};
Las clases distribuidas están definidas como interfaces Java y se mapean al tipo interface de IDL. IDL no define accesos como public o private que podríamos encontrar en el lenguaje Java, pero permite descender desde otros interfaces.
Este ejemplo añade el interface Java Registration a un registration module IDL.
module registration {
interface Registration {
};
}
Este ejemplo añade el interface Java Registration a un registration module IDL, e indica que el interface Registration desciende del interface User.
module registration {
interface Registration: User {
};
}
Métodos Java: Los métodos Java se mapean a operaciones IDL. Las operaciones IDL son similares a los métodos Java excepto en que no hay el concepto de control de acceso. También tenemos que ayudar al compilador IDL especificando qué parámetros son de entrada in, de entrada/salida inout o de salida out, definidos de esta forma:
module registration {
interface Registration {
boolean verifyPassword(in string password);
string getEmailAddress();
string getUser();
long adjustAccount(in double amount);
double getBalance();
};
interface RegistrationHome {
Registration findByPrimaryKey(
in RegistrationPK theuser)
raises (FinderException);
}
}
Arrays Java: Los Arrays Java son mapeados a los tipos array o sequence IDL usando una definición de tipo.
Este ejemplo mapea el array Java double balances[10] a un tipo array IDL del mismo tamaño.
Estos ejemplo mapean el array Java double balances[10] a un tipo sequence IDL. El primer typedef sequence es un ejemplo de secuencia sin límite, y el segundo tiene el mismo tamaño que el array.typedef double balances[10];
Excepciones Java: Las excepciones Java son mapeadas a excepciones IDL. Las operaciones usan exceptions IDL especificándolas como del tipo raises.typedef sequence<double> balances; typedef sequence<double,10> balances;
Este ejemplo mapea la CreateException desde la aplicación subastas al tipo exception IDL, y le añade el tipo raises a la operación. Las excepciones IDL siguen las sintaxis C++, por eso en lugar de lanzar una excepción (como se haría en lenguaje Java), la operación alcanza (raise) una excepción.
exception CreateException {
};
interface RegistrationHome {
RegistrationPK create(
in string theuser,
in string password,
in string emailaddress,
in string creditcard)
raises (CreateException);
}
atributo IDL: El tipo attribute IDL es similiar a los métodos get y set usados para acceder a los campos en el software de JavaBeans.
En el caso de un valor declarado como un atributo IDL, el compilador IDL genera dos métodos con el mismo nombre que el atributo IDL. Un método devuelve el campo y otro lo selecciona. Por ejemplo, este tipo attribute:
interface RegistrationPK {
attribute string theuser;
};
define estos métodos:
enum IDL: El lenguaje Java tiene una clase Enumeration para representar una colección de datos. El tipo enum IDL es diferente porque es declarado como un tipo de dato y no una colección de datos.//return user String theuser(); //set user void theuser(String arg);
El tipo enum IDL es una lista de valores que pueden se referenciados por un nombre en vez de por su posición en la lista. En el ejemplo, podemos ver que referirnos al código de estado de un enum IDL por un nombre es mucho más legible que hacerlo por su número. Esta línea mapea los valores static final int de la clase final LoginError. Podemos referirnos a estos valores como lo haríamos con un campo estático:LoginError.INVALID_USER.
enum LoginError {
INVALID_USER, WRONG_PASSWORD, TIMEOUT};
Aquí hay una versión del tipo enum que incluye un subrayado anterior para que pueda ser usado en sentencias switch:
switch (problem) {
case LoginError._INVALID_USER:
System.out.println("please login again");
break;
}
struct IDL: Un tipo struct IDL puede ser comparado con una clase Java que sólo tiene campos, que es cómo lo mapea el compilador IDL.
Este ejemplo declara una struct IDL. Observamos que los tipos IDL pueden referenciar otros tipos IDL. En este ejemplo LoginError viene del tipo enum declarado arriba.
struct ErrorHandler {
LoginError errortype;
short retries;
};
union IDL:
Una union IDL puede representar un tipo de una lista de tipos definidos para esa unión. La union mapea a una clase Java del mismo nombre con un método discriminator usado para determinar el tipo de esa unión.
Este ejemplo mapea la unión GlobalErrors a una clase Java con el nombre GlobalErrors. Se podría añadir un case por defecto case: DEFAULT para manejar cualquier elemento que podría estar en el tipo LoginErrors enum, y no está especificado con una sentencia case aquí.
union GlobalErrors switch (LoginErrors) {
case: INVALID_USER: string message;
case: WRONG_PASSWORD: long attempts;
case: TIMEOUT: long timeout;
};
En un programa escrito en lenguaje Java, la clase unión GlobalErrors se crea de esta forma:
GlobalErrors ge = new GlobalErrors();
ge.message("please login again");
El valor INVALID_USER se recupera de esta forma:
switch (ge.discriminator().value()) {
case: LoginError._INVALID_USER:
System.out.println(ge.message);
break;
}
Tipo Any: si no sabemos que tipo está siento pasado o devuelto desde una operación, podemos usar el tipo Any, que representa cualquier tipo IDL. La siguiente operación retorna y pasa un tipo desconocido:
interface RegistrationHome {
Any customSearch(Any searchField, out count);
};
Para crear un tipo Any, se pide el tipo al "Object Request Broker" (ORB). Para seleccionar un valor de un tipo Any, usamos un método insert_<type>. Para recuperar un valor, usamos el método extract_<type>.
Este ejemplo pide un objeto del tipo Any, y usa el método insert_type para seleccionar un valor.
El tipo Any tiene un valor TypeCode asignado que puede consultarse usando type().kind().value() sobre el objeto. El siguiente ejemplo muestra una prueba del TypeCode double. Este ejemplo incluye una referencia al TypeCode IDL encontrado que contiene el objeto Any. El TypeCode se usa para todos los objetos. Podemos analizar el tipo de un objeto CORBA usando los métodos _type() o type().Any sfield = orb.create_any(); sfield.insert_long(34);
public Any customSearch(Any searchField, IntHolder count){
if(searchField.type().kind().value() == TCKind._tk_double){
// return number of balances greater than supplied amount
double findBalance=searchField.extract_double();
Principal: El tipo Principal identifica al propietario de un objeto CORBA, por ejemplo, un nombre de usuario. El valor puede consultarse desde el campo request_principal de la clase RequestHeader para hacer la identificación.
Object: El tipo Object es un objeto CORBA. Si necesitamos enviar objetos Java, tenemos que traducirlos a un tipo IDL o usar un mecanismo para serializarlos cuando sean transferidos.
Los ficheros SellerBean.java y AuctionServlet.java se han actualizado para que busquen el servidor de registro CORBA.
El servidor de registros CORBA implementa los métodos create y findByPrimaryKey desdel el fichero RegistrationBean.java original, y lo amplía con los dos métodos siguientes para ilustrar las retrollamadas CORBA, y como usar el tipo Any.
module registration {
interface Registration {
boolean verifyPassword(in string password);
string getEmailAddress();
string getUser();
long adjustAccount(in double amount);
double getBalance();
};
interface RegistrationPK {
attribute string theuser;
};
enum LoginError {INVALIDUSER, WRONGPASSWORD, TIMEOUT};
exception CreateException {
};
exception FinderException {
};
typedef sequence<Registration> IDLArrayList;
interface ReturnResults {
void updateResults(in IDLArrayList results)
raises (FinderException);
};
interface RegistrationHome {
RegistrationPK create(in string theuser,
in string password,
in string emailaddress,
in string creditcard)
raises (CreateException);
Registration findByPrimaryKey(
in RegistrationPK theuser)
raises (FinderException);
void findLowCreditAccounts(in ReturnResults rr)
raises (FinderException);
any customSearch(in any searchfield, out long count);
};
};
Los argumentos -fno-cpp indican que no hay compilador C++ instalado.
Otros compiladores Java IDL también deberían funcionar, por ejemplo, jidl de ORBacus puede generar clases que pueden ser usadas por el ORB de Java 2.idltojava -fno-cpp Registration.idl
Cuando se solicita un objeto CORBA remoto o se llama a un método remoto, la llamada del cliente pasa a través de la clase stub antes de alcanzar el servidor. Este clase proxy invoca la petición CORBA para el programa cliente. El siguiente ejemplo es el código generado automáticamente por la clase RegistrationHomeStub.java.
org.omg.CORBA.Request r = _request("create");
r.set_return_type(
registration.RegistrationPKHelper.type());
org.omg.CORBA.Any _theuser = r.add_in_arg();
La plataforma Java 2 incluye un ORB en la distribución llamado el IDL ORB. Este ORB es diferente de otros muchos ORBs porque no incluye un distintivo de "Basic Object Adapter" (BOA) o "Portable Object Adapter" (POA).
Una adaptador de objetos maneja la creacción y ciclo de vida de los objetos en un espacio distribuido CORBA. Esto puede ser comparado con el contenedor del servidor de JavaBeans Enterprise que maneja el ciclo de vida de los beans de entidad y de sesión.
Los programas AuctionServlet y SellerBean crean e inicializan un ORB de Java 2 de esta forma:
En el programa RegistrationServer, el objeto servidor puede ser distribuido en unión con el ORB usando el método connect:ORB orb = ORB.init(args, null);
Un objeto conectado a un ORB puede ser eleminado con el método disconnect:RegistrationServer rs = new RegistrationServer(); orb.connect(rs);
Una vez conectado a un objeto servidor CORBA, el ORB Java2 mantiene vivo el servidor y espera peticiones del cliente para el servidor CORBA.orb.disconnect(rs);
java.lang.Object sync = new java.lang.Object();
synchronized(sync) {
sync.wait();
}
El servicio de nombres Java 2 llamado tnameserv, por defecto usa el puerto 900; sin embargo, este valor puede modificarse seleccionado el argumento -ORBInitialPort portnumber cuando se arranca tnameserv o seleccionando la propiedad org.omg.CORBA.ORBInitialPort cuando arrancamos los procesos cliente y servidor.
Las siguientes secciones describen el método main de la clase RegistrationServer.
java.util.Properties props=System.getProperties();
props.put("org.omg.CORBA.ORBInitialPort", "1050");
System.setProperties(props);
ORB orb = ORB.init(args, props);
Las siguientes líneas muestran que la referencia inicial de nombres es inicializada por la petición del servicio llamado NameService. El NamingContext es recuperado y el nombre construido y unido al servicio de nombres como elementos NameComponent. El nombre de este ejemplo tiene una raíz llamada auction que es este objeto que se está uniendo como RegistrationBean desde la raíz auction. El nombre podría ser comparado por una clase mediante el nombre de auction.RegistrationBean.
org.omg.CORBA.Object nameServiceObj =
orb.resolve_initial_references("NameService") ;
NamingContext nctx =
NamingContextHelper.narrow(nameServiceObj);
NameComponent[] fullname = new NameComponent[2];
fullname[0] = new NameComponent("auction", "");
fullname[1] = new NameComponent(
"RegistrationBean", "");
NameComponent[] tempComponent = new NameComponent[1];
for(int i=0; i < fullname.length-1; i++ ) {
tempComponent[0]= fullname[i];
try{
nctx=nctx.bind_new_context(tempComponent);
}catch (Exception e){
System.out.println("bind new"+e);}
}
tempComponent[0]=fullname[fullname.length-1];
try{
nctx.rebind(tempComponent, rs);
}catch (Exception e){
System.out.println("rebind"+e);
}
Usar un nuevo ORB en el servidor de registros requiere que las propiedades org.omg.CORBA.ORBClass y org.omg.CORBA.ORBSingletonClass apunten a las clases ORB apropiadas. En este ejemplo se usa el ORB ORBacus en lugar del ORB IDL de Java 2. Para usar otro ORB, el código de abajo debería conectarse dentro del método RegistrationServer.main.
En el código de ejemplo, se usa un ORB SingletonClass. Este ORB no es un ORB completo, y su uso primario es como factoría para TypeCodes. La llamada a ORB.init() en la última línea crea el ORB Singleton.
Properties props= System.getProperties();
props.put("org.omg.CORBA.ORBClass",
"com.ooc.CORBA.ORB");
props.put("org.omg.CORBA.ORBSingletonClass",
"com.ooc.CORBA.ORBSingleton");
System.setProperties(props);
ORB orb = ORB.init(args, props) ;
En el IDL de Java 2, no hay un objeto adaptador distinto. Como se muestra en el segmento de código inferior, usar el "Basic Object Adapter" desde ORBacus requiere un conversión explícita al ORB ORBacus, El "Broker Object Architecture" (BOA) es notificado de que el objeto ya está distribuido llamando al método impl_is_ready(null).
BOA boa = ((com.ooc.CORBA.ORB)orb).BOA_init(
args, props);
...
boa.impl_is_ready(null);
Aunque los dos ORBs ORBSingletonClass y ORBClass construyen el nombre del objeto usando NameComponent, tenemos que usar un diferente servicio de nombres ORBacus. El servicio CosNaming.Server se arranca de la siguiente forma, donde el parámetro
-OAhost es opcional:
Una vez arrancado el servicio de nombres, los programas cliente y servidor pueden encontrar el servicio de nombres usando el protocolo IIOP hacia el host y el puerto nombrados cuando se arrancó el servicio de nombrado:java com.ooc.CosNaming.Server -OAhost localhost -OAport 1060
java registration.RegistrationServer -ORBservice NameService iiop://localhost:1060/DefaultNamingContext
Las clases AuctionServlet y SellerBean usan el siguiente código para buscar el servidor CORBA:
NameComponent[] fullname = new NameComponent[2];
fullname[0] = new NameComponent("auction", "");
fullname[1] = new NameComponent(
"RegistrationBean", "");
RegistrationHome regRef =
RegistrationHomeHelper.narrow(
nctx.resolve(fullname));
En el caso del ORB ORBacus, los clientes también necesitan un "Basic Object Adapter" si se usan retrollamadas en el método SellerBean.auditAccounts. El contexto de nombrado también se configura de forma diferente para el servidor ORBacus arrancado anteriormente:
Object obj = ((com.ooc.CORBA.ORB)orb).get_inet_object ( "localhost", 1060, "DefaultNamingContext"); NamingContext nctx = NamingContextHelper.narrow(obj);
Las clases Holder contienen valores devueltos cuando se usan parámetros inout o out en un método. El llamador primero ejemplariza la clase Holder apropiada para ese tipo y recupera el valor desde la clase cuando la llamada retorna. En el siguiente ejemplo, el valor del contador customSearch se configura y recupera después de que se haya llamado a customSearch. En el lado del servidor el valor del contador se selecciona llamando a count.value=newvalue.
IntHolder count= new IntHolder();
sfield=regRef.customSearch(sfield,count);
System.out.println("count now set to "+count.value);
El método AuctionServlet.auditAccounts llama al método SellerBean.auditAccounts, que devuelve un ArrayList de registros de Registration.
//AuctionServlet.java
private void auditAccounts(ServletOutputStream out,
HttpServletRequest request) throws IOException{
// ...
SellerHome home = (SellerHome) ctx.lookup("seller");
Seller si= home.create();
if(si != null) {
ArrayList ar=si.auditAccounts();
for(Iterator i=ar.iterator(); i.hasNext();) {
Registration user=(Registration)(i.next());
addLine("<TD>"+user.getUser() +
"<TD><TD>"+user.getBalance()+
"<TD><TR>", out);
}
addLine("<TABLE>", out);
}
El objeto SellerBean llama al método CORBA RegistrationHome.findLowCreditAccounts implementado en el fichero RegistrationServer.java, y se pasa una referencia a sí mismo. La referencia es pasada siempre que la clase SellerBean implemente el interface ReturnResults declarado en el Registration.idl.
//SellerBean.java
public ArrayList auditAccounts() {
try{
NameComponent[] fullname = new NameComponent[2];
fullname[0] = new NameComponent("auction", "");
fullname[1] = new NameComponent(
"RegistrationBean", "");
RegistrationHome regRef =
RegistrationHomeHelper.narrow(
nctx.resolve(fullname));
regRef.findLowCreditAccounts(this);
synchronized(ready) {
try{
ready.wait();
}catch (InterruptedException e){}
}
return (returned);
}catch (Exception e) {
System.out.println("error in auditAccounts "+e);
}
return null;
}
El método RegistrationServer.findLowCreditAccounts recupera los registros de usuario desde la tabla Registration de la base de datos que tengan un valor de crédito menor de tres. Entonces devuelve la lista de registros Registration en un ArrayList llamando al método SellerBean.updateResults que tiene una referencia a ella.
//RegistrationServer.java
public void findLowCreditAccounts(
final ReturnResults client)
throws Finder Exception {
Runnable bgthread = new Runnable() {
public void run() {
Connection con = null;
ResultSet rs = null;
PreparedStatement ps = null;
ArrayList ar = new ArrayList();
try{
con=getConnection();
ps=con.prepareStatement(
"select theuser,
balance from registration
where balance < ?");
ps.setDouble(1, 3.00);
ps.executeQuery();
rs = ps.getResultSet();
RegistrationImpl reg=null;
while (rs.next()) {
try{
reg= new RegistrationImpl();
}catch (Exception e) {
System.out.println("creating reg"+e);
}
reg.theuser = rs.getString(1);
reg.balance = rs.getDouble(2);
ar.add(reg);
}
rs.close();
RegistrationImpl[] regarray =
(RegistrationImpl [])ar.toArray(
new RegistrationImpl[0]);
client.updateResults(regarray);
}catch (Exception e) {
System.out.println(
"findLowCreditAccounts: "+e);
return;
}
finally {
try{
if(rs != null) {
rs.close();
}
if(ps != null) {
ps.close();
}
if(con != null) {
con.close();
}
}catch (Exception ignore) {}
}
}//run
};
Thread t = new Thread(bgthread);
t.start();
}
El método SellerBean.updateResults actualiza el ArrayList global de registros de Registration devuelto por el objeto RegistrationServer y notifica al método SellerBean/auditAccounts que puede devolver este ArrayList de registros Registration al AuctionServlet.
public void updateResults(Registration[] ar)
throws registration.FinderException {
if(ar == null) {
throw new registration.FinderException();
}
try{
for(int i=0; i< ar.length; i++) {
returned.add(ar[i]);
}
}catch (Exception e) {
System.out.println("updateResults="+e);
throw new registration.FinderException();
}
synchronized(ready) {
ready.notifyAll();
}
}
http://localhost.eng.sun.com:7001/
AuctionServlet?action=customSearch&searchfield=2
El parámetro searchfield puede ser seleccionado como un número o un string. El método AuctionServlet.customFind pasa el campo de búsqueda directamente al método SellerBean.customFind que recupera un String que luego es mostrado al usuario:
private void customSearch(ServletOutputStream out,
HttpServletRequest request)
throws IOException{
String text = "Custom Search";
String searchField=request.getParameter(
"searchfield");
setTitle(out, "Custom Search");
if(searchField == null ) {
addLine("Error: SearchField was empty", out);
out.flush();
return;
}
try{
addLine("<BR>"+text, out);
SellerHome home = (SellerHome)
ctx.lookup("seller");
Seller si= home.create();
if(si != null) {
String displayMessage=si.customFind(
searchField);
if(displayMessage != null ) {
addLine(displayMessage+"<BR>", out);
}
}
}catch (Exception e) {
addLine("AuctionServlet customFind error",out);
System.out.println("AuctionServlet " +
"<customFind>:"+e);
}
out.flush();
}
El método SellerBean.customFind llama al objeto RegistrationHome implementado en la clase RegistrationServer.java, y dependiendo de si el searchField puede ser convertido a un double o a un string, inserta este valor dentro de un objeto del tipo Any. El objeto Any se crea mediante una llamada al ORB, orb.create_any();
El método customFind también usa un parámetro out, count, del tipo int que devuelve el número de registros encontrados. El valor de count se recupera usando count.value cuando la llamada retorna:
//SellerBean.java
public String customFind(String searchField)
throws javax.ejb.FinderException,
RemoteException{
int total=-1;
IntHolder count= new IntHolder();
try{
NameComponent[] fullname = new NameComponent[2];
fullname[0] = new NameComponent("auction", "");
fullname[1] = new NameComponent(
"RegistrationBean", "");
RegistrationHome regRef =
RegistrationHomeHelper.narrow(
nctx.resolve(fullname));
if(regRef == null ) {
System.out.println(
"cannot contact RegistrationHome");
throw new javax.ejb.FinderException();
}
Any sfield=orb.create_any();
Double balance;
try{
balance=Double.valueOf(searchField);
try {
sfield.insert_double(balance.doubleValue());
}catch (Exception e) {
return("Problem with search value"+balance);
}
sfield=regRef.customSearch(sfield,count);
if(sfield != null ) {
total=sfield.extract_long();
}
return(total+"
accounts are below optimal level from" +
count.value+" records");
}catch (NumberFormatException e) {
sfield.insert_string(searchField);
Registration reg;
if((reg=RegistrationHelper.extract(
regRef.customSearch(
sfield,count)))
!= null ) {
return("Found user "+reg.getUser() +"
who has email address "+
reg.getEmailAddress());
}else {
return("No users found who have email address " +
searchField);
}
}
}catch(Exception e){
System.out.println("customFind problem="+e);
throw new javax.ejb.FinderException();
}
}
El valor devuelto desde la llamada a customFind se extrae dentro de un objeto del tipo Any y se construye un String con la salida mostrada al usuario. Para los tipos sencillos se puede usar el método extract_<type> de Any. Sin embargo, para el tipo Registration, se usa la clase RegistrationHelper.
Registration reg =
RegistrationHelper.extract(
regRef.customSearch(sfield,count))
El método RegistrationServer.customSearch determina el tipo del objeto que está siendo pasado en el parámetro searchField chequeando el .type().kind().value() del objeto Any.
if(searchField.type().kind().value() ==
TCKind._tk_double)
Finalmente, como el método customSearch devuelve un objeto del tipo Any, se requiere una llamada a orb.create_any(). Para tipos sencillos como double, se usa el método insert_<type>. Para el tipo Registration , se usa la clase RegistrationHelper: RegistrationHelper.insert(returnResults, regarray[0]).
//RegistrationServer.java
public Any customSearch(Any searchField,
IntHolder count){
Any returnResults= orb.create_any();
int tmpcount=count.value;
if(searchField.type().kind().value() ==
TCKind._tk_double){
// return number of balances greater
// than supplied amount
double findBalance=searchField.extract_double();
Connection con = null;
ResultSet rs = null;
PreparedStatement ps = null;
try{
con=getConnection();
ps=con.prepareStatement("select count(*) from
registration where balance < ?");
ps.setDouble(1, findBalance);
ps.executeQuery();
rs = ps.getResultSet();
if(rs.next()) {
tmpcount = rs.getInt(1);
}
count.value=tmpcount;
rs.close();
}catch (Exception e) {
System.out.println("custom search: "+e);
returnResults.insert_long(-1);
return(returnResults);
}
finally {
try{
if(rs != null) { rs.close(); }
if(ps != null) { ps.close(); }
if(con != null) { con.close(); }
} catch (Exception ignore) {}
}
returnResults.insert_long(tmpcount);
return(returnResults);
}else if(searchField.type().kind().value() ==
TCKind._tk_string) {
// return email addresses that match supplied address
String findEmail=searchField.extract_string();
Connection con = null;
ResultSet rs = null;
PreparedStatement ps = null;
ArrayList ar = new ArrayList();
RegistrationImpl reg=null;
try{
con=getConnection();
ps=con.prepareStatement("select theuser,
emailaddress from registration
where emailaddress like ?");
ps.setString(1, findEmail);
ps.executeQuery();
rs = ps.getResultSet();
while (rs.next()) {
reg= new RegistrationImpl();
reg.theuser = rs.getString(1);
reg.emailaddress = rs.getString(2);
ar.add(reg);
}
rs.close();
RegistrationImpl[] regarray =
(RegistrationImpl [])ar.toArray(
new RegistrationImpl[0]);
RegistrationHelper.insert(
returnResults,
regarray[0]);
return(returnResults);
}catch (Exception e) {
System.out.println("custom search: "+e);
return(returnResults);
}
finally {
try{
if(rs != null) { rs.close(); }
if(ps != null) { ps.close(); }
if(con != null) { con.close(); }
} catch (Exception ignore) {}
}
}
return(returnResults);
}
_______
1 Cuando se usan en toda esta site, los términos, "Java virtual machine" o "JVM" significa una máquina virtual de la plataforma Java.