How to Change Banner in LaunchPad

This is a quick and dirty solution to change the banner in LaunchPad.

banner-launchpad

Do not forget to backup the files before any change. See SAP note 1659690 for a better solution.

  • Stop Tomcat.
  • Replace the file C:\Program Files (x86)\SAP BusinessObjects\tomcat\webapps\BOE\WEB-INF\eclipse\plugins\webpath.InfoView\web\images\SAP_logo_reg.png with desired logo (preferred dimensions: 63 x 31 px).
  • Delete all folders under C:\Program Files (x86)\SAP BusinessObjects\tomcat\work\Catalina\localhost
  • Start Tomcat.
  • The users will need to delete temporary files in browser to see the change. In IE, Tools > Internet Options > General, Delete under Browsing history:

ie-browsing-history

Getting Started with BusinessObjects Semantic Layer SDK

This post describes how to get started with BO Semantic Layer SDK in Eclipse. It explains the steps to setup a project and provides an example of the code that imports, modifies and exports a UNX universe. You will also find instructions how to run JAR from command line.

BO Client Tools and SL SDK

You need to have SAP BusinessObjects BI platform 4.x Client Tools and it should include Semantic Layer SDK.

The Semantic Layer SDK is not included in default installation. You can check if you have it and enable it in Control Panel > Programs > Programs and Features > SAP BusinessObjects BI platform 4.x Client Tools > Uninstall/change > Modify.

Make sure that you have "Semantic Layer Java SDK"

unxdemo-07

This post assumes that the client tools are installed in the default location, namely in

C:\Program Files (x86)\SAP BusinessObjects

If your setup is different, update the paths accordingly.

Setup New Project with SL SDK

Create a new Project in Eclipse.

Right click on the project and select Build Path > Add External Archives.

unxdemo-01

Add Semantic layer SDK library sl_sdk.jar. It is located in:

C:\Program Files (x86)\SAP BusinessObjects\SAP BusinessObjects Enterprise XI 4.0\SL SDK\java

unxdemo-02

JRE

Make sure that the project is using 32 bit Java Runtime Environment. The best is to use Java from the BusinessObjects folder.

Right click on JRE System Library and select Properties.

unxdemo-10

Click Installed JREs and add new JRE by clicking on Add > Standard VM, and then providing the following path:

C:\Program Files (x86)\SAP BusinessObjects\SAP BusinessObjects Enterprise XI 4.0\win32_x86\jre

unxdemo-09

Select the created JRE:

unxdemo-08

The Program

Add class Program to the project and paste the following code:

import com.crystaldecisions.sdk.exception.SDKException;
import com.crystaldecisions.sdk.framework.CrystalEnterprise;
import com.crystaldecisions.sdk.framework.IEnterpriseSession;
import com.crystaldecisions.sdk.framework.ISessionMgr;
import com.sap.sl.sdk.authoring.cms.CmsResourceService;
import com.sap.sl.sdk.authoring.businesslayer.*;
import com.sap.sl.sdk.authoring.datafoundation.*;
import com.sap.sl.sdk.authoring.local.LocalResourceService;
import com.sap.sl.sdk.framework.SlContext;
import com.sap.sl.sdk.framework.cms.CmsSessionService;

public class Program {

  public static void main(String[] args) throws SDKException {
    String username = "Administrator";
    String password = "";
    String server = "localhost";
    String auth = "secEnterprise";
    String cmspath = "/Universes/webi universes";
    String unxname = "eFashion.unx";
    String tempFolder = ".";

    SlContext context = SlContext.create();
    IEnterpriseSession enterpriseSession = null;
    try {
      System.out.println("Connecting");
      ISessionMgr sessionMgr = CrystalEnterprise.getSessionMgr();
      enterpriseSession = sessionMgr.logon(username, password, server, auth);
      CmsSessionService cmsSessionService =
        context.getService(CmsSessionService.class);
      cmsSessionService.setSession(enterpriseSession);
      CmsResourceService cmsService = 
        context.getService(CmsResourceService.class);
      LocalResourceService localResourceService =
        context.getService(LocalResourceService.class);
      BusinessLayerFactory businessLayerFactory = 
        context.getService(BusinessLayerFactory.class);
      DataFoundationFactory dataFoundationFactory =
        context.getService(DataFoundationFactory.class);
             
      System.out.println("Importing universe"); 
      String blxPath = 
        cmsService.retrieveUniverse(cmspath + "/" + unxname, tempFolder, true);
      
      System.out.println("Loading universe");
      RelationalBusinessLayer businessLayer =
        (RelationalBusinessLayer) localResourceService.load(blxPath);
      String dfxPath = businessLayer.getDataFoundationPath();
      DataFoundation dataFoundation =
        (DataFoundation) localResourceService.load(dfxPath);

      System.out.println("Modifying data foundation");
      String sql = "select count(1) as Cnt from Shop_facts";
      dataFoundationFactory.createDerivedTable("New_table", sql, dataFoundation);
      localResourceService.save(dataFoundation, dfxPath, true);

      System.out.println("Reloading data foundation");
      businessLayer =
        (RelationalBusinessLayer) localResourceService.load(blxPath);
      
      System.out.println("Modifying business layer");
      RootFolder rootFolder = businessLayer.getRootFolder();
      Measure measure = 
        businessLayerFactory.createBlItem(Measure.class, "New Measure", rootFolder);
      RelationalBinding binding = (RelationalBinding)measure.getBinding();
      binding.setSelect("New_table.Cnt");
      localResourceService.save(businessLayer, blxPath, true);

      System.out.println("Exporting universe");
      cmsService.publish(blxPath, cmspath, true);
    } finally {
      if (context != null) {
        context.close();
      }
      if (enterpriseSession != null) {
        enterpriseSession.logoff();
      }
      System.out.println("Finished");
    }
  }
}

