Library

Browse and search developer information

GS1 Client

By Connecting for Health | 5 March 2013

Introduction

The NHS is making increasing use of barcodes in a number of areas. Particularly, they are used:

  • Nationally to identify:
    • patients notes and, during hospital stays, patients themselves, using a wristband;
    • blood transfusions
  • Locally, to identify:
    • locations (eg this is ward 10)
    • medical samples
    • assets (eg ultrasound scanners)
    • disposable items, such as forceps or returnable gas cylinders

GS1 is an international organisation that manages barcodes:

  • how they are printed and formatted
  • how they are interpreted as data
  • how they map to registries of companies and organisations

GS1’s registries cover many areas, but the most useful are:

  • The GS1 company prefix. This is a prefix at the start of a barcode that identifies a company and its issuing agency.
  • The GS1 issuing agency – which is nearly always national or supra-national. This is usually the same as the country of residence of the organisation. Its identifier is the GS1 prefix.

This scheme of numbers and registries ensures that any organisation’s barcodes are unique and don’t impinge any other organisation. In addition, it means that an organisation can create and assign its own barcodes, with no need to consult or use a central authority. In this scheme, a barcode comes to be an unique number – similar perhaps to a Type 1 UUID or an Ethernet MAC. As a consequence:

  • GS1 can’t easily tell you to whom a barcode belongs
  • Barcodes don’t generally encode data (there are rare exceptions, eg for ISBNs)
  • There is no central way of taking a barcode and finding out details of the item it represents

A further complexity is that the GS1 company prefixes vary in length – a bit like credit card number prefixes.

All this can make finding information about barcodes hard.

Finding information – the HDN GS1 Web Service

To make it easier to find out information about the range of barcodes in use in the NHS, the GS1 Web Service has been written. It’s a simple REST interface with an endpoint at http://services.developer-test.nhs.uk/gs1/.

The service provides ways to either:

  • Find all the NHS organisations using GS1 barcodes, their address details and their GS1 Company Prefix.
  • Find a particular organisation given a barcode. This is a single record, but with the same details.

For convenience, we’ve written an open source java client that exposes it to end users. The client is such that it can be used either as a standalone program (in which case, it produces results in TSV format for ease of use with standard POSIX tools) or as a java library (jar). To make things as easy as possible, we’ve also written some wrappers for POSIX and Debian/Ubuntu. Your choices, in order of decreasing convenience, are:

  • Debian/Ubuntu deb packages, hosted in our apt repository
  • A tar ball, which contains a complete file system to untar over your root /. These should work on any POSIX system, including Mac OS X and Cygwin.
  • A standalone java jar, with all dependencies included, suitable for execution or as a library
  • A set of java code libraries with source
  • Forking from github

See the download page for instructions on how to install one of these.

Making Requests

The best way to get going is to use the command line. We’ll look later on how to create requests programmatically using the java library.

Using the command line client to make requests

The way you do this varies depending on what you used above:

  • If you’ve installed the deb package or the tar ball, you’ll have the program hdn-gs1-client on your PATH. To use it, open a terminal console and type hdn-gs1-client. It takes standard POSIX options.
  • If you’ve installed the standalone jar file, you’ll need to run commands from the from the folder you downloaded the file to. Open a terminal console and change folder to the folder it is in. Type java -jar hdn-gs1-client.jar. It takes the same standard POSIX options as the program above. For the rest of this document, wherever you see hdn-gs1-client … you can substitute java -jar hdn-gs1-client.jar …
  • If you’ve downloaded or forked source from github, you can use IntelliJ to run the main class. Open source\subprojects.ipr and run the main class uk.nhs.hdn.dbs.request.client.HdnDbsRequestConsoleEntryPoint. There are already some sample configurations set up for you to debug in IntelliJ. If you don’t have or use IntelliJ (and really should) then you can open in Eclipse or NetBeans. You’ll need to add the libraries ‘annotations’ (library/annotations/VERSION/annotations.jar) and ‘jopt-simple’ (library/jopt-simple/VERSION/jopt-simple-VERSION.jar) to the class path.

Checking everything’s OK

Before we get going, let’s check that everything works as expected. Run the command hdn-gs1-client –help (remember to substitute java -jar hdn-gs1-client.jar if you need to). You should see a list of supported options. At the time of writing, it looks like this:

Option                                 Description
------                                 -----------
--cache
--domain-name <domain name to connect  (default: developer-test.nhs.uk)
  to>
