Você está na página 1de 87

JMP106: Introduction to Java for LotusScript Programmers

Steve Brown, Iris Associates Gary Devendorf, Lotus Development

This Presentation
Prerequisite LotusScript After this Presentation Java basics Domino Java Classes Domino Java agent Domino JSP Recommend Java programming course OO design course

Agenda
Why Java ? Basic Java concepts Syntax / semantics Class methods Class packages, jar files Accessing Domino via Java Domino Objects Agents, Applications, Applets, Servlets, JSPs Demos throughout

Java
The Java programming language is a modern, evolutionary computing language that combines an elegant language design with powerful features that were previously available primarily in specialty languages. In addition to the core language components, Java platform software distributions include many powerful, supporting software libraries for tasks such as database, network, and graphical user interface programming.

LotusScript
LotusScript is an embedded, BASIC scripting

language with a powerful set of language extensions that enable object-oriented application development within and across Lotus products.

LotusScript's Future

LotusScript is going nowhere! Gary Devendorf - 1998

Why Java ? Java? Why


Advantages Cross Platform Nonproprietary Richer class library Multithreading Code reuse across Agents, Applications, Applets, Servlets Integrates with 3rd party Java applications Object Oriented Language Features advanced architecture inheritance design patterns

Why Java ?
Some Backend Classes are only Java String Handling abilities are superior to LS Utility Classes for Web, Networking and XML JDBC to integrate with SQL data Everyone today has a Java API Help is everywhere : Books, usergroups, ListServers, etc. Scalable Data Structures no arbitrary limits Collection Classes - Vector, Hashtable, etc. Lots of IDEs to choose from

Ok, Give me one good example of why I would choose Java.


How about URL encoding and unencoding ?

URL Encoding LS
Call an @function cmd = {@URLEncode("Platform"; "} + doc.myLink(0) + {")} encURL = Evaluate( cmd ) ************************************************************* Encode a Link in LotusScript using a reentrant function function URLEncode(query As String) As String Dim c As Integer Dim x As Integer For x = 1 To Len(query) c = Asc(Right(Left(query,x),1)) If (c < 48) Or (c > 57 And c < 65) Or (c > 90 And c < 97) Or (c > 122) Then URLEncode = URLEncode & "%" & Hex(c) Else URLEncode = URLEncode & Chr(c) End If Next End Function

Encode a URL in Java


