Quick Start

How to use :

Dependencies

For WAR Package

Add ocelot-web and ocelot-core dependency in your maven web project

<dependency>
   <groupId>org.ocelotds</groupId>
   <artifactId>ocelot-web</artifactId>
   <version>${ocelot.version}</version>
</dependency>
<dependency>
   <groupId>org.ocelotds</groupId>
   <artifactId>ocelot-core</artifactId>
   <version>${ocelot.version}</version>
</dependency>

For EAR Package

Add ocelot-core dependency in your maven ejb module project

<dependency>
   <groupId>org.ocelotds</groupId>
   <artifactId>ocelot-core</artifactId>
   <version>${ocelot.version}</version>
</dependency>

Add ocelot-web dependency in your maven web module project

<dependency>
   <groupId>org.ocelotds</groupId>
   <artifactId>ocelot-web</artifactId>
   <version>${ocelot.version}</version>
</dependency>

Optional

In no production mode, you can add dashboard to your application.
This will show all services exposed in your application, and more.

Add ocelot-dashboard dependency in your maven web module project

<!--OPTIONAL -->
<dependency>
   <groupId>org.ocelotds</groupId>
   <artifactId>ocelot-dashboard</artifactId>
   <version>${ocelot.version}</version>
</dependency>

Don't add anything else in ear pom.xml (except your own dependencies). Add ocelot only in ejb.jar modules and web.war module, only one web module.

beans.xml

In all modules, web and ejb add bean.xml.

But becarefull, ocelotds use CDI 1.1+. Your beans.xml should be like this :


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
       bean-discovery-mode="all">
</beans>

In web module add beans.xml in src/main/webapp/WEB-INF

In ejb module add beans.xml in src/main/resources/META-INF

Annotate classe service

Declare service as accessible from javascript front end


@DataService(resolver = Constants.Resolver...)
public class TestService {
		

Compilation generate javascript stub by introspection of annotated classes

Add Services and Framework to html

Add generated services and core in html page (these files are provided by REST services)

<header>
   <script src="${CONTEXT_PATH}/ocelot/services.js" type="text/javascript"></script>
   <script src="${CONTEXT_PATH}/ocelot/core.min.js" type="text/javascript"></script>

Use it

Use services directly in your code
You have a business service named TestService, so in frontend there is testService instance ready to use.

In java : Class TestService, in javascript testService.

testService.getMessage(Math.floor(Math.random()*10)).then(function (result) {
   doSomethingWithResult(result);
});

Features guide

Expose java services to javascript

It's the main feature of ocelotds, so it's really easy:

Annotate Java Service with @DataService

Just annotate the class with annotation @DataService and set the correct resolverId.

@DataService(resolver = Constants.Resolver....) // this expose all methods in ExposedService to javascript client
public class ExposedService {
...
   public Result getResult() {...}
...
}

Use java service in javascript

exposedService.getResult().then(function(result) {
   ...
});

Expose java services to javascript with different name

In javascript, package concept doesn't exist, so maybe you have many Java Classes with differents packages, but same name.

For fix that, set name attribute on @DataService annotation.

package pkg1;
@DataService(resolver = Constants.Resolver....) // this expose all methods in ExposedService to javascript client
public class ExposedService {
...
   public Result getResult() {...}
...
}
package pkg2;
@DataService(resolver = Constants.Resolver...., name="ExposedService1") // this expose all methods in ExposedService to javascript client but with objet ExposedService1
public class ExposedService {
...
   public Result getResult() {...}
...
}

Use java service in javascript

exposedService.getResult().then(function(result) {
   ...
});
exposedService1.getResult().then(function(result) {
   ...
});

Unexpose method to the front-end

As said previously, adding annotation @DataService(resolver = "RESOLVERID") expose all public methods to the front-end.
But sometimes, you have to hide methods for many reasons.
Hide it, just add an annotation @TransientDataService.

@DataService(resolver = Constants.Resolver....) // this expose all methods in ExposedService to javascript client
public class ExposedService {
   public Result methodExposed() {
   }
   @TransientDataService
   public Result methodNotExposed() {
   }
}

Use specific serialization

Sometime you want to use an objet to front-end, but it is not serializable to json.

Considerate this case

public java.util.Locale getLocale() {
...
}
public void setLocale(java.util.Locale locale) {
...
}

Locale object is not serialisable in json, but you don't want to create a Data Transfert Object (DTO) only for that.
If you do a DTO, you should map it before send it to front-end (getLocale method)
And also when you receive it from front-end to useit in back-end (setLocale method).

Annotate Argument Type with JsonUnmarshaller

public void setLocale(@JsonUnmarshaller(LocaleMarshaller.class) Locale locale) {
...
}

Annotate Return Type with JsonMarshaller

@JsonMarshaller(LocaleMarshaller.class)
public Locale getLocale() {
...
}

Considerate this other case

LocaleMarshaller is bulk with ocelotds but how write a custom Marshaller/Unmarshaller ?

public java.io.File getFile() {
...
}
public void setFile(java.io.File file) {
...
}

File object is not serialisable in json like Locale object.
Ocelotds proposes to you an elegant mecanism for marshall or unmarshall object. Write Marshaller that implements IJsonMarshaller and use annotations JsonUnmarshaller and JsonMarshaller :

Write your custom Marshaller/Unmarshaller

public class FileMarshaller implements IJsonMarshaller<File> {
   @Override
   public String toJson(File obj) throws JsonMarshallingException {
      // becarefull, here generate json, don't forget '\"' around string 
      return "{\"filename\":\""+obj.getName()+"\",\"path\":\""+obj.getAbsolutePath()+"\"}";
   }

   @Override
   public File toJava(String json) throws JsonUnmarshallingException {
      File file = null;
      if(null != json) {
         try (JsonReader reader = Json.createReader(new StringReader(json))) {
            JsonObject root = reader.readObject();
            file = new File(root.getString("path"));
         } catch(Throwable t) {
            throw new JsonUnmarshallingException(json);
         }
      }
      return file;
   }

Use your custom Marshaller/Unmarshaller

@JsonMarshaller(FileMarshaller.class)
public java.io.File getFile() {
...
}
public void setFile(@JsonUnmarshaller(FileMarshaller.class)java.io.File file) {
...
}

Advanced

Considerate this Marshaller/Unmarshaller

public NoSerializableMarshaller implements IJsonMarshaller<NoSerializable> {
   @Override
   public String toJson(NoSerializable obj) throws JsonMarshallingException {
      // transform java object to json
   }
   @Override
   public NoSerializable toJava(String json) throws JsonUnmarshallingException {
      // transform json to java object
   }
}

Marshall, unmarshall simple object

@DataService(resolver = Constants.Resolver....)
public class ExposedService {

   @JsonMarshaller(NoSerializableMarshaller.class)
   public NoSerializable getObjectNonSerializable() {
      return ...;
   }

   public void setObjectNonSerializable(@JsonUnmarshaller(NoSerializableMarshaller.class) NoSerializable ns) {
      return ...;
   }
}

Marshall, unmarshall iterable object (from 2.11.0)

If you want to re-use a marshaller/unmarshaller for iterable object, just set type to JsonMarshallerType.LIST

Works for all Iterable class and subclasses, (List, Collection, Set...)
@DataService(resolver = Constants.Resolver....)
public class ExposedService {

   @JsonMarshaller(value = NoSerializableMarshaller.class, type = JsonMarshallerType.LIST)
   public List<NoSerializable> getNonSerializableObjects() {
      return ...;
   }

   public void setNonSerializableObjects(@JsonUnmarshaller(value = NoSerializableMarshaller.class, type=JsonMarshallerType.LIST) List<NoSerializable> ns) {
   }
}

In front end you receive :

// if object is {"attr1":"value1"}
[{"attr1":"value1"}, {"attr1":"value2"}]

Marshall, unmarshall map<string, object> (from 2.11.0)

If you want to re-use a marshaller/unmarshaller for object in map, just set type to JsonMarshallerType.MAP

Works for map String->object
@DataService(resolver = Constants.Resolver....)
public class ExposedService {

