Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

What is a YOUnite Adaptor

...

The YOUnite Adaptor SDK aims to be a very small, easy to use library. As such, it has been part of the design goal to avoid depending on external libraries as much as possible. But sometimes, it is better to use well designed and tested libraries to perform menial work than attempting to roll your own. As such, the YOUnite Adaptor SDK depends on only a few libraries has one library it depends on so as to have a minimal impact on integrating within existing applications that may use the same libraries, possibly of different versions. The first of these dependencies dependency is the Google Reflections library. This library provides runtime run-time reflection capabilities needed to find and resolve classes and methods. The second library is the Jackson YAML processor. This is used primarily to support the YAML configuration file format, if selected. 


Code Block
<dependency>
  <groupId>org.reflections</groupId>
  <artifactId>reflections</artifactId>
  <!-- use latest version of Reflections -->
  <version>0.9.11</version>
</dependency> <!-- Jacksonmaintained YAML Databindhere: https://github.com/ronmamo/reflections -->
<dependency>
  <groupId>com<version>0.fasterxml.jackson.dataformat</groupId>
  <artifactId>jackson-dataformat-yaml</artifactId>
  <version>${jackson.version}</9.11</version>
</dependency>

Adaptor Architecture

Once an adaptor is connected to the YOUnite DataHub through the YOUnite Message Bus, it is able to send and receive data and ops messages. However, to streamline an adaptor developers time, the SDK has a minimal configuration step so that developers can focus on the business logic their adaptor is being built for and not the inner workings of sending and receiving messages, parsing those messages, and so on. To facilitate this, the YOUnite Adaptor SDK makes use of  annotations  developers use to define the capabilities related to the data their adaptor can produce and/or consume. These capabilities loosely translate into a Pub/Sub configuration on the YOUnite DataHub. Essentially they indicate to the YOUnite DataHub the specific domain properties they are interested in receiving changes for, and which of their own domain properties they will push out to the YOUnite DataHub when a change occurs within the associated local service(s) the adaptor is implemented to work with or domain properties requested by the YOUnite DataHub. These local services could be direct database data, an FTP server, in-memory data, or a remote service with data. In fact, the dynamic nature of YOUnite domains leave the details to individual Adaptor adaptor implementations to determine how they access and retrieve domain property data and what any transformations they may want to apply to themthe data.

Connecting Adaptors to the YOUnite DataHub

...

Because the YOUnite DataHub runtime engine may be deployed in any number of environments (including but not limited to local developer machines, QA, Staging, Production..), it is necessary to instruct the YOUnite Adaptor SDK on how to connect to the YOUnite DataHub. Specifically, the URL that the YOUnite Adaptor SDK will use to connect to the implemented transport layer, authentication details which allow the transport layer to identify the specific Adaptor to the YOUnite DataHub as a legitimate adaptor, a valid adaptor UUID and Zone UUID (previously registered with the YOUnite DataHub) and possibly the OAUTH Server URL used by the transport layer... (TODO: Determine if this is necessary.. or can the transport layer which is already configured with the URL details in order to check the validity of the adaptor authentication just use what it has configured).

There are two ways in which an Adaptor can be configured. One The other is to use a YAML configuration file that is provided to the YOUnite Adaptor SDK in the init() method. The other is to create a Config object, provided by the SDKcreate a Config object, provided by the SDK, and fill in the variety of configuration properties, such as those mentioned previously and a few others.  The YAML file is turned in to a Config object when that is the configuration option used. Either way is identical to the SDK, though providing a YAML file can present some potential runtime issues if the file is not valid, or not found, etc. On the other hand, it does allow for an external configuration file as opposed to code that has to be recompiled to pick up any configuration changes.

...

Here you see we define a single @PutToAdaptor action, yet we specify two domains. It is possible that the method will be called with a Student object OR a Course object. OR.. both! What? Yes.. if the message that arrives at the adaptor contains both student data AND course data, the method would be called with BOTH objects provided. This opens up the ability to use both objects at once before working with the local service. It may seem unlikely.. but there could very well be use cases where before the local service can be updated BOTH a student and a course domain must be provided. Maybe on the local service to create a student there is a NOT NULL column specified to a Course (e.g. course table), thus the ability to update a student can not occur without the course data being provided as well. Again, unlikely in many cases, but we can not predict when such a thing may be required. Thus, we allow for either or both domain objects to be provided in a single method call. As such, it is best to set up null checks before attempting to use objects (or properties of the object) to avoid NPEs.

Detecting Changes (rough draft.. implementation not yet in place so this is subject to change)

