[go: up one dir, main page]

Handmade Coding

Oz Blogging about hand made programming
Loading...

How to use Google Drive REST API for document conversion?

Google Drive Authorization:

First we need to follow examples at drive authorization server-side to build a nice proxy for authorization:

import java.io.IOException;
import java.io.InputStreamReader;
import java.security.GeneralSecurityException;

import com.google.api.client.auth.*;
import com.google.api.client.extensions.java6.auth.*;
import com.google.api.client.extensions.jetty.auth.*;
import com.google.api.client.googleapis.auth.oauth2.*;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.oz.drv.api.GoogDrvAuthProxy;
import com.oz.drv.api.GoogDrvConnConfig;

public class GoogDrvAuthProxyImpl implements GoogDrvAuthProxy
{
 /**
 * Follow instructions at:
 * Google Drive: Implementing Server-Side Authorization
 * Choose "Installed application" option.
 */
 private static final String PATH_CLIENT_SECRET_JSON =
  "./client_secret.json";
 private final GoogDrvConnConfig drvConnConfig;
 public GoogDrvAuthProxyImpl(final GoogDrvConnConfig drvConnConfig)
 {
  this.drvConnConfig = drvConnConfig;
 }
 @Override
 public Credential authorize(final String user)
  throws IOException, GeneralSecurityException {
  final JsonFactory jsonFactory = drvConnConfig.getJsonFactory();
  final GoogleClientSecrets clientSecrets =
   GoogleClientSecrets.load(jsonFactory
   , new InputStreamReader(
      ClassLoader.getSystemResourceAsStream(
        PATH_CLIENT_SECRET_JSON)));
  final AuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
     drvConnConfig.getTransport()
    ,jsonFactory
    ,clientSecrets
    ,drvConnConfig.getScopes()
  )
  .setDataStoreFactory(drvConnConfig.getDataStoreFactory())
  .setAccessType("offline")
  .build();
  final VerificationCodeReceiver rcvr = new LocalServerReceiver();
  return new AuthorizationCodeInstalledApp(flow, rcvr).authorize(user);
 }
 @Override
 public HttpTransport getTransport() throws GeneralSecurityException, IOException {
  return drvConnConfig.getTransport();
 }
 @Override
 public JsonFactory getJsonFactory() {
  return drvConnConfig.getJsonFactory();
 }
}
              

Using Google Drive API for file conversion:

Then we can build an example following guide at drive quick-start guide and some tips at downloading files using drive API:

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.http.AbstractInputStreamContent;
import com.google.api.client.http.FileContent;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpTransport;
import com.google.api.services.drive.Drive;
import com.oz.drv.api.GoogDrvAuthProxy;
import com.oz.drv.api.GoogDrvSvcProxy;
public class GoogDrvSvcProxyImpl implements GoogDrvSvcProxy
{
 private final String appName;
 private final GoogDrvAuthProxy drvAuthProxy;
 public GoogDrvSvcProxyImpl(final String appName, final GoogDrvAuthProxy drvAuthProxy)
 {
  this.appName = appName;
  this.drvAuthProxy = drvAuthProxy;
 }
 private Drive build(final HttpTransport transport, final Credential credential) {
  return new Drive.Builder(transport, drvAuthProxy.getJsonFactory(), credential)
  .setApplicationName(appName)
  .build();
 }
 @Override
 public String insert(final String user, final File file, final String mimeType)
  throws IOException, GeneralSecurityException
 {
  final HttpTransport transport = drvAuthProxy.getTransport();
  final Credential cred = drvAuthProxy.authorize(user);
  final AbstractInputStreamContent mediaContent = new FileContent(mimeType, file);
  final com.google.api.services.drive.model.File content =
   new com.google.api.services.drive.model.File()
  .setTitle(file.getName())
  ;
  return build(transport, cred)
  .files()
  .insert(content, mediaContent)
  .setConvert(true)
  .execute()
  .getId()
  ;
 }
 @Override
 public InputStream get(final String user, final String fileId, final String mimeType)
  throws IOException, GeneralSecurityException
 {
  final HttpTransport transport = drvAuthProxy.getTransport();
  final Credential cred = drvAuthProxy.authorize(user);
  final String downloadUrl = build(transport, cred)
   .files()
   .get(fileId)
   .execute()
   .getExportLinks()
   .get(mimeType)
  ;
  if (null == downloadUrl) {
   return null;
  }
  return build(transport, cred)
  .getRequestFactory()
  .buildGetRequest(new GenericUrl(downloadUrl))
  .execute()
  .getContent()
  ;
 }
 @Override
 public void delete(final String user, final String fileId)
  throws IOException, GeneralSecurityException
 {
  final HttpTransport transport = drvAuthProxy.getTransport();
  final Credential cred = drvAuthProxy.authorize(user);
  build(transport, cred)
   .files()
   .delete(fileId)
   .execute()
  ;
 }
}
                