   @JsonMarshaller(value = NoSerializableMarshaller.class, type = JsonMarshallerType.MAP)
   public Map<String, NoSerializable> getMapNonSerializableObject() {
      return ...;
   }

   public void setMapNonSerializableObject(@JsonUnmarshaller(value = NoSerializableMarshaller.class, type=JsonMarshallerType.MAP) MAp<String, NoSerializable> ns) {
   }
}

In front end you receive :

// if object is {"attr1":"value1"}
{"key1":{"attr1":"value1"}, "key2":{"attr1":"value2"}}

Test services

Ocelotds generate a dashboard page

Usage

Include dependecy in pom.xml of you war project


<dependency>
   <groupId>org.ocelotds</groupId>
   <artifactId>ocelot-dashboard</artifactId>
   <version>${ocelot.version}</version>
</dependency>

After deploy your web application, ocelotds expose a web page ocelot/dashboard/index.html

Secure dashboard

Dashboard is usefull for devops, but not for users. So it's probably usefull to secure it

For secure it, ocelot needs to know roles that you allow to access it.
Two ways are availables :

With CDI producer

In your application, write simply a Collection<String> producer with Qualifier for dashboard. Like this :

...
   @Produces
   @OcelotConfiguration(Constants.Options.DASHBOARD_ROLES)
   Collection<String> getRoles() {
      return Arrays.asList("DASHBOARD_ROLE", "ADMIN_ROLE");
   }
   // OR even simply
   @Produces
   @OcelotConfiguration(Constants.Options.DASHBOARD_ROLES)
   Collection<String> roles = Arrays.asList("DASHBOARD_ROLE", "ADMIN_ROLE");
...

In web.xml

In your web module/application, add context-param to web.xml :

...
<context-param>
   <param-name>ocelot.dashboard.roles</param-name>
   <param-value>DASHBOARD_ROLE,ADMIN_ROLE</param-value>
</context-param>
...

Roles as separate by ','

You can test dashboard for this website here : dashboard.

Use java services in javascript

It's the main feature of ocelotds, so it's really easy:

Like explain before you annotated the class with annotation @DataService and set the correct resolverId.

@DataService(resolver = Constants.Resolver....) // this expose all methods in ExposedService to javascript client
public class ExposedService {
...
   public Result getResult() {...}
...
}

Use java service in javascript

Now in javascript you can call directly the service like that

exposedService.getResult().then(function(result) {
    ...
});

Handle result event

For handle result event, use then

You can chain it

exposedService.getResult().then(function(result) {
   // doSomethingWithResult(result);
}).then(function(result) {
   // doSomethingElseWithResult(result);
});

Handle fault event

exposedService.getResult().catch(function(fault) {
   // doSomethingWithFault(fault);
}).then(function(result) {
   // doSomethingElseWithFault(fault);
});

Handle result and fault events

exposedService.getResult().then(function(result) {
   // doSomethingWithResult(fault);
}, function(fault) {
   // doSomethingWithFault(fault);
});

Handle constraint event

exposedService.getResult().constraint(function(violations) {
   // doSomethingWithViolations(violations);
});

Handle event

exposedService.getResult().event(function(evt) {
   if(evt.result === "RESULT") doSomethingWithResult(evt.response);
   else if(evt.result === "CONSTRAINT") doSomethingWithViolations(evt.response);
   else if(evt.result === "FAULT") doSomethingWithFault(evt.response);
});

Use frontend cache features from backend

Ocelot allow simple cache on front end

cache

Add Result to javascript Cache

You have just annotate method that you like cache to the client.

package test;
@DataService(resolver = ...)
public class TestService {
  @JsCacheResult(minute=2, keys={"a.id"})
  public Result methodCached(A a, B b, Collection<C> cs) {
     // the result of this methode will be cached on the client for 2 minute.
     // The key entry is the footprint of test.TestService.methodCached(a.getId())
     // for each differents values of a.id (a.getId()) we have different cache
     ...
  }
}

JsCacheResult

Configure expiration delay

Configure how long the cache will be persist on front-end directly with annotation.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface JsCacheResult {
   int year() default 0;
   int month() default 0;
   int day() default 0;
   int hour() default 0;
   int minute() default 0;
   int second() default 0;
   int millisecond() default 0;
  ...
}

Configure cache key

Obviously method results depend of method arguments. So cache keys depend naturaly of method arguments.

By default cache keys depend of all method arguments nut you can configure that by keys attribute of annotation.

Rules for parameters about key for cache, separate by coma
The order is important mostly if you want use JsCacheRemove
Use json notation.
{} : use 0 argument in calculating cache key. If the cache entry considerated arguments, so all cache entries will be removed
{'*'} : use all arguments in calculating cache key (default value)
{'obj.id', ...} : means that for compute the key the value of id from argument named obj will be used.
Exemple {"i", "u.id", "f.name"}
Finally {} Empty argument means that no argmuent is used for cache key

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface JsCacheResult {
   ...
   /**
    * @return set of used keys ordered
    */
   String[] keys() default {"*"};
}

Remove Results to javascript Cache

Add a result in cache is easy, but sometime you want to remove cache entries before the limit date.

With these features remove easily cache on all clients that hold these cache

The remove cache feature use push notification feature.

Remove specific Result to javascript Cache

You want to remove cache entries before the limit date.

Use JsCacheRemove and specify which cache this method have to remove.

@DataService(resolver = ...)
public class TestService {
  @JsCacheRemove(cls=TestService.class, methodName="methodCached", keys={"a.id"})
  public void doSomethingAndRemoveCache(Z z, A a) {
     // After the success of this method all cache where the entry key is footprint of 
     // test.TestService.methodCached(a.getId()) will be remove on all clients connected.
     ...
  }
}

Remove specifics Results to javascript Cache

Use JsCacheRemoves and add more than one JsCacheRemove.

@DataService(resolver = ...)
public class TestService {
  @JsCacheRemoves({
    @JsCacheRemove(cls=TestService.class, methodName="methodCached", keys={"a.id"}),
    @JsCacheRemove(cls=TestService.class, methodName="otherMethodCached", keys={"z.id"})
  })
  public void doSomethingAndRemoveCache(Z z, A a) {
     // After the success of this method all cache where the entry key is footprint of 
     // pkg.TestService.methodCached(a.getId()) will be remove on all clients connected.
     // And too
     // pkg.TestService.otherMethodCached(z.getId()) will be remove on all clients connected.
     ...
  }
}

Remove All specific Results to javascript cache

For administration reason you can to remove all method cache entries.

Just set keys value to empty list for set that all results of the method will be removed.

@DataService(resolver = ...)
public class TestService {
  @JsCacheRemove(cls=TestService.class, methodName="methodCached", keys={})
  public void doSomethingAndRemoveAllMethodCache(Z z, A a) {
     // After the success of this method all result cached for thie method 'methodCached' in BROWSER will be clear
     ...
  }
}

Remove All Results for all cache

Probably for administration reason too you can to remove all cache entries.

Use JsCacheRemoveAll.

@DataService(resolver = ...)
public class TestService {
  @JsCacheRemoveAll()
  public void doSomethingAndRemoveCache(Z z, A a) {
     // After the success of this method all cache in BROWSER will be clear
     ...
  }
}

From any business methods

Since 2.12.0 you can use JsCacheRemove, JsCacheRemoves and JsCacheRemoveAll from any business methods.

public class BusinessBean {
  @JsCacheRemove(...)
  public void doSomethingAndRemoveCache(...) {
     ...
  }
}

Manage cache from Client

In javascript you can use object ocelotController.cacheManager for clear all cache.

ocelotController.cacheManager.clearCache();

Moreover, you can attach listener on cacheManager for catch events

// listen add entry
ocelotController.cacheManager.addEventListener("add", function(evt) {
   // evt.msg : msgToClient
});

// listen remove entry
ocelotController.cacheManager.addEventListener("remove", function(evt) {
   // evt.msg : msgToClient
});

Notifications

Notification, push, Message Driven bean features, can be used to implement a mecanism for push messages from server to clients.
For exemple a chat or notify something to clients.

Publish and Subscribe-Messages are broadcast to all registered listeners through Topics.

topic schema

In Javascript

Subscribe to topic on front-end (javascript)

In javascript, create Subscriber

var sub = subscriberFactory.createSubscriber("mytopic").then(function() {
   // confirm subscrition
}).catch(function(fault) {
   // subscrition fail
}).message(function(payload) {
   doSomethingWithMsg(payload); // payload in json : object format
});

Unsubscribe to topic on front-end (javascript)

In javascript, get Subscriber and call unsubscribe

It's important to unsubscribe it, for single page application like angularjs application with a router mecanism.
sub.unsubscribe().then(function() {
   // confirm unsubscrition
}).catch(function(fault) {
   // unsubscrition fail
});

In Java

Expose method for publish payload to all subscribers

Just annotate java class method with @JsTopic

@DataService(resolver = Constants.Resolver.CDI)
public class MessageServices {
   @JsTopic("mytopic")
   public String publish(String message) {
      // when you call this methode all subscriber receive the return method.
      return message;
   }
...

Call method from javascript

messageServices.publish(message).then(function() {
   // confirm send
}).catch(function(fault) {
   // send fail
});

You can use dynamic topicname with @JsTopicName

@JsTopic
public String publishToTopic(String message, @JsTopicName topic) {
   return message;
}

Call method from javascript

messageServices.publish(message, topic).then(function() {
   // confirm send
}).catch(function(fault) {
   // send fail
});

Publish message from backend java

In java, publish message to all subcriber clients

@Inject
@JsTopicEvent("mytopic")
Event<Object> myTopicPublisher;

public void publish() {
   // You can send all serializable objects, if you want to send special object use JsonMarshaller.
   // If you want send json object use jsonPayload option
   myTopicPublisher.fire("Message From server"); 
}

Advanced features

Use Marshaller for topic payload

Ocelotds convert Java object to json object.
object have to be a serializable object, if not or if you want to use your own serialization, use JsonMarshaller.

@Inject
@JsTopicEvent("mytopic")
@JsonMarshaller(SpecificMarshaller.class)
Event<Object> myTopicPublisher;

public void publish() {
   myTopicPublisher.fire(unserializableObject);
}
public class SpecificMarshaller implements IJsonMarshaller<Object>{
   @Override
   public String toJson(Object obj) throws JsonMarshallingException {
      return ".....";
   }
Use MessageToClient for send payload with specify topic

In java, publish message with the Ocelot Object : MessageToClient

With MessageToClient you can specify id, it's the name of topic

Finally set payload.

@Inject
@MessageEvent
Event<MessageToClient> publisher;
public void publish(String topic, String msg) {
   MessageToClient mtc = new MessageToClient();
   mtc.setId(topic);
   mtc.setResult(msg); // You can send only serializable objects
   publisher.fire(mtc);
}
Send directly a json object, not a java object (from 2.11.0)

In java, publish message to topic with jsonPayload option

On JsTopic and JsTopicEvent, set jsonPayload option

@JsTopicEvent(value="mytopic", jsonPayload=true)
Event<String> myTopicPublisher;
@JsTopic(value="mytopic", jsonPayload=true)
   public String publish(String message) {
      // Return json format : "foo" in json equal "\"foo\"" 
      return json;
   }

Listen subscribers number

When you use previous features, you can listen a special topic for know how many subscribers are connected

Simply subscribe to topic name preceded by subscribers:

var sub = subscriberFactory.createSubscriber("subscribers:mytopic").then(function() {
   // confirm subscrition
}).catch(function(fault) {
   // subscrition fail
}).message(function(payload) {
   doSomethingWithMsg(payload); // payload in json : object format
});

Beans validation

A cool feature in Java EE is Bean validation (JSR 303)

With Ocelotds you can use Bean validation usualy in your backend.

You receive a new CONSTRAINT event in the frontend. You can use it for notify user that his entries are incomple or wrong.

Annotate your service or bean with JSR 303 annotation

public void methodWithArgumentNotNull(@NotNull String str0) {
   ...
}

Receive event to client

service.methodWithArgumentNotNull(null)
       .then(handlerResult)
       .constraint(function(violations) {
          violations.forEach(function(violation) {
             console.log(violation.index); // 0
             console.log(violation.name); // "str0"
             console.log(violation.message); // "can't be null" 
          });
       });

Annotate your entity

public class WithConstraint {
   @NotNull
   String name = null; // with getter setter of course
}
public void methodWithArgumentConstraint(@Valid WithConstraint wc) {
   ...
}

Receive event to client

service.methodWithArgumentNotNull(null)
       .then(handlerResult)
       .constraint(function(violations) {
          violations.forEach(function(violation) {
             console.log(violation.index); // 0
             console.log(violation.name); // "wc"
             console.log(violation.prop); // "name"
             console.log(violation.message); // "can't be null" 
          });
       });

Use internationalization features

With ocelot is very simple to localized your messages.

Ocelot uses standards mechanisms of Java SE.

Tutorial

messageFormat

Create bundles

In your module (jar or war) add your locale properties files in resources folder of your maven project.

test_fr_FR.properties
test_en_US.properties

In files add entries for translations

// test_fr_FR.properties
HELLOGUY=Bonjour {0}
// test_en_US.properties
HELLOGUY=Hello {0}

Inject OcelotServices

Now in your managed Bean (CDI Bean, EJB) inject OcelotI18nServices

@Inject 
OcelotI18nServices ocelotI18nServices;

Use it

When you want to use localization service, just call method getLocalizedMEssage

public class HelloService {
   public String getLocaleHello(String who) {
                                               // bundlename, entry   , arguments
      return ocelotI18nServices.getLocalizedMessage("test", "HELLOGUY", new Object[]{who});
   }
}

In java

String msg = getLocaleHello("François");
System.out.println(" - "+msg);

Result :

