Wednesday 13 February 2013

Create an OData service from EDMX



This entry in my blog is part of my 30 blogs in 30 days  Fortunately I didn't commit to 30 consecutive days...  The one I'm trying to 'tick off' today is the "Create Mock Responder" item.  Here my idea was to show how we could create an OData service, from an EDMX file, with an in-memory database serving up mock data.  This might be a really useful idea if your User-Agent development team cannot see the live / real data due to security restrictions.

So here we go:

Starting with an existing EDMX, demonstrate how to create a mock responder


The main point of this blog is to show how to create an OData service if you already have an EDMX file.  An EDMX file can be created using Visual Studio or, as you may already know, any existing OData service has a $metadata resource that serves up metadata in EDMX format.  The service that I'm going to 'clone' today is currently available online here with many others:

  1. Follow the instructions for creating an IRIS quickstart project, do step 1 and stop there.
    mvn archetype:generate -DgroupId=
    com.mycorp.test -DartifactId=DemoService -DarchetypeRepository=https://repository-aphethean.forge.cloudbees.com/snapshot/ -DarchetypeGroupId=com.temenos.interaction -DarchetypeArtifactId=interaction-sdk-archetype -DarchetypeVersion=0.2.0-SNAPSHOT -DinteractiveMode=false


    Our first little gotcha with this service is that we don't use the name you provided for the RIM generation - I get the following error if I specify anything other than DemoService (we'll fix this later https://github.com/aphethean/IRIS/issues/41):
    [ERROR] Failed to execute goal com.temenos.interaction:interaction-sdk-rim-plugin:0.2.0-SNAPSHOT:rim-generate (default) on project ODataTestService: Execution default of goal com.temenos.interaction:interaction-sdk-rim-plugin:0.2.0-SNAPSHOT:rim-generate failed: java.io.FileNotFoundException: C:\temp\ODataTestService\src\main\resources\ODataTestService.rim (The system cannot find the file specified) -> [Help 1]

  2. Save the service metadata as service.edmx into your new project directory.  This file name is configurable, just look in the pom.xml

    http://services.odata.org/OData/OData.svc/$metadata
  3. Now execute Step 2 of the quickstart

    mvn interaction-sdk:gen


    Our second little challenge with this service is that we don't seem to support complex types - I get the following error:
    [ERROR] Failed to execute goal com.temenos.interaction:interaction-sdk-plugin:0.2.0-SNAPSHOT:gen (default-cli) on project ODataTestService: Execution default-cli of goal com.temenos.interaction:interaction-sdk-plugin:0.2.0-SNAPSHOT:gen failed: Entity property type ODataDemo.Address not supported -> [Help 1]
    In the interest of this blog I can simply comment it out and carry on (we'll fix it later https://github.com/aphethean/IRIS/issues/42):

    <EntityType Name="Supplier">
    ...<!--        <Property Name="Address" Type="ODataDemo.Address" Nullable="false" /> -->
  4. Supply some test data in src/main/resources/META-INF/responder_insert.sql

    If you try to start your service now you'll probably see an error like the following:
    2013-02-13 16:13:20.637:WARN::Nested in org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [META-INF/resourcemanager-context.xml]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: ODataDemo.Product.null in ODataDemo.Category.Products:org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: ODataDemo.Product.null in ODataDemo.Category.Products
    At the moment we need a little help to figure out the entity relationships.  You could fix the generated JPA classes, or you could add the EDMX referential constraint, join ID columns, and regenerate.  That's what we'll do here, by merging the following into our EDMX:

    Add the following properties to Product:
            <Property Name="categoryID" Type="Edm.Int32" Nullable="false" />
        <Property Name="supplierID" Type="Edm.Int32" Nullable="false" />

    Update the following associations:
        <Association Name="Product_Category_Category_Products">        <End Role="Product_Category" Type="ODataDemo.Product" Multiplicity="*" />        <End Role="Category_Products" Type="ODataDemo.Category" Multiplicity="0..1" />                <ReferentialConstraint>                    <Principal Role="Category_Products">                        <PropertyRef Name="ID"/>                    </Principal>                    <Dependent Role="Product_Category">                        <PropertyRef Name="categoryID"/>                    </Dependent>                </ReferentialConstraint>      </Association>      <Association Name="Product_Supplier_Supplier_Products">        <End Role="Product_Supplier" Type="ODataDemo.Product" Multiplicity="*" />        <End Role="Supplier_Products" Type="ODataDemo.Supplier" Multiplicity="0..1" />                <ReferentialConstraint>                    <Principal Role="Supplier_Products">                        <PropertyRef Name="ID"/>                    </Principal>                    <Dependent Role="Product_Supplier">                        <PropertyRef Name="supplierID"/>                    </Dependent>                </ReferentialConstraint>      </Association>
  5. Start your service
    mvn jetty:run
  6. Go to http://localhost:8080/responder/

Want to see this in action? View it now on YouTube

Cloudbees private repository

My problem

Recently I was attempting to use a Cloudbees 'clickstart' to help get users of my project up and running.  This has worked in the past and was going great, but then for some unknown reason my maven project dependencies could not be found.

My error was the typical maven dependency error:
cause : Failed to read artifact descriptor for com.temenos.interaction:interaction-sdk-rim-plugin:jar:0.2.0-SNAPSHOT
Stack trace : 
org.apache.maven.plugin.PluginResolutionException: Plugin com.temenos.interaction:interaction-sdk-rim-plugin:0.2.0-SNAPSHOT or one of its dependencies could not be resolved: Failed to read artifact descriptor for com.temenos.interaction:interaction-sdk-rim-plugin:jar:0.2.0-SNAPSHOT


Searching for a solution (ask Google approach)

There didn't seem to be any information related to my problem and I started to doubt that I could actually use a 'repository' declaration in my pom.xml with Cloudbees.  Thankfully that was just a red herring and Cloudbees supports pom.xml repository declarations just fine.  Whether their usage is a good idea or not is a separate question.


Debugging the issue

The best thing you can do in these situations is turn on some debugging and see what's going on.  I changed my Jenkins build target to '-X clean install'.  This shows all the dependency resolution information.  Sure enough my repo was never accessed, but I still could tell why.  I happened to notice that the private repo being access had a familiar id 'cloudbees-private-snapshot-repository'.  This id happened to be the same id I choose to use for my external maven repository.  Change the name and bobs your uncle, it all works again.


My configuration

Didn't work:
<repositories>
<repository>
<id>cloudbees-private-snapshot-repository</id>
<url>https://repository-aphethean.forge.cloudbees.com/snapshot/</url>
<releases><enabled>true</enabled></releases> 
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>

Works:
<repositories>
<repository>
<id>cloudbees-aphethean-snapshot-repository</id>
<url>https://repository-aphethean.forge.cloudbees.com/snapshot/</url>
<releases><enabled>true</enabled></releases> 
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>