One of the features of the SDK in making it easier for developers is to provide a way when a local entity change occurs to send that change to YOUnite MDM without the developer having to do so in code. There may still need to be some code on the developers part in determining how that change is detected. However, as long as the @PutToMdm annotated method is called with a domain object, that object will be sent to YOUnite MDM. This is essentially a PUSH from the adaptor to YOUnite MDM. 

To standalone or integrate...

At some point, you need a way to get your adaptor started. In the above sections you learned how you configure the adaptor to get connected to the transport layer and to apply annotations to describe the capabilities of your adaptor. But your adaptor implementation has to actually call the AdaptorSDK.init() call some how. You can do this by either creating a standalone application wrapper... a microservice if you will... or you can integrate your adaptor implementation into an existing application, such as a web application.

Standalone

If you are starting out with a clean slate and need a way to start your adaptor, a simple application framework can be used to get things started. The most important point is to understand that the YOUnite Adaptor SDK does not have a background thread that starts up and keeps it running. If you call the AdaptorSDK.init() call, and your application wrapper does not keep a thread alive and running, the application will abruptly end. Therefore it is essential for the adaptor to be of any use to make sure your application framework maintains a thread to keep it alive.

As described in a previous section on configuration, the AdaptorSDK.init() static method needs the Config object and the String[] packages array passed to it. This is enough to get things rolling such that the Adaptor SDK can look for classes annotated with the @Adaptor annotation. The Config object you know about. The String[] packages is for specifying the package names annotated adaptor classes may reside. The primary purpose of this is to only look for classes within the specified packages, speeding up the process at runtime. Also to be noted, the classpath used to look for classes is specified by two classloaders the scanning process makes use of. The first is the Thread context classloader. This is typically the classloader that loaded the application and the SDK library itself. The second classloader is the classloader owner of the dependent reflections library the SDK makes use of to find classes. This will typically be the same classloader in a standalone application as the thread context classloader.

Integration

Integrating the YOUnite Adaptor SDK into an existing application is similar to the standalone route, but with some subtle differences depending on the applicatoin being integrated in to. The primary caveat to be aware of is the possible differences in classloader hierarchy. In a standalone application the YOUnite Adaptor SDK library will be loaded by the thread context classloader, where as in some types of applications, such as a web application that executes in a container like Tomcat or Jetty, those containers reorder the classloader heirachy to ensure web applications and their internal contents are loaded in specific orders so as to ensure the order in which dependent libraries are discovered. As such, it is possible the thread context classloader will be different than the second classloader used by the google reflections library. While this should pose no problem for the use of the YOUnite Adaptor SDK, it is neverthelese important to be aware of these potential scenarios in case a runtime classpath issue arises within the adaptor implementation. In the event that it may be needed to add additional classloaders to the two mentioned above, an overloaded init() method is available which takes in an array of ClassLoader as the middle parameter (e.g. init(Config, ClassLoader[], String...). With this, it is possible to add any additional classloaders, such as specific container loaders to the list of loaders used to search for annotated adaptor classes.

Otherwise, the integration is about the same as the Standalone method. At some point, presumably a one time initialize method, you make the call to the AdaptorSDK.init() method same as the standalone approach. Like the standalone approach, it is assumed the integration container framework has some sort of background thread running keeping the process alive. 

Other caveats (Maybe this should go in to a troubleshooting section at the end?)

The integration approach may run in to another issue that sometimes arises in applications that make use of 3rd party libraries.. versioning. It is unlikely, but possible, that the integration application would have another version of the google reflections library within the classpath due to the nature of how and why the google reflections library is used (namely.. for scanning the classpath for classes with specific annotations, signatures, etc). However, that it is a possible situation, it makes this section relevant. Many developers will be aware of a term "classpath hell". This situation arises when two (or more) of the same libraries are on the application classpath usually with different versions. Often is the case where 3rd party libraries bundle a "fat jar" that include classes from yet other libraries that might also be part of other libraries. A common case is logging. Often times log4j or other logging libraries will be bundled via the "fat jar" process and that is when you can run in to runtime classpath issues. This is espeically difficult to narrow down in integrated platform envrionments like servlet and JEE containers because of the way they munge the classloader hierarchy. A telltale sign is when you start to see runtime exceptions with ClassNotFoundExceptions showing up in the logs.. and in the case of the YOUnite Adaptor SDK, it might present as if the SDK is not finding any of your annotated adaptor classes. 

Examples

Here is a list of examples provided via the <portal? sdk.zip outside of maven? link to github projects??> with information on each.

TBD...