8.2. Sesiju EJB komponenti

Ievads

Mērķi

  • Saprast parametru nodošanas mehānismu RMI-IIOP gadījumā
  • Saprast JNDI mehānismu EJB komponenšu atrašanai

EJB klienta darbības apraksts

package ejbexample.client;

import java.util.*;
import java.math.*;
import javax.naming.*;                  // pakotne, kas satur vārdu servisa klases

import javax.rmi.PortableRemoteObject;

import ejbexample.ejb.BankSessionEJB;           // komponenta saskarne          
import ejbexample.ejb.BankSessionEJBHome;       // home saskarne        
import ejbexample.valueobjects.*;               // vērtības objektu pakotne

public class BankSessionEJBClient 
{
  public static void main(String [] args)
  {
    try
    {
      Context context = new InitialContext();

      Object obj = context.lookup("BankSessionBean");   // meklē home saskarni
        
      BankSessionEJBHome bankSessionEJBHome = (BankSessionEJBHome) 
          PortableRemoteObject.narrow(obj, BankSessionEJBHome.class);

      BankSessionEJB bankSessionEJB;                    // komponenta saskarne  

      bankSessionEJB = bankSessionEJBHome.create();     // izveido komponenta instanci  

      CustomerVO cust = new CustomerVO();       // izviedo klienta aprakstu (value object)

      cust.setPersonasKods("101078-12334");
      cust.setAdrese("Ikskile, Lapu iela - 15");
      cust.setVards("Janis");
      cust.setUzvards("Berzs");
      cust.setDzimsanasDatums( (new GregorianCalendar(1978, 9, 10)).getTime());
                                  
      bankSessionEJB.addCustomer( cust);         // pievieno klienta datus datubāzei

      bankSessionEJB.openNewAccount("101078-12334","8875264445849"); // pievieno konta datus

      bankSessionEJB.deposit("8875264445849",new BigDecimal("124.00"));
        
      System.out.println("acounts...");         
      
      AccountVO[] konti = bankSessionEJB.readCustomersAccounts("101078-12334");

      for (int i =0; i<konti.length; i++)         // izdrukā kontu sarakstu
      {
        System.out.println(konti[i].getKontaNumurs());
        System.out.println(konti[i].getSumma());
      }
    }
    catch(Throwable ex)
    {
      System.out.println("Error: "+ex.getMessage());
    }
  }
}

Klase InitialConext veic sākotnējo savienojumu ar OC4J. Savienojuma parametrus var nodod šādos veidos:

  • InitialContext konstruktorā padod heštabulu..
  • Izveidot failu jndi.properties, kurā norādīt savienojuma parametrus. Šim failam ir jāatrodas CLASSPATH ceļā.
java.naming.factory.initial=com.evermind.server.rmi.RMIInitialContextFactory
java.naming.provider.url=ormi://localhost/ExampleBankApp
java.naming.security.principal=admin
java.naming.security.credentials=admin

Tipiskie EJB komponenta būvēšanas soļi

  • Uzraksta java klases, no kurām sastāv EJB komponents: komponenta saskarne, home saskarne un komponenta implementācijas klase.
  • Izveido izvietošanas (deployment) deskriptoru.
  • Sakompilē java klases.
  • Klases kopā ar izvietošanas deskriptoru arhivē jar failā.
  • Jar failu izvieto EJB konteinerā (serverī).
  • Var izveidot vienkāršu EJB klientu (izpildāmu java klasi), kas izsauc EJB komponenta metodes.

Piemērs:"Hello World" sesijas komponenta izstrāde.

Mērķis ir izveidot vienkāršu sesijas komponentu, kurš satur vienu metodi, kas atgriež "Hello World !" rindiņu. Šis komponents izmantos attālinātās saskarnes, lai to varētu izsaukt pa tīklu.

Komponenta saskarne.

Komponenta saskarnē norāda komponenta biznesa loģikas metodes. Tā ir attālināta saskarne, tās metodes izsauc pa tīklu. Saskarnei ir šādas īpašības.

  • Visas attālinātās komponenta saskarnes tiek atvasinātas no EJBObject saskarnes.
  • Visām metodēm ir jāmet RemoteException. Šis izņēmums tiek izsaukts tīkla komunikācijas vai servera problēmu gadījumā.
  • Komponenta klasē ir nepieciešams implementēt visas šīs metodes.

Mūsu piemērā nepieciešama tikai viena metode.

package helloworldejb;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;

public interface HelloWorldSessionEJB extends EJBObject {
  String hello() throws RemoteException;        
}

Home saskarne

Šī saskarne satur ar komponenta dzīves ciklu saistītas metodes. Piemērā ir nepieciešama tikai viena metode, kas izveido sesijas komponenta instanci. Šī metode atgriež izveidotā komponenta saskarni.

