Run Spring Boot Scheduler as Heroku Worker

1. Setup Application
2. Setup Scheduler
3. Add Spring Boot dependency
4. Add Procfile
5. Scale Heroku worker

1. Setup Application

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

/**
 * Created by tmichels on 9/20/15.
 */

@SpringBootApplication
@EnableScheduling
public class Application {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class);
    }
}

2. Setup Scheduler

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Set;

/**
 * Created by tmichels on 9/18/15.
 */

@Component
public class Scheduler {

    private static final Logger logger = LoggerFactory.getLogger(Scheduler.class);

    @Scheduled(cron = "0 0 13 * * *")
    public void buyHighPercentageReturnNotes(){
       System.out.println("Scheduler is running");
    }
}

3. Add Spring Boot dependency

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.2.6.RELEASE</version>
    </parent>
  <dependencies>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter</artifactId>
      </dependency>
  </dependencies>

4. Add Procfile

worker: java $JAVA_OPTS -jar target/*.jar

5. Scale Heroku worker

heroku ps:scale worker=1

Apache Camel Kafka Spring Integration

1. Configure Camel Kafka Component

package com.integration.camel.component;

import org.apache.camel.component.kafka.KafkaComponent;
import org.apache.camel.component.kafka.KafkaConfiguration;
import org.apache.camel.component.kafka.KafkaEndpoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Created by tmichels on 9/3/15.
 */

@Configuration
public class KafkaCamelComponent {
    
    @Bean
    public KafkaEndpoint kafkaEndpoint(){
        KafkaEndpoint kafkaEndpoint = new KafkaEndpoint();
        kafkaEndpoint.setZookeeperHost("localhost");
        kafkaEndpoint.setZookeeperPort(2181);
        kafkaEndpoint.setTopic("test");
        return kafkaEndpoint;
    }

    @Bean
    public KafkaComponent kafkaComponent(KafkaEndpoint kafkaEndpoint){
        KafkaComponent kafkaComponent = new KafkaComponent();
        kafkaComponent.setEndpointClass(kafkaEndpoint.getClass());
        return kafkaComponent;
    }
}

2. Configure Kafka Consume and Producer Route

package com.integration.camel.route;

import org.apache.camel.builder.RouteBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Created by tmichels on 9/3/15.
 */

@Configuration
public class KafkaCamelRoute {

    @Bean(name = "KafkaRouteProducer")
    public RouteBuilder kafkaRouteProducer() {
        return new RouteBuilder() {
            public void configure() {
                from("direct:kafkaRoute").to("kafka:localhost:9092?topic=test&zookeeperHost=localhost&zookeeperPort=2181&groupId=group1&serializerClass=kafka.serializer.StringEncoder").bean(kafkaOutputBean.class);
            }
        };
    }

    @Bean(name="KafkaRouteConsumer")
    public RouteBuilder kafkaRouteConsumer() {
        return new RouteBuilder() {
            public void configure() {
                from("kafka:localhost:9092?topic=test&zookeeperHost=localhost&zookeeperPort=2181&groupId=group1&serializerClass=kafka.serializer.StringEncoder").bean(kafkaOutputBean.class);
            }
        };
    }

    public static class kafkaOutputBean {
        public void printKafkaBody(String body) {
            System.out.println("KafkaBody result >>>>> " + body);
        }
    }
}

3. Start Kafka test by adding routes to CamelContext and starting

package com.integration.camel.route;

import com.integration.camel.component.KafkaCamelComponent;
import com.integration.camel.context.CamelContextConfig;
import org.apache.camel.CamelContext;
import org.apache.camel.EndpointInject;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.kafka.KafkaComponent;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * Created by tmichels on 9/3/15.
 */

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {KafkaCamelComponent.class,
                                KafkaCamelRoute.class,
                                CamelContextConfig.class})
public class KafkaCamelIntegrationTest {

    @Autowired
    CamelContext camelContext;

    @Autowired
    KafkaComponent kafkaComponent;

    @Autowired
    @Qualifier("KafkaRouteProducer")
    RouteBuilder kafkaRouteProducer;

    @Autowired
    @Qualifier("KafkaRouteConsumer")
    RouteBuilder kafkaRouteConsumer;

    @EndpointInject(uri = "direct:kafkaRoute")
    ProducerTemplate kafkaProducer;
    
    @Before
    public void before() throws Exception {
        camelContext.addRoutes(kafkaRouteProducer);
        camelContext.addRoutes(kafkaRouteConsumer);
    }