 - Bonjour François

In javascript

helloService.getLocaleHello("François").then(function(msg) {
   console.log(" - "+msg);
});

Result :

 - Bonjour François

Highlight

You cannot set directly the locale from the business layout.

If you want change the locale use the javascript ocelot services

ocelotServices.setLocale({"language":"fr","country":"FR"}).then(function(msg) {
  // Reload 
  location.reload(); 
});

The getLocale is cached during 1 year on cache client so you can set the locale choosen by the client easily during load of page.

ocelotServices.getLocale().then(function(locale) {

});

If you want save the locale in user profile, in db maybe. Call your own service during loading page.

userServices.getUserLocale()then(function(locale) {
   ocelotServices.setLocale(locale).then(function(msg) {
      // Reload 
      location.reload(); 
   });
});

Example

...
   ocelotServices.getLocale().then(function(locale) {
      var localeSelector = document.getElementById("localeSelector");
      for (i = 0; i < localeSelector.options.length; i++) {
         var currentLocale = JSON.parse(localeSelector.options[i].value);
         if(currentLocale.language === locale.language && currentLocale.country === locale.country) {
            localeSelector.options.selectedIndex = i;
            break;
         }
      }
   });
   function changeLocale() {
      var localeSelector = document.getElementById("localeSelector");
      var idx = localeSelector.options.selectedIndex;
      var currentLocale = JSON.parse(localeSelector.options[idx].value);
      ocelotServices.setLocale(currentLocale);
   }
...
<select id="localeSelector" onchange="changeLocale()">
   <option value='{"language":"fr","country":"FR"}' selected>fr</option>
   <option value='{"language":"en","country":"US"}'>en</option>
</select>

Ocelotds minimizes server calls

When a specicific call is in progress, if the same, exactly the same, service is call before the result of previous call.
Ocelotds detect that and will not request again the server.
So ocelotds minimizes the loading of server.

With this feature, don't worry about disable/enable buttons. If the user clicks on many times, the server will be call one time.

Of course this feature combines with feature cache.

qos

what's determined calls are similar

Ocelotds consideres that 2 services is identic if the class, the method and arguments are identic.

service.getValue(5, "foo", {"id":5,"name":"foo"}).then(handler1) 
 == 
service.getValue(5, "foo", {"id":5,"name":"foo"}).then(handler2)
service.getValue(5, "foo", {"id":5,"name":"foo"}).then(handler1)
 != 
service.getValue(1, "foo", {"id":5,"name":"foo"}).then(handler2)

Note, the calls signature is based on cache key

If the result have to put in cache, the mecanisms for determine that 2 calls are identic is based on the class, the method name and keys declared on java service.

package test;
@DataService(resolver = ...)
public class TestService {
  @JsCacheResult(minute=2, keys={"a.id"})
  public Result methodCached(A a, B b, Collection<C> cs) {
     // the result of this methode will be cached on the client for 2 minute.
     // The key entry is the footprint of test.TestService.methodCached(a.getId())
     // for each differents values of a.id (a.getId()) we have different cache
     ...
  }
}

In this case :

testService.methodCached({"id":5,"name":"x"}, {"a":"y","b":"z"}, []).then(handler1)
 == 
testService.methodCached({"id":5,"name":"y"}, null, []).then(handler2)

About security :

Use Secure websocket

You can use websocket secure.

For use Websocket secured, use simply the https protocol for your web application. That's all

JAAS Security (JASPIC)

Websocket cannot use standard JAAS RBAC (Role Base Access Control) Cause Enpoint is not include in user thread. The endpoint is like a singleton.

This is the reason why for commons calls ocelotds uses http services. this Enpoint is include in user thread.

So if you use full java EE server and EJB, you can use standard annotation from JSR-250 as usually.

RolesAllowed, DenyAll, PermitAll, DeclareRoles, RunAs

Declare ROLES in standard web.xml, or annotate your methods following JSR-250 specifications.

Finally annotate Classes, methods with standart annotations.

public class MyService {
   @javax.annotation.security.RolesAllowed("ADMIN")
   public void adminSecureMethod() {
   }
   @RolesAllowed({"USER", "ADMIN"}) 
   public void secureMethod() {
   }
}

OcelotContext

If you don't use a Java EE specification As JAAS, you can inject a context for allows programmatic security.

public class MyService {
   @Inject
   OcelotContext oc;
   public void secureMethod() {
      if(oc.isUserInRole("ADMIN")) {
         // ok
      }	
   }
}

About topics security :

Ocelotds manages the topic security in 3 ways.

By default every clients can subscribe to a topic, but probably you need to control that.

Ocelotds is CDI based and allow an ellegant mecanism for manage it.

Prevent to subscribe to all topics.

Create a global controller for all topics.

It's Very simple just create a default CDI Bean that implements org.ocelotds.security.JsTopicAccessController in the war project.

public class GlobalTopicAccessControl implements JsTopicAccessController {
   @Override
   public void checkAccess(UserContext ctx, String topic) throws IllegalAccessException {
      // if user access to all topic not allowed, throw IllegalAccessException
      String user = ctx.getPrincipal().getName();
      if(!ctx.isUserInRole("ROLE")) {
         throw new IllegalAccessException(user);
      }
   }
}

Prevent to subscribe to specific topic.

Create a specific controller for topic.

It's Very simple too, create a annotated CDI Bean that implements org.ocelotds.security.JsTopicAccessController and Qualified by org.ocelotds.annotations.JsTopicControl.

@JsTopicControl(SPECIFIC_TOPIC)
public class SpecificTopicAccessControl implements JsTopicAccessController {
   @Override
   public void checkAccess(UserContext ctx, String topic) throws IllegalAccessException {
      // if user access to SPECIFIC_TOPIC not allowed, throw IllegalAccessException
      String user = ctx.getPrincipal().getName();
      if(!ctx.isUserInRole("CANSUBSCRIBETO_"+topic)) {
         throw new IllegalAccessException(user);
      }
   }
}
Use one AccessController for more than one topic. (since 2.10.2)

Often, you need to apply same rule for restric subscription. You can use @JsTopicControls for repeat @JsTopicControl annotation

@JsTopicControls({
   @JsTopicControl(SPECIFIC_TOPIC), 
   @JsTopicControl(SPECIFIC_TOPIC2)
})
public class SpecificTopicAccessControl implements JsTopicAccessController {
   @Override
   public void checkAccess(UserContext ctx, String topic) throws IllegalAccessException {
      // if user access to SPECIFIC_TOPIC not allowed, throw IllegalAccessException
      String user = ctx.getPrincipal().getName();
      if(!ctx.isUserInRole("CANSUBSCRIBETO_"+topic)) {
         throw new IllegalAccessException(user);
      }
   }
}

Prevent to send message to user.

Create a message controller for topic.

Again, with CDI is very simple, create a annotated CDI Bean that implements org.ocelotds.security.JsTopicMessageController and Qualified by org.ocelotds.annotations.JsTopicControl.

@JsTopicControl(SPECIFIC_TOPIC)
public class SpecificTopicMessageControl<Payload> implements JsTopicMessageController {