Run Configuration

Run the program. It will fail but a Run Configuration will be created.

Open the created run configuration in Run > Run Configurations

Add the following VM arguments

-Dbusinessobjects.connectivity.directory="C:\Program Files (x86)\SAP BusinessObjects\SAP BusinessObjects Enterprise XI 4.0\dataAccess\connectionServer"

unxdemo-04

Add PATH variable with the value:

C:\Program Files (x86)\SAP BusinessObjects\SAP BusinessObjects Enterprise XI 4.0\win32_x86

unxdemo-05

Running the Code

The code uses eFashion.unx which should be located in webi universes. So you need to convert eFashion universe to eFashion.unx in Information Design Tool.

unxdemo-05

Now we are ready to run the program.

Click the button Run. The tool should execute without error and add table New_table and a measure New measure:

unxdemo-06

Running the Code from Command Line

Export the program to Executable JAR file (e.g. unxdemo.jar) from File > Export.

Create a batch file with the following script:

SET BO=C:\Program Files (x86)\SAP BusinessObjects\SAP BusinessObjects Enterprise XI 4.0
SET PATH=%BO%\win32_x86
SET JAVA=%BO%\win32_x86\jre
SET CD=%BO%\dataAccess\connectionServer
SET CP=unxdemo.jar;%BO%\SL SDK\java\sl_sdk.jar;%BO%\java\lib\*
"%JAVA%\bin\java.exe" -Dbusinessobjects.connectivity.directory="%CD%" -cp "%CP%" Program
PAUSE

Run the batch:

unxdemo-11

Refresh button text in Web Intelligence reading mode

The Refresh button in 4.1 looks quite anonymous. It is possible to get the "Refresh" text back.

refreshbutton-0

You can edit file

C:\Program Files (x86)\SAP BusinessObjects\tomcat\webapps\BOE\WEB-INF\eclipse\plugins\webpath.AnalyticalReporting\web\webiDHTML\viewer\language\en\scripts\UIDefinition.js

and change the line

,{actionId:"refreshDPMenu", text:"}

to

,{actionId:"refreshDPMenu"}

The button has actually text but it is overriden with empty string.

You will also need to update this file in tomcat work folder.

C:\BO\SAPBusinessObjects\tomcat\work\Catalina\localhost\
BOE\eclipse\plugins\webpath.AnalyticalReporting\web\webiDHTML\viewer\language\en\scripts

(The users might need to clear temporary files in order to see the change)

refreshbutton-1

PS. This is probably not the right way to do this but it seems to be the most straightforward.

Unx Documenter

There is a free tool for extracting UNX universe metadata available on http://biclever.com/software/unx-universe-documenter/. It also allows comparing universes.

unxdoc-1

unxdoc-2

unxdoc-3

Calculation contexts issue with Sum, Where and If

Make a report based on eFashion universe with [Year], [City], and [Sales revenue].

Untitled3

Now we will try to calculate total Sales revenue for Austin in 2004 but in two steps.

Where and If

Define variables as:

[Sales Austin] =[Sales revenue] Where([City]="Austin")

[Sales Austin 2004] =Sum(If [Year]="2004" Then [Sales Austin])

The expected value for [Sales Austin 2004] is 561123.

But

1) The value calculated in the Sum line for Sales Austin for some reason is 2805617 (which is 5 x Sales Austin 2004 = 5 x 561123).

