Load balancing configurations for SAP Hybris

As mentioned in one of my previous post if not setup correctly clustered environment can be a nightmare. Below are some of the configurations worth noting beforehand:

Server Affinity: a lost session

An HTTP (s) session should always be served by only one SAP Hybris application server otherwise session will be lost and user would need to re-login, even worse anonymous user will lose his cart and all other settings. To avoid this embarrassing experience, enable Sticky Session at application load balancing layer which will stick one session to one SAP hybris server.

Redirect rules: 

Very often redirect rules are defined at load balancing layer to redirect complete URL (base + context )-e.g. http://doodle.com/fun to base URL  e.g. http://doodle.com.

Here one exception should be medias because their urls are appended with /medias at end of base URL and redirecting these URLs to base URL wont load medias:

eg. a call to medias URL –  e.g. http://doodle.com/medias/abc/456789.jpg will be replace with http://doodle.com which will fail loading of medias into SAP Hybris applications.

 

 

Production Infrastructure for SAP Hybris

What is an ideal production server setup? How many servers, cores or nodes do you or customer need?

A production system should not be “it works” but it should be a full-fledged performance setup.

The production setup really varies from customer to customer but in general: one BackOffice server with couple of storefront servers along with webservers, solr servers and a load balancer is very standard infrastructure.

prod-setup

Measurement Metric:

Hybris gauges its performance with page impressions/ second/ core. Standard hybris accelerator comes with a ballpark 10-15 page impressions/second/core. Here fun part is – most of projects start with 1-2 pi/s/core and there can be seriously optimized applications where >50 pi/s/core can be found.

So it’s advised to analyse customer’s requirements with representative data rather than proposing a typically standard infrastructure setup.

 

 

 

What are component type group in Hybris?

Not everything is for everyone. This simple law of nature binds everything together. A polar beer is best kept away tropical area. Your business may want to keep only coordinated banners in rotating image banners. The content slots are bounded to have only few type of components to achieve these restrictions.

Components are grouped on basis of their types. These groups are termed as ComponentTypeGroup. There is one to many relations between this group and the components. They are persisted in database. In earlier versions, the valid component list was used just as macro in impex.

This approach is more optimal than the previous one which had a slow many to many relationship between ContentSlotName and Cms Component type.

<relation code="ComponentTypeGroups2ComponentType" generate="true" localized="false" autocreate="true">
			<deployment table="CompTypeGrp2CompType" typecode="1097" />
			<sourceElement qualifier="componentTypeGroups" type="ComponentTypeGroup" cardinality="many" collectiontype="set"/>
			<targetElement qualifier="cmsComponentTypes" type="CMSComponentType" cardinality="many" collectiontype="set"/>
</relation>

There are few groups which are defined in OOB Hybris. We can also define our custom groups.



INSERT_UPDATE ComponentTypeGroup;code[unique=true]
;logo
;headerlinks
;searchbox
;minicart
;wide
;narrow
;footer
;navigation
;mobile

Each valid component type is added to the group.

INSERT_UPDATE ComponentTypeGroups2ComponentType;source(code)[unique=true];target(code)[unique=true]
;narrow;ProductFeatureComponent
;narrow;CategoryFeatureComponent

When a component type is checked against its validity for a given content slot, the system checks if it is contained in the component type group.

INSERT_UPDATE ContentSlotName;name[unique=true];template(uid,$contentCV)[unique=true][default='LandingPage4Template'];validComponentTypes(code);compTypeGroup(code)

;SiteLogo;;;logo
;HeaderLinks;;;headerlinks
;SearchBox;;;searchbox
;MiniCart;;;minicart
;NavigationBar;;;navigation
;Section1;;;wide
;Section2A;;;narrow
;Section2B;;;narrow
;Section2C;;;wide
;Section3;;;wide
;Section4;;;narrow
;Section5;;;wide
;Footer;;;footer
;TopHeaderSlot;;;wide
;BottomHeaderSlot;;;wide
;PlaceholderContentSlot;;;

Relative URLs in Hybris Cockpits

Very often in several business scenarios it is required to share direct Urls of order, cart or product with internal teams, e.g.:

After order is placed:

  • Send confirmation email to customer.

And

  • Also send email to a team of internal staff with direct URL of that order so that staff can directly go to that specific order just by clicking on URL rather than logging into cockpit and searching for that order.

Considering different nature of cockpit framework it’s not straight to create direct URL of an item as compared to storefront which is MVC.

Here is solution:

https://<hostname>:<port>/cscockpit/index.zul?persp=cscockpitPerspective&events=activation&act-item=8796093055021

Understanding URL:

