Stephen Smith's Blog

Musings on Machine Learning…

Posts Tagged ‘java

My First Swift Application

with 3 comments

Introduction

Back in 2013 I purchased a MacBook Air, installed XCode and wrote a small Objective-C program to draw a simple fractal on an iPad. Which I then blogged on here. Now we are a few years later and I thought I would give Apple’s new programming language Swift a try and see how iOS/OSX development has evolved as a result. For more details on Koch snowflakes and what I the program does, check out my original article.

The Evolution from Objective-C to Swift

Objective-C was one of the first object oriented extensions to the C Programming language. Its first main usage was by Steve Jobs and NeXT Computer as the primary programming language of the NeXTStep operating system which later became OS/X and iOS. Objective-C had a lot of innovative ideas behind it like treating everything as sending messages between objects (rather than directly calling methods). But then C++ came along and became the main standard for an object oriented extension to C.

One of the complaints against C is that it puts a lot of burden on programmers since they are dealing with memory and the computer architecture at a very low level. You are manipulating memory pointers directly, allocating memory buffers, etc. This is all very powerful and produces very fast, compact and efficient programs. But there is a lot of room for error, since making a mistake here will lead to buffer overruns, program crashes and such. In the days of standalone computers this was annoying but not fatal. Now with the internet, these sorts of problems lead to security vulnerabilities and server crashes. All that being said, if you have skilled programmers, C, Objective C and C++ are very powerful and you can produce great reliable programs with them.

To address these problems, Sun Microsystems invented Java. Java was essentially an object oriented extension of C, but with all the pointers and low level memory access removed. Java then included a large standard class library to give an alternate way of doing all the low level things you did in C. Java compiled to P-Code which ran on a Java Virtual Machine. This could then be sandboxed to allow greater security. To some degree this was to try to reach a compromise between scripting languages like JavaScript or VBScript and true fully compiled languages like C++. I.e. to make it easier to program with less gotchas, but still maintain the compiler checks for correctness and modules features required to program large systems.

Microsoft saw the potential and growing success of Java and came up with their own competing system namely C#. C# was initially very similar to Java with a very similar class library. Microsoft actually originally had their own implementation of Java, but it really sucked and it was easier to move true Java programs to C# than it was to Microsoft Java. Similar to the Java VM, C# runs on Microsoft’s .Net framework which isolates you from the underlying operating system.

Java got off to a great start, but as Sun workstations went into decline, Sun couldn’t put the necessary R&D resources into supporting Java and forward progress slowed. Oracle bought Sun and took over Java, but Oracle doesn’t seem to be putting much effort into Java, besides suing the various users of it like Google.

Microsoft has been doing a lot of good work developing C# and has been putting a lot of work to evolving the language and evolving the .Net framework. Certainly modern C# has come a long way and contains a lot of powerful modern object oriented features that weren’t present initially and aren’t present in Java.

A couple of years ago Apple finally noticed this trend and produced their own modern object oriented language namely Swift. Swift isn’t a true object oriented extension to C, the core language has a lot of differences to C. Some things are quite similar like building expressions, but other things are quite different, like how you define variables. Swift has all the modern object oriented features like closures, extensions, generics, etc. which you would expect. Further since a lot of the language was re-imagined over C, it has a lot of nice built in features like ranges. If you look at just the core language, its quite clean, powerful and modern.

There are quite a few blog posts comparing these various languages such as these two articles on C# vs Swift: C# vs Swift and C# vs Swift. If you Google, there are a lot of discussions on the various points of these languages. Often the discussions also consider Go and Python.

Frameworks

The ugliness in all these safe modern languages comes in with how they interact with the underlying operating system. Neither Apple nor Microsoft re-wrote their operating systems to be safe and natively support these. At some point you have the transition from the nice safe, clean object oriented world into the old pointer based C world. Microsoft with the underlying Windows DLLs and Apple with the Objective-C based application frameworks and then to the underlying Unix based operating system kernel.

Sun took the highest approach making its own frameworks for everything and then leaving it to the JVM implementation on each system to translate native to this, so to a Java programmer everything looks the same. This sounds great, but doesn’t work well in practice since it doesn’t give you access to all the operating system features and makes your program less competitive. This resulted in the development of JNI and Java programs natively calling through to the ugly world outside the JVM.

Microsoft built the .Net framework on top of Windows, which provides most things you need and has been filling in more and more. But you still often need to call native DLLs directly (which makes you application unsafe).

Apple decided to use the current iOS/OSX frameworks directly and allowed Swift to interact bi-directionally with Objective-C libraries. This then allowed Swift programmers to directly leverage their knowledge of UIKit for instance to write programs. The downside of this is that it puts a lot of ugly code directly in your nice clean Swift program to deal with these older frameworks.

Koch Snowflakes Revisited

I ported my Objective-C Koch Snowflake program from 2013 over to Swift. This turned out to be pretty straight forward. I think the program source code is much cleaner once moved over to Swift and I definitely prefer Swift to Objective-C for programming. Since I’ve been doing mostly C# programming the past few years, it fells much more natural to me than Objective-C.

Although most of the code is cleaner, you can see a bit of ugliness around the interactions with the UIKit framework. I especially don’t like using the types their rather than the native Swift data types.

Screen Shot 2016-05-16 at 9.46.04 AM

Other Development Notes

