The Path to Java: Writing, Implementing and Using APIs 5.18.2011

3,571 views

Published on

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.

Published in: Technology, Education
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,571
On SlideShare
0
From Embeds
0
Number of Embeds
1,864
Actions
Shares
0
Downloads
31
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

The Path to Java: Writing, Implementing and Using APIs 5.18.2011

  1. 1. The Path to SAF Java: Writing, Implementing and Using the Java APIs Robert Hyerle resource Hewlett-Packard CompanyFriday, April 29, 2011
  2. 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. 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 fit 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. 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, first, a little more history ..Friday, April 29, 2011
  5. 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 interfacesFriday, April 29, 2011
  6. 6. History HP later abandoned WBEM for telecom software management. We adopted SAF IMM and NTF. The first deployments of this new management framework were largely Java based. So, we Event API defined 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 specification—were adopted by SAF and used when mapping the SAF Java Standard Cluster Service and Availability Management with Cluster and Framework. AMF as wellFriday, April 29, 2011
  7. 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 difficulties with containerized applications and the collaboration on the various aspects of Java mapping proved beneficial to everyone.Friday, April 29, 2011
  8. 8. Understanding send void send() -- why this way? throws AisLibraryException, AisTimeoutException, AisTryAgainException, AisBadHandleException, AisInvalidParamException, AisNoMemoryException, AisNoResourcesException, AisVersionException, AisTooBigException, The Java mappings use AisUnavailableException The notification is sent. Objects referenced by this Notification should not be JavaDoc as the altered between the point the send() is invoked and it returns. If a notification ID has been allocated for this notification, it is used. Otherwise, a new notification “specification language”. ID is provided. Many of the operations that are performed during notification "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. 9. 3.16.10 saNtfNotificationSend() and saNtfNotificationSendWithId() Prototype SaAisErrorT saNtfNotificationSend( SaNtfNotificationHandleT notificationHandle ); The C specification 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 five 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 first 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 specification (and have a copy next to you) -You need to put on your Java “spec. writer glasses”Friday, April 29, 2011
  10. 10. Understanding The Spec Writer Glasses -There is only one API specification: 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 specification may pop up in Java.Friday, April 29, 2011
  11. 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 definitions and half the number of parametersFriday, April 29, 2011
  12. 12. The anchor points There is one (root) package per service. In each, find 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 file. There e.g. for the Event Service are very few shared type (EVT), the name is definitions. The contents EvtHandle. Now find 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. 13. Unless there is sufficient complexity/functionality From the handles, find the to warrant it, parameters associated objects defined 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 fields of the interface type (objects). The single method of that object can Understanding then be invoked.Friday, April 29, 2011
  14. 14. In depth: Understanding finding the implementation (of the client library) - An application should not need to know (or even be able to know?) about the specific 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. 15. - Use the Java system properties and security manager. - The implementation class— finding the implementation expressed as properties defining 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. 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 usecs handle.dispatch(DispatchFlags.DISPATCH_ONE); } catch (AisException e) { e.printStackTrace(); // acceptable for an example, but not for production } return; } Friday, April 29, 2011
  17. 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. 18. Using An example using NTF: -- how does all this play out? - marshaling data - callbacks - exceptions Producer - initialize service and pre-allocate notifications - send notifications in response to internal Consumer events - initialize service and set - shutdown up callbacks - receive notifications - shutdownFriday, April 29, 2011
  19. 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"); } producerFriday, April 29, 2011
  20. 20. Send notifications 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 } } producerFriday, April 29, 2011
  21. 21. Using Shutdown public void shutdown() { if (handle == null) return; // already shutdown, or tried to long timeout = 500; // sleep timeout millis 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) { // dont bury this, its a bug! re-throw! throw new IllegalStateException("Expected the notification handle to always be valid",e); } } while (--attempts > 0); handle = null; // indicates were done, and lets the garbage collector go to work } producerFriday, April 29, 2011
  22. 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 } } consumerFriday, April 29, 2011
  23. 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 } } } consumerFriday, April 29, 2011
  24. 24. Using Receiving Notifications /** * 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! consumerFriday, April 29, 2011
  25. 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
  26. 26. org.saforum.ais.clm.ClusterMembershipManager.java: public interface ClusterMembershipManager { Implementing ClusterNode getClusterNode( int nodeId, long timeout ) throws AisLibraryException, AisTimeoutException, AisTryAgainException, AisBadHandleException, AisInvalidParamException, AisNoMemoryException, AisNoResourcesException; org.opensaf.ais.clm.ClusterMembershipManagerImpl.java: public class ClusterMembershipManagerImpl implements ClusterMembershipManager { public native ClusterNode getClusterNode(int nodeId, long timeout) throws AisLibraryException, AisTimeoutException, AisTryAgainException, AisBadHandleException, AisInvalidParamException, AisNoMemoryException, AisNoResourcesException; ${INSTALL}/java/ais_api_impl_native.clm.j_ais_clm_manager.c: no /************************************************************************** * FUNCTION: Java_org_opensaf_ais_clm_ClusterMembershipManagerImpl_getClusterNode * TYPE: native method code! * Class: ais_clm_ClusterMembershipManager * Method: getClusterNode * Signature: (IJ)Lorg/saforum/ais/clm/ClusterNode; *************************************************************************/ JNIEXPORT jobject JNICALL Java_org_opensaf_ais_clm_ClusterMembershipManagerImpl_getClusterNode( JNIEnv* jniEnv, jobject thisClusterMembershipManager, jint nodeId, jlong timeout ) { // call saClmClusterNodeGet _saStatus = saClmClusterNodeGet( _saClmHandle, (SaClmNodeIdT) nodeId, (SaTimeT) timeout, &_saClusterNode );Friday, April 29, 2011
  27. 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-RPCFriday, April 29, 2011
  28. 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 specifications Complete the Java implementations in OpenSAF!Friday, April 29, 2011

×