2) The value calculated in the table on the right is 13498366 (Which is the total of all values in the column Sales Austin i.e. 5 x (561123 + 1003071 + 1135479) = 13498366).

Untitled

This was a result for BO3.1 SP7. In BO4.1 SP7, the result is slightly different, we will get #MULTIVALUE in the right table.

We can explicitly add [Year] to calculation context.

[Sales Austin 2004] =Sum((If [Year]="2004" Then [Sales Austin])ForEach([Year]))

This will fix the value in the right table, but not in the Sum line.

If we explicitly add [Year] and remove [City], then the result will become correct.

[Sales Austin 2004] =Sum((If [Year]="2004" Then [Sales Austin])ForEach([Year])ForAll([City]))

or

[Sales Austin 2004] =Sum((If [Year]="2004" Then [Sales Austin])In([Year]))

So the calculation context in the variable [Sales Austin 2004] is derived incorrectly. Since we have [City] in the expression, it should have been included by BO. The variable [Sales Austin] has Where([City]="Austin"), which removes [City] from context, so [City] should have been excluded. But for some reason, we have to specify this manually.

If

[Sales Austin] =If [City]="Austin" Then [Sales revenue]

[Sales Austin 2004] =Sum(If [Year]="2004" Then [Sales Austin])

This works fine unless we add sum to [Sales Austin]:

[Sales Austin] =Sum(If [City]="Austin" Then [Sales revenue])

[Sales Austin 2004] =Sum(If [Year]="2004" Then [Sales Austin])