    @Test
    public void testKafkaRoute(){
        kafkaProducer.sendBody("direct:KafkaRouteProducer", "testKafkaMessage");
        try {
            camelContext.start();
            camelContext.stop();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Jax-rs Basic Authentication using HeaderParam

@Path("/v1/docs")
@Consumes({ MediaType.APPLICATION_JSON })
@Named
@Api(value = "/v1/docs", description = "Docs Service" )
public interface DocumentService {

    @POST
    @Path("/{caseId}/{emailMessageid}")
    @PermitAll
    @ApiOperation(value = "Docs Service",
            notes = "Create Salesforce Account, Contact"
    )
    @ApiResponses(value = {
            @ApiResponse(code = 500, message = "Unexpected failure")
    })
    void uploadAndDeleteDocumentFromSalesforceAndUpdateCase(@PathParam("caseId") String caseId, 
                                                            @PathParam("emailMessageid") String emailMessageid,  
                                                            @HeaderParam("authorization") String authentication);
}
@Named
public class DocumentServiceImpl implements DocumentService {

    private static final Logger logger = org.slf4j.LoggerFactory.getLogger(DocumentServiceImpl.class);

    @Value("${SF_DOCS_USERNAME}")
    private String sfDocsUsername;

    @Value("${SF_DOCS_PASSWORD}")
    private String sfDocsPassword;

    @Override
    public synchronized void uploadAndDeleteDocumentFromSalesforceAndUpdateCase(String caseId, String emailMessageId, String authentication) {
        if(isUserAuthenticated(authentication)){
          
        } else {
            logger.error("caseId {} emailId {} authentication failed {}", caseId, emailMessageId, authentication);
        }
    }

    public boolean isUserAuthenticated(String authString){
        if (authString!=null){
            String[] authParts = authString.split("\\s+");
            String authInfo = authParts[1];
            byte[] bytes  =  DatatypeConverter.parseBase64Binary(authInfo);
            String decodedAuth = new String(bytes);
            String[] userNameAndPassword = decodedAuth.split(":");
            if (userNameAndPassword[0].equals(sfDocsUsername) && userNameAndPassword[1].equals(sfDocsPassword)){
                return true;
            } else
                logger.info("isUserAuthenticated did not match decodedAuth {} username {}", decodedAuth, sfDocsUsername+":"+sfDocsPassword);
        }
        return false;
    }
}

Metrics Dashboard using STOMP WebSocket

http://cloudsole-metrics.herokuapp.com
https://github.com/thysmichels/cloudsole-metrics

1. Basic WebAppInitializer
2. WebConfiguration with EnableScheduling
3. WebSocketConfiguration with EnableWebSocketMessageBroker
4. Random Number Generator with ApplicationListener
5. Sock.js Javascript

1. Basic WebAppInitializer

package  com.thysmichels.websockets.configuration;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.context.support.GenericWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

public class WebAppInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(final ServletContext context) throws ServletException {

        final AnnotationConfigWebApplicationContext root = new AnnotationConfigWebApplicationContext();

        root.scan("com.thysmichels.websockets");

        context.addListener(new ContextLoaderListener(root));

        final ServletRegistration.Dynamic appServlet = context.addServlet("appServlet",new DispatcherServlet(new GenericWebApplicationContext()));
        appServlet.setAsyncSupported(true);
        appServlet.setLoadOnStartup(1);
        appServlet.addMapping("/*");
    }
}

2. WebConfiguration with EnableScheduling

package  com.thysmichels.websockets.configuration;

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
@EnableWebMvc
@EnableScheduling
public class WebConfiguration extends WebMvcConfigurerAdapter {

    @Override
    public void configureDefaultServletHandling(
        final DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

3. WebSocketConfiguration with EnableWebSocketMessageBroker

package  com.thysmichels.websockets.configuration;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
    }

    @Override
    public void registerStompEndpoints(final StompEndpointRegistry registry) {
        registry.addEndpoint("/metrics").withSockJS();
    }

    @Override
    public void configureClientInboundChannel(final ChannelRegistration registration) {}

    @Override
    public void configureClientOutboundChannel(final ChannelRegistration registration) { }

}

4. Random Number Generator with ApplicationListener

package  com.thysmichels.websockets.utils;

import java.util.Random;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.messaging.core.MessageSendingOperations;
import org.springframework.messaging.simp.broker.BrokerAvailabilityEvent;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class RandomDataGenerator implements ApplicationListener<BrokerAvailabilityEvent> {

    private final MessageSendingOperations<String> messagingTemplate;

    @Autowired
    public RandomDataGenerator(final MessageSendingOperations<String> messagingTemplate) {
        this.messagingTemplate = messagingTemplate;
    }

    @Override
    public void onApplicationEvent(final BrokerAvailabilityEvent event) {
    }

    @Scheduled(fixedDelay = 1000)
    public void sendDataUpdates() {
        this.messagingTemplate.convertAndSend("/data", new Random().nextInt(100));
    }
}

5. Sock.js Javascript

/**
 * Created by tmichels on 8/19/14.
 */

//var stompClient = null;
var updateOpts = {'minVal':'0','maxVal':'100','newVal':'1'};

var randomData;
var socket = new SockJS('/metrics');
var client = Stomp.over(socket);

client.connect('admin', 'password', function(frame) {

    client.subscribe("/data", function(message) {
        var point = [ (new Date()).getTime(), parseInt(message.body) ];
        gaugeUpdate('cf-gauge-1', {'minVal':'0','maxVal':'100','newVal':parseInt(message.body)})
        $('#spark-1').each(function(){

            customSparkOptions = {};
            customSparkOptions.minSpotColor = true;
            var sparkOptions = cf_defaultSparkOpts;
            var sparkOptions = $.extend({}, cf_defaultSparkOpts, customSparkOptions);

            data.push(parseInt(message.body));
            createSparkline($(this), data, sparkOptions);
        });

        $('#metric-1 .metric').html(message.body);
        $('#metric-1 .large').html(message.body);
        $('#metric-2 .metric').html(message.body);
        $('#metric-2 .large').html(message.body);
        var element = $(this).data('update');
        cf_rSVPs[$('#svp-1').attr('id')].chart.update(parseInt(message.body));
        $('#svp-1 .chart').data('percent', parseInt(message.body));
        $('#svp-1 .metric').html(message.body);
       // $('#cf-svmc-sparkline .metric').html(message.body);

        $('#cf-rag-1').each(function(){
            // Dummy data for RAG
            ragData = [60,30,parseInt(message.body)];
            ragLabels = ['Success','Bounce','Abandoned'];
            ragOpts = {postfix:'%'}

            cf_rRags[$(this).prop('id')] = new RagChart($(this).prop('id'), ragData, ragLabels, ragOpts);
        });

        $('#cf-pie-1').each(function(){

            var pdata = [
                {
                    value : parseInt(message.body),
                    color : pieSegColors[3],
                    label: 'Success'
                },
                {
                    value : parseInt(message.body)+50,
                    color : pieSegColors[2],
                    label: 'Bounce'
                },
                {
                    value: parseInt(message.body)+100,
                    color: pieSegColors[1],
                    label: 'Abandoned'
                }
            ]

            var $container = $(this);
            var pId = $container.prop('id');

            // Store chart information
            cf_rPs[pId] = {};
            cf_rPs[pId].data = pdata;


             // Set options per chart
             customOptions = {};
             customOptions.animation = false;
             cf_rPs[pId].options = customOptions;

            // Create chart
            createPieChart($container);
        });

        $('#cf-funnel-1').each(function(){
            funData = [parseInt(message.body)+3000,parseInt(message.body)+1500,parseInt(message.body)+500,parseInt(message.body)+250,parseInt(message.body)];
            funLabels = ['Visits','Cart','Checkout','Purchase','Refund'];
            funOptions = {barOpacity:true, layout:'left'};

            cf_rFunnels[$(this).prop('id')] = new FunnelChart($(this).prop('id'), funData, funLabels, funOptions);
        });

    });
});


Spring MVC AngularJS Address Book

Try it: http://cloudsole-angular.herokuapp.com
Clone it: https://github.com/thysmichels/cloudsole-angular

1. Address Book Model
2. Address Book Service
3. Address Book Service Implementation
4. Address Book Controller
5. Address Book Angular Controller
6. Address Book HTML

1. Address Book Model

package com.cloudsole.angular.model;

/**
 * Created by tmichels on 8/3/14.
 */
public class AddressBook {

    private long id;
    private String firstName;
    private String lastName;
    private String phone;
    private String email;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

2. Address Book Service

package com.cloudsole.angular.service;

import com.cloudsole.angular.model.AddressBook;

import java.util.List;

/**
 * Created by tmichels on 8/3/14.
 */
public interface AddressBookService {
     List<AddressBook> viewAllAddressBook();
     void createAddressBook(AddressBook addressBook);
     void updateAddressBook(int pos, AddressBook updateAddressBook);
     void deleteAddressBook(int id);
     void deleteAllAddressBook();
     AddressBook findAddressBook(int id);
}

3. Address Book Service Implementation

package com.cloudsole.angular.service;

import com.cloudsole.angular.model.AddressBook;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by tmichels on 8/3/14.
 */
@Service
public class AddressBookServiceImpl implements AddressBookService {

    List<AddressBook> addressBooks = new ArrayList<AddressBook>();
    private static Long id = 0L;

    @Override
    public List<AddressBook> viewAllAddressBook() {
        return addressBooks;
    }

    @Override
    public void createAddressBook(AddressBook addressBook) {
        addressBook.setId(id);
        addressBooks.add(addressBook);
        ++id;
    }

    @Override
    public void updateAddressBook(int pos, AddressBook updateAddressBook) {
        addressBooks.set(pos, updateAddressBook);
    }

    @Override
    public void deleteAddressBook(int id) {
        addressBooks.remove(id);
    }

    @Override
    public void deleteAllAddressBook() {
        addressBooks.clear();
        id = 0L;
    }

    @Override
    public AddressBook findAddressBook(int id) {
        return addressBooks.get(id);
    }
}

4. Address Book Controller

package com.cloudsole.angular.controller;

import com.cloudsole.angular.model.AddressBook;
import com.cloudsole.angular.service.AddressBookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * Created by tmichels on 8/3/14.
 */

@Controller
@RequestMapping("/address")
public class AddressBookController {

    @Autowired
    AddressBookService addressBookService;

    @RequestMapping(value = "/all.json", method = RequestMethod.GET)
    public @ResponseBody List<AddressBook> viewAllAddressBook(){
        return addressBookService.viewAllAddressBook();
    }

    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public @ResponseBody void addAddressBookEntry(@RequestBody AddressBook addressBook){
        addressBookService.createAddressBook(addressBook);
    }

    @RequestMapping(value = "/delete/{id}", method = RequestMethod.DELETE)
    public @ResponseBody void deleteAddressBookEntry(@PathVariable("id") String id){
        addressBookService.deleteAddressBook(Integer.valueOf(id));
    }

    @RequestMapping(value = "/update/{pos}", method = RequestMethod.PUT)
    public @ResponseBody void updateAddressBook(@RequestBody AddressBook addressBook, @PathVariable("pos") String pos){
        addressBookService.updateAddressBook(Integer.valueOf(pos), addressBook);
    }

    @RequestMapping(value="/delete/all", method = RequestMethod.DELETE)
    public @ResponseBody void deleteAllAddressBook(){
        addressBookService.deleteAllAddressBook();
    }

    @RequestMapping("/layout")
    public String getTodoPartialPage() {
        return "addressbook/layout";
    }
}

5. Address Book Angular Controller

/**
 * Created by tmichels on 8/3/14.
 */


var AddressBookController = function($scope, $http){

    $scope.editMode = false;
    $scope.position = '';

    $scope.viewAllAddressBook = function(){
        $http.get('address/all.json').success(function(response){
            $scope.addressBooks = response;
        })
    }

    $scope.resetAddressBookField = function(){
        $scope.ab.firstName='';
        $scope.ab.lastName='';
        $scope.ab.phone = '';
        $scope.ab.email = '';
        $scope.editMode = false;
    }

    $scope.addAddressBook = function(ab) {
        $http.post('address/add', ab).success(function(response){
            $scope.viewAllAddressBook();
            $scope.ab.firstName='';
            $scope.ab.lastName='';
            $scope.ab.phone = '';
            $scope.ab.email = '';
        }).error(function(response){
            console.log(response);
        })
    }

    $scope.updateAddressBook = function(ab) {
        $http.put('address/update/'+$scope.position, ab).success(function(response){
            $scope.ab.firstName='';
            $scope.ab.lastName='';
            $scope.ab.phone = '';
            $scope.ab.email = '';
            $scope.viewAllAddressBook();
            $scope.editMode = false;
        }).error(function(response){
            console.log(response);
        })
    }

    $scope.deleteAddressBook = function(id) {
        $http.delete('address/delete/' + id).success(function(response){
            $scope.viewAllAddressBook();
        }).error(function(response){
            console.log(response);
        })
    }

    $scope.deleteAllAddressBook = function(){
        $http.delete('address/delete/all').success(function(response){
            $scope.viewAllAddressBook();
        })
    }

    $scope.editAddressBook = function(pos, addressBook){
        $scope.position = pos;
        $scope.ab = addressBook;
        $scope.editMode = true;
    }

    $scope.viewAllAddressBook();
}

6. Address Book HTML

<div class="alert alert-error" ng-show="error">{{errorMessage}}</div>
<div class="row">
    <form ng-submit="addAddressBook(ab)">
        <div class="col-lg-8">
            <input class="form-control" placeholder="Enter First Name" type="text" ng-model="ab.firstName" required min="1" />
            <input class="form-control" placeholder="Enter Last Name" type="text" ng-model="ab.lastName" required min="1" />
            <input class="form-control" placeholder="Enter Phone" type="text" ng-model="ab.phone" required min="1" />
            <input class="form-control" placeholder="Enter Email" type="text" ng-model="ab.email" required min="1" />
        </div>
    </form>

    <button class="btn btn-primary" ng-disabled="!ab" ng-hide="editMode" ng-click="addAddressBook(ab)">Add Entry</button>
    <button type="btn btn-primary" class="btn btn-primary"
            ng-disabled="!ab" ng-show="editMode"
            ng-click="updateAddressBook(ab)">Save</button>
    <button type="btn btn-primary" class="btn btn-primary" ng-click="resetAddressBookField()">Reset</button>
</div>
<hr />

<div class="row">
    <div class="col-lg-8">
        <div class="form-group">
            <div class="input-group">
                <input type="text" class="form-control" placeholder="Search" id="search-query-3" ng-model="searchAddressBook">
                  <span class="input-group-btn">
                    <button type="submit" class="btn"><span class="fui-search"></span></button>
                  </span>
            </div>
        </div>
    </div>
</div>
<hr />

<div class="alert alert-info" style="width:400px;margin-left:100px;" ng-show="addressBooks.length == 0">
    No address book entry found
</div>
<table class="table table-bordered table-striped" ng-show="addressBooks.length > 0">
    <thead>
    <tr>
        <th style="text-align: center; width: 25px;">Delete</th>
        <th style="text-align: center; width: 25px;">Update</th>
        <th style="text-align: center;">First Name</th>
        <th style="text-align: center;">Last Name</th>
        <th style="text-align: center;">Phone Number</th>
        <th style="text-align: center;">Email</th>
    </tr>
    </thead>
    <tbody>
    <tr ng-repeat="addressBook in addressBooks">
        <td  style="width:70px;text-align:center;"><button class="btn btn-mini btn-danger" ng-click="deleteAddressBook(addressBooks.indexOf(addressBook))">Delete</button></td>
        <td  style="width:70px;text-align:center;"><button class="btn btn-mini btn-danger" ng-click="editAddressBook(addressBooks.indexOf(addressBook), addressBook)">Update</button></td>
        <td>{{addressBook.firstName}}</td><td>{{addressBook.lastName}}</td><td>{{addressBook.phone}}</td><td>{{addressBook.email}}</td>
    </tr>
    </tbody>
</table>
<button class="btn btn-danger"  ng-show="addressBooks.length >= 1" ng-click="deleteAllAddressBook()">Delete All Address Book</button>

Activiti REST Client Example

Activiti REST Client

package com.nuke.activiti.client;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONStringer;
import org.restlet.data.ChallengeScheme;
import org.restlet.data.MediaType;
import org.restlet.ext.json.JsonRepresentation;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
import org.restlet.resource.ClientResource;
import org.slf4j.Logger;

/**
 * @author tmichels
 */
public class ActivitiClient {

	private static final Logger logger = org.slf4j.LoggerFactory.getLogger(NucleusClient.class);
	
	private static final String REST_URI = "http://activiti-dev.com:8090/api/activiti/v1";
	private static final String LDAP_USERNAME="xxxx";
	private static final String LDAP_PASSWORD="xxxx";
	
	private static ClientResource getClientResource(String uri) throws IOException{
		ClientResource clientResource = new ClientResource(uri);
		clientResource.setChallengeResponse(ChallengeScheme.HTTP_BASIC, LDAP_USERNAME, LDAP_PASSWORD);
        return clientResource;
	}
	
	public static List<String> getQueueNames(String username) throws JSONException, IOException{
		String authenticateUserUri = REST_URI + "/identity/users/"+username;
		Representation authenticateUserJSON = getClientResource(authenticateUserUri).get(MediaType.APPLICATION_JSON);
		JSONObject authenticateObject = new JSONObject(authenticateUserJSON.getText());
		logger.info("Get Queue Name: " + authenticateObject);
		if (!authenticateObject.opt("id").equals(null)) {
			List<String> queueForUser = new ArrayList<String>();
			String uri = REST_URI +"/repository/process-definitions";
			Representation response = getClientResource(uri).get(MediaType.APPLICATION_JSON);
			JSONObject object = new JSONObject(response.getText());
			JSONArray arr = (JSONArray) object.get("data");
			for (int i = 0; i < arr.length(); i++) {
				JSONObject ob = (JSONObject) arr.get(i);
				queueForUser.add(ob.get("name").toString());
			}
			return queueForUser;
		}
		return null;
	}

	public static String claimAndCompleteTaskByProcessIdAndActorName(String processInstanceId, String actorName) throws JSONException, IOException {
        try {
            String authenticateUserUri = REST_URI + "/identity/users/"+actorName;
            Representation authenticateUserJSON = getClientResource(authenticateUserUri).get(MediaType.APPLICATION_JSON);
            JSONObject authenticateObject = new JSONObject(authenticateUserJSON.getText());
            logger.info("AuthenticateObject: " + authenticateObject);
            if (!authenticateObject.opt("id").equals(null)) {
                String getTaskUri = REST_URI + "/runtime/tasks?candidate=" + actorName + "&size=1&order=asc&taskDefinitionKey=UnassignedLoan";
                Representation response = getClientResource(getTaskUri).get(MediaType.APPLICATION_JSON);
                JSONObject object = new JSONObject(response.getText());
                if (object != null) {
                    JSONArray arr = (JSONArray) object.get("data");
                    for (int i = 0; i < arr.length(); i++) {
                        JSONObject ob = (JSONObject) arr.get(i);
                        if (processInstanceId != null) {
                            if (ob.get("processDefinitionId").equals(processInstanceId)) {
                                logger.info("Returned task: " + ob);
                                if (ob.get("id") != null) {
                                    String claimUri = REST_URI + "/runtime/tasks/" + ob.get("id");
                                    logger.info("Claim URI: " + claimUri);
                                    JSONStringer jsAssignee = new JSONStringer();
                                    jsAssignee.object().key("assignee").value(actorName);
                                    jsAssignee.key("owner").value(actorName);
                                    jsAssignee.endObject();
                                    logger.info("jsAssignee: " + jsAssignee.toString());
                                    
                                    JSONStringer jsClaim = new JSONStringer();
                                    jsClaim.object().key("action").value("claim");
                                    jsClaim.endObject();
                                    logger.info("jsClaim: " + jsClaim.toString());
                                    
                                    JsonRepresentation jsonAssigneeRepresentation = new JsonRepresentation(jsAssignee);
                                    logger.info("jsonAssigneeRepresentation: " + jsonAssigneeRepresentation.getText());
                                    
                                    JsonRepresentation jsonClaimRepresentation = new JsonRepresentation(jsClaim);
                                    logger.info("jsonClaimRepresentation: " + jsonClaimRepresentation.getText());
                                    
                                    Representation assigneeResponse = getClientResource(claimUri).put(jsonAssigneeRepresentation);
                                    Representation claimResponse = getClientResource(claimUri).post(jsonClaimRepresentation);
                                    	
                                    logger.info("Assignee Response: " + assigneeResponse.getText());
                                    
                                    if (claimResponse.getText() == null) {
                                    	boolean completeTask = NucleusClient.completeTaskByIdWithVariables(ob.getString("description").toString(), actorName, null);
                                        logger.info("Complete Response: "  + completeTask);
                                        if (completeTask) {
                                        	 JSONObject taskByLoan = getTaskByLoanId(ob.getString("description").toString());
                                        	 JSONArray taskByLoanArray = (JSONArray) taskByLoan.get("data");
                                        	 if (!taskByLoanArray.isNull(0)) {
                                        		 JSONObject obTask = (JSONObject) taskByLoanArray.get(0);
                                            	 JSONStringer jsAssigneeAfterComplete = new JSONStringer();
                                            	 jsAssigneeAfterComplete.object().key("assignee").value(actorName);
                                            	 jsAssigneeAfterComplete.key("owner").value(actorName);
                                            	 jsAssigneeAfterComplete.endObject();
                                                 JsonRepresentation jsonAssigneeAfterCompleteRepresentation = new JsonRepresentation(jsAssigneeAfterComplete);
                                                 String claimAfterCompleteUri = REST_URI + "/runtime/tasks/" + obTask.get("id");
                                                 getClientResource(claimAfterCompleteUri).put(jsonAssigneeAfterCompleteRepresentation);
                                        	 }
                                            return ob.getString("description");
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                return null;
            }
            return null;
        }catch (Exception e){
        	logger.error("claimAndCompleteTaskByProcessIdAndActorName", e);
            return null;
        }
	}
	
	public static JSONObject getTaskByLoanId(String loanId) throws JSONException, IOException {
		String getTaskByLoanId = REST_URI +"/runtime/tasks?description=" + loanId + "&size=1";
        Representation response = getClientResource(getTaskByLoanId).get(MediaType.APPLICATION_JSON);
        JSONObject object = new JSONObject(response.getText());
        return object;
	}
	
	public static boolean completeTaskByIdWithVariables(String loanId, String actorName, Map<String, String> completeVariables) throws Exception {
        try {
            JSONObject loanjsonObjs = getTaskByLoanId(loanId);
            JSONArray loanArr = (JSONArray) loanjsonObjs.get("data");
            logger.info("LoanArr: " + loanArr);
            if (!loanArr.isNull(0) && loanArr.getJSONObject(0).has("id")) {
              JSONObject loanjsonObj = loanArr.getJSONObject(0);
          	  String uri = REST_URI + "/runtime/tasks/" + loanjsonObj.get("id").toString();

                JSONStringer jsComplete = new JSONStringer();
                jsComplete.object().key("action").value("complete");
              
                if (completeVariables!=null){
                    jsComplete.key("variables").array().object();
                    for (String completeVariable : completeVariables.keySet()) {
                        jsComplete.key("name").value(completeVariable).key("value").value(completeVariables.get(completeVariable));
                    }
                    jsComplete.endObject().endArray();
                }

                jsComplete.endObject();

                JsonRepresentation jsonCompleteRepresentation = new JsonRepresentation(jsComplete);
                logger.info("jsonCompleteRepresentation: " + jsonCompleteRepresentation.getText());
                if(completeVariables==null || !(loanArr.getJSONObject(0).getString("taskDefinitionKey").equals("SaveForLater") && completeVariables.get("disposition").equals("saveforlater"))) {
                	Representation completeResponse = getClientResource(uri).post(jsonCompleteRepresentation, MediaType.APPLICATION_JSON);
                	logger.info("Complete Response: " + completeResponse.getText());
                    if (completeResponse.getText()==null){
                    	if (completeVariables!=null && !completeVariables.get("disposition").equals("incomplete")) {
                    		 JSONObject taskByLoan = getTaskByLoanId(loanId);
                        	 JSONArray taskByLoanArray = (JSONArray) taskByLoan.get("data");
                        	 
                        	 if (!taskByLoanArray.isNull(0)) {
                        		 JSONObject obTask = (JSONObject) taskByLoanArray.get(0);
                         
                            	 JSONStringer jsAssigneeAfterComplete = new JSONStringer();
                            	 jsAssigneeAfterComplete.object().key("assignee").value(actorName);
                            	 jsAssigneeAfterComplete.key("owner").value(actorName);
                            	 jsAssigneeAfterComplete.endObject();
                                
                                 JsonRepresentation jsonAssigneeAfterCompleteRepresentation = new JsonRepresentation(jsAssigneeAfterComplete);
                                 String claimAfterCompleteUri = REST_URI + "/runtime/tasks/" + obTask.get("id");
                                 getClientResource(claimAfterCompleteUri).put(jsonAssigneeAfterCompleteRepresentation);
                        	 }
                    	}
                        return true;
                    }
                } else {
                	return true;
                }
            }
        }catch (Exception e){
          	e.printStackTrace();
              
        }
        return false;
	}
	
	public static String getProcessIdByName(String queueName) throws IOException, JSONException {
		String uri = REST_URI + "/repository/process-definitions";
		Representation response = getClientResource(uri).get(MediaType.APPLICATION_JSON);
		JSONObject object = new JSONObject(response.getText());
		if (object != null) {
			JSONArray arr = (JSONArray) object.get("data");
			for (int i = 0; i < arr.length(); i++) {
				JSONObject jsonObject = (JSONObject) arr.get(i);
				if (jsonObject.get("key").equals(queueName)) {
					logger.info("Returning processDefinitionId " + jsonObject.get("id"));
					return (String) jsonObject.get("id");
				}
			}
		}
		return null;
	}

	public static String createTaskByProcessId(String processDefinitionId, Map<String, String> taskVariables) throws Exception {
		String uri = REST_URI + "/runtime/process-instances";
		JSONStringer jsRequest = new JSONStringer();
		jsRequest.object().key("processDefinitionId").value(processDefinitionId).key("variables").array().object();
		for (String taskVariable : taskVariables.keySet()) {
			jsRequest.key("name").value(taskVariable).key("value").value(taskVariables.get(taskVariable));
		}
		jsRequest.endObject().endArray();
		jsRequest.endObject();
		Representation rep = new StringRepresentation(jsRequest.toString(), MediaType.APPLICATION_JSON);
		JSONObject jsObj = new JSONObject(getClientResource(uri).post(rep).getText());
		logger.info("Returned process: " + jsObj);
		if (jsObj.has("id")) {
			return  jsObj.getString("id");
		} else {
			return null;
		}
	}

	public static String getTaskIdByProcessIdAndActorName(String processInstanceId, String actorName) throws Exception {
		String uri = REST_URI + "/runtime/tasks?candidate=" +actorName;
		Representation response = getClientResource(uri).get(MediaType.APPLICATION_JSON);
		JSONObject object = new JSONObject(response.getText());
		if (object != null) {
			JSONArray arr = (JSONArray) object.get("data");
			for (int i = 0; i < arr.length(); i++) {
				JSONObject ob = (JSONObject) arr.get(i);
				if (ob.get("processDefinitionId").equals(processInstanceId)) {
					logger.info("Returned task: " + ob);
					logger.info("Returning taskId " + ob.get("id"));
					return (String) ob.get("id");
				}
			}
		}
		return null;
	}

	public static boolean claimTaskByIdAndActorName(String taskId, String actorName) throws Exception {
		String uri = REST_URI + "/runtime/tasks/" + taskId;

        JSONStringer jsClaim = new JSONStringer();
        jsClaim.object().key("action").value("claim");
        jsClaim.endObject();
        logger.info("jsClaim: " + jsClaim.toString());

        JsonRepresentation jsonClaimRepresentation = new JsonRepresentation(jsClaim);
        logger.info("jsonClaimRepresentation: " + jsonClaimRepresentation.getText());
		Representation claimResponse = getClientResource(uri).post(jsonClaimRepresentation, MediaType.APPLICATION_JSON);

        JSONStringer jsAssignee = new JSONStringer();
        jsAssignee.object().key("assignee").value(actorName);
        jsAssignee.key("owner").value(actorName);
        jsAssignee.endObject();
        logger.info("jsAssignee: " + jsAssignee.toString());
        
        JsonRepresentation jsonAssignRepresentation = new JsonRepresentation(jsAssignee);

        getClientResource(uri).put(jsonAssignRepresentation, MediaType.APPLICATION_JSON);

        return claimResponse.getText()==null;
	}


	@SuppressWarnings("unused")
	public static int getTaskCountByActorNameAndStatus(String actorName, String status) throws Exception {
		String uri = REST_URI + "/runtime/tasks?candidate=" + actorName + "&status=" + status;
		Representation response = getClientResource(uri).get(MediaType.APPLICATION_JSON);
		JSONObject object = new JSONObject(response.getText());
		if (object != null) {
			JSONArray arr = (JSONArray) object.get("data");
			logger.info("Tasklist " + actorName + " " + status + " size "	+ arr.length());
			return arr.length();
		}
		return -1;
	}
	
	public static List<String> taskQueryByActorNameBySizeAndOrder(String actorName,int size, String order, String status) throws Exception {
		List<String> taskForActor = new ArrayList<String>();
		String url = REST_URI + "/runtime/tasks?assignee="+ actorName +"&size=" + size + "&order="+order +"&status="+status;
		Representation response = getClientResource(url).get(MediaType.APPLICATION_JSON);
		if (response != null) {
			JSONObject object = new JSONObject(response.getText());
			if (object != null) {
				JSONArray arr = (JSONArray) object.get("data");
				for (int i = 0; i < arr.length(); i++) {
					JSONObject ob = (JSONObject) arr.get(i);
					taskForActor.add(ob.toString());
				}
			}
		}
		return taskForActor;
	}
}

Activiti REST Client Test

package com.nuke.activiti.client;

import static org.junit.Assert.*;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.json.JSONException;
import org.junit.Test;

import com.nuke.activiti.common.AbstractTest;

/**
 * @author tmichels
 */
public class ActivitiClientTest extends AbstractTest {
	
	@Test
	public void testGetQueueNamesAdmin() throws IOException, JSONException {
        List<String> queueNames = NucleusClient.getQueueNames("qatester_tools");
        for (String queueName : queueNames) {
            System.out.println(queueName);
        }
        assertNotNull(queueNames);
	}
	
	
	@Test
	public void testGetQueueNames() throws JSONException, IOException {
		List<String> queueNames = NucleusClient.getQueueNames("qatester_tools");
		for (String queueName : queueNames) {
			System.out.println(queueName);
		}
		assertNotNull(queueNames);
	}
	
	@Test
	public void testGetQueueTestEdgarNames() throws JSONException, IOException {
		List<String> queueNames = NucleusClient.getQueueNames("qatester_tools");
		for (String queueName : queueNames) {
			System.out.println(queueName);
		}
		assertNotNull(queueNames);
	}
	
	@Test
	public void testNewDiagram() throws IOException, JSONException {
		assertNotNull(NucleusClient.getProcessIdByName("underwritingQueue"));
		System.out.println("OUTPUT: " + NucleusClient.getProcessIdByName("bankVerificationQueue"));
		assertNotNull(NucleusClient.getProcessIdByName("bankVerificationQueue"));
	}
	
	
	@Test
	public void testWithUserInSameLDAPGroup() throws Exception {
		assertNotNull(NucleusClient.getTaskIdByProcessIdAndActorName(NucleusClient.getProcessIdByName("underwritingQueue"), "qatester_tools"));
	}
	
	@Test
	public void testGetQueueNamesNonReturn() throws JSONException, IOException {
		List<String> queueNames = NucleusClient.getQueueNames("dummy");
		assertNull(queueNames);
	}

    @Test
    public void testLoanComplete() throws Exception {
        String underwritingQueueProcessId = NucleusClient.getProcessIdByName("underwritingQueue");
        Map<String, String> processVariables = new HashMap<String, String>();
        processVariables.put("LoanId", "1");
        String processInstanceId = NucleusClient.createTaskByProcessId(underwritingQueueProcessId, processVariables);
        assertNotNull(processInstanceId);
        assertNotNull(NucleusClient.taskQueryByActorNameBySizeAndOrder("qatester_admin", 1, "asc", "open"));
        Map<String, String> completeVariables = new HashMap<String, String>();
        completeVariables.put("disposition", "complete");
        assertTrue(NucleusClient.completeTaskByIdWithVariables("1", "qatester_tools",completeVariables));
    }

 
    @Test
    public void testLoanInComplete() throws Exception{
        String underwritingQueueProcessId = NucleusClient.getProcessIdByName("underwritingQueue");
        Map<String, String> processVariables = new HashMap<String, String>();
        processVariables.put("LoanId", "2");
        NucleusClient.createTaskByProcessId(underwritingQueueProcessId, processVariables);
        assertTrue(NucleusClient.completeTaskByIdWithVariables("2", "qatester_admin", null));
        assertNotNull(NucleusClient.taskQueryByActorNameBySizeAndOrder("qatester_admin", 1, "asc", "open"));
        Map<String, String> completeVariables = new HashMap<String, String>();
        completeVariables.put("disposition", "incomplete");
        assertTrue(NucleusClient.completeTaskByIdWithVariables("2","qatester_tools", completeVariables));
        assertNotNull(NucleusClient.taskQueryByActorNameBySizeAndOrder("qatester_admin", 1, "asc", "open"));
    }

    @Test
    public void testSaveForLater() throws Exception{
        String underwritingQueueProcessId = NucleusClient.getProcessIdByName("underwritingQueue");
        Map<String, String> processVariables = new HashMap<String, String>();
        processVariables.put("LoanId", "3");
        String processInstanceId = NucleusClient.createTaskByProcessId(underwritingQueueProcessId, processVariables);
        System.out.println(processInstanceId);
        assertTrue(NucleusClient.completeTaskByIdWithVariables("3","qatester_tools", null));
        assertNotNull(NucleusClient.taskQueryByActorNameBySizeAndOrder("qatester_admin", 1, "asc", "open"));
        Map<String, String> completeVariables = new HashMap<String, String>();
        completeVariables.put("disposition", "saveforlater");
        assertTrue(NucleusClient.completeTaskByIdWithVariables("3", "qatester_tools",completeVariables));
    }


	@Test
	public void endToEndTest() throws Exception {
		String underwritingQueueProcessId = NucleusClient.getProcessIdByName("underwritingQueue");
		assertNotNull(underwritingQueueProcessId);
		Map<String, String> processVariables = new HashMap<String, String>();
		processVariables.put("LoanId", "00000000000");
		String processInstanceId = NucleusClient.createTaskByProcessId(underwritingQueueProcessId, processVariables);
		System.out.println("processInstanceId: " + underwritingQueueProcessId);
		assertNotNull(processInstanceId);
		String taskId = NucleusClient.getTaskIdByProcessIdAndActorName(NucleusClient.getProcessIdByName("underwritingQueue"), "qatester_tools");
		assertNotNull(taskId);
		assertNotNull(NucleusClient.claimTaskByIdAndActorName(taskId, "qatester_tools"));

		assertNotNull(NucleusClient.completeTaskByIdWithVariables(taskId,"qatester_tools", null));

		assertNotNull(NucleusClient.getTaskCountByActorNameAndStatus("qatester_tools", "closed"));

        Map<String, String> completeVariables = new HashMap<String, String>();
        completeVariables.put("disposition", "saveforlater");
        NucleusClient.completeTaskByIdWithVariables(taskId,"qatester_tools", completeVariables);
        assertNotNull(NucleusClient.getTaskCountByActorNameAndStatus("qatester_tools", "closed"));
	}
	
	
	@Test
	public void testUserInSameLDAPGroup() throws Exception {
		assertNotNull(NucleusClient.getTaskIdByProcessIdAndActorName(NucleusClient.getProcessIdByName("underwritingQueue"), "qatester_tools"));
	}
	
	
	@Test
	public void testGetProcessIdByName() throws Exception {
		String queueProcessId = NucleusClient.getProcessIdByName("underwritingQueue");
		assertNotNull(queueProcessId);
	}
	
	@Test
	public void testCreateTaskByProcessId() throws Exception {
		String underwritingQueueProcessId = NucleusClient.getProcessIdByName("underwritingQueue");
		assertNotNull(underwritingQueueProcessId);
		Map<String, String> processVariables = new HashMap<String, String>();
		processVariables.put("LoanId", "332233555");
		String createComplete = NucleusClient.createTaskByProcessId(underwritingQueueProcessId, processVariables);
		System.out.println("CREATE COMPLETE: " + createComplete);
		assertNotNull(createComplete);
	}
	
	@Test
	public void testTaskByActorName() throws Exception {
		String processId = NucleusClient.getProcessIdByName("underwritingQueue");
		String taskId = NucleusClient.getTaskIdByProcessIdAndActorName(processId, "qatester_tools");
		assertNotNull(taskId);
	}
	
	@Test
	public void testClaimTaskById() throws Exception {
		String processId = NucleusClient.getProcessIdByName("underwritingQueue");
		String taskId = NucleusClient.getTaskIdByProcessIdAndActorName(processId, "qatester_tools");
		assertNotNull(NucleusClient.claimTaskByIdAndActorName(taskId, "qatester_tools"));
	}
	
	@Test
	public void testCompleteTaskById() throws Exception {
		String processId = NucleusClient.getProcessIdByName("underwritingQueue");
		Map<String, String> processVariables = new HashMap<String, String>();
		processVariables.put("LoanId", "3333333333");
		NucleusClient.createTaskByProcessId(processId, processVariables);
		String taskId = NucleusClient.getTaskIdByProcessIdAndActorName(processId, "qatester_tools");
		Map<String, String> completeVariables = new HashMap<String, String>();
		completeVariables.put("disposition", "complete");
		assertNotNull(NucleusClient.completeTaskByIdWithVariables(taskId,"qatester_tools", completeVariables));
	}
	
	@Test
	public void testIncompleteTaskById() throws Exception{
		String processId = NucleusClient.getProcessIdByName("underwritingQueue");
		Map<String, String> processVariables = new HashMap<String, String>();
		processVariables.put("LoanId", "4444444444");
		NucleusClient.createTaskByProcessId(processId, processVariables);
		String taskId = NucleusClient.getTaskIdByProcessIdAndActorName(processId, "qatester_tools");
		Map<String, String> completeVariables = new HashMap<String, String>();
		completeVariables.put("disposition", "incomplete");
		assertNotNull(NucleusClient.completeTaskByIdWithVariables(taskId,"qatester_tools", completeVariables));
	}
	
	@Test
	public void testSaveForLaterTaskById() throws Exception {
		String processId = NucleusClient.getProcessIdByName("underwritingQueue");
		Map<String, String> processVariables = new HashMap<String, String>();
		processVariables.put("LoanId", "5555555555");
		String creatProcessId = NucleusClient.createTaskByProcessId(processId, processVariables);
		System.out.println("CreatedProcessId: " + creatProcessId);
		String taskId = NucleusClient.getTaskIdByProcessIdAndActorName(processId, "qatester_tools");
		Map<String, String> completeVariables = new HashMap<String, String>();
		completeVariables.put("disposition", "saveforlater");
		assertNotNull(NucleusClient.completeTaskByIdWithVariables(taskId,"qatester_tools", completeVariables));
	}
	
	@Test
	public void testGetTaskCountByActorNameAndStatus() throws Exception {
		int taskCount = NucleusClient.getTaskCountByActorNameAndStatus("qatester_tools", "open");
		assertNotNull(taskCount);
	}
	
	@Test
	public void testTaskQueryForActorNameBySizeAndOrder() throws Exception {
		List<String> taskIds = NucleusClient.taskQueryByActorNameBySizeAndOrder("qatester_tools", 5, "asc", "open");
		for (String taskId : taskIds) {
			System.out.println("TASKID: " + taskId);
		}
		assertNotNull(taskIds);
	}
	
	@Test
	public void testGetTaskClaimAndCompleteTaskByProcessIdAndActorName() throws JSONException, IOException {
		assertNotNull(NucleusClient.claimAndCompleteTaskByProcessIdAndActorName(NucleusClient.getProcessIdByName("underwritingQueue"), "qatester_tools"));
	}

    @Test
    public void testGetTaskClaimAndCompleteTaskByProcessIdAndActorNameWithWrongActorName() throws Exception{
        assertNull(NucleusClient.claimAndCompleteTaskByProcessIdAndActorName(NucleusClient.getProcessIdByName("underwritingQueue"), "dummy"));
    }

    @Test
    public void testGetTaskClaimAndCompleteTaskByProcessIdAndActorNameWithWrongProcessName() throws Exception{
        assertNull(NucleusClient.claimAndCompleteTaskByProcessIdAndActorName(NucleusClient.getProcessIdByName("dummy"), "dummy"));
    }


    @Test
    public void testProccessIdReturnNullWhenInvalid() throws IOException, JSONException {
        assertNull(NucleusClient.getProcessIdByName("dummy"));
    }

	@Test
	public void testGetTaskByLoanId() throws JSONException, IOException {
		assertNotNull(NucleusClient.getTaskByLoanId("259190367").getJSONArray("data"));
	}
	
	@Test
	public void testSaveForlater() throws Exception {
		Map<String, String> completeVariables = new HashMap<String, String>();
		completeVariables.put("disposition", "complete");
		NucleusClient.completeTaskByIdWithVariables("685363", "qatester_tools", completeVariables);
	}
	
	@Test
	public void testSaveForLaterIfAlreadySaveForLater() throws Exception {
		String processId = NucleusClient.getProcessIdByName("underwritingQueue");
		String taskId = NucleusClient.getTaskIdByProcessIdAndActorName(processId, "qatester_tools");
		System.out.println("TaskId: " + taskId);
		Map<String, String> completeVariables = new HashMap<String, String>();
		completeVariables.put("disposition", "incomplete");
		assertNotNull(NucleusClient.completeTaskByIdWithVariables("1111111111","qatester_tools", completeVariables));
	}
}

Integrating Activiti with Active Directory

Integrating Activiti with LDAP can be tricky. Through trail and error I got Active Directory working with Activit. The configuration may not be exactly the same for your organization, all depends on how your LDAP is setup.

package com.nuke.ldap;

import java.util.HashMap;
import java.util.Map;

import org.activiti.ldap.LDAPConfigurator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import com.nuke.activiti.annotations.ActivitiProd;

@Configuration
@ActivitiProd
public class LDAPProdConfiguration {

	@Value("${LDAP_URL:}")
	private String ldapUrl;

	@Value("${LDAP_SERVICE_ACCOUNT_NAME:}")
    private String ldapServiceAccountUserName;

	@Value("${LDAP_SERVICE_ACCOUNT_CN:}")
    private String ldapServiceAccountUserNameCn;
    
	@Value("${LDAP_SERVICE_ACCOUNT_PASSWORD:}")
    private String ldapServiceAccountPassword;

	@Value("${LDAP_SEARCH_FILTER:}")
    private String ldapSearchFilter;

	@Bean(name="ldapProd")
	public LDAPConfigurator LDAProdPConfig(){
		LDAPConfigurator ldapProdConfig = new LDAPConfigurator();
		ldapProdConfig.setServer(ldapUrl);
		ldapProdConfig.setUser(ldapServiceAccountUserNameCn);
		ldapProdConfig.setPassword(ldapServiceAccountPassword);
		
		ldapProdConfig.setBaseDn("OU=Security Groups,DC=Corp,DC=internal,DC=us"); 
		ldapProdConfig.setUserBaseDn("OU=Service Accounts,DC=Corp,DC=internal,DC=us");
		ldapProdConfig.setGroupBaseDn("OU=Security Groups,DC=Corp,DC=internal,DC=us");
		
		ldapProdConfig.setQueryUserByUserId("(&(objectClass=user)(sAMAccountName={0}))");
		ldapProdConfig.setQueryUserByFullNameLike("(&(objectClass=user)(|({0}=*{1}*)({2}=*{3}*)))");
		ldapProdConfig.setQueryGroupsForUser("(&(objectClass=group)(member={0}))");
		
		Map<String, String> connectionMap = new HashMap<String, String>();
		connectionMap.put("InitialDirContext", "Context.REFERRAL");
		ldapProdConfig.setCustomConnectionParameters(connectionMap);
		
		ldapProdConfig.setUserIdAttribute("cn");
		ldapProdConfig.setUserFirstNameAttribute("givenName");
		ldapProdConfig.setUserLastNameAttribute("sn");
		
		ldapProdConfig.setGroupIdAttribute("sAMAccountName");
		ldapProdConfig.setGroupNameAttribute("sAMAccountName");
		
		return ldapProdConfig;
	}
}

Dropbox OAuth 2.0 Scribe Java Example

Dropbox OAuthServiceConfig and OAuthServiceProvider Bean

<bean id="dropboxServiceConfig" class="com.example.oauth.OAuthServiceConfig">
		<constructor-arg value="xxx" />
		<constructor-arg value="xxx"/>
		<constructor-arg value="https://www.example.com/oauth/dropbox"/>
		<constructor-arg value="com.example.oauth.DropBoxApi20"/>
	</bean>
	
	<bean id="dropboxServiceProvider" class="com.example.oauth.OAuthServiceProvider">
		<constructor-arg name="config" ref="dropboxServiceConfig" />
	</bean>

Dropbox OAuth Spring MVC Controller

package com.example.oauth.controller;

import static org.springframework.web.context.request.RequestAttributes.SCOPE_SESSION;

import java.util.Map;

import javax.servlet.http.HttpSession;

import org.scribe.model.Token;
import org.scribe.model.Verifier;
import org.scribe.oauth.OAuthService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.WebRequest;

import com.example.oauth.OAuthServiceProvider;

@Controller
@RequestMapping("/oauth/dropbox")
public class DropboxController {
	
	@Autowired
	@Qualifier("dropboxServiceProvider")
	private OAuthServiceProvider dropboxServiceProvider;
	
	private static final Token EMPTY_TOKEN = null;
	
	@RequestMapping(value = "/login-dropbox", method = RequestMethod.GET)
	public String loginToDropBox(Map<String, Object> map, WebRequest request) {
		OAuthService service = dropboxServiceProvider.getService();
		String authUrl = service.getAuthorizationUrl(EMPTY_TOKEN);
		System.out.println("RequestToken: " + authUrl);
		return "redirect:" + authUrl;
	}

	@RequestMapping(value = { "" }, method = RequestMethod.GET)
	public String callback(
			@RequestParam(value = "oauth_token", required = false) String oauthToken,
			@RequestParam(value = "code", required = false) String oauthVerifier,
			WebRequest request, Map<String, Object> map) {

		OAuthService service = dropboxServiceProvider.getService();

		// getting access token
		Verifier verifier = new Verifier(oauthVerifier);
		Token accessToken = service.getAccessToken(EMPTY_TOKEN, verifier);

		// store access token as a session attribute
		request.setAttribute("oauthAccessToken", accessToken, SCOPE_SESSION);

		ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder
				.currentRequestAttributes();
		HttpSession session = attr.getRequest().getSession(false); // create a
																	// new
																	// session
		session.setAttribute("accessToken", accessToken);

		return "settings";
	}
}

DropBoxApi20 extends DefaultApi20

package com.example.oauth;

import org.scribe.builder.api.DefaultApi20;
import org.scribe.extractors.AccessTokenExtractor;
import org.scribe.extractors.JsonTokenExtractor;
import org.scribe.model.OAuthConfig;
import org.scribe.model.Verb;

public class DropBoxApi20 extends DefaultApi20 {

	@Override
	public String getAccessTokenEndpoint() {
		return "https://api.dropbox.com/1/oauth2/token?grant_type=authorization_code";
	}

	@Override
	public String getAuthorizationUrl(OAuthConfig config) {
		// TODO Auto-generated method stub
		return String.format("https://www.dropbox.com/1/oauth2/authorize?client_id=%s&response_type=code&redirect_uri=%s", config.getApiKey(), config.getCallback());
	}

	 @Override
	 public Verb getAccessTokenVerb(){
	       return Verb.POST;
	 }
	 
	@Override
    public AccessTokenExtractor getAccessTokenExtractor() {
        return new JsonTokenExtractor();
    }
}

Google OAuth 2.0 Scribe Java Example

Google OAuthServiceConfig and OAuthServiceProvider Bean

<bean id="gdServiceConfig" class="com.example.oauth.OAuthServiceConfig">
		<constructor-arg value="xxx.apps.googleusercontent.com" />
		<constructor-arg value="xxx"/>
		<constructor-arg value="https://www.example.com/oauth/gd"/>
		<!-- <constructor-arg value="openid profile email https://www.googleapis.com/auth/drive.file"/> -->
		<constructor-arg value="com.example.oauth.GoogleDriveOauthApi"/>
	</bean>
	<bean id="gdServiceProvider" class="com.example.oauth.OAuthServiceProvider">
		<constructor-arg name="config" ref="gdServiceConfig" />
	</bean>
</bean>

Google OAuth Spring MVC Controller

package com.example.oauth.controller;

import static org.springframework.web.context.request.RequestAttributes.SCOPE_SESSION;

import java.util.Map;

import javax.servlet.http.HttpSession;

import org.scribe.model.Token;
import org.scribe.model.Verifier;
import org.scribe.oauth.OAuthService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.WebRequest;

import com.example.oauth.OAuthServiceProvider;

@Controller
@RequestMapping("/oauth/gd")
public class GoogleDriveController {

	@Autowired
	@Qualifier("gdServiceProvider")
	private OAuthServiceProvider gdServiceProvider;
	
	private static final Token EMPTY_TOKEN = null;
	
	@RequestMapping(value = "/login-gd", method = RequestMethod.GET)
	public String loginToGoogleDrive(Map<String, Object> map, WebRequest request) {
		OAuthService service = gdServiceProvider.getService();
		String auth = service.getAuthorizationUrl(EMPTY_TOKEN);
		System.out.println("RequestToken: " + auth);
		return "redirect:" + auth;
	}
	
	@RequestMapping(value={""}, method = RequestMethod.GET)
	public String callback(@RequestParam(value="oauth_token", required=false) String oauthToken,
			@RequestParam(value="code", required=false) String oauthVerifier, WebRequest request, Map<String, Object> map) {

		OAuthService service = gdServiceProvider.getService();

		// getting access token
		Verifier verifier = new Verifier(oauthVerifier);
		Token accessToken = service.getAccessToken(EMPTY_TOKEN, verifier);

		// store access token as a session attribute
		request.setAttribute("oauthAccessToken", accessToken, SCOPE_SESSION);

		ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
		HttpSession session = attr.getRequest().getSession(false); //create a new session
		session.setAttribute("accessToken",accessToken);

		return "settings";
	}
}

Google OAuth extends DefaultApi20

package com.example.oauth;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.scribe.builder.api.DefaultApi20;
import org.scribe.extractors.AccessTokenExtractor;
import org.scribe.model.OAuthConfig;
import org.scribe.model.Verb;
import org.scribe.exceptions.OAuthException;
import org.scribe.model.OAuthConstants;
import org.scribe.model.OAuthRequest;
import org.scribe.model.Response;
import org.scribe.model.Token;
import org.scribe.model.Verifier;
import org.scribe.oauth.OAuth20ServiceImpl;
import org.scribe.oauth.OAuthService;
import org.scribe.utils.OAuthEncoder;
import org.scribe.utils.Preconditions;

public class GoogleDriveOauthApi extends DefaultApi20{

	  private static final String AUTHORIZE_URL = "https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=%s&redirect_uri=%s";
	    private static final String SCOPED_AUTHORIZE_URL = AUTHORIZE_URL + "&scope=%s";
	 
	    @Override
	    public String getAccessTokenEndpoint() {
	        return "https://accounts.google.com/o/oauth2/token";
	    }
	    
	    @Override
	    public AccessTokenExtractor getAccessTokenExtractor() {
	        return new AccessTokenExtractor() {
	            
	            @Override
	            public Token extract(String response) {
	                Preconditions.checkEmptyString(response, "Response body is incorrect. Can't extract a token from an empty string");
	 
	                Matcher matcher = Pattern.compile("\"access_token\" : \"([^&\"]+)\"").matcher(response);
	                if (matcher.find())
	                {
	                  String token = OAuthEncoder.decode(matcher.group(1));
	                  return new Token(token, "", response);
	                } 
	                else
	                {
	                  throw new OAuthException("Response body is incorrect. Can't extract a token from this: '" + response + "'", null);
	                }
	            }
	        };
	    }
	 
	    @Override
	    public String getAuthorizationUrl(OAuthConfig config) {
            return String.format(SCOPED_AUTHORIZE_URL, config.getApiKey(),
                    OAuthEncoder.encode(config.getCallback()),
                    OAuthEncoder.encode("openid profile email https://www.googleapis.com/auth/drive.file"));
	    }
	    
	    @Override
	    public Verb getAccessTokenVerb() {
	        return Verb.POST;
	    }
	    
	    @Override
	    public OAuthService createService(OAuthConfig config) {
	        return new GoogleOAuth2Service(this, config);
	    }
	    
	    private class GoogleOAuth2Service extends OAuth20ServiceImpl {
	 
	        private static final String GRANT_TYPE_AUTHORIZATION_CODE = "authorization_code";
	        private static final String GRANT_TYPE = "grant_type";
	        private DefaultApi20 api;
	        private OAuthConfig config;
	 
	        public GoogleOAuth2Service(DefaultApi20 api, OAuthConfig config) {
	            super(api, config);
	            this.api = api;
	            this.config = config;
	        }
	        
	        @Override
	        public Token getAccessToken(Token requestToken, Verifier verifier) {
	            OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint());
	            switch (api.getAccessTokenVerb()) {
	            case POST:
	                request.addBodyParameter(OAuthConstants.CLIENT_ID, config.getApiKey());
	                request.addBodyParameter(OAuthConstants.CLIENT_SECRET, config.getApiSecret());
	                request.addBodyParameter(OAuthConstants.CODE, verifier.getValue());
	                request.addBodyParameter(OAuthConstants.REDIRECT_URI, config.getCallback());
	                request.addBodyParameter(GRANT_TYPE, GRANT_TYPE_AUTHORIZATION_CODE);
	                break;
	            case GET:
	            default:
	                request.addQuerystringParameter(OAuthConstants.CLIENT_ID, config.getApiKey());
	                request.addQuerystringParameter(OAuthConstants.CLIENT_SECRET, config.getApiSecret());
	                request.addQuerystringParameter(OAuthConstants.CODE, verifier.getValue());
	                request.addQuerystringParameter(OAuthConstants.REDIRECT_URI, config.getCallback());
	                if(config.hasScope()) request.addQuerystringParameter(OAuthConstants.SCOPE, "openid profile email https://www.googleapis.com/auth/drive.file");
	            }
	            Response response = request.send();
	            return api.getAccessTokenExtractor().extract(response.getBody());
	        }
	    }
}

GitHub OAuth 2.0 Scribe Java Example

Github OAuth ServiceConfig and ServiceProvider Bean

<bean id="githubServiceConfig" class="com.example.oauth.OAuthServiceConfig">
		<constructor-arg value="xxx" />
		<constructor-arg value="xxx"/>
		<constructor-arg value="https://www.example.com/oauth/github"/>
		<!--  <constructor-arg value="user,public_repo"/>-->
		<constructor-arg value="com.example.oauth.GithubOauthApi"/>
	</bean>
	<bean id="githubServiceProvider" class="com.example.oauth.OAuthServiceProvider">
		<constructor-arg name="config" ref="githubServiceConfig" />
	</bean>

Github OAuth Spring MVC Controller

package com.example.oauth.controller;

import static org.springframework.web.context.request.RequestAttributes.SCOPE_SESSION;

import java.util.Map;

import javax.servlet.http.HttpSession;

import org.scribe.model.Token;
import org.scribe.model.Verifier;
import org.scribe.oauth.OAuthService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.ModelAndView;

import com.example.oauth.OAuthServiceProvider;

@Controller
@RequestMapping("/oauth/github")
public class GitHubController {
	
	@Autowired
	@Qualifier("githubServiceProvider")
	private OAuthServiceProvider githubServiceProvider;
	
	private static final Token EMPTY_TOKEN = null;
	
	@RequestMapping(value = "/login-github", method = RequestMethod.GET)
	public String loginToGithub(Map<String, Object> map, WebRequest request) {
		OAuthService service = githubServiceProvider.getService();
		String auth = service.getAuthorizationUrl(EMPTY_TOKEN);
		System.out.println("RequestToken: " + auth);
		return "redirect:" + auth;
	}

	@RequestMapping(value = { "" }, method = RequestMethod.GET)
	public String callback(
			@RequestParam(value = "oauth_token", required = false) String oauthToken,
			@RequestParam(value = "code", required = false) String oauthVerifier,
			WebRequest request, Map<String, Object> map) {

		// getting request token
		OAuthService service = githubServiceProvider.getService();
		Token requestToken = (Token) request.getAttribute("oauthRequestToken",
				SCOPE_SESSION);

		// getting access token
		Verifier verifier = new Verifier(oauthVerifier);
		Token accessToken = service.getAccessToken(requestToken, verifier);

		// store access token as a session attribute
		request.setAttribute("oauthAccessToken", accessToken, SCOPE_SESSION);

		ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder
				.currentRequestAttributes();
		HttpSession session = attr.getRequest().getSession(false); // create a
																	
		session.setAttribute("accessToken", accessToken);

		return "settings";
	}
}

GithubOauthApi extends DefaultApi20

package com.example.oauth;

import org.scribe.builder.api.DefaultApi20;
import org.scribe.model.OAuthConfig;

public class GithubOauthApi extends DefaultApi20{

	private static final String ACCESSTOKEN="https://github.com/login/oauth/access_token";
	
	@Override
	public String getAccessTokenEndpoint() {
		// TODO Auto-generated method stub
		return ACCESSTOKEN;
	}

	@Override
	public String getAuthorizationUrl(OAuthConfig config) {
		// TODO Auto-generated method stub
		return String.format("https://github.com/login/oauth/authorize?client_id=%s&scope=%s&redirect_uri=%s", config.getApiKey(), config.getScope(), config.getCallback());
	}
}