   /**
    * For check right you can use user context and payload
    * @param ctx
    * @param topic
    * @param payload : this is object that will be send to user if check is ok
    */
   @Override
   public void checkRight(UserContext ctx, String topic, Payload payload) throws NotRecipientException {
      // if user must not receive payload, throw NotRecipientException
      String user = ctx.getPrincipal().getName();
      if(!payload.getRecipients().contains(user)) { // this is an example
         throw new NotRecipientException(user);
      }
   }
}
Use one MessageController for more than one topic. (since 2.10.2)

Often, you need to apply same rule for restric message send. You can use @JsTopicControls for repeat @JsTopicControl annotation

@JsTopicControls({
   @JsTopicControl(SPECIFIC_TOPIC), 
   @JsTopicControl(SPECIFIC_TOPIC2)
})
public class SpecificTopicMessageControl<Payload> implements JsTopicMessageController {
   @Override
   public void checkRight(UserContext ctx, String topic, Payload payload) throws NotRecipientException {
   }
}

Configuration :

Configuration is completly optional.

Configure stacktrace length

You can set the length of stacktrace. (default value 50)

In web.xml

<context-param>
   <param-name>ocelot.stacktrace.length</param-name>
   <param-value>10</param-value>
</context-param>

Use CDI producer

Create simply a cdi producer qualifed by annotation
@org.ocelotds.annotations.OcelotConfiguration(OcelotConfigurationName.STACKTRACELENGTH).

@Produces
@OcelotConfiguration(Constants.Options.STACKTRACE_LENGTH)
String getStacktraceConfiguration() {
   return "20";
}

Enabled monitoring (2.7.0)

Add to the url the following param for enabled monitoring
http://hostname/index.html?ocelot={"monitor":true}

Use monitoring

exposedService.getResult().event(function(evt) {
   console.log("getResult total : "+evt.totaltime+" ms / java : "+evt.javatime+" ms");
});

Monitoring services

You can see monitoring on the services test page dashboard

Servers guide

Ocelotds works on Java EE servers with CDI support, but can works in servlet containers.

There are major limitations to using a servlet container; servlet containers doesn’t support deploying session beans, injection using @EJB or @PersistenceContext, or using transactional events. For enterprise features such as these, you should really be looking at a Java EE application server.

Glassfish 4

Overview

GlassFish Server Open Source Edition is a Java EE compatible application server that is developed by Oracle and the GlassFish community using an open source license. The Java EE Reference Implementation is a subset of GlassFish, and therefore the GlassFish roadmap is closely tied to Java EE releases. GlassFish Server is typically released with support for the latest Java EE platform release shortly when the platform has been released, well ahead of other application server implementations.

Like Wildlfy and WAS liberty, Glassfish is full jee 7 implementation. Also, just add ocelot dependencies, and use it.

Dependencies

EAR Package

JAR Module
<!-- IN EJB MODULE -->
<dependency>
   <groupId>org.ocelotds</groupId>
   <artifactId>ocelot-core</artifactId>
   <version>${ocelot.version}</version>
</dependency>
WAR Module
<!-- IN WEB MODULE -->
<dependency>
   <groupId>org.ocelotds</groupId>
   <artifactId>ocelot-web</artifactId>
   <version>${ocelot.version}</version>
</dependency>

WAR Package

<!-- IN WEB MODULE -->
<dependency>
   <groupId>org.ocelotds</groupId>
   <artifactId>ocelot-web</artifactId>
   <version>${ocelot.version}</version>
</dependency>
<dependency>
   <groupId>org.ocelotds</groupId>
   <artifactId>ocelot-core</artifactId>
   <version>${ocelot.version}</version>
</dependency>

Configuration

Nothing... :-)

Wildfly 9

Overview

WildFly is a flexible, lightweight, managed application runtime that helps you build amazing applications.

Like Glassfish and WAS liberty, Wildfly is full jee 7 implementation. Also, just add ocelot dependencies, and use it.

Dependencies

EAR Package

JAR Module
<!-- IN EJB MODULE -->
<dependency>
...<groupId>org.ocelotds</groupId>
...<artifactId>ocelot-core</artifactId>
...<version>${ocelot.version}</version>
</dependency>
WAR Module
<!-- IN WEB MODULE -->
<dependency>
...<groupId>org.ocelotds</groupId>
...<artifactId>ocelot-web</artifactId>
...<version>${ocelot.version}</version>
</dependency>
</dependency>

WAR Package

<!-- IN WEB MODULE -->
<dependency>
...<groupId>org.ocelotds</groupId>
...<artifactId>ocelot-web</artifactId>
...<version>${ocelot.version}</version>
</dependency>
<dependency>
...<groupId>org.ocelotds</groupId>
...<artifactId>ocelot-core</artifactId>
...<version>${ocelot.version}</version>
</dependency>

Configuration

Nothing... :-)

Weblogic 12

Overview

Oracle WebLogic Server 12c R2 is the industry's best application server for building and deploying enterprise Java EE applications with support for new features for lowering cost of operations, improving performance, enhancing scalability and supporting the Oracle Applications portfolio.

Weblogic 12 is full jee 7 implementation. Also, just add ocelot dependencies, and use it.

Dependencies

EAR Package

JAR Module
<!-- IN EJB MODULE -->
<dependency>
...<groupId>org.ocelotds</groupId>
...<artifactId>ocelot-core</artifactId>
...<version>${ocelot.version}</version>
</dependency>
WAR Module
<!-- IN WEB MODULE -->
<dependency>
...<groupId>org.ocelotds</groupId>
...<artifactId>ocelot-web</artifactId>
...<version>${ocelot.version}</version>
</dependency>
</dependency>

WAR Package

<!-- IN WEB MODULE -->
<dependency>
...<groupId>org.ocelotds</groupId>
...<artifactId>ocelot-web</artifactId>
...<version>${ocelot.version}</version>
</dependency>
<dependency>
...<groupId>org.ocelotds</groupId>
...<artifactId>ocelot-core</artifactId>
...<version>${ocelot.version}</version>
</dependency>

Configuration

Nothing... :-)

IBM Websphere 8.5.5

Overview

WAS Liberty has now 'grown-up' to provide the full Java EE7 platform. Combining that API set with operational capabilities that have been rapidly expanded over the past three years, Liberty is now a great deployment choice for many Java applications.

WAS liberty is full jee 7 implementation. Also, just add ocelot dependencies, and use it.

Dependencies

EAR Package

JAR Module
<!-- IN EJB MODULE -->
<dependency>
	<groupId>org.ocelotds</groupId>
	<artifactId>ocelot-core</artifactId>
	<version>${ocelot.version}</version>
</dependency>
WAR Module
<!-- IN WEB MODULE -->
<dependency>
	<groupId>org.ocelotds</groupId>
	<artifactId>ocelot-web</artifactId>
	<version>${ocelot.version}</version>
</dependency>

WAR Package

<!-- IN WEB MODULE -->
<dependency>
	<groupId>org.ocelotds</groupId>
	<artifactId>ocelot-web</artifactId>
	<version>${ocelot.version}</version>
</dependency>
<dependency>
	<groupId>org.ocelotds</groupId>
	<artifactId>ocelot-core</artifactId>
	<version>${ocelot.version}</version>
</dependency>

Configuration

Nothing... :-)