A request event handler parses request parameters and dispatches a Cockpit business event. Different components are then notified of this dispatched event.

According to above URL, Activation request events is triggered for order item with pk – 8796093055021

What is the use ProductOption Enum in Hybris?

Products are core to any commerce. This is the product which drives the whole business and workflow associated with it. Be it procurement, inventory management, Media management, order management, fulfillment to name a few. Everything revolves around Product.

Essentially, this leads to having hundreds of attribute associated with products. Price, Stock , promotion, categories are few data set attached to product. Since not every page will want to have every data set to be populated. This is also not efficient to propagate all data sets to front layer.

Product Option is an Enum, which categorize hundreds of attribute of products into few data sets. For example attribute related to stocks (stock level, stock status) will be under Stock data set. Now the population of Product data will be done on the required data sets of Product Option Enum.

Take the example of Order history page, here we don’t want user to see stock data, product reviews, product delivery modes. So it is useless to populate these data sets.

 

Hybris provides a Bean class DefaultModifableConfigurablePopulator, which takes a Map of populators as one of the property. This map will contain Product Option enum as the key and corresponding populator bean id as the value.

 

<alias name="defaultProductConfiguredPopulator" alias="productConfiguredPopulator"/>
<bean id="defaultProductConfiguredPopulator" class="de.hybris.platform.commercefacades.converter.impl.DefaultConfigurablePopulator" >
<property name="populators">
<map key-type="de.hybris.platform.commercefacades.product.ProductOption">
<entry key="BASIC" value-ref="productBasicPopulatorList"/>
<entry key="PRICE" value-ref="productPricePopulatorList"/>
<entry key="PRICE_RANGE" value-ref="productPriceRangePopulator"/>
<entry key="GALLERY" value-ref="productGalleryPopulatorList"/>
<entry key="SUMMARY" value-ref="productSummaryPopulatorList"/>
<entry key="DESCRIPTION" value-ref="productDescriptionPopulatorList"/>
<entry key="CATEGORIES" value-ref="productCategoriesPopulatorList"/>
<entry key="PROMOTIONS" value-ref="productPromotionsPopulatorList"/>
<entry key="STOCK" value-ref="productStockPopulatorList"/>
<entry key="REVIEW" value-ref="productReviewPopulatorList"/>
<entry key="CLASSIFICATION" value-ref="productClassificationPopulatorList"/>
<entry key="VARIANT_FULL" value-ref="productVariantFullPopulatorList"/>
<entry key="REFERENCES" value-ref="productReferencesPopulator"/>
<entry key="DELIVERY_MODE_AVAILABILITY" value-ref="productDeliveryModeAvailabilityPopulator"/>
</map>
</property>
</bean>

 

If You want to add a new populator to the system, for example say video.

<enum class="de.hybris.platform.commercefacades.product.ProductOption">
<value>VIDEO</value>
</enum>

 

Now redefine the configureable populator bean to include your populator. Now the product data will start having your data set as well.

<alias name="videoProductConfiguredPopulator" alias="productConfiguredPopulator"/>
<bean id="myProductConfiguredPopulator" parent="defaultProductConfiguredPopulator">
    <property name="populators">
        <map key-type="com.myproject.facades.product.MyProductOption" merge="true">
            <entry key="Video" value-ref="videoOptionPopulator"/>
        </map>
    </property>
</bean>
 

 

 

Hook Interfaces and Strategies in Hybris

Customizing ordering processes could not be easier. Those were days when we used to override facades, services and strategies to inject a line of code just before or after a specific event in an ordering process. E.g. Before and After:

  • payment Authorization
  • adding product to cart
  • cart calculation lifecycle
  • cloning a saved cart
  • flagging a cart for deletion
  • Placing order
  • saving a cart
  • restoring a cart
  • updating cart entry

  Since version 5.4 onward (if I am not wrong) SAP Hybris has introduced a number of hook interfaces which can be implemented to inject your code at a specific time without hassle of overriding a chain of classes. Below is a list of few of them:

  • AuthorizePaymentMethodHook
  • CommerceAddToCartMethodHook
  • CommerceCartCalculationMethodHook
  • CommerceCloneSavedCartMethodHook
  • CommerceFlagForDeletionMethodHook
  • CommercePlaceOrderMethodHook
  • CommerceSaveCartMethodHook
  • CommerceSaveCartRestorationMethodHook
  • CommerceUpdateCartEntryHook

Usage: Implement required interface and corresponding method. And define below bean and add entry in directive: <bean id=yourCustomAddToCartMethodHook class=”xx.yy.zz”/> <bean id=“commerceAddToCartMethodHooksListMergeDirective” depends-on=“commerceAddToCartMethodHooks” parent=“listMergeDirective”> <property name=“add” ref=yourCustomAddToCartMethodHook /> </bean>    