You can git this project at here

How to use Dozer map-id for multiple destinations?

Java source:

Dozer's map-id attribute has to be unique across, but we can still use map-id to define mapping to multiple destinations of the same source. Here is a method that is trying achieve this:

@Component("theMapper")
public class TheMapper {
 @Resource
 private Mapper dozerMapper;
 public SomeDestinationBean map(
  final SomeSrcBean ssb
 ,final AnotherSrcBean asb
 ,final String destinationType)
 {
  final SomeDestinationBean sdb = new SomeDestinationBean();
  //Do mapping. Note that map id has to be unique
  dozerMapper.map(ssb, sdb, destinationType+"_ss2sd");
  dozerMapper.map(asb, sdb, destinationType+"_as2sd");
  return sdb;
 }
}
                

Dozer XML mappings:

How do the dozer mapping configuration look like? This is for the first destination type (dozer-mapping-A.xml):

<?xml version="1.0" encoding="UTF-8"?>
 <mappings xmlns="http://dozer.sourceforge.net"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="
  http://dozer.sourceforge.net
  http://dozer.sourceforge.net/schema/beanmapping.xsd"
  <mapping map-id="destA_ss2sd">
   <class-a>SomeSrcBean</class-a>
   <class-b>SomeDestinationBean</class-b>
   <field>
    <a>someSrcStr</a>
    <b>someDestinationStr</b>
   </field>
  </mapping>
  <mapping map-id="destA_as2sd">
   <class-a>AnotherSrcBean</class-a>
   <class-b>SomeDestinationBean</class-b>
   <field>
    <a>anotherSrcDbl</a>
    <b>someDestinationDbl</b>
   </field>
  </mapping>
 </mappings>
                

...And the one for the second destination type (dozer-mapping-B.xml):

<?xml version="1.0" encoding="UTF-8"?>
 <mappings xmlns="http://dozer.sourceforge.net"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="
   http://dozer.sourceforge.net
   http://dozer.sourceforge.net/schema/beanmapping.xsd"
  <mapping map-id="destB_ss2sd">
   <class-a>SomeSrcBean</class-a>
   <class-b>SomeDestinationBean</class-b>
   <field>
    <a>someSrcStr2</a>
    <b>someDestinationStr</b>
   </field>
  </mapping>
  <mapping map-id="destB_as2sd">
   <class-a>AnotherSrcBean</class-a>
   <class-b>SomeDestinationBean</class-b>
   <field>
    <a>anotherSrcDbl</a>
    <b>someDestinationDbl</b>
   </field>
  </mapping>
 </mappings>
                

Spring configuration:

I have used spring to hook up the XML mappings to Java source (spring-beans.xml):

<?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/context
  http://www.springframework.org/schema/context/spring-context-3.0.xsd"
  <context:component-scan base-package="com.oz.dozer.mapper"/>
  <context:annotation-config/>
  <bean id="mapper" class="org.dozer.DozerBeanMapper">
   <property name="mappingFiles">
    <list>
     <value>dozer-mapping-A.xml</value>
     <value>dozer-mapping-B.xml</value>
    </list>
   </property>
   </bean>
  </beans>
                

TestNG Spring Run:

I have used testng spring context test to connect the dots and test this out:

@ContextConfiguration("classpath:spring-beans.xml")
public class TheMapperTestNg extends AbstractTestNGSpringContextTests {
 @Autowired
 private TheMapper theMapper;
 @Test
 public void mappingTest() {
  final SomeSrcBean ssb = new SomeSrcBean();
  final AnotherSrcBean asb = new AnotherSrcBean();
  final String someSrcStr = "TESTING!!";
  final String someSrcStr2 = "TESTING2!!";
  ssb.setSomeSrcStr(someSrcStr);
  ssb.setSomeSrcStr2(someSrcStr2);
  final double anotherSrcDbl = 666.66d;
  asb.setAnotherSrcDbl(anotherSrcDbl);
  final SomeDestinationBean sdb1 =
  theMapper.map(ssb, asb, "destA");
  AssertJUnit.assertEquals("Some str"
   , sdb1.getSomeDestinationStr(), someSrcStr);
  AssertJUnit.assertEquals("Another dbl"
   , sdb1.getSomeDestinationDbl(), anotherSrcDbl);
  final SomeDestinationBean sdb2 =
  theMapper.map(ssb, asb, "destB");
  AssertJUnit.assertEquals("Some str 2"
  , sdb2.getSomeDestinationStr(), someSrcStr2);
  AssertJUnit.assertEquals("Another dbl"
  , sdb2.getSomeDestinationDbl(), anotherSrcDbl);
 }
}
                

You can git this project at here

Active MQ Proof of Concept

This is a proof of concept blog about Active MQ using Tomee+ JMS, Servlet, Endpoint on the server side, and Angular, JS web sockets on the client side. Java testing is done via JUnit, Mockito. Javascript testing is done via Jasmine. Let's first start out by describing the maven setup of the project...
1. Maven Compiler plugin (the usual) version 3.1 and the project requires JDK 1.7 and up:

<plugin>
 <artifactId>maven-compiler-plugin</artifactId>
 <version>3.1</version>
 <configuration>
  <source>1.7</source>
  <target>1.7</target>
 </configuration>
</plugin>
                    

2. a) Jasmine Maven plugin simplifies integrating JS testing into maven test lifecycle goal (Notice the executions tag):

<plugin>
 <groupId>com.github.searls</groupId>
 <artifactId>jasmine-maven-plugin</artifactId>
 <version>1.3.1.5</version>
 <executions>
  <execution>
   <goals>
    <goal>test</goal>
   </goals>
  </execution>
 </executions>
</plugin>
                    

2. b) We add some custom configuration to Jasmine Maven plugin to support Angular testing with a selenium chrome driver (angular-mocks helps with mocking the angular plumbing), and we have a customized JS source directory (default is src/main/javascript):

<plugin>
 <groupId>com.github.searls</groupId>
 <artifactId>jasmine-maven-plugin</artifactId>
 <version>1.3.1.5</version>
 <configuration>
  <webDriverClassName>
   org.openqa.selenium.chrome.ChromeDriver
  </webDriverClassName>
  <preloadSources>
   <source>${jquery.load.path}/jquery.js</source>
   <source>${angular.load.path}/angular.js</source>
   <source>${angular.load.path}/angular-mocks.js</source>
  </preloadSources>
  <jsSrcDir>${project.basedir}/src/main/webapp/js</jsSrcDir>
 </configuration>
 <executions>
  <execution>
   <goals>
    <goal>test</goal>
   </goals>
  </execution>
 </executions>
</plugin>
                

3. a) Last piece is a TOMEE maven plugin that helps with packaging/deploying to a TOMEE server and starting debugging

<plugin>
 <groupId>org.apache.openejb.maven</groupId>
 <artifactId>tomee-maven-plugin</artifactId>
 <version>1.7.1</version>
</plugin>
                  

3. b) We add reload synchronization configuration to TOMEE maven plugin that automates hot code deploy (Java, JS or HTML changes) to a TOMEE+ server at debug mode:

<plugin>
 <groupId>org.apache.openejb.maven</groupId>
 <artifactId>tomee-maven-plugin</artifactId>
 <version>1.7.1</version>
 <configuration>
  <tomeeVersion>1.7.1</tomeeVersion>
  <tomeeClassifier>plus</tomeeClassifier>
  <synchronization>
   <extensions>
    <extension>.class</extension>
    <extension>.js</extension>
    <extension>.html</extension>
   </extensions>
  </synchronization>
  <reloadOnUpdate>true</reloadOnUpdate>
 </configuration>
