As with SDK version 1.4 and 1.5 I  want to give you an overview over the new features of the Design Studio SDK in version 1.6.

This time I have more to share than I originally expected.

Using sap.m SAPUI5 Controls

You might know that Design Studio was one of the first SAP products that were based on the  SAPUI5 framework. What you might not know: There are two flavors of SAPUI5 available that are unfortunately incompatible. The team first started with a library of typical controls, e.g. buttons, input fields, containers, etc. in a package called “sap.ui.commons”. This library was used in Design Studio from the beginning on. However, they were designed with the desktop browser in mind. Later the SAPUI5 team decided to create a second library especially for mobile devices called “sap.m”. It was incompatible to sap.ui.commons, e.g. there was different handling of click and touch events. When SAP Fiori was started, the teams from application development decided to use “sap.m”, for both, mobile and desktop. Therefore the meaning of “sap.m” changed from “mobile” to “main”.

I know that many of the fellow SDK developers tried to use sap.m controls as SDK components in Design Studio, mainly as there are so many nice controls available in the sap.m library. Unfortunately this doesn’t work well, e.g. events are lost and controls don’t visually fit to the rest.

Therefore we decided to have a new mode in Design Studio where all components are implemented with sap.m controls.

For the Design Studio user there is not much change in this mode, except the different look and feel and some missing components in the “sap.m” mode.

For SDK components the change could be significant: As we don’t know if your component will work correctly in “m-mode”, by default, existing SDK components won’t show up if an app is in m-mode.

If your SDK component uses handler_type=”div”, it is likely that it will work. Test your component and mark it in your contribution.xml as “sap.m” compliant:


<?xml version="1.0" encoding="UTF-8"?>
<sdkExtension
  xmlns="http://www.sap.com/bi/zen/sdk"
  id="com.sap.sample.coloredbox"
  title="Design Studio SDK Extension Sample Colored Box"
  version="16.0"
  vendor="SAP">
  <component
  id="ColoredBox"
  title="Colored Box"
  icon="res/icon.png"
  handlerType="div"
  modes="commons m"
  propertySheetPath="res/additional_properties_sheet/additional_properties_sheet.html">
...

For SDK components with handler_type=”sapui5″ it depends on the controls you use: If you use only “sap.ui.commons” controls, leave the “modes” attributes out or set modes=”common”. For components that use only “sap.m” controls, set modes=”m”. Never mix “sap.ui.commons” and “sap.m” controls in the same component. You can however create a dynamic component that uses different implementations depending on the mode: You could have one require.js module for “m” and one for “commons”.

Full Require.js Support

In version 1.4 I announced limited support to load JavaScript files with require.js . This was sufficient for some use cases, but often our old loading mechanism had disadvantages. E.g. some some browsers (e.g. Firefox) have problems loading JavaScript files synchronously.

Now we completely switched to require.js to load our own JS files – and SDK components can also do so.

The old loader is still in place to stay compatible. However, I strongly recommend to use the require.js feature also in your SDK components, as you will gain several benefits.

As example we look at the contribution.xml of ColoredBox as it was in 1.5:


<?xml version="1.0" encoding="UTF-8"?>
<sdkExtension xmlns="http://www.sap.com/bi/zen/sdk"
  title="Design Studio SDK Extension Sample Colored Box"
  version="15.0"
  vendor="SAP"
  id="com.sap.sample.coloredbox">
  <component id="ColoredBox"
  title="Colored Box"
  icon="res/icon.png"
  handlerType="div"
  propertySheetPath="res/additional_properties_sheet/additional_properties_sheet.html">
  <jsInclude>res/js/component.js</jsInclude>
  <cssInclude>res/css/component.css</cssInclude>
...
  </component>
</sdkExtension>

And component.js looked like:


sap.designstudio.sdk.Component.subclass("com.sap.sample.coloredbox.ColoredBox", function() {
// Content here
});

To load you extension with require.js, use the new <requireJs> tag:


<?xml version="1.0" encoding="UTF-8"?>
<sdkExtension
  xmlns="http://www.sap.com/bi/zen/sdk"
  id="com.sap.sample.coloredbox"
  title="Design Studio SDK Extension Sample Colored Box"
  version="16.0"
  vendor="SAP">
  <component
  id="ColoredBox"
  title="Colored Box"
  icon="res/icon.png"
  handlerType="div"
    modes="commons m"
  propertySheetPath="res/additional_properties_sheet/additional_properties_sheet.html">
  <requireJs modes="commons m">res/js/component</requireJs>
...
  </component>
</sdkExtension>

The tag name <jsInclude> is replaced by <requireJs> and the extension “.js” is omitted – simple. You also need to specify for which modes (sap.ui.commons, sap.m, or both) the module is loaded. Using this feature you can have different modules for different modes.

The <cssInclude> and potential <stdInclude> tags can be removed. Why?

Let’s look at the changed component.js:


define(["sap/designstudio/sdk/component", "css!../css/component.css"], function(Component, css) {
  Component.subclass("com.sap.sample.coloredbox.ColoredBox", function() {
 // Content here
  });
});

Using “define” we make our handler a require.js module. The fist parameter is an array of other require.js modules or CSS files that your module needs. As an SDK module it requires the base “class” for all SDK handlers. Before this, the base class was requested using the global variable “sap.designstudio.sdk.Component”. Now the same is “injected” as  a local variable “Component” in response to the required module “sap/designstudio/sdk/component”. The second required module is a CSS file – or to be more precise: “css!” marks a requirs.js plugin for loading CSS files which is always available. The path “../css/component.css” is the relative path to the CSS file. The result of the css file is injected into local variable “css” – which is not really needed for a CSS file and is here only to show the symmetry between the required modules array and the callback function’s parameters.