For the UI, I used the standard storyboard screen designer which is shared by both Objective-C and Swift. Like most systems that edit your code, you just need to be careful not to edit the code inserted by the UI designer or they get out of sync and produce weird errors. I changed a variable name generated by the UI designer and it was a bit of a head scratcher tracing back from the error message to what was wrong.

I created the project as a standard single page application and set it to run on both an iPhone and iPad. There are now 12 standard devices of various iPad and iPhone models directly supported, I tried a couple of them, but certainly didn’t test with each one.

Generally, when you make a project you can create any new class in either Swift or Objective-C and have them interoperate. So you can bring in older code rather than porting it.

The debugger is quite nice, its easy to step through your code and see what is going on. Generally, XCode is a very powerful development platform and has a lot of great tools to support you in your programming. I haven’t added any unit tests yet, but I plan to have a look at the testing framework next and perhaps that will be the topic of a future blog.

Screen Shot 2016-05-16 at 9.44.39 AM

Summary

I think Swift is a huge improvement for programming iOS and OS/X over Objective-C.  Although Swift is open source and can be run on Linux, the Apple UI frameworks like UIKit are not open source. So I don’t think Swift will be any help in developing cross platform programs (unless they are very simple command line utilities). Swift is quite a modern language and its object oriented implementation is quite nice. Apple seems to be putting quite a bit of effort into Swift with version 3 of the language soon to be released. There is certainly a large community of iOS developers out there who should be putting it to good use.

This was a fun little project and I think I will be spending a bit more time dabbling is iOS development using Swift.

Source Code Listings

//
//  ViewController.swift
//  KochSnowFlake
//
//  Created by Stephen Smith on 2016-05-13.
//  Copyright © 2016 Stephen Smith. All rights reserved.
//

import UIKit

class ViewController: UIViewController {

    // MARK: Properties
    @IBOutlet weak var fractalLevelTextField: UITextField!
    @IBOutlet weak var fracView: FractalView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        //

        fractalLevelTextField.text = "2";
        fracView.level = 2;