String encURL = URLEncoder.encode(doc.getItemValueString(myLink);

Traditional Programming Environment


Class Libraries (e.g. *.lib)
Compiler Linker

Executable
e.g. myprog.exe

Source Files e.g. *.C++

Objects e.g. *.obj

O/S Specific Machine Code

Your Machine at Runtime

Executable
e.g. myprog.exe

Operating System

Java Environment
Class Packages Source Files
Compiler

(analogy - script libraries)

Java ByteCode Files *.class

*.java

Runtime

Java Virtual Machine (JVM) Win32 JavaOs Solaris Mac Others

"Write Once Run Anywhere"

Notes Client
Notes Form
12 11 1 10 2 9 3 8 7 6 5 4

Notes Agent

Applet

*.class

4.6.5 / 5.0
Notes Java Classes (Notes.jar) JVM 1.1.x & Core Classes Notes Client API OS

Forms execute Applets Agents imported Java Classes code Java directly compiled in Designer

RPC

.NSF

Domino Server

Your Machine

Notes 4.6 / 5.0

Domino Server
Server

Notes Agent

Servlets

*.class

*.class

Notes Java Classes (Notes.jar) JVM 1.1.x & Core Classes Notes Client API OS

Agents execute HTTP, Agent Manager Imported Java Classes Written Java in Agent compiled in Designer Servlets Domino Servlet Engine Use Domino Classes **

RPC

.NSF

Domino 4.6/5.0

Domino Java Classes


Introduced in 4.6 Java language binding to backend CAPI Agents, stand-alone apps, Servlets, Applets JVM 1.1.1 Enhanced in 5.0 JVM 1.1.8 (5.06) New and enhanced classes Agents, stand-alone apps*, Servlets*, Applets* retrieval of Session object different

Session please!
Agent: AgentBase.getSession() Applet: AppletBase.openSession() Application, Servlet, JSP: NotesFactory.createSession()

Bug Report

Brief Description:* Java agent crashes when creating a large number of documents ------------------------------------------------------------------------------------------------------------------Steps to Reproduce: If you create a Java agent to create large number of documents in a database, the agent will fail with error message: MemAlloc: OUT OF PRIVATE HANDLES! -- pid 0000035C Handles used so far 16415, In my environment the problem occurs consistently after 10331 documents.

I include the agent to reproduce the problem here since it is so short. It runs in an endless loop creating documents. It prints Ping! to the console every 1000 documents, and Poing! every 10 000 documents.

Work Around:

None, apart from writing the script in LotusScript

Impact: Potentially large. Currently none.

Bug Report Code


Database db = agentContext.getCurrentDatabase(); while (true) { doc = db.createDocument(); doc.appendItemValue("Address", "1.2.3.4"); doc.appendItemValue("Form","Entry"); a++; if (a % 1000 == 0 ) System.out.println("Ping!"); if (a % 10000 == 0 ) System.out.println("Poing!"); if (!doc.save()) System.out.println("Unable to save document"); }

Memory Management
In LotusScript you don't worry about it, in C you do. In Java you don't get Memory leaks but you can get yourself in to trouble with "unCollected" memory. Java has a Garbage Collector that runs when Java thinks it should. But Java can only see the wrappers not the code under them.

Notes 4.6/5.0 Java Notes Classes


Java LotusScript
Agents Events Agents* Applications Servlets Applets

LotusScript Binding OLE

Java Binding

COM

Notes Backend "C" API ( nlsxbe.dll )

Like an Iceberg
Actual memory usage is like an iceberg. The part that Java can see is the visible part of the iceberg. The 7/8 under the water part is the back end classes. Your application is the Titanic if you are not careful

The answer is : Recycle()


use doc.recycle() after .doc.save(). stopped your script after 120K docs.

Recycle()
Garbage collection has no effect on Domino objects unless you first explicitly recycle them. If you appear to have memory problems, try recycle but adhere to the following guidelines: Recycle an object only if it is no longer needed. Recycle an object in the same thread in which it is created. Recycling a parent recycles all the children. In Session, call recycle only after all threads exit. Loops enumerating documents or items are good candidates for recycling

Recycle().Suggestion
Go to : www.notes.net www.looseleaf.net and do a search on recycle.

R5 LotusScript
FRONT END CLASSES
NotesUIWorkspace NotesUIDatabase NotesUIView

BACK END CLASSES


NotesSession
get ...
NotesDbDirectory
NotesInternational

create ... NotesDateTime


NotesNewsletter

NotesName

NotesLog

NotesDateRange

NotesTimer

NotesRichTextStyle

NotesRichTextParagraphStyle NotesRichTextTab

Database
NotesUIDocument

Forms

ACL

Agents

Outline AllDocuments FTSearch Replication NotesOutline Search


NotesReplication

getView Views
NotesView

NotesForm

NotesACL

NotesAgent

NotesOutlineEntry

NotesDocumentCollection NotesACLEntry NotesViewColumn

NotesViewNavigator

NotesViewEntryCollection

NotesDocument

NotesViewEntry

NotesItem

NotesRichTextItem

NotesEmbeddedObject

inherits

Contains New Method

NotesRegistration

R5 Java
getAgentContext
getDocumentContext

BACK END CLASSES


Session
get ... create ...
DbDirectory DateTime DateRange International Name Log

AgentContext

Newsletter

Registration

RichTextStyle

RichTextParagraphStyle RichTextTab

createDocument

Database

getForms getACL

getAgents getAllDocuments getReplication FTSearch Search


ACL Agent Replication

getOutline
Outline

getView getViews
View

Form

OutlineEntry

NotesDocumentCollection NotesACLEntry ViewColumn ViewNavigator

ViewEntryCollection

Document

ViewEntry

Item

RichTextItem

EmbeddedObject

inherits

NO FRONT END CLASSES NO NEW CLASS METHOD NO DIRECT PROPERTY ACCESS NO NOTESTIMER CLASS

AgentContext() Class
Session Class
Property accessing methods
getAddressBooks isOnServer getNotesVersion getPlatform getAgentContext getUserName getUserNameObject getCommonUserName getInternational

AgentContext Class
Property accessing Methods
getCurrentAgent ** getCurrentDatabase ** getDocumentContext getEffectiveUserName getLastExitStatus getLastRun getSavedData ** getUnprocessedDocuments

Methods
createDateRange createDateTime createLog createName createNewsletter createRegistration createRichTextStyle

** evaluate
freeTimeSearch getDatabase getDbDirectory getEnvironmentString getEnvironmentValue setEnvironmentVar openURLDatabase

Methods
unprocessedFTSearch unprocessedSearch updateProcessedDoc

Domino Java Classes


All properties are accessed via methods is or has properties are accessed via the same method name
doc.isNewNote is accessed via doc.isNewNote( )

remaining properties are accessed via get or set prefixed to the property name as a method

db.title is accessed via db.getTitle( ) db.title is modified via db.setTitle( )

Accessing Domino Data


NO extended notation method i.e. doc.subject or doc.subject(0) Single value methods per datatype doc.getItemValueString( "fname" ) doc.getItemValueInteger( "fname" ) doc.getItemValueDouble( "fname" ) Multi value methods doc.getItemValue( "fname" ) returns a vector ( ~ dynamic array )

Class
The class construct supports the creation of user-defined data types, that represent both data and the methods used to manipulate the data. You can represent real world objects with these user-defined classes.

Dog
class Dog { void bark() { System.out.println("Woof"); } } MyAgent code: Dog Lassie = new Dog(); Lassie.bark();

Basic Java Class


public class Demo { private int v1, v2 ; public Demo( int v1, int v2 ) { v1 = v1; v2 = v2; } public int getMax( ) { return getMaximum( ); } private int getMaximum( ) { if ( v1>v2) return v1; else return v2; } }

private instance properties constructor public instance method private instance method

Sample Code

Demo d = new Demo( 20, 10 ); System.out.println ( d.getMax( ) );

Object Reference Variables


LotusScript
Dim doc as NotesDocument set doc = view.getfirstdocument while Not( doc Is Nothing ) ' code set doc = thisview.getNextDocument(doc) wend

Java

Document doc = thisview.getFirstDocument( ); while ( doc != null ) { // code doc = thisview.getNextDocument(doc); }

Inheritance
In LotusScript the NotesRichTextItem class inherits properties and methods of the NotesItem Class This is called Inheritance or subclassing In Java inheritance is represented by the extends keyword class myapplet extends Applet mandatory for Applet classes class myagent extends AgentBase mandatory for Domino Java Agent classes

Simple Java Agent


BACK END CLASSES
Session

import lotus.domino.* public class Demo1 extends AgentBase


{

getAgentContext getDatabase
AgentContext

public void NotesMain( ) {

getCurrentDatabase
Database

LotusScript
Dim s as new NotesSession Dim db as NotesDatabase Set db = s.CurrentDatabase print( db.Title )

try { Session s = getSession( ); AgentContext ac = s.getAgentContext( ); Database db = ac.getCurrentDatabase( ) System.out.println(db.getTitle()); } catch( NotesException e ) { e.printStackTrace(); } } }

Instance method of AgentBase Class

Loading a Java Agent


Create instance of Java class Create and initialize AgentBase class Call the NotesMain() method

AgentBase Debugging Methods


Output to console or Notes Log: setDebug (boolean debug); dbgMsg ("Some helpful text"); Output to Web Browser: import java.io.PrintWriter; PrintWriter pw = getAgentOutput(); pw.print("Output Text to Browser");

Exception Handling
import lotus.domino.* public class Demo1 extends AgentBase { public void NotesMain( ) {
try { Session s = getSession( );

Domino methods throw exceptions ( NotesException Object)


} }

AgentContext ac = s.getAgentContext( ); Database db = ac.getCurrentDatabase( ); } catch( NotesException e ) { System.out.println(e.id + " " + e.text); e.printStackTrace(); }

Demo1.java

Exception Handling
Exception throwing methods contained in a try block All Domino methods throw exceptions send exception object to runtime system Catching an Exception Runtime finds a routine to handle exception defined by catch block looks in current and calling methods program terminates if unsuccessful

Java insists that you do it right !

Java Primitive Datatypes


LotusScript Java byte short int float double N/A N/A N/A char boolean Bytes 1 2 4 4 8 8

integer long single double currency string variant

2 (unicode) true or false

Java Variables
LotusScript
Dim value1 as integer, value 2 as integer

declarations - implicit declarations - default values Java - explicit declarations - NO implicit - NO default values - CASE SENSITIVE

- explicit

value2 = 100 value3 = 100 int value1 = 100, value2 ; value2 = 100 ; char initial ; initial = 'J' ;

SEMI- COLONS

Initializing variables
Another customer report for Gary. The Java compiler is complaining about the line highlighted in red on the next slide.

Session session = getSession(); AgentContext ac = session.getAgentContext(); Double cRate; String sRate; int x; x =1; if (x==1){ sRate = "345.983"; cRate = Double.valueOf(sRate); } Database db=ac.getCurrentDatabase(); Document doc = db.createDocument(); doc.replaceItemValue("Form","Test"); doc.replaceItemValue("Value",cRate); doc.save(true,true);

Variable cRate may not have been initialized.

This will compile


Session session = getSession(); AgentContext ac = session.getAgentContext(); Double cRate = 0; String sRate = "0"; int x; x =1; if (x==1){ sRate = "345.983"; cRate = Double.valueOf(sRate); } Database db=ac.getCurrentDatabase(); Document doc = db.createDocument(); doc.replaceItemValue("Form","Test"); doc.replaceItemValue("Value",cRate); doc.save(true,true);

IF Statement
LotusScript
Dim i, j, found as Integer if i=10 or j=30 then age = "Young" found = True elseif i=20 then found = True else found = False end if

Java
short i , j; boolean found; String age; i=0; j=0; if (i ==10 || j == 30) { age = "Young"; found = true; } else if (i == 20) found = true; else found = false;

Comparison / Logical Operators LotusScript = or and Java == || &&

!= , <, <=, >, >= same

Loops and Select


LotusScript
for ( i = 1 to 10 ) ' statements Next i

Java
for ( i = 1; i <= 10; i++ ) { // statements with ; } while ( found == true ) { // statements with ; } do { // statements } while (found == true ) switch ( age ) { case 20 : person = "young"; break; case 70 : person = "Old"; break; default : person = "dead"; }

while ( found = True ) ' statements wend

do ' statements Loop while (found = True ) Select Case age Case 20 : person = "young" Case 70 : person = "old" Case Else : person = "dead" end select

Class Uses
Instantiating objects Group common Methods and Properties never instantiated referenced via Class Name class methods and class properties Math Class Math.PI, Math.E Math.abs( 2.78 ) Class Properties Math.max ( 100, 90 ) Math.min ( 44, 999 ) Class Methods Math.round ( 2.34 )
( static methods )

Stump Gary x = 100 , b = ?


int x = 100; int a = 0; int myValue = 50; if ( x == 100) { Semicolons, semicolons semicolons MyValue = x; Capitalization Matters !!!! b=a } System.out.println("The value of b is:" + b);

What is the value of "b" if x is 45


int a = 0; int b = 50; if ( x == 100) a = x; b = a; System.out.println("The value of b is:" + b); int a = 0; int b = 50; if ( x == 100) { // Always use braces a = x; b = a; } System.out.println("The value of b is:" + b);

What is the value of "a" if x is 100


int a = 0; If x = 100 { a = 300; } System.out.println("The value of a is" + a); int a = 0; If 100 == x // "=" is not the same as "==" { a = 300; } System.out.println("The value of a is" + a);

System Class
Standard I/O Class Cannot be instantiated.
System.out.println("Hello World");

PrintStream Instance Method System Class System Class Property - PrintStream Class Object

The above java code prints to standard output Server Console, Notes Log - background agents Java Debugger Console - foreground agents

Double, Float, Integer, Long Class


Classes for primitive datatypes double, float, int, long methods to manipulate primitives creation via passing primitive in constructor Integer i = new Integer( 10 ); Double i = new Double( "10" ); Float i = new Float(12.89);

Double, Float, Integer, Long Class


instance methods to manipulate native data i.toString(); // returns String representation i.intValue(); // returns int value class methods Integer.valueof( String s ) returns Integer object Integer.toString( int d ) converts native data to String Domino Objects Some methods require Objects document.replaceItemValue ( "fieldname", object ) new Double( 12.3 ) "Becky Gibson"

Pay attention this is important !

String Class
NO string datatype but a String class Assignment via "=" String s = "hello world"; Comparison via instance methods s.equals("HELLO WORLD") returns false s.equalsIgnoreCase("Hello World") returns true Instance methods replace, toLowerCase, toUpperCase, trim, etc .. CIass methods String.valueOf ( primitives or objects )

Why Two String Classes ?


The Java platform provides two classes, String and StringBuffer, that store and manipulate strings-character data consisting of more than one character. The String class provides for strings whose value will not change. The StringBuffer class provides for strings that will be modified; you use string buffers when you know that the value of the character data will change. You typically use string buffers for constructing character data dynamically.

Strings Demo

Reverse the characters of a string. This program uses both a string and a string buffer.

public class StringsDemo { public static void main(String[] args) { String palindrome = "Dot saw I was Tod"; int len = palindrome.length(); StringBuffer dest = new StringBuffer(len); for (int i = (len - 1); i >= 0; i--) { dest.append(palindrome.charAt(i)); } System.out.println(dest.toString()); } }

Strings Demo Output


The output from this program is:

doT saw I was toD

Demo : Report 1
Create a report document of Sales by the person running the agent (i.e. SalesPerson = UserName )
Sales Report 1 SalesPerson : SalesPerson Total Sales : TotalSales

Vectors ( dynamic arrays )


import java.util.Vector; Vector contains OBJECTS automatically increase in size on add
LotusScript
myArray( ) as Integer REDIM myArray(2) myArray(0) = 10 myArray(1) = 20 myArray(2) = 30 REDIM PRESERVE myArray(3) myArray(3) = 40

Java
import java.util.Vector; Vector myVector = new Vector( ); myVector.addElement( new Integer(10) ); myVector.addElement( new Integer(20) ); myVector.addElement( new Integer(30) ); myVector.addElement( new Integer(40) ); System.out.println( "elements : " + myVector.size() );

Demo : Report 2
Store customer values from all matching documents in Customers multivalue text field item.appendToTextList( String s ); Store each double sales amount value for customer create a Vector of Double objects
Sales Report 2
SalesPerson :
Customer Customers

Sales Report 2
SalesPerson : Doctor Notes

SalesPerson
Sales Sales

Customer

Sales

Total Sales :

TotalSales

Mary Smith Mary Smith Gary Martin Total Sales :

1000.00 2000.00 3456.90 6456.90

Class Packages Class Packages


Class Packages Source Files
Compiler

(analogy - script libraries)

Java ByteCode Files *.class

*.java

Classes and Classpath


The Import statement is used to find class files at compile and runtime .class extension not required
import Window1 import Window2 MyApp.java
c:\work\ibm\input\Window1.class c:\work\ibm\input\Window2.class

CLASSPATH Environment variable searched at COMPILE / RUNTIME for imported classes Contains Imported Class filepaths
CLASSPATH = c:\work\ibm\input

Classes of the same context can be grouped together Referenced easily as a whole Simplifies CLASSPATH This container is called a Package Relates to directory path of the class file defined at the top of source file via Package name
package lotus.domino ; ...
Database.java
COMPILE

Class Packages Class Packages

Database.class

c:\notes\lotus\domino\ import lotus.domino.* ; ... MyApp.java


COMPILING

CLASSPATH = C:\NOTES

Core Java Packages Core Java Packages


java.applet
applet classes
Not Applicable for Java Agents

java.lang
Java language related classes default package automatically imported

java.util
useful utility classes

java.io
file input and output classes

java.net
network related classes

java.awt **
windowing and graphic classes

Archive Files Files Archive


Packages can be saved to zip or JAR file Java ARchive File Domino Java classes lotus.domino package Notes.jar ( installed Notes directory )
NOTES.JAR

\lotus\domino\Session.class \lotus\domino\Database.class \lotus\domino\View.class \lotus\domino\Document.class

The path + jar filename is placed in the Classpath compiling / executing outside Domino
SET CLASSPATH= .;C:\NOTES\NOTES.JAR

Domino Internal Classpath


Domino has internal CLASSPATH Find Domino & JVM classes @ compile / runtime notes.jar i18n.jar, icsclass.jar, rt.jar Notes.ini variable extends internal CLASSPATH Add your own Classes and Jar files JavaUserClasses= ... ;c:\jdk\work\toolkit.jar Classes that access 'C' must be on disk

What is a JSP ?
JavaServer Pages (JSP) technology provides a simplified, fast way to create web pages that display dynamically-generated content. They are HTML documents that include special tags and Java. JSPs run on a server and work with requests and responses. The response is usually in the form of an HTML document but can also be XML. A JSP is compiled into a Servlet the first time it is accessed.

What happens when my browser hits a URL with a .jsp extension ?


Web Server hands the request off to Web App Server The JSP source code turned into Servlet source code The Servlet source code is compiled and run HTML response is sent back to the Web Server and then to the browser The compiled servlet remains in memory

Using JSPs with Domino


Domino does not currently provide integrated JSP support Use a third party JSP container Tomcat, WebSphere Install JSP container Run Domino & JSP container For JSP support access JSP container directly
http://bgibsontpad:8080/Hello.jsp?user Name=Becky

"Garnet"
Code name for the new web programming model in the next release of Domino (Rnext) Garnet consists of: A Standard Servlet/JSP container A Custom tag library and Java classes over Domino Data "Template" applications Third Party Tool integration

Custom Tag Libraries


JSP 1.1 - Custom Tags Domino Custom Tags Session Database View DocumentCollection Loop Mail Message

Simple.JSP

report1.jsp
Let's take those LotusScript/Java reports and turn them into JSPs.

SQLdemo.jsp
Ever want to build an application that could interact with SQL data ?

Java Applications 1
Setup Notes.jar in CLASSPATH Java Classes Include Notes installed directory in path load DLLs Session NotesThread.sinitThread(); Session s = NotesFactory.createSession([user, password]); NotesThread.stermThread();

Demo 4

Applets / Browser
Your Machine
Browser
HTML Page
12 11 1 10 2 9 3 8 7 6 5 4

Demo 6
R5.0 CORBA Classes

1
HTTP

HTTP
Domino Server

JVM + Core Classes

IIOP
OS

DIIOP

Setup Select "Applet uses Notes CORBA Classes" property Session AppletBase.getSession( ) instance method

Java Applications 1
Your Machine

Stand Alone Java Application

Notes 4.6/5.0 Java Classes Notes Client API JVM 1.1 & Core Classes OS

Executed by Java interpreter (e.g. java.exe) at OS Access the Java Notes Classes Notes Client/Server Installed access local API Domino RPC
Server

Java Applications 2
Your Machine

Stand Alone Java Application

NO Local Client/Server Executed by Java interpreter (e.g. java.exe) at OS Setup NCSO.jar in CLASSPATH Session NotesFactory.createSession( host )
IIOP Domino Server

Notes 5.0 CORBA Classes ( NCSO.JAR) JVM Classes OS

Demo 5

Applets / Notes Client


Notes 5.0
Notes Form
12 11 1 10 2 9 3 8 7 6 5 4

Demo 7

Applet

Notes Java Classes (Notes.jar) JVM Classes Notes Client API OS

Setup Java Applet Security options in User Preferences Session NotesThread.sinitThread(); Session s = this.getSession(); NotesThread.stermThread();

Graphical Java Agents


Build your agent with a GUI (or not) Use AWT or Swing Swing.jar not included with Domino import into agent or reference in Notes.ini Attach your agent to an event Use Simple Action: Run Agent Use Formula @Command([ToolsRunMacro];"AgentName")
Demo 8

Domino 5.x and Java


Domino Java Objects new Package, new Classes, methods and properties CORBA implementation of Backend Classes Remote access of Domino Java Agents new programmers pane, Script Library Domino UI Applets Outline, Editor, View, Action

Servlets 4.6 & enhanced for 5.0 Attend AD109 - Java Programming with Domino: A look at Servlets & JSPs Enterprise Integration Java Lotus Connectors Domino JDBC driver Attend AD203 - Know the Code: Integrating Relational Data Using Java

Domino 5.x and Java (continued)

Let's update your resumes


Java Servlets Java Server Pages J2EE Technologies

Other JSP Related Sessions


TS102: Building and Deploying Java Web Applications on a Domino Server TS113: The Architecture of the New Web Programming Model in Rnext HC103: Building R5 Web Applications using JavaServer Pages and WebSphere HC109: Implementing the Domino Rnext JSP Tag Library HC114: Tools and Tips for Working with the Rnext JSP Tag Library

Please complete your evaluations

Summary
Why Java ? Basic Java concepts Accessing Domino via Java JSP and Servlets

Questions?

Please complete your evaluations

Você também pode gostar