Example 11: REST Interface

This example illustrates how to add a REST interface to our WEX using the PTC framework.

You can find the code for the example in our GitHub repository.

Introduction

PTC has introduced a REST (Representational State Transfer) interface into Windchill. It is very effective and provides a number of services out-of-the-box. The following example is used to add a new interface to the standard PTC REST one and automate both the deployment and the Java components of the interface.

The PTC documentation describes its REST features here.

Implementation

The implementation of this REST interface is very simple. We are effectively deploying a standard extension and calling it via a WexInvoke, taking full advantage of the dynamic nature of the WEX Framework. The REST part is dynamic, it's available at the moment of deployment and does not need a server restart.

The extension is extending the standard PTC v3 domain. The standard way to extend the PTC REST service is to add these files:

1628451578100

The implementation is as follows:

function function_HelloWorld(data, params) {
    return "Hello World!";
}

function function_HelloWex(data, params) {
    var WindchillRequester = Java.type('com.wincomplm.wex.kernel.api.invoke.WexInvoker');
    return WindchillRequester.invoke("com.wincomplm.wex-example-rest","wex-example-rest-methods.hello-world",data, params);
}

It declares the Nashorn JavaScript. In the Hello World method we simply return a simple string from Javascript, while in the HelloWex we call the extension.

The JSON code declares these functions to the ODATA REST Framework that is included in Windchill:

{
    "functions": [
        {
            "name": "HelloWorld",
            "importName": "HelloWorld",
            "description": "Hello to the world",
            "parameters":[],
            "returnType": {
                "type": "String"
            }
        },       
        {
            "name": "HelloWex",
            "importName": "HelloWex",
            "description": "Hello to the world from Wex",
            "parameters":[],
            "returnType": {
                "type": "String"
            }
        }
    ]
}

The Java code that is in the extension is as follows:

    @WexMethod(name = "hello-world", description = "Hello World")
    public String helloWorld(Object data, Object params) throws Exception {
        logger.trace("=>helloWorld");
        String result = "Hello " + SessionHelper.getPrincipal().getName() + " from Wex!";
        logger.trace("<=helloWorld " + result);
        return result;
    }

Execution

The execution is simple: as these are non-complex GET interfaces, they can be called from a browser; otherwise, we would have to test will a REST tool such as Postman.

Hello World Example

1628451189701

Hello WEX Example

1628451211422

Other Implementations

It is not mandatory to use REST interfaces to get data from Windchill to another system. A simple JSP page that uses a WexInvoker.invoke method can do the trick:

114545886

<%@ page trimDirectiveWhitespaces="true" %>
<%@ page import="com.wincomplm.wex.kernel.api.invoke.WexInvoker"%>
<html>
    <body style="font-family: Arial, Helvetica, sans-serif;">
        <%= (String) WexInvoker.invoke("com.wincomplm.wex-example-rest","wex-example-rest-methods.getModifiedParts", request,response) %>
</html>

This page can also be secured as seen in previous examples. The access to this information is also handled by Windchill's access control.

Below is the getModifiedParts method that is being invoked. It simply gets modified parts from X days ago (defined in configuration) and returns raw json.

@WexMethod(name = "getModifiedParts", description = "Gets modified parts from x days ago")
    public String getModifiedParts(HttpServletRequest request, HttpServletResponse response) throws Exception {
        logger.trace("=>getModifiedParts");
        String daysAgo = config.getPartsFromDaysAgo();
        Calendar today = Calendar.getInstance();
        today.add(Calendar.DATE, -Integer.parseInt(daysAgo));
        Date fromDaysAgo = today.getTime();
        List<WTPart> parts = getModifiedPartsFrom(fromDaysAgo);
        List<WexExamplePart> exParts = new ArrayList();
        for (WTPart part : parts) {
            WexExamplePart exPart = new WexExamplePart();
            exPart.setNumber(part.getNumber());
            exPart.setOid(WexQueryHelper.getOid(part));
            exPart.setVersion(part.getVersionIdentifier().getValue() + "." + part.getIterationIdentifier().getValue());
            exParts.add(exPart);
        }
        Gson gson = new Gson();
        String result = gson.toJson(exParts);
        logger.trace("<=getModifiedParts");
        return result;
    }
    private List<WTPart> getModifiedPartsFrom(Date from) throws QueryException, WTException {
        QuerySpec qs = new QuerySpec(WTPart.class);
        qs.appendWhere(new SearchCondition(
            WTPart.class, 
            WTPart.MODIFY_TIMESTAMP, 
            SearchCondition.GREATER_THAN_OR_EQUAL,
            new Timestamp(from.getTime())), 
           new int[]{0});
        QueryResult qr = PersistenceHelper.manager.find(qs);
        return WexQueryHelper.getListFromQueryResult(qr);
    }

Because the object WTPart is too complex, we need to create a simpler object that represents the data we need. That is what WexExamplePart is for and is what we send back as JSON:

389649562

public class WexExamplePart {

    String number;
    String oid;
    String version;

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    public String getOid() {
        return oid;
    }

    public void setOid(String oid) {
        this.oid = oid;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

}

The result when accessing the JSP {hostname}/Windchill/netmarkets/jsp/com/wincomplm/wex/example/rest/getModifiedParts.jsp is the simple JSON:

367395629