Amis Blog

Subscribe to Amis Blog feed
Friends of Oracle and Java
Updated: 5 hours 51 min ago

Java: How to fix Spring @Autowired annotation not working issues

Thu, 2018-02-22 12:48

Spring is a powerful framework, but it requires some skill to use efficiently. When I started working with Spring a while ago (actually Spring Boot to develop microservices) I encountered some challenges related to dependency injection and using the @Autowired annotation. In this blog I’ll explain the issues and possible solutions. Do note that since I do not have a long history with Spring, the provided solutions might not be the best ones.

Introduction @Autowired

In Spring 2.5 (2007), a new feature became available, namely the @Autowired annotation. What this annotation basically does is provide an instance of a class when you request it in for example an instance variable of another class. You can do things like:


@Autowired
MyClass myClass;

This causes myClass to automagically be assigned an instance of MyClass if certain requirements are met.

How does it know which classes can provide instances? The Spring Framework does this by performing a scan of components when the application starts. In Spring Boot the @SpringBootApplication provides this functionality. You can use the @ComponentScan annotation to tweak this behavior if you need to. Read more here.

The classes of which instances are acquired, also have to be known to the Spring framework (to be picked up by the ComponentScan) so they require some Spring annotation such as @Component, @Repository, @Service, @Controller, @Configuration. Spring manages the life-cycle of instances of those classes. They are known in the Spring context and can be used for injection.

Order of execution

When a constructor of a class is called, the @Autowired instance variables do not contain their values yet. If you are dependent on them for the execution of specific logic, I suggest you use the @PostConstruct annotation. This annotation allows a specific method to be executed after construction of the instance and also after all the @Autowired instances have been injected.

Multiple classes which fit the @Autowired bill

If you create an instance of a class implementing an interface and there are multiple classes implementing that interface, you can use different techniques to let it determine the correct one. Read here.

You can indicate a @Primary candidate for @Autowired. This sets a default class to be wired. Some other alternatives are to use @Resource, @Qualifier or @Inject. Read more here. @Autowired is Spring specific. The others are not.

You can for example name a @Component like:


@Component("beanName1")
public class MyClass1 implements InterfaceName {
}

@Component("beanName2")
public class MyClass2 implements InterfaceName {
}

And use it in an @Autowired like


@Autowired
@Qualifier("beanName1")
InterfaceName myImpl;

MyImpl will get an instance of MyClass1

When @Autowired doesn’t work

There are several reasons @Autowired might not work.

When a new instance is created not by Spring but by for example manually calling a constructor, the instance of the class will not be registered in the Spring context and thus not available for dependency injection. Also when you use @Autowired in the class of which you created a new instance, the Spring context will not be known to it and thus most likely this will also fail.
Another reason can be that the class you want to use @Autowired in, is not picked up by the ComponentScan. This can basically be because of two reasons.

  • The package is outside the ComponentScan search path. Move the package to a scanned location or configure the ComponentScan to fix this.
  • The class in which you want to use @Autowired does not have a Spring annotation. Add one of the following annotatons to the class: @Component, @Repository, @Service, @Controller, @Configuration. They have different behaviors so choose carefully! Read more here.
Instances created not by Spring

Autowired is cool! It makes certain things very easy. Instances created not by Spring are a challenge and stand between you and @Autowired. How do you deal with this?

Do not create your own instances; let Spring handle it

If you can do this (refactor), it is the easiest way to go. If you need to deal with instances created not by Spring, there are some workarounds available below, but most likely, they will have unexpected side-effects. It is easy to add Spring annotations, have the class be picked up by the ComponentScan and let instances be @Autowired when you need it. This avoids you having to create new instances regularly or having to forward them through a call stack.

Not like this

//Autowired annotations will not work inside MyClass. Other classes who want to use MyClass have to create their own instances or you have to forward this one.

public class MyClass {
}

public class MyParentClass {
MyClass myClass = new MyClass();
}

But like this

Below how you can refactor this in order to Springify it.


//@Component makes sure it is picked up by the ComponentScan (if it is in the right package). This allows @Autowired to work in other classes for instances of this class
@Component
public class MyClass {
}

//@Service makes sure the @Autowired annotation is processed
@Service
public class MyParentClass {
//myClass is assigned an instance of MyClass
@Autowired
MyClass myClass;
}

Manually force Autowired to be processed

If you want to manually create a new instance and force the @Autowired annotation used inside it to be processed, you can obtain the  SpringApplicationContext (see here) and do the following (from here):


B bean = new B();
AutowireCapableBeanFactory factory = applicationContext.getAutowireCapableBeanFactory();
factory.autowireBean( bean );
factory.initializeBean( bean, "bean" );

initializeBean processes the PostConstruct annotation. There is some discussion though if this does not break the inversion of control principle. Read for example here.

Manually add the bean to the Spring context

If you not only want the Autowired annotation to be processed inside the bean, but also make the new instance available to be autowired to other instances, it needs to be present in the SpringApplicationContext. You can obtain the SpringApplicationContext by implementing ApplicationContextAware (see here) and use that to register the bean. A nice example of such a ‘dynamic Spring bean’ can be found here and here. There are other flavors which provide pretty similar functionality. For example here.

The post Java: How to fix Spring @Autowired annotation not working issues appeared first on AMIS Oracle and Java Blog.

Set up continuous application build and delivery from Git to Kubernetes with Oracle Wercker

Thu, 2018-02-22 03:22

It is nice – to push code to a branch in a Git repository and after a little while find the freshly built application up and running in the live environment. That is exactly what Wercker can do for me.

image

The Oracle + Wercker Cloud service allows me to define applications based on Git repositories. For each application, one or more workflows can be defined composed out of one or more pipelines (steps). A workflow can be triggered by a commit on a specific branch in the Git repository. A pipeline can do various things – including: build a Docker container from the sources as runtime for the application, push the Docker container to a container registry and deploy containers from this container registry to a Kubernetes cluster.

In this article, I will show the steps I went through to set up the end to end workflow for a Node JS application that I had developed and tested locally and then pushed to a repository on GitHub. This end to end workflow is triggered by any commit to the master branch. It builds the application runtime container, stores it and deploys it to a Kubernetes Cluster running on Oracle Cloud Infrastructure (the Container Engine Cloud).

The starting point is the application – eventmonitor-microservice-soaring-clouds-sequel – in the GitHub Repository at: https://github.com/lucasjellema/eventmonitor-microservice-soaring-clouds-sequel . I already have a free account on Wercker (http://www.wercker.com/)

The steps:

1. Add an Application to my Wercker account

image

2. Step through the Application Wizard:

image

Select GitHub (in my case).

Since I am logged in into Wercker using my GitHub account details, I get presented a list of all my repositories. I select the one that holds the code for the application I am adding:

image

Accept checking out the code without SSH key:

image

Step 4 presents the configuration information for the application. Press Create to complete the definition of the application.

image

The successful creation of the application is indicated.

image

3. Define the build steps in a wercker.yml

The build steps that Wercker executes are described by a wercker.yml file. This file is expected in the root of the source repository.

Wercker offers help with the creation of the build file. For a specific languagem it can generate the skeleton wercker.yml file that already refers to the base box (a language specific runtime) and has the outline for the steps to build and push a container.

image

In my case, I have created the wercker.yml file manually and already included it in my source repo.

Here is part of that file.

image

Based on the box node8 (the base container image), it defines three building block: build, push-to-releases and deploy-to-oke. The first one is standard for Node applications and builds the application (well, it gathers all node modules). The second one takes the resulting container image from the first step and pushes it to the Wercker Container Registry with a tag composed from the branch name and the git commit id. The third one is a little more elaborate. It takes the container image from the Wercker registry and creates a Kubernetes deployment that is subsequently pushed to the Kubernetes cluster that is indicated by the environment variables KUBERNETES_MASTER and KUBERNETES_TOKEN.

4. Define Pipelines and Workflow

In the Wercker console, I can define workflows for my application. These workflows consist of pipelines, organized in a specific sequence. Each pipeline is triggered by the completion of the previous one. The first pipeline is typically triggered by a commit event in the source repository.

image

 

Before I can compose the workflow I need, I first have to set up the Pipelines – corresponding to the build steps in the wercker.yml file in the application source repo. Click on Add new pipline.

Define the name for the new pipeline (anything you like) and the name of the YML Pipeline – this one has to correspond exactly with the name of the building block in the wercker.yml file.

image

Click on Create.

Next, create a pipeline for the ”deploy-to-oke” step in the YML file

image

Press Create to also create this pipeline.

With all three pipelines available, we can complete the workflow.

image

Click on the plus icon to add step in the workflow. Associate this step with the pipeline push-docker-image-to-releases:image

Next, add a step for the final pipeline:

image

This completes the workflow. If you now commit code to the master branch of the GitHub repo, the workflow will be triggered and will start to execute. The execution will fail however: the wercker.yml file contains various references to variables that need to be defined for the application (or the workflow or even the individual pipeline) before the workflow can be successful.

image

Crucial in making the deployment to Kubernetes successful are the files kubernetes-deployment.yml.template and ingress.yml.template. These files are used as template for the Kubernetes deployment and ingress definitions that are applied to Kubernetes. These files define important details such as:

  • Container Image in the Wercker Container Registry to create the Pod for
  • Port(s) to be exposed from each Pod
  • Environment variables to be published inside the Pod
  • URL path at which the application’s endpoints are accessed (in ingress.yml.template)

image

5. Define environment variables

Click on the Environment tab. Set values for all the variables used in the wercker.yml file. Some of these define the Kubernetes environment to which deployment should take place, others provide values that are injected into the Kubernetes Pod and made available as environment variables to the application at run timeSNAGHTMLce5b5cb

6. Trigger a build of the application

At this point, the application is truly ready to be built and deployed. One way to trigger this, is by committing something to the master branch. Another option is shown here:

image

The build is triggered. The output from each step is available in the console:image

When the build is done, the console reflects the result.

image

Each pipeline can be clicked to inspect details for all individual steps, for example the deployment to Kubernetes:

image

Each step can be expanded for even more details:

image

In these details, we can find the values that have been injected for the environment variables.

7. Access the live application

This final step is not specific to Wercker. It is however the icing on the cake – to make actual use of the application.

The ingress definition for the application specifies:

image

This means that the application can be accessed at the endpoint for the K8S ingress at the path /eventmonitor-ms/app/.

Given the external IP address for the ingress service, I can now access the application:

SNAGHTMLd0a5d95

Note: /health is one of the operations supported by the application.

8. Change the application and Roll out the Change – the ultimate proof

The real proof of this pipeline is in changing the application and having that change rolled out as a result of the Git commit.

I make a tiny change, commit the change to GitHub

image

and push the changes. Almost immediately, the workflow is triggered:

image 

After a minute or so, the workflow is complete:

and the updated application is live on Kubernetes:

SNAGHTMLd156705

Check the live logs in the Pod:

SNAGHTMLd1447ba

And access the application again – now showing the updated version:


SNAGHTMLd14d46a

The post Set up continuous application build and delivery from Git to Kubernetes with Oracle Wercker appeared first on AMIS Oracle and Java Blog.

Communicate events in ADF based UI areas to embedded Rich Client Applications such as Oracle JET, Angular and React

Wed, 2018-02-21 02:33

For one of our current projects I have done some explorations into the combination of ADF (and WebCenter Portal in our specific case) with JET. Our customer has existing investments in WC Portal and many ADF Taskflows and is now switching to JET as a WebApp implementation technology – for reasons of better user experience and especially better availability of developers. I believe that this is a situation that many organizations are in or are contemplating (including those who want to extend Oracle EBusiness Suite or Fusion Apps). This is not the ideal green field technology mix of course. However, if either WebCenter Portal (heavily steeped in ADF) or an existing enterprise ADF application are the starting point for new UI requirements, you are bound to end up with a combination of ADF and the latest and greatest technology used for building those requirements.

We have to ensure that the rich client based ‘Portlets’ are nicely embedded in the ADF host environment. We also have to take care that events triggered by user actions in the ADF UI areas are communicated to the embedded rich client based UI areas in the page and lead to appropriate actions over there.

In a previous article, I have described how events in the embedded area are communicated to the ADF side of the fence and lead to UI synchronization: Publish Events from any Web Application in IFRAME to ADF Applications. In the current article, I describe the reverse route: events in the ADF based areas on the page are communicated to the rich client based UI and trigger the appropriate synchronization. The implementation is suitably decoupled, using ADF mechanisms such as server listener, conetxtual event, partial page refresh and the standard HTML5 mechanism for publishing events on embedded IFRAME windows.image_thumb11

 

It is quite likely that an IFRAME is used as a container for the new UI components

The UI components created in ADF and those built in other technologies are fairly well isolated from each other, through the use of the IFRAME. However, in certain instances, the isolation has to be pierced. When a user performs an action in one UI component, it is quite possible that is action should have an effect in another UI area in the same page. The other area may need to refresh (get latest data), synchronize (align with the selection), navigate, etc. We need a solid, decoupled way of taking an event in the ADF based UI area to the UI sections embedded in IFRAMEs and based on one of the proponents of the latest UI technology.

This article describes such an appoach – that allows our ADF side of the User Interface to send events in a well defined way to the JET, React or Angular UI component and thus make these areas play nice with each other after all. The visualization of the end to end flow is shown below:

image

 

Note: where it says JET, it could also say Angular, Vue or React – or even plain HTM5.

The steps are:

  1. A user action is performed and the event to be published is identified in the ADF UI – the ADF X taskflow in the figure.
  2. Several options are available for the communication to the server – from an auto-submit enabled input component with a value change listener associated with a managed bean to a UI comonent with a client listener that leverages a server listener to queue a custom event to be sent to the server – also ending up in a managed bean
  3. The managed bean, defined in the context of the ADF Taskflow X, gets hold of the binding container for the current view, gets hold of the publishEvent method binding and executes that binding
  4. The publishEvent method binding is specified in the page definition for the current page. It invokes method publishEvent on the Data Control EventPublisherBean that was created for the POJO EventPublisherBean. The method binding in the page definition contains an events element that specifies that execution of this method binding will result in the publication of a contextual event called CountrySelectedEvent that takes the result from the method publishEvent as its payload.

    At this point, we leave the ADF X Taskflow. It has done its duty by reporting the event that took place in the client. It is available at the heart of the ADF framework, ready to be processed by one or more consumers – that each have to take care of refreshing their own UI if so desired.

  5. The contextual event CountryChangedEvent is consumed in method handleCountryChangedEvent in POJO EventConsumer. A Data Control is created for this POJO. A method action is configured for handleCountryChangedEvent in the page definition for the view in JET Container ADF Taskflow. This page definition also contains an eventMap element that specifies that the CountryChangedEvent event is to be handled by method binding handleCountryChangedEvent. The method binding specifies a single parameter that will receive the payload of the event (from the EL Expression ${payLoad} for attribute NDValue)
  6. The EventConsumer receives the payload of the event and writes a JavaScript snippet to be executed in the browser at the event of the partial page request processing.
  7. The JavaScript snippet, written by EventConsumer, is executed in the client; it invokes function processCountryChangedEvent (loaded in JS library adf-jet-client-app.js) and pass the payload of the countrychanged event.
  8. Function processCountryChangedEvent locates the IFRAME element that contains the target client application and posts a message on its content window – carrying the event’s payload
  9. A message event handler defined in the IFRAME, in the JET application, consumes the message, extracts the event’s payload and processes it in the appropriate way – probably synchronizing the UI in some way or other.At this point, all effects that the action in ADF X area should have in the JET application in the IFRAME have been achieved.

image

And now for some real code.

Starting point:

  • ADF Web Application (may have Model, such as ADF BC, not necessarily)
    • an index.jsf page – home of the application
    • the ADF JET Container Taskflow with a JETView.jsff that has the embedded IFRAME that loads the index.xhtml
    • a jet-web-app folder with an index.html – to represent the JET application (note: for now it is just a plain HTML5 application)
    • the ADF X Taskflow with a view.jsff page – representing the existing WC Portal or ADF ERP application

 

image_thumb21

 

From ADF X Taskflow to ADF Contextual Event

The page view.jsff contains a selectOneChoice component

image

 

Users can select a country.

The component has autoSubmit set to true – which means that when the selection changes, the change is submitted (in an AJAX request) to the server. The valueChangeListener has been configured – with the detailsBean managed bean, defined in the ADF-X-taskflow.

<?xml version='1.0' encoding='UTF-8'?>
<ui:composition xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
    <af:panelHeader text="Classic ADF X Taskflow" id="ph1">
        <af:selectOneChoice label="Choose a country" id="soc1" autoSubmit="true"
                            valueChangeListener="#{pageFlowScope.detailsBean.countryChangeHandler}">
            <af:selectItem label="The Netherlands" value="nl" id="si1"/>
            <af:selectItem label="Germany" value="de" id="si2"/>
            <af:selectItem label="United Kingdom of Great Brittain and Northern Ireland" value="uk" id="si3"/>
            <af:selectItem label="United States of America" value="us" id="si4"/>
            <af:selectItem label="Spain" value="es" id="si5"/>
            <af:selectItem label="Norway" value="no" id="si6"/>
        </af:selectOneChoice>
    </af:panelHeader>
</ui:composition>

image

The detailsBean is defined for the ADF-X-taskflow:

<?xml version="1.0" encoding="windows-1252" ?>
<adfc-config xmlns="http://xmlns.oracle.com/adf/controller" version="1.2">
  <task-flow-definition id="ADF-X-taskflow">
    <default-activity>view</default-activity>
    <data-control-scope>
      <shared/>
    </data-control-scope>
    <managed-bean id="__1">
      <managed-bean-name>detailsBean</managed-bean-name>
      <managed-bean-class>nl.amis.frontend.jet2adf.view.adfX.DetailsBean</managed-bean-class>
      <managed-bean-scope>pageFlow</managed-bean-scope>
    </managed-bean>
    <view id="view">
      <page>/view.jsff</page>
    </view>
    <use-page-fragments/>
  </task-flow-definition>
</adfc-config>

The bean is based on class DetailsBean. The relevant method here is countryChangedHandler:

    public void countryChangeHandler(ValueChangeEvent valueChangeEvent) {
        System.out.println("Country Changed to = " + valueChangeEvent.getNewValue());
        // find operation binding publishEvent and execute in order to publish contextual event
        BindingContainer bindingContainer = BindingContext.getCurrent().getCurrentBindingsEntry();
        OperationBinding method = bindingContainer.getOperationBinding("publishEvent");
        method.getParamsMap().put("payload", valueChangeEvent.getNewValue());
        method.execute();
    }

CODE details bean country changed handler

This method gets hold of the binding container (for the current page, view.jsff) and in it of the method action publishEvent

<?xml version="1.0" encoding="UTF-8" ?>
<pageDefinition xmlns="http://xmlns.oracle.com/adfm/uimodel" version="12.2.1.9.14" id="viewPageDef"
                Package="nl.amis.frontend.jet2adf.view.pageDefs">
    <parameters/>
    <executables>
        <variableIterator id="variables"/>
    </executables>
    <bindings>
        <methodAction id="publishEvent" RequiresUpdateModel="true" Action="invokeMethod" MethodName="publishEvent"
                      IsViewObjectMethod="false" DataControl="EventPublisherBean"
                      InstanceName="bindings.publishEvent.dataControl.dataProvider"
                      ReturnName="data.EventPublisherBean.methodResults.publishEvent_publishEvent_dataControl_dataProvider_publishEvent_result">
            <NamedData NDName="payload" NDType="java.lang.Object"/>
            <events xmlns="http://xmlns.oracle.com/adfm/contextualEvent">
                <event name="CountryChangedEvent"/>
            </events>
        </methodAction>
    </bindings>
</pageDefinition>

This Page Definition defines the method action and specifies that execution of that method action publishes the Contextual Event CountryChangedEvent.

 

At this point, we leave the ADF X Taskflow. It has done its duty by reporting
the event that took place in the client. It is available at the heart of the ADF
framework, ready to be processed by one or more consumers – that each have to
take care of refreshing their own UI if so desired.

 

From ADF Contextual Event to JET Application

A method action is configured for method
handleCountryChangedEvent in data control EventConsumer created for POJO EventConsumer, in the page definition for the view in JET
Container ADF Taskflow. This page definition also contains an eventMap
element that specifies that the CountryChangedEvent event is to be handled by this method binding handleCountryChangedEvent. The method binding specifies
a single parameter that will receive the payload of the event (from the EL
Expression ${payLoad} for attribute NDValue)

Here is the code for the Page Definition for the JETView.jsff:

<?xml version="1.0" encoding="UTF-8" ?>
<pageDefinition xmlns="http://xmlns.oracle.com/adfm/uimodel" version="12.2.1.9.14" id="JETViewPageDef"
                Package="nl.amis.frontend.jet2adf.view.pageDefs">
    <parameters/>
    <executables>
        <variableIterator id="variables"/>
    </executables>
    <bindings>
         <methodAction id="handleCountryChangedEvent" RequiresUpdateModel="true" Action="invokeMethod"
                      MethodName="handleCountryChangedEvent" IsViewObjectMethod="false" DataControl="EventConsumer"
                      InstanceName="bindings.handleCountryChangedEvent.dataControl.dataProvider">
            <NamedData NDName="payload" NDValue="${payLoad}" NDType="java.lang.Object"/>
        </methodAction>
    </bindings>
    <eventMap xmlns="http://xmlns.oracle.com/adfm/contextualEvent">
        <event name="CountryChangedEvent">
            <producer region="*">
            <!-- http://www.jobinesh.com/2014/05/revisiting-contextual-event-dynamic.html -->
                <consumer handler="handleCountryChangedEvent" refresh="false"/>
            </producer>
        </event>
    </eventMap>
</pageDefinition>

Note: the refresh attribute in the consumer element is crucial: it specifies that the page should not be refreshed when the event is consumed. The default is that the page does refresh; that would mean in our case that the IFRAME refreshes and reloads the JET application that is reinitialized and loses all it state.

And here is the EventConsumer class – for which a Data Control has been created – that handles the CountryChangedEvent:

package nl.amis.frontend.jet2adf.view.adfjetclient;

import javax.faces.context.FacesContext;

import org.apache.myfaces.trinidad.render.ExtendedRenderKitService;
import org.apache.myfaces.trinidad.util.Service;

public class EventConsumer {
    public EventConsumer() {
        super();
    }

    public void handleCountryChangedEvent(Object payload) {
        System.out.println(">>>>>> Consume Event: " + payload);
        writeJavaScriptToClient("console.log('CountryChangeEvent was consumed; the new country value = "+payload+"'); processCountryChangedEvent('"+payload+"');");
      }

    //generic, reusable helper method to call JavaScript on a client
    private void writeJavaScriptToClient(String script) {
        FacesContext fctx = FacesContext.getCurrentInstance();
        ExtendedRenderKitService erks = null;
        erks = Service.getRenderKitService(fctx, ExtendedRenderKitService.class);
        erks.addScript(fctx, script);
    }
}

The contextual event CountryChangedEvent is consumed in method handleCountryChangedEvent in this POJO EventConsumer. It receives the payload of the event and writes a
JavaScript snippet to be executed in the browser at the event of the partial
page request processing, using the ExtendedRenderKitService in the ADF framework.

The JavaScript snippet, written by EventConsumer:

  console.log('CountryChangeEvent was consumed; the new country value = uk'); 
  processCountryChangedEvent('uk');

It is executed in the client;
it invokes function processCountryChangedEvent (loaded in JS library
adf-jet-client-app.js) and passes the payload of the countrychanged event (that is: the country code for the selected country).

Function processCountryChangedEvent locates the IFRAME element that
contains the target client application and posts a message on its content
window – carrying the event’s payload:

function findIframeWithIdEndingWith(idEndString) {
    var iframe;
    var iframeHtmlCollectionArray = document.getElementsByTagName("iframe");
    //http://clubmate.fi/the-intuitive-and-powerful-foreach-loop-in-javascript/#Looping_HTMLCollection_or_a_nodeList_with_forEach
    [].forEach.call(iframeHtmlCollectionArray, function (el, i) {
        if (el.id.endsWith(idEndString)) {
            iframe = el;
        }
    });
    return iframe;
}

function processCountryChangedEvent(newCountry) {
    console.log("Client Side handling of Country Changed event; now transfer to IFRAME");    
    var iframe = findIframeWithIdEndingWith('jetIframe::f');
    var targetOrigin = '*';
    iframe.contentWindow.postMessage({'eventType':'countryChanged','payload':newCountry}, targetOrigin);
}

A message event handler defined in the IFRAME, in the JET application,
consumes the message, extracts the event’s payload and processes it.

          function init() {
              // attach listener to receive message from parent; this is not required for sending messages to the parent window
              window.addEventListener("message", function (event) {
                  console.log("Iframe receives message from parent" + event.data);
                  if (event.data &amp;&amp; event.data.eventType == 'countryChanged' &amp;&amp; event.data.payload) {
                      var countrySpan = document.getElementById('currentCountry');
                      countrySpan.innerHTML = "Fresh Country: " + event.data.payload;
                  }
              },
              false);
          }
          //init
          document.addEventListener("DOMContentLoaded", function (event) {
              init();
          });

It receives the event, gets the data property that contains the payload (as a String) and parses it as JSON (to turn it into a JavaScript object). It extracts the country from the event. Then it locates a SPAN element in the DOM and updates its innerHTML property. This will update the UI:

image

Here the salient details of the index.html of the embedded web application:

    <body>
        <h2>Client Web App</h2>
        <p>
            Country is 
            <span id="currentCountry"></span>
        </p>
    </body>

The full project looks like this:

image

 

A simple animated visualization of what happens:

Webp.net-gifmaker (4)

Resources

Sources for this article: https://github.com/lucasjellema/WebAppIframe2ADFSynchronize. (note: this repository also contains the code for the flow from JET IFRAME to the ADF Taskflow X

Docs on postMessage: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage

Blog ADF: (re-)Introducing Contextual Events in several simple steps  : https://technology.amis.nl/2013/03/14/adf-re-introducing-contextual-events-in-several-simple-steps/