B2B with B2C – a perfect blend of accelerators (storefronts)

One Hybris – One Accelerator – B2B and B2C – a vision few years ago has been implemented and is at its best now.

There was always need of a blend of B2B and B2C where a B2C customer might have requirement of small organisations concept(B2B units) or a B2B customer might expect a little more from promotions.

B2B features are now available across all Accelerators: in B2C accelertor  -Business units can be created along with their associated supporting elements, such as cost centers, permissions,budgets, users and user groups.

Additionally, commerceorgaddon and b2bacceleratoraddon are introduced to provide B2B storefront support to default accelerator – empowering single accelerator with all features of B2B and B2C.

Load balancer Redirection issue

In clustered environment it’s very common to terminate SSL at load balancer layer and pass non secure requests to Hybris applications servers.

This brings in some complexities if configuration is not correct.

As hybris servers get http request so in case redirection is required they redirect to same non secure protocol which is then recreated into secure request by rules configured at load balancer layer.

To avoid this unnecessary redirection and other potential session issues you have to configure all application servers to redirect to specific port and name.

Add following attributes to <Connector port=”${tomcat.http.port}” in server.xml files of all servers:

proxyPort=”443″ proxyName=”${proxyName}

proxyName – URL of application e.g. google.com

Setting up ASM (assisted service module ) and facing : “Error creating bean with name ‘getAssistedservicestorefrontBeforeViewHandler’”

Are you facing this exception while setting up Assisted Service Module:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘getAssistedservicestorefrontBeforeViewHandler’: Post-processing of FactoryBean’s singleton object failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class com.sun.proxy.$Proxy160]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class class com.sun.proxy.$Proxy160

Solution:

Disable Autoproxy – remove entry “<aop:aspectj-autoproxy />” from assistedservicestorefront-web-spring.xml and restart server.. boom!!

 

Understanding Spring Events in Hybris

We humans are known to celebrate certain milestones in our life journey. We do celebrate birth, tying knots etc. Similarly, in the buying journey of a customer, there are few milestones which are worth celebrating or react to. Order placement, registration of a customer is few of them. The reaction could be about sending a welcome email, or sending the order data to a third party system for fulfillment.

Further, let’s say, a customer registered on a web site, and wants to start browsing the cool products. But the lousy code of sending a fancy welcome email with a promotional voucher in it, took around one minute. He will regret his decision to register, and will walk away.

spring-listener.jpg

Spring based events, provides the exact same infrastructure. So now we know, whenever we have a situation where some lousy code is to be executed after some thing happens (an event), we will rely on events.

 First we need to create an Event class, that will hold the necessary data to pass to the listener.

protected AbstractCommerceUserEvent initializeEvent(final AbstractCommerceUserEvent event, final CustomerModel customerModel)
{
event.setBaseStore(getBaseStoreService().getCurrentBaseStore());
event.setSite(getBaseSiteService().getCurrentBaseSite());
event.setCustomer(customerModel);
event.setLanguage(getCommonI18NService().getCurrentLanguage());
event.setCurrency(getCommonI18NService().getCurrentCurrency());
return event;
}

Spring provides a way to publish an event.

getEventService().publishEvent(initializeEvent(new RegisterEvent(), customerModel));

There are dedicated listeners lying around, who listens to these wishes, and reacts the way, they are programmed.

Listeners can be bonded to publishing services via common event object.

public class RegisterEventListener extends AbstractEventListener
{
   @Override
   protected void onEvent(final AbstractEvent event)
   {
      if (event instanceof RegisteEvent)
      {
          // Do whatever you want. send email/voucher or whatever
      }
   }
}

Please note that since, listener code starts in a new thread, it will not hamper customer journey on your site. The listener code will execute as a back end process.

Adding a new attribute to edit view area in Product Cockpit.

I was working on a Project and there was a requirement to add a new attribute in edit view area In Product Cockpit.

I followed these few step to do this task.

1.Create a new Model e.g MyProduct that extends ProductModel in Items.xml, add attribute in this model e.g:

“<itemtype code=”MyProduct” extends=”Product”>
<description>my product that contains additional attributes.</description>
<attributes>
<attribute qualifier=”attribute1″ type=”localized:java.lang.String”>
<description>example for product cockpit</description>
</attribute>
</attributes>
</itemtype>”

after this,Build the project and start the server, do Update running system from Hac.

Add some product in your “Myproduct” Model.

2.Create  a editorArea_MyProduct.xml file in your Cockpit extension and add key for new attribute in xml file

