Tracking App for Playbook using RESTful Powerbuilder Web Service

gb_thumb

Let me tell you a story about a German US immigrant called Jason driving taxi in New York (this is the repeating excuse for some bad English pitfalls I’ll run into). He was unhappy about his CEO because he gets always calls like “Where you are?”, “How many guests do you drive?” and “Where is daily route document?”.

He read Yakov’s article about PowerBuilder and RESTful Web Services and wanted to learn more about RESTful Web Services in Powerbuilder. He asked himself : “Is it possible to create it instead just consuming it?”.

Yes it is! Let me show you how deep the rabbit hole goes Zwinkerndes Smiley.


What means RESTful?

514NtpATBhL._SL110_To get a feeling what’s the idea of RESTful Web Services I would recommend you two sources. The term “RESTful” is like “object oriented” and means “using REST” (Representational State Transfer). Maybe the “ful” to the REST is just invited to make it easier to Google.  If you want to get a quick overview, visit the this MSDN document. It’s specialized to .NET developers and this should include PowerBuilder developers too.

If you want to dive into the topic I would suggest “RESTful Web Services” by Leonard Richardson and Sam Ruby. It has a lot of theoretical stuff into but it’s nice to read and it will offer you what’s the advantages of REST compared to SOAP or RPC.

What PowerBuilder can do?

If you ask, what PowerBuilder Classic can do, then the answer is: nothing. I didn’t find a way to get a RESTful Web Service in PB without thinking about some strange converter and wrapper stuff. Nobody wants to see something like this.

The door we use was opened when Sybase offers WCF Services in PowerBuilder .NET. The WCF Service brings us all the cool stuff from .NET including the way to configure or endpoint in that way we want.

So let’s code and implement the first WCF Service.

The CEO-Server

It sounds like we have to implement data-catching server for Jason’s boss. He wants to see where the cabs are. (are cabs only New York taxis? Someone could post it in the comments)

So let’s think about some methods:

n_taxi // that's the name of the service

n_taxi.of_setPosition(
    as_cab,            // Name of cab
    al_guest_count,    // count of guests
    adc_longitude,     // longitude
    adc_latitude,      // latitude
    al_accuracy,       // accuracy
    adc_speed,         // important! This is New York city! 
    al_catchmillis )   // the timestamp

n_taxi.of_getPosition(
    as_id )            // maybe we want to get a position

n_taxi.of_renderPdf(
    as_cab )           // it renders a PDF of a report 
                       // Jason's CEO can print and archive

That’s nice. This is all Jason’s CEO need to get happy. To save all this data a database would be nice. I prefer MS SQL. Here is the script to create table for positions.

CREATE TABLE position (
    id INTEGER PRIMARY KEY IDENTITY,
    cab NVARCHAR(50),
    guest_count INTEGER,
    longitude DECIMAL(9,6),
    latitude DECIMAL(9,6),
    accuracy INTEGER,
    speed DECIMAL(4,1));

To let someone see this database (without being admin) I configure the new user taxi with pass dontdrinkanddrive. Keep in mind the user needs read and write permission to the database.

Creating WCF Service in PowerBuilder

Creating a WCF Service in PowerBuilder .NET is easy. You can select File->New in your Workspace. There you can navigate to Target->WCF Service. Follow the wizard and the job is done.

The initial protocol WCF is configured for is SOAP. SOAP? Are you serious? This bloated inflexible monster? I am just kidding Zwinkerndes Smiley