package helloworldejb;
import javax.ejb.EJBHome;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import helloworldejb.HelloWorldSessionEJB;

public interface HelloWorldSessionEJBHome extends EJBHome {
  HelloWorldSessionEJB create() throws RemoteException, CreateException;
}

Izvietošanas deskriptors (deployment descriptor)

Izvietošanas deskriptors ejb-jar.xml apraksta komponenta tipu un tā sastāvdaļas.

<?xml version = '1.0' encoding = 'windows-1257'?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN" "http://java.sun.com/dtd/ejb-jar_2_0.dtd">
<ejb-jar>
  <enterprise-beans>
    <session>
      <description>Session Bean ( Stateless )</description>
      <display-name>HelloWorldSessionEJB</display-name>
      <ejb-name>HelloWorldSessionEJB</ejb-name>
      <home>helloworldejb.HelloWorldSessionEJBHome</home>
      <remote>helloworldejb.HelloWorldSessionEJB</remote>
      <ejb-class>helloworldejb.HelloWorldSessionEJBBean</ejb-class>
      <session-type>Stateless</session-type>
      <transaction-type>Container</transaction-type>
    </session>
  </enterprise-beans>
</ejb-jar>

OC4J izvietošanas deskriptors

<?xml version = '1.0' encoding = 'windows-1257'?>
<!DOCTYPE orion-ejb-jar PUBLIC "-//Evermind//DTD Enterprise JavaBeans 1.1 runtime//EN" "http://xmlns.oracle.com/ias/dtds/orion-ejb-jar.dtd">
<orion-ejb-jar>
  <enterprise-beans>
    <session-deployment name="HelloWorldSessionEJB" location="HelloWorldBean"/>
  </enterprise-beans>
  <assembly-descriptor>
    <default-method-access>
      <security-role-mapping impliesAll="true" name="<default-ejb-caller-role>"/>
    </default-method-access>
  </assembly-descriptor>
</orion-ejb-jar>

Sesijas komponenta klients

package helloworldejb;
import javax.naming.*;
import javax.rmi.PortableRemoteObject;
import helloworldejb.HelloWorldSessionEJB;
import helloworldejb.HelloWorldSessionEJBHome;
import javax.naming.NamingException;

public class HelloWorldSessionEJBClient {
  public static void main(String [] args) {
    try {
      Context context = new InitialContext();
        
      // atrod home saskarni
      Object obj = context.lookup("HelloWorldBean");    

      // atrasto objektu pârveido par home saskarni     
      HelloWorldSessionEJBHome helloWorldSessionEJBHome = 
          (HelloWorldSessionEJBHome)PortableRemoteObject.narrow(obj, HelloWorldSessionEJBHome.class);

      // izveido sesijas komponenta instanci
      HelloWorldSessionEJB helloWorldSessionEJB = helloWorldSessionEJBHome.create();

      // izsauc vienu no komponenta metodēm
      String greeting = helloWorldSessionEJB.hello();

      System.out.println(greeting);

      helloWorldSessionEJB.remove();    // izdzēš komponenta instanci
    }
    catch(Throwable ex) {
      System.out.println("Error: "+ex.getMessage());
    }
  }
}

JNDI

Izvietojuma caurspīdīgums

Fiziskais resursu izvietojums klientam EJB vidē ir noslēpts. Sekojošas priekšrocības:

  • Klienta kods nevar tikt piesaistīts konkrētās vides konfigurācijai, jo resursu atrašanās vietas nekur nav iešūtas. Atkalizmantojamās komponentes var cita citu atrast dažādās daudzslāņu situācijās.
  • EJB konteineri var veidot resursu klasterus un klientus automātiski pārsūtīt uz alternatīviem servisiem, ja sākotnēji pieprasītie servisi nedarbojas

EJB aplikācijas klients, gribot atrast EJB komponentu raksta sekojoši:

// savāc objektu pēc vārdu servisa nosaukuma
Object obj = initialContext.lookup("BankSessionBean");
// pārveido par "Home" interfeisu
BankSessionEJBHome home = (BankSessionEJBHome)
    PortableRemoteObject.narrow(obj,
    BankSessionEJBHome.class);

Vārdu serviss

  • Vārdu serviss saņem vārdu un sameklē šim vārdam atbilstošo objektu
  • Piemēri - "118" telefona dienests, DNS (Domain Name Service), failu sistēma, kura pēc vārda atrod faila fizisko novietojumu, utml.
  • Dažus no objektiem vārdu sistēmā sauc par "direktorijiem" (directory object) - tiem var būt atribūti
  • Līdzīgas vārdu servisiem ir arī java.util.Map datu struktūras, bet vārdu servisi parasti ir nevis "plakanas", bet gan "sazarotas" vārdnīcas

