Featured post

Docker setup for Liferay 7 with MySQL

Showing posts with label liferay dxp. Show all posts
Showing posts with label liferay dxp. Show all posts

Thursday, 3 May 2018

Background Task Liferay 7 or DXP Part 1


Executing or scheduling background task is always an important activity for developers.

There are so many scenarios where background tasks are used like import/export, sending bulk notification or email, processing orders etc.

You can find this interesting link here which talks in length for background tasks.
What it does not offer is implementation example and changes for background task in Liferay 7/DXP

By following few Liferay conventions, we can easily use this weapon.
In this example I will explain creation of background task, pass parameters and handle errors if any.

In next example I will demonstrate check status, progress and display it over UI.


We will be creating two osgi components for this activity.
1. Create osgi module for background task creation and register it to handler
2. Use above module to create background tasks


Render method to create background task every time portlet loads.

@Overridepublic void doView(RenderRequest renderRequest, RenderResponse renderResponse) throws IOException, PortletException {
    Random random = new Random(12);

    HttpServletRequest request = PortalUtil
            .getHttpServletRequest(renderRequest);

    ThemeDisplay themeDisplay = (ThemeDisplay) renderRequest
            .getAttribute(WebKeys.THEME_DISPLAY);

    ServiceContext serviceContext = null;
    try {
        serviceContext = ServiceContextFactory.getInstance(renderRequest);
    } catch (PortalException e) {
        logger.error("Eror in getting service context", e.getCause());
    }

    // This taskContextMap can be used as transporter to background job
    Map taskContextMap = new HashMap<>();
    taskContextMap.put("processName", "testing " + random.nextInt());
    taskContextMap.put("totalNodes", String.valueOf(random.nextInt()));
    //taskContextMap.put("serviceContext", serviceContext);    try {
        // Adding the job to liferay background manager
        com.liferay.portal.kernel.backgroundtask.BackgroundTask backgroundTask = backgroundTaskmanager.addBackgroundTask(themeDisplay.getUserId(),
                themeDisplay.getScopeGroupId(), SampleBackgroundTaskExecutor.class.getName(),SampleBackgroundTaskExecutor.class.getName(),taskContextMap, serviceContext);
        // With returned background object you can check status, id etc.
        renderRequest.setAttribute("backgroundTaskId",
                backgroundTask.getBackgroundTaskId());

    } catch (PortalException e) {
        logger.error(e.getCause());
    } catch (SystemException e) {
        logger.error(e.getCause());
    }




Create Background Task Executor

@Component(
        immediate = true,
        property = {"background.task.executor.class.name=com.netcracker.cabinet.background.executor.MigrationBackgroundTaskExecutor"}, 
        // Without this property osgi will not register this as background executor/handler
        service = BackgroundTaskExecutor.class)
public class SampleBackgroundTaskExecutor extends BaseBackgroundTaskExecutor {


isSerial - True

// if it's not serial then multiple instances of this executor can run parallel, to run it in queue mode, we use isSerial true
@Overridepublic boolean isSerial() {
    return true;
}



Main execute method for the Job

public BackgroundTaskResult execute(BackgroundTask backgroundTask)
      throws Exception {
      // taskContextMap which is sent by the caller
      Map taskContextMap = backgroundTask.getTaskContextMap();

      String taskName = (String)taskContextMap.get("processName") ;
      String totalNodes = (String)taskContextMap.get("totalNodes");

      //ServiceContext serviceContext  = (ServiceContext) taskContextMap.get("serviceContext");
   if(LOGGER.isDebugEnabled()){
      LOGGER.debug("Task Name : "+ taskName);
   }

   BackgroundTaskVO messageContent = new BackgroundTaskVO();
   messageContent.setTotalNodes(totalNodes);

   // Sending the data to util for MessageBus
   SampleDataHandlerStatusMessageSenderUtil.sendStatusMessage(messageContent);

  // Telling the system if, background task is successful or not
   BackgroundTaskResult backgroundTaskResult = new BackgroundTaskResult(
      BackgroundTaskConstants.STATUS_SUCCESSFUL);
   backgroundTaskResult.setStatusMessage("Wonder full");
   return backgroundTaskResult;
}



Message Bus update

public static void sendStatusMessage(BackgroundTaskVO messageContent) {
   // Leave if no background task
   if (!BackgroundTaskThreadLocal.hasBackgroundTask()) {
      return;
   }

   // Message Creation
   Message message = createMessage(messageContent);

   // Send message to message bus
   MessageBusUtil.sendMessage(DestinationNames.BACKGROUND_TASK_STATUS,
         message);
}


You can download the source code from here

You are just done, Try & Enjoy the function.............:)

Tuesday, 10 April 2018

Osgi Target, Property attributes and Multiple Implementations

Osgi Target and Property attributes

In Spring you can use @Qualifier to get the correct class for respective instances.
How can we achieve this in OSGI framework is the question?

