Preparing Your Building Blocks For Learn SaaS

Version 18

    Blackboard Building Blocks have long been a staple in the Blackboard Learn platform. These Java Web Apps allow you to customize the workflow and experience that you and your faculty use to interact with the system.


    We realize that this is an important part of the Learn ecosystem, and so Blackboard Learn SaaS with the Original Experience will continue to support your Building Blocks going forward. That said, the architecture of the SaaS-delivered platform is dramatically different than that of the self- and managed-hosted servers you are used to. As a result, your Building Block will likely need to be modified to run in the new Blackboard.


    The following sections list requirements that must be met for a B2 to function in a SaaS environment. For example, your B2 must be compiled with Java 8, the database may be Postgres so the best practice is to use Schema.xml, the shared content must be accessed as described below, etc. Many of these are also requirements for a B2 to function on Learn Q2 2016 (3000.x.x) and Q4 2016 (3100.x.x). The best practice is to code to meet all of these requirements, then your B2 will function on SaaS, Managed Hosted, and Self Hosted systems.



    In SaaS, the database schema name will no longer be BBLEARN or bb_bb60. Your B2 code must determine the actual schema name if it has any dependency on the value. See Bye Bye BBLEARN & bb_bb60


    Also, in SaaS, the database is Postgres. If you’ve been testing your code on the Developer Virtual Machine, this isn’t that big of a deal. Schema.xml will continue to be supported as it is today. If you are providing SQL statements in the form of stored procedures, post_schema_updates, etc, you will just need to be sure to supply those files in postgres form, as well. These files will take the suffix, db-pgsql.

    In addition, its important to note that Exceptions encountered during postgres transactions stop all processing. You must code to handle this occurrence. One approach is to create a save point before you start the transaction and roll back to that save point upon exception. Here’s a small sample demonstrating this.


    // Much of the error handling stripped for space

    public static T withSavePoint(Callable c, Connection con) throws SQLException {    

         Savepoint savepoint = null;    

         try {      

              if ( null != con && !con.getAutoCommit() )  {        

                   savepoint = con.setSavepoint();      



         } catch ( SQLException e ) {      

              if ( con != null && savepoint != null ) {         

                   con.rollback( savepoint );      


              throw e;    




    Lastly, postgres handles timestamps differently. There are two types of timestamps: localtimestamp and clock_timestamp::timestamp. The localtimestamp returns the time at the start of the transaction. The clock_timestamp()::timestamp returns the actual current time. As a result, it is best practice to use clock_timestamp()::timestamp in your Building Block, as this matches the behavior of timestamps in other databases.


    Shared Content Folder


    In the Enterprise Blackboard Learn you are accustomed to developing for, the Building Block home lives in the shared content directory. For instance, if I wrote a building block and set my vendor ID to ‘bbdn’ and my plugin handle to ‘my-b2’, my building block and all of its related files would live in blackboard/content/vi/BBLEARN/plugins/bbdn-my-b2, and this directory would exist once, in only one place, so changes were persisted to all application servers.


    In Learn SaaS, there are two building block homes. There is still the shared file system that is shared amongst the entire group of application servers, but there is also a local cache on each individual server. As a result, the Building Block would still reside in the shared directory, similar to .../content/vi/BBLEARN/plugins/bbdn-my-b2, however the web app would live in the local cache on each server, in a directory similar to .../cache/vi/BBLEARN/plugins/bbdn-my-b2.


    As a result of this change, several of the Plugin API methods have been modified to handle the dual-folder deployment. PlugInManager.getPlugInDir() and PlugInManager.getPluginsDirectory() can now only be used to access the read-only files from the exploded war in the cache folder. If you need to access the shared config folder for your Building Block, you can use PlugInUtil.getConfigDirectory(). Calling methods like ServletContent.getRealPath() will point to the cache folder, so be sure that any method you are calling that returns a path or a file is returning what you expect it to.


    As an example, with prior versions of Learn you could use the following code to write to a file in your plugin’s folder and create a configuration file:


    PlugInManager manager = PlugInManagerFactory.getInstance();


    File myDir = manager.getPlugInDir( manager.getPlugIn( "myVendorId", "myB2Handle" ) );

    File myConfigDir = new File( myDir, "config" );

    File myConfigFile = new File( myConfigDir, "config.txt" );


    // read/write myConfigFile


    You will now need to re-write the above code code to look like the following:


    File myConfigDir = PlugInUtil.getConfigDirectory( "myVendorId", "myB2Handle" );

    File myConfigFile = new File(myConfigDir, “config.txt”);


    // read/write myConfigFile


    If you just need to read from a file that is included with in your Building Block, you can use the following code snippet to access the cached copy.


    PlugInManager manager = PlugInManagerFactory.getInstance();


    File myDir = manager.getPlugInDir( manager.getPlugIn( "myVendorId", "myB2Handle" ) );

    File myStaticDirectory = new File (myDir, "webapp/myStaticStuff");


    // read from myStaticDirectory - files as originally present in war file


    See the section in Developer Virtual Machine for how to configure your DVM to behave like Learn SaaS in regards to the shared content folder.


    Eventually, all write access to the shared folder will be phased out, and write access for logging will be limited to the log directory returned by PlugInUtil.getLogDirectory().

    Logging Changes


    In SaaS, logging is handled a bit differently, as clients will not have back-end access to the system. You can still log to the log directory, but those logs are redirected to Kibana so your Building Block won’t be able to read that log file. There will be access to the logs through the System Admin panel.


    In order to see your B2s logs in Kibana-Elasticsearch, the only SaaS interface for log files, your B2 must do the following:

    1. Write the log files to the directory returned by blackboard.platform.plugin.PlugInUtil.getLogDirectory. PlugInUtil (Building Blocks API 3000.1.0)
      1. Typically looks like <blackboard home>/logs/plugins/<vendorId>-<handle>/
      2. Read the API documentation on how to get write permission.
    2. Use this format, with four columns that are pipe separated:

    2016-03-15 01:00:00 | DEBUG | 41:c.b.c.i.task.UsageReportingTask | Generating Usage Report...
    2016-03-15 01:00:00 | ERROR | | Invocation of method 'doUsageReport' on target class ...failed
    java.lang.NullPointerException: null


    The b2 logging configuration in the logback.xml file that produces this log format is:

    <appender ... >



              <pattern>%date{yyyy-MM-dd HH:mm:ss} | %-5level | %-45(%L:%logger{40}) | %m%n%ex{10}</pattern>





    Sample code that works in a SaaS environment.




    The Learn SaaS cloud architecture is built to the best practices of cloud computing. As such, in SaaS, Learn is stateless. As a result, you can no longer rely on HttpSession persisting across requests. As a result, Building Blocks that synchronize data on sessions will need to be refactored. You can still use BbSession.setGlobalKey() to store data, but you will need to be cognizant of the size of the data, as this is stored in the database.


    As an example, if you currently employ code like the following to store an object in the session:


    request.getSession().setAttribute( "myKey", "myValue" );

    request.getSession().setAttribute( "myObjectKey", myObject );


    You will need to refactor to look like this:


    ContextManagerFactory.getInstance().getContext().getSession().setGlobalKey( "myVendorId.myB2Handle.myKey", "myValue" );


    Non-String values need to be serialized to save on the BbSession - refactor to avoid if at all possible.


    Java 8


    Blackboard Learn SaaS runs on Java 8. As a result, Building Block that are to be installed in the cloud, or on 9.1 Q2 2016 or later, need to be built with Java 8. For Learn Java 8 releases, Spring 4.2.0+ will be required for B2s that use Spring. For more information see Preparing Your Building Block for Blackboard Learn 9.1 Q2 2016

    Original UI


    Original courses run in an iframe on Learn SaaS. This shouldn’t affect your Building Block, except in the two following cases:

      • If you set top.document.location or top.location.href or any other similar settings that change the top page for the browser, your Building Block will not display properly. You can use window.location instead.
      • To meet accepted best practices in web design, there is a new maximum browser width of 1228px. Make sure you plan accordingly.
      • B2s using the bbUI tag library should be refactored were at all possible to use bbNG.


    Ultra UI


    There are currently no extension points for Building Blocks in the Ultra UI.


    Continuous Delivery


    Blackboard strives to deliver updates every two weeks. As a result, you should be using only public APIs whenever possible, as the continuous delivery model, coupled with the possibility of undocumented private API changes without warning, makes using private APIs extremely risky.


    Installing Building Blocks in Learn SaaS


    There is no way to install a Building Block in Learn SaaS, regardless of the User Interface you are using. If you have licensed Learn SaaS Plus or Advantage, you do have the ability to install Building Blocks, but you must work with support to schedule the installation.