In this case, the right table will show wrong value (13498366 in BO3 and #MULTIVALUE in BO4). We need to add [Year] to calculation context in [Sales Austin 2004].

Where

[Sales Austin] =[Sales revenue] Where ([City]="Austin")

[Sales Austin 2004] =[Sales Austin] Where ([Year]="2004")

The result will be correct if we use variable value [Sales Austin 2004] in Sum line. If we add Sum, the result in Sum line will become wrong 2805617.

You might also find it interesting that the following two formulas return different result

=Sum([Sales revenue] Where ([Year]="2004" And [City]="Austin"))

=Sum([Sales revenue] Where ([Year]="2004")Where([City]="Austin"))

The first is correct but the second will return 1683370. (I am not sure what this number is.)

BusinessObjects BI Tomcat logs

BO BI 4 Tomcat generates some logs in the root folder such as SBOPWebapp_BIlaunchpad, SBOPWebapp_CMC, SBOPWebapp_Mobi_Server:

BO_logs_folder

To move the logs to another folder, add Tomcat Java option

-Duser.home=C:\Program Files (x86)\SAP BusinessObjects\BO Logs

BO Logs

How to disable version check in Upgrade Management Tool

To avoid error

"Version check failed. The source system or source BIAR file must be of an older version. The destination system system must be of the current version. (UMT 20012)"

you can use (at own risk) the option

-internal_use_only_noversioncheck

internal_use_only_noversioncheck

Web Intelligence RESTful Web Services SDK with Java

In this post you will find an example of how to use Web Intelligence RESTful Web Services SDK with Java. The code displays names of the variables for each web intelligence document.

To run the code, you will need json library that can be found for instance on http://mvnrepository.com/artifact/org.json/json/20160212

Example (Program.java)

import org.json.JSONArray;
import org.json.JSONObject;

public class Program {
  public static void main(String[] args) throws Exception {
    // Establish connection
    Bo4Connection connection = new Bo4Connection("http://ANALYTIX:6405/biprws");
    connection.connect("Administrator", "1-Password", "secEnterprise");
    try {
      // Get list of documents
      String docs = connection.query("GET", "/documents", "application/json");
      JSONArray documents = new JSONObject(docs).getJSONObject("documents").getJSONArray("document");
      for (int i = 0; i < documents.length(); i++) {
        JSONObject document = documents.getJSONObject(i);
        int id = document.getInt("id");
	// Get information about the document
        String doc = connection.query("GET", "/documents/" + id, "application/json");
        JSONObject info = new JSONObject(doc).getJSONObject("document");
        String name = info.getString("name");
        String path = info.getString("path");
        System.out.println(path + "/" + name);
	// Get variables of the document
        String var = connection.query("GET", "/documents/" + id + "/variables", "application/json");
        JSONArray variables = new JSONObject(var).getJSONObject("variables").getJSONArray("variable");
        for (int j = 0; j < variables.length(); j++) {
	  // Print variable information
          JSONObject variable = variables.getJSONObject(j);
          String variableName = variable.getString("name");
          System.out.println(variableName);
        }
      }
    } finally {
      connection.disconnect();
    }
  }
}

API (Bo4Connection.java)

The helper API is very simple and contains a constructor and 3 functions: connect(username, password, auth), disconnect(), and query(method, link, format).

The constructor's parameter is the link to web services.

Bo4Connection connection = new Bo4Connection("http://ANALYTIX:6405/biprws");

The call to web services is performed with function query. For instance, to get list of documents we send a GET request to /documents, and we want to get result in json format.

connection.query("GET", "/documents", "application/json");

To get the list of all possible request, please refer to RESTful Web Services SDK Developer Guides on http://scn.sap.com/docs/DOC-27465

import java.io.*;
import java.net.*;
import org.json.JSONObject;

public class Bo4Connection {
  
  private String biprws;
  private String token;
  
  public Bo4Connection(String biprws) {
    this.biprws = biprws;
  }
  
  public String query(String method, String link, String format) throws Exception {
    return query(method, biprws + "/raylight/v1" + link, format, token, null);
  }
  
  public void connect(String username, String password, String auth) throws Exception {
    String link = biprws + "/logon/long/";    
    String method = "POST";
    String format = "application/json";
    String body = "<attrs xmlns=\"http://www.sap.com/rws/bip\">"
      + "<attr name=\"userName\" type=\"string\">" + username + "</attr>"
      + "<attr name=\"password\" type=\"string\">" + password + "</attr>"
      + "<attr name=\"auth\" type=\"string\">" + auth + "</attr>"
      + "</attrs>";
    JSONObject json = new JSONObject(query(method, link, format, null, body));
    token = json.getString("logonToken");
  }
  
  public void disconnect() throws Exception {
    String link = biprws + "/logoff/";    
    String method = "POST";
    String format = "application/json";
    query(method, link, format, null, null);
  }

  public static String query(String method, String link, String format, 
      String token, String content) throws Exception {
    HttpURLConnection conn = null;
    try {
      URL url = new URL(link);    
      conn = (HttpURLConnection) url.openConnection();
      conn.setRequestMethod(method);
      conn.setRequestProperty("Accept", format);
      if (token != null) {
        String logonToken = "\"" + token + "\"";
        conn.setRequestProperty("X-SAP-LogonToken", logonToken);
      }
      conn.setDoOutput(true);
      conn.setDoInput(true);
      if (content != null) {
        conn.setRequestProperty("Content-Type", 
            "application/xml; charset=utf-8");
        conn.setRequestProperty("Content-Length", 
            Integer.toString(content.length()));
        OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream());
        out.write(content, 0, content.length());
        out.flush();
      }
      conn.connect();
      if (conn.getResponseCode() != 200) {
        throw new Exception("HTTP Error Code: " + conn.getResponseCode() 
          + " " + conn.getResponseMessage());
      }
      BufferedReader br = new BufferedReader(
        new InputStreamReader(conn.getInputStream()));
      StringBuilder result = new StringBuilder(); 
      String output;
      while ((output = br.readLine()) != null) {
        result.append(output);
        result.append('\n');
      }
      br.close();
      return result.toString();
    } finally {
      if (conn != null) {
        conn.disconnect();
      }
    }
  }
}

Re-deploying BusinessObjects XI 3.1 web applications

These steps describe the procedure for re-deploying web applications for BO XI 3.1 with Tomcat 7. Re-deploying is required, for instance, if you uninstall a language pack from Business Objects.

  • Stop Tomcat
  • Backup Tomcat7 folder located in Business Objects folder
  • Remove all applications except ROOT, manager, host-manager, docs, examples (these are Tomcat applications) from Tomcat7\webapps
  • Remove all subfolders from Tomcat7\work\Catalina\localhost
  • Start cmd.exe as Administrator
  • Change directory to deployment located in Business Objects folder
  • Run wdeploy tomcat7 deployall
  • If the message is "BUILD SUCCESSFUL", the application are successfully deployed.
  • Start Tomcat and check web applications.

How to edit merged dimensions in BO BI 4.x

In BO XI 3.1, to edit a merged dimension, we could right click on a merged dimension, and select "Edit merged dimension" from pop up menu, this would open a dialog for editing merged dimensions. This has changed in BI 4.x, and it might be not obvious how to adjust merged dimensions.

Add dimensions to a merged dimension:

Select the merged dimension and objects you want to add, click right button, select “Add to Merge”.

merge1

Remove dimensions from a merged dimension:

Select dimensions that you want to remove from merged dimensions, click right button, and select "Remove from Merge"

merge2