Responding to the OpenSAF 2010 Developer Days "wish list" for a LOG service Java API, a complete walkthrough of developing, implementing and using such an API will be presented. Attendees will leave with a deep understanding of how to use all the Java API's as well as insight into how the LOG and other services can be extended to Java. HP's experience in implementing and deploying the Java API's--including wrapping C with Java, 100% Java, and wrapping Java with C--will complete the walkthrough.
The Path to Java: Writing, Implementing and Using APIs 5.18.2011
1. The Path to SAF Java:
Writing, Implementing and Using the Java API's
Robert Hyerle
resource
Hewlett-Packard Company
Friday, April 29, 2011
2. History
-- how did we get here?
Understanding
-- why this way?
Using
-- how does all this play out?
Implementing
-- OpenSAF, and other approaches
Going For ward
-- whatās needed next?
Friday, April 29, 2011
3. History
-- how did we get here?
Hewlett-Packardācirca 2005āWe needed to
deliver events (locally) from a Java application to
a C++ WEBM CIMOM. I didnāt want to invent an
API. I had some acquaintance with SAF. The
Event Service API ļ¬t the bill, except that it didnāt
have a Java version. I read through the spec. Then
I got out my IDE (vi) and wrote the Java mapping
based on 3 basic interfaces/object types and
simple data types/structures. Everything else
followed, more or less.
Friday, April 29, 2011
4. The Event Service mapping in SAF remains pretty
much the same today:
Ser vice Handle Channel Events
is a factory for .. is a factory for, producer of ..
.. and so does the mapping style that has been
used for the other mappings.
If you understand the mapping style, youāll
understand the mappings.
Weāll get to that shortly. But, ļ¬rst, a little more
history ..
Friday, April 29, 2011
5. The HP Event Service API was built and
deployed. Note that we never built or used an
Event Service: the API provided a single, local
channel (via named pipes) from the Java
Application to the C++ CIMOM. History
We settled on:
ā¢ object/interface factory style
ā¢ use of interfaces vs. use of classes
ā¢ exceptions instead of return values
ā¢ naming for packages, types, and parameters
ā¢ leverage of Java types and memory management
We did not address:
ā¢ callbacks and the āselection objectā
ā¢ bootstrapping: associating implementations with
service interfaces
Friday, April 29, 2011
6. History
HP later abandoned WBEM for telecom software
management. We adopted SAF IMM and NTF. The
ļ¬rst deployments of this new management
framework were largely Java based. So, we
Event API
deļ¬ned IMM and NTF Java mappings.
At this point, we had to address callbacks,
selection, and bootstrapping.
HP NTF & IMM APIās
These mappings were contributed to SAF for and
Implementations
standardization. The approachāstyle and tight
correspondence with the C speciļ¬cationāwere
adopted by SAF and used when mapping the
SAF Java Standard
Cluster Service and Availability Management with Cluster and
Framework. AMF as well
Friday, April 29, 2011
7. History
It wasnāt only HP!
Ericsson had been working on Java mappings and on Java
standardization as well. In particular, Ericsson was addressing:
-SAF services/interfaces used in containerized applications,
esp. J2EE
-Providing higher level interfaces. These can built on top of
the lower level mappings, hiding them from the application
developer.
-Bridges to other Java management standards, esp. JMX
HP ran into difļ¬culties with containerized
applications and the collaboration on the various
aspects of Java mapping proved beneļ¬cial to
everyone.
Friday, April 29, 2011
8. Understanding send
void send()
-- why this way? throws AisLibraryException,
AisTimeoutException,
AisTryAgainException,
AisBadHandleException,
AisInvalidParamException,
AisNoMemoryException,
AisNoResourcesException,
AisVersionException,
AisTooBigException,
The Java mappings use AisUnavailableException
The notiļ¬cation is sent. Objects referenced by this Notiļ¬cation should not be
JavaDoc as the altered between the point the send() is invoked and it returns. If a notiļ¬cation ID
has been allocated for this notiļ¬cation, it is used. Otherwise, a new notiļ¬cation
āspeciļ¬cation languageā. ID is provided. Many of the operations that are performed during notiļ¬cation
"allocate" in the C API are performed during the send and this may give rise to
Letās take a look!
exceptions not found in the C API version of "send."
SAF Reference: saNtfNotificationSend, saNtfNotificationSendWithId
Throws:
AisLibraryException
AisTimeoutException
AisTryAgainException
AisBadHandleException
AisInvalidParamException
AisNoMemoryException
AisNoResourcesException
AisVersionException
AisTooBigException
Kind of sucks.
AisUnavailableException
See Also:
Notification.getNotificationID()
Friday, April 29, 2011
9. 3.16.10 saNtfNotificationSend() and saNtfNotificationSendWithId() Prototype
SaAisErrorT saNtfNotificationSend( SaNtfNotificationHandleT
notificationHandle
);
The C speciļ¬cation has
SaAisErrorT saNtfNotificationSendWithId( SaNtfNotificationHandleT
notificationHandle, SaNtfIdentifierT notificationIdentifier
);
Parameters
notificationHandle ā [in] The handle which was obtained by a previous call to one of
the saNtf<notification type>NotificationAllocate*() functions and which
ļ¬ve pages of explanation
identifies this particular notification instance. The SaNtfNotificationHandleT type is
defined in Section 3.14.1.2 on page 48.
for send (only the ļ¬rst is
shown here).
notificationIdentifier ā [in] The notification identifier to be assigned to the
notification to be sent. This notification identifier must have been previously allocated by
invoking the saNtfIdentifierAllocate() function, and it must not already have been
used previously to send a notification successfully. The SaNtfIdentifierT type is defined
in Section 3.14.11 on page 54.
Description
These functions are used to send a notification. The notification is identified by the notification
handle that is returned in the notification structure created with a preceding call to one of the
saNtf<notification type>NotificationAllocate*() functions.
You canāt simply use the JavaDoc to understand the API:
-You have to understand the C speciļ¬cation (and have a
copy next to you)
-You need to put on your Java āspec. writer glassesā
Friday, April 29, 2011
10. Understanding
The Spec Writer Glasses
-There is only one API speciļ¬cation: the one based on C.
-The Java mapping is at the same semantic level and with the
same semantics.
-It is not required, but it must be a straightforward exercise
to implement the Java API by wrapping an existing C
implementation. This wrapper must be thin (minimum
state, no threads).
-āC errorsā present in the speciļ¬cation may pop up in Java.
Friday, April 29, 2011
11. Start with the C spec, Understanding
then ...
- ditch all the memory allocation and reuse mechanisms
- forget all those C arrays with auxiliary variables to
describe the valid portions, allocated size, etc.
- all the status return values go away: they become
exceptions. Either the function becomes void, or, the now
available return value slot is used for something more
interesting.
- the names become shorter (take advantage of package
scope)
- primitive Java types are often used for parameters
- we have half the number of type deļ¬nitions and half the
number of parameters
Friday, April 29, 2011
12. The anchor points There is one (root) package
per service. In each, ļ¬nd the
There is a shared package: handle type (an interface).
org.saforum.ais and it is Its name is derived from
actually quite a bit different the name of the service:
from the ais.h ļ¬le. There e.g. for the Event Service
are very few shared type (EVT), the name is
deļ¬nitions. The contents EvtHandle. Now ļ¬nd the
center on service factories, handleās factory: in this
the dispatch mechanism, example: EvtHandleFactory
and exceptions. Unlike the (itās a class extending
C specās, there is some FactoryImpl). Everything
implementation code in this else follows!
package!
Friday, April 29, 2011
13. Unless there is sufļ¬cient
complexity/functionality
From the handles, ļ¬nd the to warrant it, parameters
associated objects deļ¬ned to methods are usually
using interfaces. Java primitive types/
classes, simple structures,
or arrays. No āgetter/
setterā or other
Function pointers are encapsulation style is
mapped as interfaces with imposed.
a single method. So the
callback structures have
ļ¬elds of the interface type
(objects). The single
method of that object can Understanding
then be invoked.
Friday, April 29, 2011
14. In depth:
Understanding
ļ¬nding the implementation
(of the client library)
- An application should not need to know (or even be able to know?) about
the speciļ¬c SAF implementation; should not be able to control which
implementation is used.
- The surrounding environment needs to be able to deploy/launch an
application with the desired SAF implementation.
- For certain situationsānotably testingābeing able to select an
implementation is desirable.
Version version = new Version('A', (short) 4, (short) 1);
for example:
NtfHandle.Callbacks callbacks = new NtfHandle.Callbacks();
callbacks.notificationCallback = new OnNotificationCallback();
NtfHandleFactory factory = new NtfHandleFactory();
try {
handle = factory.initializeHandle(callbacks, version);
Consumer consumer = handle.getConsumer();
} catch (AisException e) {
e.printStackTrace(); // acceptable for an example, but not for production
}
Friday, April 29, 2011
15. - Use the Java system properties and
security manager.
- The implementation classā
ļ¬nding the implementation
expressed as properties deļ¬ning the
class name and URLāis loaded via public FactoryImpl() {
the URLClassLoader.
/*
* obtain the keys from the extending class
- The security manager controls */
implClassNamePropertyKey = getImplClassPropertyKey();
which properties an application can
implClassURLPropertyKey = getImplURLClassLoaderPropertyKey();
read and write.
/*
- There is a rather complicated * using the keys, obtain the implementation class name
* and location
relation between the shared factory */
implClassName = System.getProperty(implClassNamePropertyKey);
implementation and those of each
implClassURL = System.getProperty(implClassURLPropertyKey);
service. }
/*
* set up the URL classloader. Note that we tolerate a null URL--converting it to a
* bogus URL--since the URL may never be used.
*/
URL[] path = new URL[1];
path[0] = new URL((implClassURL == null) ? "http://bogus-impl-class.url.com/" : implClassURL);
ClassLoader loader = new URLClassLoader(path,Thread.currentThread().getContextClassLoader());
/*
* load the implementation class using the URL classloader to find it only if it is not found
* by the current classload chain.
*/
Class<? extends Object> implClass = loader.loadClass(implClassName);
Friday, April 29, 2011
16. Understanding In depth:
dispatch();
- There are two methods of determining if a callback is pending:
- via the ālegacyā Java mapping hasPendingCallback() method
- via the use of java.nio channels which closely follows the C standard
- HP used the hasPendingCallback() method before ānioā existed. The
implementation was an RMI call to the area server which returned when a
callback was pending.
public void doOne() {
old way:
try {
handle.hasPendingCallback(10000); // 10 usec's
handle.dispatch(DispatchFlags.DISPATCH_ONE);
} catch (AisException e) {
e.printStackTrace(); // acceptable for an example, but not for production
}
return;
}
Friday, April 29, 2011
17. In depth:
dispatch(); public void listenNIO() throws IOException, AisException {
SelectableChannel channel; // obtained from our service handle
Selector selector; // only selecting on one channel here
SelectionKey key;
Set<SelectionKey> keys;
new way: channel = handle.getSelectableChannel();
channel.configureBlocking(false);
selector = Selector.open();
key = channel.register(selector, SelectionKey.OP_READ);
while (true) {
if (selector.select() == 0) continue; // false alarm?
keys = selector.selectedKeys();
keys.remove(key); // need to clear this to avoid loop
try {
how can this fail? handle.dispatch(DispatchFlags.DISPATCH_ALL);
let us count the } catch (AisTryAgainException e) {
continue;
ways! } catch (AisTimeoutException e) {
continue;
} catch (AisBadHandleException e) {
break; // must have shutdown
}
}
easy way:
public void doOne() {
try {
handle.dispatchBlocking(10000);
} catch (AisException e) {
e.printStackTrace(); // acceptable for an example, but not for production
unfortunate }
choice of name return;
}
Friday, April 29, 2011
18. Using An example using NTF:
-- how does all this play out? - marshaling data
- callbacks
- exceptions
Producer
- initialize service and
pre-allocate notiļ¬cations
- send notiļ¬cations in
response to internal Consumer
events
- initialize service and set
- shutdown up callbacks
- receive notiļ¬cations
- shutdown
Friday, April 29, 2011
19. private Producer producer;
Initialize &
pre-allocate
private ClassId classId;
private ServiceUser serviceProvider;
private SecurityAlarmDetector securityAlarmDetector;
/**
* Set up the Notification service for the GateKeeper. All notifications will have the
* same ClassId, ServiceUser, and SecurityAlarmDetector; defined here. Other fields
* in the notification will depend on the specific security violation.
* @see com.example.notifications.NotificationWrapper#initialize()
*/
public void initialize() {
Version version = new Version('A', (short) 2, (short) 1);
NtfHandle.Callbacks callbacks = new NtfHandle.Callbacks();
NtfHandleFactory factory = new NtfHandleFactory();
try {
handle = factory.initializeHandle(callbacks, version);
producer = handle.getProducer();
} catch (AisException e) {
e.printStackTrace(); // acceptable for an example
}
classId = new ClassId();
classId.vendorId = 33333;
classId.majorId = 995;
classId.minorId = 1;
serviceProvider = new ServiceUser();
serviceProvider.value = new SafClientUtils.ValueStringImpl("switch configurator");
securityAlarmDetector = new SecurityAlarmDetector();
securityAlarmDetector.value = new SafClientUtils.ValueStringImpl(
Using
"com.example.notifications.GateKeeper.checkAccess");
}
producer
Friday, April 29, 2011
20. Send
notiļ¬cations Using
public void alertAccess(String user, String role, String object, String operation) {
ServiceUser serviceUser = new ServiceUser();
serviceUser.value = new SafClientUtils.ValueStringImpl(user);
Date now = new Date();
try {
SecurityAlarmNotification alarm = producer.createSecurityAlarmNotification(
EventType.OPERATION_VIOLATION,
object,
classId,
now.getTime() * 1000 * 1000, // SAF uses nanoseconds, not milliseconds
ProbableCause.AUTHENTICATION_FAILURE,
Severity.SEVERITY_MAJOR,
securityAlarmDetector,
serviceUser,
serviceProvider);
alarm.setAdditionalText("Access Denied!");
alarm.send();
} catch (AisException e) {
e.printStackTrace(); // acceptable for an example, but not for production
}
}
producer
Friday, April 29, 2011
21. Using
Shutdown
public void shutdown() {
if (handle == null) return; // already shutdown, or tried to
long timeout = 500; // sleep timeout milli's if library is not responding
int attempts = 3; // max tries to shutdown, double timeout each attempt
do {
try {
handle.finalizeHandle();
} catch (AisLibraryException e) {
break; // library dead, can we report this?, give up here
} catch (AisTimeoutException e) {
try {
Thread.sleep(timeout);
} catch (InterruptedException e1) { // ignore this, just try again
}
timeout <<= 1; // wait longer next time if we try again
continue;
} catch (AisTryAgainException e) {
continue; // might work next time?
} catch (AisBadHandleException e) { // don't bury this, it's a bug! re-throw!
throw new IllegalStateException("Expected the notification handle to always be valid",e);
}
} while (--attempts > 0);
handle = null; // indicates we're done, and let's the garbage collector go to work
}
producer
Friday, April 29, 2011
22. public void initialize() {
Version version = new Version('A', (short) 4, (short) 1);
NtfHandle.Callbacks callbacks = new NtfHandle.Callbacks();
Initialize
callbacks.notificationCallback = new OnNotificationCallback();
ClassId classIds[] = new ClassId[] { new ClassId() };
classIds[0].vendorId = 33333;
classIds[0].majorId = 995;
classIds[0].minorId = 1;
Severity severities[] = new Severity[] { Severity.SEVERITY_MAJOR, Severity.SEVERITY_CRITICAL };
NtfHandleFactory factory = new NtfHandleFactory();
try {
handle = factory.initializeHandle(callbacks, version);
Consumer consumer = handle.getConsumer();
NotificationFilters filters = new NotificationFilters();
filters.securityAlarmFilter =
consumer.createSecurityAlarmNotificationFilter(
null,
(FilterName []) null,
null,
classIds,
null,
severities,
null,
null,
null);
consumer.createSubscription(filters); // we drop the return value
} catch (AisException e) {
e.printStackTrace(); // acceptable for an example, but not for production
}
}
consumer
Friday, April 29, 2011
23. Using
callbacks
/**
* A SecurityAlarmNotification callback that just prints some basic information.
*/
class OnNotificationCallback implements NotificationCallback {
@Override
public void notificationCallback(
Subscription subscription,
ConsumedNotification notification) {
try {
SecurityAlarmNotification alarm = (SecurityAlarmNotification) notification;
// and if the cast fails?
Date eventTime = new Date(alarm.getEventTime() / (1000*1000)); // convert to milliseconds
System.out.println(
"Security Alarm at " +
eventTime.toString() +
", with severity " +
alarm.getSeverity() +
", type: " +
alarm.getEventType() +
", probable cause: " +
alarm.getProbableCause() +
" (" + alarm.getAdditionalText() + " " + alarm.getLocalizedMessage() + ")");
} catch (AisException e) {
e.printStackTrace(); // acceptable for an example, but not for production
}
}
}
consumer
Friday, April 29, 2011
24. Using
Receiving Notiļ¬cations
/**
* Listen for security alarms until we shutdown this GateWatcher.
* This method blocks and will only return after {@link #shutdown()} is called.
* Hence, the caller should provide a separate thread to make this call.
*/
public void listen() {
try {
handle.dispatch(DispatchFlags.DISPATCH_BLOCKING);
} catch (AisException e) {
e.printStackTrace(); // acceptable for an example, but not for production
}
return;
}
Shutdown:
no difference between
consumer and producer!
consumer
Friday, April 29, 2011
25. Implementing
-- OpenSAF, and other approaches
JNI wrappers
Because of the close semantic levels of the C standard and
the Java mappings (plus other considerations when the
mappings were designed), using the Java Native Interface as
the glue between the Java and C APIās is feasible. This is the
OpenSAF approach.
Friday, April 29, 2011
27. Native Java
When HP built an IMM Implementing
implementation, there were no C
implementations. As well, we had a
database database
Java application framework
(SmartFrog) that lent itself to IMM. So, Java IMM Java IMM
we built the area server, the client
libraries, and the transport with active/standby
standard Java plus a replicated
database. rmi
Java Client Lib
application
Mixed Implementations
HP has also prototyped C client libraries communicating
with Java area servers using a neutral protocol: JSON-RPC
Friday, April 29, 2011
28. Going For ward
-- whatās needed next?
Apply the recipe to the Log Service
This is an important service for a wide range of
applications
Keep the Java mappings up to date with
changes in the underlying speciļ¬cations
Complete the Java implementations in
OpenSAF!
Friday, April 29, 2011