Plakana (nehierarhiska) vārdapgabala trūkumi ir acīmredzami. Piemēram, A.Čehova stāstā "Vaņka Žukovs" galvenais varonis raksta garu vēstuli, kurai pievieno adresi "На деревню дедушке".

Vārdu servisu protokoli

  • Lightweight Directory Access Protocol (LDAP)
  • Network Information System (NIS)
  • Network Directory System (NDS) - Novell
  • JNDI - Javas platformneatkarīgs interfeiss uz visiem šiem protokoliem

Tāpat kā JDBC, arī JNDI piedāvā Javas klientiem unificētu interfeisu, bet JNDI funkcionalitāti priekš dažādiem serveriem nodrošina dažādi "servisa piegādātāji", kuri darbojas līdzīgi datubāžu draiveriem.

JNDI jēdzieni

  • Atomārs vārds (atomic name) - nedalāms komponents, kurš var ietilpt saliktā vārdā. Piemēram, /etc/fstab satur divus atomārus vārdus: "etc" un "fstab".
  • Salikts vārds - viens vai vairāki atomāri vārdi, kuri sarakstīti ar atdalītājsimbolu, piemēram "/"
  • Saistījums (binding) - (atomāra) vārda pierakstīšana konkrētam objektam
  • Konteksts - īpaša veida objekts, kurš satur 0 vai vairāk saistījumus
  • Apakškonteksts (subcontext) - tāds konteksts, kura saistījums ir augstāka līmeņa kontekstā
  • Vārdu sistēma (naming system) - savstarpēji saistīti konteksti, kuri izmanto konkrētu vārdu piešķiršanas mehānismu (LDAP personu direktorija, failu sistēma, Javas pakotņu sistēma)
  • Vārdtelpa (namespace) - visi vārdi, kuri ietilpst vārdu sistēmā (piemēram, visu failu vārdi uz diska)
  • Kompozīta vārdtelpa (composite namespace) - vairāku vārdu sistēmu kopsalikums vienā. Piemēram http://java.sun.com/products/ejb/index.html sastāv no šādām vārdtelpām:
  • http - protokolu vārdtelpas elements; tur var lietot arī, piemēram, ftp
  • java.sun.com - DNS kontekstā šim vārdam atbilst konkrēta IP adrese
  • products, ejb, index.html - failu sistēmas vārdtelpas elementi uz Web servera

Sākotnējais konteksts

Sākotnējā konteksta inicializācija:

Hashtable env = new Hashtable(); 
env.put(Context.INITIAL_CONTEXT_FACTORY, 
                  "com.evermind.server.rmi.RMIInitialContextFactory"); 
env.put(Context.SECURITY_PRINCIPAL, "admin"); 
env.put(Context.SECURITY_CREDENTIALS, "base"); 
env.put(Context.PROVIDER_URL, "ormi://localhost/ExampleBankApp"); 
InitialContext ic = new InitialContext(env); 

Šādi izskatās "JNDI pārlūka" spraudnis priekš Eclipse. Tas attēlo JNDI koka struktūru dotajiem JNDI servisa parametriem.

Darbības ar JNDI kontekstiem

  • list(String), listBindings(String) - Atgriež visu saistījumu sarakstu dotajam apakškontekstam. Piemēram, list("") atgriež paša konteksta saistījumus.
  • lookup() - Atgriež konkrētajam vārdam atbilstošo objektu. Var izmantot, lai mainītu kontekstus (tāpat kā "cd" komanda direktoriju maiņām)
  • rename() - maina konteksta vārdu
  • createSubcontext(), destroySubcontext() - veido un izmet apakškontekstu
  • bind() - izveido jaunu saistījumu kontekstā, rodas izņēmums, ja vārds jau ir aizņemts
  • rebind() - izveido jaunu saistījumu kontekstā, vai izmaina veco
  • unbind() - izmet saistījumu no konteksta

    Konteksts tomēr uzvedas savādāk, nekā, piemēram failu sistēmas direktorija java.io.File. Konteksts var darboties tikai ar saviem saistījumiem, bet tas nevar pateikt savu absolūto taciņu (salikto vārdu), vai pāriet direktoriju kokā uz augšu. JNDI kontekstiem bieži nevar arī viennozīmīgi pateikt, kura ir saknes direktorija, jo InitialContext var uzstādīt dažādās vietās - to apakškonteksti pēc tam var daļēji pārklāties.

Komponentu konteinera un JNDI integrācija

  • Konteiners publicē savas komponentes JNDI kokā
  • Klients meklē komponenti labi zināmā JNDI sākotnējā kontekstā
  • Pēc vārda savāc klienta puses starpnieku, utt.