Monday 28 October 2013

The making of Restbucks

The making of Restbucks

Even great coffee starts with a very small seed.  In this blog post we'll be implementing the worlds best known RESTful coffee shop with IRIS.  IRIS is an open source project for create Interaction, Reporting and Information Services; what better example for our project than a nice cup of Restbucks coffee.  If you happen to be reading this post with a cuppa, Restbucks will be ready for service by the time you finish.

Entity Relationships

We're going to kick start our example with a simple Entity Relationship diagram and follow most of the steps in the Quickstart.  The Restbucks database contains Orders and Payments:



It seems a little odd for a Java project to start an example using Visual Studio, but one of the easiest ways to get an IRIS project started is to create services from an EDMX file.  Visual Studio is still the best tool to create our Restbucks EDMX file.  With only a few minor alterations to remove unsupported annotations and keeping complex types out of the picture for the moment it looks as follows:

<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="1.0" xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
    <edmx:DataServices m:DataServiceVersion="1.0">
        <Schema xmlns="http://schemas.microsoft.com/ado/2006/04/edm" Namespace="RestbucksModel">
        <EntityContainer Name="Restbucks" m:IsDefaultEntityContainer="true">
          <EntitySet Name="Orders" EntityType="RestbucksModel.Order" />
          <EntitySet Name="Payments" EntityType="RestbucksModel.Payment" />
          <AssociationSet Name="OrderPayment" Association="RestbucksModel.OrderPayment">
            <End Role="Order" EntitySet="Orders" />
            <End Role="Payment" EntitySet="Payments" />
          </AssociationSet>
        </EntityContainer>
        <EntityType Name="Order">
          <Key>
            <PropertyRef Name="Id" />
          </Key>
          <Property Type="Int32" Name="Id" Nullable="false" />
          <Property Type="String" Name="location" Nullable="false" />
          <NavigationProperty Name="Payment" Relationship="RestbucksModel.OrderPayment" FromRole="Order" ToRole="Payment" />
          <Property Type="String" Name="name" Nullable="false" />
          <Property Type="String" Name="milk" Nullable="false" />
          <Property Type="Int16" Name="quantity" Nullable="false" />
          <Property Type="String" Name="size" Nullable="false" />
        </EntityType>
        <EntityType Name="Payment">
          <Key>
            <PropertyRef Name="Id" />
          </Key>
          <Property Type="Int32" Name="Id" Nullable="false" />
          <Property Type="String" Name="authorisationCode" Nullable="false" />
          <NavigationProperty Name="Order" Relationship="RestbucksModel.OrderPayment" FromRole="Payment" ToRole="Order" />
        </EntityType>
<Association Name="OrderPayment">
<End Type="RestbucksModel.Order" Role="Order" Multiplicity="1" />
<End Type="RestbucksModel.Payment" Role="Payment" Multiplicity="0..1" />
                <ReferentialConstraint>
                <Principal Role="Order">
                <PropertyRef Name="Id"/>
                </Principal>
                <Dependent Role="Payment">
                <PropertyRef Name="Id"/>
                </Dependent>
                </ReferentialConstraint>
</Association>
        </Schema>
    </edmx:DataServices>
</edmx:Edmx>


Generating the project mock responder

We now use the above EDMX to generate a simple mock database and mock responder services.

$ mvn interaction-sdk:gen

Once we've executed the generation step we can immediately try to start our project with 'jetty:run'.  Using the EDMX provided here in this post does result in one or two errors initially because we want our service to be called Orders; this is a reserved word in SQL.  Therefore we need to make one minor correction to the generated table name.

$ mvn clean package jetty:run


21:38:04.177 [main] DEBUG o.h.tool.hbm2ddl.SchemaExport - create table Order (Id integer not null, location varchar(255), milk varchar(255), name varc
har(255), quantity integer, size varchar(255), primary key (Id))
21:38:04.178 [main] ERROR o.h.tool.hbm2ddl.SchemaExport - Unsuccessful: create table Order (Id integer not null, location varchar(255), milk varchar(2
55), name varchar(255), quantity integer, size varchar(255), primary key (Id))
21:38:04.180 [main] ERROR o.h.tool.hbm2ddl.SchemaExport - Unexpected token: ORDER in statement [create table Order]
21:38:04.180 [main] DEBUG o.h.tool.hbm2ddl.SchemaExport - create table Payment (Id integer not null, authorisationCode varchar(255), primary key (Id))

21:38:04.186 [main] DEBUG o.h.tool.hbm2ddl.SchemaExport - alter table Order add constraint FK48E972EB5B9E338 foreign key (Id) references Payment
21:38:04.186 [main] ERROR o.h.tool.hbm2ddl.SchemaExport - Unsuccessful: alter table Order add constraint FK48E972EB5B9E338 foreign key (Id) references
 Payment
21:38:04.187 [main] ERROR o.h.tool.hbm2ddl.SchemaExport - Unexpected token: ORDER in statement [alter table Order]
21:38:04.188 [main] DEBUG o.h.tool.hbm2ddl.SchemaExport - alter table Payment add constraint FK3454C9E659675240 foreign key (Id) references Order
21:38:04.189 [main] ERROR o.h.tool.hbm2ddl.SchemaExport - Unsuccessful: alter table Payment add constraint FK3454C9E659675240 foreign key (Id) referen
ces Order
21:38:04.190 [main] ERROR o.h.tool.hbm2ddl.SchemaExport - Unexpected token: ORDER in statement [alter table Payment add constraint FK3454C9E659675240
foreign key (Id) references Order]
...
Oct 27, 2013 9:38:05 PM com.temenos.interaction.sdk.util.ResponderDBUtils fillDatabase
SEVERE: Failed to insert SQL statements.
java.sql.SQLException: Unexpected token: ORDER in statement [INSERT INTO Order]
        at org.hsqldb.jdbc.Util.sqlException(Unknown Source)
        at org.hsqldb.jdbc.jdbcStatement.fetchResult(Unknown Source)
        at org.hsqldb.jdbc.jdbcStatement.executeUpdate(Unknown Source)
        at com.temenos.interaction.sdk.util.ResponderDBUtils.fillDatabase(ResponderDBUtils.java:74)