Apache Tomcat 7/8

Overview

Apache Tomcat™ is an open source software implementation of the Java Servlet, JavaServer Pages, Java Expression Language and Java WebSocket technologies. The Java Servlet, JavaServer Pages, Java Expression Language and Java WebSocket specifications are developed under the Java Community Process.

Dependencies

Ocelot Framework use massively cdi. So ocelot need cdi implementation for worked.
Tomcat do not use CDI implementation, so we have to add one. We choose to add Weld implementation as is describe on weld site.
TomEE use an other CDI implementation OpenWebBean, so it's probably possible to use it.
Moreever we have to add json-p api and implementation.

WAR Module

<!-- IN WEB MODULE -->
<!-- OCELOT -->
<dependency>
   <groupId>org.ocelotds</groupId>
   <artifactId>ocelot-web</artifactId>
   <version>${ocelot.version}</version>
</dependency>
<dependency>
   <groupId>org.ocelotds</groupId>
   <artifactId>ocelot-core</artifactId>
   <version>${ocelot.version}</version>
</dependency>
<!-- END OCELOT -->
<!-- JSONP -->
<dependency>
   <groupId>javax.json</groupId>
   <artifactId>javax.json-api</artifactId>
   <version>1.0</version>
</dependency>
<dependency>
   <groupId>org.glassfish</groupId>
   <artifactId>javax.json</artifactId>
   <version>1.0.4</version>
</dependency>
<!-- END JSONP -->
<!-- WELD -->
<dependency>
   <groupId>org.jboss.weld.servlet</groupId>
   <artifactId>weld-servlet-core</artifactId>
   <version>2.2.14.Final</version>
</dependency>
<!-- END WELD -->

Configuration

CDI is not native in Tomcat, so we have to bootstrap it in the Web archive

WAR Module

In web.xml

<!-- WELD -->
<listener>
   <listener-class>org.jboss.weld.environment.servlet.Listener</listener-class>
</listener>
<resource-env-ref>
   <resource-env-ref-name>BeanManager</resource-env-ref-name>
   <resource-env-ref-type>javax.enterprise.inject.spi.BeanManager</resource-env-ref-type>
</resource-env-ref>-->
In context.xml

<!-- WELD -->
<Resource name="BeanManager" auth="Container" type="javax.enterprise.inject.spi.BeanManager" factory="org.jboss.weld.resources.ManagerObjectFactory"/>

Other servers

Ocelotds use standard interface, so if your server is compatible with CDI 1.2+ and Websocket, it's going to work probably

Back-end guide

Use with EJB

It's the best way to use Ocelot, but you have to deploy your project in Full Java EE server.

You have just annotate EJB that you like expose to the client.

@Stateless
@DataService(resolver = Constants.Resolver.EJB)
public class TestEJBService {
   public Result exposedMethodToGetResult(Arg1 arg1, Arg2 arg2, Collection<Arg3> args3) {
      ...
   }
   @TransientDataService
   public void unexposedMethod() {
      ...
   }
}

When you will build your project, a spi extension will generate stub in javascipt.
Like it, just call the EJB from client like it :

testEJBService.getMessage(Math.floor(Math.random()*10)).then(function (result) {
   doSomethingWithMsg(result); // payload
}).catch(function (fault) {
   alert(fault.message + "\n" + fault.classname + "\n" + fault.stacktrace.join('\n'));
});

Scope managements

For EJB, all scopes are managed by types of EJB