</plugin>
                

4. Following maven dependencies are added to the project. Log4j is the usual logging library. Javax servlet, websocket and JMS are needed for server-side setup. GSON is a JSON library. JUnit, and mockito are needed for java testing. Jasmine and Selenium are needed for client-side JS testing:

<dependencies>
 <dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
 </dependency>
 <dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>3.1.0</version>
 </dependency>
 <dependency>
  <groupId>javax.jms</groupId>
  <artifactId>javax.jms-api</artifactId>
  <version>2.0.1</version>
 </dependency>
 <dependency>
  <groupId>javax.websocket</groupId>
  <artifactId>javax.websocket-api</artifactId>
  <version>1.1</version>
 </dependency>
 <dependency>
  <groupId>com.google.code.gson</groupId>
  <artifactId>gson</artifactId>
  <version>2.3.1</version>
 </dependency>
 <dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.11</version>
  <scope>test</scope>
 </dependency>
 <dependency>
  <groupId>org.mockito</groupId>
  <artifactId>mockito-all</artifactId>
  <version>1.10.19</version>
  <scope>test</scope>
 </dependency>
 <dependency>
  <groupId>com.github.searls</groupId>
  <artifactId>jasmine-maven-plugin</artifactId>
  <version>1.3.1.5</version>
  <scope>test</scope>
 </dependency>
 <dependency>
  <groupId>org.seleniumhq.selenium</groupId>
  <artifactId>selenium-chrome-driver</artifactId>
  <version>2.3.1</version>
  <scope>test</scope>
 </dependency>
</dependencies>
                

Next, let's take a look at the client-side...

1) We will start out with HTML for the Producer page:

We include angular library at the head, and add angular app/controller JS at the bottom of HTML body by convention. There is a simple form with a text area and submit button. Text area is required for the form and the button is disabled unless the form text area has something in it. On the bottom of the form, we have a small space for feedback from the server:

<!DOCTYPE html>
<html ng-app="producerApp">
 <head>
  <meta charset="ISO-8859-1">
  <title>Producer</title>
  <script
   src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"
  ></script>
 </head>
 <body>
  <div ng-controller="producerCtrl">
   <form name="produceForm" ng-submit="produce()">
    <h2>Message</h2>
    <p>
     <textarea ng-model="message" name="message" rows="25" cols="40" required>
     </textarea>
    </p>
    <p>
     <button type="submit"
      ng-disabled="produceForm.$pristine ||!produceForm.$dirty"
     >Produce</button>
    </p>
   </form>
   <div>{{feedback}}</div>
  </div>
  <script src="js/producerApp.js"></script>
  <script src="js/producerCtrl.js"></script>
 </body>
</html>
                

2) Next, let's take a look at HTML for the Consumer page:

Again, we include angular library at the head, and add angular app/controller JS at the bottom of HTML body by convention. There is a simple read-only text area where messages we listen to will show up:

<!DOCTYPE html>
<html ng-app="consumerApp">
 <head>
  <meta charset="ISO-8859-1">
  <title>Consumer</title>
  <script
   src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"
  ></script>
 </head>
 <body>
  <div ng-controller="consumerCtrl">
   <h2>Message(s)</h2>
   <p>
    <textarea ng-model="message" name="message" rows="25" cols="40" readonly>
    </textarea>
   </p>
   </div>
   <script src="js/consumerApp.js"></script>
   <script src="js/consumerCtrl.js"></script>
 </body>
</html>
                

3) Next, we move onto Angular.JS for the Producer page:

Here is the producer angular app:

/**
* Producer App
*/
var app = angular.module("producerApp", []);
                

...and the producer controller which talks to the JMS producer servlet to send the message and get a feedback of message receipt:

/**
* Producer Controller
*/
app.controller("producerCtrl", function($scope, $http, $location) {
 $scope.jmsMode = "PUBLISH_TO_TOPIC";
 $scope.jpsUrl =
  "http://localhost:"+$location.port()+"/_testing_/JPS/produce.do";
 $scope.message = "";
 $scope.feedback = "";
 $scope.produce = function() {
  $http.post($scope.jpsUrl, {
   mode : $scope.jmsMode
   ,msg: $scope.message
  })
  .success(function(data, status, headers, config){
   $scope.feedback = "Success: "+data;
  })
  .error(function(data, status, headers, config){
   $scope.feedback = "Error: "+data;
  });
 };
});
                

4) On the consumer client side we have the following Angular.JS:

The consumer app:

/**
* Consumer App
*/
var app = angular.module("consumerApp", []);
                

...and the consumer controller that establishes a web socket connection to the message consumer server endpoint to listen to any messages:

/**
* Consumer Controller
*/
app.controller("consumerCtrl", function($scope, $location) {
 $scope.endpointUrl =
  "ws://localhost:"+$location.port()+"/_testing_/endpoint";
 $scope.message = "";
 var webSocket = new WebSocket($scope.endpointUrl);
 webSocket.onerror = function (event) {
  $scope.message = event.data;
  $scope.$apply();
 };
 webSocket.onopen = function(event) {
  webSocket.send("Connected");
 };
 webSocket.onmessage = function(event) {
  if (event && event.data) {
   var jsonObj = JSON.parse(event.data);
   $scope.message = $scope.message + '\n'
    + jsonObj.timeStamp + ': '+ jsonObj.msg;
   $scope.$apply();
  }
  webSocket.send("listen");
 };
 $scope.$on('$destroy', function() {
  webSocket.close(); //Close web socket connection
 });
});
                

Now, for the server-side...

1) The JMS producer servlet is a simple servlet that defines a JMS topic and a connection factory resources (via TOMEE+ injection binding).

public final class JmsProducerServlet extends HttpServlet {
...
@Resource(name = "foo")
private Topic fooTopic;
@Resource
private ConnectionFactory jmsConnFactory;
...
                

On a POST from the client side: 

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
...
                

a) Creates/starts a JMS connection

jmsConn = jmsConnFactory.createConnection();
jmsConn.start();
                

b) Creates a JMS session (no transaction, auto acknowledge message sent)

jmsSession =
jmsConn.createSession(false,Session.AUTO_ACKNOWLEDGE);
                

c) Parses the JSON request (via Gson) to extract message to be sent

final Request jsonReq =
gson.fromJson(req.getReader(), Request.class);
                

d) Creates a non-persistent message producer for the JMS topic

final MessageProducer producer =
 jmsSession.createProducer(fooTopic);
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
                

e) Creates/sends the JMS message envelope and put the message in that was extracted

// Create a message
final TextMessage message = jmsSession.createTextMessage(msg);
// Tell the producer to send the message
producer.send(message);
                

f) Returns a feedback to caller

//Write back ack to response
pw.println(SUCCESS_MSG);
                

2) The JMS consumer server endpoint also utilizes TOMEE+ binding to create the same JMS topic by name and a connection factory resource:

@ServerEndpoint(value = "/endpoint")
public final class JmsConsumerServerEndPoint {
...
 @Resource(name = "foo")
 private Topic fooTopic;
 @Resource
 private ConnectionFactory jmsConnFactory;
...
                

a) On a web socket open request from the client-side:

@OnOpen
public void onOpen(final Session session) {
                  

It creates/starts a JMS connection and session (again no transaction, auto acknowledge message receipt):

jmsConn = jmsConnFactory.createConnection();
jmsConn.start();
jmsSession =
jmsConn.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
                

b) On a web socket request to listen:

@OnMessage
public String onMessage(final String message, final Session session) {
...
 switch (message) {
 case "listen":
                  

It creates a JMS consumer of the topic and waits to receive the message:

// Create a MessageConsumer from the Session for the Topic or Queue
final MessageConsumer consumer =
 jmsSession.createConsumer(fooTopic);
// Wait to receive the message
final Message msg = consumer.receive();
                

When there is a JMS message to be consumed, it wraps it up into a JSON response and sends it back to the client:

  ...
  else if (msg instanceof TextMessage)
  {//Send back msg text to response
   resp = new Response((TextMessage)msg);
  }
  ...
  return new Gson().toJson(resp);
                

c) It is a good idea to clean up the JMS resources on closing of the web socket:

@OnClose
public void onClose(final Session session, final CloseReason closeReason) {
...
 try {
  if (session.isOpen()) {
   session.close();
  }
  if (jmsSession != null) {
   jmsSession.close();
  }
  if (jmsConn != null) {
   jmsConn.stop();
   jmsConn.close();
  }
 }
 catch (JMSException | IOException e) {
  logger.debug("Problem closing resources", e);
 }
}
                

How about some Java Unit Testing...

1) We utilize mockito annotations to mock out the JMS communication portion of the JMS producer servlet to test the behavior:

@RunWith(MockitoJUnitRunner.class)
public class TestJmsProducerServlet {
@Mock(name = "jmsConnFactory")
private ConnectionFactory mockJmsConnFactory;
@InjectMocks
private JmsProducerServlet servlet;
@Mock
private HttpServletRequest mockReq;
@Mock
private HttpServletResponse mockResp;
@Mock
private Connection mockJmsConn;
@Mock
private Session mockJmsSession;
@Mock
private MessageProducer mockProducer;
...
@Before
public final void beforeTest() throws Exception {
 Mockito
 .when(mockJmsConnFactory.createConnection())
 .thenReturn(mockJmsConn);

 Mockito
 .when(mockJmsConn.createSession(
 Mockito.anyBoolean(), Mockito.anyInt()))
 .thenReturn(mockJmsSession);

 Mockito.when(mockJmsSession.createProducer(
 Mockito.any(Destination.class)))
 .thenReturn(mockProducer);
}
...
                

2) Same goes for the JMS consumer endpoint:

@RunWith(MockitoJUnitRunner.class)
public class TestJmsConsumerServerEndPoint {
 @Mock(name = "jmsConnFactory")
 private ConnectionFactory mockJmsConnFactory;
 @InjectMocks
 private JmsConsumerServerEndPoint endPoint;
 @Mock
 private Session mockSession;
 @Mock
 private MessageConsumer mockConsumer;
...
                

How about some Javascript Jasmine Testing...

We utilize angular-mocks.js to mock out the angular client/server communication:

/**
* Testing client-side producer
*/
describe('Producer Controller', function() {
 var $httpBackend, $rootScope, createController;
 beforeEach(module('producerApp'));
 beforeEach(inject(function($injector) {
 // Set up the mock http service responses
 $httpBackend = $injector.get('$httpBackend');
 // Get hold of a scope (i.e. the root scope)
 $rootScope = $injector.get('$rootScope');
 // The $controller service is used
 //to create instances of controllers
 var $controller = $injector.get('$controller');
 createController = function() {
  return $controller('producerCtrl'
  , {'$scope' : $rootScope });
 };
}));
afterEach(function() {
 $httpBackend.verifyNoOutstandingExpectation();
 $httpBackend.verifyNoOutstandingRequest();
});
                

Here is a test of the post the message to producer servlet:

it('should send a successful post message to JMS Producer Servlet (JPS)', function() {
 var testToken = 'TESTING!!';
 $httpBackend.expectPOST(testToken
  , {mode : testToken, msg: testToken})
 .respond(testToken);
 createController();
 $rootScope.jmsMode = testToken;
 $rootScope.jpsUrl = testToken;
 $rootScope.message = testToken;
 $rootScope.produce();
 $httpBackend.flush();
 expect($rootScope.feedback)
 .toEqual("Success: "+testToken);
});
                

You can git this project at here

You can download a compressed version of the whole project here.

How do you run it? Depends on the IDE, I use Eclipse maven launcher to package it:

... and here is my launcher to start TOMEE+ in debug mode (listening to any changes in source code to reload):

About


Producing high-end quality, fully-tested software units based on constant collaboration with our customers to deduce exact specifications.

Striving to understand the business workflows to design custom-tailored software to fit the needs from management/owner level to end-users of our clients.