Modify Order.java
@Entity
@Table(name="rb_order")
public class Order {

Modify responder_insert.sql
INSERT INTO `rb_order`(`Id` , `location` , `name` , `milk` , `quantity` , `size`) VALUES('1' , 'abc' , 'abc' , 'abc' , '1' , 'abc');


Data Service into Interaction Service

By default the interaction SDK creates an OData service when generated from an EDMX.  OData is a good example of a Data Service - it has links between entities.  However, to become an Interaction Service we need to use these links to guide our client interactions.  You can open the IRIS default OData javascript client http://localhost:8080/responder and you should see the following :



Restbuck Interactions and State Machine

If we take a moment to refer back to Rest In Practice, Section 5.4.1. The Restbucks Domain Application Protocol, we'll see a very useful diagram that describes our client interactions with Restbucks.  As the book points out, this diagram is great in terms of understanding the HTTP requests and responses, but to codify the service we need to think in terms of the state machine.

Interactions:

State machine:


If we have to think in terms of a state machine, why do we seldom see services written in terms of a state machine?  The Resource Interaction Model (RIM) is a domain specific language to help us do just that.  The RIM language is intended to be a conceptual level language that removes the need to understand the HTTP implementation details; at the same time this means we can ensure the RIM implementation is logically correct according to the RESTful constraints - by using verbs, status codes, representations, and links appropriately.


Resource Interaction Model

To turn the generated Restbucks Data Service into an Interaction Service we need to edit the src/main/resources/Restbucks.rim and implement the states from Figure 5-5.  Your first task will be to remove the generated CRUD states, and after some small modifications your Restbucks.rim will now look as follows:

RIM visualisation plugin



Restbucks.rim

domain RestbucksModel {
rim Restbucks {

event DELETE {
method: DELETE
}
event GET {
method: GET
}
event POST {
method: POST
}
event PUT {
method: PUT
}

command CreateEntity
command DeleteEntity
command GETEntities
command GETEntity
command GETException
command GETNavProperty
command GETServiceDocument
command NoopGET
command UpdateEntity

initial resource ServiceDocument {
type: item
entity: ServiceDocument
view { GETServiceDocument }
path "/"
GET -> Orders
GET -> Payments
GET -> shop
}

resource shop {
type: item
entity: ServiceDocument
view { NoopGET }
path "/shop"
POST -> OrderCreated
}


resource Orders {
type: collection
entity: Order
view { GETEntities }
path "/Orders()"
POST -> OrderCreated
GET *-> order id=Id
GET title="Payment" *-> payment
PUT *-> OrderUpdated
DELETE *-> OrderCancelled
}

resource order {
type: item
entity: Order
view { GETEntity }
path "/Orders({Id})"
GET title="Payment" -> payment
// Can only update or delete if we haven't paid
PUT -> OrderUpdated (NOT_FOUND(payment))
DELETE -> OrderCancelled (NOT_FOUND(payment))
POST -> PaymentAccepted (NOT_FOUND(payment))
}

resource OrderCancelled {
type: item
entity: Order
actions { DeleteEntity }
relations { "http://relations.restbucks.com/cancel" }
path "/Orders({Id})"
}

resource OrderUpdated {
type: item
entity: Order
actions { UpdateEntity }
relations { "http://relations.restbucks.com/update" }
path "/Orders({Id})"
}

resource OrderCreated {
type: item
entity: Order
actions { CreateEntity }
path "/Orders()"
GET --> order (OK(order))
}

resource Payments {
type: collection
entity: Payment
view { GETEntities }
path "/Payments()"
GET *-> payment
GET title="Order" *-> order
}

resource payment {
type: item
entity: Payment
view { GETEntity }
path "/Payments({Id})"
GET title="Order" -> order
}

resource PaymentAccepted {
type: item
entity: Payment
actions { CreateEntity }
relations { "http://relations.restbucks.com/payment" }
path "/Payments()"
GET --> payment (OK(payment))
}

}
}


Ready for service

Our shop is now ready for business.  Let's start our server and POST an order.  Using Postman POST the following entry, with Content-Type application/atom+xml to http://localhost:8080/responder/interaction-hateoas-restbucks.svc/Orders()

<?xml version='1.0' encoding='utf-8'?>
<entry 
    xmlns="http://www.w3.org/2005/Atom" 
    xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" 
    xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xml:base="http://localhost:8080/responder/interaction-hateoas-restbucks.svc/">
    <id>http://localhost:8080/responder/interaction-hateoas-restbucks.svc/Orders(1)</id>
    <title type="text" />
    <updated>2013-10-28T11:57:15Z</updated>
    <author>
        <name />
    </author>
    <category term="RestbucksModel.Order" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
    <content type="application/xml">
        <m:properties>
            <d:size>abc</d:size>
            <d:milk>abc</d:milk>
            <d:name>abc</d:name>
            <d:location>abc</d:location>
            <d:quantity m:type="Edm.Int32">1</d:quantity>
        </m:properties>
    </content>
</entry>


You will receive the following response 201 Created:

<?xml version='1.0' encoding='utf-8'?>
<entry 
    xmlns="http://www.w3.org/2005/Atom" 
    xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" 
    xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xml:base="http://localhost:8080/responder/interaction-hateoas-restbucks.svc/">
    <id>http://localhost:8080/responder/interaction-hateoas-restbucks.svc/Orders(1001)</id>
    <title type="text" />
    <updated>2013-10-28T23:36:07Z</updated>
    <author>
        <name />
    </author>
    <link rel="self" type="application/atom+xml;type=entry" title="order" href="Orders(1001)" />
    <link rel="http://relations.restbucks.com/update" type="application/atom+xml;type=entry" title="OrderUpdated" href="Orders(1001)" />
    <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Payment" type="application/atom+xml;type=entry" title="Payment" href="Payments(1001)" />
    <link rel="http://relations.restbucks.com/payment" type="application/atom+xml;type=entry" title="PaymentAccepted" href="Payments()" />
    <link rel="http://relations.restbucks.com/cancel" type="application/atom+xml;type=entry" title="OrderCancelled" href="Orders(1001)" />
    <category term="RestbucksModel.Order" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
    <content type="application/xml">
        <m:properties>
            <d:milk>abc</d:milk>
            <d:location>abc</d:location>
            <d:name>abc</d:name>
            <d:size>abc</d:size>
            <d:Id m:type="Edm.Int32">1001</d:Id>
            <d:quantity m:type="Edm.Int32">1</d:quantity>
        </m:properties>
    </content>
</entry>


Notice that you can pay this Order by using the 'payment' link:
    <link rel="http://relations.restbucks.com/payment" type="application/atom+xml;type=entry" title="PaymentAccepted" href="Payments()" />


This implementation of the Restbucks API is a nice little Hypermedia API example, but it could be improved in a few ways.  The first problem is how to get the initial Entry to post to /Orders.  I prefer to coin a new link relation for this task e.g. http://relations.restbucks.com/new bound to /Orders/new.  We could then default a few details and provide an Entry that could be filled out and POSTed back.

Another issue is the 'application/atom+xml' media type.  Perhaps I would like a json payload.  IRIS comes with a few different media type Providers, including 'application/hal+xml' and 'application/hal+json'.  HAL is an xml or json media type suitable for Hypermedia API's because it specifies the all important syntax for links.  The IRIS server can simply be configured with the HalProvider by modifying the spring-beans.xml as follows:

<bean id="halProvider" class="com.temenos.interaction.media.hal.HALProvider">
    <constructor-arg ref="edmMetadata" />
        <constructor-arg ref="stateMachine" />
</bean>
<bean class="com.temenos.interaction.winkext.RegistrarWithSingletons">
<property name="singletons">
<list>
<ref local="atomProvider" />
<ref local="edmxProvider" />
<ref local="serviceDocumentProvider" />
<ref local="errorProvider" />
<ref local="xhtmlProvider" />
<ref local="halProvider" />
  </list>
    </property>
        <property name="serviceRoots">
            <list>
                <ref local="ServiceRoot" />
                <ref local="Metadata" />
            </list>
        </property>
</bean>

You will also need to update your pom.xml to add the HALProvider dependency:

    <!-- Add dependency for our hal provider -->
    <dependency>
        <groupId>com.temenos.interaction</groupId>
        <artifactId>interaction-media-hal</artifactId>
        <version>0.4.0-SNAPSHOT</version>
        <scope>runtime</scope>
    </dependency>


With very little effort you could now use the HAL Browser to understand your API:
I hope you enjoyed your coffee.



Friday 6 September 2013

Resolving an issue with maven-tycho-plugin(P2) and xtext-maven-examples

My problem

A while ago now, I created a very simple project with examples of how to build XText projects with Maven / Tycho plugins.  https://github.com/aphethean/xtext-maven-examples  Thank you to everyone who has shown interest and starred the project - I'm utterly amazed that anyone has followed the project, but it must be useful to a few people who also struggled to get a maven build for XText working.  Anyway, back to the problem.  It stopped building!

A good build should always be stable; and only unstable if the code has changed.  In my case our internal project stopped working and when I checked my little example it too had stopped working.

My build error is as follows:
     [INFO] --- fornax-oaw-m2-plugin:3.4.0:run-workflow (default) @ org.xtext.example.mydsl ---
[INFO] Fornax Model Workflow Maven2 Plugin V3.4.0
[INFO] Executing workflow in forked mode.
[INFO] 0    INFO  StandaloneSetup    - Registering platform uri 'C:\projects\xtext-maven-examples1\2.3.0-example'
[INFO] 186611 INFO  StandaloneSetup    - Adding generated EPackage 'org.eclipse.xtext.xbase.XbasePackage'
[INFO] 187449 INFO  GenModelHelper     - Registered GenModel 'http://www.eclipse.org/Xtext/Xbase/XAnnotations' from 'platform:/resource/org.eclipse.xtext.xbase/model/Xbase.genmod
'
[INFO] 187453 INFO  GenModelHelper     - Registered GenModel 'http://www.eclipse.org/xtext/xbase/Xtype' from 'platform:/resource/org.eclipse.xtext.xbase/model/Xbase.genmodel'
[INFO] 187467 INFO  GenModelHelper     - Registered GenModel 'http://www.eclipse.org/xtext/xbase/Xbase' from 'platform:/resource/org.eclipse.xtext.xbase/model/Xbase.genmodel'
[INFO] 187468 INFO  GenModelHelper     - Registered GenModel 'http://www.eclipse.org/xtext/common/JavaVMTypes' from 'platform:/resource/org.eclipse.xtext.common.types/model/JavaV
ypes.genmodel'
[INFO] 188862 INFO  DirectoryCleaner   - Cleaning C:\projects\xtext-maven-examples1\2.3.0-example\org.xtext.example.mydsl\..\org.xtext.example.mydsl\src-gen
[INFO] 188866 INFO  DirectoryCleaner   - Cleaning C:\projects\xtext-maven-examples1\2.3.0-example\org.xtext.example.mydsl\..\org.xtext.example.mydsl.ui\src-gen
[INFO] 189378 INFO  LanguageConfig     - generating infrastructure for org.xtext.example.mydsl.MyDsl with fragments : ImplicitRuntimeFragment, ImplicitUiFragment, GrammarAccessFr
ment, EcoreGeneratorFragment, SerializerFragment, ResourceFactoryFragment, XtextAntlrGeneratorFragment, JavaValidatorFragment, ImportNamespacesScopingFragment, QualifiedNamesFrag
nt, BuilderIntegrationFragment, GeneratorFragment, FormatterFragment, LabelProviderFragment, OutlineTreeProviderFragment, QuickOutlineFragment, QuickfixProviderFragment, JavaBase
ontentAssistFragment, XtextAntlrUiGeneratorFragment, Junit4Fragment, RefactorElementNameFragment, TypesGeneratorFragment, XbaseGeneratorFragment, CodetemplatesGeneratorFragment,
mpareFragment
[INFO] 193899 INFO  GenModelHelper     - Registered GenModel 'http://www.xtext.org/example/mydsl/MyDsl' from 'platform:/resource/org.xtext.example.mydsl/src-gen/org/xtext/example
ydsl/MyDsl.genmodel'
[ERROR] 195973 ERROR CompositeGeneratorFragment - Explicit encoding was set but is not supported by the available version of the AntlrToolRunner.
[INFO] Please use the ANTLR parser generator in version 2.1 or better
[INFO] java.lang.IllegalStateException: Explicit encoding was set but is not supported by the available version of the AntlrToolRunner.
[INFO] Please use the ANTLR parser generator in version 2.1 or better
[ERROR]         at org.eclipse.xtext.generator.parser.antlr.AntlrToolFacade.runWithEncodingAndParams(AntlrToolFacade.java:178)
[ERROR]         at org.eclipse.xtext.generator.parser.antlr.XtextAntlrGeneratorFragment.generate(XtextAntlrGeneratorFragment.java:47)
[ERROR]         at org.eclipse.xtext.generator.CompositeGeneratorFragment.generate(CompositeGeneratorFragment.java:92)
[ERROR]         at org.eclipse.xtext.generator.LanguageConfig.generate(LanguageConfig.java:113)
[ERROR]         at org.eclipse.xtext.generator.Generator.generate(Generator.java:366)
[ERROR]         at org.eclipse.xtext.generator.Generator.invokeInternal(Generator.java:132)
[ERROR]         at org.eclipse.emf.mwe.core.lib.AbstractWorkflowComponent.invoke(AbstractWorkflowComponent.java:126)
[ERROR]         at org.eclipse.emf.mwe.core.lib.Mwe2Bridge.invoke(Mwe2Bridge.java:34)
[ERROR]         at org.eclipse.emf.mwe.core.lib.AbstractWorkflowComponent.invoke(AbstractWorkflowComponent.java:201)
[ERROR]         at org.eclipse.emf.mwe2.runtime.workflow.AbstractCompositeWorkflowComponent.invoke(AbstractCompositeWorkflowComponent.java:35)
[ERROR]         at org.eclipse.emf.mwe2.runtime.workflow.Workflow.run(Workflow.java:19)
[ERROR]         at org.eclipse.emf.mwe2.launch.runtime.Mwe2Runner.run(Mwe2Runner.java:102)
[ERROR]         at org.eclipse.emf.mwe2.launch.runtime.Mwe2Runner.run(Mwe2Runner.java:62)
[ERROR]         at org.eclipse.emf.mwe2.launch.runtime.Mwe2Runner.run(Mwe2Runner.java:52)
[ERROR]         at org.eclipse.emf.mwe2.launch.runtime.Mwe2Launcher.run(Mwe2Launcher.java:74)
[ERROR]         at org.eclipse.emf.mwe2.launch.runtime.Mwe2Launcher.main(Mwe2Launcher.java:35)
[INFO] 195995 INFO  JavaValidatorFragment - generating Java-based EValidator API
[ERROR] 196438 ERROR CompositeGeneratorFragment - Explicit encoding was set but is not supported by the available version of the AntlrToolRunner.
[INFO] Please use the ANTLR parser generator in version 2.1 or better
[INFO] java.lang.IllegalStateException: Explicit encoding was set but is not supported by the available version of the AntlrToolRunner.
[INFO] Please use the ANTLR parser generator in version 2.1 or better
[ERROR]         at org.eclipse.xtext.generator.parser.antlr.AntlrToolFacade.runWithEncodingAndParams(AntlrToolFacade.java:178)
[ERROR]         at org.eclipse.xtext.generator.parser.antlr.XtextAntlrUiGeneratorFragment.generate(XtextAntlrUiGeneratorFragment.java:85)
[ERROR]         at org.eclipse.xtext.generator.CompositeGeneratorFragment.generate(CompositeGeneratorFragment.java:92)
[ERROR]         at org.eclipse.xtext.generator.LanguageConfig.generate(LanguageConfig.java:113)
[ERROR]         at org.eclipse.xtext.generator.Generator.generate(Generator.java:366)
[ERROR]         at org.eclipse.xtext.generator.Generator.invokeInternal(Generator.java:132)
[ERROR]         at org.eclipse.emf.mwe.core.lib.AbstractWorkflowComponent.invoke(AbstractWorkflowComponent.java:126)
[ERROR]         at org.eclipse.emf.mwe.core.lib.Mwe2Bridge.invoke(Mwe2Bridge.java:34)
[ERROR]         at org.eclipse.emf.mwe.core.lib.AbstractWorkflowComponent.invoke(AbstractWorkflowComponent.java:201)
[ERROR]         at org.eclipse.emf.mwe2.runtime.workflow.AbstractCompositeWorkflowComponent.invoke(AbstractCompositeWorkflowComponent.java:35)
[ERROR]         at org.eclipse.emf.mwe2.runtime.workflow.Workflow.run(Workflow.java:19)
[ERROR]         at org.eclipse.emf.mwe2.launch.runtime.Mwe2Runner.run(Mwe2Runner.java:102)
[ERROR]         at org.eclipse.emf.mwe2.launch.runtime.Mwe2Runner.run(Mwe2Runner.java:62)
[ERROR]         at org.eclipse.emf.mwe2.launch.runtime.Mwe2Runner.run(Mwe2Runner.java:52)
[ERROR]         at org.eclipse.emf.mwe2.launch.runtime.Mwe2Launcher.run(Mwe2Launcher.java:74)
[ERROR]         at org.eclipse.emf.mwe2.launch.runtime.Mwe2Launcher.main(Mwe2Launcher.java:35)
[INFO] 196439 INFO  Junit4Fragment     - generating Junit4 Test support classes
[INFO] 196495 INFO  CompareFragment    - generating Compare Framework infrastructure
[INFO] 196731 INFO  Workflow           - Done.
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] org.xtext.example.mydsl.MyDsl - Parent ............ SUCCESS [3.759s]
[INFO] org.xtext.example.mydsl.MyDsl - Grammar ........... FAILURE [3:51.033s]
[INFO] org.xtext.example.mydsl.MyDsl - UI ................ SKIPPED
[INFO] org.xtext.example.mydsl.sdk ....................... SKIPPED
[INFO] org.xtext.example.mydsl.tests ..................... SKIPPED
[INFO] org.xtext.example.mydsl.MyDsl - Repackaged Eclipse plugins  SKIPPED
[INFO] org.xtext.example.mydsl.MyDsl - Generator ......... SKIPPED
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 7:13.750s
[INFO] Finished at: Thu Sep 05 16:27:04 BST 2013
[INFO] Final Memory: 98M/625M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.fornax.toolsupport:fornax-oaw-m2-plugin:3.4.0:run-workflow (default) on project org.xtext.example.mydsl: Workflow execution failed. -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException
[ERROR]
[ERROR] After correcting the problems, you can resume the build with the command
[ERROR]   mvn <goals> -rf :org.xtext.example.mydsl
C:\projects\xtext-maven-examples1\2.3.0-example\org.xtext.example.mydsl.parent>