There are 3 EJB main types :

  • Stateless
  • Stateful
  • Singleton

The front-end correspondance is :

  • EJB Stateless = REQUEST Scope
  • EJB Stateful = SESSION Scope
  • EJB Singleton = APPLICATION Scope

Limitations

EJB Light

If you use ejbs with interfaces in war packaging, add Localbean annotation

@Stateless
@Localbean
@DataService(resolver = Constants.Resolver.EJB)
public class TestService implements ITestService {

highlight

For Stateless bean, don't stocke anything inside the EJB, cause, no garantie that an other user don't share the bean with an other, EJB is taken in a pool, not generate each time.

Use with CDI

It's the easiest way to use Ocelot, if you use a Full JEE server like glassfish, but you can use a servlet container like Tomcat, but there will be more configuration.

You have just annotate CDI Bean that you like expose to the client.

@DataService(resolver = Constants.Resolver.CDI)
public class TestCDIService {
   public Result exposedMethodToGetResult(Arg1 arg1, Arg2 arg2, Collection<Arg3> args3) {
      ...
   }
   @TransientDataService
   public void unexposedMethod() {
      ...
   }
}

When you will build your project, a spi extension will generate stub in javascipt.
Like it, just call the Bean from client like it :

testCDIService.getMessage(Math.floor(Math.random()*10)).then(function (result) {
   doSomethingWithMsg(result); // payload
}).catch(function (fault) {
   alert(fault.message + "\n" + fault.classname + "\n" + fault.stacktrace.join('\n'));
});

Scope managements

For CDI, all scopes are managed by types of CDI

There are 4 main types of Scope with CDI :

  • ApplicationScoped
  • SessionScoped
  • RequestScoped
  • ConversationScoped

This 4 scopes have no impact in ocelot, use annotation following.

And 2 pseudos scopes

  • Singleton
  • Dependent

The front-end correspondance is :

  • CDI Bean Singleton = APPLICATION Scope
  • CDI Bean Dependent = SESSION Scope
  • CDI Bean = REQUEST Scope

Use with Spring framework from ocelotds-2.4.3

Event Ocelotds uses mainly weld CDI, you can use spring framework for backend.
Ocelotds was test with latest version of spring : 4.2.1.RELEASE, but works probably with older version with same major version

For use spring, add ocelot-extra-spring dependency to your pom.xml.

<dependency>
   <groupId>org.ocelotds</groupId>
   <artifactId>ocelot-extra-spring</artifactId>
   <version>${ocelot.version}</version>
</dependency>

You have to add too the minimal spring dependencies

<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-core</artifactId>
   <version>${spring.version}</version>
</dependency>
<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context</artifactId>
   <version>${spring.version}</version>
</dependency>

Annotate beans

For expose beans to client, annotate springbeans with DataService annotation.

Specify spring resolver.

@DataService(resolver = Constants.Resolver.SPRING)
public class SpringBeanService {
   public Result exposedMethodToGetResult(Arg1 arg1, Arg2 arg2, Collection<Arg3> args3) {
      ...
   }
   @TransientDataService
   public void unexposedMethod() {
      ...
   }
}

When you will build your project, a apt extension will generate stub in javascipt.
Like it, just call the Bean from client like it :

springBeanService.getMessage(Math.floor(Math.random()*10)).then(function (result) {
   doSomethingWithMsg(result); // payload
}).catch(function (fault) {
   alert(fault.message + "\n" + fault.classname + "\n" + fault.stacktrace.join('\n'));
});
			

Configuration

Ocelotds support class configuration Spring.

Just configure as usualy your beans in annotated class with @org.springframework.context.annotation.Configuration

@Configuration
public class ApplicationContextConfig {
   @Bean
   @Scope(...)
   public SpringBeanService springBeanService() {
      return new SpringBeanService();
   }
...   

Scope managements

Ocelot supports 3 differents scopes

For Springbean, all scopes are managed by types of Springbean

There are 3 Spring beans support :

  • prototype
  • client
  • singleton

The front-end correspondance is :