editorArea_MyProduct

3. Localized the key name in  ProjetcNamecockpits-locales_en.properties and i3-label.properties file.

ProjetcNamecockpits-locales_en

 

4.Restart the server and do update from HAC. Open ProductCockpit in url “http://localhost:9001/productcockpit&#8221; and search your product which you added in “MyProduct” model .

5.Open that product and in left side you will find your new attribute in Editor_area.

Capture1

Error submitting a form due to Cross Site Request Forgery (CSRF)

Recently I faced a problem when I was trying to simply create a Form and a Controller to accept the values submitted from the form. I was using Hybris 5.7 version.

When I entered and submitted some values in the form (or even submitting an empty form) I was continuously getting the following error in the browser and the program control was not reaching my controller:

http status 403 bad or missing CSRF value

After doing lot of googling I found that the above error was coming while sending a “POST” request from any Form and was due to the interceptor “csrfHandlerInterceptor” configured in spring-mvc-config.xml of my storefront. This interceptor is configured to prevent Cross Site Request Forgery (CSRF).

Now to fix this error, there are 2 options:

  1. either the CSRF token in the request matches the session CSRF token to ascertain the validity of incoming posts requests.
  2. or the requested URL is a trusted path and is allowed to go through without CSRF token validation

For the 1st point you need to configure and send a valid CSRF token in your request. You can find information regarding this at: http://docs.spring.io/spring-security/site/docs/3.2.0.CI-SNAPSHOT/reference/html/csrf.html

For the 2nd point you can add your URL entry to “csrfAllowedUrlPatternsList” in your storefront’s spring-mvc-config.xml file as shown below:

<util:list id=”csrfAllowedUrlPatternsList” value-type=”java.lang.String”>
             <value>/upload/createmedia</value>
</util:list>

Please note that for additional details there is a good article on CSRF in Hybris Wiki at the following URL:

https://wiki.hybris.com/display/accdoc/Spring+Security

 

 

Hybris Mobile and Desktop Site

Most people face problem in understanding the difference between Desktop site and Mobile site in Hybris and how do we setup the Mobile site and what parameters control the switching of Desktop and Mobile site. Also, how does a responsive site differ from both Desktop and Mobile site?

Technical Difference in terms of UI

First of all, the mobile site differs from the desktop site generally in terms of the UI. The back end code mostly remains the same for both the mobile and desktop sites.

The UI change is controlled by CSS, JS and images.

Also, the UI, which is defined using Hybris WCMS, need to define different Page Template, ContentSlot, ContentPage, ProductPage and the relationship between them for Mobile site and Desktop site.

Technical Difference in terms of Java code

For accessing the UI of either the Mobile site or the Desktop site, we need set the UiExperienceLevel to corresponding device type for which we need to first of all detect the device from which request is coming. This is done using an interceptor i.e. DeviceDetectionBeforeControllerHandler using class DefaultDeviceDetectionFacade and more specifically in SpringMobileRequestDeviceDataPopulator.

After device detection, the detected device needs to be mapped to a UiExperienceLevel (i.e whether it is desktop, tablet, mobile), which is done in class DeviceDataUiExperiencePopulator.

After this the detected UiExperienceLevel is compared with the supported UiExperienceLevel and if matched then DetectedUiExperienceLevel is set to this value.

Parameters controling switching of Desktop and Mobile site

UiExperienceLevel is configured in your properties file using the property “uiexperience.level.supported”.

Please note the correct format of specifying the value for “uiexperience.level.supported” is comma separated Camel Case names like Mobile,Desktop or Desktop,Mobile for the functionality to work correctly.

There is one more interceptor SetUiExperienceBeforeControllerHandler called before the request reaches the controller. This interceptor checks for the parameter “uiel” (like ?uiel=Mobile) in the request and if set its value is used to override all previous UiExperienceLevel.

Based on the UiExperienceLevel set, the corresponding CSS, JS and images are set and we see either the Mobile site or the Desktop site.

Responsive

When we have constructed our website for Responsive UI then the UI automatically adjusts itself according to the device type and this is done because of the responsive JS used. Hybris has made the desktop site responsive hence eliminating the need for separate Desktop and Mobile sites.

Different Views for Electronics Site:

MobileSite
Mobile Site – Non Responsive
ResponsiveSite-MobileView
Mobile Site – Responsive

 

 

 

 

 

 

 

 

 

ResponsiveSite-DesktopView

Desktop Site – Responsive

ResponsiveSite-TabView

Tab Site – Responsive

 

Understanding Cronjobs

A Cronjob is a scheduler where we can schedule a particular Job to be performed at a specified time or over a period of time.