Searching for a solution (ask Google approach)

After a couple false starts I started to get the feeling that I'd have to debug this one.  The error seemed to point to an encoding problem "Explicit encoding was set but is not supported by the available version of the AntlrToolRunner" - this just doesn't make sense.  My test source file might have a dodgy character it it (but the source hadn't changed remember) or it could also have been an Antlr version problem, but I'm still using the same configuration.

Here are a few sites I search for answers:
Maybe its something to do with Antlr?  No.  http://mojo.codehaus.org/antlr-maven-plugin/
Maybe the project pom configuration is wrong?  No.  http://kthoms.wordpress.com/2010/08/18/building-xtext-projects-with-maven-tycho/
Something to do with the P2 resolver that I don't understand very well?  Maybe.  http://stackoverflow.com/questions/6682028/use-dependencies-from-eclipse-p2-repository-in-a-regular-maven-build
More P2 knowledge required I think:  http://thomaskratz.blogspot.de/2013/04/consuming-emf-bundles-from-p2-with.html
Trying to understand Tycho a bit better:  http://eclipsesource.com/blogs/2012/08/26/building-an-eclipse-project-with-tycho-experience-report/
Tycho configuration issue?  Maybe.  http://stackoverflow.com/questions/15006721/configuration-issue-with-tycho-maven-tycho-build-extension-not-configured

After this problem and another problem where the build mysteriously broke, I was pretty sure that I was somehow picking up a different version of the XText libraries.  Turns out I was, and I simply didn't understand how the P2 resolving worked.  Basically the OSGi configuration in the MANIFEST.MF is used to form the maven dependencies.  I hadn't configured a specific version, this is the gold:
http://stackoverflow.com/questions/13288822/maven-tycho-takes-the-wrong-bundle-version/13290831#13290831
http://fusesource.com/docs/esb/4.4/esb_deploy_osgi/BestPractices-BuildBundles.html


My configuration

It's all there in the GitHub project, but the two main pieces were the pom.xml and the MANIFEST.MF


Debugging the problem

To debugging the problem I used mvn -X and the mvn dependency:tree option.  With these I could clearly see the wrong version of XText.

$ mvn -X clean install

[look for the java -classpath part of the output, you'll see the wrong jars for xtext]
[DEBUG] C:\Java\jdk1.7.0_25\jre\bin\java.exe -classpath 


$ mvn dependency-tree

[INFO] +- p2.eclipse-plugin:org.eclipse.emf.mwe.core:jar:1.2.1.v201309030422:system
[INFO] +- p2.eclipse-plugin:org.eclipse.emf.mwe.utils:jar:1.3.1.v201309030422:system
[INFO] +- p2.eclipse-plugin:org.eclipse.xtext:jar:2.4.3.v201309030823:system
[INFO] +- p2.eclipse-plugin:org.eclipse.xtext.generator:jar:2.4.3.v201309030823:system
[INFO] +- p2.eclipse-plugin:org.eclipse.xtext.common.types:jar:2.4.3.v201309030823:system
[INFO] +- p2.eclipse-plugin:org.eclipse.xtext.util:jar:2.4.3.v201309030823:system
[INFO] +- p2.eclipse-plugin:org.antlr.runtime:jar:3.2.0.v201101311130:system
[INFO] +- p2.eclipse-plugin:org.eclipse.emf.mwe2.lib:jar:2.4.1.v201309030422:system
[INFO] +- p2.eclipse-plugin:org.apache.log4j:jar:1.2.15.v201012070815:system
[INFO] +- p2.eclipse-plugin:org.eclipse.emf.mwe2.language:jar:2.4.1.v201309030840:system
[INFO] +- p2.eclipse-plugin:org.eclipse.emf.mwe2.launch:jar:2.4.1.v201309030840:system
[INFO] +- p2.eclipse-plugin:com.google.guava:jar:10.0.1.v201203051515:system
[INFO] +- p2.eclipse-plugin:org.eclipse.jdt.annotation:jar:1.0.1.v20130111-135246:system
[INFO] +- p2.eclipse-plugin:org.eclipse.jdt.debug:jar:jdi.jar:3.7.101.v20120913-153601:system
[INFO] +- p2.eclipse-plugin:org.eclipse.jdt.debug:jar:jdimodel.jar:3.7.101.v20120913-153601:system
[INFO] +- p2.eclipse-plugin:org.eclipse.xtend:jar:1.4.0.v201306110406:system
[INFO] +- p2.eclipse-plugin:org.eclipse.xpand:jar:1.4.0.v201306110406:system
[INFO] +- p2.eclipse-plugin:org.eclipse.xtext.xbase.lib:jar:2.4.3.v201309030823:system
[INFO] +- p2.eclipse-plugin:org.eclipse.xtend.lib:jar:2.4.3.v201309030823:system
[INFO] +- p2.eclipse-plugin:org.eclipse.xtend.typesystem.emf:jar:1.4.0.v201306110406:system
[INFO] +- p2.eclipse-plugin:org.eclipse.xtext.smap:jar:2.4.3.v201309030823:system
[INFO] \- p2.eclipse-plugin:org.eclipse.xtext.ecore:jar:2.4.3.v201309030823:system


Now for the solution

Understand how the OSGi configuration in the MANIFEST.MF works.  It's pretty simple syntax, but this page helped me:  http://fusesource.com/docs/esb/4.4/esb_deploy_osgi/BestPractices-BuildBundles.html

You can also use the manifest editor included with Eclipse and just set your version ranges appropriately

xtext-maven-examples\2.3.0-example\org.xtext.example.mydsl\META-INF\MANIFEST.MF
Require-Bundle: org.eclipse.xtext;bundle-version="[2.3.0,2.3.1]";visibility:=reexport,
 org.eclipse.xtext.xbase;bundle-version="[2.3.0,2.3.1]";resolution:=optional;visibility:=reexport,
 org.apache.commons.logging;bundle-version="1.0.4";resolution:=optional,
 org.eclipse.emf.codegen.ecore;resolution:=optional,
 org.eclipse.emf.mwe.utils;resolution:=optional,
 org.eclipse.emf.mwe2.launch;resolution:=optional,
 org.eclipse.xtext.util;bundle-version="[2.3.0,2.3.1]",
 org.eclipse.emf.ecore,
 org.eclipse.emf.common,
 org.antlr.runtime,

 org.eclipse.xtext.common.types;bundle-version="[2.3.0,2.3.1]"

Sound obvious, but make sure you change the version in all your MANIFEST.MFs.  I missed the test project and scratched my head for a few minutes more than I needed to:

Require-Bundle: org.xtext.example.mydsl,
 org.xtext.example.mydsl.ui,
 org.eclipse.core.runtime,

 org.eclipse.xtext.junit4;bundle-version="[2.3.0,2.3.1]",
 org.eclipse.ui.workbench;resolution:=optional


Now the build will work and hopefully carry on working!


Thursday 11 April 2013

Using an embedded Mule to lookup a JMS connection factory with Spring on JBoss 4.2.3


My problem

I recently had the pleasure of working with Mule.  In my use case I wanted to lookup a JMS queue, post to that queue and receive a reply – a synchronous JMS bridge pattern.  This would be packaged into a web application that uses Spring, and deployed on JBoss 4.2.3.  With such a “simple” solution I can’t see why I ran into problems…


My error in the JBoss console was as follows:
Caused by: org.mule.retry.RetryPolicyExhaustedException: Unsupported ConnectionFactory type: $Proxy286
                at org.mule.retry.policies.AbstractPolicyTemplate.execute(AbstractPolicyTemplate.java:105)
                at org.mule.transport.AbstractConnector.connect(AbstractConnector.java:1616)
                at org.mule.transport.jms.JmsConnector.connect(JmsConnector.java:460)
                at org.mule.transport.AbstractConnector.start(AbstractConnector.java:428)
                at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
                at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
                at java.lang.reflect.Method.invoke(Method.java:601)
                at org.mule.lifecycle.phases.DefaultLifecyclePhase.applyLifecycle(DefaultLifecyclePhase.java:225)
                at org.mule.lifecycle.RegistryLifecycleManager$RegistryLifecycleCallback.onTransition(RegistryLifecycleManager.java:276)
                at org.mule.lifecycle.RegistryLifecycleManager.invokePhase(RegistryLifecycleManager.java:155)
                at org.mule.lifecycle.RegistryLifecycleManager.fireLifecycle(RegistryLifecycleManager.java:126)
                at org.mule.registry.AbstractRegistryBroker.fireLifecycle(AbstractRegistryBroker.java:80)
                at org.mule.registry.MuleRegistryHelper.fireLifecycle(MuleRegistryHelper.java:120)
                at org.mule.lifecycle.MuleContextLifecycleManager$MuleContextLifecycleCallback.onTransitio



Searching for a solution (ask Google approach)

As usual I ended up trawling through loads of forums, Stackoverflow, MuleSoft, etc and of course the Mule documentation.  The main stumbling block seemed to be my desire to use the local JNDI lookup.  Mule is predominantly used for integration and as such will usually need to run standalone.  However, my use case called for it to run embedded in my web application and I didn’t want to specify the JNDI configuration of each Web container I choose to deploy on.

Mule docs:
Possible issues:





My configuration

My configuration was pretty straight forward.

web.xml

<!-- Mule Bridge Settings -->
<resource-ref id="ResourceRef_jmsConnectionFactory">
       <description>Used to get connections to JMS queue, using JMS as a bridge to Mule</description>
       <res-ref-name>jms/jmsConnectionFactory</res-ref-name>
       <res-type>javax.jms.ConnectionFactory</res-type>
       <res-auth>Container</res-auth>
</resource-ref>

jboss-web.xml

    <resource-ref>
        <res-ref-name>jms/jmsConnectionFactory</res-ref-name>
        <res-type>javax.jms.ConnectionFactory</res-type>
        <jndi-name>java:/ConnectionFactory</jndi-name>
    </resource-ref>

uil2-service.xml

   <mbean code="org.jboss.naming.LinkRefPairService"
          name="jboss.jms:alias=QueueConnectionFactory">
      <attribute name="JndiName">QueueConnectionFactory</attribute>
      <attribute name="RemoteJndiName">ConnectionFactory</attribute>
      <attribute name="LocalJndiName">java:/JmsXA</attribute>
      <depends>jboss:service=Naming</depends>
   </mbean>




Debugging the problem

Debugging the problem was a little tedious as I mostly received exceptions in the JBoss log and not much else.  In hindsight, perhaps attaching a debugger and stepping through the Mule source might have been quicker. The main points were to confirm that I could connect to the queue, I could lookup the queue in my web application, and I could  



Now for the solution

Unfortunately we need one new class in our web application to help lookup the queue and this class depends on Spring.

package my.package;

import javax.naming.NamingException;

import org.mule.transport.jms.jndi.AbstractJndiNameResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jndi.JndiTemplate;

public class SpringJndiNameResolver extends AbstractJndiNameResolver implements InitializingBean {
    private static Logger logger = LoggerFactory.getLogger(SpringJndiNameResolver.class);
    private JndiTemplate jndiTemplate;

    @Override
    public void afterPropertiesSet() throws Exception {
        if (jndiTemplate == null) {
            jndiTemplate = new JndiTemplate();
        }
    }

    @Override
    public Object lookup(String name) throws NamingException {
        Object object = null;
        if (name != null) {
            logger.debug("Looking up name "+name);
            object = jndiTemplate.lookup(name);
            logger.debug("Object "+object+" found for name "+name);
        }
        return object;
    }

    public JndiTemplate getJndiTemplate() {
        return jndiTemplate;
    }

    public void setJndiTemplate(JndiTemplate jndiTemplate) {
        this.jndiTemplate = jndiTemplate;
    }
}

mule-config.xml (client or local side)

<?xml version="1.0" encoding="UTF-8"?>

<mule xmlns:jms="http://www.mulesoft.org/schema/mule/jms"
     xmlns:file="http://www.mulesoft.org/schema/mule/file"
     xmlns:vm="http://www.mulesoft.org/schema/mule/vm"
     xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation" xmlns:spring="http://www.springframework.org/schema/beans" version="EE-3.3.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="
http://www.mulesoft.org/schema/mule/vm http://www.mulesoft.org/schema/mule/vm/current/mule-vm.xsd
http://www.mulesoft.org/schema/mule/file http://www.mulesoft.org/schema/mule/file/current/mule-file.xsd
http://www.mulesoft.org/schema/mule/jms http://www.mulesoft.org/schema/mule/jms/current/mule-jms.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd ">
    <spring:beans>
        <spring:bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate"/>
    </spring:beans>

     <!--
     The following suggestion works:
     http://stackoverflow.com/questions/12461714/mule-embedded-use-the-containers-own-jndiinitialfactory
     -->
     <jms:connector
           name="local-jms-connector"
           connectionFactoryJndiName="java:comp/env/jms/jmsConnectionFactory"
           jndiDestinations="true"
           forceJndiDestinations="true"
           specification="1.1" doc:name="JMS">
    <jms:custom-jndi-name-resolver class="com.temenos.hothouse.mule.SpringJndiNameResolver">
           <spring:property name="jndiTemplate" ref="jndiTemplate"/>
    </jms:custom-jndi-name-resolver>   
     </jms:connector>

<!--
throws exception with "Unsupported ConnectionFactory type: $Proxy338"
     <spring:bean id="jmsConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
           <spring:property name="jndiName" value="java:env/comp/jms/jmsConnectionFactory"/>
           <spring:property name="lookupOnStartup" value="false"/>
           <spring:property name="cache" value="true"/>
           <spring:property name="proxyInterface" value="javax.jms.ConnectionFactory"/>
     </spring:bean>

    <jms:connector name="local-jms-connector" connectionFactory-ref="jmsConnectionFactory"
        jndiDestinations="true" forceJndiDestinations="true" disableTemporaryReplyToDestinations="true" doc:name="JMS Local">
    </jms:connector>
 -->   
   
    <!-- TODO Can't seem to get the local jms connection factory lookup to work
    (even this remote way barfs with the same exception as above) -->
<!--
    <jms:connector name="remote-jms-connector" jndiInitialFactory="org.jnp.interfaces.NamingContextFactory"
        jndiProviderUrl="jnp://127.0.0.1:1099"
        maxRedelivery="1"
        connectionFactoryJndiName="QueueConnectionFactory" jndiDestinations="true"
        forceJndiDestinations="true" disableTemporaryReplyToDestinations="true" doc:name="JMS">
    </jms:connector>
 -->

    <flow name="vm://Forecast-view-command" doc:name="vm://Forecast-view-command">
        <vm:inbound-endpoint exchange-pattern="request-response" path="Forecast-view-command" doc:name="VM"/>
        <logger message="VM Request:  #[payload]" level="INFO" doc:name="Logger"/>
        <set-payload value="#[mule:message.payload(java.lang.String)]" doc:name="Set Payload"/>
        <file:outbound-endpoint path="c:\logs\" outputPattern="forecast-request-#[function:datestamp].xml" responseTimeout="10000" doc:name="File"/>
        <jms:outbound-endpoint exchange-pattern="request-response" queue="queue/MuleRequest" doc:name="MuleRequest" connector-ref="local-jms-connector" />
        <file:outbound-endpoint path="c:\logs\" outputPattern="forecast-response-#[function:datestamp].xml" responseTimeout="10000" doc:name="File"/>
        <jms:jmsmessage-to-object-transformer doc:name="JmsMessage to Object"/>
        <object-to-string-transformer doc:name="Object to String"/>
        <logger message="Response:  #[payload]" level="INFO" doc:name="Logger"/>
    </flow>

</mule>


mule-config.xml (server or remote side)

<?xml version="1.0" encoding="UTF-8"?>

<mule xmlns:mulexml="http://www.mulesoft.org/schema/mule/xml" xmlns:stdio="http://www.mulesoft.org/schema/mule/stdio"
      xmlns:jms="http://www.mulesoft.org/schema/mule/jms" xmlns:data-mapper="http://www.mulesoft.org/schema/mule/ee/data-mapper" xmlns:file="http://www.mulesoft.org/schema/mule/file" xmlns:tracking="http://www.mulesoft.org/schema/mule/ee/tracking" xmlns:vm="http://www.mulesoft.org/schema/mule/vm" xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation" xmlns:spring="http://www.springframework.org/schema/beans" version="EE-3.3.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="
http://www.mulesoft.org/schema/mule/xml http://www.mulesoft.org/schema/mule/xml/current/mule-xml.xsd
http://www.mulesoft.org/schema/mule/file http://www.mulesoft.org/schema/mule/file/current/mule-file.xsd
http://www.mulesoft.org/schema/mule/stdio http://www.mulesoft.org/schema/mule/stdio/current/mule-stdio.xsd
http://www.mulesoft.org/schema/mule/jms http://www.mulesoft.org/schema/mule/jms/current/mule-jms.xsd
http://www.mulesoft.org/schema/mule/ee/data-mapper http://www.mulesoft.org/schema/mule/ee/data-mapper/current/mule-data-mapper.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/ee/tracking http://www.mulesoft.org/schema/mule/ee/tracking/current/mule-tracking-ee.xsd
http://www.mulesoft.org/schema/mule/vm http://www.mulesoft.org/schema/mule/vm/current/mule-vm.xsd ">
    <custom-transformer class="org.mule.transformer.codec.XmlEntityDecoder" name="XmlEntityDecoder" doc:name="Java"/>

    <jms:connector name="remote-jms-connector" jndiInitialFactory="org.jnp.interfaces.NamingContextFactory"
        jndiProviderUrl="jnp://127.0.0.1:1099"
        maxRedelivery="1"
        connectionFactoryJndiName="java:/QueueConnectionFactory" jndiDestinations="true"
        forceJndiDestinations="true" disableTemporaryReplyToDestinations="true" doc:name="JMS">
        <!--retry:forever-policy frequency="2000"/-->
    </jms:connector>
    <data-mapper:config name="wsdlresponsetoentityresponse" transformationGraphPath="wsdlresponsetoentityresponse.grf" doc:name="DataMapper"/>
    <data-mapper:config name="reqtores" transformationGraphPath="reqtores.grf" doc:name="reqtores"/>

    <flow name="ProcessFromQueue" doc:name="ProcessFromQueue">
        <jms:inbound-endpoint queue="queue/MuleRequest" connector-ref="remote-jms-connector" doc:name="Request" />
        <set-payload value="#[mule:message.payload(java.lang.String)]" doc:name="Set Payload"/>
        <logger message="Before WSDL #[payload]" level="INFO" doc:name="Logger"/>
        <set-payload value="#[xpath('//viewcommand/pathparameters/postcode').text]" doc:name="Extract PostCode"/>
        <set-variable variableName="Requested_PostCode" value="#[payload]" doc:name="Set Requested_PostCode"/>
        <logger message="Requested_PostCode:  #[Requested_PostCode]" level="INFO" doc:name="Logger"/>
        <flow-ref name="ForecastWSDL" doc:name="ForecastWSDL"/>
        <logger message="Latest Forecast:  #[xpath('//forecastResult/forecasts/com.cdyne.ws.weatherws.Forecast[1]/desciption').text]" level="INFO" doc:name="Logger"/>
        <data-mapper:transform config-ref="wsdlresponsetoentityresponse" doc:name="DataMapper">
            <data-mapper:input-arguments>
                <data-mapper:input-argument key="Requested_PostCode">#[Requested_PostCode]</data-mapper:input-argument>
            </data-mapper:input-arguments>
        </data-mapper:transform>
        <object-to-string-transformer doc:name="Object to String"/>
        <jms:outbound-endpoint queue="queue/MuleResponse" connector-ref="remote-jms-connector" doc:name="Response"/>
    </flow>
    <sub-flow name="ForecastWSDL" doc:name="ForecastWSDL">
        <outbound-endpoint exchange-pattern="request-response" address="wsdl-cxf:http://wsf.cdyne.com/WeatherWS/weather.asmx?WSDL&amp;method=GetCityForecastByZIP" doc:name="WSDL"/>
        <mulexml:object-to-xml-transformer doc:name="Object to XML"/>
        <stdio:outbound-endpoint system="OUT" doc:name="STDIO"/>
        <file:outbound-endpoint path="c:\logs\" outputPattern="wsdl-response-#[function:datestamp].xml" responseTimeout="10000" doc:name="File"/>
<!--         <file:outbound-endpoint path="c:\logs" outputPattern="jms-response-#[function:datestamp].xml" responseTimeout="10000" doc:name="File"/>
 -->
     </sub-flow>
    <flow name="mule-configFlow1" doc:name="mule-configFlow1">
        <data-mapper:transform config-ref="reqtores" doc:name="DataMapper">
            <data-mapper:input-arguments>
                <data-mapper:input-argument key="Requested_PostCode">#[Requested_PostCode]</data-mapper:input-argument>
            </data-mapper:input-arguments>
        </data-mapper:transform>
    </flow>
</mule>