Blog Revisiting Contextual Event : Dynamic Event Producer, Manual Region Refresh, Conditional Event Subscription and using Managed Bean as Event Handler (with the crucial hint regarding supressing the automatic refresh of pages after consuming a contextual event

The post Communicate events in ADF based UI areas to embedded Rich Client Applications such as Oracle JET, Angular and React appeared first on AMIS Oracle and Java Blog.

Introducing Elastic Search NoSQL to Oracle SQL developers – comparing dozens of ElasticSearch and SQL operations (a bit like Rosetta)

Tue, 2018-02-20 07:47

Even for organizations with strong roots in relational databases such as Oracle RDBMS, there may be valuable opportunities for leveraging additional data sources, for example to support special (search) use cases. Elastic Search (Index) is one of those data stores that can add value – for example to provide powerfur search capabilities to web applicaties, to handle metrics and logging output from live applications or to collect and analyze any data set in your landacape. The Elastic Stack also consists of Kibana (visualizations/dashboards), Log Stash & Beats (gathering data, for example through harvesting log files).

For developers with SQL at their fingerprints, after sometimes decades of relational querying, it can be a little challenging to get started with Elastic Search. Especially for these developers, I have compiled a Postman collection (the interface to Elastic Search is a REST API) and a Powerpoint Presentation. These two cover over two dozen of operations – index management (DDL) and data manipulation (DML) as well as searches – with the familiar Employees and Departments data set. The presentation lists these operations side by side: the left hand side of the slide shows the action in Elastic Search and the right hand side the more familiar Oracle SQL syntax. By showing equivalent statements in a well known language and the new to be grasped language, I hope to help Oracle SQL developers get kick-started with Elastic Search.

The searches make use of stored scripts, geo_point and geospatial operators, text searches, aggregations, highlighting, sorting, limiting, etc.

Note: the GitHub repository (https://github.com/lucasjellema/sig-elasticsearch-february-2018) also contains hands-on labs that you could make use of to get more acquainted with both Elastic Search and Kibana.

Resources

Sources: https://github.com/lucasjellema/sig-elasticsearch-february-2018

Slides: https://www.slideshare.net/lucasjellema/comparing-30-elastic-search-operations-with-oracle-sql-statements

The post Introducing Elastic Search NoSQL to Oracle SQL developers – comparing dozens of ElasticSearch and SQL operations (a bit like Rosetta) appeared first on AMIS Oracle and Java Blog.

Publish Events from any Web Application in IFRAME to ADF Applications

Thu, 2018-02-15 16:04

Even though ADF does not always play nice with guests, it is becoming increasingly common to have ADF applications – pure ADF or WebCenter Portal – that are the host to embedded user interfaces, created in technologies such as Oracle JET, Vue.js, Angular or React. This is not the ideal green field technology mix of course. However, if either WebCenter Portal (heavily steeped in ADF) or an existing enterprise ADF application are the starting point for new UI requirements, you are bound to end up with a combination of ADF and the latest and greatest technology used for building those requirements.

It is quite likely that an IFRAME is used as a container for the new UI components image

 

The UI components created in ADF and those built in other technologies are fairly well isolated from each other, through the use of the IFRAME. However, in certain instances, the isolation has to be pierced. When a user performs an action in one UI component, it is quite possible that is action should have an effect in another UI area in the same page. The other area may need to refresh (get latest data), synchronize (align with the selection), navigate, etc. We need a solid, decoupled way of taking an event in the embedded IFRAME area of the latest UI technology and bring it all the way to the other (regular) ADF taskflows in the page.

This article describes such an appoach – that allows our JET, React or Angular UI component to send events in a well defined way to the ADF side of the User Interface and thus make these areas play nice with each other after all. The visualization of the end to end flow is shown below:

image

Note: where it says JET, it could also say Angular, Vue or React – or even plain HTM5.

The steps are:

  1. The event to be published is identified in the next gen UI, client side in JavaScript. A JavaScript function is invoked. This function uses the postMessage method on the parent window (the one that contains the IFRAME) to send an event to the enclosing page (see for example: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage)
  2. The ADF JET Container Taskflow that embeds the IFRAME references a JavaScript library that attaches an event listener to the window, to listen for messages posted from within the IFRAME. When a message is received, this library locates the <af:inlineFrame> component, and queues a custom event on its serverListener element
  3. The custom event is sent asynchronously (AJAX style) from the browser to a managed bean, defined in the context of the JET Container Taskflow. This managed bean gets hold of the binding container for the current view in the ADF JET Container Taskflow, gets hold of the publishEvent method binding and executes that binding
  4. The publishEvent method binding is specified in the page definition for the current page. It invokes method publishEvent on the Data Control EventPublisherBean that was created for the POJO EventPublisherBean. The method binding in the page definition contains an events element that specifies that execution of this method binding will result in the publication of a contextual event called ClientAppEvent that takes the result from the method publishEvent as its payload.

    At this point, we leave the ADF JET Container Taskflow. It has done its duty by reporting the event that took place in the client. It is available at the heart of the ADF framework, ready to be processed by one or more consumers – that each have to take care of refreshing their own UI if so desired.

  5. The contextual event ClientAppEvent is consumed in method handleEvent in POJO EventHandlerBean. A Data Control is created for this POJO. A method action is configured for handleEvent in the page definition for the view in ADF Taskflow X. This page definition also contains an eventMap element that specifies that the ClientAppEvent event is to be handled by method binding handleEvent. The method binding specifies a single parameter that will receive the payload of the event (from the EL Expression ${payLoad} for attribute NDValue)
  6. The EventHandlerBean receives the payload of the event and enlists the managed detailsBean to do the actual work for this event
  7. DetailsBean will derive relevant data from the payload, update bean properties, add UI components as partial targets (to be refreshed in the client) and adds some JavaScript to be executed in the browser once the request completes – the request that started with the custom event queued via the serverListener in response to the postMessage from within the IFRAME
  8. Upon completing the request, ADF will refresh the partial targets in the browser and will execute the JavaScript provided by DetailsBean.

    At this point, all effects that the action in the IFRAME should have in the ADF Application – both server side as well as client side – have been achieved.

image

And now for some real code.

Starting point:

  • ADF Web Application (may have Model, such as ADF BC, not necessarily)
    • an index.jsf page – home of the application
    • the ADF JET Container Taskflow with a JETView.jsff that has the embedded IFRAME that loads the index.xhtml
    • a jet-web-app folder with an index.html – to represent the JET application (note: for now it is just a plain HTML5 application)
    • the ADF X Taskflow with a view.jsff page – representing the existing WC Portal or ADF ERP application

 

image

 

Adding:

JavaScript function callParent in index.html; this function is invoked to post a message to the parent window.

          function callParent() {
              console.log('send message from Web App to parent window');
              var jetinputfield = document.getElementById('jetinputfield');
              var inputvalue = jetinputfield.value;

              var message = {
                  "message" :  {
                     "value" : inputvalue
                  },
                  "mydata" :  {
                      "param1" : 42, "param2" : "train"
                  }
              };
              // here we can restrict which parent page can receive our message
              // by specifying the origin that this page should have
              var targetOrigin = '*';
              parent.postMessage(message, targetOrigin);
          }

JavaScript library adf-jet-client-app.js, associated with JETView.jsff – registers a message listener on the window and handles incoming events (by queueing a custom event on server listener)

var jetIframeClientId ="";

function init() {
    window.addEventListener("message", function (event) {
        console.log("Parent receives message from iframe " + event);
        sendMessageFromJetToServer(event.data);
    },
    false);
    //    jetIframe.contentWindow.postMessage("hello tyhere", '*');
}

document.addEventListener("DOMContentLoaded", function (event) {
    init();
});

function sendMessageFromJetToServer(message) {
   var jetIframeADF = AdfPage.PAGE.findComponentByAbsoluteId(jetIframeClientId);
    AdfCustomEvent.queue(jetIframeADF, "messageRouter", message, true);
}

The page JETView.jsff contains the inlineFrame element that has a serverListener that provides the connection to the server – managed bean client2serverBean

<?xml version='1.0' encoding='UTF-8'?>
<ui:composition xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:af="http://xmlns.oracle.com/adf/faces/rich"
                xmlns:f="http://java.sun.com/jsf/core">
    <af:resource type="javascript" source="resources/js/adf-jet-client-app.js"/>
    <af:panelStretchLayout id="psl1" styleClass="AFStretchWidth" bottomHeight="0px" topHeight="40px" endWidth="0px"
                           startWidth="0px" dimensionsFrom="parent">
        <f:facet name="top">
            <af:panelHeader text="JET Client" id="ph1">
            <!-- we do not really care about this client attribute; however, by rendering it we force execution of 
            method getJetInlineFrameClientId()  on Client2Server that also writes a little snippet of JavaScript to set
            the JavaScript variable jetIframeClientId with the actual client side id for the iframe -->
                <af:clientAttribute name="inlineFrameId" value="#{client2serverBean.jetInlineFrameClientId}"/>
            </af:panelHeader>
        </f:facet>
        <f:facet name="center">
            <af:panelGroupLayout id="pg1">
                <af:inlineFrame source="jet-web-app/index.xhtml?token=yuweyuweyu" id="jetIframe" sizing="preferred"
                                styleClass="AFStretchWidth" binding="#{client2serverBean.jetIframe}">
                    <af:serverListener type="messageRouter" method="#{client2serverBean.handleMessageFromJet}"/>
                </af:inlineFrame>
            </af:panelGroupLayout>
        </f:facet>
    </af:panelStretchLayout>
</ui:composition>

image

Note the binding attribute on inlineFrame that ties this component to the jetIframe property on the managed bean. We use this component binding in Client2Server to extract the real client id for the IFrame component and make that available in JavaScript; this happens in method getJetInlineFrameClientId() that is invoked when rendering the (utterly useless) clientAttribute inlineFrameId on the panelHeader (we only create this clientAttribute in order to force execution of method getJetInlineFrameClientId().

Java Class Client2Server – handles server side of server listener: processing incoming custom events from client, i.e. JETView.jsff.

package nl.amis.frontend.jet2adf.view.adfjetclient;

import javax.faces.context.FacesContext;

import oracle.adf.model.BindingContext;
import oracle.adf.view.rich.component.rich.output.RichInlineFrame;
import oracle.adf.view.rich.render.ClientEvent;

import oracle.binding.BindingContainer;
import oracle.binding.OperationBinding;

import org.apache.myfaces.trinidad.render.ExtendedRenderKitService;
import org.apache.myfaces.trinidad.util.ComponentReference;
import org.apache.myfaces.trinidad.util.Service;

public class Client2Server {

    public Client2Server() {
        super();
    }
    public String getJetInlineFrameClientId() {
        FacesContext ctx = FacesContext.getCurrentInstance();
        String id = this.getJetIframe().getClientId(ctx);
        writeJavaScriptToClient("console.log('Inline Frame Id = "+id+"'); jetIframeClientId ='"+id+"'");
        return id;
    }
    //generic, reusable helper method to call JavaScript on a client
    private void writeJavaScriptToClient(String script) {
        FacesContext fctx = FacesContext.getCurrentInstance();
        ExtendedRenderKitService erks = null;
        erks = Service.getRenderKitService(fctx, ExtendedRenderKitService.class);
        erks.addScript(fctx, script);
    }

    
    // this method is called from the serverListener in client-to-server.jsf; it receives an event with a payload that contains a key helpTopic
    public void handleMessageFromJet(ClientEvent clientEvent) {
        System.out.println("handleMessageFromJet in Server!" + clientEvent);
        String message = clientEvent.getParameters()
                                    .get("message")
                                    .toString();
        System.out.println("String from JET = " + message);
        // find operation binding publishEvent and execute in order to publish contextual event
        BindingContainer bindingContainer = BindingContext.getCurrent().getCurrentBindingsEntry();
        OperationBinding method = bindingContainer.getOperationBinding("publishEvent");
        method.getParamsMap().put("payload", message);
        method.execute();
    }


    private ComponentReference jetInlineFrame;

    public void setJetIframe(RichInlineFrame jetIframe) {
        this.jetInlineFrame = ComponentReference.newUIComponentReference(jetIframe);
    }

    public RichInlineFrame getJetIframe() {
        if (this.jetInlineFrame != null) {
            return (RichInlineFrame) this.jetInlineFrame.getComponent();
        } else {
            return null;
        }
    }
}

configured as Managed Bean client client2serverBean in ADF JET Container Taskflow

<?xml version="1.0" encoding="windows-1252" ?>
<adfc-config xmlns="http://xmlns.oracle.com/adf/controller" version="1.2">
  <task-flow-definition id="ADF-JET-Container-taskflow">
    <default-activity>JETView</default-activity>
    <data-control-scope>
      <shared/>
    </data-control-scope>
    <managed-bean id="__1">
      <managed-bean-name>client2serverBean</managed-bean-name>
      <managed-bean-class>nl.amis.frontend.jet2adf.view.adfjetclient.Client2Server</managed-bean-class>
      <managed-bean-scope>request</managed-bean-scope>
    </managed-bean>
    <view id="JETView">
      <page>/JETView.jsff</page>
    </view>
    <use-page-fragments/>
  </task-flow-definition>
</adfc-config>

Java Class EventPublisherBean – a simple POJO that will publish the contextual event – by virtue of being a data control and having its method configured in the page definition as method action with contextual event effect ClientAppEvent

package nl.amis.frontend.jet2adf.view.adfjetclient;

public class EventPublisherBean {
    public EventPublisherBean() {
        super();
    }
    
    public  Object publishEvent(Object payload) {
        System.out.println("<<< Publish Event: "+payload);
        return payload;
    }
}

Create Data Control for this Java Class.

Create the Page Definition for JETViewPageDef. Create method action for EventPublisherBean.publishEvent with event element for ClientAppEvent in JETViewPageDef (page definition for JETView.jsff):

<?xml version="1.0" encoding="UTF-8" ?>
<pageDefinition xmlns="http://xmlns.oracle.com/adfm/uimodel" version="12.2.1.9.14" id="JETViewPageDef"
                Package="nl.amis.frontend.jet2adf.view.pageDefs">
  <parameters/>
  <executables>
    <variableIterator id="variables"/>
  </executables>
  <bindings>
    <methodAction id="publishEvent" RequiresUpdateModel="true" Action="invokeMethod" MethodName="publishEvent"
                  IsViewObjectMethod="false" DataControl="EventPublisherBean"
                  InstanceName="bindings.publishEvent.dataControl.dataProvider"
                  ReturnName="data.EventPublisherBean.methodResults.publishEvent_publishEvent_dataControl_dataProvider_publishEvent_result">
 <NamedData NDName="payload"  NDType="java.lang.Object"/>
      <events xmlns="http://xmlns.oracle.com/adfm/contextualEvent">
        <event name="ClientAppEvent"/>
      </events>
      </methodAction>
  </bindings>
</pageDefinition>

image

Consume Event and Update ADF X Client

The ClientAppEvent should be consumed in TaskFlow ADF-X-taskflow’s view.jsff page. This done by defining an event-map entry for that event in the page definition for view.jsff:

<?xml version="1.0" encoding="UTF-8" ?>
<pageDefinition xmlns="http://xmlns.oracle.com/adfm/uimodel" version="12.2.1.9.14" id="viewPageDef"
                Package="nl.amis.frontend.jet2adf.view.pageDefs">
    <parameters/>
    <executables>
        <variableIterator id="variables"/>
    </executables>
    <bindings>
        <methodAction id="handleEvent" RequiresUpdateModel="true" Action="invokeMethod" MethodName="handleEvent"
                      IsViewObjectMethod="false" DataControl="EventHandlerBean"
                      InstanceName="bindings.handleEvent.dataControl.dataProvider">
            <NamedData NDName="payload" NDValue="${payLoad}" NDType="java.lang.Object"/>
        </methodAction>
    </bindings>
    <eventMap xmlns="http://xmlns.oracle.com/adfm/contextualEvent">
        <event name="ClientAppEvent">
            <producer region="*">
                <consumer handler="handleEvent"/>
            </producer>
        </event>
    </eventMap>
</pageDefinition>

The consumer for the event references the handleEvent method action that is created for the handleEvent method in the EventHandlerBean POJO for which a data control is created.

 

image

package nl.amis.frontend.jet2adf.view.adfX;


import javax.el.ELContext;
import javax.el.ExpressionFactory;
import javax.el.ValueExpression;

import javax.faces.context.FacesContext;

public class EventHandlerBean {
    public EventHandlerBean() {        
        super();
    }

    public void handleEvent(Object payload) {
        System.out.println(">>>>>> Consume Event: " + payload);
        DetailsBean db =
            (DetailsBean)evaluateEL("#{pageFlowScope.detailsBean}");
        db.process(payload);
    }

    public static Object evaluateEL(String el) {
        FacesContext fc = FacesContext.getCurrentInstance();
        ELContext elContext = fc.getELContext();
        ExpressionFactory ef = fc.getApplication().getExpressionFactory();
        ValueExpression exp =
            ef.createValueExpression(elContext, el, Object.class);
        Object obj = exp.getValue(elContext);
        return obj;
    }

}

CODE eventhandlerbean

 

The EventHandlerBean leverages the DetailsBean – a POJO that is setup as pageflow scope managed bean in the ADF-X-taskflow:

<?xml version="1.0" encoding="windows-1252" ?>
<adfc-config xmlns="http://xmlns.oracle.com/adf/controller" version="1.2">
  <task-flow-definition id="ADF-X-taskflow">
    <default-activity>view</default-activity>
    <data-control-scope>
      <shared/>
    </data-control-scope>
    <managed-bean id="__1">
      <managed-bean-name>detailsBean</managed-bean-name>
      <managed-bean-class>nl.amis.frontend.jet2adf.view.adfX.DetailsBean</managed-bean-class>
      <managed-bean-scope>pageFlow</managed-bean-scope>
    </managed-bean>
    <view id="view">
      <page>/view.jsff</page>
    </view>
    <use-page-fragments/>
  </task-flow-definition>
</adfc-config>

The POJO DetailsBean:

package nl.amis.frontend.jet2adf.view.adfX;


import javax.el.ELContext;
import javax.el.ExpressionFactory;
import javax.el.ValueExpression;

import javax.faces.context.FacesContext;

public class EventHandlerBean {
    public EventHandlerBean() {        
        super();
    }

    public void handleEvent(Object payload) {
        System.out.println(">>>>>> Consume Event: " + payload);
        DetailsBean db =
            (DetailsBean)evaluateEL("#{pageFlowScope.detailsBean}");
        db.process(payload);
    }

    public static Object evaluateEL(String el) {
        FacesContext fc = FacesContext.getCurrentInstance();
        ELContext elContext = fc.getELContext();
        ExpressionFactory ef = fc.getApplication().getExpressionFactory();
        ValueExpression exp =
            ef.createValueExpression(elContext, el, Object.class);
        Object obj = exp.getValue(elContext);
        return obj;
    }

}

This bean’s process method is called by the EventHandlerBean when it consumes the ClientAppEvent. This method does several things:

  • it updates the message property (that provides the value for the inputText comonent)
  • it adds the inputText component as partial target to be updated in the client
  • it writes a JavaScript snippet to be executed in the client (that writes a simple line of logging to the console – and potentially could do all kinds of wild stuff)

The view.jsff file defines the inputText element that is bound to the detailsBean and also takes its value from that bean. This component will be updated in client when the event consumption is completed.

<?xml version='1.0' encoding='UTF-8'?>
<ui:composition xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
    <af:panelHeader text="Classic ADF X Taskflow" id="ph1">
        <af:inputText label="Message" id="it1" binding="#{pageFlowScope.detailsBean.messageComponent}"
                      value="#{pageFlowScope.detailsBean.message}" columns="120" rows="1"/>
    </af:panelHeader>
</ui:composition>

image

 

The full project:

image

And the event exchange in action:

Webp.net-gifmaker (3)

 

Resources

Sources for this article: https://github.com/lucasjellema/WebAppIframe2ADFSynchronize.

Docs on postMessage: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage

Blog ADF: (re-)Introducing Contextual Events in several simple steps  : https://technology.amis.nl/2013/03/14/adf-re-introducing-contextual-events-in-several-simple-steps/

The post Publish Events from any Web Application in IFRAME to ADF Applications appeared first on AMIS Oracle and Java Blog.

The Tale of the Ardent SpringCleaning Maniac (ASM)

Wed, 2018-02-14 17:55

or

If you want to get rid of stuff, you can always do a good spring-cleaning. Or you can do what I do. Move.

Ellen DeGeneres

In order to prepare for a network reorganization we needed to move our current Oracle Enterprise Manager machine into another network segment. So we grabbed the chance, and instead of just moving the existing OEM12c Rel.3 machine we decided to install a new Oracle Enterprise Manager 13(.2.2.0.0) onto two Linux RedHat 6.9 machines and get rid of the SPOF (single point of failure) as well in one elegant movement.

Little did we know that we would almost cripple our RAC-clusters by just preparing for the new agents.

Here comes the story of this little adventure.

When we were finally ready to move the monitored targets into the new OMS, I started to install second agents on all the hosts, so we still could quickly switch back to EM12c in case we hit a major road block with 13c. Starting with our single instance development- and test machines and then tentatively I began to install on some acceptance machines. Most of our acceptance and production databases are 2-node RAC 11.2.0.4 with (active) DataGuard, meaning each database consisting of 4 instances on four different machines. Except for some connectivity problems in the network, so far all went smoothly.

In order to install the new agents I had to check each machine upfront for the required 1Gb free space in /tmp. Often, I had to clean out a lot of old stuff from years ago like long-forgotten Oracle install logs, spoolfiles from SQL adhoc-queries of the developers, downloaded rpm’s of the Linux administrators, never-checked Puppet logs and so on. I was intrigued that in all cases node 2 was more cluttered than node 1.

BTW: 1G was not even enough, I would advise to go for at least 1,3G free space in /tmp for the 13c-agents.

At first, I removed only all old files of user oracle before I even considered removing root files or stuff other users had left years ago. And I did not touch Oracle files and directories younger than 4 weeks. Often that was sufficient to clear enough space to be able to install the agent.

But there were machines which were particularly cluttered and I was happily cleaning away until I had enough free space in /tmp freed to install the agent via the GUI. The first node of the first cluster I installed, was ready at about 2018-01-29 15:10h, followed by Node2, due to the necessary cleanup in /tmp, at about 15:45h. The newly installed agents on the cluster worked fine and produced entries about “Compliance …”  which were quickly suppressed for the target. Everything seemed fine and I went home quite contently.

The next morning we noted that one of the databases on this cluster behaved strangely and complained about not being able to complete the backup due to unrecognized files in the FRA. What?! Some hours later, other databases on this node suddenly began likewise to issue events in OMS13c and slowly hell broke loose which reminded us about ASM issues we had some weeks earlier after some updates. But the only change this machine had undergone lately was the new EM agent. No releation with ASM, forget about that idea….

We took a look into the alert log of the first complaining database (primary) and it looked like this (DB and instance names changed):

2018-01-29 22:00:00.031000 +01:00

Setting Resource Manager plan SCHEDULER[0x32D9]:DEFAULT_MAINTENANCE_PLAN via scheduler window

Setting Resource Manager plan DEFAULT_MAINTENANCE_PLAN via parameter

Starting background process VKRM

VKRM started with pid=43, OS id=13348

Errors in file /u01/oracle/diag/rdbms/db1_prim/DB1_PRIM2/trace/DB1_PRIM2_m000_13400.trc:

ORA-01114: IO error writing block to file (block # )

2018-01-29 22:18:11.242000 +01:00

Errors in file /u01/oracle/diag/rdbms/db1_prim/DB1_PRIM2/trace/DB1_PRIM2_m001_35726.trc:

ORA-01114: IO error writing block to file (block # )

2018-01-29 22:28:11.814000 +01:00

Errors in file /u01/oracle/diag/rdbms/db1_prim/DB1_PRIM2/trace/DB1_PRIM2_m001_53927.trc:

ORA-01114: IO error writing block to file (block # )

2018-01-29 22:38:12.346000 +01:00

Errors in file /u01/oracle/diag/rdbms/db1_prim/DB1_PRIM2/trace/DB1_PRIM2_m001_65572.trc:

ORA-01114: IO error writing block to file (block # )

2018-01-29 22:48:12.872000 +01:00

Errors in file /u01/oracle/diag/rdbms/db1_prim/DB1_PRIM2/trace/DB1_PRIM2_m001_75911.trc:

ORA-01114: IO error writing block to file (block # )

2018-01-29 22:58:13.371000 +01:00

Errors in file /u01/oracle/diag/rdbms/db1_prim/DB1_PRIM2/trace/DB1_PRIM2_m001_86086.trc:

ORA-01114: IO error writing block to file (block # )

2018-01-29 23:05:10.655000 +01:00

Thread 2 advanced to log sequence 917 (LGWR switch)

Current log# 3 seq# 917 mem# 0: +DATA01/db1_prim/onlinelog/group_3.294.918479161

Current log# 3 seq# 917 mem# 1: +FRA01/db1_prim/onlinelog/group_3.306.918479161

2018-01-29 23:08:02.093000 +01:00

WARNING: ASM communication error: op 11 state 0x50 (3113)

ERROR: slave communication error with ASM

Unable to create archive log file ‘+FRA01’

Errors in file /u01/oracle/diag/rdbms/db1_prim/DB1_PRIM2/trace/DB1_PRIM2_arc0_18470.trc:

ORA-19816: WARNING: Files may exist in db_recovery_file_dest that are not known to database.

ORA-17502: ksfdcre:4 Failed to create file +FRA01

ORA-03113: end-of-file on communication channel

Process ID:

Session ID: 708 Serial number: 1895

*************************************************************

WARNING: A file of type ARCHIVED LOG may exist in

db_recovery_file_dest that is not known to the database.

Use the RMAN command CATALOG RECOVERY AREA to re-catalog

any such files. If files cannot be cataloged, then manually

delete them using OS command. This is most likely the

result of a crash during file creation.

*************************************************************

ARC0: Error 19504 Creating archive log file to ‘+FRA01’

ARCH: Archival stopped, error occurred. Will continue retrying

ORACLE Instance DB1_PRIM2 – Archival Error

ORA-16038: log 4 sequence# 916 cannot be archived

ORA-19504: failed to create file “”

ORA-00312: online log 4 thread 2: ‘+DATA01/db1_prim/onlinelog/group_4.295.918479161’

ORA-00312: online log 4 thread 2: ‘+FRA01/db1_prim/onlinelog/group_4.307.918479163’RNING: A file of type ARCHIVED LOG may exist in …

Remember the installation of the agent was in the afternoon at about 4 o’clock and the trouble seemed to have started at 22:00h and not all databases on the cluster seem to have a problem with ASM or their FRA’s. The connection between FRA-trouble and agent was not obvious, I would say.

At that moment we did not ‘see’ the WARNING: ASM communication error: op 11 state 0x50 (3113) but reacted to the archival error. But of course, that did not solve the problem.

When we had a look into the ASM-log on node2 of this cluster, it revealed that its ASM instance had crashed with ORA-29701… while node1 still functioned normally!

A quick search on MOS for “ORA-29701” resulted in nothing that seem to fit our problem, until I widened the search on the net and almost accidentally found a website about EXADATA, describing an ORA-29701 error of ASM and giving advice about “restoring a lost socket file” by trying to restart the complete CRS stack on the node. And if that didn’t do the trick, try rebooting the server. The last was in this environment a little tricky and takes some time to get approval, so I was prepared to do whatever necessary, as long as it did not involve a reboot of the server to solve this problem.

But a lost socket file?!? They used to be in /var/tmp on Linux, if I was not mistaken, but I only touched /tmp…

I decided to go along with this advice and stopped the CRS stack on the unhealthy node and therefore all database instances on it, which were still limping on as good or bad as they could on one node, as grid owner with crsctl stop crs.
Then I checked if all processes were really down, which they weren’t. Therefore I wielded the kill-9–sword until everything which had anything to do with CRS or oracle was gone from the process list (Do I hear some deep groans about this?). And started anew with crsctl start crs.
Luckily, the result was a node with CRS, ASM, listeners and all database instances started as if the machine just came back from a successful and smooth reboot. And even a new /tmp/.oracle directory … one of the older directories I had cleaned up in /tmp to make room for the agent installs!
Pfew, blessed are the innocent! It all worked again.

But what had caused all this trouble? According to this article, the socket files for Linux can be found in a hidden directory called .oracle. Depending on the *NIX dialect that could be under /tmp/.oracle, /usr/tmp/.oracle or /var/tmp/.oracle. Under Linux86_64 this directory is officially under /var/tmp but Oracle also hides a copy under /tmp/.oracle and also stores socket files of the Oracle Net Listener or Cluster Healthy Monitor in there (see DOC ID 1322234.1).

The article also cited DOC ID 391790.1, which is not among the first 10 documents presented when you query MOS for “ORA-29701”, which would have helped to find the culprit far quicker!

What an irony – that later that day, Jan 30th 2018, Oracle released DOC ID 370605.1 …. which tries to remind us all again:
Never delete any hidden .oracle directory you come across or fear for your cluster!

Have fun!

 

The post The Tale of the Ardent SpringCleaning Maniac (ASM) appeared first on AMIS Oracle and Java Blog.

The curious case of the blank plugin page in OEM13c

Mon, 2018-02-12 04:05

After an upgrade of Oracle Enterprise Manager from 12.1.0.5 to 13.2, I wanted to deploy  a new version of the plugin oracle.sysman.db. Downloaded the new version and got to the plugin-management page to apply the patch. But all I got was a blank page. Cost me quite a while to figure out what the – almost humiliating – workaround / solution eventually was.

Which page am I talking about:  Setup—> Extensibility –> Plug-ins.

image

And the blank page appears.

image

But.. it could be Microsoft Explorer.

image

Tried it in Firefox:

image

Had no Google Chrome to test it out (working in a Citrix client environment, can’t install stuff ).

Examined the html-page source: there is some content, but nothing is shown. Checked the enablement of Java script (unlikely cause, rest is working.  Put the browsers in compatibility mode (yes it can): didn’t help either.

Googled, searched My Oracle Support and the community, got 1 good hit, no solution.

Took too much time, back to the command-line for deploying my plugin.

So, checked the plugins through EMCLI.

$ ./emcli list_plugins_on_server
OMS name is <server>:4889_Management_Service
Plug-in Name                                 Plugin-id                     Version [revision]


Oracle Database Appliance                    oracle.dba.odba               13.1.1.1.0
Oracle Cloud Framework                       oracle.sysman.cfw             13.2.1.0.0
Oracle Database                              oracle.sysman.db              13.2.1.0.0
Oracle Fusion Middleware                     oracle.sysman.emas            13.2.1.0.0
Systems Infrastructure                       oracle.sysman.si              13.2.1.0.0
Oracle Exadata                               oracle.sysman.xa              13.2.1.0.0

Deployed the plugin by first exporting the config, deploy the plugin, checked the status (requires downtime):

./emctl exportconfig oms -sysman_pwd “Oracle123#” -dir /home/oracle/job
./emcli deploy_plugin_on_server -plugin=”oracle.sysman.db:13.2.2.0.0″ -repo_backup_taken
./emcli get_plugin_deployment_status -plugin=oracle.sysman.db

So, I moved on, but it really bothers me, back to the browser… must be the browser…

And really, astonished is the right word I think: when zooming out (ctrl – scrolling wheel), the following appears:

image

Pulled the bar down, scrolled back to the original format, and voilà:

image

Worked for both browsers. Incredible simple workaround in the end, quite an unexpected feature….

Regardz

 

The post The curious case of the blank plugin page in OEM13c appeared first on AMIS Oracle and Java Blog.

Calling the Oracle Apiary Mock Server from Java code (via JUnit and Maven)

Sat, 2018-02-10 15:47

In my article “Oracle API Platform Cloud Service: Design-First approach and using Oracle Apiary”, I talked about using Oracle Apiary and interacting with its Mock Server by using code examples.
[https://technology.amis.nl/2018/01/31/oracle-api-platform-cloud-service-design-first-approach-using-oracle-apiary/]

One of the examples for the “HumanResourceService” API is about the action “Get all employees” in combination with “Java” as language. With that example, also the dependencies to be used in a Maven pom.xml (java6+) are given. This example triggered me to try out the Oracle JDeveloper, Maven and Java combination.

In this article the focus will there for be on calling the Oracle Apiary Mock Server from Java code, with the help of Oracle JDeveloper (in combination with JUnit and Maven).

For more information about using Maven, see: https://maven.apache.org

So this time I needed a recent version of Oracle JDeveloper and fortunately I already had a pre-built Oracle VM VirtualBox appliance, named: “Oracle SOA Suite VirtualBox Appliance [May 2016]”, available which contains the following:

  • Oracle Linux (64-bit) EL 6 Update 7
  • Oracle Database, Enterprise Edition 12.1.0.2
  • Oracle SOA Suite 12.2.1 (includes Service Bus, B2B, Oracle Enterprise Scheduler (ESS) and Business Activity Monitoring)
  • NEW: Oracle Real-Time Integration Business Insight 12.2.1
  • Oracle Managed File Transfer 12.2.1
  • Oracle Stream Explorer 12.2.1
  • Oracle JDeveloper 12.2.1
  • Java JDK 1.8.0_51-b16 (64-bit)

For this appliance see:
http://www.oracle.com/technetwork/middleware/soasuite/learnmore/soa-vm-2870913.html
and
http://www.oracle.com/technetwork/middleware/soasuite/learnmore/soasuite1221vbox-readme-2870943.pdf

For a newer version of an appliance with Oracle JDeveloper 12.2.1.1.0 and Java JDK 1.8.0_92(64-bit) [July 2016], take a look at for example:
http://www.oracle.com/technetwork/middleware/soasuite/learnmore/prebuiltvm-soasuite122110-3070567.html
or
http://www.oracle.com/technetwork/community/developer-vm/index.html

For the latest version of Oracle JDeveloper see:
http://www.oracle.com/technetwork/developer-tools/jdev/downloads/index.html

After importing (Integration_12.2.1_OTN.ova) and starting the appliance in VirtualBox, the desktop looks like:

Before starting the appliance I included a SharedFolder (visible on the desktop as sf_MySharedFolder).

Interacting with the Mock Server by using code examples

In Oracle Apiary, for the “HumanResourceService” API, one of the code examples for interacting with its Mock Server is about the action “Get all employees” in combination with “Java” as language.

// Maven : Add these dependecies to your pom.xml (java6+)
// <dependency>
//     <groupId>org.glassfish.jersey.core</groupId>
//     <artifactId>jersey-client</artifactId>
//     <version>2.8</version>
// </dependency>
// <dependency>
//     <groupId>org.glassfish.jersey.media</groupId>
//     <artifactId>jersey-media-json-jackson</artifactId>
//     <version>2.8</version>
// </dependency>

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;

Client client = ClientBuilder.newClient();
Response response = client.target("https://private-b4874b1-humanresourceservice.apiary-mock.com/employees")
  .request(MediaType.TEXT_PLAIN_TYPE)
  .get();

System.out.println("status: " + response.getStatus());
System.out.println("headers: " + response.getHeaders());
System.out.println("body:" + response.readEntity(String.class));

With that example, also the dependencies to be used in a Maven pom.xml (java6+) are given.

Maven implementation in Oracle JDeveloper

I started Oracle JDeveloper and first I took a closer look at the Maven implementation.

Via menu Tools | Preferences.. | Maven you can manage the Maven implementation.

The first time, click on button “Load Extension”.

Then you can see that the JDeveloper Embedded Version (3.2.5) is used. Next I took a look at the other Maven preferences.

Logging/Error:

Phases/Goals:

The “Default” Goal Profile is used with the following phases:

  • clean (project cleaning)
  • compile (compile the source code of the project)
  • test (test the compiled source code using a suitable unit testing framework. These tests should not require the code be packaged or deployed)
  • package (take the compiled code and package it in its distributable format, such as a JAR)
  • install (install the package into the local repository, for use as a dependency in other projects locally)
  • site (project site documentation)

[https://docs.oracle.com/middleware/1221/jdev/user-guide/GUID-609458B4-79BD-4F5E-8ABE-C5C616576E7D.htm#OJDUG5847]

For more information about Maven build lifecycles and phases, see:
https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html

Repositories:

Here you can see which repositories are used.
A Maven repository holds build artifacts and dependencies of varying types.

The local repository refers to a copy on your own installation that is a cache of the remote downloads. It also contains the temporary build artifacts that you have not yet released.

By default the local repository is located at ${user.home}/.m2/repository/. You can change the path and repository name by clicking the Edit button
[Oracle JDeveloper Help, Maven: Repositories Dialog]

The “Maven Central” repository is a remote repository. The URL for the Maven central repository is http://repo1.Maven.org/Maven2
[Oracle JDeveloper Help, Maven: Repositories Dialog]

For more information about Maven repositories, see:
https://maven.apache.org/guides/introduction/introduction-to-repositories.html

Settings:

Here you can specify the location of the Maven settings.xml file and specify command line options for Maven.

The content of file /home/oracle/.m2/setting.xml is:

<?xml version="1.0" encoding="UTF-8"?>
<settings xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd" xmlns="http://maven.apache.org/SETTINGS/1.1.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <proxies>
    <proxy>
      <port>80</port>
      <host>www-proxy.us.oracle.com</host>
      <nonProxyHosts>localhost|127.0.0.0/8|integration|integration.oracle.com|*.us.oracle.com|localhost.localdomain|127.0.0.1|[::1]|127.0.0.1</nonProxyHosts>
    </proxy>
  </proxies>
</settings>

For more information about Maven Settings, see:
https://maven.apache.org/settings.html

Maven standard directory layout

Having a common directory layout would allow for users familiar with one Maven project to immediately feel at home in another Maven project. The advantages are analogous to adopting a site-wide look-and-feel.
[https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html]

For calling the Oracle Apiary Mock Server from Java code, I used the following maven standard directory layout:

To match the default maven structure in JDeveloper I created a Custom Application.

Be aware that there are other options in JDeveloper to work with Maven projects. For example by using Maven Archetypes.
[https://docs.oracle.com/middleware/1221/jdev/user-guide/GUID-609458B4-79BD-4F5E-8ABE-C5C616576E7D.htm#OJDUG6528%5d]

Creating a Custom Application

Via menu File | New | Application.. | Custom Application an application can be created that matches the default maven structure (maven standard directory layout).
[https://docs.oracle.com/middleware/1221/jdev/user-guide/GUID-609458B4-79BD-4F5E-8ABE-C5C616576E7D.htm#OJDUG6874]

In the wizard, I created application “ServicesApplication” with Application Package Prefix “nl.xyz.services”.

After clicking on the button “Next >” I filled in the Project Name “HumanResourceServiceProject” and as Project Features, selected “Maven”.

In the next screen, I changed the default Group ID from nl.xyz.services to nl.xyz.services.humanresourceservice.

Also the checkbox was checked, to modify the normal project structure to match the default maven structure.

In the next screen, “Use Maven” was chosen.

After finishing the wizard, the Applications window for the “ServicesApplication” looked like:

The Project level Maven POM file overview screen looks like:

The “HumanResourceServiceProject”, Project Properties look like:

In the “Project Source Paths” screen, I changed the Default Package from nl.xyz.services to nl.xyz.services.humanresourceservice.

Here you can see that the Project Source Path settings are set in the Project level Maven POM file. The Java Source Path (matching the default maven structure) is set to:
/u02/oracle/developer/mywork/ServicesApplication/HumanResourceServiceProject/src/main/java

After finishing the wizard, the Application level pom.xml content is:

<?xml version="1.0" encoding="UTF-8" ?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>
  <groupId>nl.xyz.services</groupId>
  <artifactId>ServicesApplication</artifactId>
  <version>1.0-SNAPSHOT</version>
  <description>Super POM for ServicesApplication</description>
  <modules>
    <module>HumanResourceServiceProject</module>
  </modules>
  <packaging>pom</packaging>
  <dependencies>
    <dependency>
      <groupId>nl.xyz.services.humanresourceservice</groupId>
      <artifactId>HumanResourceServiceProject</artifactId>
      <version>1.0-SNAPSHOT</version>
      <type>jar</type>
    </dependency>
  </dependencies>
</project>

After finishing the wizard, the Project level pom.xml content is:

<?xml version="1.0" encoding="UTF-8" ?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>
  <groupId>nl.xyz.services.humanresourceservice</groupId>
  <artifactId>HumanResourceServiceProject</artifactId>
  <version>1.0-SNAPSHOT</version>
  <description>Project for HumanResourceServiceProject</description>
  <build>
    <resources>
      <resource>
        <directory>${basedir}</directory>
        <includes>
          <include>*</include>
        </includes>
      </resource>
      <resource>
        <directory>src/main/resources/</directory>
        <includes>
          <include>*</include>
        </includes>
      </resource>
    </resources>
  </build>
</project>
Creating Java classes “Employee” and “Department”

As described in my previous article, the “Human Resource Service” API contains actions for “Employees” and “Departments”.

So there for I decided to create two Java classes corresponding with them.

Via menu File | New | Java Class…, I created a Java class called “Employee” and in the wizard, I unchecked “Implement Abstract Methods”.

package nl.xyz.services.humanresourceservice;

public class Employee {
    public Employee() {
        super();
    }
}

In the same way I created a Java class called “Department”.

package nl.xyz.services.humanresourceservice;

public class Department {
    public Department() {
        super();
    }
}

After creating the Java classes, the Applications window for the “ServicesApplication” looked like:

The two Java classes (with extension .java) where created in directory:
/u02/oracle/developer/mywork/ServicesApplication/HumanResourceServiceProject/src/main/java/nl/xyz/services/humanresourceservice

Apache Maven – compile

So far so good. In JDeveIoper, I created an Application, Project and two Java classes. Time to put Maven to work.

Via a right-click on the pom.xml | Run Maven, the Phases/Goals you can choose from, become visible:

After choosing “compile”, the following popped-up:

Here I clicked the button “Yes”. After this, the content of file /home/oracle/.m2/setting.xml is:

<?xml version="1.0" encoding="UTF-8"?>
<settings xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd" xmlns="http://maven.apache.org/SETTINGS/1.1.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <proxies/>
</settings>

Remark:
The JDeveloper proxy settings (see Tools | Preferences | Web Browser and Proxy) were set on: Use System Default Proxy Settings.

With this option, the server uses the default proxy settings on your machine. These are taken from the operation system (in the case of Windows and MacOS) or from the window manager (in the case of Linux).
[Oracle JDeveloper Help, Preferences Dialog – Web Browser and Proxy Page – Proxy Settings]

The content of Apache Maven – compile – Log is:

[INFO] Compiling 2 source files to /u02/oracle/developer/mywork/ServicesApplication/HumanResourceServiceProject/target/classes
[INFO] ————————————————————————
[INFO] BUILD SUCCESS
[INFO] ————————————————————————
[INFO] Total time: 7.375 s
[INFO] Finished at: 2018-02-06T13:23:45-08:00
[INFO] Final Memory: 14M/133M
[INFO] ————————————————————————
Process exited with exit code 0.

The two compiled Java classes (with extension .class) where created in directory:
/u02/oracle/developer/mywork/ServicesApplication/HumanResourceServiceProject/target/classes/nl/xyz/services/humanresourceservice

Java class “Employee” interacting with the Mock Server

In Oracle Apiary, for the “HumanResourceService” API, one of the code examples for interacting with its Mock Server is about the action “Get all employees”.

There for I changed the code for Java class “Employee” and used the Java code example from Oracle Apiary.

package nl.xyz.services.humanresourceservice;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;

public class Employee {
    public Employee() {
        super();
    }
    
    public Response getAllEmployees() {
        Client client = ClientBuilder.newClient();
        Response response = client.target("https://private-b4874b1-humanresourceservice.apiary-mock.com/employees")
          .request(MediaType.TEXT_PLAIN_TYPE)
          .get();

        System.out.println("status: " + response.getStatus());
        System.out.println("headers: " + response.getHeaders());
        System.out.println("body:" + response.readEntity(String.class));
        return response;
    }
}

Of course then there were several issues found.

In line with the comment (// Maven : Add these dependecies to your pom.xml (java6+)) in the Java code example from Oracle Apiary, dependencies had to be added in the Project level Maven POM file.

Based on the following comment:

// Maven : Add these dependecies to your pom.xml (java6+)
// <dependency>
//    <groupId>org.glassfish.jersey.core</groupId>
//    <artifactId>jersey-client</artifactId>
//    <version>2.8</version>
// </dependency>

// <dependency>
//    <groupId>org.glassfish.jersey.media</groupId>
//    <artifactId>jersey-media-json-jackson</artifactId>
//    <version>2.8</version>
// </dependency>

I added the dependency in the Project level Maven POM file overview screen, tab “Dependencies” (by using the icon +):

After doing that, the Project level pom.xml content was:

<?xml version="1.0" encoding="UTF-8" ?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>
  <groupId>nl.xyz.services.humanresourceservice</groupId>
  <artifactId>HumanResourceServiceProject</artifactId>
  <version>1.0-SNAPSHOT</version>
  <description>Project for HumanResourceServiceProject</description>
  <build>
    <resources>
      <resource>
        <directory>${basedir}</directory>
        <includes>
          <include>*</include>
        </includes>
      </resource>
      <resource>
        <directory>src/main/resources/</directory>
        <includes>
          <include>*</include>
        </includes>
      </resource>
    </resources>
  </build>
  <dependencies>
    <dependency>
      <groupId>org.glassfish.jersey.core</groupId>
      <artifactId>jersey-client</artifactId>
      <version>2.8</version>
    </dependency>
  </dependencies>
</project>

The other comment part I copied / pasted in the tab Source of the pom.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>
  <groupId>nl.xyz.services.humanresourceservice</groupId>
  <artifactId>HumanResourceServiceProject</artifactId>
  <version>1.0-SNAPSHOT</version>
  <description>Project for HumanResourceServiceProject</description>
  <build>
    <resources>
      <resource>
        <directory>${basedir}</directory>
        <includes>
          <include>*</include>
        </includes>
      </resource>
      <resource>
        <directory>src/main/resources/</directory>
        <includes>
          <include>*</include>
        </includes>
      </resource>
    </resources>
  </build>
  <dependencies>
    <dependency>
      <groupId>org.glassfish.jersey.core</groupId>
      <artifactId>jersey-client</artifactId>
      <version>2.8</version>
    </dependency>
    <dependency>
       <groupId>org.glassfish.jersey.media</groupId>
       <artifactId>jersey-media-json-jackson</artifactId>
       <version>2.8</version>
    </dependency>
  </dependencies>
</project>

After saving the pom.xml, the dependencies where being resolved.

The Maven local repository was updated, with among others, the following two jar files:

See also:
http://repo1.maven.org/maven2/org/glassfish/jersey/core/jersey-client/2.8/
respectively:
http://repo1.maven.org/maven2/org/glassfish/jersey/media/jersey-media-json-jackson/2.8/

JUnit

In order to invoke the Oracle Apiary Mock Server for the action “Get all employees” from the Java class “Employee”, I decided to use a JUnit TestCase class.

For this I had to install an Oracle Extension for Oracle JDeveloper. Via menu Help | Check for Updates…, from the list of Available Updates, I choose “JUnit Integration” and installed it.

After the install, a JDeveloper restart was done.

For more information about JUnit, see:
https://junit.org/junit4

Creating Java class “EmployeeTest”

In the Applications window for the “ServicesApplication”, after a right-click on package nl.xyz.services.humanresourceservice, via the menu File | New | From gallery… | General | Unit Tests | Test Case, a wizard was started.

In the dropdown box I selected the Java class “Employee”, checked the check box for method getAllEmployees() and clicked on button “Next >”.

After clicking on Next and then Finish (in the screen above) a Java class “EmployeeTest” was created.

There were several issues found. The needed JUnit library seemed not to be part of the classpath.
A right-click on Java class “EmployeeTest” followed by Make and then followed by Reformat fixed the problem and the issues disappeared.

After creating the JUnit TestCase class, the Project level pom.xml content was:

<?xml version="1.0" encoding="UTF-8" ?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>
  <groupId>nl.xyz.services.humanresourceservice</groupId>
  <artifactId>HumanResourceServiceProject</artifactId>
  <version>1.0-SNAPSHOT</version>
  <description>Project for HumanResourceServiceProject</description>
  <build>
    <resources>
      <resource>
        <directory>${basedir}</directory>
        <includes>
          <include>*</include>
        </includes>
      </resource>
      <resource>
        <directory>src/main/resources/</directory>
        <includes>
          <include>*</include>
        </includes>
      </resource>
    </resources>
  </build>
  <dependencies>
    <dependency>
      <groupId>org.glassfish.jersey.core</groupId>
      <artifactId>jersey-client</artifactId>
      <version>2.8</version>
      <type>jar</type>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.glassfish.jersey.media</groupId>
      <artifactId>jersey-media-json-jackson</artifactId>
      <version>2.8</version>
      <type>jar</type>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>com.oracle.adf.library</groupId>
      <artifactId>JUnit-4-Runtime</artifactId>
      <version>12.2.1-0-0</version>
      <type>pom</type>
      <scope>provided</scope>
    </dependency>
  </dependencies>
</project>

Notice that a dependency for JUnit was automatically added to the Project level POM file.

After creating the JUnit TestCase class, the Applications window for the “ServicesApplication” looked like:

Remark:
Of course you can also create a dedicated project in the application for testing. See also the tab “Test Settings” in the Project level Maven POM file overview screen.

Apache Maven – test

After a right-click on the pom.xml | Run Maven | test, the content of Apache Maven – test – Log is:


INFO] — maven-surefire-plugin:2.12.4:test (default-test) @ HumanResourceServiceProject —
[INFO] No tests to run.
[INFO] ————————————————————————
[INFO] BUILD SUCCESS
[INFO] ————————————————————————
[INFO] Total time: 1.160 s
[INFO] Finished at: 2018-02-08T12:10:12-08:00
[INFO] Final Memory: 9M/191M
[INFO] ————————————————————————
Process exited with exit code 0.

There were no test classes found! This is because the Java class “EmployeeTest” is not in the correct default maven structure (maven standard directory layout).
To fix this, the JUnit TestCase class has to be in the right subdirectory under the src/test/java (Test sources) directory. This can be done in several ways, but I used File | Save As… .

In the Save As screen, via repeated use of the icon “Create new subdirectory”, I created the following subdirectory: src/test/java/nl/xyz/services/humanresourceservice and then clicked on button “Save”.

In the “Save Warning” pop-up I clicked on button “Yes”.

In the “Add to Project Content” pop-up, I selected “Java Paths” and clicked on button “OK”.

Then the Java class “EmployeeTest” was deleted, via right-click | Delete.

In the “Confirm Delete” pop-up, I clicked on button “OK”.

After a right-click on the pom.xml | Run Maven | test, the content of Apache Maven – test – Log is:

[INFO] — maven-surefire-plugin:2.12.4:test (default-test) @ HumanResourceServiceProject —
[INFO] Surefire report directory: /u02/oracle/developer/mywork/ServicesApplication/HumanResourceServiceProject/target/surefire-reports

——————————————————-
T E S T S
——————————————————-
Running nl.xyz.services.humanresourceservice.EmployeeTest
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.013 sec <<< FAILURE!
nl.xyz.services.humanresourceservice.EmployeeTest.testGetAllEmployees() Time elapsed: 0.008 sec <<< FAILURE!
java.lang.AssertionError: Unimplemented
at org.junit.Assert.fail(Assert.java:88)
at nl.xyz.services.humanresourceservice.EmployeeTest.testGetAllEmployees(EmployeeTest.java:15)

Results :

Failed tests: nl.xyz.services.humanresourceservice.EmployeeTest.testGetAllEmployees(): Unimplemented

Tests run: 1, Failures: 1, Errors: 0, Skipped: 0

[INFO] ————————————————————————
[INFO] BUILD FAILURE
[INFO] ————————————————————————
[INFO] Total time: 2.473 s
[INFO] Finished at: 2018-02-08T12:42:57-08:00
[INFO] Final Memory: 15M/200M
[INFO] ————————————————————————
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test (default-test) on project HumanResourceServiceProject: There are test failures.
[ERROR]
[ERROR] Please refer to /u02/oracle/developer/mywork/ServicesApplication/HumanResourceServiceProject/target/surefire-reports for the individual test results.
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
Process exited with exit code 1.

There was a test class found, but it failed. This was an expected outcome, because of the org.junit.Assert.fail(String message) method, which fails a test with the given message.
[https://junit.org/junit4/javadoc/4.12/org/junit/Assert.html#fail(java.lang.String)]

package nl.xyz.services.humanresourceservice;

import static org.junit.Assert.*;
import org.junit.Test;

public class EmployeeTest {
    public EmployeeTest() {
    }

    /**
     * @see Employee#getAllEmployees()
     */
    @Test
    public void testGetAllEmployees() {
        fail("Unimplemented");
    }
}
Making Java class “EmployeeTest” visible in the Applications window

In order to make the Java class “EmployeeTest” visible in the Applications window for the “ServicesApplication”, the Project level POM file had to be modified.

I added a resource directory in the Project level Maven POM file overview screen, tab “Source Paths”, part “Resources | Resource Directories” (by using the icon +):

Where I selected the src/test/java subdirectory.

After adding the resource directory, the Project level pom.xml content is:

<?xml version="1.0" encoding="UTF-8" ?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>
  <groupId>nl.xyz.services.humanresourceservice</groupId>
  <artifactId>HumanResourceServiceProject</artifactId>
  <version>1.0-SNAPSHOT</version>
  <description>Project for HumanResourceServiceProject</description>
  <build>
    <resources>
      <resource>
        <directory>${basedir}</directory>
        <includes>
          <include>*</include>
        </includes>
      </resource>
      <resource>
        <directory>src/main/resources/</directory>
        <includes>
          <include>*</include>
        </includes>
      </resource>
      <resource>
        <directory>src/test/java/</directory>
      </resource>
    </resources>
  </build>
  <dependencies>
    <dependency>
      <groupId>org.glassfish.jersey.core</groupId>
      <artifactId>jersey-client</artifactId>
      <version>2.8</version>
      <type>jar</type>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.glassfish.jersey.media</groupId>
      <artifactId>jersey-media-json-jackson</artifactId>
      <version>2.8</version>
      <type>jar</type>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>com.oracle.adf.library</groupId>
      <artifactId>JUnit-4-Runtime</artifactId>
      <version>12.2.1-0-0</version>
      <type>pom</type>
      <scope>provided</scope>
    </dependency>
  </dependencies>
</project>

After adding the resource directory, the Applications window for the “ServicesApplication” looked like:

Modifying Java class “EmployeeTest”

Then I modified the Java class “EmployeeTest” in order the make it call the Java class “Employee”:

package nl.xyz.services.humanresourceservice;

import javax.ws.rs.core.Response;

import static org.junit.Assert.*;
import org.junit.Test;

public class EmployeeTest {
    public EmployeeTest() {
    }

    private Employee employee = new Employee();

    /**
     * @see Employee#getAllEmployees()
     */
    @Test
    public void testGetAllEmployees() {
        Response response = employee.getAllEmployees();
    }
}

After a right-click on the pom.xml | Run Maven | test, the content of Apache Maven – test – Log:
(Remark: I shortened the response list of employees)



[INFO] — maven-surefire-plugin:2.12.4:test (default-test) @ HumanResourceServiceProject —
[INFO] Surefire report directory: /u02/oracle/developer/mywork/ServicesApplication/HumanResourceServiceProject/target/surefire-reports

——————————————————-
T E S T S
——————————————————-
Running nl.xyz.services.humanresourceservice.EmployeeTest
status: 200
headers: {Server=[Cowboy], Access-Control-Allow-Origin=[*], Access-Control-Allow-Methods=[OPTIONS,GET,HEAD,POST,PUT,DELETE,TRACE,CONNECT], Connection=[keep-alive], X-Apiary-Ratelimit-Limit=[120], Date=[Thu, 08 Feb 2018 21:29:12 GMT], Via=[1.1 vegur], X-Apiary-Transaction-Id=[5a7cc1282efcd107002bce05], Content-Length=[15519], Access-Control-Max-Age=[10], X-Apiary-Ratelimit-Remaining=[119], Content-Type=[application/json]}
body:{
“items”: [
{
“employee_id”: 100,
“first_name”: “Steven”,
“last_name”: “King”,
“email”: “SKING”,
“phone_number”: “515.123.4567”,
“hire_date”: “1987-06-17T04:00:00Z”,
“job_id”: “AD_PRES”,
“salary”: 24000,
“commission_pct”: null,
“manager_id”: null,
“department_id”: 90,
“links”: [
{
“rel”: “self”,
“href”: “http://localhost:9090/ords/hr/employees/100”
}
]
},
{
“employee_id”: 101,
“first_name”: “Neena”,
“last_name”: “Kochhar”,
“email”: “NKOCHHAR”,
“phone_number”: “515.123.4568”,
“hire_date”: “1989-09-21T04:00:00Z”,
“job_id”: “AD_VP”,
“salary”: 17000,
“commission_pct”: null,
“manager_id”: 100,
“department_id”: 90,
“links”: [
{
“rel”: “self”,
“href”: “http://localhost:9090/ords/hr/employees/101”
}
]
},

{
“employee_id”: 123,
“first_name”: “Shanta”,
“last_name”: “Vollman”,
“email”: “SVOLLMAN”,
“phone_number”: “650.123.4234”,
“hire_date”: “1997-10-10T04:00:00Z”,
“job_id”: “ST_MAN”,
“salary”: 6500,
“commission_pct”: null,
“manager_id”: 100,
“department_id”: 50,
“links”: [
{
“rel”: “self”,
“href”: “http://localhost:9090/ords/hr/employees/123”
}
]
},
{
“employee_id”: 124,
“first_name”: “Kevin”,
“last_name”: “Mourgos”,
“email”: “KMOURGOS”,
“phone_number”: “650.123.5234”,
“hire_date”: “1999-11-16T05:00:00Z”,
“job_id”: “ST_MAN”,
“salary”: 5800,
“commission_pct”: null,
“manager_id”: 100,
“department_id”: 50,
“links”: [
{
“rel”: “self”,
“href”: “http://localhost:9090/ords/hr/employees/124”
}
]
}
],
“hasMore”: true,
“limit”: 25,
“offset”: 0,
“count”: 25,
“links”: [
{
“rel”: “self”,
“href”: “http://localhost:9090/ords/hr/employees/”
},
{
“rel”: “edit”,
“href”: “http://localhost:9090/ords/hr/employees/”
},
{
“rel”: “describedby”,
“href”: “http://localhost:9090/ords/hr/metadata-catalog/employees/”
},
{
“rel”: “first”,
“href”: “http://localhost:9090/ords/hr/employees/”
},
{
“rel”: “next”,
“href”: “http://localhost:9090/ords/hr/employees/?offset=25”
}
]
}
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.147 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO] ————————————————————————
[INFO] BUILD SUCCESS
[INFO] ————————————————————————
[INFO] Total time: 4.570 s
[INFO] Finished at: 2018-02-08T13:33:14-08:00
[INFO] Final Memory: 16M/246M
[INFO] ————————————————————————
Process exited with exit code 0.

Looking at this output it was obvious that interacting with the Oracle Apiary MockServer action “Get all employees” from Java class “Employee” via the Java class “EmployeeTest” worked correctly.

API Inspector

I then started Oracle Apiary, via: https://apiary.io/ , where I signed in and looked at the API Inspector.

Each request and response from the Mock Server is logged in the API Inspector, which can be found by clicking “Inspector” in the Apiary header. There you will see each request received, each response given, and any validation errors that were found.
[https://help.apiary.io/tools/api-inspector/]

There I could see, the request that was made from the Java class “EmployeeTest”:

For that request (by clicking on it) more details are available:

Completing the HumanResourceServiceProject

So now a successful call to the Oracle Apiary MockServer action “Get all employees” was made, from Java class “Employee” via the Java class “EmployeeTest”.

There were other actions that had to be implemented, and also in the Java class “Department”. So, in the same way as Java class “EmployeeTest”, I created Java class “Department Test”.

After adding the Java class, the Applications window for the “ServicesApplication” looks like:

Below you can find the code for the Java classes.

Remark:
All the Java code in this article is not suitable for a production environment, but meant as simple examples.

Java class “Employee”:

package nl.xyz.services.humanresourceservice;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;

public class Employee {
    private String mockServerURI;

    public Employee(String mockServerURI) {
        this.mockServerURI = mockServerURI;
    }

    public Response getAllEmployees() {
        Client client = ClientBuilder.newClient();
        String requestURI = this.mockServerURI + "/employees";
        System.out.println("requestURI: " + requestURI);
        Response response = client.target(requestURI).request(MediaType.TEXT_PLAIN_TYPE).get();

        System.out.println("status: " + response.getStatus());
        System.out.println("headers: " + response.getHeaders());
        System.out.println("body:" + response.readEntity(String.class));
        return response;
    }

    public Response getEmployee(int id) {
        Client client = ClientBuilder.newClient();
        String requestURI = this.mockServerURI + "/employees/" + id;
        System.out.println("requestURI: " + requestURI);
        Response response = client.target(requestURI).request(MediaType.TEXT_PLAIN_TYPE).get();

        System.out.println("status: " + response.getStatus());
        System.out.println("headers: " + response.getHeaders());
        System.out.println("body:" + response.readEntity(String.class));
        return response;
    }

    public Response createEmployee() {
        Client client = ClientBuilder.newClient();
        String requestURI = this.mockServerURI + "/employees";
        System.out.println("requestURI: " + requestURI);
        Entity payload =
            Entity.json("{  'LAST_NAME': 'TESTINSERT',  'FIRST_NAME': 'TESTFIRST',  'EMAIL': 'TESTMAIL',  'HIRE_DATE': '25-JUN-15',  'JOB_ID': 'IT_PROG',  'SALARY': 6000,  'MANAGER_ID': 103,  'DEPARTMENT_ID': 60}");
        Response response = client.target(requestURI).request(MediaType.APPLICATION_JSON_TYPE).post(payload);

        System.out.println("status: " + response.getStatus());
        System.out.println("headers: " + response.getHeaders());
        System.out.println("body:" + response.readEntity(String.class));
        return response;
    }

    public Response updateEmployee(int id) {
        Client client = ClientBuilder.newClient();
        String requestURI = this.mockServerURI + "/employees/" + id;
        System.out.println("requestURI: " + requestURI);
        Entity payload =
            Entity.json("{  'LAST_NAME': 'TESTUPDATE',  'JOB_ID': 'SA_REP',  'SALARY': 8000,  'DEPARTMENT_ID': 80}");
        Response response = client.target(requestURI).request(MediaType.APPLICATION_JSON_TYPE).put(payload);

        System.out.println("status: " + response.getStatus());
        System.out.println("headers: " + response.getHeaders());
        System.out.println("body:" + response.readEntity(String.class));
        return response;
    }
}

Java class “EmployeeTest”:

package nl.xyz.services.humanresourceservice;

import javax.ws.rs.core.Response;

import static org.junit.Assert.*;
import org.junit.Test;

public class EmployeeTest {
    private static final String HTTP_STATUS_CODE = "HTTP status code";
    private static final int HTTP_STATUS_CODE_OK = 200;
    private static final int HTTP_STATUS_CODE_CREATED = 201;
    private Employee employee = new Employee("https://private-b4874b1-humanresourceservice.apiary-mock.com");

    public EmployeeTest() {
    }

    /**
     * @see Employee#getAllEmployees()
     */
    @Test
    public void testGetAllEmployees() {
        Response response = employee.getAllEmployees();
        assertEquals(HTTP_STATUS_CODE, HTTP_STATUS_CODE_OK, response.getStatus());
    }

    /**
     * @see Employee#getEmployee()
     */
    @Test
    public void testGetEmployee() {
        int id = 100;
        Response response = employee.getEmployee(id);
        assertEquals(HTTP_STATUS_CODE, HTTP_STATUS_CODE_OK, response.getStatus());
    }

    /**
     * @see Employee#createEmployee()
     */
    @Test
    public void testCreateEmployee() {
        Response response = employee.createEmployee();
        assertEquals(HTTP_STATUS_CODE, HTTP_STATUS_CODE_CREATED, response.getStatus());
    }

    /**
     * @see Employee#updateEmployee()
     */
    @Test
    public void testUpdateEmployee() {
        int id = 220;
        Response response = employee.updateEmployee(id);
        assertEquals(HTTP_STATUS_CODE, HTTP_STATUS_CODE_OK, response.getStatus());
    }
}

Java class “Department”:

package nl.xyz.services.humanresourceservice;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;

public class Department {
    private String mockServerURI;

    public Department(String mockServerURI) {
        this.mockServerURI = mockServerURI;
    }

    public Response getDepartment(int id) {
        Client client = ClientBuilder.newClient();
        String requestURI = this.mockServerURI + "/department/" + id;
        System.out.println("requestURI: " + requestURI);
        Response response = client.target(requestURI).request(MediaType.TEXT_PLAIN_TYPE).get();

        System.out.println("status: " + response.getStatus());
        System.out.println("headers: " + response.getHeaders());
        System.out.println("body:" + response.readEntity(String.class));
        return response;
    }

    public Response getDepartmentEmployee(int department_id, int employee_id) {
        Client client = ClientBuilder.newClient();
        String requestURI = this.mockServerURI + "/departments/" + department_id + "/employees/" + employee_id;
        System.out.println("requestURI: " + requestURI);
        Response response = client.target(requestURI).request(MediaType.TEXT_PLAIN_TYPE).get();

        System.out.println("status: " + response.getStatus());
        System.out.println("headers: " + response.getHeaders());
        System.out.println("body:" + response.readEntity(String.class));
        return response;
    }
}

Java class “DepartmentTest”:

package nl.xyz.services.humanresourceservice;

import javax.ws.rs.core.Response;

import static org.junit.Assert.*;
import org.junit.Test;

public class DepartmentTest {
    private static final String HTTP_STATUS_CODE = "HTTP status code";
    private static final int HTTP_STATUS_CODE_OK = 200;    
    private Department department = new Department("https://private-b4874b1-humanresourceservice.apiary-mock.com");

    public DepartmentTest() {
    }

    /**
     * @see Department#getDepartment(int)
     */
    @Test
    public void testGetDepartment() {
        int id = 30;
        Response response = department.getDepartment(id);
        assertEquals(HTTP_STATUS_CODE, HTTP_STATUS_CODE_OK, response.getStatus());
    }

    /**
     * @see Department#getDepartmentEmployee(int,int)
     */
    @Test
    public void testGetDepartmentEmployee() {
        int department_id = 30;
        int employee_id = 119;
        Response response = department.getDepartmentEmployee(department_id, employee_id);
        assertEquals(HTTP_STATUS_CODE, HTTP_STATUS_CODE_OK, response.getStatus());
    }
}

After a right-click on the pom.xml | Run Maven | test, the content of Apache Maven – test – Log:
(Remark: I shortened the response list of employees)


[INFO] — maven-surefire-plugin:2.12.4:test (default-test) @ HumanResourceServiceProject —
[INFO] Surefire report directory: /u02/oracle/developer/mywork/ServicesApplication/HumanResourceServiceProject/target/surefire-reports

——————————————————-
T E S T S
——————————————————-
Running nl.xyz.services.humanresourceservice.DepartmentTest
requestURI: https://private-b4874b1-humanresourceservice.apiary-mock.com/department/30
status: 200
headers: {Server=[Cowboy], Access-Control-Allow-Origin=[*], Access-Control-Allow-Methods=[OPTIONS,GET,HEAD,POST,PUT,DELETE,TRACE,CONNECT], Connection=[keep-alive], X-Apiary-Ratelimit-Limit=[120], Date=[Fri, 09 Feb 2018 20:10:55 GMT], Via=[1.1 vegur], X-Apiary-Transaction-Id=[5a7e004fc5c2ab07003caec1], Content-Length=[112], Access-Control-Max-Age=[10], X-Apiary-Ratelimit-Remaining=[119], Content-Type=[application/json]}
body:{
“department_id”: 30,
“department_name”: “Purchasing”,
“manager_id”: 114,
“location_id”: 1700
}
requestURI: https://private-b4874b1-humanresourceservice.apiary-mock.com/departments/30/employees/119
status: 200
headers: {Server=[Cowboy], Access-Control-Allow-Origin=[*], Access-Control-Allow-Methods=[OPTIONS,GET,HEAD,POST,PUT,DELETE,TRACE,CONNECT], Connection=[keep-alive], X-Apiary-Ratelimit-Limit=[120], Date=[Fri, 09 Feb 2018 20:10:56 GMT], Via=[1.1 vegur], X-Apiary-Transaction-Id=[5a7e005099b117070044215c], Content-Length=[146], Access-Control-Max-Age=[10], X-Apiary-Ratelimit-Remaining=[118], Content-Type=[application/json]}
body:{
“department_id”: 30,
“department_name”: “Purchasing”,
“employee_id”: 119,
“first_name”: “Karen”,
“last_name”: “Colmenares”
}
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.909 sec
Running nl.xyz.services.humanresourceservice.EmployeeTest
requestURI: https://private-b4874b1-humanresourceservice.apiary-mock.com/employees
status: 201
headers: {Server=[Cowboy], Access-Control-Allow-Origin=[*], Access-Control-Allow-Methods=[OPTIONS,GET,HEAD,POST,PUT,DELETE,TRACE,CONNECT], Connection=[keep-alive], X-Apiary-Ratelimit-Limit=[120], Date=[Fri, 09 Feb 2018 20:10:57 GMT], Via=[1.1 vegur], X-Apiary-Transaction-Id=[5a7e005179e3cd0700eedbb3], Content-Length=[301], Access-Control-Max-Age=[10], X-Apiary-Ratelimit-Remaining=[117], Content-Type=[application/json]}
body:{
“employee_id”: 220,
“first_name”: “TESTFIRST”,
“last_name”: “TESTINSERT”,
“email”: “TESTMAIL”,
“phone_number”: null,
“hire_date”: “2015-06-25T04:00:00Z”,
“job_id”: “IT_PROG”,
“salary”: 6000,
“commission_pct”: null,
“manager_id”: 103,
“department_id”: 60
}
requestURI: https://private-b4874b1-humanresourceservice.apiary-mock.com/employees/220
status: 200
headers: {Server=[Cowboy], Access-Control-Allow-Origin=[*], Access-Control-Allow-Methods=[OPTIONS,GET,HEAD,POST,PUT,DELETE,TRACE,CONNECT], Connection=[keep-alive], X-Apiary-Ratelimit-Limit=[120], Date=[Fri, 09 Feb 2018 20:10:58 GMT], Via=[1.1 vegur], X-Apiary-Transaction-Id=[5a7e00520fb27c07004e4008], Content-Length=[300], Access-Control-Max-Age=[10], X-Apiary-Ratelimit-Remaining=[116], Content-Type=[application/json]}
body:{
“employee_id”: 220,
“first_name”: “TESTFIRST”,
“last_name”: “TESTUPDATE”,
“email”: “TESTMAIL”,
“phone_number”: null,
“hire_date”: “2015-06-25T04:00:00Z”,
“job_id”: “SA_REP”,
“salary”: 8000,
“commission_pct”: null,
“manager_id”: 103,
“department_id”: 80
}
requestURI: https://private-b4874b1-humanresourceservice.apiary-mock.com/employees/100
status: 200
headers: {Server=[Cowboy], Access-Control-Allow-Origin=[*], Access-Control-Allow-Methods=[OPTIONS,GET,HEAD,POST,PUT,DELETE,TRACE,CONNECT], Connection=[keep-alive], X-Apiary-Ratelimit-Limit=[120], Date=[Fri, 09 Feb 2018 20:10:59 GMT], Via=[1.1 vegur], X-Apiary-Transaction-Id=[5a7e005399b117070044215f], Content-Length=[806], Access-Control-Max-Age=[10], X-Apiary-Ratelimit-Remaining=[115], Content-Type=[application/json]}
body:{
“employee_id”: 100,
“first_name”: “Steven”,
“last_name”: “King”,
“email”: “SKING”,
“phone_number”: “515.123.4567”,
“hire_date”: “1987-06-17T04:00:00Z”,
“job_id”: “AD_PRES”,
“salary”: 24000,
“commission_pct”: null,
“manager_id”: null,
“department_id”: 90,
“links”: [
{
“rel”: “self”,
“href”: “http://localhost:9090/ords/hr/employees/100”
},
{
“rel”: “edit”,
“href”: “http://localhost:9090/ords/hr/employees/100”
},
{
“rel”: “describedby”,
“href”: “http://localhost:9090/ords/hr/metadata-catalog/employees/item”
},
{
“rel”: “collection”,
“href”: “http://localhost:9090/ords/hr/employees/”
}
]
}
requestURI: https://private-b4874b1-humanresourceservice.apiary-mock.com/employees
status: 200
headers: {Server=[Cowboy], Access-Control-Allow-Origin=[*], Access-Control-Allow-Methods=[OPTIONS,GET,HEAD,POST,PUT,DELETE,TRACE,CONNECT], Connection=[keep-alive], X-Apiary-Ratelimit-Limit=[120], Date=[Fri, 09 Feb 2018 20:11:00 GMT], Via=[1.1 vegur], X-Apiary-Transaction-Id=[5a7e00540fb27c07004e400b], Content-Length=[15519], Access-Control-Max-Age=[10], X-Apiary-Ratelimit-Remaining=[119], Content-Type=[application/json]}
body:{
“items”: [
{
“employee_id”: 100,
“first_name”: “Steven”,
“last_name”: “King”,
“email”: “SKING”,
“phone_number”: “515.123.4567”,
“hire_date”: “1987-06-17T04:00:00Z”,
“job_id”: “AD_PRES”,
“salary”: 24000,
“commission_pct”: null,
“manager_id”: null,
“department_id”: 90,
“links”: [
{
“rel”: “self”,
“href”: “http://localhost:9090/ords/hr/employees/100”
}
]
},
{
“employee_id”: 101,
“first_name”: “Neena”,
“last_name”: “Kochhar”,
“email”: “NKOCHHAR”,
“phone_number”: “515.123.4568”,
“hire_date”: “1989-09-21T04:00:00Z”,
“job_id”: “AD_VP”,
“salary”: 17000,
“commission_pct”: null,
“manager_id”: 100,
“department_id”: 90,
“links”: [
{
“rel”: “self”,
“href”: “http://localhost:9090/ords/hr/employees/101”
}
]
},

{
“employee_id”: 123,
“first_name”: “Shanta”,
“last_name”: “Vollman”,
“email”: “SVOLLMAN”,
“phone_number”: “650.123.4234”,
“hire_date”: “1997-10-10T04:00:00Z”,
“job_id”: “ST_MAN”,
“salary”: 6500,
“commission_pct”: null,
“manager_id”: 100,
“department_id”: 50,
“links”: [
{
“rel”: “self”,
“href”: “http://localhost:9090/ords/hr/employees/123”
}
]
},
{
“employee_id”: 124,
“first_name”: “Kevin”,
“last_name”: “Mourgos”,
“email”: “KMOURGOS”,
“phone_number”: “650.123.5234”,
“hire_date”: “1999-11-16T05:00:00Z”,
“job_id”: “ST_MAN”,
“salary”: 5800,
“commission_pct”: null,
“manager_id”: 100,
“department_id”: 50,
“links”: [
{
“rel”: “self”,
“href”: “http://localhost:9090/ords/hr/employees/124”
}
]
}
],
“hasMore”: true,
“limit”: 25,
“offset”: 0,
“count”: 25,
“links”: [
{
“rel”: “self”,
“href”: “http://localhost:9090/ords/hr/employees/”
},
{
“rel”: “edit”,
“href”: “http://localhost:9090/ords/hr/employees/”
},
{
“rel”: “describedby”,
“href”: “http://localhost:9090/ords/hr/metadata-catalog/employees/”
},
{
“rel”: “first”,
“href”: “http://localhost:9090/ords/hr/employees/”
},
{
“rel”: “next”,
“href”: “http://localhost:9090/ords/hr/employees/?offset=25”
}
]
}
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.594 sec

Results :

Tests run: 6, Failures: 0, Errors: 0, Skipped: 0

[INFO] ————————————————————————
[INFO] BUILD SUCCESS
[INFO] ————————————————————————
[INFO] Total time: 8.834 s
[INFO] Finished at: 2018-02-09T12:15:04-08:00
[INFO] Final Memory: 15M/198M
[INFO] ————————————————————————
Process exited with exit code 0.

In the Oracle Apiary API Inspector, I could see, the 6 request that were made in total from the Java classes “EmployeeTest” and “DepartementTest” during the last Maven test run:

Apache Maven – clean

After a right-click on the pom.xml | Run Maven | clean, the content of Apache Maven – clean – Log is:

INFO] — maven-clean-plugin:2.5:clean (default-clean) @ HumanResourceServiceProject —
[INFO] Deleting /u02/oracle/developer/mywork/ServicesApplication/HumanResourceServiceProject/target
[INFO] ————————————————————————
[INFO] BUILD SUCCESS
[INFO] ————————————————————————
[INFO] Total time: 0.316 s
[INFO] Finished at: 2018-02-09T07:51:02-08:00
[INFO] Final Memory: 8M/151M
[INFO] ————————————————————————
Process exited with exit code 0.

This results in subdirectory target being deleted from the file system.

Apache Maven – package

After a right-click on the pom.xml | Run Maven | package, the content of Apache Maven – package – Log is:

[INFO] Scanning for projects…
[INFO]
[INFO] ————————————————————————
[INFO] Building HumanResourceServiceProject 1.0-SNAPSHOT
[INFO] ————————————————————————
[INFO]
[INFO] — maven-resources-plugin:2.6:resources (default-resources) @ HumanResourceServiceProject —
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 2 resources
[INFO] Copying 0 resource
[INFO] Copying 2 resources
[INFO]
[INFO] — maven-compiler-plugin:3.1:compile (default-compile) @ HumanResourceServiceProject —
[INFO] Nothing to compile – all classes are up to date
[INFO]
[INFO] — maven-resources-plugin:2.6:testResources (default-testResources) @ HumanResourceServiceProject —
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /u02/oracle/developer/mywork/ServicesApplication/HumanResourceServiceProject/src/test/resources
[INFO]
[INFO] — maven-compiler-plugin:3.1:testCompile (default-testCompile) @ HumanResourceServiceProject —
[INFO] Changes detected – recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 2 source files to /u02/oracle/developer/mywork/ServicesApplication/HumanResourceServiceProject/target/test-classes
[INFO]
[INFO] — maven-surefire-plugin:2.12.4:test (default-test) @ HumanResourceServiceProject —
[INFO] Surefire report directory: /u02/oracle/developer/mywork/ServicesApplication/HumanResourceServiceProject/target/surefire-reports

——————————————————-
T E S T S

Results :

Tests run: 6, Failures: 0, Errors: 0, Skipped: 0

[INFO]
[INFO] — maven-jar-plugin:2.4:jar (default-jar) @ HumanResourceServiceProject —
[INFO] Building jar: /u02/oracle/developer/mywork/ServicesApplication/HumanResourceServiceProject/target/HumanResourceServiceProject-1.0-SNAPSHOT.jar
[INFO] ————————————————————————
[INFO] BUILD SUCCESS
[INFO] ————————————————————————
[INFO] Total time: 8.963 s
[INFO] Finished at: 2018-02-09T12:36:23-08:00
[INFO] Final Memory: 17M/201M
[INFO] ————————————————————————
Process exited with exit code 0.

In the log above, you can see that after compiling code and running the tests, the following jar file is made:
/u02/oracle/developer/mywork/ServicesApplication/HumanResourceServiceProject/target/HumanResourceServiceProject-1.0-SNAPSHOT.jar

Summary

As a follow up from my previous article, in this article the focus is on calling the Oracle Apiary Mock Server from Java code.

One of the examples for the “HumanResourceService” API is about the action “Get all employees” in combination with “Java” as language. With that example, also the dependencies to be used in a Maven pom.xml (java6+) are given. This example triggered me to try out the Oracle JDeveloper, Maven and Java combination.

This article shows you how, with the help of Oracle JDeveloper 12.2.1 in combination with JUnit and Maven, a successful call to the Oracle Apiary MockServer action “Get all employees” from a Java class was made, and also to other actions for “Employees” and “Departments”.

The post Calling the Oracle Apiary Mock Server from Java code (via JUnit and Maven) appeared first on AMIS Oracle and Java Blog.

Deploying Oracle OEM agents 13c on windows targets (2008 R2) while OMS is on Linux

Tue, 2018-02-06 09:00

Situation at customer’s site: OMS 13.2 on Oracle Linux, targets are Windows machines, and a bit ancient: Windows 2008 R2. How to deploy agents on those targets? Several methods are possible in theory. In this blog I’ll describe my efforts to determine what is really possible and what is efficient.

When using Linux targets, there’s no question how to deploy agents, there’s a wunderfull mechanism to push agents to the targets. No pain (mostly). Even to targets in the Oracle Cloud.

Using the same mechanism for Windows, we need cywin to emulate a Linux connection. That’s my first attempt, but there are other possilities to explore:

1. The above named cygwin option. Install cygwin on every Windows target host. When that’s done (automated perhaps), it should be easy to push agents.

2. Using a Windows staging server to push agents to a Windows target, from a Windows machine. Should be easier, and less labour at the target-site.

3. A silent install at every Windows target of the agent.

 

1. Cygwin option.

There is quite a bit documentation to be found and blogposts about this subject, mentioned in the resources below this post. Most striking details of the configurartion:

– you need to edit the file \oui\prov\resources\ssPaths_msplats.properties on the OMS server. Change properties like SCP_PATH, SH_PATH, CHMOD_PATH, LS_PATH, SSH_PATH, MKDIR_PATH. Small but minor detail, not mentioned in the documentation: use forward slashes instead of backslash !! D:/ instead of D:\.

– put the user of the target and password in the password file on the target:

$ /bin/mkpasswd -l -u oracle >> /etc/passwd

When Cygwin has been installed, push the agents through Setup —> add target –> manually.

image

And installling:

image

And in my case it fails unfortunately:

Execution of command d:/oracle/agent13c/ADATMP_2018-01-19_15-17-49-PM/agentDeploy.bat -ignorePrereqs ORACLE_HOSTNAME=<hostname> AGENT_BASE_DIR=d:/oracle/agent13c OMS_HOST=<hostname> EM_UPLOAD_PORT=4903 AGENT_INSTANCE_HOME=d:/oracle/agent13c/agent_inst b_doDiscovery=false START_AGENT=false b_forceInstCheck=true -force AGENT_PORT=3872 on host <hostname> Failed

At this time I did not know what went wrong, thought I ran into bug 23499235 : 13c Cloud Control Agent not Deploying on Microsoft Windows x64 2008 R2.  To investigate what went wrong, it’s best to to run the command manual at the target. But that’s what I’m basically doing in the third option, so I stopped this action of pushing the agents for the moment.

2. Using a Windows staging server

This should be the most promising option, a centralised staging server, pushing the agents to the targets. Oracle has documented this by an example:

Example Deployment of an Enterprise Manager 12.1.0.5/13c Cloud Control Agent on an MS Windows Host with the PsExec Method from an MS Windows Staging Server when the OMS is on Unix (Doc ID 2304834.1)

But there are several exellent blogs about this configuration, like here.

In general:

Choose and configure a Windows server to act as a ‘pushing’ agents server. Copy the agent software, install psexec tools, test connection to the target host, create a property file and push the agent.

  • How to obtain the agent has been excellent described by dbakevlar in this blog.
  • How to install psexec tools and test the connection has been described here.
  • The property file I used:

HOST_NAMES=<hostname>
USER_NAME=<windows domain>\oracle
PASSWORD=<password>
AGENT_IMAGE_PATH=D:\tools\staging\13.2.0.0.0_AgentCore_233.zip
AGENT_BASE_DIR=D:\oracle\agent13c
OMS_HOST=<Linux-OMS>
EM_UPLOAD_PORT=4903
AGENT_REGISTRATION_PASSWORD=<agent_passw>
PSEXEC_DIR=D:\tools\pstools
REMOTE_SYS_DIR=C:\Windows

And off we go, pushing the agent to a Windows target:

agentDeployPSExec.bat PROPERTIES_FILE=d:\tools\staging\<property_file>

But then, the following error:

Error copying D:\tools\staging\\unzip_tmp.exe to remote system:

D:\tools\pstools\psexec.exe \\<targethostname> -u <windows-domainname>\oracletab -p ****** cmd.exe /c move C:\Windows\unzip_tmp.exe D:\oracle\agent13c\ADATMP_–_–

PsExec v2.2 – Execute processes remotely

Copyright (C) 2001-2016 Mark Russinovich

Sysinternals – www.sysinternals.com

Access is denied.

And.. there’s a note for this:

EM 13c: PsExec.exe Access Is Denied When Attempting To Move C:\Windows\unzip_tmp.exe On Target Host (Doc ID 2201143.1)

Solution: Turn off UAC at the target Windows server. This is not an option for the organization , so I’ll skip this alternative….

3. A silent install at every Windows target of the agent

At first, this seemed not to be the fastest way, but after the experiences of the other options, it could be the best option.

There’s a good description how to do this  at DBAkevlar’s site, won’t repeat this in this blog.

After some struggle with missing dll’s (Windows 2008 R2), read a lot of notes about this (see below this post), got the job done.

The trick for me: add the following dll’s to a directory which is in the PATH variable:

  • perl58.dll
  • msvcp71.dll
  • msvcr71.dll
  • msvr100.dll

Conclusion: first do a silent install to ensure the installation works. After that you may decide to choose one of the other options. I decided to go for the silent install by the way.

 

Resources:

Install cygwin on Windows servers: https://docs.oracle.com/cd/E73210_01/EMBSC/GUID-B8ED3864-2CCC-4508-9E98-73E79E0E852D.htm#EMBSC152

Download cygwin: https://cygwin.com/install.html

Cygwin installation blogpost: http://www.carajandb.com/en/blogs/blog-swinkler-en/190-oem-12c-agent-deploy-on-windows-no-problem-with-cygwin

Another cygwin installation blogpost: http://www.carajandb.com/en/blogs/blog-swinkler-en/190-oem-12c-agent-deploy-on-windows-no-problem-with-cygwin

Doc ID 2304834.1: Example Deployment of an Enterprise Manager 12.1.0.5/13c Cloud Control Agent on an MS Windows Host with the PsExec Method from an MS Windows Staging Server when the OMS is on Unix

Toadword about staging server:

https://www.toadworld.com/platforms/oracle/b/weblog/archive/2014/07/04/enterprise-manager-agent-deployment-gets-better-on-windows

Download psexec tool: http://technet.microsoft.com/en-us/sysinternals/bb897553.aspx

dbakevlar, deploying standalone agents: http://dbakevlar.com/2013/10/em12c-agent-deployment-on-windows/

Access denied with psexec: EM 13c: PsExec.exe Access Is Denied When Attempting To Move C:\Windows\unzip_tmp.exe On Target Host (Doc ID 2201143.1)

The post Deploying Oracle OEM agents 13c on windows targets (2008 R2) while OMS is on Linux appeared first on AMIS Oracle and Java Blog.

Relocating base directory Enterprise Manager’s central agent

Thu, 2018-02-01 09:00

After the upgrade of the 12c central agent to 13c, I noticed the new central agent’s home located under the wrong – old agent – directory: /u01/app/oracle/agent12c/agent_13.2.0.0.0. Must have done something wrong. How to correct this? Want to move the directory to /u01/app/oracle/agent13c/. A very short blog to describe the two steps.

It’s a bit hidden away in the documentation (in the appendixes) but there’s a special procedure to do this, and it worked like a charm for me.

So, /u01/app/oracle/agent12c/agent_13.2.0.0.0  should be /u01/app/oracle/agent13c/agent_13.2.0.0.0

Step 1:  Create a list of plugins by using this command:

/u01/app/oracle/agent12c/agent_13.2.0.0.0/perl/bin/perl      /u01/app/oracle/agent12c/agent_13.2.0.0.0/sysman/install/create_plugin_list.pl -instancehome /u01/app/oracle/agent12c/agent_inst/

A file /u01/app/oracle/agent12c/plugins.txt is created, used by the next command.

 

Step 2: Creating a new base directory for agent13c – where I didn’t need to stop the agent by the way, that’s all included in the script:

/u01/app/oracle/agent12c/agent_13.2.0.0.0/perl/bin/perl /u01/app/oracle/agent12c/agent_13.2.0.0.0/sysman/install/AgentMigrate.pl -instanceHome /u01/app/oracle/agent12c/agent_inst -newAgentBaseDir /u01/app/oracle/agent13c

Post actions to be done:

  • Run root.sh from /u01/app/oracle/agent13c/agent_13.2.0.0.0/root.sh manually
  • Deinstall (rm -R) agent home /u01/app/oracle/agent12c/agent_13.2.0.0.0 manually

And that’s it! Sometimes life is easy.

Regardz..

 

Resources:

Moving the central agent base directory : https://docs.oracle.com/cd/E73210_01/EMUPG/GUID-C807B2F8-DA3A-4934-8C2E-7DFB63E9F65D.htm#EMUPG377

The post Relocating base directory Enterprise Manager’s central agent appeared first on AMIS Oracle and Java Blog.

Oracle API Platform Cloud Service: Design-First approach and using Oracle Apiary

Wed, 2018-01-31 01:10

At the Oracle Partner PaaS Summer Camps VII 2017 in Lisbon last year, at the end of august, I attended the API Cloud Platform Service & Integration Cloud Service bootcamp.

In a series of article’s I will give a high level overview of what you can do with Oracle API Platform Cloud Service. The version used of Oracle API Platform CS was Release 17.3.3 — August 2017.

See https://docs.oracle.com/en/cloud/paas/api-platform-cloud/whats-new/index.html to learn about the new and changed features of Oracle API Platform CS in the latest release.

In preparation of creating an API Blueprint document, I took a closer look at Oracle REST Data Services and used the RESTful Services feature in Oracle SQL Developer, to generate example JSON payload’s based on some tables in the “HR’ schema. For more information about this, see my article “Oracle REST Data Services (ORDS)”.
[https://technology.amis.nl/2018/01/22/oracle-rest-data-services-ords/]

In this first article in the series about Oracle API Platform CS, the focus will be on the Design-First approach and using Oracle Apiary.

Short overview of Oracle API Platform Cloud Service

Oracle API Platform Cloud Service enables companies to thrive in the digital economy by comprehensively managing the full API lifecycle from design and standardization to documenting, publishing, testing and managing APIs. These tools provide API developers, managers, and users an end-to-end platform for designing, prototyping. Through the platform, users gain the agility needed to support changing business demands and opportunities, while having clear visibility into who is using APIs for better control, security and monetization of digital assets.
[https://cloud.oracle.com/en_US/api-platform/datasheets]

Architecture

Management Portal:
APIs are managed, secured, and published using the Management Portal. The Management Portal is hosted on the Oracle Cloud, managed by Oracle, and users granted API Manager privileges have access.

Gateways:
API Gateways are the runtime components that enforce all policies, but also help in collecting data for analytics. The gateways can be deployed anywhere – on premise, on Oracle Cloud or to any third party cloud providers.

Developer Portal:
After an API is published, Application Developers use the Developer Portal to discover, register, and consume APIs. The Developer Portal can be customized to run either on the Oracle Cloud or directly in the customer environment on premises.
[https://cloud.oracle.com/opc/paas/datasheets/APIPCSDataSheet_Oct2017.pdf]

More about this follows in another article in the series.

Design-First approach

API-First Development is a fundamental paradigm shift in the process of API design where APIs are built before applications and mirror the goals and objectives of the company. API-First Development is also commonly referred to as Design-First Development.

Before a developer builds a web, mobile, or other application, they develop the API first, then start defining the channels that the API will be available on. Developers kick off the development process discussing the API with their potential customers, generate use cases, and mock up the API before even developing the application.
[https://cloud.oracle.com/opc/paas/datasheets/Apiary-Datasheet-Oracle.pdf]

API-First Development” experience is one of the Key Design Principles of Oracle API Platform Cloud Service. The Developer Portal, tightly linked with Apiary, allows application developers to search for, learn about, test and register to use APIs, and then track their own usage.
[https://cloud.oracle.com/opc/paas/datasheets/APIPCSDataSheet_Oct2017.pdf]

Oracle Apiary provides you with the ability to design APIs using either API Blueprint or Swagger 2.0. From these description files, Oracle Apiary generates interactive documentation and a console for making calls to the APIs from the UI.
[https://docs.oracle.com/en/cloud/paas/api-platform-cloud/apfad/oracle-apiary-integration.html]

API Blueprint

API Blueprint is a documentation-oriented web API description language. The API Blueprint is essentially a set of semantic assumptions laid on top of the Markdown syntax used to describe a web API.

An API Blueprint document – a blueprint – is a plain text Markdown document describing a Web API in whole or in part. The document is structured into logical sections. Each section has its distinctive meaning, content and position in the document.
[https://github.com/apiaryio/api-blueprint/blob/master/API%20Blueprint%20Specification.md]

For the Full Language Specification of API Blueprint see for example:
https://github.com/apiaryio/api-blueprint/blob/master/API%20Blueprint%20Specification.md

For an API Blueprint tutorial see for example:
https://docs.oracle.com/cloud/apiary/api_101/api_blueprint_tutorial/index.html

The recommended file extension for API Blueprint is .apib

Oracle Apiary

Apiary provides the world’s first platform, API Flow, specifically designed to help companies accelerate and control the design, development, and documentation of their APIs and microservices. This allows its users to create products and services that customers, business partners, and even machines love to use.

Apiary API Flow is an open platform supporting both API Blueprint and OpenAPI (Swagger). The platform provides tools to developers for every step of an API-First Development Lifecycle and delivers several significant benefits to a team of developers.
[https://cloud.oracle.com/opc/paas/datasheets/Apiary-Datasheet-Oracle.pdf]

In this article I will discuss some of the functionality of Oracle Apiary, but not all. Please see the available documentation for more details.

Go to https://apiary.io/ to start Oracle Apiary.

You can Sign Up for free with a GitHub, Twitter or email account. Once you have done that, you can Sign In to start working with Oracle Apiary.

Via menu | Create New API Project, the New API wizard is started, where you can choose between creating a Personal API or Team API and in the field “New API name” you can fill in your API name, for example: HumanResourceService.

Then click on button “Create API”.

Apiary Editor

The “HumanResourceService” API automatically shows up in the Apiary Editor. The header item “Editor” is high-lighted.

The Apiary Editor, contains 3 panes, with an API Blueprint tutorial (a polls service, which allows consumers to view polls and vote in them) already in place. For more information about that tutorial, see: https://help.apiary.io/api_101/api_blueprint_tutorial/

The Apiary Editor is the foundation of your API design. Apiary Editor supports API Blueprint and Swagger API Description languages.
[https://help.apiary.io/tools/apiary-editor/]

In the left pane (Editor), an API Blueprint document, structured into logical sections, is shown.
The center pane (Documentation preview) shows what your API document will look like when rendered as documentation. It also lets you to try out your API as you build. The documentation preview is dynamically updated as you type in the Editor.
In the right pane the code examples/console is shown.

You can modify the API Blueprint tutorial document to fulfill your needs, or use some other text editor and copy and paste it into the Editor.

As described in my previous article, mentioned above, in the table below I summarized the requests that I created:

Request name Method Request URL GetAllEmployeesRequest GET http://localhost:9090/ords/hr/demo1/employees/ CreateEmployeeRequest POST http://localhost:9090/ords/hr/demo1/employees/ GetEmployeeRequest GET http://localhost:9090/ords/hr/demo1/employees/100 UpdateEmployeeRequest PUT http://localhost:9090/ords/hr/demo1/employees/219 GetDepartmentRequest GET http://localhost:9090/ords/hr/demo1/departments/30 GetDepartmentEmployeeRequest GET http://localhost:9090/ords/hr/demo1/departments/30/employees/119

The first step for creating an API Blueprint document is to specify the Metadata, API name and description.

Metadata section:
The API Blueprint document starts with a Metadata section, which consists of Key-Value pairs. Each Key is separated from its Value by a colon (:). One pair per line. The FORMAT keyword is required and denotes that document is API Blueprint.

FORMAT: 1A

API name & overview section:
In the API name & overview section, the API name is defined by the first Markdown header in the API Blueprint document.

# HumanResourceService

Human Resource Service is an API to manage Human Resources.

Resource section:
An API consists of resources specified by their URIs.

In line with the API Blueprint tutorial (a polls service, which allows consumers to view polls and vote in them), I used a Resource section with as format:
## <identifier> [<URI template>]

For example:
## Employees Collection [/employees]

Action section:
You should specify each action you may make on a resource. An action is specified with a sub-heading with the name of the action followed by the HTTP method.

Within the Resource section I created multiple Action sections, with as format:
### <identifier> [<HTTP request method> <URI template>]

For example:
### Get all employees [GET /employees]

As you can see, in my API, some Action sections contain an URI parameters section and/or a Request section and a Response section.

URI parameters section:
The URI parameters section describes any URI parameters specific to the action, with as format:
+ <parameter name>: `<example value>` (<type> | enum[<type>], required | optional) – <description>

For example:
+ id: `220` (number, required) – Id of an employee.

Request section:
For the Request sections the following format was used:
+ Request <identifier> (<Media Type>)

For example:
+ Request (application/json)

        {“LAST_NAME”:”TESTUPDATE”, “JOB_ID”:”SA_REP”, “SALARY”:8000, “DEPARTMENT_ID”:80}

Remark:
Specifying the media type generates a HTTP Content-Type header.

Response section:
For the Response sections the following format was used:
+ Response <HTTP status code> (<Media Type>)

For example:
+ Response 200 (application/json)

{
“employee_id”: 220,
“first_name”: “TESTFIRST”,
“last_name”: “TESTUPDATE”,
“email”: “TESTMAIL”,
“phone_number”: null,
“hire_date”: “2015-06-25T04:00:00Z”,
“job_id”: “SA_REP”,
“salary”: 8000,
“commission_pct”: null,
“manager_id”: 103,
“department_id”: 80
}

Remark:
Specifying the media type generates a HTTP Content-Type header.

My HumanResourceService.apib therefor looks like:

FORMAT: 1A

# HumanResourceService

Human Resource Service is an API to manage Human Resources.

## Employees Collection [/employees]

### Get all employees [GET /employees]

Get all employees.

+ Response 200 (application/json)

        {
            "items": [
                {
                    "employee_id": 100,
                    "first_name": "Steven",
                    "last_name": "King",
                    "email": "SKING",
                    "phone_number": "515.123.4567",
                    "hire_date": "1987-06-17T04:00:00Z",
                    "job_id": "AD_PRES",
                    "salary": 24000,
                    "commission_pct": null,
                    "manager_id": null,
                    "department_id": 90,
                    "links": [
                        {
                            "rel": "self",
                            "href": "http://localhost:9090/ords/hr/employees/100"
                        }
                    ]
                },
                {
                    "employee_id": 101,
                    "first_name": "Neena",
                    "last_name": "Kochhar",
                    "email": "NKOCHHAR",
                    "phone_number": "515.123.4568",
                    "hire_date": "1989-09-21T04:00:00Z",
                    "job_id": "AD_VP",
                    "salary": 17000,
                    "commission_pct": null,
                    "manager_id": 100,
                    "department_id": 90,
                    "links": [
                        {
                            "rel": "self",
                            "href": "http://localhost:9090/ords/hr/employees/101"
                        }
                    ]
                },
                {
                    "employee_id": 102,
                    "first_name": "Lex",
                    "last_name": "De Haan",
                    "email": "LDEHAAN",
                    "phone_number": "515.123.4569",
                    "hire_date": "1993-01-13T05:00:00Z",
                    "job_id": "AD_VP",
                    "salary": 17000,
                    "commission_pct": null,
                    "manager_id": 100,
                    "department_id": 90,
                    "links": [
                        {
                            "rel": "self",
                            "href": "http://localhost:9090/ords/hr/employees/102"
                        }
                    ]
                },
                {
                    "employee_id": 103,
                    "first_name": "Alexander",
                    "last_name": "Hunold",
                    "email": "AHUNOLD",
                    "phone_number": "590.423.4567",
                    "hire_date": "1990-01-03T05:00:00Z",
                    "job_id": "IT_PROG",
                    "salary": 9000,
                    "commission_pct": null,
                    "manager_id": 102,
                    "department_id": 60,
                    "links": [
                        {
                            "rel": "self",
                            "href": "http://localhost:9090/ords/hr/employees/103"
                        }
                    ]
                },
                {
                    "employee_id": 104,
                    "first_name": "Bruce",
                    "last_name": "Ernst",
                    "email": "BERNST",
                    "phone_number": "590.423.4568",
                    "hire_date": "1991-05-21T04:00:00Z",
                    "job_id": "IT_PROG",
                    "salary": 6000,
                    "commission_pct": null,
                    "manager_id": 103,
                    "department_id": 60,
                    "links": [
                        {
                            "rel": "self",
                            "href": "http://localhost:9090/ords/hr/employees/104"
                        }
                    ]
                },
                {
                    "employee_id": 105,
                    "first_name": "David",
                    "last_name": "Austin",
                    "email": "DAUSTIN",
                    "phone_number": "590.423.4569",
                    "hire_date": "1997-06-25T04:00:00Z",
                    "job_id": "IT_PROG",
                    "salary": 4800,
                    "commission_pct": null,
                    "manager_id": 103,
                    "department_id": 60,
                    "links": [
                        {
                            "rel": "self",
                            "href": "http://localhost:9090/ords/hr/employees/105"
                        }
                    ]
                },
                {
                    "employee_id": 106,
                    "first_name": "Valli",
                    "last_name": "Pataballa",
                    "email": "VPATABAL",
                    "phone_number": "590.423.4560",
                    "hire_date": "1998-02-05T05:00:00Z",
                    "job_id": "IT_PROG",
                    "salary": 4800,
                    "commission_pct": null,
                    "manager_id": 103,
                    "department_id": 60,
                    "links": [
                        {
                            "rel": "self",
                            "href": "http://localhost:9090/ords/hr/employees/106"
                        }
                    ]
                },
                {
                    "employee_id": 107,
                    "first_name": "Diana",
                    "last_name": "Lorentz",
                    "email": "DLORENTZ",
                    "phone_number": "590.423.5567",
                    "hire_date": "1999-02-07T05:00:00Z",
                    "job_id": "IT_PROG",
                    "salary": 4200,
                    "commission_pct": null,
                    "manager_id": 103,
                    "department_id": 60,
                    "links": [
                        {
                            "rel": "self",
                            "href": "http://localhost:9090/ords/hr/employees/107"
                        }
                    ]
                },
                {
                    "employee_id": 108,
                    "first_name": "Nancy",
                    "last_name": "Greenberg",
                    "email": "NGREENBE",
                    "phone_number": "515.124.4569",
                    "hire_date": "1994-08-17T04:00:00Z",
                    "job_id": "FI_MGR",
                    "salary": 12000,
                    "commission_pct": null,
                    "manager_id": 101,
                    "department_id": 100,
                    "links": [
                        {
                            "rel": "self",
                            "href": "http://localhost:9090/ords/hr/employees/108"
                        }
                    ]
                },
                {
                    "employee_id": 109,
                    "first_name": "Daniel",
                    "last_name": "Faviet",
                    "email": "DFAVIET",
                    "phone_number": "515.124.4169",
                    "hire_date": "1994-08-16T04:00:00Z",
                    "job_id": "FI_ACCOUNT",
                    "salary": 9000,
                    "commission_pct": null,
                    "manager_id": 108,
                    "department_id": 100,
                    "links": [
                        {
                            "rel": "self",
                            "href": "http://localhost:9090/ords/hr/employees/109"
                        }
                    ]
                },
                {
                    "employee_id": 110,
                    "first_name": "John",
                    "last_name": "Chen",
                    "email": "JCHEN",
                    "phone_number": "515.124.4269",
                    "hire_date": "1997-09-28T04:00:00Z",
                    "job_id": "FI_ACCOUNT",
                    "salary": 8200,
                    "commission_pct": null,
                    "manager_id": 108,
                    "department_id": 100,
                    "links": [
                        {
                            "rel": "self",
                            "href": "http://localhost:9090/ords/hr/employees/110"
                        }
                    ]
                },
                {
                    "employee_id": 111,
                    "first_name": "Ismael",
                    "last_name": "Sciarra",
                    "email": "ISCIARRA",
                    "phone_number": "515.124.4369",
                    "hire_date": "1997-09-30T04:00:00Z",
                    "job_id": "FI_ACCOUNT",
                    "salary": 7700,
                    "commission_pct": null,
                    "manager_id": 108,
                    "department_id": 100,
                    "links": [
                        {
                            "rel": "self",
                            "href": "http://localhost:9090/ords/hr/employees/111"
                        }
                    ]
                },
                {
                    "employee_id": 112,
                    "first_name": "Jose Manuel",
                    "last_name": "Urman",
                    "email": "JMURMAN",
                    "phone_number": "515.124.4469",
                    "hire_date": "1998-03-07T05:00:00Z",
                    "job_id": "FI_ACCOUNT",
                    "salary": 7800,
                    "commission_pct": null,
                    "manager_id": 108,
                    "department_id": 100,
                    "links": [
                        {
                            "rel": "self",
                            "href": "http://localhost:9090/ords/hr/employees/112"
                        }
                    ]
                },
                {
                    "employee_id": 113,
                    "first_name": "Luis",
                    "last_name": "Popp",
                    "email": "LPOPP",
                    "phone_number": "515.124.4567",
                    "hire_date": "1999-12-07T05:00:00Z",
                    "job_id": "FI_ACCOUNT",
                    "salary": 6900,
                    "commission_pct": null,
                    "manager_id": 108,
                    "department_id": 100,
                    "links": [
                        {
                            "rel": "self",
                            "href": "http://localhost:9090/ords/hr/employees/113"
                        }
                    ]
                },
                {
                    "employee_id": 114,
                    "first_name": "Den",
                    "last_name": "Raphaely",
                    "email": "DRAPHEAL",
                    "phone_number": "515.127.4561",
                    "hire_date": "1994-12-07T05:00:00Z",
                    "job_id": "PU_MAN",
                    "salary": 11000,
                    "commission_pct": null,
                    "manager_id": 100,
                    "department_id": 30,
                    "links": [
                        {
                            "rel": "self",
                            "href": "http://localhost:9090/ords/hr/employees/114"
                        }
                    ]
                },
                {
                    "employee_id": 115,
                    "first_name": "Alexander",
                    "last_name": "Khoo",
                    "email": "AKHOO",
                    "phone_number": "515.127.4562",
                    "hire_date": "1995-05-18T04:00:00Z",
                    "job_id": "PU_CLERK",
                    "salary": 3100,
                    "commission_pct": null,
                    "manager_id": 114,
                    "department_id": 30,
                    "links": [
                        {
                            "rel": "self",
                            "href": "http://localhost:9090/ords/hr/employees/115"
                        }
                    ]
                },
                {
                    "employee_id": 116,
                    "first_name": "Shelli",
                    "last_name": "Baida",
                    "email": "SBAIDA",
                    "phone_number": "515.127.4563",
                    "hire_date": "1997-12-24T05:00:00Z",
                    "job_id": "PU_CLERK",
                    "salary": 2900,
                    "commission_pct": null,
                    "manager_id": 114,
                    "department_id": 30,
                    "links": [
                        {
                            "rel": "self",
                            "href": "http://localhost:9090/ords/hr/employees/116"
                        }
                    ]
                },
                {
                    "employee_id": 117,
                    "first_name": "Sigal",
                    "last_name": "Tobias",
                    "email": "STOBIAS",
                    "phone_number": "515.127.4564",
                    "hire_date": "1997-07-24T04:00:00Z",
                    "job_id": "PU_CLERK",
                    "salary": 2800,
                    "commission_pct": null,
                    "manager_id": 114,
                    "department_id": 30,
                    "links": [
                        {
                            "rel": "self",
                            "href": "http://localhost:9090/ords/hr/employees/117"
                        }
                    ]
                },
                {
                    "employee_id": 118,
                    "first_name": "Guy",
                    "last_name": "Himuro",
                    "email": "GHIMURO",
                    "phone_number": "515.127.4565",
                    "hire_date": "1998-11-15T05:00:00Z",
                    "job_id": "PU_CLERK",
                    "salary": 2600,
                    "commission_pct": null,
                    "manager_id": 114,
                    "department_id": 30,
                    "links": [
                        {
                            "rel": "self",
                            "href": "http://localhost:9090/ords/hr/employees/118"
                        }
                    ]
                },
                {
                    "employee_id": 119,
                    "first_name": "Karen",
                    "last_name": "Colmenares",
                    "email": "KCOLMENA",
                    "phone_number": "515.127.4566",
                    "hire_date": "1999-08-10T04:00:00Z",
                    "job_id": "PU_CLERK",
                    "salary": 2500,
                    "commission_pct": null,
                    "manager_id": 114,
                    "department_id": 30,
                    "links": [
                        {
                            "rel": "self",
                            "href": "http://localhost:9090/ords/hr/employees/119"
                        }
                    ]
                },
                {
                    "employee_id": 120,
                    "first_name": "Matthew",
                    "last_name": "Weiss",
                    "email": "MWEISS",
                    "phone_number": "650.123.1234",
                    "hire_date": "1996-07-18T04:00:00Z",
                    "job_id": "ST_MAN",
                    "salary": 8000,
                    "commission_pct": null,
                    "manager_id": 100,
                    "department_id": 50,
                    "links": [
                        {
                            "rel": "self",
                            "href": "http://localhost:9090/ords/hr/employees/120"
                        }
                    ]
                },
                {
                    "employee_id": 121,
                    "first_name": "Adam",
                    "last_name": "Fripp",
                    "email": "AFRIPP",
                    "phone_number": "650.123.2234",
                    "hire_date": "1997-04-10T04:00:00Z",
                    "job_id": "ST_MAN",
                    "salary": 8200,
                    "commission_pct": null,
                    "manager_id": 100,
                    "department_id": 50,
                    "links": [
                        {
                            "rel": "self",
                            "href": "http://localhost:9090/ords/hr/employees/121"
                        }
                    ]
                },
                {
                    "employee_id": 122,
                    "first_name": "Payam",
                    "last_name": "Kaufling",
                    "email": "PKAUFLIN",
                    "phone_number": "650.123.3234",
                    "hire_date": "1995-05-01T04:00:00Z",
                    "job_id": "ST_MAN",
                    "salary": 7900,
                    "commission_pct": null,
                    "manager_id": 100,
                    "department_id": 50,
                    "links": [
                        {
                            "rel": "self",
                            "href": "http://localhost:9090/ords/hr/employees/122"
                        }
                    ]
                },
                {
                    "employee_id": 123,
                    "first_name": "Shanta",
                    "last_name": "Vollman",
                    "email": "SVOLLMAN",
                    "phone_number": "650.123.4234",
                    "hire_date": "1997-10-10T04:00:00Z",
                    "job_id": "ST_MAN",
                    "salary": 6500,
                    "commission_pct": null,
                    "manager_id": 100,
                    "department_id": 50,
                    "links": [
                        {
                            "rel": "self",
                            "href": "http://localhost:9090/ords/hr/employees/123"
                        }
                    ]
                },
                {
                    "employee_id": 124,
                    "first_name": "Kevin",
                    "last_name": "Mourgos",
                    "email": "KMOURGOS",
                    "phone_number": "650.123.5234",
                    "hire_date": "1999-11-16T05:00:00Z",
                    "job_id": "ST_MAN",
                    "salary": 5800,
                    "commission_pct": null,
                    "manager_id": 100,
                    "department_id": 50,
                    "links": [
                        {
                            "rel": "self",
                            "href": "http://localhost:9090/ords/hr/employees/124"
                        }
                    ]
                }
            ],
            "hasMore": true,
            "limit": 25,
            "offset": 0,
            "count": 25,
            "links": [
                {
                    "rel": "self",
                    "href": "http://localhost:9090/ords/hr/employees/"
                },
                {
                    "rel": "edit",
                    "href": "http://localhost:9090/ords/hr/employees/"
                },
                {
                    "rel": "describedby",
                    "href": "http://localhost:9090/ords/hr/metadata-catalog/employees/"
                },
                {
                    "rel": "first",
                    "href": "http://localhost:9090/ords/hr/employees/"
                },
                {
                    "rel": "next",
                    "href": "http://localhost:9090/ords/hr/employees/?offset=25"
                }
            ]
        }

### Get an employee [GET /employees/{id}]

Get a particular employee by providing an identifier.

+ Response 200 (application/json)

        {
            "employee_id": 100,
            "first_name": "Steven",
            "last_name": "King",
            "email": "SKING",
            "phone_number": "515.123.4567",
            "hire_date": "1987-06-17T04:00:00Z",
            "job_id": "AD_PRES",
            "salary": 24000,
            "commission_pct": null,
            "manager_id": null,
            "department_id": 90,
            "links": [
                {
                    "rel": "self",
                    "href": "http://localhost:9090/ords/hr/employees/100"
                },
                {
                    "rel": "edit",
                    "href": "http://localhost:9090/ords/hr/employees/100"
                },
                {
                    "rel": "describedby",
                    "href": "http://localhost:9090/ords/hr/metadata-catalog/employees/item"
                },
                {
                    "rel": "collection",
                    "href": "http://localhost:9090/ords/hr/employees/"
                }
            ]
        }

### Create an employee [POST /employees]

Create an employee, by using post with the complete payload

+ Request (application/json)

        {"LAST_NAME":"TESTINSERT", "FIRST_NAME":"TESTFIRST", "EMAIL":"TESTMAIL", "HIRE_DATE":"25-JUN-15", "JOB_ID":"IT_PROG", "SALARY":6000, "MANAGER_ID":103, "DEPARTMENT_ID":60}

+ Response 201 (application/json)

        {
            "employee_id": 220,
            "first_name": "TESTFIRST",
            "last_name": "TESTINSERT",
            "email": "TESTMAIL",
            "phone_number": null,
            "hire_date": "2015-06-25T04:00:00Z",
            "job_id": "IT_PROG",
            "salary": 6000,
            "commission_pct": null,
            "manager_id": 103,
            "department_id": 60
        }

### Update an employee [PUT /employees/{id}]

Update an employee, by using put with the a payload containing: last_name, job_id, salary and department_id.

+ Parameters
    + id: `220` (number, required) - Id of an employee.

+ Request (application/json)

        {"LAST_NAME":"TESTUPDATE", "JOB_ID":"SA_REP", "SALARY":8000, "DEPARTMENT_ID":80}

+ Response 200 (application/json)

        {
            "employee_id": 220,
            "first_name": "TESTFIRST",
            "last_name": "TESTUPDATE",
            "email": "TESTMAIL",
            "phone_number": null,
            "hire_date": "2015-06-25T04:00:00Z",
            "job_id": "SA_REP",
            "salary": 8000,
            "commission_pct": null,
            "manager_id": 103,
            "department_id": 80
        }

## Departments Collection [/departments]

### Get a department [GET /department/{id}]

Get a particular department by providing an identifier.

+ Parameters
    + id: `30` (number, required) - Id of a department.

+ Response 200 (application/json)

        {
            "department_id": 30,
            "department_name": "Purchasing",
            "manager_id": 114,
            "location_id": 1700
        }

### Get a department and employee [GET /departments/{department_id}/employees/{employee_id}]

Get a particular department by providing a department identifier and a particular employee within that department by providing an employee identifier.

+ Parameters
    + department_id: `30` (number, required) - Id of a department.
    + employee_id: `119` (number, required) - Id of an employee.

+ Response 200 (application/json)

        {
            "department_id": 30,
            "department_name": "Purchasing",
            "employee_id": 119,
            "first_name": "Karen",
            "last_name": "Colmenares"
        }
Apiary Editor, instant feedback

The Apiary Editor gives you instant feedback on any warnings or errors in your document as you type. The feedback will include line numbers and explanations for the warnings and errors, which will take you to the corresponding line in the editor when clicked.
[https://help.apiary.io/tools/apiary-editor/]

For example:

The semantic issues shown above where solved by, changing the URI template to:

### Get a department and employee [GET /departments/{department_id}/employees/{employee_id}]

After changing the content of the API Blueprint tutorial (a polls service, which allows consumers to view polls and vote in them) in order to describe the functionality of the HumanResourceService, the Apiary Editor looks like:

Mock Server

The Mock Server allows you to try out your API as you design it, giving immediate feedback along the way in how it may be used.

The Mock Server accomplishes this by listening for requests as you’ve defined them in your blueprint. When a request is received to your Mock Server for a URL you’ve defined, the corresponding response for that request will be returned.
[https://help.apiary.io/tools/mock-server/]

The Mock Server for the “HumanResourceService” API is listening at:
http://private-b4874b1-humanresourceservice.apiary-mock.com

You will have your own private URL for the Mock Server. This is to ensure that other users do not see the traffic you’re sending to the server.
[https://help.apiary.io/tools/mock-server/]

Interacting with the Mock Server can be done:

  • Directly
  • By using code examples
  • By using the console

Interacting with the Mock Server directly
This URL may be used to interact directly with the server. You can make requests with applications like curl or Paw to that URL and will get responses defined in the API Description.
[https://help.apiary.io/tools/mock-server/]

Interacting with the Mock Server by using code examples
Apiary provides code examples that you may use to interact with the Mock Server. You can get to these examples by clicking on any action in the documentation.
[https://help.apiary.io/tools/mock-server/]

Interacting with the Mock Server by using the console
The console in the documentation is where you can send requests to the Mock Server directly from the documentation (along with the Debugging Proxy and your production server). You can get to this by clicking on an action in the documentation, then clicking “Switch to Console” in the machine column.
[https://help.apiary.io/tools/mock-server/]

Interactive Documentation

When you don’t need to see the left pane (Editor) anymore, you can switch in the header to “Documentation”.

The interactive documentation contains two main columns: the human and machine columns. These two columns provide the separation that is important for reading the documentation and actually using tooling to interact with it.
[https://help.apiary.io/tools/interactive-documentation/]

After clicking on an action (for example: “Get all employees”) in the documentation (human column) the right pane (machine column) shows code examples.

Default the “Raw” type is selected, but there is a drop down that has a list of the available languages for the code examples.

When choosing for example “Java” as language for the code example, the following is shown:

// Maven : Add these dependecies to your pom.xml (java6+)
// <dependency>
//     <groupId>org.glassfish.jersey.core</groupId>
//     <artifactId>jersey-client</artifactId>
//     <version>2.8</version>
// </dependency>
// <dependency>
//     <groupId>org.glassfish.jersey.media</groupId>
//     <artifactId>jersey-media-json-jackson</artifactId>
//     <version>2.8</version>
// </dependency>

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType;

Client client = ClientBuilder.newClient();
Response response = client.target("https://private-b4874b1-humanresourceservice.apiary-mock.com/employees")
  .request(MediaType.TEXT_PLAIN_TYPE)
  .get();

System.out.println("status: " + response.getStatus());
System.out.println("headers: " + response.getHeaders());
System.out.println("body:" + response.readEntity(String.class));

This “Java” code example can be used to interact with the Mock Server.

When choosing for example “Python” as language for the code example, the following is shown:

from urllib2 import Request, urlopen

request = Request('https://private-b4874b1-humanresourceservice.apiary-mock.com/employees')

response_body = urlopen(request).read()
print response_body

This “Python” code example can be used to interact with the Mock Server.

After clicking on an action (for example: “Get all employees”) in the documentation (human column) and then in the right pane (machine column) clicking on button “Switch to Console” (or on the button “Try” in the code example), the Console is shown.

For the action “Get all employees”, in line with the specifications, the URI Parameters, Headers and Body parts are empty.

When you click on the button “Call Resource”, the response is shown:

After clicking on the action “Create an employee” in the documentation (human column) and then in the right pane (machine column) clicking on button “Switch to Console”, the following is shown.

For the action “Create an employee”, in line with the specifications, the Headers part is filled.

For the action “Create an employee”, in line with the specifications, the Body part is filled.

After clicking on the action “Get a department and employee” in the documentation (human column) and then in the right pane (machine column) clicking on button “Switch to Console”, the following is shown.

For the action “Get a department and employee”, in line with the specifications, the URI Parameters part is filled.

API Inspector

Each request and response from the Mock Server is logged in the API Inspector, which can be found by clicking “Inspector” in the Apiary header. There you will see each request received, each response given, and any validation errors that were found.
[https://help.apiary.io/tools/api-inspector/]

For example:

For each request (by clicking on it) more details are available:

API Test

When you click on the header item “Test”, the following is shown.

For more information about testing your API , please see the available documentation.

Summary

In this first article in the series about Oracle API Platform Cloud Service, the focus is on the Design-First approach and using Oracle Apiary.

“API-First Development” experience (commonly referred to as Design-First Development) is one of the Key Design Principles of Oracle API Platform Cloud Service. Developers kick off the development process discussing the API with their potential customers, generate use cases, and mock up the API before even developing the application.

The Developer Portal of API Platform CS is tightly linked with Oracle Apiary.

Oracle Apiary provides you with the ability to design APIs using either API Blueprint or Swagger 2.0. From these description files, Oracle Apiary generates interactive documentation and a console for making calls to the APIs from the UI.

This article shows you how, with the help of Oracle Apiary, a “HumanResourceService” API is created, based on API Blueprint and example JSON payload’s (based on some tables in the “HR’ schema).

The post Oracle API Platform Cloud Service: Design-First approach and using Oracle Apiary appeared first on AMIS Oracle and Java Blog.

Upgrade OEM 12.1.0.5 OMS to OEM13.2

Sun, 2018-01-28 09:00

Despite the potential of Oracle Management Cloud, I’m a fan of Oracle Enterprise Manager, with its small imperfections. So when noticing a 12c Oracle Enterprise Manager on Linux, and targets on Windows, I took the challenge of modernizing the customer’s environment a bit. And the starting point , 12.1.0.5 for OMS and 12.1.0.2 database as repository wasn’t that bad at all. This post is purely about upgrading the OMS. Upgrading agents is a different story with Windows as target.

Preparation

I think upgrading an OMS is all about preparation, so I read the upgrade guide carefully, and still it’s almost inevitable to miss something (also in this case).

Roughly the plan for my situation (single OMS, 1 instance repository, Oracle Linux):

  1. Patching the Opatch to 12.1.0.1.7 or above.
  2. Setting the parameter optimizer_adaptive_features in the repository  to FALSE in advance.
  3. Patch the database with patch 160419 (= cumulative patch 22291127) including of running the datapatch utility
  4. Enable the ‘DELETE TARGET’ – audit setting
  5. Download the required plugin’s to a temporary directory (I did’nt do this by the way at my first attempt, had to start the upgrade again…)
  6. Copy the EMkey to the repository
  7. Backup config of OMS
  8. Upgrade
  9. Run root script
  10. Upgrade central agent
Execution 1. Patching Opatch.

Downloaded patch 12.2.0.1.11.

Plain and simple. Renamed the Opatch directory, unzip p6880880_121010_Linux-x86-64, and ready to go.

Check with ‘opatch version’ if it’s done.

2. Setting the parameter ‘optimizer_adaptive_features’.

alter system set optimizer_adaptive_features=false scope=both

3. Patch the database with patch 160419

However this patch is superseded, I stuck with the plan (very conservative):

image

Stop database – unfortunately also stop OMS of course.

Check at conflicts:

opatch prereq CheckConflictAgainstOHWithDetail -ph ./

And patch 22291127 with ‘opatch apply’

Post-check

opatch lsinventory –detail

Running the datapatch utility.

Start database and oms.

cd $ORACLE_HOME/OPatch

./datapatch –verbose

Unfortunately an error appears:

Datapatch fails with “catconInit: database is not open on the default instance” (Doc ID 2003488.1)

Solution: temporarily rename glogin.sql

Cd $ORACLE_HOME/sqplus/admin

Rename glogin.sql temp_glogin.sql

Start datapatch utility again

Cd $ORACLE_HOME/OPatch

./datapatch –verbose

Validating logfiles…

Patch 22291127 apply: SUCCESS

logfile: /u01/app/oracle/cfgtoollogs/sqlpatch/22291127/19694308/22291127_apply_OCCMREP_2018Jan16_11_26_58.log (no errors)

SQL Patching tool complete on Tue Jan 16 11:27:25 2018

Restore the glogin.sql

Cd $ORACLE_HOME/sqplus/admin

Rename temp_glogin.sql glogin.sql

Check the following log files in $ORACLE_BASE/cfgtoollogs/sqlpatch/22291127/<unique patch ID> for errors:

22291127_apply_<database SID>_<CDB name>_<timestamp>.log.

Didn’t need an RMAN upgrade, skipped this step.

4. Enable the ‘DELETE TARGET’ – audit setting

Cd /u01/app/oracle/oms12c/oms/bin

./emcli update_audit_settings -operations_to_enable=”DELETE_TARGET” -audit_switch=”ENABLE” -file_prefix=”aud”

5. Download the required plugin’s to a temporary directory

In my case, I forgot to downoad a compatible ODA plugin the first time.

image

Downloaded the 13.1.1.1.0_oracle.dba.odba_2000_0.opar from the plugin download pagina.

6. Copy the EMkey to the repository

/u01/app/oracle/oms12c/oms/bin/emctl config emkey -copy_to_repos [-sysman_pwd <sysman_pwd>]

Check this with

/u01/app/oracle/oms12c/oms/bin/emctl status emkey

The EMKey is configured properly, but is not secure.

Secure the EMKey by running “emctl config emkey -remove_from_repos”.

7. Backup OMS config

/u01/app/oracle/oms12c/oms/bin/emctl exportconfig oms -sysman_pwd Oracle123# -dir /home/oracle/job

8. Upgrade

Shutdown OMS and central agent

/u01/app/oracle/oms12c/oms/bin/emctl stop oms –all

/u01/app/oracle/agent12c/agent_inst/bin/emctl stop agent

Start upgrade with the parameter for the downloaded plugin

./em13200_linux64.bin “PLUGIN_LOCATION=/u01/orasoftware/OMS_13.2”

Mostly it’s next,next, hereby only the mentionable screens.

A warning about ports, ignored this.

image

The summary screen:

image

And.. Upgrade

During the upgrade I had an error, related to the ODA plugin:

EM 13c: Upgrading Enterprise Manager Cloud Control OMS from 12.1.0.5 To 13.2 Fails in Repository Configuration with ORA-00942 Error for Table EM_ODBA_ODASYS_TAGS_E (Doc ID 2340514.1)

Solution:

as sysman: alter table EM_ODBA_ODASYS_TAGS rename to EM_ODBA_ODASYS_TAGS_E

And retry the repository upgrade.

Finished:

image

9. Running the root script

~]# /u01/app/oracle/oms132/allroot.sh

Starting to execute allroot.sh ………

Starting to execute /u01/app/oracle/oms132/root.sh ……

/etc exist

/u01/app/oracle/oms132

Finished execution of /u01/app/oracle/oms132/root.sh ……

OMS done:

10. Upgrade central agent

From the Setup menu, select Manage Cloud Control, then select Upgrade Agents.

Add the required agent, and upgrade.

image

Don’t forget to cleanup the agent as a post-upgrade task.

image

And done….

Regardz.

Resources

Upgrade guide: https://docs.oracle.com/cd/E73210_01/EMUPG/toc.htm

Plugin download: http://www.oracle.com/technetwork/oem/enterprise-manager/downloads/oem-plugins-2882950.html

Upgrade fails, cause plugin ODA: Upgrading Enterprise Manager Cloud Control OMS from 12.1.0.5 To 13.2 Fails in Repository Configuration with ORA-00942 Error for Table EM_ODBA_ODASYS_TAGS_E (Doc ID 2340514.1)

Datapatch failure: Datapatch fails with “catconInit: database is not open on the default instance” (Doc ID 2003488.1)

The post Upgrade OEM 12.1.0.5 OMS to OEM13.2 appeared first on AMIS Oracle and Java Blog.

Automate calls to SOAP and REST webservices using simple Python scripts

Fri, 2018-01-26 05:10

Probably not many people will tell you running batches over webservices is a good idea. Sometimes though, it can be handy to have a script available to generate webservice calls based on a template message with variables and automate processing the response messages. In addition, if you have a large number of calls, executing the calls in parallel might save you a lot of time if your service platform can handle the concurrency.

Scripts such as this might help bridging part of the gap between the old fashioned batch oriented world and the service world. You can for example use it to call services based on a text-file export from an old system to create certain entities in a modern system which offers APIs. Scripts such as these should of course not be used to actually perform structured regular integration but are valuable as one-off solutions. The provided scripts of course come with no warranties or guarantees of any nature. Most likely you will need to make them suitable for your specific use-case.

Python setup

The scripts below require Python 3.6 with the requests module installed (pip install requests). The other used modules are present by default in your usual Python installations. I’ve used PyCharm by JetBrains as IDE. Without IDE, you can also install Python 3.6 from python.org here to just run the script.

Performing calls to SOAP services

SOAP services use the same endpoint for all requests. Based on the operation (the SOAPAction HTTP header), a different part of the service is executed. The message contents can differ. For example, you want to call a service for a list of different customers. A template message is ideally suited for such a case. The below Python 3.6 script will do just that. Generate messages based on a template and an input file and fire them at a service endpoint with a specified number of concurrent threads. After the response is received, it is parsed and a specific field from the response is saved in an output file. Errors are saved in a separate file.

You can view the script here. The script is slightly more than 50 lines and contains Python samples of (among other things):

  • How to execute SOAP calls (POST request with HTTP headers) with the requests module
  • How to work with a message template and variables with string.Template
  • How to concurrently execute calls with concurrent.futures
  • How to parse SOAP responses with xml.etree.ElementTree

The line Maarten in input.txt will give you Maarten : Hello Maarten in the outputok.txt file if the call succeeded. I’ve used a simple SOA Suite test service which you can also find in the mentioned directory.

Performing calls to REST services

When working with REST services, usually the URL contains variables. In this example I’m calling an online and publicly available API at the Dutch Chamber of Commerce to search for companies based on their file number (KvK number). When I receive the result, I’ll look if it is found and only has a single location. In other cases, I’ll consider it an error.

You can view the script here. It contains samples of (among other things) on how you can do URL manipulation and GET requests. The parsing of the response for this sample is extremely simple. I just check if the result document contains specific text strings. For a ‘real’ REST service you might want to do some more thorough JSON parsing. For this example, I’ve kept the code as simple/short as possible.

The post Automate calls to SOAP and REST webservices using simple Python scripts appeared first on AMIS Oracle and Java Blog.

Oracle, Azure Cloud and licensing with hyperthreading enabled.

Thu, 2018-01-25 02:11

Until recently, there was one rule when deploying Oracle software in the Azure cloud:

  • count one Azure CPU Core as equivalent to one Oracle Processor license.

But there was somehow what confusion about hyperthreading VM’s. This has been nuanced by Oracle in their cloud-licensing document very recently

The document now states the following about licensing Oracle in Azure:

Microsoft Azure – count two vCPUs as equivalent to one Oracle Processor license if hyperthreading is enabled, and one vCPU as equivalent to one Oracle Processor license if hyperthreading is not enabled.

But.. when you are designing / choosing your VM’s in Azure, how can you tell which VM is using Hyperthreading? Well, that’s a simple rule: all VM’s which are older than the V3 are NOT using hyperthreading. See also this blog of Microsoft.

Other Azure changes, related to this change:

  • When using Standard Edition (2) :

Four or fewer (!) Azure vCPUs, are counted as 1 socket, which is considered equivalent to an Oracle processor license. –> (instead of two).

Oracle Database Standard Edition may only be licensed up to 16 Azure vCPUs

Oracle Standard Edition One and Standard Edition 2 may only be licensed up to eight Azure vCPUs.

Database Standard Edition 2 on NUPS:  the minimums are 10 NUP licenses per 8 Azure vCPUs.

  • Oracle Linux:

For Oracle Linux purposes, each Authorized Cloud Environment instance is counted as a “System”. “Basic Limited” and “Premier Limited” support is not available for Authorized Cloud Environment instances greater than  eight Azure vCPUs.

Resources:

http://www.oracle.com/us/corporate/pricing/cloud-licensing-070579.pdf

https://azure.microsoft.com/nl-nl/blog/introducing-the-new-dv3-and-ev3-vm-sizes/

http://webcache.googleusercontent.com/search?q=cache:http://www.oracle.com/us/corporate/pricing/cloud-licensing-070579.pdf

The post Oracle, Azure Cloud and licensing with hyperthreading enabled. appeared first on AMIS Oracle and Java Blog.

Getting started with Spring Boot microservices. Why and how.

Wed, 2018-01-24 11:15

In order to quickly develop microservices, Spring Boot is a common choice. Why should I be interested in Spring Boot? In this blog post I’ll give you some reasons why looking at Spring Boot is interesting and give some samples on how to get started quickly. I’ll shortly talk about microservices, move on to Spring Boot and end with Application Container Cloud Service which is an ideal platform to run and manage your Spring Boot applications on. This blog touches many subjects but they fit together nicely. You can view the code of my sample Spring Boot project here. Most of the Spring Boot knowledge has been gained by the following free course by Java Brains.

Microservices

Before we go deeper into why Spring Boot for microservices, we of course first need to know what microservices are. An easy question to ask but a little complex to answer in a few lines in this blog. One of the first people describing characteristics of microservices and actually calling them that was Martin Fowler in 2014. What better source to go back to then the articles he has written. For example here.

‘In short, the microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery. There is a bare minimum of centralized management of these services, which may be written in different programming languages and use different data storage technologies.’

— James Lewis and Martin Fowler

Of course there are a lot of terms involved in this definition

  • It is an architectural style for developing a single application.
  • A suite of small services each running in its own process.
  • Communicating with lightweight mechanisms, often HTTP.
  • Build around business capabilities. Look up bounded context.
  • A bare minimum of centralized management of these services. This implies no application server which provides centralized management of the applications running on it.
  • May be written in different programming languages or use different storage technologies.

A microservice architectural style also has several characteristics. It is very interesting to look at such an architecture in more detail like for example the OMESA initiative to help you get started. As is of course obvious and true with all architectural styles, you will gain most benefits when doing it right. It is however often not trivial to determine ‘right’.

Spring Boot microservices Spring Boot features and microservice principles

Spring Boot is based on certain principles which align with microservice architecture. The primary goals of Spring Boot:

  • Provide a radically faster and widely accessible getting started experience for all Spring development.
  • Be opinionated out of the box, but get out of the way quickly as requirements start to diverge from the defaults.
  • Provide a range of non-functional features that are common to large classes of projects (e.g. embedded servers, security, metrics, health checks, externalized configuration).
  • Absolutely no code generation and no requirement for XML configuration.

The features provided by Spring Boot also make it a good fit to implement microservices in.

  • Spring Boot applications can contain an embedded Tomcat server. This is a completely standalone Tomcat container which has its configuration as being part of the application.
  • Spring Boot is very well suited to create light weight JSON/REST services.
  • Features like health checks are provided. Spring Boot offers Actuator. A set of REST services which allow monitoring and management. Look here. Also externalized configuration can be used. Few centralized management features are required.
  • Since different storage techniques can be used, Spring provides Spring Data JPA. JPA is Java Persistence API. This API provides ORM capabilities to make working with relational databases easier (mostly vendor independent, supports EclipseLink, Hibernate and several others).

Example of an Actuator call to request health status

Easy to implement API design patterns

There are plenty of descriptions online to provide API design guidelines. See for example here. An example API URL can be something like: http://api.yourservice.com/v1/companies/34/employees. Notice the structure of the URL which amongst other things contains a version number. Oracle Mobile Cloud Service documentation also has several design recommendations. See here. These design considerations are of course easily implemented in Spring Boot.

See for example the below code sample:

A simple Spring Boot controller

You can see how the HTTP operations are used and the way method calls are mapped to URLs. Added benefit of this sample is that it also shows how to access the body of the request message.

Integration with backend systems

Spring Boot integrates with JPA. JPA provides an API to easily do ORM. It allows you to work with objects in Java which are backed by database data. For basic CRUD operations, the effort required to implement JPA in Spring Boot is minimal.

You only need three things to do simple CRUD operations when using the embedded Derby database.

  • An annotated entity. You only require two annotations inside your POJO. @Entity to annotate the class and @Id to indicate the variable holding primary key.
  • A repository interface extending CrudRepository (from org.springframework.data.repository)
  • Inside your service, you can use the @Autowired annotation to create a local variable with an instance of the repository.

Connection details for the embedded Derby server are not required. They are for external databases though. If you want to connect to an Oracle database, read the following here.

Pretty comparable to microservices on Node

Node or Spring Boot? This is of course a topic which has many opinions. Many blogs have been written to compare the 2. See for example here.

In several aspects, Spring Boot beats Node.js.

  • Performance. Read the following article here. Spring Boot microservices can achieve higher throughput than similar services on Node.js.
  • Maturity. Spring has a long history of running Enterprise Applications. Node.js can also be used but is less mature.
  • Security. Spring and Spring Boot are clearly better than Node.js. For example, Kerberos support in Node is limited while Spring Boot provides easy abstractions for several security implementations amongst which Kerberos tokens.
  • RDBMS. This is more easy to use in Spring Boot because of JPA.

Node.js beats Spring Boot also in several aspects

  • Build/package management. People who have experience with Maven and NPM often prefer NPM
  • UI. JavaScript is of course the language of choice for front-end applications. The Java based frameworks such as the JSF variants by far do not have the productivity as for example a framework like AngularJS.
  • Document databases like MongoDB. When you can work with JSON, JavaScript code running on Node.js makes it very easy to interact with the database.

Spring Boot, being in the Java ecosystem can also be combined with for example Ratpack. See here. Ratpack provides a high throughput, non-blocking web layer. The syntax is similar to how you would code Node.js code. This is of course not so much of an argument for Spring Boot since modules on Node.js provides similar functionality. Both solutions are more alike than you would think on first glance.

It depends probably mainly on the skills you have available and your application landscape if you would choose Node.js or Spring Boot. If you’re from the JavaScript world, you might prefer to write your microservices on Node.js. If you’re from the Java world, you will prefer Spring Boot. It is important to understand there is not an obvious superior choice whether to go for Node.js or Spring Boot.

Getting started with Spring Boot

The easiest way to get started is first watch some online courses. For example this one from Java Brains. I’ll provide some nice to knows below.

Spring Tool Suite (STS)

As for an IDE, every Java IDE will do, however, since Spring Boot is build on top of Spring, you could consider using Spring Tool Suite (STS). This is a distribution of Eclipse with many specific Spring features which make development of Spring applications easier.

Spring Initializr

An alternative way to get your start project is to go to https://start.spring.io/, indicate your dependencies and click the Generate project button. This will generate a Maven or Gradle project for you with the required dependencies already added.

With STS, you can also use the Spring Initializr functionality easily.

Spring Boot CLI

Spring Boot CLI offers features to create and run Groovy Spring Boot applications. Groovy requires less code than Java to do similar things. It is a script language which runs on the JVM and from Groovy you can access regular Java classes/libraries.

You can for example create an application like:

@RestController
class HelloWorldClass {

@RequestMapping(“/”)
String home() {
return “Hello World!”
}
}

Save this as a Groovy script (e.g. app.groovy) and run it with Spring Boot CLI like: spring run app.groovy

Getting actually started

To get started with Spring Boot, you have to add some entries to your pom.xml file and you’re ready to go. Easiest is to use the New Spring Starter project from STS since it will generate a pom, a main and test class for you. That is what I used for my sample project here.

A simple pom.xml to get started with Spring Boot

Spring and Oracle

Spring is a very common Java framework. You can find traces of it in several Oracle products and features. Below some examples. If you look in other Oracle products, especially those who are Java based, I expect you will find many more examples.

SOA Suite

For example in Oracle SOA Suite.

  • SOA Suite itself under the covers uses Spring
  • SOA Suite can use Spring components
Massive Open Online Course

Oracle uses Spring Boot in courses it provides. For example in the Develop RESTful Java Microservices deployable on Oracle Cloud MOOC.

Application Container Cloud Service (ACCS)

ACCS has been around for a while. Together with Spring Boot, they provide an ideal combination to get your microservices developed and running quickly.

Application Container Cloud Service provides the features of The Twelf-Factor App out of the cloudy box your application so you don’t have to develop these yourself. These of course also align with the microservice principles like executing apps as stateless processes.

If you want to use ACCS with Spring Boot, there are two ways you can deploy your Spring Boot application.

  • You can create a WAR file by specifying war in the packaging tag in the pom.xml file. Next you can deploy this WAR file as a Java EE Web Application. This runs WebLogic in the background. Read more here.
  • You can create a JAR file by specifying jar in the packaging tag in the pom.xml file. Next you can run this JAR file directly since you’ll get an embedded Tomcat with it and can run it as a Java SE application. The configuration will be part of the application here.

I’ve not compared both options in detail but I can imagine if you want to run a ‘micro’-service, an entire WebLogic Server might make it more of a ‘macro’-service.

The post Getting started with Spring Boot microservices. Why and how. appeared first on AMIS Oracle and Java Blog.

Hard Partitioning with Oracle VM Server

Sat, 2018-01-20 09:03

Some quick notes to “pin” (or hard partition) a virtual machine to a specific core.

Download OVM utils which is found in patch 13602094 (Oracle Support): ORACLE VM 3.0 UTILS RELEASES: 1.0.2, 2.0.1, 2.1.0.

When you extract the zip file you will find three zip files for the different Oracle VM versions:
Patch Details
ovm utils now consists of 3 packages
* ovm_utils_1.0.2.zip : for Oracle VM versions 3.0, 3.1 and 3.2
* ovm_utils_2.0.1.zip : for Oracle VM version 3.3
* ovm_utils_2.1.0.zip : for Oracle VM version 3.4

Extract the correct version on the Oracle VM Manager server to /u01/app/oracle/ovm-manager-3/

You can use “xm info” on an Oracle VM Server to print out CPU information:xm info
This server has one socket with four cores and two threads per core.

The “xenpm  get-cpu-topology” command prints out the thread/core/socket topology:
xenpm get-cpu-topology
CPU0 is thread 1 of core 0 and CPU1 is thread 2 of core 0.

You can check which virtual machines are using which CPU’s with the command “xm vcpu-list”:
xm vcpu-list
The actual pinning is performed on the Oracle VM Manager server by using the command “ovm_vmcontrol”.

Use the parameter “-c getvcpu” the get the current hard partition information for a virtual machine:getcpuUse the parameter “-c setvcpu” to pin a virtual machine to a CPU:setcpu
Stop and start the virtual machine after pinning the CPU’s.

You can also pin CPU’s by editing vm.cfg:
vmcfgStop and start the virtual machine after making changes to the vm.cfg file.

The post Hard Partitioning with Oracle VM Server appeared first on AMIS Oracle and Java Blog.

Will you help me build the zoo of programming languages?

Sun, 2018-01-14 12:23

Have you ever come across the following challenge? You have to name something; your own project, own product, company, your boat or even your own child. Coming up with the right name is very important since this is something you have worked on for a long time. So the name has to reflect your inspiration and effort. You used your own blood sweat and tears creating this. Spend many long lonely nights to finalize (just forget the child metaphor here). And now you are ready to launch it. But wait….. it has no name. Best way to name something is to find an example in nature. And animals are powerful and good inspirations for names. Here are 14 programming languages, software, and tools who are named after an animal grouped together in my zoo of programming languages. And there are probably many more. Feel free to help me and add yours as comments on this article.

Impala

Image resultThis is one of the major tools for querying a big data database. Impala is a tool for querying big data. Impala is a query engine that runs on Hadoop. Impala offers scalable parallel database technology to Hadoop, enabling users to issue low-latency SQL queries to data stored in HDFS and Apache HBase without requiring data movement or transformation. Impala is integrated with Hadoop to use the same file and data formats, metadata, security and resource management frameworks used by MapReduce, Apache Hive, Apache Pig and other Hadoop software.Image result for impala male
Impala is promoted for analysts and data scientists to perform analytics on data stored in Hadoop via SQL or business intelligence tools. The result is that large-scale data processing (via MapReduce) and interactive queries can be done on the same system using the same data and metadata – removing the need to migrate data sets into specialized systems and/or proprietary formats simply to perform the analysis. https://impala.apache.org/

The other Impala is a medium-sized antelope found in eastern and southern Africa. The sole member of the genus Aepyceros.

Toad

Image result for quest toadToad is a database management toolset from Quest Software that database developers, database administrators, and data analysts use to manage both relational and non-relational databases using SQL. There are Toad products for developers and DBAs, which run on Oracle, SQL Server, IBM DB2 (LUW & z/OS), SAP and MySQL, as well as, a Toad product for data preparation, which supports most data platforms. Toad solutions enable data professionals to automate processes, minimize risks and cut project delivery timelines. https://www.quest.com/toad/Image result for toad

The other toad Is a common name for certain frogs, especially of the family Bufonidae, that is characterized by dry, leathery skin, short legs, and large bumps covering the parotoid glands. Wikipedia

Elk

Image result for elastic stackThe ELK (now called Elastic Stack) stack consists of Elasticsearch, Logstash, and Kibana. Although they’ve all been built to work exceptionally well together, each one is a separate project that is driven by the open-source vendor Elastic—which itself began as an enterprise search platform vendor. It has now become a full-service analytics software company, mainly because of the success of the ELK stack. Wide adoption of Elasticsearch for analytics has been the main driver of its popularity.
Elasticsearch is a juggernaut solution for your data extraction problems. A single developer can use it to find the high-value needles underneath all of your data haystacks, so you can put your team of data scientists to work on another project.  https://en.wikipedia.org/wiki/ElasticsearchImage result for elk

The other elk, or wapiti (Cervus canadensis), is one of the largest species within the deer family, Cervidae, in the world, and one of the largest land mammals in North America and Eastern Asia. This animal should not be confused with the still larger moose (Alces alces) to which the name “elk” applies in British English and in reference to populations in Eurasia.

Ant

Related imageApache Ant is a software tool for automating software build processes, which originated from the Apache Tomcat project in early 2000. It was a replacement for the Make build tool of Unix and was created due to a number of problems with Unix’s make. It is similar to Make but is implemented using the Java language, requires the Java platform, and is best suited to building Java projects. The most immediately noticeable difference between Ant and Make is that Ant uses XML to describe the build process and its dependencies, whereas Make uses Makefile format. By default, the XML file is named build.xml. Ant is an open-source project, released under the Apache License, by Apache Software Foundation. https://ant.apache.org/index.htmlImage result for ant

The other Ant is a eusocial insect of the family Formicidae and, along with the related wasps and bees, belong to the order Hymenoptera. Ants evolved from wasp-like ancestors in the Cretaceous period, about 99 million years ago, and diversified after the rise of flowering plants. More than 12,500 of an estimated total of 22,000 species have been classified. They are easily identified by their elbowed antennae and the distinctive node-like structure that forms their slender waists.

Rhino

Inicio de ldp para 260px50px moziyarinocrnt.jpgThe Rhino project was started at Netscape in 1997. At the time, Netscape was planning to produce a version of Netscape Navigator written fully in Java and so it needed an implementation of JavaScript written in Java. When Netscape stopped work on Javagator, as it was called, the Rhino project was finished as a JavaScript engine. Since then, a couple of major companies (including Sun Microsystems) have licensed Rhino for use in their products and paid Netscape to do so, allowing work to continue on it. https://developer.mozilla.org/en-US/docs/Mozilla/Projects/RhinoImage result for rhino

The other Rhino ( rhinoceros, from Greek rhinokeros, meaning ‘nose-horned’, from rhinos, meaning ‘nose’, and keratos, meaning ‘horn’), commonly abbreviated to rhino, is one of any five extant species of odd-toed ungulates in the family Rhinocerotidae, as well as any of the numerous extinct species. Two of the extant species are native to Africa and three to Southern Asia.

Python

Image result for python softwarePython is an interpreted high-level programming language for general-purpose programming. Created by Guido van Rossum and first released in 1991, Python has a design philosophy that emphasizes code readability, and a syntax that allows programmers to express concepts in fewer lines of code, notably using significant whitespace. It provides constructs that enable clear programming on both small and large scales. Python features a dynamic type system and automatic memory management. It supports multiple programming paradigms, including object-oriented, imperative, functional and procedural, and has a large and comprehensive standard library. https://en.wikipedia.org/wiki/Python_(programming_language)Image result for python snake

The other Python, is a genus of nonvenomous Pythonidae found in Africa and Asia. Until recently, seven extant species were recognised; however, three subspecies have been promoted and a new species recognized. A member of this genus, Python reticulatus, is among the longest snake species and extant reptiles in the world.

Goat

Related imageWebGoat or GOAT is a deliberately insecure web application maintained by OWASP designed to teach web application security lessons. This program is a demonstration of common server-side application flaws. The exercises are intended to be used by people to learn about application security and penetration testing techniques. https://www.owasp.org/index.php/Category:OWASP_WebGoat_ProjectImage result for goat

The other goat is a member of the family Bovidae and is closely related to the sheep as both are in the goat-antelope subfamily Caprinae. There are over 300 distinct breeds of goat. Goats are one of the oldest domesticated species and have been used for their milk, meat, hair, and skins over much of the world.

Lama

LAMA is a framework for developing hardware-independent, high-performance code for heterogeneous computing systems. It facilitates the development of fast and scalable software that can be deployed on nearly every type of system with a single code base. The framework supports multiple target platforms within a distributed heterogeneous environment. It offers optimized device code on the backend side, high scalability through latency hiding and asynchronous execution across multiple nodes. https://www.libama.org/Image result for lama animal

The other Lama (Lama glama) is a domesticated South American camelid, widely used as a meat and pack animal by Andean cultures since the Pre-Columbian era.
They are very social animals and live with other llamas as a herd. The wool produced by a llama is very soft and lanolin-free. Llamas are intelligent and can learn simple tasks after a few repetitions. When using a pack, they can carry about 25 to 30% of their body weight for 8 to 13 km (5–8 miles).[5]

Serpent

The serpent is one of the high-level programming languages used to write Ethereum contracts. The language, as suggested by its name, is designed to be very similar to Python; it is intended to be maximally clean and simple, combining many of the efficiency benefits of a low-level language with ease-of-use in programming style, and at the same time adding special domain-specific features for contract programming. The latest version of the Serpent compiler, available on GitHub, is written in C++, allowing it to be easily included in any client.

The serpent, or snake, is one of the oldest and most widespread mythological symbols. The word is derived from Latin serpents, a crawling animal or snake. Snakes have been associated with some of the oldest rituals known to humankind and represent the dual expression of good and evil.

Penguin

LogoPENGUIN is a grammar-based language for programming graphical user interfaces. Code for each thread of control in a multi-threaded application is confined to its own module, promoting modularity and reuse of code. Networks of PENGUIN components (each composed of an arbitrary number of modules) can be used to construct large reactive systems with parallel execution, internal protection boundaries, and plug-compatible communication interfaces. We argue that the PENGUIN building-block approach constitutes a more appropriate framework for user interface programming than the traditional Seeheim Model. We discuss the design of PENGUIN and relate our experiences with applications. https://en.wikipedia.org/wiki/Penguin_SoftwareImage result for penguin

The other Penguin(order Sphenisciformes, family Spheniscidae) are a group of aquatic, flightless birds. They live almost exclusively in the Southern Hemisphere, with only one species, the Galapagos penguin, found north of the equator. Highly adapted for life in the water, penguins have countershaded dark and white plumage, and their wings have evolved into flippers. Most penguins feed on krill, fish, squid and other forms of sea life caught while swimming underwater. They spend about half of their lives on land and half in the oceans. Although almost all penguin species are native to the Southern Hemisphere, they are not found only in cold climates, such as Antarctica. In fact, only a few species of penguin live so far south. Several species are found in the temperate zone, and one species, the Galápagos penguin, lives near the equator.

Cheetah

Image result for cheetah template engineCheetah is a Python-powered template engine and code generator. It can be used standalone or combined with other tools and frameworks. Web development is its principle use, but Cheetah is very flexible and is also being used to generate C++ game code, Java, SQL, form emails and even Python code.  Cheetah is an open source template engine and code-generation tool written in Python. Cheetah can be used unto itself or incorporated with other technologies and stacks regardless of whether they’re written in Python or not. https://pythonhosted.org/Cheetah/Image result for cheetah

At its core, Cheetah is a domain-specific language for markup generation and templating which allows for full integration with existing Python code but also offers extensions to traditional Python syntax to allow for easier text-generation.

Porcupine

Image result for porcupine application serverPorcupine is an open-source Python-based Web application server that provides front-end and back-end revolutionary technologies for building modern data-centric Web 2.0 applications. Many of the tasks required for building web applications as you know them, are either eliminated or simplified. For instance, when developing a Porcupine application you don’t have to design a relational database. You only have to design and implement your business objects as Python classes, using the building blocks provided by the framework (data-types). Porcupine integrates a native object – key/value database, therefore the overheads required by an object-relational mapping technique when retrieving or updating a single object are removed. http://www.innoscript.org/Image result for porcupine

The other Porcupines are rodentian mammals with a coat of sharp spines, or quills, that protect against predators. The term covers two families of animals, the Old World porcupines of family Hystricidae, and the New World porcupines of family Erethizontidae. Both families belong to the infraorder Hystricognathi within the profoundly diverse order Rodentia and display superficially similar coats of quills: despite this, the two groups are distinct from each other and are not closely related to each other within the Hystricognathi.

Orca

Orca is a language for implementing parallel applications on loosely coupled distributed systems. Unlike most languages for distributed programming, it allows processes on different machines to share data. Such data are encapsulated in data-objects, which are instances of user-defined abstract data types. The implementation of Orca takes care of the physical distribution of objects among the local memories of the processors. In particular, an implementation may replicate and/or migrate objects in order to decrease access times to objects and increase parallelism.
programming language for distributed systems http://courses.cs.vt.edu/~cs5314/Lang-Paper-Presentation/Papers/HoldPapers/ORCA.pdfImage result for ORCA

The other orca (Orcinus orca) is a toothed whale belonging to the oceanic dolphin family, of which it is the largest member. Killer whales have a diverse diet, although individual populations often specialize in particular types of prey. Some feed exclusively on fish, while others hunt marine mammals such as seals and dolphins. They have been known to attack baleen whale calves, and even adult whales. Killer whales are apex predators, as there is no animal that preys on them. Killer whales are considered a cosmopolitan species, and can be found in each of the world’s oceans in a variety of marine environments, from Arctic and Antarctic regions to tropical seas – Killer whales are only absent from the Baltic and Black seas, and some areas of the Arctic ocean.

Seagul

SeagullSeagull is an Open Source (GPL) multi-protocol traffic generator test tool. Primarily aimed at IMS (3GPP, TISPAN, CableLabs) protocols (and thus being the perfect complement to SIPp for IMS testing), Seagull is a powerful traffic generator for functional, load, endurance, stress and performance/benchmark tests for almost any kind of protocol. Seagul is a traffic generator for load testing. Created by HP and released in 2006. http://gull.sourceforge.net/Image result for seagull

The other Seagull is a seabird of the family Laridae in the suborder Lari. They are most closely related to the terns (family Sternidae) and only distantly related to auks, skimmers, and more distantly to the waders. Until the 21st century, most gulls were placed in the genus Larus, but this arrangement is now known to be polyphyletic, leading to the resurrection of several genera.

Sloth

Sloth is the world’s slowest computer language. Proudly announced by Lary Page at the 2014 Google WWDC as a reaction on Microsoft C-flat-minor. Both languages are still competing in the race for the slowest computer language. Sloth stands for Seriously Low Optimization ThresHolds, has been under development for a really, really long time. I mean, like, forever, man. https://www.eetimes.com/author.asp?doc_id=1322644

Larry Page at the recent WWDC introducing SLOTH.

The other Sloths are arboreal mammals noted for the slowness of movement and for spending most of their lives hanging upside down in the trees of the tropical rainforests of South America and Central America. The six species are in two families: two-toed sloths and three-toed sloths. In spite of this traditional naming, all sloths actually have three toes. The two-toed sloths have two digits, or fingers, on each forelimb. The sloth is so named because of its very low metabolism and deliberate movements, sloth being related to the word slow.

Image result for sloth

 

Add your languages

Hope you enjoyed this small tour. There are probably many more languages named after animals. Please add them as comments and I will update the article. Hopefully, we can cover the entire animal kingdom. Thank you in advance for your submissions.

Sources from Wikipedia.

 

The post Will you help me build the zoo of programming languages? appeared first on AMIS Oracle and Java Blog.

Promoting a simple Oracle JET input text component to a Visual Country Selection component

Thu, 2018-01-04 00:49

To select a country (or any other geographical entity), we can offer our end users a dropdown list, a list of values in a popup window, an auto suggest input text component or a more visual, map based approach. In this article, I describe the latter: Open a modal popup that presents a map of the world and allows the user to inspect and select a country. Note: this article is a logical extrapolation from previous articles on OpenLayers and Oracle JET, specifically this article.

This article demonstrates how we go from a simple input text component and extend it: with a background image (a little globe) that the user can click.

 

image

When the user clicks the icon, a popup opens and shows the world map. If the input component contains a valid country name, this country is selected in the map. The user can click on any country and in doing so, selects the country.

 

image

The name of the country is written back to the input text component (Mali in the next figure).

image

The main challenges discussed in this article:

Starting with the situation from the previous article (the third bullet overhead), this article describes how to create an input text component with clickable icon that opens a popup window that shows the map.

The final code is found here on GitHub: https://github.com/lucasjellema/jet-and-openlayers.

Embed the OpenLayers map in an Oracle JET Popup Component

This step is a simple one. I used the entry in the JET Cookbook on Popup Component: http://www.oracle.com/webfolder/technetwork/jet/jetCookbook.html?component=popup&demo=modal. I copied the styles used in this example to the file src/css/app.css:

.demo-popup {
    width: 80vw;  
    height: 80vh; 
    display: none;
  } 
  .demo-popup-body {
    width: 75vw;  
    height: 75vh; 
    display: flex; 
    flex-direction: column;
    align-items: stretch;
  } 
  .demo-popup-header {
    align-self: flex-start;
    margin-bottom: 10px;
  } 
  .demo-popup-content {
    align-self: stretch;
    overflow: auto;
    flex-basis: 60vh;
  } 
  .demo-popup-footer {
    align-self: flex-end;
    margin-top: 10px; 
  } 

Next I added the popup component in the mapArea.html file, with the DIV that acts as the map container in

<div id="popupWrapper">
    <oj-popup class="demo-popup" id="countrySelectionPopup" tail="none" position.my.horizontal="center" position.my.vertical="bottom" position.at.horizontal="center"
        position.at.vertical="bottom" position.of="window" position.offset.y="-10" modality="modal" data-bind="event:{'ojAnimateStart': startAnimationListener}">
        <div class="demo-popup-body">
            <div class="demo-popup-header">
                <h5>Select a country by clicking on it</h5>
            </div>
            <div class="demo-popup-content">
                <div id="countryInfo"></div>
                <div id="mapContainer" class="map"></div>
            </div>
            <div class="demo-popup-footer">
                <oj-button id="btnClose" data-bind="click: function()
                            {
                              var popup = document.querySelector('#countrySelectionPopup');
                              popup.close();
                            }">
                    Close
                </oj-button>
            </div>
        </div>
    </oj-popup>
</div>

I added the function startAnimationListener to the ViewModel in mapArea.js:


            self.startAnimationListener = function (data, event) {
                var ui = event.detail;
                if (!$(event.target).is("#countrySelectionPopup"))
                    return;

                if ("open" === ui.action) {
                    event.preventDefault();
                    var options = { "direction": "top" };
                    oj.AnimationUtils.slideIn(ui.element, options).then(ui.endCallback);
                    // if the map has not yet been initialized, then do the initialization now (this is the case the first time the popup opens)
                    if (!self.map) initMap();
                }
                else if ("close" === ui.action) {
                    event.preventDefault();
                    ui.endCallback();
                }
            }

The initialization of the map is taken care of in open stage for the popup this startAnimationListener – on the first occasion this popup is opened.

 

Embed a clickable icon in an input text component to allow the end user to open the popup with a mouse click

It is important that the user has a convenient way of opening the country selection popup. Just like the date and time input components provide a little clickable icon, inline in the input component, I want the country selector to consist of a regular JET input text component with an inline icon that the user can click on to bring up the popup.

image

The HTML code to make this happen is added to mapArea.html:

    <div id="countryInput">
        <oj-label for="country-input">Country</oj-label>
        <oj-input-text id="country-input" value="{{selectedCountry}}" on-value-changed="[[countryChangedListener]]"
        ></oj-input-text>
        <img id="countrySelectorOpen" src="css/images/icon-world.png" class='iconbtnintext' title ="Click to open World Map in Popup" data-bind="click: openPopup" /> 
    </div>

Note how the click event on the image is bound to the KnockOut ViewModel’s openPopup function using the data-bind notation,

The CSS style iconbtnintext takes care of the positioning of the icon. This style is added to app.css:

.iconbtnintext {	
    position:absolute;
    cursor:pointer;
    width:22px;
    height:23px;
    margin-left:-24px;
    padding-top:4px;
}

Finally the icon to be used – icon-world.png – is copied to src/css/images

image

 

 

References

JET Cookbook on Popup Component: http://www.oracle.com/webfolder/technetwork/jet/jetCookbook.html?component=popup&demo=modal

The post Promoting a simple Oracle JET input text component to a Visual Country Selection component appeared first on AMIS Oracle and Java Blog.

Using an OpenLayers map to select countries in an Oracle JET application

Tue, 2018-01-02 07:03

In a recent article I discussed how the open source geo, GIS and map library OpenLayers can be used in an Oracle JET application. That article shows how countries selected in a standard Oracle JET Checkbox Set component are represented on a world map. In this article, we take this one step further – or actually several steps. By adding interactivity to the map, we allow users to select a country on the world map and we notify JET components of this selection. The synchronization works both ways: if the user types the name of a country in a JET input text component, this country is highlighted on the world map. Note that the map has full zooming and panning capabilities.

Webp.net-gifmaker (2)

 

The Gif demonstrates how the map is first shown with France highlighted. Next, the user types Chile into the input-text component and when that change is complete, the map is synchronized: Chile is highlighted. The user then hovers over Morocco – and the informational DIV element shows that fact. No selection is made yet. Then the user mouse clicks on Morocco. The country is selected on the map and the JET input-text component is synchronized with the name of the country. Subsequently, the same is done with India: hover and then select. Note: the map can easily support multi-country selection; I had to turn off that option explicitly (default behavior is to allow it).

The challenges I faced when implementing this functionality:

  • add vector layer with countries (features)
  • highlight country when mouse is hovering over it
  • add select interaction to allow user to select a country
  • communicate country selection event in map to “regular” JET component
  • synchronize map with country name typed into JET component

The steps (assuming that the steps in this article are performed first):

  1. Create mapArea.html with input-text, informational DIV and map container (DIV)
  2. Create mapArea.js for the mapArea module
  3. Add a div with data bind for mapArea module to index.html
  4. Download a file in GEOJSON format with annotated geo-json geometries for the countries of the world
  5. Initialize the map with two layers – raster OSM world map and vector country shapes
  6. Add overlay to highlight countries that are hovered over
  7. Add Select Interaction – to allow selection of a country – applying a bold style to the selected country
  8. Update JET component from country selection
  9. Set country selection on map based on value [change]in JET component

And here is the code used to implement this: https://github.com/lucasjellema/jet-and-openlayers .

 

Create mapArea.html with input-text, informational DIV and map container (DIV)
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/4.6.4/ol-debug.css" />
<h2>Select Country on Map</h2>
<div id="componentDemoContent" style="width: 1px; min-width: 100%;">
    <div id="div1">

        <oj-label for="text-input">Country</oj-label>
        <oj-input-text id="text-input" value="{{selectedCountry}}" on-value-changed="[[countryChangedListener]]"></oj-input-text>
    </div>
</div>
<div id="info"></div>
<div id="map2" class="map"></div>
Create ViewModel mapArea.js for the mapArea module

 

define(
    ['ojs/ojcore', 'knockout', 'jquery', 'ol', 'ojs/ojknockout', 'ojs/ojinputtext', 'ojs/ojlabel'],
    function (oj, ko, $, ol) {
        'use strict';
        function MapAreaViewModel() {
            var self = this;

            self.selectedCountry = ko.observable("France");
            self.countryChangedListener = function(event) {
            }
...
      }
        return new MapAreaViewModel();
    }
);
Add a DIV with data bind for mapArea module to index.html
    ...</header>
    <div role="main" class="oj-web-applayout-max-width oj-web-applayout-content">
    <div data-bind="ojModule:'mapArea'" /> 
    </div>
    <footer class="oj-web-applayout-footer" role="contentinfo">
    ...
Download a file in GEOJSON format with annotated geo-json geometries for the countries of the world

I downloaded a GEOJSON file with country data from GitHub: https://github.com/johan/world.geo.json and placed the file in the directory src\js\viewModels of my JET application:

image

 

Initialize the map with two layers – raster OSM world map and vector country shapes
        function MapAreaViewModel() {
            var self = this;

            self.selectedCountry = ko.observable("France");
            self.countryChangedListener = function(event) {
                // self.selectInteraction.getFeatures().clear();
                // self.setSelectedCountry(self.selectedCountry())                
            }


            $(document).ready
                (
                // when the document is fully loaded and the DOM has been initialized
                // then instantiate the map
                function () {
                    initMap();
                })


            function initMap() {
                var style = new ol.style.Style({
                    fill: new ol.style.Fill({
                        color: 'rgba(255, 255, 255, 0.6)'
                    }),
                    stroke: new ol.style.Stroke({
                        color: '#319FD3',
                        width: 1
                    }),
                    text: new ol.style.Text()
                });

                self.countriesVector = new ol.source.Vector({
                    url: 'js/viewModels/countries.geo.json',
                    format: new ol.format.GeoJSON()
                });
               self.map2 = new ol.Map({
                    layers: [
                        new ol.layer.Vector({
                            id: "countries",
                            renderMode: 'image',
                            source: self.countriesVector,
                            style: function (feature) {
                                style.getText().setText(feature.get('name'));
                                return style;
                            }
                        })
                        , new ol.layer.Tile({
                            id: "world",
                            source: new ol.source.OSM()
                        })
                    ],
                    target: 'map2',
                    view: new ol.View({
                        center: [0, 0],
                        zoom: 2
                    })
                });
         }//initMap

 

Add overlay to highlight countries that are hovered over

Note: this code is added to the initMap function:

                // layer to hold (and highlight) currently selected feature(s) 
                var featureOverlay = new ol.layer.Vector({
                    source: new ol.source.Vector(),
                    map: self.map2,
                    style: new ol.style.Style({
                        stroke: new ol.style.Stroke({
                            color: '#f00',
                            width: 1
                        }),
                        fill: new ol.style.Fill({
                            color: 'rgba(255,0,0,0.1)'
                        })
                    })
                });

                var highlight;
                var displayFeatureInfo = function (pixel) {

                    var feature = self.map2.forEachFeatureAtPixel(pixel, function (feature) {
                        return feature;
                    });

                    var info = document.getElementById('info');
                    if (feature) {
                        info.innerHTML = feature.getId() + ': ' + feature.get('name');
                    } else {
                        info.innerHTML = '&nbsp;';
                    }

                    if (feature !== highlight) {
                        if (highlight) {
                            featureOverlay.getSource().removeFeature(highlight);
                        }
                        if (feature) {
                            featureOverlay.getSource().addFeature(feature);
                        }
                        highlight = feature;
                    }

                };

                self.map2.on('pointermove', function (evt) {
                    if (evt.dragging) {
                        return;
                    }
                    var pixel = self.map2.getEventPixel(evt.originalEvent);
                    displayFeatureInfo(pixel);
                });

 

Add Select Interaction – to allow selection of a country – applying a bold style to the selected country

This code is based on this example: http://openlayers.org/en/latest/examples/select-features.html .

                // define the style to apply to selected countries
                var selectCountryStyle = new ol.style.Style({
                    stroke: new ol.style.Stroke({
                        color: '#ff0000',
                        width: 2
                    })
                    , fill: new ol.style.Fill({
                        color: 'red'
                    })
                });
                self.selectInteraction = new ol.interaction.Select({
                    condition: ol.events.condition.singleClick,
                    toggleCondition: ol.events.condition.shiftKeyOnly,
                    layers: function (layer) {
                        return layer.get('id') == 'countries';
                    },
                    style: selectCountryStyle

                });
                // add an event handler to the interaction
                self.selectInteraction.on('select', function (e) {
                    //to ensure only a single country can be selected at any given time
                    // find the most recently selected feature, clear the set of selected features and add the selected the feature (as the only one)
                    var f = self.selectInteraction.getFeatures()
                    var selectedFeature = f.getArray()[f.getLength() - 1]
                    self.selectInteraction.getFeatures().clear();
                    self.selectInteraction.getFeatures().push(selectedFeature);
                });

and just after the declaration of self.map2:

...
    self.map2.getInteractions().extend([self.selectInteraction]);

Update JET component from country selection

Add to the end of the select event handler of the selectInteraction:

                    var selectedCountry = { "code": selectedFeature.id_, "name": selectedFeature.values_.name };
                    // set name of selected country on Knock Out Observable
                   self.selectedCountry(selectedCountry.name);

Create

                self.setSelectedCountry = function (country) {
                    //programmatic selection of a feature
                    var countryFeatures = self.countriesVector.getFeatures();
                    var c = self.countriesVector.getFeatures().filter(function (feature) { return feature.values_.name == country });
                    self.selectInteraction.getFeatures().push(c[0]);
                }
Set country selection on map based on value [change]in JET component

Implement the self.countryChangedListener that is refered to in the mapArea.html file in the input-text componentL:

            self.countryChangedListener = function(event) {
                self.selectInteraction.getFeatures().clear();
                self.setSelectedCountry(self.selectedCountry())                
            }

Create the following listener (for the end of loading the GeoJSON data in the countriesVector); when loading is ready, the current country value in the selectedCountry observable backing the input-text component is used to select the initial country:

                var listenerKey = self.countriesVector.on('change', function (e) {
                    if (self.countriesVector.getState() == 'ready') {
                        console.log("loading dione");
                        // and unregister the "change" listener 
                        ol.Observable.unByKey(listenerKey);
                        self.setSelectedCountry(self.selectedCountry())
                    }
                });

 

References

GitHub Repo with the code (JET Application) : https://github.com/lucasjellema/jet-and-openlayers .

Countries GeoJSON file – https://github.com/johan/world.geo.json

Open Layers Example of Select Interaction – http://openlayers.org/en/latest/examples/select-features.html

Open Layers API – Vector: http://openlayers.org/en/latest/apidoc/ol.source.Vector.html

Event Listener for OpenLayers Vector with GEOJSON source – https://gis.stackexchange.com/questions/123149/layer-loadstart-loadend-events-in-openlayers-3/123302#123302

Animated Gif maker – http://gifmaker.me/

OpenLayers 3 : Beginner’s Guideby Thomas Gratier; Erik Hazzard; Paul Spencer, Published by Packt Publishing, 2015

OpenLayers Book – Handling Selection Events –  http://openlayersbook.github.io/ch11-creating-web-map-apps/example-08.html

The post Using an OpenLayers map to select countries in an Oracle JET application appeared first on AMIS Oracle and Java Blog.

Embedding OpenLayers in Oracle JET for Advanced Maps and GIS style User Interfaces

Mon, 2018-01-01 10:49

Oracle JET is a toolkit for the creation of rich web applications. Many applications will have location-related aspects. Such applications can benefit from advanced map capabilities – for presenting data in maps, allowing users to interact with maps in order to formulate queries, navigate to relevant details or manipulate data. OpenLayers is one of the most prominent open source JavaScript libraries for working with maps in web applications. It provides an API for building rich web-based geographic applications similar to Google Maps and Bing Maps. One of the geographic data providers that OpenLayers works well with is Open Street Map (OSM) – also fully open source.

In this article, I will report on my first steps with OpenLayers and OSM integrated in Oracle JET. In a few simple steps, I will create the JET application illustrated below –a  mix of a JET Checkbox Set where countries can be selected and an OpenLayers map that is manipulated from JavaScript to show (and hide) markers for the countries that are selected (and deselected).

Webp.net-gifmaker

This article should provide you with a starting point for working with OpenLayers in JET yourself. Source code for this article can be downloaded from GitHub: https://github.com/lucasjellema/jet-and-openlayers .

Steps:

  • create new JET application (for example with JET CLI)
  • download OpenLayers distribution and add to JET application’s folders
  • configure the JET application for the OpenLayers library
  • add a DIV as map container to the HTML file
  • add the JavaScript code to initialize and manipulate the map to the ViewModel JS file

In more detail:

1. Create a new Oracle JET application

Follow for example the steps described on the Oracle JET Web Pages: http://www.oracle.com/webfolder/technetwork/jet/globalGetStarted.html 

Use

ojet create projectname

to create the new JET application

2. Download OpenLayers Distribution and Add to the JET Application

Download the OpenLayers distribution – a zip-file with the combined JavaScript and CSS files for OpenLayers (4.x) from https://github.com/openlayers/openlayers/releases/ 

In the JET application’s js/libs directory, create a new directory openlayers and add the new library and any accompanying files to it.

image

3. Configure the JET application for the OpenLayers library

In the js directory update the js/main-release-paths.json file to include the new library.

  "ol": "libs/openlayers/ol-debug",
  "olcss": "libs/openlayers/ol.css"
}

In your RequireJS bootstrap file, typically main.js, add a link to the new file in the path mapping section and include the new library in the require() definition.

  paths:
  //injector:mainReleasePaths
  {
    ...
    'ol': 'libs/openlayers/ol-debug'
  }
  //endinjector

In the same file add a Shim Configuration for OpenLayers

// Shim configurations for modules that do not expose AMD
  shim:
  {
    'jquery':
    {
      exports: ['jQuery', '$']
    }
    ,'ol':
    {
      exports: ['ol']
    }
  }
}

Finally, add module ‘ol’ to the call to require ad as parameter in the callback function (if you want to perform special initialization on this module):

require(['ojs/ojcore', 'knockout', 'appController','ol', 'ojs/ojknockout', 'ojs/ojbutton', 'ojs/ojtoolbar', 'ojs/ojmenu','ojs/ojmodule'],
  function (oj, ko, app, ol) { // this callback gets executed when all required modules are loaded
    ...

Now to actually include  map in a View in the JET application:

4. Add a DIV as map container to the HTML file

The View contains a DIV that will act as the container for the map. It also contains a Checkbox Set with checkboxes for five different countries. The checkbox set is data bound to the ViewModel; any change in selection status will trigger an event listener. Additionally, the currentCountries variable in the ViewModel is updated with any change by the user.

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/4.6.4/ol-debug.css" />
<h2>Workarea with Map - Select Countries</h2>
<div id="div1">
        <oj-checkboxset id="countriesCheckboxSetId" labelled-by="mainlabelid" class="oj-choice-direction-row" value="{{currentCountries}}"
                on-value-changed="[[selectionListener]]">
                <oj-option id="uruopt" value="uy">Uruguay</oj-option>
                <oj-option id="romopt" value="ro">Romania</oj-option>
                <oj-option id="moropt" value="ma">Morocco</oj-option>
                <oj-option id="spaopt" value="es">Spain</oj-option>
                <oj-option id="indopt" value="in">India</oj-option>
        </oj-checkboxset>
        <br/>
</div>
<div id="map2" class="map"></div>

5. Add JavaScript code to initialize and manipulate the map to the ViewModel JS file

Add OpenLayers dependency in workArea.js:

define(
    ['ojs/ojcore', 'knockout', 'jquery', 'ol', 'ojs/ojknockout', 'ojs/ojinputtext', 'ojs/ojbutton', 'ojs/ojlabel', 'ojs/ojcheckboxset'],
    function (oj, ko, $, ol) {
        'use strict';
        function WorkAreaViewModel() {
            var self = this;

The following code defines a countryMap – a collection of five elements (one for each of five countries) that hold longitude and lattitude for each country, as well as a display name and country code (also the key in the map). Subsequenty, an OpenLayers feature is created for each country, and referenced from the countryMap element for later use.

            self.currentCountries = ko.observableArray([]);

            self.countryMap = {};
            self.countryMap['in'] = { "place_id": "177729185", "licence": "Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright", "osm_type": "relation", "osm_id": "304716", "boundingbox": ["6.5546079", "35.6745457", "68.1113787", "97.395561"], "lat": "22.3511148", "lon": "78.6677428", "display_name": "India", "class": "boundary", "type": "administrative", "importance": 0.3133568788165, "icon": "http://nominatim.openstreetmap.org/images/mapicons/poi_boundary_administrative.p.20.png", "address": { "country": "India", "country_code": "in" } };
            self.countryMap['es'] = { "place_id": "179962651", "licence": "Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright", "osm_type": "relation", "osm_id": "1311341", "boundingbox": ["27.4335426", "43.9933088", "-18.3936845", "4.5918885"], "lat": "40.0028028", "lon": "-4.003104", "display_name": "Spain", "class": "boundary", "type": "administrative", "importance": 0.22447060272487, "icon": "http://nominatim.openstreetmap.org/images/mapicons/poi_boundary_administrative.p.20.png", "address": { "country": "Spain", "country_code": "es" } };
            self.countryMap['ma'] = { "place_id": "217466685", "licence": "Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright", "osm_type": "relation", "osm_id": "3630439", "boundingbox": ["21.3365321", "36.0505269", "-17.2551456", "-0.998429"], "lat": "31.1728192", "lon": "-7.3366043", "display_name": "Morocco", "class": "boundary", "type": "administrative", "importance": 0.19300832455819, "icon": "http://nominatim.openstreetmap.org/images/mapicons/poi_boundary_administrative.p.20.png", "address": { "country": "Morocco", "country_code": "ma" } }
            self.countryMap['ro'] = { "place_id": "177563889", "licence": "Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright", "osm_type": "relation", "osm_id": "90689", "boundingbox": ["43.618682", "48.2653964", "20.2619773", "30.0454257"], "lat": "45.9852129", "lon": "24.6859225", "display_name": "Romania", "class": "boundary", "type": "administrative", "importance": 0.30982735099944, "icon": "http://nominatim.openstreetmap.org/images/mapicons/poi_boundary_administrative.p.20.png", "address": { "country": "Romania", "country_code": "ro" } };
            self.countryMap['uy'] = { "place_id": "179428864", "licence": "Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright", "osm_type": "relation", "osm_id": "287072", "boundingbox": ["-35.7824481", "-30.0853962", "-58.4948438", "-53.0755833"], "lat": "-32.8755548", "lon": "-56.0201525", "display_name": "Uruguay", "class": "boundary", "type": "administrative", "importance": 0.18848351906936, "icon": "http://nominatim.openstreetmap.org/images/mapicons/poi_boundary_administrative.p.20.png", "address": { "country": "Uruguay", "country_code": "uy" } };

            for (const c in self.countryMap) {
                // create a feature for each country in the map 
                var coordinates = ol.proj.transform([1 * self.countryMap.lon, 1 * self.countryMap.lat], 'EPSG:4326', 'EPSG:3857');
                var featurething = new ol.Feature({
                    name: self.countryMap.display_name,
                    geometry: new ol.geom.Point(coordinates)
                });
                self.countryMap.feature = featurething;
            }

Then add the code to do the initialization of the Map itself – to be performed when the DOM is ready

            $(document).ready
                (
                // when the document is fully loaded and the DOM has been initialized
                // then instantiate the map
                function () {
                    initMap();
                })

            function initMap() {
                self.elem = document.getElementById("text-input");
                self.map = new ol.Map({
                    target: 'map2',
                    layers: [
                        new ol.layer.Tile({
                            source: new ol.source.OSM()
                        })
                    ],
                    view: new ol.View({
                        center: ol.proj.fromLonLat([-2, -5]),
                        zoom: 3
                    })
                });
            }

and the DIV target container is available:

Also add the code for the selectionListener to be executed whenever countries are selected or deselected.
This code adds OpenLayers features for each of the currently selected countries. Next, construct a layer which contains these features and has a specific style (red circle with big X) associated with it. Finally, add this layer to the map – to have the features displayed in the web page.

            // triggered whenever a checkbox is selected or deselected
            self.selectionListener = function (event) {
                console.log("Country Selection Changed");

                var vectorSource = new ol.source.Vector({}); // to hold features for currently selected countries
                for (var i = 0; i < self.currentCountries().length; i++) {
                    // add the feature to the map for each currently selected country
                    vectorSource.addFeature(self.countryMap[self.currentCountries()[i]].feature);
                }//for

                var layers = self.map.getLayers();
                // remove the feature layer from the map if it already was added
                if (layers.getLength() > 1) {
                    self.map.removeLayer(layers.item(1));
                }
                //Create and add the vector layer with features to the map
                // define the style to apply to these features: bright red, circle with radius 10 and a X as (text) content
                var vector_layer = new ol.layer.Vector({
                    source: vectorSource
                    ,style: function(feature) {
                        var style = new ol.style.Style({
                            image: new ol.style.Circle({
                              radius: 10,
                              stroke: new ol.style.Stroke({
                                color: '#fff'
                              }),
                              fill: new ol.style.Fill({
                                //color: '#3399CC' // light blue
                                color: 'red' // light blue
                            })
                            }),
                            text: new ol.style.Text({
                              text: "X",
                              fill: new ol.style.Fill({
                                color: '#fff'
                              })
                            })
                          });
                          return style;
                        }
                 } )
                self.map.addLayer(vector_layer);

            }//selectionListener
        }

References

Source code in GitHub Repo: https://github.com/lucasjellema/jet-and-openlayers 

Blog article by Enno Schulte (Virtual7) on adding Socket.io as third part library to a JET 3.x application: http://www.virtual7.de/blog/2017/07/oracle-jet-3-add-third-party-libraries-example-socket-io/ 

Documentation on adding 3rd party libraries to JET 4.0: https://docs.oracle.com/middleware/jet410/jet/developer/GUID-EC40DF3C-57FB-4919-A066-73E573D66B67.htm#JETDG-GUID-EC40DF3C-57FB-4919-A066-73E573D66B67 

OJET Docs Checkbox Set – http://www.oracle.com/webfolder/technetwork/jet/jsdocs/oj.ojCheckboxset.html

The post Embedding OpenLayers in Oracle JET for Advanced Maps and GIS style User Interfaces appeared first on AMIS Oracle and Java Blog.

Pages