The require.js support also makes the <stdInclude> feature obsolete. The following snippet from Sparkline’s handler JavaScript file:


define(["d3", "sap/designstudio/sdk/component"], function(d3, Component) {
  Component.subclass("com.sap.sample.sparkline.Sparkline", function() {
  var that = this;
  var sparkline_data = undefined;
  var cssstyle = undefined;
  var path = undefined;
  this.init = function() {
  var container = this.$()[0];
  var graph = d3.select(container).append("svg:svg").attr("width", "100%").attr("height", "100%").attr("viewBox", "0 0 1000 1000").attr("preserveAspectRatio", "none");
  //appending an svg:path element
  path = graph.append("svg:path");
  };

...

The D3 library is required as “built-in” library and injected into local variable “d3”.

Currently we support the following built-in libraries:

Module Name Description Alternative
“jquery” jQuery Also always available via global variables “jQuery” and “$”

“d3”

D3 (d3.v3.js) Also available via global variable “d3″, if requested by other components or <stdInclude kind=”d3”>.
“_” Underscore (underscore.js) Currently always available via global variable “_”
“sap/designstudio/sdk/component” Base class for DIV-SDK-Components Backward compatible global variable “sap.designstudio.sdk.Component”.
“sap/designstudio/sdk/datasource” Base class for pure SDK data sources Backward compatible global variable “sap.designstudio.sdk.DataSource”. 
“sap/designstudio/sdk/databuffer” Base class for SDK datasources using Data Buffer Backward compatible global variable “sap.designstudio.sdk.DataBuffer”. 

Note: Don’t use the backward compatible global in required modules, this could give strange results!

Instead requiring a built-in module, you can also require your own modules or third-party libraries that come with your extension. You could even use your own copy of d3 – if you are sure that it comes as AMD-enabled module that doesn’t create global variables:


define(["./tp/myd3", "sap/designstudio/sdk/component"], function(d3, Component) {
  Component.subclass("com.sap.sample.sparkline.Sparkline", function() {
// ...

Commercial SDK components still must not bring their own copy of built-in libraries, as this will bloat the loaded JavaScript files, but at least it works now.

Technical Components

Since a few releases Design Studio supports so-called technical components that have no visual representation and accordingly don’t appear in the Components palette. Now such component can be implemented with the SDK. A good example is the “Timer” sample component that triggers an event after a configurable time. The sample has been productized as official component in Design Studio 1.6, but it still ships as a sample.

The most important change is setting group=”TECHNICAL_COMPONENTS” in contribution.xml. This will remove the component from palette and let it appear in the “Technical Components” context menu. The second change is in in the JavaScript handler


this.init = function() {
  this.$().css("display", "none");
};

You see that a technical component is still a normal SDK component with handler_type=”div”, but the root DIV is not rendered.

Complex Properties and Arrays

So far the type system for properties of SDK components was quite limited: Besides the data-bound properties there were only primitive types available. But most components need some structured or array-like properties.

SDK components often simply used a String-typed property and encoded some data via JSON.stringify. But this approach has several disadvantages:

  • In biapp-files and bookmarks, such properties appear as an unstructured blob, while regular structured properties have an XML format that is better for diagnostics.
  • Users can’t directly enter such JSONs, thus the component need an additional properties sheet to enter data in such a property.
  • Working with ZTL script functions on such a property is quite cumbersome, e.g. you have to JSON.parse it, modify, serialize it back etc.

Now he added native support for structures properties and arrays. Here is the concept:

If a property has the new type “Object”, it must contain nested properties. This will form a structure.


      <property
        id="item"
        type="Object"
        title="Item">
        <property
          id="text"
          type="Text"
          title="Text"/>
        <property
          id="key"
          type="String"
          title="Key">
         </property>   
      </property>

You can nest all property types into an Object property, except data-bound properties and ScriptTexts. But you can could nest properties of type Object into others, producing a deep structure.


The standard property view has built-in editing support for nested properties.


The other new property type is “Array”. It must contain exactly one property specifying the type of the array. The following example creates a so-called simple array for integers:


<property
      id="intArray"
      type="Array"
      title="Int Array">
      <property
        id="item"
        type="int"
        title="Item"> 
      </property>
    </property>

You can also use a property of type “Object” as array type – forming a table-like property.


<property
      id="items"
      type="Array"
      title="Items">
      <property
        id="item"
        type="Object"
        title="Item">
        <property
          id="text"
          type="Text"
          title="Text"/>
        <property
          id="key"
          type="String"
          title="Key">
          <option name="keyfield" value="true"/>
         </property>
        <property
          id="image"
          type="Url"
          title="Image">
  <option name="kind" value="Image" />
        </property>
      </property>
    </property>

The standard property view has built-in support for simple and table-like arrays. Deep nesting however is not supported in arrays.

/wp-content/uploads/2015/11/array_editor_833111.png

The example screenshot and XML snippet above are taken from the new UI5List sample component. They also demonstrate the new <option> features“keyfield” and “kind” that can be applied to properties. Option “keyfield” tells the editor that the property value in this columns should be unique. The option “kind” for properties of type “Url” gives the editor a hint to provide automatic browse and upload feature for pictures and some other file types.

Both, structured properties and arrays are passed to the JavaScript handler as a JSON or JSON array. Thus your setter will receive a valid JavaScript object – and the setter must return a JavaScript object that can be serialized with JSON.parse.

The same is also the case for accessing the property in ZTL functions. You can access the property via this.<propertyName>. Because of some internal logic you must however re-assign it back in case your changed it. Here  a snipped from UI5List’s contribution.ztl:


void addItem(String key, String text) {*
    var current = this.items || [];
    current.push({
      key: key,
      text: text,
      image: ""
    });
    this.items = current; // Write back changed object
  *}

You can access and modify structured properties and arrays also via your Additional Properties Sheet (APS). Again you access them via JSON:


this.items = function(value) {
  if (value === undefined) {
  return this._items;
  } else {
  this._items = value;
  return this;
  }
  };
// ...
$("#form").submit(function() {
  var array = that.items() || [];
  array.push({
  key: $("#key").val(),
  text: $("#text").val()
  });
  that.items(array).firePropertiesChanged(["items"]);
  return false;
  });

Call ZTL Functions from Component and APS

One powerful feature that came into the SDK in 1.4 is the possibility to enrich the SDK component’s possibilities calling BIAL functions whenever needed: The JavaScript handler running in the browser could call a function written in server-side JavaScript defined in contribution.ztl. However, this was more a concept than a feature and hard to use. E.g. you had to define private properties and events to transport data from browser to sever and vice versa.

Now the SDK provides a handy function that does all this under the hood.

E.g. assume your SDK component at some point of time needs a list of dimensions. Then define a private function in your contribution.ztl:


@Visibility(private)
  // The method is called by the callZTLFunction feature. The return type is not correct,
  // but as it can't be used in scripts, this doesn't care.
  String getDimensions(String axis) {*
     if (!this.getDataSource())
        return null;
     if (axis) {
        return this.getDataSource().getDimensions(axis);
     }
     return this.getDataSource().getDimensions();
  *}

The call “this.getDataSource().getDimensions()” consists of a navigation to the data source of your component (this.getDataSource()) and the BIAL script function “getDimensions()” returning a list of dimensions.

Now you can easily call this private function from your component.js code:


that.callZTLFunction("getDimensions", function(result) {
  // do something with the dimension array
}, "ROWS");

The function callZTLFunction(sFunction, fResultCallback, parameters…) expects the name of the function to call (parameter sFunction), a callback function that receives the result (parameter fResultCallback) and an arbitrary number of parameter to pass in to your ZTL function.

Internally this feature uses the same principles that came with 1.4 – but under the hood uses some built-in properties and events. Therefore all parameters and results must be JSON-serializeable and the execution is asynchronous.

There was the wish from some famous Design Studio SDK coder to be able to use the same trick also from a

Additional Properties Sheet – and voilá, here you are: You can use the exact same API from an APS JavaScript. Internally is works differently, but the ZTL script is executed in the normal runtime – also in Design Mode. You can however only read data with such a script. Modifying operations, e.g. setFilter are not allowed to be called from the APS and will bring up an error message in the Designer’s error log.

Examples for using callZTLFunction, both from component and APS are contained in the new UI5List sample.

APS Improvements

By the way, there are a few little improvements in APS API:

  1. Now there are also beforeUpdate and afterUpdate callbacks available that have the same behavior as the corresponding functions in component JS.
  2. The order for finding changed properties has changed a bit. This makes transactional behavior easier to achieve. The order is now:
    1. init or componentSelected
    2. beforeUpdate
    3. getter for property 1.
    4. getter for property 2.
    5. setter for propertyv 1 – in the case that new value and value returned from getter are different
    6. setter for property 2 – in the case that new value and value returned from getter are different
    7. afterUpdate
  3. There is a new function “log(message, severity)” available, where “message” is your text and “severity” is “info”, “warn” or “error”. Depending on the given severity and the configured log level, the message will appear in Design Studio’s Error log view and in Debug mode also in the JavaScript console.

Update SDK Components without Restart

The logging feature is one of the features to make SDK component development easier. Another is a feature that virtually removes the need to restart your target Design Studio while developing your component.

You might know that you can easily debug and change your JavaScript code without restarting Design Studio: Regardless if there are problems in Design Mode, simply launch a test app with your component in your favorite external browser. In the debug tools of the browser disable the browser cache and debug. Whenever you need to change something, change your JavaScript file and hit F5 on your browser.

If the change was however in the contribution.xml or contribution.ztl file – e.g. because you added a property – you had to restart Design Studio. Now you simply switch to Debug mode by pressing Ctrl+Alt+Shift+D. Now you will see the new menu entry Tools->Refresh SDK Extensions. This function will clear several caches and make almost all changes immediately active. For some changes it might be necessary to close and reopen the current editor or the additional properties view.

The Debug mode also makes development of Design Mode behavior easier. The JavaScript console shows detailed information about what’s happening in the main browser, and the scripting tab allows to evaluate JavaScript there. For APS debugging it also greatly helps to  increase the log level in Tools->Preferences->Application Design->Support Settings and to configure the Error Log viewer to have a very high limit of logging events.

For in-depth debugging of APS logic however is still best to attach a Microsoft Studio Web debugger to your target Design Studio’s Internet Explorer instances. In most cases the free Express edition should be sufficient.

The SDK component update without restart is also available on the BI Platform. The runtime will detect changes in deployed SDK extensions and make the changes available without BIP restart. As within the Designer this is the case for most changes, but not for completely new SDK components – which still require a restart.

One More Thing

Besides the stuff mentioned so far I have worked on a new feature that makes it easy to bind Design Studio data to SAPUI5 controls. It might later become a part of the Design Studio SDK but was not fully finished for 1.6. I only mention here that something will be coming to save you from investing a lot of time and energy in other solutions to map SDK Result Set JSONs to SAPUI5 tables etc.

You see that you still have an interesting job creating Design Studio extensions and I’m looking forward to seeing more interesting ones from you – the Design Studio SDK community.

To report this post you need to login first.

22 Comments

You must be Logged on to comment or reply to a post.

  1. Mustafa Bensan

    Hi Reiner,

    Thanks for the detailed write-up and continued enhancements to the SDK.  I think the new SDK features you’ve described definitely open up the doors for developing more creative extensions.

    I must admit the new features cover most (if not all) of my current wish list 🙂 .  One question indirectly related to your last point about binding data to SAPUI5 controls:  Now that Design Studio effectively allows multiple data sources to be assigned to a single component via the property binding feature, is there a possibility that an SDK function could be introduced to allow a data-bound property to be dynamically assigned/bound to a nominated data source from within the component.js code at run-time?

    Regards,

    Mustafa.

    (0) 
    1. Reiner Hille-Doering Post author

      Hi Mustafa,

      I’m happy that you like it 🙂 .

      About the possibility to configure property binding programmatically:

      I heard this request before and can understand it. But it is not as easy as it sounds. For this it’s important to understand that property bindings are in fact technical components. They bind to the resultSetChanged event and update another component’s property.

      Therefore configuring property binding means to create or at least re-assign such a binding component.

      This could be enabled in future even in normal BIAL scripting. Then you could change binding’s properties like normal component’s properties. Here some pseudo code how it could look like:

      var CELL_BINDING_1 = APPLICATION.createComponent(“CELL_BINDING”);

      CELL_BINDING_1.setDataSelection(“<something>”);

      CELL_BINDING_1.set…;

      BUTTON_1.bind(“TEXT”, CELL_BINDING_1);

      Reminder: This is not working today and only a rough idea.

      BUT: I could imagine that such a feature is too complicated for many Design Studio users, therefore we concentrated in 1.6 to make the usage of Binding in Properties View easier.

      But I think for 1.6 we already delivered quite some nice features anyway 😉 .

      Reiner.

      (0) 
      1. Mustafa Bensan

        Hi Reiner,

        Thanks for the explanation of the property binding.  Yes, I completely agree and appreciate that some very nice features are indeed being delivered with DS 1.6 already.  I particularly like the support for the sap.m library, technical components, complex properties and ZTL function calls 🙂 .

        A couple of more questions if I may:

        1)  If my understanding is correct, prior to DS 1.6, all SDK components were automatically wrapped inside a UI5 AbsoluteLayout control from the sap.ui.commons library.  With the introduction of the new “M” mode, given that the commons library is incompatible with the sap.m library, which wrapper will SDK components be contained in for “M” mode?

        2)  As per your example above, the “define” method is used to specify a requireJS module.  In the case where the handlerType is “ui5”, is it appropriate to use the “sap.ui.define” method instead?

        Mustafa.

        (0) 
        1. Reiner Hille-Doering Post author

          Hi Mustafa,

          1. Indeed. In 1.6 we have our own copy of AbsoluteLayout that is compatible with both, commons and m-mode.Thus there is no change and each component is still contained in a PositionContainer.
          2. No – require.js and sap.ui5 require must not be confused, but can be mixed. Design Studio SDK files are always loaded with require.js and thus are  require.js modules – so you must use define(…). You can however inside of your define call $.sap.require to load a sapui5 control.
          (0) 
  2. Mike Howles

    Wow – Thanks so much, Reiner for the detailed write-up.  It feels like Christmas came early.  I’m really happy with all of these additions, and am really happy that maybe I no longer have to type JSON.stringify ever again!  🙂

    (0) 
  3. Mike Howles

    Hey Reiner,

    Quick question regarding your section on calling ZTL functions via the APS…  Your specific example actually is a good one that frames up my specific questions:

    The statement:

    this.getDataSource()

    Is this only based on the older data binding technique of using databound=true?  Is there a way to achieve similar if you are using the new binding techniques?  If so, how would I differentiate on multiple potential data sources as a result?

    Thanks again for this blog, very helpful!

    (0) 
    1. Reiner Hille-Doering Post author

      Hi Mike,

      you are right, but unfortunately there is no easy way to “navigate” to the data source of property binding without have script access to property binding component itself.

      A nice syntax following the sketched idea in the response to Mustafa would be

      var binding = this.getBinding(“PROP1”);

      if (binding) {

      var ds = binding.getDatatSource();

      }

      Problem here is that there are multiple different possible property binding component types, where not each would necessarily have a “getDataSource” function.

      I still need to come to a good concept here…

      Reiner.

      (0) 
      1. Mike Howles

        Thanks, Reiner.

        A follow-up question.  Is it possible to give the new Object/Array properties a default value in contribution.xml?  If so, what is the correct notation?  JSON?  XML?  I cannot seem to get either to work.

        (0) 
      2. Mike Howles

        Hey Reiner,

        Just to let you know, I’m still very interested in performing either programmatic bindings via APS/BIAL.  I’ve got a multiple datasource use case that would benefit from this.  We also briefly touched on this in 1.5 What’s New discussion comment.

        I was able to partially solve this by trying to reproduce how the Geo Map supports multiple data sources in it for different layers by using promising (and undocumented/unsupported) calls to ds_getDataSourceAliases and ds_openDataSelectionDialog to create tuple selections and I thought I was making good progress however it looked like ds_getDataJSON doesn’t work for SDK components (it always returns “{}”, seems the Java is checking if APS control/page is an instance of IZenUiQueryReferenceHelper).  🙁   So I’ve given up the APS route thru ds_calls.

        I was then going to try to poke around in Rhino with callZTLFunction but it looks like there’s nothing I can grab there either to inspect data sources like ds_getDataJSON does for the Geo Map.

        I then tried creating just like 5 bindable properties of ResultSet type in contribution.xml but this means I cannot add to APS since APS does not support binding.  I’d like either a binding dialog to be available, or a data selection property dialog (available in old databound=true pattern) to work, but with multiple data sources, etc.

        (0) 
        1. Reiner Hille-Doering Post author

          Hi Mike,

          the functions you mentioned are indeed undocumented and will be removed as soon as possible. I understand that there is a need to implemented components like our Map that has a unlimited set of data sources. But we really need a clean concept for this.

          Thanks,

          Reiner.

          (0) 
          1. Mike Howles

            Thanks, Reiner – Understood about using a better concept.  As usual, I like to use ‘whatever will work’ until 🙂

            On this part however:

            I then tried creating just like 5 bindable properties of ResultSet type in contribution.xml

            This ended up not working either.  I cannot get bindable ResultSet properties to work at all.  It is always passed just a null value at both design time and runtime.  It would seem that I have no means at all to have more than one data source for properties of type ResulSet/ResultCellSet/etc.

            Could you share if this is a limitation or a bug?  I’ve not gone back to 1.5 to try there, as I invested a lot of time into using new Require in 1.6.

            (0) 
  4. David Dowling

    Just one thing to note: For SDK component update without restart on the BI Platform it should pick up new SDKs and updated SDKs without a restart. If you delete a SDK from the BI Platform a restart would be required for that change to take.

    (0) 
  5. Mike Howles

    Hi Reiner,

    There was the wish from some famous Design Studio SDK coder to be able to use the same trick also from a Additional Properties Sheet – and voilá, here you are: You can use the exact same API from an APS JavaScript. Internally is works differently, but the ZTL script is executed in the normal runtime – also in Design Mode. You can however only read data with such a script. Modifying operations, e.g. setFilter are not allowed to be called from the APS and will bring up an error message in the Designer’s error log.

    Examples for using callZTLFunction, both from component and APS are contained in the new UI5List sample.

    I have used this within APS in an upcoming component to great effect, so thank you for the callZtlFunction for the APS.  It’s solved half of my puzzle as it comes to inspection of component/datasource state and properties.  Regarding the stated ‘only read data’ limitation, is this to infer also a read-only limitation for component properties, too?

    I (of course) have another request, or seek some clarity on something.  If I were to wish to update a different component’s property via callZTLFunction, is this something that is at all possible?  Despite all my efforts, I cannot get other component property state changes to be updated to the DS Designtime (even though the canvas does accept them, their property state remains unchanged.)  This doesn’t appear to be the case at runtime, and works as intended.

    (0) 
    1. Reiner Hille-Doering Post author

      Hi Mike,

      Design time and runtime state are two different things. In fact in the designer there is a runtime that renders your preview and that is also used for callZTLFunction. But it’s state is never written to BIApp file. Designer’s state (which later goes into BIApp file) is kept somewhere else, thus the only possibility to change it is via the firePropertiesChanged() of the selected component. Thus indeed consistent property changes across multiple components is not possible. There is also no such feature for built-in components.

      Regards,

      Reiner.

      (0) 
      1. Mike Howles

        Thanks, Reiner.  I believe I understand, and I assume these are safeguards put in place for good reasons, as well.  We wouldn’t want components to be manipulating the BIAPP file without the designer knowingly making this changes, after all…

        Even at that, the read-only nature of callZTLFunction still is proving useful for a more limited use case then.  Thanks again!

        (0) 
  6. Martin Pankraz

    Hi Reiner,

    I was just trying to upgrade one of my custom SDK datasource components (extends SdkDataBuffer) to use the new property type “Array” on my contribution.xml file. It appears that is not working. I get an indexOutOfBoundsException. Is that not supported for sdk datasources? I couldn’t find any limitations on the developer guide though. Please be aware that the property type Object also throws an exception.

    It would be nice if you could clarify.

    Kind regards

    Martin

    (0) 
    1. Reiner Hille-Doering Post author

      Hi Martin,

      yes, they are supposed to work, but I didn’t try it.

      Could you post the callstack and perhaps also the code of your SDK datasource?

      Thanks,

      Reiner.

      (0) 
      1. Martin Pankraz

        Hi Reiner,

        Thanks for checking. Here comes the callstack:

        !ENTRY org.eclipse.e4.ui.workbench 4 2 2016-03-23 12:44:32.182

        !MESSAGE Problems occurred when invoking code from plug-in: “org.eclipse.e4.ui.workbench”.

        !STACK 0

        java.lang.ArrayIndexOutOfBoundsException: 0

          at com.sap.ip.bi.zen.util.PropertyHelper.isTopLevelNonComplexSdkArray(PropertyHelper.java:261)

          at com.sap.ip.bi.zen.ui.propertysheet.PropertyHelperUI.handleSpecialParameterValues(PropertyHelperUI.java:709)

          at com.sap.ip.bi.zen.ui.propertysheet.PropertyHelperUI.getParameterValueForPropertySheet(PropertyHelperUI.java:401)

          at com.sap.ip.bi.zen.ui.internal.propertysheet.LeafParameterItemPropertyDescriptor.getPropertyValue(LeafParameterItemPropertyDescriptor.java:48)

          at org.eclipse.emf.edit.ui.provider.PropertySource.getPropertyValue(PropertySource.java:86)

          at org.eclipse.emf.transaction.ui.provider.TransactionalPropertySource$3.run(TransactionalPropertySource.java:137)

          at org.eclipse.emf.transaction.impl.TransactionalEditingDomainImpl.runExclusive(TransactionalEditingDomainImpl.java:328)

          at org.eclipse.emf.transaction.util.TransactionUtil.runExclusive(TransactionUtil.java:328)

          at org.eclipse.emf.transaction.ui.provider.TransactionalPropertySource.run(TransactionalPropertySource.java:78)

          at org.eclipse.emf.transaction.ui.provider.TransactionalPropertySource.getPropertyValue(TransactionalPropertySource.java:135)

          at org.eclipse.ui.views.properties.PropertySheetEntry.refreshValues(PropertySheetEntry.java:590)

          at org.eclipse.ui.views.properties.PropertySheetEntry.refreshChildEntries(PropertySheetEntry.java:547)

          at org.eclipse.ui.views.properties.PropertySheetEntry.setValues(PropertySheetEntry.java:736)

          at org.eclipse.ui.views.properties.PropertySheetViewer.setInput(PropertySheetViewer.java:986)

          at org.eclipse.ui.views.properties.PropertySheetPage.selectionChanged(PropertySheetPage.java:518)

          at org.eclipse.emf.edit.ui.view.ExtendedPropertySheetPage.selectionChanged(ExtendedPropertySheetPage.java:326)

          at org.eclipse.emf.transaction.ui.view.ExtendedPropertySheetPage.access$1(ExtendedPropertySheetPage.java:1)

          at org.eclipse.emf.transaction.ui.view.ExtendedPropertySheetPage$2.run(ExtendedPropertySheetPage.java:104)

          at org.eclipse.emf.transaction.impl.TransactionalEditingDomainImpl.runExclusive(TransactionalEditingDomainImpl.java:328)

          at org.eclipse.emf.transaction.ui.view.ExtendedPropertySheetPage.selectionChanged(ExtendedPropertySheetPage.java:102)

          at com.sap.ip.bi.zen.ui.internal.propertysheet.ApplicationPropertySheetPage.selectionChanged(ApplicationPropertySheetPage.java:167)

          at org.eclipse.ui.views.properties.PropertySheet.selectionChanged(PropertySheet.java:335)

          at org.eclipse.ui.internal.e4.compatibility.SelectionService.notifyListeners(SelectionService.java:237)

          at org.eclipse.ui.internal.e4.compatibility.SelectionService.handlePostSelectionChanged(SelectionService.java:122)

          at org.eclipse.ui.internal.e4.compatibility.SelectionService.access$1(SelectionService.java:112)

          at org.eclipse.ui.internal.e4.compatibility.SelectionService$3.selectionChanged(SelectionService.java:80)

          at org.eclipse.e4.ui.internal.workbench.SelectionAggregator$4.run(SelectionAggregator.java:167)

          at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)

          at org.eclipse.e4.ui.internal.workbench.SelectionAggregator.notifyPostListeners(SelectionAggregator.java:164)

          at org.eclipse.e4.ui.internal.workbench.SelectionAggregator.access$8(SelectionAggregator.java:161)

          at org.eclipse.e4.ui.internal.workbench.SelectionAggregator$8$1.run(SelectionAggregator.java:273)

          at org.eclipse.e4.core.contexts.RunAndTrack.runExternalCode(RunAndTrack.java:56)

          at org.eclipse.e4.ui.internal.workbench.SelectionAggregator$8.changed(SelectionAggregator.java:270)

          at org.eclipse.e4.core.internal.contexts.TrackableComputationExt.update(TrackableComputationExt.java:114)

          at org.eclipse.e4.core.internal.contexts.EclipseContext.processScheduled(EclipseContext.java:341)

          at org.eclipse.e4.core.internal.contexts.EclipseContext.set(EclipseContext.java:356)

          at org.eclipse.e4.ui.internal.workbench.SelectionServiceImpl.setPostSelection(SelectionServiceImpl.java:36)

          at org.eclipse.ui.internal.e4.compatibility.CompatibilityPart$3.selectionChanged(CompatibilityPart.java:132)

          at org.eclipse.ui.part.PageBookView$SelectionManager$1.run(PageBookView.java:263)

          at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)

          at org.eclipse.ui.part.PageBookView$SelectionManager.selectionChanged(PageBookView.java:260)

          at org.eclipse.ui.part.PageBookView$SelectionProvider.postSelectionChanged(PageBookView.java:340)

          at org.eclipse.ui.part.PageBookView.postSelectionChanged(PageBookView.java:949)

          at org.eclipse.ui.part.PageBookView.access$3(PageBookView.java:944)

          at org.eclipse.ui.part.PageBookView$3.selectionChanged(PageBookView.java:179)

          at org.eclipse.ui.views.contentoutline.ContentOutlinePage$1.run(ContentOutlinePage.java:112)

          at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)

          at org.eclipse.ui.views.contentoutline.ContentOutlinePage.fireSelectionChanged(ContentOutlinePage.java:109)

          at org.eclipse.ui.views.contentoutline.ContentOutlinePage.selectionChanged(ContentOutlinePage.java:158)

          at org.eclipse.jface.viewers.StructuredViewer$3.run(StructuredViewer.java:877)

          at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)

          at org.eclipse.ui.internal.JFaceUtil$1.run(JFaceUtil.java:50)

          at org.eclipse.jface.util.SafeRunnable.run(SafeRunnable.java:173)

          at org.eclipse.jface.viewers.StructuredViewer.firePostSelectionChanged(StructuredViewer.java:874)

          at org.eclipse.jface.viewers.StructuredViewer.handlePostSelect(StructuredViewer.java:1243)

          at org.eclipse.jface.viewers.StructuredViewer$5.widgetSelected(StructuredViewer.java:1269)

          at org.eclipse.jface.util.OpenStrategy.firePostSelectionEvent(OpenStrategy.java:265)

          at org.eclipse.jface.util.OpenStrategy.access$5(OpenStrategy.java:259)

          at org.eclipse.jface.util.OpenStrategy$1$2.run(OpenStrategy.java:440)

          at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:35)

          at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:135)

          at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:4155)

          at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3772)

          at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$4.run(PartRenderingEngine.java:1127)

          at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:337)

          at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1018)

          at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:156)

          at org.eclipse.ui.internal.Workbench$5.run(Workbench.java:654)

          at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:337)

          at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:598)

          at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:150)

          at com.sap.ip.bi.zen.ui.internal.application.ZenApplication.start(ZenApplication.java:36)

          at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196)

          at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:134)

          at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104)

          at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:380)

          at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:235)

          at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

          at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

          at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

          at java.lang.reflect.Method.invoke(Unknown Source)

          at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:669)

          at org.eclipse.equinox.launcher.Main.basicRun(Main.java:608)

          at org.eclipse.equinox.launcher.Main.run(Main.java:1515)

          at org.eclipse.equinox.launcher.Main.main(Main.java:1488)

        !ENTRY org.eclipse.e4.ui.workbench 4 0 2016-03-23 12:44:32.186

        !MESSAGE

        !STACK 0

        java.lang.ArrayIndexOutOfBoundsException: 0

          at com.sap.ip.bi.zen.util.PropertyHelper.isTopLevelNonComplexSdkArray(PropertyHelper.java:261)

          at com.sap.ip.bi.zen.ui.propertysheet.PropertyHelperUI.handleSpecialParameterValues(PropertyHelperUI.java:709)

          at com.sap.ip.bi.zen.ui.propertysheet.PropertyHelperUI.getParameterValueForPropertySheet(PropertyHelperUI.java:401)

          at com.sap.ip.bi.zen.ui.internal.propertysheet.LeafParameterItemPropertyDescriptor.getPropertyValue(LeafParameterItemPropertyDescriptor.java:48)

          at org.eclipse.emf.edit.ui.provider.PropertySource.getPropertyValue(PropertySource.java:86)

          at org.eclipse.emf.transaction.ui.provider.TransactionalPropertySource$3.run(TransactionalPropertySource.java:137)

          at org.eclipse.emf.transaction.impl.TransactionalEditingDomainImpl.runExclusive(TransactionalEditingDomainImpl.java:328)

          at org.eclipse.emf.transaction.util.TransactionUtil.runExclusive(TransactionUtil.java:328)

          at org.eclipse.emf.transaction.ui.provider.TransactionalPropertySource.run(TransactionalPropertySource.java:78)

          at org.eclipse.emf.transaction.ui.provider.TransactionalPropertySource.getPropertyValue(TransactionalPropertySource.java:135)

          at org.eclipse.ui.views.properties.PropertySheetEntry.refreshValues(PropertySheetEntry.java:590)

          at org.eclipse.ui.views.properties.PropertySheetEntry.refreshChildEntries(PropertySheetEntry.java:547)

          at org.eclipse.ui.views.properties.PropertySheetEntry.setValues(PropertySheetEntry.java:736)

          at org.eclipse.ui.views.properties.PropertySheetViewer.setInput(PropertySheetViewer.java:986)

          at org.eclipse.ui.views.properties.PropertySheetPage.selectionChanged(PropertySheetPage.java:518)

          at org.eclipse.emf.edit.ui.view.ExtendedPropertySheetPage.selectionChanged(ExtendedPropertySheetPage.java:326)

          at org.eclipse.emf.transaction.ui.view.ExtendedPropertySheetPage.access$1(ExtendedPropertySheetPage.java:1)

          at org.eclipse.emf.transaction.ui.view.ExtendedPropertySheetPage$2.run(ExtendedPropertySheetPage.java:104)

          at org.eclipse.emf.transaction.impl.TransactionalEditingDomainImpl.runExclusive(TransactionalEditingDomainImpl.java:328)

          at org.eclipse.emf.transaction.ui.view.ExtendedPropertySheetPage.selectionChanged(ExtendedPropertySheetPage.java:102)

          at com.sap.ip.bi.zen.ui.internal.propertysheet.ApplicationPropertySheetPage.selectionChanged(ApplicationPropertySheetPage.java:167)

          at org.eclipse.ui.views.properties.PropertySheet.selectionChanged(PropertySheet.java:335)

          at org.eclipse.ui.internal.e4.compatibility.SelectionService.notifyListeners(SelectionService.java:237)

          at org.eclipse.ui.internal.e4.compatibility.SelectionService.handlePostSelectionChanged(SelectionService.java:122)

          at org.eclipse.ui.internal.e4.compatibility.SelectionService.access$1(SelectionService.java:112)

          at org.eclipse.ui.internal.e4.compatibility.SelectionService$3.selectionChanged(SelectionService.java:80)

          at org.eclipse.e4.ui.internal.workbench.SelectionAggregator$4.run(SelectionAggregator.java:167)

          at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)

          at org.eclipse.e4.ui.internal.workbench.SelectionAggregator.notifyPostListeners(SelectionAggregator.java:164)

          at org.eclipse.e4.ui.internal.workbench.SelectionAggregator.access$8(SelectionAggregator.java:161)

          at org.eclipse.e4.ui.internal.workbench.SelectionAggregator$8$1.run(SelectionAggregator.java:273)

          at org.eclipse.e4.core.contexts.RunAndTrack.runExternalCode(RunAndTrack.java:56)

          at org.eclipse.e4.ui.internal.workbench.SelectionAggregator$8.changed(SelectionAggregator.java:270)

          at org.eclipse.e4.core.internal.contexts.TrackableComputationExt.update(TrackableComputationExt.java:114)

          at org.eclipse.e4.core.internal.contexts.EclipseContext.processScheduled(EclipseContext.java:341)

          at org.eclipse.e4.core.internal.contexts.EclipseContext.set(EclipseContext.java:356)

          at org.eclipse.e4.ui.internal.workbench.SelectionServiceImpl.setPostSelection(SelectionServiceImpl.java:36)

          at org.eclipse.ui.internal.e4.compatibility.CompatibilityPart$3.selectionChanged(CompatibilityPart.java:132)

          at org.eclipse.ui.part.PageBookView$SelectionManager$1.run(PageBookView.java:263)

          at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)

          at org.eclipse.ui.part.PageBookView$SelectionManager.selectionChanged(PageBookView.java:260)

          at org.eclipse.ui.part.PageBookView$SelectionProvider.postSelectionChanged(PageBookView.java:340)

          at org.eclipse.ui.part.PageBookView.postSelectionChanged(PageBookView.java:949)

          at org.eclipse.ui.part.PageBookView.access$3(PageBookView.java:944)

          at org.eclipse.ui.part.PageBookView$3.selectionChanged(PageBookView.java:179)

          at org.eclipse.ui.views.contentoutline.ContentOutlinePage$1.run(ContentOutlinePage.java:112)

          at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)

          at org.eclipse.ui.views.contentoutline.ContentOutlinePage.fireSelectionChanged(ContentOutlinePage.java:109)

          at org.eclipse.ui.views.contentoutline.ContentOutlinePage.selectionChanged(ContentOutlinePage.java:158)

          at org.eclipse.jface.viewers.StructuredViewer$3.run(StructuredViewer.java:877)

          at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)

          at org.eclipse.ui.internal.JFaceUtil$1.run(JFaceUtil.java:50)

          at org.eclipse.jface.util.SafeRunnable.run(SafeRunnable.java:173)

          at org.eclipse.jface.viewers.StructuredViewer.firePostSelectionChanged(StructuredViewer.java:874)

          at org.eclipse.jface.viewers.StructuredViewer.handlePostSelect(StructuredViewer.java:1243)

          at org.eclipse.jface.viewers.StructuredViewer$5.widgetSelected(StructuredViewer.java:1269)

          at org.eclipse.jface.util.OpenStrategy.firePostSelectionEvent(OpenStrategy.java:265)

          at org.eclipse.jface.util.OpenStrategy.access$5(OpenStrategy.java:259)

          at org.eclipse.jface.util.OpenStrategy$1$2.run(OpenStrategy.java:440)

          at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:35)

          at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:135)

          at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:4155)

          at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3772)

          at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$4.run(PartRenderingEngine.java:1127)

          at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:337)

          at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1018)

          at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:156)

          at org.eclipse.ui.internal.Workbench$5.run(Workbench.java:654)

          at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:337)

          at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:598)

          at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:150)

          at com.sap.ip.bi.zen.ui.internal.application.ZenApplication.start(ZenApplication.java:36)

          at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196)

          at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:134)

          at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104)

          at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:380)

          at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:235)

          at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

          at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

          at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

          at java.lang.reflect.Method.invoke(Unknown Source)

          at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:669)

          at org.eclipse.equinox.launcher.Main.basicRun(Main.java:608)

          at org.eclipse.equinox.launcher.Main.run(Main.java:1515)

          at org.eclipse.equinox.launcher.Main.main(Main.java:1488)

        And now the code of the datasource:

        sap.designstudio.sdk.DataBuffer.subclass(“com.convista.ds.DSTest”, function() {

          var that = this;

          var saveColumnHeaders = null;

          this.init = function() {

          this.defineDimensions([{

          key: “cols”,

          text: “Columns”,

          “axis”: “COLUMNS”,

          “axis_index”: 0

          }, {

          key: “rows”,

          text: “Rows”,

          “axis”: “ROWS”,

          “axis_index”: 0

          }]);

          };

          //make sure all hooks are present

          this.beforeUpdate = function(){};

          this.afterUpdate = function() {

          if(saveColumnHeaders){

          alert(saveColumnHeaders);

          }

          };

          //make sure all hooks are present

          this.componentDeleted = function(){};

          this.columnHeader = function(value) {

          if (value === undefined) {

          return saveColumnHeaders;

          } else {

          saveColumnHeaders = value;

          return this;

          }

          };

        });// End of SDK

        And finally the contribution.xml

        <?xml version=”1.0″ encoding=”UTF-8″?>

        <sdkExtension

          xmlns=”http://www.sap.com/bi/zen/sdk

          id=”com.convista.ds”

          title=”Design Studio SDK Extension DS Test”

          version=”1.0″

          vendor=”ConVista, Pankraz”>

          <component

          id=”DSTest”

          title=”WebRFC DS”

          tooltip=””

          icon=”res/icon.jpg”

          handlerType=”datasource”>

          <jsInclude>res/js/component.js</jsInclude>

          <property

          id=”columnHeader”

          title=”Column Headers”

          type=”Array”

          visible=”true”/>

          <property id=”text” type=”String” title=”Column Entries”></property>

          <initialization>

          </initialization>

          </component>

        </sdkExtension>

        I reduced everything to a minimum.

        Kind regards

        Martin

        (0) 

Leave a Reply