The 3 parts of a Cronjob are:

  1. CronJob
  2. Job
  3. Trigger

For understanding each of these clearly I would take an example:

1. CronJob:

First of all you need to identify what task you CronJob need to do.

In this case lets say that your cronjob needs to send NewsletterEmail periodically with a personalized message.

You would create a NewsletterEmailCronJob item in your items.xml file having attributes for storing a personalizedMessage

<itemtype code=”NewsletterEmailCronJob” autocreate=”true” generate=”true” extends=”CronJob”
jaloclass=”com.email.jalo.NewsletterEmailCronJob”>
<description>Cronjob for sending Newsletter Email</description>
<attribute qualifier=”personalizedMessage” type=”java.lang.String”>
<persistence type=”property”/>
</attribute>
</itemtype>

Now the above code would create the required Model class when you save it and run Ant Build script.

2. Job:

Job is where your business logic goes and it uses the CronJob Model you created as part of CronJob above.

Here you would create a class say NewsletterEmailJob which would extend AbstractJobPerformable and would work on NewsletterEmailCronJobModel.

You would override the perform method to write the business logic in it.

Code Snippet:

public class NewsletterEmailJob extends AbstractJobPerformable<NewsletterEmailCronJobModel>
{

@Override
public PerformResult perform(final NewsletterEmailCronJobModel cronJob)
{

….. Logic for sending News letter Email with Personalized Message ….

}

}

Also, you would define a bean for the above class in your spring.xml file as follows:

<bean id=”newsletterEmailJob” class=”com.email.NewsletterEmailJob”
parent=”abstractJobPerformable” >
</bean>

3. Trigger

Trigger defines the time event when the Job would be executed.

The trigger is linked to the Job via CronJob. You would first create a CronJob item and associate the bean id of the Job to it. Then you would create a Trigger item which contains the CronJob item reference and the time schedule for execution. The time schedule expression is written using Quartz Job Scheduler which can found at http://www.quartz-scheduler.org/documentation/quartz-1.x/tutorials/crontrigger

INSERT_UPDATE NewsletterEmailCronJob; code[unique=true];job(code);
 ;newsletterEmailCronJob;newsletterEmailJob;
INSERT_UPDATE Trigger;cronJob(code)[unique=true];active;year;month;day;hour;minute;second;maxAcceptableDelay;relative;weekInterval;daysOfWeek
 ;newsletterEmailCronJob;true;-1;-1;-1;-1;10;-1;1;true;

Store Locator – Unable to locate stores with Google maps when a proxy is installed

Problem:

Usually while testing the “Store Finder” page on demo site like “electroncis” site, you can’t locate stores with Google maps because of a proxy installed  and generally it works without a proxy.

Solution:

For overcoming this problem, you may try adding the following entries to your hybris installation in the file: hybris\bin\platform\tomcat\conf\catalina.properties

http.proxyHost=
http.proxyPort=
http.proxyUser=
http.proxyPassword=

Note: It may work by just adding the 1st two properties itself.

How to run multiple hybris instance in one machine?

Basically hybris runs on a tomcat instance. Hybris is shipped with a bundled tomcat. So the question here is actually, how to run multiple tomcat in one machine.

We can run as many hybris we want, till our machine memory permits. To do so, we need to make each instance of tomcat to have it’s own ports to use. Make below ports unique for each instance. We should add below properties in local property file of each instance with unique values..

tomcat.http.port=7001
tomcat.ssl.port=7002
tomcat.ajp.port=7009
tomcat.jmx.port=7003
tomcat.jmx.server.port=7004

Managing configuration properties for multiple environments

An enterprise level solution always have different environments for different stakeholders like local, dev for developers, UAT/SIT for QAs etc. Then we have a production environment.

Every environment may have different characteristics,  like different email SMTP servers or different database etc. Hybris based projects are no different and have multiple multiple environment based on lifecycle of project.

Now the problem is how to manage configuration for multiple environments. Of course, we can do it manually, where we can have different property files set up on individual server. But this solution is  not modular and efficient.

The approach i am going to present is based on using apache ant to create environment specific file. It will copy properties from one common file and one environment specific file and merge them to create a complete local.properties file.

Follow below steps:

  1. Create below  folder structure inside hybris config directory. 

config

 2. Place your properties in each one of local files as per environment related values. The property which have same values across environment will go to common local property file.

3. Create a build.xml file inside config project and copy below content. 

build

4. Now we have a ant build xml, which will create a environment specific property file. You can run like below, pass the environment name as required. 

antbuild

Now you have a flexible solution to configure your hybris system more efficiently, thanks to open source apache ant.