Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
The aBPM framework is typically used in context of BPM for fast and versioned controlled task generation with SAPUI5/WebDynpro Java, but with this powerful framework it is also possible to create powerful standalone applications outside the context of BPM. The application can regularly deployed and hosted on the same application server where aBPM/BPM is running.

Inside this blog I want to show you how you can create very fast a standalone application with aBPM in example of a custom inbox, but other application content would also be possible.

Before we drive into more details here a list of some soft/hard advantages for this development approach:

  • fast UI/application generation like in regular aBPM scenarios (with MSOffice spreadsheets)

  • no frustrating SAPUI5 development/experimentals to build UIs or features

  • usage of the full Java Stack (inside callbacks, etc.) and no tinker with JavaScript/Browser Bugs

  • usage of the full aBPM feature toolkit (UI elements, helpvalues, BusinessCard etc.)

  • reuse of the same theming/custom theming that is used inside aBPM tasks

  • proven authorization schema to enable or disable action buttons based on roles/groups

  • proven I18N concept

  • no additional application server necessary (standalone aBPM applications running on the same PO system -> hint: maybe the system sizing must be increased/adapted)


There are definitely a lot of challenges to discuss (pros and cons) before this aBPM application development approach is used in your project and/or team. On the one hand you have a fast result on the other hand maybe you have some UI limitations (but here aBPM has also a custom UI extension concept so that you can use additional SAPUI5 controls besides the aBPM default controls). But before you decide, one massive fact cannot be ignored the outcome of a complex standalone application with a minimum invest of time.

The following image shows an example for a custom inbox scenario (with a custom theme):





Now lets start with the step-by-step roadmap for the aBPM standalone application generation:

  1. Creation of the DCs inside NWDS (except of the /prc DC, this DC is not relevant for a standalone application)

  2. Regular fill of the aBPM scenario spreadsheet (result <aBPMScenario>.xml + config.xml)

    • All aBPM UI elements, data types, etc. can be used

    • Only one difference exists to a "normal" aBPM scenario inside visibleOnCreate and editableOnCreate only an asterics must be used (inside of a standalone app doesn't exists multiple UI states like in the context of BPM tasks)

    • Only logical version V1 must be used (because normally the end user get access to the latest application version with the latest application features (in case you want/need multiple versions the approach of logical versions can also be used like in standard aBPM scenarios)



  3. Creation of the Generate.xml ant script (bpmDC entry points to the EJB DC, so that the <Sceario>Extract.xsd will be generated there)

  4. Run the ant script

  5. Fill the ejb DC inside onBeforeDisplay, handleUIEvent with the content of your application logic

    • inside of an custom inbox scenario some source code must be used to get/read BPM tasks/substitution rules etc.

    • in other standalone applications additional components like a JPA persistence/business layer and/or interface calls into other systems (e.g. via AEX or directly) are necessary (dependent on the application scope)



  6. Build the ear DC an deployment

  7. Run the application

  8. Check-In and activate the standalone scenario DCs


Example of the standalone application DC structure:



Example of the standalone application Generate.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project name="<Scenario>_Inbox" default="<Scenario>_Inbox" basedir="../">

<!-- <property file="C:/Workspaces/abpm_inbox.properties" /> -->
<property file="C:/NWDS_731_SP18/abpm_inbox.properties" />

<property name="lib.dir" value="${generator.target.path}" />

<taskdef name="ABPMGen"
classname="com.sap.consulting.abpm.generator.ant.GeneratorTask">
<classpath>
<fileset dir="${lib.dir}" includes="**/*.jar" />
</classpath>
</taskdef>

<target name="<Scenario>_Inbox" description="<Scenario>_Inbox">
<javac srcdir="src" target="1.6" compiler="javac1.6" source="1.6" executable="C:/NWDS/jdk1.6.0_45/bin" fork="true" taskname="javac1.6"></java>
<ABPMGen displayName="Inbox"
description="Inbox"
blockKey="InboxNumber" interfaceVersion="2">

<BusinessObject
name="Inbox"
boPackage="<Vendor>.inbox.bo"
interfacePackage="<Vendor>.inbox.bean"
beanPackage="<Vendor>.inbox.beanimpl"
metaDataPackage="<Vendor>.inbox.bomd"
defaultLanguage="en"
logicalVersion="V1" />

<PathInformation
xml="xml/Inbox.xml"
basePath="${dcroot}"
ddicPath="../ddic"
softwareComponent="DCs"
vendor="<Vendor>"
ejbDC="<DC prefix>/inbox/ejb"
warDC="<DC prefix>/inbox/war"
umeDC="<DC prefix>/inbox/ume"
bpmDC="<DC prefix>/inbox/ejb"
genDC="<DC prefix>/inbox/gen" />

<I18N language="de" file="Inbox_de.xml"/>
<I18N language="en" file="Inbox_en.xml"/>
<I18N language="hu" file="Inbox_hu.xml"/>
<I18N language="nl" file="Inbox_nl.xml"/>
<I18N language="fr" file="Inbox_fr.xml"/>
</ABPMGen>
<native2ascii encoding="UTF-8" src="${properties.path}" dest="${properties.path}" ext=".properties" includes="**/*.raw"/>
<delete>
<fileset dir="${properties.path}" includes="**/*.raw"/>
</delete>
</target>
</project>


Before the standalone application DCs will be checked in and activated here some best practices and tips for standalone apps:

  • for a custom inbox the BPM API can be used directly inside callback implementations (getting tasks, search tasks, read/write substitution rules, etc.) but for re-usability and a better maintenance it is better to separate the logic of the BPM API into an additional facade implementation where task data can be enriched with additional data from a separated persistence (e.g. in case of additional custom requirements)

  • a custom inbox should display additional columns/most used columns per default from the application start onto (so the end user must not switch the perspective/task type before he/she can see additional data columns, see application screenshot above)

  • an own substitution tab is much better and more intuitive to create or maintain substitution rules like small or hidden icons in other inbox solutions like BPMInbox or MyInbox

  • for substitution rules it is very helpfully to use business cards, so that an end user can see who is exactly the substitute (e.g. the name Thomas Müller can exists multiple times in the identity management)

  • create an additional redirect servlet inside the war DC so that end user can open the standalone application with a relative application URL, they will be forwarded to the long aBPM application URL (e.g. /inbox -> /abpmui5/create/index.html?boTechnicalName=bo://<Vendor>.inbox.bo/Inbox&companyCode=100&processMode=CREATE&processState=START
    package <Vendor>.inbox.redirect;

    import java.io.IOException;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    /**
    * Servlet implementation class RedirectServlet
    */
    public class RedirectServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;
    private static final String STATIC_URL_PART = "abpmui5/create/index.html?boTechnicalName=bo://<Vendor>.inbox.bo/Inbox&companyCode=100&processMode=CREATE&processState=START";

    /**
    * @see HttpServlet#HttpServlet()
    */
    public RedirectServlet() {
    super();
    }

    /**
    * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
    */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // get url parameters
    String queryString = request.getQueryString();
    if (null == queryString) {
    // redirect without url parameters
    response.sendRedirect(STATIC_URL_PART);
    } else {
    // redirect with url parameters
    response.sendRedirect(STATIC_URL_PART + "&" + queryString);
    }
    }

    /**
    * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
    */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    doGet(request, response);
    }

    }