        NSNotificationCenter.defaultCenter().addObserver(self,
               selector: #selector(textChangeNot),
               name: UITextFieldTextDidChangeNotification, object: fractalLevelTextField);
    }

    func textChangeNot( object: AnyObject )
    {
        if let enteredLevel = NSNumberFormatter().numberFromString(fractalLevelTextField.text!)
        {
            fracView.level = Int(enteredLevel);
            fracView.setNeedsDisplay();
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

//
//  FractalView.swift
//  KochSnowFlake
//
//  Created by Stephen Smith on 2016-05-13.
//  Copyright © 2016 Stephen Smith. All rights reserved.
//

import UIKit
class FractalView: UIView {
    var level = 1;

    // Only override drawRect: if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    override func drawRect(rect: CGRect) {
        var frac: KochFlake;

        // Drawing code

        let currentColor = UIColor.blackColor();
        let context = UIGraphicsGetCurrentContext()
        frac = KochFlake(inContext: context!);

        //Set the width of the "pen" that will be used for drawing
        CGContextSetLineWidth(context,1);

        //Set the color of the pen to be used
        CGContextSetStrokeColorWithColor(context, currentColor.CGColor);

        frac.KockSnowflake(level);

        //Apply our stroke settings to the line.
        CGContextStrokePath(context);

    }
}

//
//  KochFlake.swift
//  KochSnowFlake
//
//  Created by Stephen Smith on 2016-05-13.
//  Copyright © 2016 Stephen Smith. All rights reserved.
//

import UIKit

class KochFlake
{
    var tg:TurtleGraphics;
    var context:CGContextRef;

    init(inContext: CGContextRef)
    {
        context = inContext;
        tg = TurtleGraphics(inContext: context);
    }

    func KockSnowflake(level:Int)
    {
        tg.turn( 60 );
        KockSnowflakeSide( level , size:400);
        tg.turn( -120 );
        KockSnowflakeSide( level, size: 400);
        tg.turn( -120 );
        KockSnowflakeSide( level, size: 400);
    }

    func KockSnowflakeSide(level:Int, size:Int)
    {
        if (level == 0)
        {
            tg.move( size );
        }
        else
        {
            KockSnowflakeSide( level - 1, size: size / 3 );
            tg.turn( 60 );
            KockSnowflakeSide( level-1, size: size / 3);
            tg.turn( -120 );
            KockSnowflakeSide( level-1, size: size / 3);
            tg.turn(60);
            KockSnowflakeSide( level-1, size: size / 3);
        }
    }
}

//
//  TurtleGraphics.swift
//  KochSnowFlake
//
//  Created by Stephen Smith on 2016-05-13.
//  Copyright © 2016 Stephen Smith. All rights reserved.
//

import UIKit
let pi:CGFloat = 3.14159;

class TurtleGraphics
{
    var x, y: CGFloat;
    var angle: CGFloat;
    var context: CGContextRef;

    init(inContext: CGContextRef)
    {
        context = inContext;
        x = 50.0;
        y = 150.0;
        CGContextMoveToPoint(context, x, y);
        angle = 0.0;
    }

    func move( dist: Int )
    {
        x = x + CGFloat(dist) * cos( angle * pi / 180.0);
        y = y + CGFloat(dist) * sin( angle * pi / 180.0);

        CGContextAddLineToPoint(context, x, y);
    }

    func turn( angleIncrement: Int)
    {
        angle = angle + CGFloat(angleIncrement);
    }
}

 

Advertisements

Written by smist08

May 16, 2016 at 8:00 pm

The Sage 300 ERP Java API

with 13 comments

Introduction

With version 6.0A of Sage 300 ERP we introduced a native Java API to all the Sage 300 Business Logic (Views). We did this in support of our SData implementation which we wrote in Java. This API allows Java programmers to access all the Sage 300 ERP business logic along the same lines as our .Net API and our COM API. This API isn’t built on top of either COM or .Net, it talks directly to the underlying C DLLs in System Manager. This then provides better performance, as well as allows us to compile this part of the system for Linux with no Microsoft dependencies. Internally we usually refer to this API as SAJava.

All the Sage 300 Business Logic objects have the same API, this makes it easier for us to produce these different APIs to facilitate interoperability with all sorts of external systems, allowing the programmers there to write code in a natural manner where any required interop layer is provided by us. The Java API uses a Java Native Interface (JNI) interop layer to talk to our Windows DLLs (or Linux shared objects). This is a one way communication where we only use this to call the DLLs, we never have the DLLs calling our Java code (this direction is often dangerous and leads to the problems often encountered with JNI). Our JNI code handles all the data conversions between Java and C as well as provides exception handling to trap and handle exceptions that can happen in C code (like bad pointers).

I’ve blogged about this API a bit indirectly in the past when talking about how to write server side code for our SData service, for instance here, here and here. Generally to add custom programming to SData feeds you write Java classes that inherit from our standard SData classes to provide this. When you interact with the Views in this environment you use this Java API, but all the libraries are already included and all the details of signing on are handled for you. The framework starts you off at a point where you can directly open and call Views. In this posting we’ll back up a bit to cover full usage, unlike the case where the SData programming framework does a lot of the work for you. So that you can use this API directly in isolation without requiring any other framework.

Getting Started

First to use the Java API, you need to include its jar file into your project. This file is located in the Tomcat\lib folder. This changed a bit between version 6.0A and then the 2012 version. For 6.0A the folder is: C:\Program Files (x86)\Common Files\Sage\Sage ERP Accpac\Tomcat\lib and the file is SystemManager.jar. For the 2012 version the folder is: C:\Program Files (x86)\Common Files\Sage\Sage 300 ERP\Tomcat\lib and the file is com.sage.accpac.sdk.accpac.sajava-6.1.jar. Then you need to import the classes into any source file that uses them via:

import com.sage.accpac.sm.*;

Once you have these things included in your Java project you can start creating objects and calling methods. However due to security you first must sign-on to a session and then create all other objects from this session.

The documentation is in the form of JavaDoc and is located on the DPP Wiki. The 2012 version is here: http://dppwiki.sage300erp.com//javadocs/v6.1/SystemManager/. You can find all the classes, methods and properties here. To access this, you must be part of the Sage 300 ERP Developer Program. A key benefit to joining this program is access to this wiki which contains all the developer documentation that we produce.

Signing On

First you must create a session with some code like:

Session session;
session = new Session(new ProgramSet(), new SharedDataSet(), "ADMIN",
     "ADMIN", "SAMINC", new Date());

This will sign-on your session to the company SAMINC using today’s date as the session date. The ProgramSet and SharedDataSet are used when we deploy in a hosted configuration and run multi-tenant. In this case they must be setup correctly by the system to configure which tenant this session is for. In most normal on-premise applications the indicated calls are fine to give the one default tenant that exists.

Then you must create a program from the session:

Program program;
program = new Program(session, "XZ", "XZ0001", "61A");

If you read my last blog post, this might appear a bit backwards to the COM API where this looks like the session.Init call that comes first. This is true, but the information is required regardless.

Using Views

Now that you have a program you can start opening and using Views. As an example, let’s look at a method that enters A/R Invoices. Like many things I started with macro recording to get the right Views and some syntax. Macro recording produces VBA code, but it isn’t hard to convert this to Java quickly. Anyone familiar with Sage 300 ERP macro recording will recognize the style and variable names in the following method. This method assumes there are class variables for the program and session that were created as indicated above. The key point of the following example is to show how to open Views, compose Views and then use the Views. For more general information on Sage 300 ERP’s Views have a look at this and this.

    public String enterARInvoices()

    {

        int iEntry;
        int iDetail;
        int numEntries = 20;
        int numDetails = 5;
        String sBatchNum;

        View ARINVOICE1batch = new View(program, "AR0031");
        View ARINVOICE1header = new View(program, "AR0032");
        View ARINVOICE1detail1 = new View(program, "AR0033");
        View ARINVOICE1detail2 = new View(program, "AR0034");
        View ARINVOICE1detail3 = new View(program, "AR0402");
        View ARINVOICE1detail4 = new View(program, "AR0401");
        View ARCUSTOMER1header = new View(program, "AR0024");

        ARINVOICE1batch.compose ( ARINVOICE1header );
        ARINVOICE1header.compose (ARINVOICE1batch, ARINVOICE1detail1, ARINVOICE1detail2, ARINVOICE1detail3, null);
        ARINVOICE1detail1.compose (ARINVOICE1header, ARINVOICE1batch, ARINVOICE1detail4);
        ARINVOICE1detail2.compose (ARINVOICE1header);
        ARINVOICE1detail3.compose (ARINVOICE1header);
        ARINVOICE1detail4.compose (ARINVOICE1detail1);

        // Create the batch

        ARINVOICE1batch.recordGenerate(RecordGenerateMode.Insert);
        ARINVOICE1batch.set("PROCESSCMD","1");      // Process Command

        ARINVOICE1batch.process();
        ARINVOICE1batch.read(false);

        sBatchNum = ARINVOICE1batch.get("CNTBTCH").toString();

        // Loop through creating the entries

        for ( iEntry = 0; iEntry < numEntries; iEntry++ )
        {
            try
            {
                ARINVOICE1detail1.cancel();
                ARINVOICE1detail2.cancel();
                ARINVOICE1header.recordGenerate(RecordGenerateMode.DelayKey);
                ARINVOICE1detail1.recordClear();
                ARINVOICE1detail2.recordClear();

                ARINVOICE1header.set("PROCESSCMD","4");

                ARINVOICE1header.process();

                if ( false == ARCUSTOMER1header.goNext() )
                {
                    ARCUSTOMER1header.goTop();
                }

                ARINVOICE1header.set("IDCUST", "1200");

                for ( iDetail = 0; iDetail < numDetails; iDetail++ )
                {
                    ARINVOICE1detail1.recordClear();
                    ARINVOICE1detail1.recordGenerate (RecordGenerateMode.NoInsert);
                    ARINVOICE1detail1.process();

                    ARINVOICE1detail1.set("IDITEM", "CA-78" );                     // Item Number

                    ARINVOICE1detail1.insert();

                }

                ARINVOICE1header.insert();
            }
            catch( Exception e )
            {
                int count = program.getErrors().getCount();
                if ( 0 == count )
                {
                    e.printStackTrace();                   
                }
                for ( int i = 0; i < count; i++ )
                {
                    System.out.println(program.getErrors().get(i).getMessage());
                }
            }
        }
        ARINVOICE1batch.dispose();
        ARINVOICE1header.dispose();
        ARINVOICE1detail1.dispose();
        ARINVOICE1detail2.dispose();
        ARINVOICE1detail3.dispose();
        ARINVOICE1detail4.dispose();
        ARCUSTOMER1header.dispose();

        return( sBatchNum );
    }

Notice that you can explicitly close things by calling the dispose method. This is usually preferred to waiting for the Java garbage collector to reclaim things, it tends to keep down resource usage if you are opening and closing things a lot.

Errors

If a call fails, there are a couple of cases. If it’s a simple expected thing like reaching the end of records when fetching through them then the routine will return a simple return code that you can easily handle in your code. If something worse happens then the routine will throw an exception. As in other Sage 300 ERP APIs, there is an error stack which will contain possibly a number of error messages explaining what went wrong. In the catch expression above we first check if there are any errors on the error stack, if not then we print the stack trace to allow debugging of what went wrong. Otherwise we loop through the Sage 300 errors and print them for diagnostic purposes. When programming Sage 300 ERP, always make sure you have an error handler as it can give you very good information when debugging your program.

Summary

The Sage 300 ERP Java API gives yet another tool for integrators to integrate to Sage 300 ERP from external systems. It is ideal for Java programmers who would like to write their integration entirely in Java. This is often a benefit when the SDK for the external system is itself written around the Java programming language.

Writing Server Side Code for Accpac 6 Web UIs

with 11 comments

We discussed how to create Accpac 6 Web based UIs in https://smist08.wordpress.com/2010/08/14/creating-a-web-form-for-accpac-6/ and how to write code that runs as JavaScript in the Browser in https://smist08.wordpress.com/2010/08/21/client-logic-for-an-accpac-6-web-form/. This blog posting looks at how you can write server side code in Java to assist your UIs do their job more efficiently.

Accpac 6 Web UIs communicate with the Server using SData (https://smist08.wordpress.com/2009/11/24/sdata-in-sage-erp-accpac-6/ and http://sdata.sage.com/) which is a REST based Web Services protocol. On the Server we run a Java application that converts incoming SData requests into Accpac View calls. Accpac Views are the business logic objects within Accpac. The Accpac business logic is remaining largely unchanged in this new environment and it’s the job of this Java application to convert the new world into the older world.

Theoretically then, you could do much of what we are talking about directly in the Views. We could expose new calculated fields or new functionality directly from the Views. However Views are used by all sorts of things including VB based UIs, macros, import/export as well as by third party applications. We don’t want to clutter up the Views with lots of extra logic needed to support or optimize the new Web based UIs. What we are looking to do is split the code that used to run entirely in VB UIs to half run in the browser and half run on the server. We also want to provide support for our Web based UIs to minimize program size running in the Browser and to minimize the number of RPC (remote procedure calls) they need to make to the server. The current VB UIs call many Views at once to do what they need, but this won’t work so well over the Internet since it will require many SData calls to do things, we really want to consolidate all the info the UI requires in one feed, so it only needs to do one SData call to get everything it needs.

Within the SData server’s configuration are a number of XML files that define all the SData feeds for the various accounting applications. For each application there is a classmap.xml file that lists all the feeds and the Java class that processes that feed. Then for each feed there is a specific resourcemap.xml file that defines various properties or configurations for that feed such as which Views is sits on, or some extra fields to present. To add an SData feed on top of an existing View is easy and just a matter of creating these simple XML files. There is a generic class ViewResourceKind that will provide standard SData functionality for any View. But if you want to add your own functionality you need to extend one of our classes and add your own logic.

The hardest way to implement an SData service is to create a class that implements our SDataResourceKind interface. This interface defines what a class needs to implement in order to be used as an SData feed from our SData application server. Here you need to do all the work, but if you want to implement a feed that hasn’t got anything to do with normal Accpac processing, perhaps this is a way to go. Fortunately we provide a class that implements this interface that you can extend. This is the ViewResourceKind class, mentioned previously, that implements SData on top of a View. This is really intended for data type Views. You can extend this to add fields and/or provide other helpful services for your UI form (or other consumer of your SData feed).

SData provides data type feeds similar to Views built closely over database tables, but it also provides “service” feeds that are similar to Accpac superviews. These feeds are meant to perform service type operations like printing a report, posting a batch or doing a calculation. They can operate asynchronously, meaning the originating call returns immediately, but then can poll the service for status updates (which can be used to update a meter control).

Here is a bit of sample code from the SData service that provides the data for the pie charts in the G/L Balance Sheet data snapshot.

public class GLBALSHTService extends BaseService
{
   private final SDataView            viewGLFSUM;
   private final List<ServiceField>   requestFields;
   private final List<ServiceField>   responseFields;
   private static final String        STR_RSCID  = "GLBALSHTSP";
   private boolean                    bPermitted = false;
   private GLLanguageResourceContents GLLanguageResource;
   public GLBALSHTService(final ApplicationContext applicationContext, final ResourceContextImpl resourceContext,
         final Resource resource, final Service service, final SDataViewSet viewSet)
   {
      super(applicationContext, resourceContext, resource, service, viewSet);
      viewGLFSUM = new SDataView(getResourceContext().getAccpacProgram(), GLFSUM.VIEW, resource);
      bPermitted = getResourceContext().getAccpacProgram().isPermitted(STR_RSCID);
      if (!bPermitted)
      {
         String appVersion;
         String appLang;
         //language dependent resource content is only used when access is not permitted.
         appVersion = resourceContext.getAccpacProgram().getActiveApplications().get(GLLanguageResourceContents.APPL)
               .getAppVersion();
         appLang = viewGLFSUM.getProgram().getSession().getUserLanguage();
         GLLanguageResource = new GLLanguageResourceContents(appVersion, appLang);
      }
      requestFields = new ArrayList<ServiceField>();
      responseFields = new ArrayList<ServiceField>();
      setupRequestFields();
      setupResponseFields();
    }
   @Override
   public void shutdown()
   {
      if (this.viewGLFSUM != null)
         this.viewGLFSUM.dispose();
   }
    private void setupRequestFields()
   {
      requestFields.add(ServiceHelper.viewFieldToServiceField(viewGLFSUM.getFields().get(GLFSUM.IDX_YEAR)));
      requestFields.add(ServiceHelper.viewFieldToServiceField(viewGLFSUM.getFields().get(GLFSUM.IDX_PERIOD)));
      requestFields.add(ServiceHelper.viewFieldToServiceField(viewGLFSUM.getFields().get(GLFSUM.IDX_PERIODS)));
   }
   private void setupResponseFields()
   {
      responseFields.add(ServiceHelper.viewFieldToServiceField(viewGLFSUM.getFields().get(GLFSUM.IDX_ENDDATE)));
      responseFields.add(ServiceHelper.viewFieldToServiceField(viewGLFSUM.getFields().get(GLFSUM.IDX_CASH)));
     // … Lots more response fields defined …
   }
   protected List<ServiceField> createRequestFields(final SDataRequest request)
   {
      return requestFields;
   }
   @Override
   protected List<ServiceField> createResponseFields(final SDataRequest request)
   {
      return responseFields;
   }
   @Override
   protected List<ServiceField> execute(SDataResourceElement payload, SDataRequest request, AsyncStatusListener listener)
   {
      List<ServiceField> list = new ArrayList<ServiceField>();
      if (!bPermitted)
      {
         throw (new RuntimeException(GLLanguageResource.getValue(GlobalContent.ACCESS_DENIED_MSG.toString()), null));
      }
      int errorCode;
      this.viewGLFSUM.recordClear();
      setGLFSUMViewFieldsValue(payload);
      MeterEventListener meterListener = null;
      if (listener != null)
      {
         meterListener = new MeterListener(listener, listener.getProgress().getProgressPct(),
               TrackingPayload.PROGRESSPCT_GET_RESULT);
      }
      errorCode = viewGLFSUM.process(meterListener);
      if (errorCode > 0)
      {
         throw (new RuntimeException(null, null));
      }
      list.add(ServiceHelper.viewFieldToServiceField(viewGLFSUM.getFields().get(GLFSUM.IDX_ENDDATE)));
      list.add(ServiceHelper.viewFieldToServiceField(viewGLFSUM.getFields().get(GLFSUM.IDX_CASH)));
      // … Lots more reponse fields added …
      return list;
   }
 
   /**
    * function to set field value
    */
   private void setGLFSUMViewFieldsValue(SDataResourceElement payload)
   {
      for (SDataResourceElement elem : payload.getContents())
      {
         final String name = elem.getName();
         String value = (String)elem.getValue();
         if (value != null)
         {
            viewGLFSUM.putBySDataPropertyName(name, value, Boolean.FALSE);
         }
      }
   } 
   /**
    * set the default value for the request fields
    */
   @Override
   protected List<ServiceField> createTemplateFields(final SDataRequest request)
   {
      List<ServiceField> list = new ArrayList<ServiceField>();
      this.viewGLFSUM.recordClear(); 
      list.add(ServiceHelper.viewFieldToServiceField(viewGLFSUM.getFields().get(GLFSUM.IDX_YEAR)));
      list.add(ServiceHelper.viewFieldToServiceField(viewGLFSUM.getFields().get(GLFSUM.IDX_PERIOD)));
      return list;
   }
}

 

This service is then defined in the GLServiceMap.xml file under the tomcat\portal directory:

<?xml version="1.0" encoding="UTF-8"?>
<serviceMap>
<services>
       <service
         className="com.sage.accpac.gl60a.common.server.GLBALSHTService">
       </service>
… other definitions …
</services>   
</serviceMap> 

 

In the execute method, notice the View calls to the GLSUM View. You can see calls to recordClear and process. Basically these are calls to our Java JNI layer (Java Native Interface, http://en.wikipedia.org/wiki/Java_Native_Interface). Here we have a hierarchy of classes very similar to what we had in COM for VB programming where you can access all the View methods as well as a selection of convenient other System Manager APIs.

The basic logic here is to first initialize the operation in the constructor, get the input fields from the SData feed (the request fields as in setupRequestFields), perform the various View operations that are required (the execute method) and then setup the response fields to send back to the UI running in the Browser (created in setupResponseFields and then set in the execute method).

Hopefully, this gives a bit of a flavor for what sort of programming goes on the server for helping User Interface programs from an SData point of view.

Written by smist08

August 28, 2010 at 4:21 pm

Posted in sage 300

Tagged with , , , ,

Client Logic for an Accpac 6 Web Form

with 6 comments

Last week we talked about creating Accpac 6 Web UIs (https://smist08.wordpress.com/2010/08/14/creating-a-web-form-for-accpac-6/). Part of the process is creating the XML representation of the Web Form which we discussed previously: https://smist08.wordpress.com/2010/05/28/how-to-layout-forms-in-sage-erp-accpac-6/. Now suppose you have an XML form designed either in an XML editor or using our SwtUIDesigner and you want functionality beyond that which is given to you by default. By default you have record navigation, save and delete. From last week’s article you will now have a simple runnable project. So where do you put your client side code? Within the client files is a file xxFeatureModule.java which contains:

/*
 * Copyright 2010 Sage Software Inc. All rights reserverd.
 */
package com.sage.accpac.SS60A.ss1005.client;
import com.sage.swt.client.ui.builder.AbstractSynchronousFeatureModule;
import com.sage.swt.client.ui.builder.InstanceContext; 
public class SS1005FeatureModule extends AbstractSynchronousFeatureModule
{
   public String getDebugName()
   {
      return "SS1005 Feature Module";
   } 
   protected boolean handleInstanceLoaded(InstanceContext context)
   {
      // TODO: Handle what happens when an instance context is loaded.
      // Return true if the handling was successful; false otherwise.
      return true;
   }
}

This is where you put your code. The handleInstanceLoaded routine is provided for you to hook your code in. Basically this is called once the UI has initialized and the XML form has been processed and everything has been created. At this point you can hook into any UI controls, data sources or do any other initialization you require. The context that is passed into this routine is the object from which you can get to everything else in the UI. It contains the collection of controls on the form, collection of datasources , etc. Below is a snippet of code for a simple handleInstanceLoaded routine that retrieves a button from the context by calling its getWidgets() method. Then it calls addClickListener() to add a listener that will be called whenever the button is clicked.

   protected boolean handleInstanceLoaded(InstanceContext context)
   {
      DefaultAllClickListener defaultAllListener = new DefaultAllClickListener(context); 
      SwtButton btnDefaultAll = (SwtButton) context.getWidgets().get
          ( UIConstants.WidgetIDs.CONFIG_BTN_DEFAULTALL );
      if (null != btnDefaultAll)
         btnDefaultAll.addClickListener(defaultAllListener);      
      return true;
   } 
   private class DefaultAllClickListener implements ClickListener
   {
      public DefaultAllClickListener(InstanceContext context)
      {
      } 
      public void onClick(Widget sender)
      {
          SwtMessageDialog.showMessageDialog(
               UILanguageResources.AppContent.DIALOG_RESTORE_DEFAULT.getValue(),
               UILanguageResources.AppContent.DIALOG_CONFIRM_DEFAULT.getValue(),
               SwtMessageDialog.ResponseButtonSetType.YES_NO,
               SwtMessageDialog.MessageType.CONFIRM, "",
               new MessageDialogListener[] {new ConfirmDefaultAllListener()});
      }
   }
   private class ConfirmDefaultAllListener implements MessageDialogListener
   {
      public void onRespond(MessageDialogEvent evt)
      {
         SwtMessageDialog.ResponseButtonType response = evt.getResponse();
         if (response == SwtMessageDialog.ResponseButtonType.YES)
         {
            defaultConfigInfo(popupContext);
         }
      }
   }

Notice that we defined a new class DefaultAllClickListener which implements ClickListener. Basically this is defining the class for the object that will be called whenever a button is pressed. So in handleInstanceLoaded we call “new DefaultAllClickListener(context)” to create a new object in the DefaultAllClickListener class. This object will now be called whenever the button is pressed. In Java this is the typically way you get event notifications. There is nothing like a COM Event special type of method. Event handlers are just regular methods in regular classes. You hook them up by passing them to the object that does the notifying, usually by some sort of addXXXListener() type method call. It’s up to each routine that does notifications to keep a list of all the objects they need to notify and to call each one in turn whenever anything happens.

This is true for all UI events, notice that even the confirmation dialog displayed has a ConfirmDefaultAllListener() that will be called when the button is pressed on this dialog. All notifications are asynchronous like this. You can’t call a UI action and expect to wait (in the code) for a response. If VB we could just do answer = MsgBox () and our code would wait for the user to choose Yes or No. In the browser, everything is asynchronous, nothing waits, everything is via notifications. This is a different metaphor than people are used to. In languages like VB some things are asynchronous and work via event notifications and some things are synchronous. The big difference is that calls to the server are asynchronous, so whenever you make an SData call, you are notified via this mechanism when something arrives. Contrast this to VB where you make a server call, your code waits for the response and no more code is executed until you get it. In the web world this could take quite a long time and the browser would be completely unresponsive if no code executed until you got this response. This is the world of AJAX (Asynchronous JavaScript and XML http://en.wikipedia.org/wiki/Ajax_(programming) ). AJAX is the key to creating rich internet applications in the Browser. If we didn’t use this, then the input forms would be quite boring lists of titles and edit boxes followed by a submit button, where nothing happens until you hit submit. This is another reason that you want to put groups of View calls in your server module, where everything works synchronously as normal. Generally you only want to make one server call at a time, since then you don’t need to chain together a number of responses, plus you get the performance benefit of only making one server call over the Internet.

Here we’ve been putting the code in the xxFeatureModule.java file, but unlike VB, there are no restrictions on how many source files you have and no rules that certain things have to be in certain files. So you can freely create as many class files as you like and organize your code in whatever manner you like. The nice thing is that you can follow industry best practices and aren’t under constraints like an event listener has to be in a special file in order to be called. So the expectation would be that you can create common Java libraries to share over UI projects and that you can keep your class files small and maintainable without putting everything including the kitchen sink into the xxFeatureModule.java file.

One aspect of Java that trips up VB (and C) programmers is the use of inheritance. When using an API or framework in C or VB, basically you look for all the functions, properties and events you can use. If you take this approach with Java, you will probably find that you are having trouble finding what you are looking for. This is because Java supports inheritance in a proper object oriented manner. So often the paradigm isn’t to do something by calling API functions, rather what you do is extend a class that does much of what you want and then just add the code that’s needed to make the difference. People new to Java often take a bit of time getting used to this. If you are stuck and aren’t finding an API that you are sure must be provided, instead look for a class that is along the lines of what you need that you can extend.

This code is Java and definitely not JavaScript. Yet this is what we are writing to run in the Browser. To produce the final runnable code we have to feed this through the GWT Compiler to get it compiled into JavaScript. The GWT Compiler will produce 6 versions of JavaScript from this code, one optimized for each major Browser family including WebKit (Safari and Chrome), Gekko (Firefox) and separate versions for IE 6 and 8. This then is the final code that you would deploy to your customer’s web servers to run.

In the meantime while you are developing, you keep working in Java. You can run this code as a Java application inside the Eclipse IDE. You can use the Eclipse Java debugger to single step through the code, set breakpoints and examine variables. You can also use all sorts of other Java tools to make yourself more productive like JUnit (http://www.junit.org/), Emma (http://emma.sourceforge.net/) or TPTP (http://www.eclipse.org/articles/Article-TPTP-Profiling-Tool/tptpProfilingArticle.html). Plus you can use all the plug-ins that are available for Eclipse such as say SubVersion for source control (http://en.wikipedia.org/wiki/Apache_Subversion).

All the methods are documented in the HTML JavaDoc which is generated from the comments in the source code. This is included as part of the SDK. Below is a sample of the beginning of the page that documents the InstanceContext. This page documents all the methods in this object.

 

Customization

Generally you can do quite a bit of customization just with the screen XML file. But at some point you are going to have to write some code to do the more deep customizations. To customize such UIs with code, you basically want to get hold of the context object, so you can access the collections of widgets and datasources. Then you can add your own listeners, or change the properties of the various objects. Basically you extend the main entry point class of the UI and extend the xxFeatureModule class. Add your functionality and then feed everything back through the GWT compiler to generate a new set of web pages to deploy.

Summary

Hopefully this gives a flavor for how the Browser part of a UI control is coded in the new Accpac SWT framework. Most of the basic functionality is automatic from the XML screen definition, but UIs are more complex than just CRUD (Create/Read/Update/Delete) and this is how you add that extra complexity to handle sophisticated UI needs.

Written by smist08

August 21, 2010 at 4:31 pm

Posted in sage 300

Tagged with , , , ,

Migrating Customizations to Sage ERP Accpac 6

with 12 comments

As Accpac makes the transformation from being a Windows Desktop Application to becoming a true Web Based Application, people often ask: how will we migrate our current customizations to the new platform? I talked about how to customize the new Web Based screens in: https://smist08.wordpress.com/2010/02/05/sage-erp-accpac-6-customization/; but, didn’t really discuss a strategy for migrating existing customization to this new model.

Ultimately, the new customization model is far more powerful than the current model and this will allow far more complex customizations. It will also be much easier to perform some customizations that currently require quite a bit of code. However it took the channel quite a bit of time to master the current model. It took quite a while for everyone to learn the complexities of VBA programming and our UI customization model. Now we will be starting over.

Care will have to be taken when scheduling and estimating conversion projects. Perhaps starting with easier ones first, to learn from and become experienced in the new technologies before tackling the bigger more complex jobs.

Customization that Remain Largely Unchanged

For report customizations we are still using Crystal Reports and so all current Crystal customizations can still be used. There are fewer database changes for version 6 than we have had in the last few versions, so most customized Crystal Reports should work with no changes whatsoever. We will be moving to the newer Crystal 2008 reporting engine which means you can now use any features introduced in Crystal 2008. In the same way your Accpac Financial Reports will continue to work fine as will anything you have created with Accpac Intelligence or Accpac Insights. Additionally we now have the new “Accpac Inquiry” (https://smist08.wordpress.com/2009/12/11/sage-erp-accpac-6-0-adhoc-query/) which you might want to construct some custom queries for.

Any customizations that talk to the Accpac COM API or the Accpac .Net interface will also remain mostly unchanged and should continue to work. Basically just following the usual upgrade procedures that you follow for any new version.

Any customizations that use View subclassing will continue to work. The View layer is largely unchanged. You should re-instantiate your Views with the View template in the Accpac 6.0 SDK, but this is the same procedure you should do with any new version.

For these API type customizations, it usually involves running a VB EXE program or VBA macro. These can be run from any workstation that has workstation setup installed, but as we run workstation setup on fewer and fewer workstations, you might want to think about running these on the Web Server.

Customizations of the new Web Based UI model

First a couple of caveats:

  1. We will continue to ship the current VB based UI forms for many versions. This means you can continue to use your current screen customizations for quite some time.
  2. We will continue to ship the current Accpac Desktop to allow you to run things exactly as you do today.
  3. Version 6.0A will only have new screens running inside SageCRM. Most other screens will move to the new Web Based technology for 6.1A.

However you will want to consider a strategy for moving your customer’s VB based customization to the new Web Based forms.

If the customizations only involved changes to the form design or changing text, then the customization in 6.x will be very simple and won’t require any coding. All the screen definitions and strings are stored in simple text XML files that can be edited either with special purpose tools (like a visual screen designer) or with a simple text editor like Notepad.

If the customization requires quite a bit of extra processing logic, then this code will need to be written in Java. The programming model is similar to that for the VB UIs. You subclass the UI and add your logic. There are collections of data sources and controls that you can tap into to get events to trigger your logic and allow your logic to manipulate the forms.

Expect a learning curve, to learn the new technologies. To become familiar with Java programming. To understand the programming and customization model used in our new Web Based UIs and how to deploy your updated UI controls on the server.

Although you can customize the current UIs just like you could customize the VB UIs, there is also a new alternative that lets you extend/customize the Accpac SData feeds. With this you can customize the code on the server that generates the data for the UIs. Sometimes this will be a better place to apply customizations. And is a new option you have available.

Like when we introduced VBA, it was a while before people were using it to extensively customize our screens. It took time to learn it and it took time to figure out what could be done. Like then, we will need to provide quite a bit of training, either live at our various conferences or via on-line training on Sage University. The good news is that the new customization model is more powerful than the current model and once you do master it, you will be able to more easily customize the look and feel for the product than ever before.

If the customization involves communications with programs running on the client’s workstation or involves saving files on the client’s workstation, these sort of operations may not be allowed from a Browser based application. There are definite restrictions on what you can do, in order to maintain workstation security. There are some things that you might do right now on the workstation that you might consider moving to the server or might need to find another approach, perhaps by communicating with a cloud service using web service calls. These will be dependent on what exactly you are trying to do and what is allowed.

The main points of customization are:

  • Various text files such as XML Screen definition files, XML string resource files and configuration files.
  • Cascading Style Sheets (CSS) that control the overall look and feel of the web pages.
  • Using Java to programmatically enhance the UIs.
  • Using Java on the server to programmatically enhance the Accpac SData feeds.

Older Web Technologies are Being Dropped

The Accpac SOAP Web Services are being removed in version 6.0A, so you will need to move any customizations using these to either the Accpac COM API, the Accpac .Net interface or to the new REST based SData Web Services being introduced in version 6.0A.

The Web Desktop is being removed. We will only be including CAB files for screens used by the SageCRM integration. All other Web Based VB screens are being removed. Any customizations that require the old Web Desktop will need to be moved to the new Web Desktop/Portal.

Summary

Any customizations that involve only the database, reporting or the business logic will continue to work or only require minor tweaks similar to the past few versions.

For screen customizations, you can continue to use your current VB customization for quite some time. However you should start to plan how to move these customizations over to the new Web base model.

Any customizations based on our older Web Technologies need to be moved over the new sooner than later.

Written by smist08

July 3, 2010 at 5:15 pm

Posted in sage 300

Tagged with , , , ,