With help of Osgi and it's SCR(Service Component Runtime) you can use dynamic instantiation of Declarative Services.
Correct implementation should be selected for each instance to develop and use IS-A relationships.

Developers can instantiate a interface with different implementations by utlizing "target" attribute inside @Reference annotation. All you need is to enable the classes to get it from SCR with "property" attribute inside @Component annotation.

Below is the sample code to achieve the described matter

Problem Statement : We need to convert Numbers from string in different formats like Integer, Double, Float etc. and use them in different osgi components.


We will create interface and two classes which will implement this interface and consume them in another component for demonstration purpose.



public interface OsgiTarget {

 public Number convert(String str);
}

As you can see we defined multiple properties inside @Component annotation.

@Component(property = {
        "osgi.target.example.integer=true",
        "osgi.target.example.integer.again=true" // Dummy entry for multiple target example, you can get this instance with this property as well
})
public class OsgiTargetIntegerImpl implements OsgiTarget {
 @Override
 public Integer convert(String str) {
  return Integer.valueOf(str);
 }
}

Here we have single property for the same.

@Component(property = "osgi.target.example.double=true")
public class OsgiTargetDoubleImpl implements OsgiTarget{

 @Override
 public Double convert(String str) {
  return Double.valueOf(str);
 }
}


Now we have basic converters with different implementations.
We will use them in our component to show how it's going to work.


@Component(immediate = true)
public class OsgiTargetExampleConsumer {
 
 // Osgi target property for OsgiTargetIntegerImpl
 @Reference(target = "(osgi.target.example.integer=true)")
 OsgiTarget osgiTargetInteger;
 
 // Osgi target property for OsgiTargetIntegerImpl with another property but same instance 
 @Reference(target = "(osgi.target.example.integer.again=true)")
 OsgiTarget osgiTargetIntegerAgain;
 
 // Osgi target property for OsgiTargetDoubleImpl
 @Reference(target = "(osgi.target.example.double=true)")
 OsgiTarget osgiTargetDouble;

 
 @Activate
 public void run() {
  System.out.println("__________________" + osgiTargetInteger.convert("21"));
  System.out.println("__________________");
  System.out.println("__________________" + osgiTargetInteger.convert("12"));
  System.out.println("__________________");
  System.out.println("__________________" + osgiTargetDouble.convert("1987"));
 }
}


In the above class we are using the interface with @Reference annotation to get the object from SCR with respective classes just by adding target . If you look closely, you can see one class can be invoked by multiple properties as well.

Output will be similar like this :



You are just done, Try & Enjoy the function.............:)

Thursday, 2 November 2017

Override OOTB javascript in Liferay 7 or LIferay DXP

Liferay becomes modular and it requires more efforts to extend it's functionality. I believe that is for our own good but yeah it becomes complex.

To override a javascript file now requires a bundle with some specific entries.
In this example I am overriding logo editor of Liferay and adding a console log to demonstrate the same.

  1. BND
  2. CONFIG.JS
  3. FILE_TO_OVERRIDE
1.BND

Liferay-JS-Config takes the config file which does the magic to override. Even though this file is useful for multiple occasions, for now we will use it for this purpose only.
Web context path for web resources, in our case it's java script.




Bundle-Name: logo-editor-for-snap
Bundle-SymbolicName: logo-editor-for-snap
Bundle-Version: 1.0.0
Liferay-JS-Config: /META-INF/resources/js/config.js
Web-ContextPath: /logo-editor-web-override



2.CONFIG.JS

  • base: File location to override
  • path: File Name
  • condition: In which scenario this module should be loaded
    • name: Name of the module
    • trigger: Should trigger when a call made to module, e.g. liferay-logo-editor
    • when: This module replace triggered module with value instead. There are multiple values possible for this, subject to explore.
;(function() {
    AUI().applyConfig(
        {
            groups: {
                sessionext: {
                    base: MODULE_PATH + '/js/',
                    combine: Liferay.AUI.getCombine(),
                    filter: Liferay.AUI.getFilterConfig(),
                    modules: {
                        'liferay-logo-editor-override': {
                            path: 'liferay-logo_editor.js',
                            condition: {
                                name: 'liferay-logo-editor-override',
                                trigger: 'liferay-logo-editor',
                                when: 'instead'
                            }
                        }
                    },
                    root: MODULE_PATH + '/js/'
                }
            }
        }
    );
})();



3. FILE TO OVERRIDE [e.g. logo_editor.js renamed to liferay-logo_editor.js]

Change the module name at first line.


AUI.add( 'liferay-logo-editor-override', function(A) { var Lang = A.Lang;
....
....
....
renderUI: function() { var instance = this; console.log("LOGO EDITOR OVERRIDDEN...");
....
....


I took the reference from this post and explained as much I can.
Thanks to Liferay Staff members for the snippets!

You are just done, Try & Enjoy the function.............:)