But if we want to use a clean JSON response or request the first we have to do is to change the <service_name>.config like this.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="Sybase.PowerBuilder.WCFNVO.n_taxi" behaviorConfiguration="ServiceNameBehavior">
        <!-- old stuff! <endpoint address="" binding="basicHttpBinding" contract="Sybase.PowerBuilder.WCFNVO.n_taxi" />-->
        <!-- old stuff! <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />-->
        
        <!-- new stuff -->
        <endpoint address="" behaviorConfiguration="NewBehavior" binding="webHttpBinding" bindingConfiguration="" contract="Sybase.PowerBuilder.WCFNVO.n_taxi" />
      </service>
    </services>
    <!--For debugging purposes set the includeExceptionDetailInFaults attribute to true-->
    <!-- old stuff!
    <behaviors>
      <serviceBehaviors>
        <behavior name="ServiceNameBehavior">
          <serviceMetadata httpGetEnabled="True" />
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>-->


    <!-- new stuff -->
    <behaviors>
      <endpointBehaviors>
        <behavior name="NewBehavior">
          <webHttp />
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="ServiceNameBehavior">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

After editing you can implement your service. You don’t have to do it by your own. The completed project is ready to download in GitHub.

Is it RESTful now?

No. It’s not really/completely/full RESTful to have a method of_setPosition and of_getPosition and different URLs to invoke them. So it is necessary to configure some more stuff in PB after coding is done.

Let’s have a look in the Project Painter.

Taxi - PowerBuilder

In PB 12.5 .NET it is possible to define Operation Attributes to every public function. This is the chance to let this service behave like a RESTful Web Service.

Please setup your method in area WebInvoke.

of_getPosition

BodyStyle WebMessageBodyStyle.Wrapped
Method GET
ResponseFormat WebMessageFormat.Json
UriTemplate position/{as_id}

of_renderPdf

BodyStyle WebMessageBodyStyle.Wrapped
Method GET
ResponseFormat WebMessageFormat.Json
UriTemplate position/render/pdf/{as_cab}

of_setPosition

BodyStyle WebMessageBodyStyle.Wrapped
Method POST
ResponseFormat WebMessageFormat.Json
UriTemplate position

The Service is now accessible under http://myserver/position to insert data and http://myserver/position/<id> to get some data. A request to http://myserver/position/render/pdf/<cab> will response with a URL to download a PDF of a report.

That’s all the magic. Keep in mind to set the Service Attribute RequirementMode to AspNetCompatibilityRequirementsMode.Allowed.

Id didn’t post some PowerBuilder code here. Visit GitHub to browser and download the project.

The CEO App

James is dozing in his cab when his CEO is calling: “Good job, this peace of software! But what’s the f****** trick to get this f****** report where your lazy ass was this morning at 8 o’clock?”. James babbles: “It’s easy let me code it for you.”.

Taxi - Windows Internet Explorer_2012-03-07_22-39-51

http192.168.30.2482taxiceoTAX_667

 

 

 

 

 

 

<html>
<head>
    <title>Taxi</title>
    <link rel="stylesheet" href="jquery.mobile-1.0.css" />
    <script type="text/javascript" src="jquery-1.7.1.js"></script>
    <script type="text/javascript">
        $(document).ready(function(){
            $('#searchButton').click(function(){
                $.getJSON('http://homeserver:82/_taxi/n_taxi.svc/position/render/pdf/' + $('#taxi').val(),function(data){
                    $.each(data,function(key,val){
                        location.href = val;
                    });
                });
            });
        });
    </script>
</head>
<body>
<input type="text" id="taxi" value="TAX 667">
<button type="submit" data-theme="a" id="searchButton">Report</button>
</body>
</html>

 

This really simple Web App can be used to let James’ boss enter a cab and get the report of positions.

If you want to implement this site it’s necessary to host it under the same top level domain then the service. Many browser don’t allow cross-site-requests for security reasons.

The Tracking App

If you followed my blog you know that there is one tablet I prefer for app development at the moment. It’s RIM’s Blackberry Playbook. The easiest way to code application for Playbook is to use HTML5 an JavaScript. So in one Script I get my Tracking App to let James’ CEO know where his lazy … I mean … where he was.

Ripple_2012-03-07_22-35-52

 