  • prototype = REQUEST Scope
  • client = SESSION Scope
  • singleton = APPLICATION Scope

Hightlights

The default scope for springbeans is singleton

Front-end guide

Web developper, gulp user

Ocelotds generate javascript services from java services.
However Your Web IDE (netbeans, IntelliJ, Eclipse, Webstorm...) don't know generate services.

It will be great, if the front-end developper can use code completion. So for that you can use the followig option

Chose target services directory

In pom.xml add compiler option jsdir

If you build enterprise application (ear), add this option in parent pom.xml or in each pom.xml

Becareful, use the correct version of maven plugin

Maven 2

<build>
   ...
   <plugins>
      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-compiler-plugin</artifactId>
         <version>2.5.1</version>
         <configuration>
            <compilerArguments>
               <Ajsdir>/home/user/projects/frontend/services</Ajsdir>
            </compilerArguments>
         </configuration>
      </plugin>
   </plugins>
</build>

Maven 3

<build>
   ...
   <plugins>
      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-compiler-plugin</artifactId>
         <version>3.5.1</version>
         <configuration>
           <!-- FOR MULTI ARGUMENTS -->
           <compilerArgs>
               <arg>Ajsdir=/home/user/projects/frontend/services</arg>
               <arg>Aargx</arg>
            </compilerArgs>
            <!-- OR FOR SINGLE ARGUMENT -->
            <!--compilerArgument>-Ajsdir=/home/user/projects/frontend/services</compilerArgument-->
         </configuration>
      </plugin>
   </plugins>
</build>

Angularjs (from 2.11.0)

From version 2.11.0 ocelotds generate javascript for flat javascript and angularjs framework
If you want to generate angularjs service in external directory you have to specify this with javascript framework option 'jsfwk'. Use correct writing depending version of maven

<build>
...
<!-- for maven 2 -->
               <Ajsfwk>ng</Ajsfwk>
<!-- or for maven 3 -->
               <arg>Ajsfwk=ng</arg>
...
</build>

Use services in your application

However the easy way to use generated services in your application, is include javascript from url.

Common case

<script src="${CONTEXT_PATH}/ocelot/services.js" type="text/javascript"></script>
<script src="${CONTEXT_PATH}/ocelot/core.min.js" type="text/javascript"></script>

Angularjs case

<script src="${CONTEXT_PATH}/ocelot/services.ng.js" type="text/javascript"></script>
<script src="${CONTEXT_PATH}/ocelot/core.ng.min.js" type="text/javascript"></script>

Other framework case

In all case you can use the common case, ask me if you need more support for an other framework.

Configure frontend

Add Services and Framework to html

Add generated services and core in html page (these scripts are provided by ocelot REST)

<header>
   <script src="${CONTEXT_PATH}/ocelot/services.js" type="text/javascript"></script>
   <script src="${CONTEXT_PATH}/ocelot/core.min.js" type="text/javascript"></script>

Use it

Use services directly in your code


testEJBService.getMessage(Math.floor(Math.random()*10)).event(function (evt) {
   if(evt.type === "RESULT") {
      doSomethingWithResult(evt.result);
   } else {
      doSomethingWithFault(evt.fault);
   }
}).then(function (result) {
   doSomethingWithResult(result);
}).then(function (result) {
   doSomethingWithResult(result);
}, function (fault) {
   doSomethingWithFault(fault);
}).constraint(function (violations) {
   doSomethingWithViolations(violations);
}).catch(function (fault) {
   doSomethingWithFault(fault);
});

Notifications

Message Driven bean features, can be used to implement a chat for example or notify something to the client.

create TopicConsumer

In javascript, create an instance of TopicConsumer


var sub = subscriberFactory.createSubscriber("mytopic").then(function (res) {
  subscriptionOk();
}).catch(function (f) {
  subscriptionNok();
}).message(function (msg) {
   doSomethingWithMsg(msg); 
});

sub.unsubscribe("mytopic").then(function (res) {
	unsubscriptionOk();
}).catch(function (f) {
   unsubscriptionNok();
});

Publish messages

See details in Notifications section

Use angular framework

Add Angular version of Services and Framework to html

Add generated angular services and core in html page (these scripts are provided by ocelot REST)

<header>
   <script src="${CONTEXT_PATH}/ocelot/services.ng.js" type="text/javascript"></script>
   <script src="${CONTEXT_PATH}/ocelot/core.ng.min.js" type="text/javascript"></script>

Add ocelot.ds as application dependency

angular.module('my.app', ['ocelot.ds']);

Inject your service in your controller, naturally

MyController.$inject = ['$scope', 'myService'];
function MyController($scope, myService) {
	var inst = this;
	inst.list = [];
	inst.getListOfSomething = function() {
		myService.getEntityList().then(function (list) {
			inst.list = list;
			$scope.$apply();
		});
	};
}

Don't forget to use $scope.$apply() for apply async result to controller scope.

Notifications

Message Driven bean features, can be used to implement a chat for example or notify something to the client.

create TopicSubscriber

In javascript, create an instance of TopicSubscriber

Inject subscriberFactory in your controller

MyController.$inject = ['$scope', 'subscriberFactory'];
function MyController($scope, subscriberFactory) {
   var sub = subscriberFactory.createSubscriber("mytopic").then(function (res) {
      subscriptionOk();
   }).catch(function (f) {
      subscriptionNok();
   }).message(function (msg) {
      doSomethingWithMsg(msg); 
   });
   sub.unsubscribe("mytopic").then(function (res) {
	   unsubscriptionOk();
   }).catch(function (f) {
      unsubscriptionNok();
   });

Publish messages

See details in Notifications section

OcelotServices

ocelotService is service served by Ocelot. It allow some actions

ocelotServices is a standard services like every service like you can create.

Method getLocale

ocelotServices.getLocale():String

Get user locale from server Exemple

ocelotServices.getLocale().then(function(loc) {
   doSmethingWithLocale(loc);
});

Method setLocale

ocelotServices.setLocale(loc:String)

set user locale to server Exemple

ocelotServices.setLocale({"language":"fr", "country":"FR"});

subscriberFactory

subscriberFactory is factory for create Subscriber

See details in Notifications section

subscriberFactory.createSubscriber("topicname");

Angularjs

   MyCtrl.$inject = ['$scope', 'subscriberFactory'];
   function MyCtrl($scope, subscriberFactory) {
      ...
      subscriberFactory.createSubscriber("topicname").message(function(msg) {
      });
      ...
   }

OcelotController

ocelotController is the controller of Ocelot…
An instance is created on loading of page. You can access it by “ocelotController”.
You can do some actions with it.

Attribute status

ocelotController.status [Readonly]

Return the state of websocket : 'CONNECTING', 'OPEN', 'CLOSING', 'CLOSED', 'ERROR'

You can monitor status by subscribe to 'ocelot-status' topic

subscriberFactory.createSubscriber('ocelot-status').message(function(status) {
   if(status === 'ERROR') {
      console.log("websocket is in error");
   }
})

Method cacheManager.addEventListener

ocelotController.cacheManager.addEventListener(type:String, listener:Function(evt:Event))

Add handler for "add" or "remove" entry in cache

Method cacheManager.clearAll

ocelotController.cacheManager.clearAll()

Remove all entries in cache

OcelotPromise

OcelotPromise is very similar of the promise object from javascript ES6 > OcelotPromise is serve by method calling

var ocelotPromise = service.getMessage();

Use your java service in javascript

OcelotPromise uses the design pattern 'methods chaining'

With this design you can separate clearly your code

service.getMessage().ignoreCache(false).timeout(10000)
  .then(function(obj) {
   doSomethingWithResult(obj);
}).then(function(obj) {
   doSomethingWithResult(obj);
}, function(fault) {
   doSomethingWithFault(fault);
}).catch(function(fault) {
   doSomethingWithFault(fault);
}).event(function(evt) { // event method is particulary usefull when you want to get the origin of calling because you can get the arguments.
   console.debug("Receive result of "+evt.dataservice+"."+evt.operation+"("+JSON.stringify(evt.args)+")"); 
   if(event.type === "RESULT") {
      doSomethingWithResult(evt.response);
   } else if(event.type === "FAULT") {
      doSomethingWithFault(evt.response);
   }
});

ignoreCache(boolean);

occelotPromise.ignoreCache(cacheIgnored:boolean):OcelotPromise

If the java service is annotated with JsCacheResult the result will be cache in the browser storage.
With ignoreCache method, you can change the common behavior.

timeout(number);

occelotPromise.timeout(ms:number):OcelotPromise

With this method you can change the default timeout. By default the timeout is defined to 10 secondes.

then(function(result));

occelotPromise.then(resultHandler:Function(result:Object)):OcelotPromise

Add result handler.
The Object result is the result returned by the java method in json format.

then(function(result), function(fault));

occelotPromise.then(resultHandler:Function(result:Object), Function(fault:Fault)):OcelotPromise

Add result handler and fault handler.
The Object result is the result returned by the java method in json format.
The Object fault is a fault.
The Object event is a event

catch(function(fault));

occelotPromise.catch(Function(fault:Fault)):OcelotPromise

Add fault handler.
The Object fault is an object that embeds the exception throws by the java method in json format.

event(function(event));

occelotPromise.event(Function(event:OcelotEvent)):OcelotPromise

Add event handler.
The Object event support result or fault returned by the java method in json format.
event Object support informations about the initial request

This object is particulary usefull if you need to get informations about the request.
For example, imagine your ui allows to delete a row from a list.
Your ui includes a button on each rows. When you click on it, you call method itemService.removeItem(id).
Of course you don't delete the row, cause, you have to wait the response of server.
When you receive positive response from server, you need informations to remove the good row. But the method returns void, so you don't have any information from response (response is null).

@DataService(resolver = Constants.Resolver....) // this expose all methods in ExposedService to javascript client
public class ItemService {
...
   public void removeItem(Long id) {...}
...
}
itemService.removeItem(id).event(function(evt) { // event method is particulary usefull when you want to get the origin of calling because you can get the arguments.
   console.debug("Receive result of "+evt.dataservice+"."+evt.operation+"("+JSON.stringify(evt.args)+")"); 
   var id = evt.args[0];
   if(event.type === "RESULT") {
      console.debug("Item "+id+" Removed correctly on server, remove on ui");
      removeRowWithItemId(id);
   } else if(event.type === "FAULT") {
      removeRowWithItemIdFailed(id);
   }
});

Special Ocelotds Objects

Object Fault

var fault = {
   "classname": "", // java full classname of the exception
   "message": "", // message of error
   "stacktrace": ["", ""] // stacktrace
}

The length of stacktrace is settable in the option see details

Object Violation

var violation = {
   "index": 0, // the index of argument in the call that concern this violation
   "name": "", // the name of argument
   "message": "", // the message of violation constraint
   "prop": "" // the name of property concerned
}

Object OcelotEvent

var event = {
   "type": "RESULT/FAULT/MESSAGE", // MESSAGE IS ONLY FOR PUSH FEATURES
   "dataservice": "", // The simple classname of service called
   "operation": "", // The method name called
   "args": [], // arguments
   "response": Object // response
}

Object Locale

var loc = {
   "language": "",
   "country": ""
}

Advanced feature on front-end

Javascript integration

minify/magnify javascript

Ocelotds serves ocelot/services.js minified.

However, by default ocelot serves ocelot/core.js not minified
For preserve the band width you can include it minified. Just use /ocelot/core.min.js

<body>
   <script src="ocelot/services.js" type="text/javascript"></script>
   <script src="ocelot/core.min.js" type="text/javascript"></script>

Optional configuration

configure stacktrace size sent to client

In your web project, you can specify the size of stacktrace sent on client side.
The default value is 50
If you set 0 the stacktrace will be flush in backend logger.
In deed, you can internationalize the error message easily. Show i18n section i18n.
But for debugging, it's usefull to get the full stacktrace throwed from back-end.
Limit the size in production environnement for preserve the band width.
Add section in the standard web descriptor web.xml

<context-param>
   <param-name>ocelot.stacktrace.length</param-name>
   <param-value>50</param-value>
</context-param>

SPI, extends Ocelotds

How to extends Ocelot.

Create a new resolver

In case or defaults resolvers doen't support your service. You can create your own resolver.

Create librairy include implementations of DataServiceResolver

Dependency :

<dependency>
   <groupId>org.ocelotds</groupId>
   <artifactId>ocelot-spi</artifactId>
   <version>${ocelot.version}</version>
</dependency>

Class DataServiceResolver :

@DataServiceResolverId("MyRID")
public class MyResolver implements DataServiceResolver {
   @Override
   public Object resolveDataService(String dataService) throws DataServiceException {
   // how to get dataService implementation
   }
   @Override
   public Scope getScope(Class clazz) {
      // Scope.MANAGED : The Scope is managed by the application.
      // Scope.SESSION : The bean will be associate with the client session.
      return Scope.MANAGED;
   }

Use it :

And on the services

@DataService(resolver = "MyRID")
public class MyService {

Use exotic javascript framework

Ocelotds enable hability to use the same object in javascript from java.

For marshall to javascript from java, it's almost very easy. But in the other side, ocelotds has to determine how it must transform json object to java.

For that ocelotds helps itself with the name of method called. Finally it tries to convert json object to java object, calls the empty constructor, and calls all setters from json attributes.

For example if json object owns an attribute named "name", ocelotds will try to call the method "setName".

But some javascript frameworks add some technic attributes for internal process.

For example angularjs add an attribute named : "$$hashkey".

So we have to ignore them.

Ocelotds allow an ellegant mecanism for add json argument cleaner.

Implements Cleaner

Just implement the interface org.ocelotds.core.Cleaner. The argument is the json object in string format. So the easiest way is to use a regular expression for remove unusefull attributes.

public class AngularClearner implements org.ocelotds.core.Cleaner {
   /**
    * Method allow cleaning all extra fields on arguments from framework web For sample angularjs add some variables begin $$ So replace : ,"$$hashKey":"object:\d" 
    *
    * @param arg 
    * @return
    */
   @Override
   public String cleanArg(String arg) {
      String angularvar = "(,\"\\$\\$\\w+\":\".*\")";
      return arg.replaceAll(angularvar, "");
   }
}

Put this class in your web module, that's all.

Tips

This section compile some tips, that I fixed for this project.

Test authentication & autorisation with arquillian and embedded glassfish

For test authentication & autorisation, users must be added to glassfish.

How to add users

Actually, you cannot add users to glassfish with Embedded container and arquillian mecanism.

So maybe we can to use commandRunner of glassfish.

@RunWith(Arquillian.class)
public class OcelotTest {
   @Resource(mappedName = "org.glassfish.embeddable.CommandRunner")
   CommandRunner commandRunner;

   private static boolean INITIALIZED = false;

   @Before
   public void configureLoginRealm() {
      if (!INITIALIZED) {
         INITIALIZED = true;
			CommandResult commandResult = commandRunner.run("create-file-user", "--groups=GROUP",  "USER");
         System.out.println(commandResult.getExitStatus().toString() + " " + commandResult.getOutput());
         Throwable throwable = commandResult.getFailureCause();
         if (throwable != null) {
            System.out.println(throwable.getMessage());
         }
      }
   }
}

That seems fine, but with commandRunner, it is not possible to specify password. Neither with userpassword option or passwordfile option

Add a realm

An other solution is to use the existant realm and change the file where the users are stored.

But the probleme is that you cannot change the users file for an existant realm.

Solution create a new realm and specify the users file "keyfile".

Before we have to create users file, for that, just manage your users in your local glassfish, and get "keyfile" file in config folder.

Put "keyfile" file in src/test/resources/glassfish folder of your project (maven) . Example, this defines some users with password equals to username, and some groupnames

test;{SSHA256}dXiBUbqQ1FQZGGAQ7fiwtnF+BF4Htq5Q8TZ89WeEmItcFh+/In119w==;USERGP,TESTGP
admin;{SSHA256}dlIcDOextJ5m6bhgCiEfXyUTfr4toeR4/k8qURqr3W54nOBjocMNug==;USERGP,ADMINGP
demo;{SSHA256}9tmmAdO+T5oCndLdxaEizfBPTAJ03cqVP2TyTRhg9aMCegAWgJaxSQ==;USERGP
user;{SSHA256}78jzX0iBjXiLGWXC7m8J3PS50P4RLGEWLO+kfrE0AVNmYY4kAdnJEw==;USERGP

Now we can to create our realm "test-file"

   ...
   @Resource(mappedName = "org.glassfish.embeddable.CommandRunner")
   CommandRunner commandRunner;

   private static boolean INITIALIZED = false;

   @Before
   public void configureLoginRealm() {
      if (!INITIALIZED) {
         INITIALIZED = true;
         File keyfile = new File("src/test/resources/glassfish/keyfile");
         // have to escape ":" and "\" chars
         String keyfilepath = keyfile.getAbsolutePath().replace("\\", "\\\\").replace(":", "\\:");
         CommandResult commandResult = commandRunner.run("create-auth-realm", "--classname=com.sun.enterprise.security.auth.realm.file.FileRealm",
            "--property=jaas-context=fileRealm:file=" + keyfilepath, "test-realm");
         System.out.println(commandResult.getExitStatus().toString() + " " + commandResult.getOutput());
      }
   }
   ...

Now if you try to authenticate user.

   ...
   public static boolean loginFileUser(final String username, final String password) throws Exception {
      final ProgrammaticLogin login = new ProgrammaticLogin();
      return login.login(username, password.toCharArray(), "test-realm", true);
   }
   ...

You receive an exception.

WEB9102: Web Login Failed: com.sun.enterprise.security.auth.login.common.LoginException: Login failed: No LoginModule setted for fileRealm

In fact the file login.conf is not defined.

login.conf

Create the login.conf

Create login.conf in resources folder

fileRealm {
	com.sun.enterprise.security.auth.login.FileLoginModule required;
};

Specify the login.conf

The login.conf, is java system file, and is override by JVM Option. But when we create the realm, the JVM is already started. So we have to use surefire plugin to set it.

In pom.xml use surefire plugin for add system property and specify the path of login.conf

   ...
   <build>
      <plugins>
         <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.19.1</version>
            <configuration>
               <systemProperties>
                  <property>
                     <name>java.security.auth.login.config</name>
                     <value>src\test\resources\glassfish\login.conf</value>
                  </property>
               </systemProperties>
            </configuration>
         </plugin>
      </plugins>
   </build>
   ...

Finally secure web app as usual

web.xml

   ...
    <security-constraint>
        <display-name>authentication</display-name>
        <web-resource-collection>
            <web-resource-name>secure</web-resource-name>
            <url-pattern>/SECURERESOURCE</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <description/>
            <role-name>USERR</role-name>
        </auth-constraint>
    </security-constraint>
    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>test-realm</realm-name>
    </login-config>
    <security-role>
        <description/>
        <role-name>TESTR</role-name>
    </security-role>
    <security-role>
        <description/>
        <role-name>USERR</role-name>
    </security-role>
    <security-role>
        <description/>
        <role-name>ADMINR</role-name>
    </security-role>
   ...

glasssfisg-web.xml or glassfish-application.xml

   ...
   <security-role-mapping>
      <role-name>USERR</role-name>
      <group-name>USERGP</group-name>
   </security-role-mapping>
   <security-role-mapping>
      <role-name>ADMINR</role-name>
      <group-name>ADMINGP</group-name>
   </security-role-mapping>
   <security-role-mapping>
      <role-name>TESTR</role-name>
      <group-name>TESTGP</group-name>
   </security-role-mapping>
   ...

This method and more are used in sources of ocelotds