--gtin <A sequence of 12, 13 or 14     (default: 5055220798768)
  barcode digits 0 to 9 inclusive
  representing a GTIN-12, GTIN-13 or
  GTIN-14 (Global Trade Item Number)>
--help                                 Displays help for options
--port <Integer: port to connect to    (default: 80)
  HTTP(S) on>
--use-https
--version                              Displays version

If the output seems a bit compressed, it’s because we’re formatting for a 40 character wide screen – useful if you’re running this over ssh on Android. Whilst you can’t see it above, help output always produces an exit code of 1.

Since the options are regular POSIX long options (and are named similarly to those in the GNU coding standards), we can abbreviate them. Hence hdn-gs1-client -h and hdn-gs1-client –he will produce the same output. The only time you can’t do this is if the abbreviation would be ambiguous.

Let’s try out one of those options: –version.

Checking the version installed

Let’s run hdn-gs1-client –version:

hdn-gs1-client 2013.03.01.1537-development
© Crown Copyright 2013

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Written by Raphael Cohn (raphael.cohn@stormmq.com)

Standard GNU-like stuff. It’s worth understanding the version number, in this case, 2013.03.01.1537-development. The part before the hyphen is the timestamp of the last git check in used to build the binary – you should be able to find it using git log. Additionally, this should match the version of the deb package. The part after is the git branch the code was built from. Usually this will be either development or master.

If instead it says unknown version then it means you’re using code you’ve compiled yourself or wasn’t released ‘officially’.

Simple Query to Find all GS1 Company Prefixes for the NHS

It couldn’t be simpler to find all the NHS organisations and their GS1 company prefixes:

hdn-gs1-client

This will produce TSV (tab separated value) output to standard out (stdout) of all the known NHS organisations using GS1 company prefixes:

gs1CompanyPrefix    trust   organisationName    PostCode
5050898 NHS connecting for Health   NHS Connecting for health   ls1 4HY
50552071    Cambridge University Hospitals NHS Foundation Trus  Addenbrooke's Hospital  CB2 0QQ
50552093    Airedale NHS Trust  Airedale General Hospital   BD20 6TD
50552323    Northumbria Healthcare NHS Foundation Trust Alnwick Infirmary, Hillcrest Maternity Unit NE63 9JJ
50552261    Leicestershire County & Rutland Community Health Services (LCRCHS)  Ashby & District Hospital   LE13 1SJ
50552171    Ashford & St Peters NHS Trust   Ashford Hospital    TW15 3AA
50552348    Barnet and Chase Farm Hospitals NHS Trust   Barnet and Chase Farm Hospitals NHS Trust   EN5 3DJL
50552207    Barts Health NHS Trust  Barts Hospital  E1 8PR
50552143    Basildon and Thurrock NHS Trust Basildon University Hospital    SS16 5NJ
...

It starts with a header line. If you don’t want the header, you can use tail to remove it:

hdn-gs1-client | tail -n +2

The output can be further used in other POSIX tools, such as awk or cut. It can also be piped to sort.

The format is flexible. In the future, additional data might be added. If so, it will be as additional columns. Currently, the extension columns are only:

  • Post Code: Unformatted, uncorrected post code data that will need to be case converted, trimmed and whitespace normalised. After that, it can be parsed using AbstractPostCode.valueOf() in the module common-postCodes.

Advanced Query to find a NHS organisation for a barcode

To find a NHS organisation using a particular barcode is very useful. Barcodes come in many different formats. Those used in the NHS are typically encoded as GTINs. A GTIN is a Global Trade Item Number. There are four different formats:

  • GTIN-8: A compressed 8 digit number.
  • GTIN-12: The original standard, sometimes called a UPC (Universal Product Code). Common in the US. Has 12 digits.
  • GTIN-13: The most common around the world. Also known as an EAN (European Article Number). Has 13 digits.
  • GTIN-14: The latest standard. Has a prefix digit to identify a ‘packaging level’ (corresponds to whether it’s an item, a box items or a pallet of boxes of items, etc). Not common. 14 digits.

All GTINs include a check digit to verify the number is correct. The web service and GS1 client enforces this check.

The web service and client don’t use GTIN-8. This isn’t a problem, as GTIN-8s are rare in the NHS.

Let’s say you have a known barcode, eg 5055220798768. To find details for it, type:

hdn-gs1-client --gtin 5055220798768

If successful, you’ll get two lines on standard out:

gs1CompanyPrefix    trust   organisationName    PostCode
50552207    Barts Health NHS Trust  Barts Hospital  E1 8PR

A header line and a details line. The format is identical to that for all organisations. The exit code will be 0.

If unsuccessful, the program will produce a stack trace and an exit code of 2.

Using Caching

You might haven noticed an additional option, –cache. This demonstrates how the organisation data can be downloaded (simple query above) and used as a cache to resolve advanced queries. The command line output is identical:

hdn-gs1-client --cache --gtin 5055220798768

To see how this code works, you’ll need to look into the java code. Take a look at uk.nhs.hdn.barcodes.gs1.client.application.Demonstration, and the method demonstrateClientApplication. This is the starting point for the client proper, once the command line switches have been parsed and checked. This nicely brings us on to the next section.

Using the java library to make requests programmatically

The way you do this varies depending on what you used above:

  • If you’ve downloaded or forked source from github, you can use IntelliJ. Open source/subprojects.ipr and start hacking.
  • If you’ve downloaded the jars (and source zips), create a project or add them to an existing project in your favourite IDE (if it isn’t IntelliJ, then switch now).

You can either add the hdn-gs1-client.jar as is (simple option), or, for more refinement and better integration with other HDN tools, its dependent jar files:

  • barcodes-gs1-client
  • barcodes-gs1
  • common-http-client
  • common-http
  • common-http-client-json
  • barcodes-gs1-organisation
  • common-serialisers-separatedValues
  • common
  • and the third-party library annotations.jar (this is a compile-time only dependency)

This list may change. To find the most up-to-date list, either extract META-INF/MANIFEST.MF from hdn-gs1-client.jar and read the Class-Path entry, or open the IntelliJ project (source/subprojects.ipr) and look at the dependencies of the module dbs-request-client (sensibly, module names match jar names and source zip names). Note that you’ll not need the common-commandLine module (jar) or jopt-simple library.

Simple Query to Find all GS1 Company Prefixes for the NHS

The ‘guts’ of the java library’s API is the class ClientApplication. It’s in the package uk.nhs.hdn.barcodes.gs1.client.

A simple code snippet might look like:

final ClientApplication clientApplication = new ClientApplication();
final Tuple[] results = clientApplication.listAllKnownCompanyPrefixes();

The first line creates a ClientApplication. This object is immutable and thread-safe.

The second line gets results (if successful). This method is thread-safe and side-effect free; it can be called many times to get the latest list. If it fails, it may throw one of three exceptions:

  • UnacceptableResponseException: This should not normally occur. It implies that the web service returned a non-200 OK response code.
  • CorruptResponseException: The returned response was corrupt in some way. If this occurs consistently, then the web server is broken. Intermittently, it may be due to unreliable network topology.
  • CouldNotConnectHttpException: A ronseal exception. Usually indicative of poor network conditions or that the remote server is unavailable. Being hosted on azure, pinging the server will not work to try to isolate the cause.

This code an array of Tuple. A tuple is the equivalent of a data row returned by the command line client. A tuple has three main values:

for (final Tuple tuple : tuples)
{
    final Gs1CompanyPrefix gs1CompanyPrefix = tuple.gs1CompanyPrefix();
    final String organisationName = tuple.organisationName();
    final String trust = tuple.trust();
}

Additionally, a gs1CompanyPrefix has a gs1Prefix:

    final Gs1Prefix gs1Prefix = gs1CompanyPrefix.gs1Prefix();

It’s also possible to extract any additional information (extension details). Currently, this is limited to raw Post Code data that will need trimming, case normalising and white space normalising:

    final String postCode = (String) tuple.additionalInformationFor(PostCode);

Additional information is parsed generically as a JSON map. Consequently, it can not be known in advance the type of likely data. However, the parser will make sensible choices. Currently, the only additional information is for PostCode and this is always a String. Additional information uses a known key, as defined in the enum AdditionalInformationKey. PostCode is an enumerated constant in this class.

Putting it all together, a simple sample class might look like:

package uk.nhs.hdn.barcodes.gs1.client.application;

import uk.nhs.hdn.barcodes.gs1.client.ClientApplication;
import uk.nhs.hdn.barcodes.gs1.companyPrefixes.Gs1CompanyPrefix;
import uk.nhs.hdn.barcodes.gs1.gs1Prefixes.Gs1Prefix;
import uk.nhs.hdn.barcodes.gs1.organisation.Tuple;
import uk.nhs.hdn.common.http.client.exceptions.CorruptResponseException;
import uk.nhs.hdn.common.http.client.exceptions.CouldNotConnectHttpException;
import uk.nhs.hdn.common.http.client.exceptions.UnacceptableResponseException;