<html>
<head>
    <title>Taxi</title>
    <link rel="stylesheet" href="jquery.mobile-1.0.css" />
    <script type="text/javascript" src="jquery-1.7.1.js"></script>
    <script type="text/javascript" src="jquery.mobile-1.0.js"></script>
    <script type="text/javascript">
        var running;
        
        function postPosition(){
            var taxi = $('#taxi').val();
            var guestCount = $('#guestCount').val();
            var longitude = $('#longitude').val();
            var latitude = $('#latitude').val();
            var accuracy = $('#accuracy').val();
            var speed = $('#speed').val();
            var millis = $('#millis').val();
        
            $.ajax({
                type: "POST",
                url: "http://homeserver:82/_taxi/n_taxi.svc/position",
                contentType: "application/json",
                data: '{ "as_cab": "' + taxi + '", ' +
                        '"al_guest_count": ' + guestCount + ', ' +
                        '"adc_longitude": ' + longitude + ', ' +
                        '"adc_latitude": ' + latitude + ', ' +
                        '"al_accuracy": ' + accuracy + ', ' +
                        '"adc_speed": ' + speed + ', ' +
                        '"al_catchmillis": ' + millis + '}'
            });
        }
        
        $(document).ready(function(){
            $('#startTracking').click(function(){
                running = window.setInterval(function(){
                    navigator.geolocation.getCurrentPosition(function(position){
                        $('#longitude').val(position.coords.longitude);
                        $('#latitude').val(position.coords.latitude);
                        $('#speed').val(position.coords.speed);
                        $('#accuracy').val(position.coords.accuracy);
                        $('#millis').val(new Date().getTime());
                        
                        postPosition();
                    });
                },10000);
                
                $('#statusTracking').html("Tracking started!");
            });

            $('#stopTracking').click(function(){            
                window.clearInterval(running);
                $('#statusTracking').html("Tracking stopped!");
            });
        });
    </script>
</head>
<body>
<div data-role="fieldcontain">
    <label for="taxi">Taxi:</label>
    <input type="text" id="taxi" value="TAX 667">
        
    <label for="guestCount">Guest Count:</label>
    <input type="text" id="guestCount" value="4">

    <label for="longitude">Longitude:</label>
    <input type="text" id="longitude" value="1.567">
    
    <label for="latitude">Latitude:</label>
    <input type="text" id="latitude" value="2.345">
    
    <label for="accuracy">Accuracy:</label>
    <input type="text" id="accuracy" value="5">
    
    <label for="speed">Speed:</label>
    <input type="text" id="speed" value="60">
    
    <label for="millis">Millis:</label>
    <input type="text" id="millis" value="100">
</div>

<fieldset class="ui-grid-b">
    <div class="ui-block-a">
        <button data-theme="a" id="startTracking">Start</button>
    </div>
    <div class="ui-block-b">
        <button data-theme="a" id="stopTracking">Stop</button>
    </div>
</fieldset>

<div id="statusTracking" >
    Tracking stopped!
</div>

</body>
</html>



 

Not everyone have a Playbook (but everyone should Zwinkerndes Smiley ). So the easiest way to test this app is to use the Ripple Emulator. You can download the stand-alone application here or a Chrome plugin here.

You should host this app on same server like ceo app. It’s also possible to package it to run client-side but not in emulator.

Conclusion

James is happy. James’ boss is happy. The PowerBuilder developer is happy? Not at all.

I am tired about discussing the quality of PowerBuilder 12.5 .NET. It’s bad and not like an professional high prized development tool.

There are two extremely different technologies in this project. On the one hand there is the big bloated PowerBuilder with all the DataWindow stuff the heavy IDE a lot of Painters to help you get you job is done.

On the other hand there is HTML5, JavaScript, JQuery and an Editor. Not very comfortable but light and fast.

On the one hand we have PowerScript with hard and old ideas, not very elegant and in many ways outdated.

On the other hand we have JavaScript with a high flexible but sometimes hard to understand and a potential to produce unreadable code.

And the all over question for PowerBuilder developers in these days: Why I can’t use .NET or Java for this job?

Happy coding! And we’ll see how long PowerBuilder will live, Yakov Zwinkerndes Smiley

Links

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>