import static uk.nhs.hdn.barcodes.gs1.organisation.AdditionalInformationKey.PostCode;

public class Example1
{
    public void example() throws UnacceptableResponseException, CorruptResponseException, CouldNotConnectHttpException
    {
        final ClientApplication clientApplication = new ClientApplication();
        final Tuple[] tuples = clientApplication.listAllKnownCompanyPrefixes();
        for (final Tuple tuple : tuples)
        {
            final Gs1CompanyPrefix gs1CompanyPrefix = tuple.gs1CompanyPrefix();
            final String organisationName = tuple.organisationName();
            final String trust = tuple.trust();

            final Gs1Prefix gs1Prefix = gs1CompanyPrefix.gs1Prefix();

            final String postCode = (String) tuple.additionalInformationFor(PostCode);
        }
    }
}

To see it in code, look at the class uk.nhs.hdn.barcodes.gs1.client.application.Example1.

Advanced Query to Find an Organisation’s Details

This is similar to the code above. Instead of listAllKnownCompanyPrefixes() we use listCompanyPrefixForGlobalTradeItemNumber() which just returns a single Tuple:

final Tuple tuple = clientApplication.listCompanyPrefixForGlobalTradeItemNumber("5055220798768");

The rest of the code is very similar, although there’s no need for the loop:

final Gs1CompanyPrefix gs1CompanyPrefix = tuple.gs1CompanyPrefix();
final String organisationName = tuple.organisationName();
final String trust = tuple.trust();

final Gs1Prefix gs1Prefix = gs1CompanyPrefix.gs1Prefix();

final String postCode = (String) tuple.additionalInformationFor(PostCode);

If the barcode can’t be resolved to a tuple, then the service returns a 404 Not Found response code. The client will then throw a UnacceptableResponseException which can be inspected for this:

catch(UnacceptableResponseException e)
{
    if (e.isNotFound())
    {
        return;
    }
    throw e;
}

In this code, the second line checks for a 404 and swallows the exception.

Putting it all together, a simple sample class might look like:

package uk.nhs.hdn.barcodes.gs1.client.application;

import uk.nhs.hdn.barcodes.gs1.client.ClientApplication;
import uk.nhs.hdn.barcodes.gs1.companyPrefixes.Gs1CompanyPrefix;
import uk.nhs.hdn.barcodes.gs1.gs1Prefixes.Gs1Prefix;
import uk.nhs.hdn.barcodes.gs1.organisation.Tuple;
import uk.nhs.hdn.common.http.client.exceptions.CorruptResponseException;
import uk.nhs.hdn.common.http.client.exceptions.CouldNotConnectHttpException;
import uk.nhs.hdn.common.http.client.exceptions.UnacceptableResponseException;

import static uk.nhs.hdn.barcodes.gs1.organisation.AdditionalInformationKey.PostCode;

public class Example2
{
    public void example() throws CorruptResponseException, CouldNotConnectHttpException, UnacceptableResponseException
    {
        final ClientApplication clientApplication = new ClientApplication();
        final Tuple tuple;
        try
        {
            tuple = clientApplication.listCompanyPrefixForGlobalTradeItemNumber("5055220798768");
        }
        catch (UnacceptableResponseException e)
        {
            if (e.isNotFound())
            {
                return;
            }
            throw e;
        }

        final Gs1CompanyPrefix gs1CompanyPrefix = tuple.gs1CompanyPrefix();
        final String organisationName = tuple.organisationName();
        final String trust = tuple.trust();

        final Gs1Prefix gs1Prefix = gs1CompanyPrefix.gs1Prefix();

        final String postCode = (String) tuple.additionalInformationFor(PostCode);
    }
}

To see it in code, look at the class uk.nhs.hdn.barcodes.gs1.client.application.Example2.

Using caching

This is beyond the scope of this article. Take a look at the class uk.nhs.hdn.barcodes.gs1.client.CachingClientApplication. In its constructor, it takes an instance of ClientApplication.

This class could be improved – for example, by periodically re-querying the list of organisations, or even by making use of the REST service’s HTTP response cache response headers to determine cache freshness. To do this, you might want to explore the underlying common-http-client classes. Much of the logic used in the Index here is the same code as used by the REST service itself.