SAP H[y]bris V6.2 Certified Development Professional Study Guide (P_HYCD_62) wcms, backoffice, cockpit, accelerator, data model, order, search, platform, pcm, price, user 250 questions to prepare the final exam! @2017 Hybhub Benoit Vanalderweireldt SAP Hybris Commerce 6.2 Developer Study Guide Benoit Vanalderweireldt 2018-02-01 Contents 1 Introduction 1.1 About the author 1.2 Requirements 1.3 Groovy console 1.4 Assessment test 1.4.1 Questions 1.4.2 Solutions 7 8 8 8 9 10 13 2 WCMS 2.1 CMS Items hierarchy 2.2 CMSItem CMSRelation 2.3 CMSItem types 2.4 CMS Components 2.4.1 Base Stores and websites 2.4.2 Cms component 2.5 Restrictions 2.6 Personalization 2.6.1 Segmentation input rules 2.6.2 Segmentation output actions 2.6.3 Result scope 2.6.4 Evaluation method 2.6.5 Create a CMS segment 2.7 WCMS cockpit 2.8 Point of services 2.8.1 Warehouse 2.8.2 Store 2.9 Warehouse 17 18 18 18 20 20 21 25 27 27 28 28 28 29 29 30 30 31 31 3 Backoffice 3.1 Create a new Backoffice application 3.2 Create and connect a new widget 3.2.1 Widget application context 3.3 Dynamic form 3.3.1 Example how to update the approval status 3.4 Backoffice and tenants 33 34 35 38 39 39 40 4 Cockpit 4.1 Create a new cockpit 4.2 Cockpit modification 43 44 45 2 CONTENTS 3 5 Commerce and Accelerator 5.1 Create a new addon 5.2 OCC Webservices 5.2.1 Install OCC 5.2.2 OAUTH 2 5.2.3 Create / customize webservices 5.3 CMS navigation bar 5.4 Order splitting 5.5 Promotion 5.5.1 Promotion Module 5.5.2 Promotion Engine 5.6 Payment 5.6.1 HOP 5.6.2 SOP 5.6.3 PCI compliance 5.7 Internationalization 5.8 Request handling 5.8.1 Request filters 5.9 Hot folders 5.9.1 Create a new import configuration 5.10 Data Modeling 5.10.1 Create new types 5.11 Flexible Search Query 5.11.1 Cache 5.11.2 Session and restrictions 5.11.3 Restrictions 5.11.4 Examples 5.12 Product Variants & Category Variants 5.12.1 Product Variants 5.12.2 Category Variants 5.13 Classification attributes 47 48 53 56 56 58 64 65 66 66 69 76 77 78 78 78 79 79 82 82 84 84 102 103 103 103 106 108 108 110 111 6 Order management 6.1 Business process 6.2 Fulfillment process 6.3 Shopping cart handling 6.4 Checkout flow 6.5 ASM 6.6 CS Cocpkit 113 114 118 119 120 126 127 7 Search and navigation 7.1 Solr index 7.2 Solr Indexed Property 7.2.1 Solr Indexed Query 7.3 Indexing listeners 7.4 Solr Facet 7.5 Solr Facet Range 7.6 Auto suggestion 7.7 Keywords 131 132 133 136 137 140 140 141 141 4 CONTENTS 7.8 Stopwords 7.9 Synonyms 7.10 Hero products & boost rules 7.10.1 Hero products 7.10.2 Boost rules 142 143 143 143 143 8 Platform basics 8.1 Initialization 8.2 Update 8.3 Update and Initialization lock 8.4 Update and Initialization hooks 8.5 Essential & project data 8.5.1 Import by convention 8.5.2 Import by configuration 8.5.3 Control the order of import 8.6 Sessions handling 8.7 Extensions structures 8.7.1 extensioninfo.xml 8.7.2 localextensions.xml 8.7.3 Create a new extension 8.7.4 Maven 8.8 Configuration 8.8.1 Configuration Service 8.8.2 Environment variables 8.8.3 Runtime optional configuration 8.9 Build 8.9.1 Callbacks 8.10 Tenant 8.11 Cache 8.11.1 Region cache 8.12 Interceptors 8.13 Transactions 8.14 Tasks 149 150 151 151 152 154 154 154 155 155 156 156 157 158 158 159 160 160 161 161 162 162 163 163 165 167 168 9 Platform core 9.1 Events 9.2 Impex 9.2.1 Header 9.2.2 Comment 9.2.3 Macro 9.2.4 Abbreviations 9.2.5 Document ID 9.2.6 Translators 9.2.7 Alternative pattern 9.2.8 Distributed Impex 9.2.9 Access properties from Impex 9.3 Spring context 9.4 Cronjobs 9.4.1 Abortable Cronjob 169 170 173 173 175 176 176 177 177 177 178 178 179 180 182 CONTENTS 9.5 9.6 Cluster 9.5.1 Cache invalidation 9.5.2 Configuration Testing 9.6.1 Unit tests 9.6.2 Integration tests 9.6.3 Other tests 5 183 184 184 184 185 187 187 10 PCM and price 10.1 Price calculation 10.2 Taxes 10.3 Discounts 10.4 Media object 10.4.1 Media formats and Media container 10.4.2 Media contexts 10.4.3 Secured media 10.5 Synchronization jobs 10.5.1 Dependent Synchronization 10.6 Workflow 189 190 191 192 192 193 194 194 194 195 195 11 User Management 11.1 User rights 11.1.1 Access rights 11.2 Search restrictions 11.2.1 Service layer example 11.2.2 Impex example 11.3 B2B hierarchy 197 198 198 199 200 200 200 12 Study tips 12.1 Study plan 12.2 Play with SAP Hybris 12.3 Identify your weakest point 12.4 Understand the Questions 12.5 Checking the answer 203 204 204 204 205 205 13 Mock exam 1 13.1 Questions 13.2 Solutions 207 208 230 14 Mock exam 2 14.1 Questions 14.2 Solutions 239 240 263 15 Mock exam 3 15.1 Questions 15.2 Solutions 273 274 291 6 CONTENTS Chapter 1 Introduction 7 8 CHAPTER 1. INTRODUCTION Dear reader, thank you for choosing SAP Hybris Commerce 6.2 Developer Study Guide to prepare for your SAP certification. At Hybhub, we would be grateful to have your feedback on this e-book. Feel free to share your thoughts about this study guide by emailing benoit.v@hybhub.com. If you disagree or think there are technical errors, please also share it with us. About the author Benoit Vanalderweireldt has worked as an SAP Hybris developer for the last five years for different companies in Asia, US and Europe. Besides being an enthusiastic SAP Hybris developer, he loves building software and infrastructures using automation. You can learn more about him by visiting his profile on Linkedin : https://www.linkedin.com/in/ benoitvanalderweireldt Requirements The following prerequisites are required in order to effectively use this study guide : 1. SAP H[y]bris V6.2 2. access to http://help.hybris.com 3. access to http://experts.hybris.com 4. a working IDE compatible with SAP Hybris (Eclipse, IntelliJ, Netbeans...) 5. JDK 8 must be installed Groovy console SAP Hybris Commerce has the ability of running a groovy script on a running system. From your script you are able to access all services, facades and therefore items. It makes the groovy console the best place to run any sort of quick tests or validate a piece of code on SAP Hybris (http://groovy-lang.org). To run a groovy script, open http://localhost:9001/console/scripting/ and select groovy as script type. If rollback mode is on, no data would be persisted, so if you want to manipulate data remember to switch the mode to commit Example on how to load a product with the groovy console: 9 1.4. ASSESSMENT TEST import import import import de.hybris.platform.servicelayer.search.FlexibleSearchService de.hybris.platform.catalog.CatalogVersionService de.hybris.platform.catalog.model.CatalogVersionModel de.hybris.platform.core.model.product.ProductModel FlexibleSearchService fss = spring.getBean("flexibleSearchService") CatalogVersionService cvs = spring.getBean("catalogVersionService") CatalogVersionModel cvm = cvs.getCatalogVersion("electronicsProductCatalog","Online") ProductModel pm = new ProductModel() pm.setCode("1099413") pm.setCatalogVersion(cvm) pm = fss.getModelByExample(pm) println pm.getName() Figure 1.1: Example Groovy console Assessment test When you prepare for the P_HYCD_62 exam to become a SAP Certified Development Professional - SAP Hybris Commerce 6.2 Developer you have a lot of questions like: • How do I know when I’m ready for the exam ? • How hard are the questions ? • How do I know what chapter I need to focus on ? • Where can I find a dump of the final exam ? (not here and I strongly suggest you not to even try) 10 CHAPTER 1. INTRODUCTION This chapter, the assessment test or pretest, is designed as a mental dipstick to know how likely you would be to successfully pass P_HYCD_62 if you were trying today. So let’s get started; try to answer all the questions under real conditions. Questions Pretest - Question 1 What actions are executed when you initialize SAP Hybris from ant? • Aborts all running cronjobs • Delete and recreate the database • Delete all tables • Import all project data Pretest - Question 2 Changing SAP Hybris configuration from the project.properties file of your platform project is considered bad because ? • This doesn’t comply with the SAP Hybris licensing model • It makes future updates harder • The file is already too big • This file is ignored after you create a new configuration folder from it Pretest - Question 3 Is this a valid Flexible Search Query ? SELECT {p.pk} FROM {Principal} AS p WHERE {p.uid} = ’admin’ • No, Principal is an abstract type • No, SQL aliases definition needs to be inside the brackets • No, for another reason • Yes, this is a valid Flexible Search Query. 11 1.4. ASSESSMENT TEST Pretest - Question 4 According to the following extensioninfo.xml what statements are true ? <?xml version="1.0" encoding="UTF-8" standalone="no"?> <extensioninfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="extensioninfo.xsd"> <extension abstractclassprefix="Generated" classprefix="HybhubStorefront" name="hybhubstorefront"> <requires-extension <requires-extension <requires-extension <requires-extension <requires-extension name="assistedservicestorefront"/> name="smarteditaddon"/> name="captchaaddon"/> name="acceleratorstorefrontcommons"/> name="hybhubfacades"/> <webmodule jspcompile="false" webroot="/hybhubstorefront"/> <meta key="extgen-template-extension" value="true"/> </extension> </extensioninfo> • This extension could define new item types from its hybhubstorefront-items.xml like all other extensions • This extension can be used as a template to create a new extension • This extension can define HMC configuration and localization • This extension requires the presence of a web directory Pretest - Question 5 Which of the following Interfaces are valid SAP Hybris Interceptors ? • de.hybris.platform.servicelayer.interceptor.ValidateInterceptor<MODEL> • de.hybris.platform.servicelayer.interceptor.PrepareInterceptor<MODEL> • de.hybris.platform.servicelayer.interceptor.CheckInterceptor<MODEL> • de.hybris.platform.servicelayer.interceptor.InitAllInterceptor<MODEL> • de.hybris.platform.servicelayer.interceptor.RemoveInterceptor<MODEL> • de.hybris.platform.servicelayer.interceptor.DeleteInterceptor<MODEL> 12 CHAPTER 1. INTRODUCTION Pretest - Question 6 What is the recommended way to create a new extension within SAP Hybris V6 ? • Use the installer script with the extgen recipe • Extensions are automatically created by the build framework based on your dependencies • Use the build framework with the extgen ant target to create a new extension from a template • Use the build framework with the extgen maven goal to create a new extension from a template Pretest - Question 7 What bean would I get when I execute spring.getBean("alias") for the following spring configuration? <alias name="bean1" alias="alias"/> <bean name="bean1" class="spring.exos.MyBean" /> <alias name="bean2" alias="alias"/> <bean name="bean2" class="spring.exos.MyBean" /> • bean1 • bean2 • alias • The configuration is wrong Pretest - Question 8 Using a standard SAP Hybris configuration, is this a valid item type definition ? <itemtype code="MyItem" abstract="false" extends="GenericItem"> </itemtype> • No, this item needs a deployment table • No, this item needs at least one attribute • No, only abstract items can extends GenericItem • No, GenericItem is a not a valid type • Yes, this is a valid item type definition 1.4. ASSESSMENT TEST 13 Pretest - Question 9 All CMS item types extend ? • GenericItem • CMS Item • CMSItem or CMSRelation • CatalogAware Pretest - Question 10 Next Generation (NG) Cockpit dynamic forms can be used to ? • Automatically generate forms for new item types • Automatically open the next tab when required data has been entered • Automatically disable a field when data is being entered • Automatically create an event when data is validated Pretest - Question 11 What are the rule engine components (select 2) ? • Promotion Module • Rule Builder • Drool Rules • Rule Processor Pretest - Question 12 What steps should you follow in order to integrate Hybris with a new payment provider? • Use Cybersource extension • Use OCP extension • Implement needed commands for the new payment provider • Configure a new command factory bean Solutions Pretest - Solution 1 1,4 are correct. 1. The database is never deleted by SAP Hybris even if the configured database user had ti right to do so. 2. SAP Hybris will delete only tables he knows from its type definition. 14 CHAPTER 1. INTRODUCTION Pretest - Solution 2 2 is correct. Keeping your configuration isolated from SAP Hybris out of the box configuration is recommenced. Pretest - Solution 3 2 is correct. Aliases need to be defined within the brackets to be attached to attributes otherwise the resolver doesn’t know about it. You can search for abstract item types within a Flexible Search Query. Pretest - Solution 4 2,4 are correct. To be able to define new item types, an extension needs to have a core module defined. To be able to add HMC configuration, an extension needs to have an HMC module defined. Pretest - Solution 5 1,2,5 are correct. The available interceptors are : • PrepareInterceptor • LoadInterceptor • RemoveInterceptor • ValidateInterceptor • InitDefaultsInterceptor Pretest - Solution 6 3 is correct. Installer script and the build framework don’t create any extensions. SAP Hybris V6.2 is using ant to generate new extensions, maven can be used to configure libraries dependencies. Pretest - Solution 7 2 is correct. The last configured alias will be used. Pretest - Solution 8 1 is correct. An item type definition can have no attribute and extends by default GenericItem, but if it extends GenericItem it musts define a deployment table for performance reasons. To ignore this set build.development.mode=false (not recommended). 1.4. ASSESSMENT TEST 15 Pretest - Solution 9 1,3 are correct. All item types extend GenericItem type. All CMS item types extend either CMSItem or CMSRelation. Pretest - Solution 10 2,3 are correct. Dynamic forms are used to add logic within a wizard or editor area. Pretest - Solution 11 2,4 are correct. The rule engine consists of two independent components : rule builder, UI where you create and maintain rules and rule templates rule processor, process and execute the rules Pretest - Solution 12 3,4 are correct. Cybersource is no longer part of the SAP Hybris commerce distribution, and is suitable only for Cybersource, not any new payment provider. You need to implement all payment actions (authorization, capture, void...) for the new payment provider and configure them within a command factory bean. 16 CHAPTER 1. INTRODUCTION Chapter 2 WCMS 17 18 CHAPTER 2. WCMS The SAP Hybris Commerce 6.2 Developer certification expects you to know the Web Content Management System (WCMS) module extensions. In this chapter, readers will learn the different CMS components and item hierachy, learn how to create new CMS components and restrictions, explore the store and website data model and learn how to create and manage warehouses and point of sales. Finally, you will learn how to create segments using the BTG extension. The WCMS module includes 3 extensions : • cms2lib (contains cms components, depends on CMS2) • cmscockpit (cms cockpit customization) • cms2 (core of the wcms, depends on basecommerce) A module is a set of extensions CMS Items hierarchy Every CMS component inherits from the ’CMSItem’ type. This type declares 3 attributes : • catalogVersion • name • uid Each CMS component may have multiple versions; default versions are Staged and Online. Hence all items within the CMS hierarchy are catalog version aware. It gives you the ability to manage any sort of contents (Medias, Pages, Slots, Nodes...) within one catalog version and then synchronize your content once it’s ready to go live. Tip 1: CMS and catalogs CMSItem and CMSRelation are managed inside a content catalog, this catalog is only a subtype of the hybris catalog and doesn’t add extra functionalities. CMSItem CMSRelation CMSItem types The uniqueness of the types CMSItem and CMSRelation is guaranteed because of a composed key between catalogVersion and UID. This also means that each item in a specific content catalog can be considered unique and therefore only exist once. 2.3. CMSITEM TYPES 19 Figure 2.1: Simplified view of the CMS Items hierarchy <itemtype code="CMSItem" extends="GenericItem"> <attributes> <attribute qualifier="catalogVersion"> <modifiers unique="true"/> </attribute> <attribute qualifier="name"> </attribute> <attribute qualifier="uid"> <modifiers unique="true"/> </attribute> </attributes> </itemtype> <itemtype code="CMSRelation" extends="GenericItem"> <attributes> <attribute qualifier="catalogVersion"> <modifiers unique="true"/> </attribute> <attribute qualifier="uid"> <modifiers unique="true"/> </attribute> </attributes> </itemtype> When do we use CMSItem type and when do we use CMSRelation? • You can use CMSItem when you create a new CMS item, for instance a new component or a new type of pages. 20 CHAPTER 2. WCMS • You can use CMSRelation when you create a new type which is not a CMS component but still needs to be catalog version aware CMS Components A CMS component is the base type of each component found on a page. A component can be as simple as a text paragraph or something more complex like a product rotating component. Each CMS component consists of three things: • an item that extends AbstractCMSComponent or one of its subtypes • a Spring MVC controller that extends AbstractCMSComponentController • a JSP (or other front end technology) to render the component Tip 2: Usage To change how customized item types are rendered within the WCMS cockpit, you need to create editorArea_myType.xml, contentEditor_myType.xml and wizardConfig_myType.xml. Base Stores and websites SAP Hybris divides a store front into two main components: • a base store • a website The following listing will give you a short overview of those two components. A website (defined through the item type CMSSite) • the content catalog • the theme • the channel (B2B or B2C) • the homepage • the URL mapping • one or more stores 2.4. CMS COMPONENTS 21 A base store (defined through the item type BaseStore) • the currencies • product catalogs • the warehouses • delivery countries • tax groups • point of services • SOLR configuration Cms component CMS components are reusable components that you use to customize your storefronts. They are easily configurable from the WCMS cockpit. Out of the box the following components exist : • Paragraph component • Banner component • Product list component • Rotating Images component You can explore the cmslib extension to find out more about the out of the box cms components. Now we will create a new CMS component to display a reminder for a order time limit that must be complied if you want the item to be delivered the following day: <typegroup name="hybhubCms"> <itemtype code="OrderDeliveryTimeLimitComponent" extends="SimpleCMSComponent" ֒→ autocreate="true" generate="true"> <description>Simple CMS component that displays a reminder of when the limit time to order for next day delivery</description> ֒→ <attributes> <attribute qualifier="message" type="localized:java.lang.String"> <persistence type="property" /> </attribute> </attributes> </itemtype> </typegroup> 22 CHAPTER 2. WCMS First create a new constant for your component in the class ControllerConstants: package com.hybhub.storefront.controllers; //All imports /** */ public interface ControllerConstants { // Constant names cannot be changed due to their usage in dependant extensions, thus nosonar ֒→ /** * Class with action name constants */ interface Actions { interface Cms // NOSONAR String _Prefix = "/view/"; // NOSONAR String _Suffix = "Controller"; // NOSONAR /** * Default CMS component controller */ String DefaultCMSComponent = _Prefix + "DefaultCMSComponentController"; // ֒→ ֒→ NOSONAR /** * CMS components that have specific handlers */ String OrderDeliveryTimeLimitComponent = _Prefix + OrderDeliveryTimeLimitComponentModel._TYPECODE + _Suffix; } } } Now we need to create our controller: 2.4. CMS COMPONENTS package com.hybhub.storefront.controllers.cms; import de.hybris.platform.servicelayer.time.TimeService; import java.text.MessageFormat; import java.util.Calendar; import java.util.Date; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import com.hybhub.core.model.OrderDeliveryTimeLimitComponentModel; import com.hybhub.storefront.controllers.ControllerConstants; @Controller("OrderDeliveryTimeLimitComponentController") @RequestMapping(value = ControllerConstants.Actions.Cms.OrderDeliveryTimeLimitComponent) public class OrderDeliveryTimeLimitComponentController extends AbstractAcceleratorCMSComponentController <OrderDeliveryTimeLimitComponentModel> { @Resource private TimeService timeService; private static final int MAX_HOUR_FOR_NEXT_DAY_DELIVERY = 16; private static final Date DATE_LIMIT; static { ֒→ ֒→ ֒→ ֒→ } DATE_LIMIT = new Calendar.Builder().set(Calendar.HOUR_OF_DAY, MAX_HOUR_FOR_NEXT_DAY_DELIVERY).set(Calendar.SECOND, 0) .build().getTime(); } @Override protected void fillModel(final HttpServletRequest request, final Model model, final OrderDeliveryTimeLimitComponentModel component) { final Calendar cal = new Calendar.Builder().setInstant(timeService.getCurrentTime()).build(); model.addAttribute("nextDayDelivery", (Boolean.valueOf(cal.get(Calendar.HOUR_OF_DAY) < MAX_HOUR_FOR_NEXT_DAY_DELIVERY))); model.addAttribute("nextDayDeliveryMessage", MessageFormat.format(component.getMessage(), DATE_LIMIT)); } 23 24 CHAPTER 2. WCMS Tip 3: CMS Controller Creating a controller is not mandatory. If you don’t do it, then the DefaultCMSComponentController will be used, it loads all attributes from the item type into your model. We need to create a JSP to display our cms component and create a new file hybhubstorefront/ web/webroot/WEB-INF/views/responsive/cms/orderdeliverytimelimitcomponent.jsp: <%@ page trimDirectiveWhitespaces="true"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> <div class="ui-front"> <p> <c:if test="${nextDayDelivery}"> ${nextDayDeliveryMessage} </c:if> <c:if test="${not nextDayDelivery}"> <fmt:message key="next.day.delivery.tooLate"/> </c:if> </div> Add this into your localization : next.day.delivery.tooLate = Too late for next delivery Run ant all, start your server and update your system, then run this (example based on the electronic store front): $contentCatalog=electronicsContentCatalog $contentCatalogName=Electronics Content Catalog $contentCV=catalogVersion(CatalogVersion.catalog(Catalog.id[default=$contentCatalog]),Catalo ⌋ gVersion.version[default=Online])[default=$contentCatalog:Online] ֒→ INSERT_UPDATE OrderDeliveryTimeLimitComponent;$contentCV[unique=true];uid[unique=true];name; ⌋ message[lang=en];&componentRef; ֒→ ;;orderDeliveryTimeLimitComponent;order Delivery Time Limit Component;"Order before {0} to ֒→ receive your order tomorrow.";orderDeliveryTimeLimitComponent; INSERT_UPDATE ContentSlot;$contentCV[unique=true];uid[unique=true];name;active;cmsComponents ⌋ ֒→ (&componentRef) ;;Section1Slot-Homepage;Section1 Slot for Homepage;true;orderDeliveryTimeLimitComponent 25 2.5. RESTRICTIONS Tip 4: Usage You should now see your new CMS component under your home page. Restrictions Warning! Do not get flexible search query restrictions mixed up with CMS restrictions ! One is only adding filters in the FSQL WHERE clause while the other has its own java implementation. Restrictions can be applied to any CMSItem. They use a Java implementation to filter visible or hidden items. 26 CHAPTER 2. WCMS For example the catalog restriction evaluator: package de.hybris.platform.cms2.servicelayer.services.evaluator.impl; import import import import import de.hybris.platform.catalog.model.CatalogModel; de.hybris.platform.cms2.model.restrictions.CMSCatalogRestrictionModel; de.hybris.platform.cms2.servicelayer.data.RestrictionData; de.hybris.platform.cms2.servicelayer.services.evaluator.CMSRestrictionEvaluator; org.apache.log4j.Logger; public class CMSCatalogRestrictionEvaluator implements ֒→ CMSRestrictionEvaluator<CMSCatalogRestrictionModel> { private static final Logger LOG = Logger.getLogger(CMSCatalogRestrictionEvaluator.class); public CMSCatalogRestrictionEvaluator() { } ֒→ ֒→ public boolean evaluate(CMSCatalogRestrictionModel catalogaRestrictionModel, RestrictionData context) { CatalogModel catalogModel = null; if(context == null) { return true; } else { if(context.hasProduct()) { catalogModel = context.getProduct().getCatalogVersion().getCatalog(); } else if(context.hasCategory()) { catalogModel = context.getCategory().getCatalogVersion().getCatalog(); } else { if(!context.hasCatalog()) { LOG.warn("Could not evaluate CMSCatalogRestriction. RestrictionData contains neither a catalog, a category or a product. Returning false."); return false; } catalogModel = context.getCatalog(); } return catalogaRestrictionModel.getCatalogs().contains(catalogModel); } } } Warning! Each restriction evaluator needs to implement de.hybris.platform.cms2.servicelayer.services.evaluator.CMSRestrictionEvaluator. 2.6. PERSONALIZATION 27 A restriction also needs an item type definition: <itemtype code="CMSCatalogRestriction" ֒→ jaloclass="de.hybris.platform.cms2.jalo.restrictions.CatalogRestriction" extends="AbstractRestriction" autocreate="true" generate="true"> <attributes> <attribute qualifier="description" type="java.lang.String" redeclare="true"> <persistence type="dynamic" ֒→ attributeHandler="catalogRestrictionDynamicDescription" /> <modifiers write="false" /> </attribute> </attributes> </itemtype> Personalization The BTG (Behavioral Targeting Group) extension provides the AP module (Advanced Personalization). When the module is activated it allows you to customize which CMSItem to render for a given customer or to add another CMSItem for a given customer. SAP Hybris customer segmentation groups your customers into multiple subset and activation actions. Segmentation input rules Out of the box, you have access to the following pre-configured segmentation rules: Cart Rules • cart is empty • total sum of cart • categories of products in cart • products in cart • size of the cart • quantity of one product in cart Order rules • categories of product in last orders • total sum of all orders • total order value • products in last orders • number of orders in a date range • last order date 28 CHAPTER 2. WCMS Customer rules • belongs to a group • gender • country • postal code Website rules • has viewed products • has viewed categories • has viewed content page • referral URL match • URL contains parameter Segmentation output actions When a user successfully passes each rule of a segment, an output action is executed. Actions can be: • assign a user to a group (used for post rules) • actions (modify the visibility of cms components) Result scope When a segment is fulfilled, the result can be stored in two different scopes: • session scope (only available option for anonymous users) • permanent scope, segment rule results are stored for the user (anonymous users or authenticated users) Evaluation method There are two ways to evaluate rules: • Optimized processing, a segment evaluation would stop as soon as one rule is not fulfilled • Full processing, all rules are evaluated Warning! Performances are heavily impacted by full processing, however when running optimized processing your reports don’t contain all rule evaluation results. 2.7. WCMS COCKPIT 29 Create a CMS segment Example from the electronics store default segmentation: ################################################################## # Configuration 1: Regular Customer Segment ################################################################## INSERT_UPDATE BTGSegment;uid[unique=true];name;sites(uid);active[default=true];scope(code)[d ⌋ efault=ONLINE];$contentCatalogVersion ֒→ ;electronicsRegularCustomerSegment;Regular Customer Segment;electronics INSERT_UPDATE BTGRule;uid[unique=true];code;segment(uid, ֒→ $contentCatalogVersion);ruleType(code);$contentCatalogVersion ;electronicsRegularCustomerRule;Regular Customer Rule;electronicsRegularCustomerSegment;ORDER INSERT_UPDATE BTGOperator;uid[unique=true];code;$contentCatalogVersion ;electronicsGreaterOperator;numGreaterThanOrEqual INSERT_UPDATE BTGNumberOfOrdersRelativeDateOperand;uid[unique=true];value;unit(code);$conten ⌋ tCatalogVersion ֒→ ;electronicsNumberOfOrdersOperand;6;MONTH INSERT_UPDATE BTGIntegerLiteralOperand;uid[unique=true];literal;$contentCatalogVersion ;electronicsOrdersIntegerOperand;4 INSERT_UPDATE BTGExpression;uid[unique=true];leftOperand(uid, $contentCatalogVersion);rightOperand(uid, $contentCatalogVersion);operator(uid, ֒→ $contentCatalogVersion);rule(uid, $contentCatalogVersion);$contentCatalogVersion ֒→ ;electronicsRegularCustomerExpression;electronicsNumberOfOrdersOperand;electronicsOrdersInte ⌋ ֒→ gerOperand;electronicsGreaterOperator;electronicsRegularCustomerRule INSERT_UPDATE ֒→ BTGAssignToGroupDefinition;uid[unique=true];code;target;userGroups(uid);segment(uid, ֒→ $contentCatalogVersion);$contentCatalogVersion ;electronicsBtgAssignToRegularGroup;Add to Regular ֒→ Segment;assignToGroup;regulargroup;electronicsRegularCustomerSegment WCMS cockpit SAP Hybris end users can use the WCMS (Web Content Management System) cockpit (based on the legacy cockpit framework, hopefully future versions will include a revamped WCMS cockpit using the next generation cockpit framework). It provides an easy way for end users to manage web content, such as: • manage pages • manage cms components • synchronize components and pages between different catalogs • manage navigation nodes • manage advanced personalization (if BTG cockpit is activated) 30 CHAPTER 2. WCMS Point of services A point of service can be one of the following types: • store • warehouse A point of service is used to attached a physical facility to an SAP Hybris model. Out of the box, you only have stores and warehouses, but it is possible to extend this by creating new enumeration values. To do so add this to your custom extension -items.xml file: <enumtypes> <enumtype code="PointOfServiceTypeEnum" autocreate="false" generate="false"> <value code="SERVICEDESK" /> </enumtype> </enumtypes> A point of service defines : • an address • a longitude • a latitude • an employee group attached to this point of service • hours of services Tip 5: POS POS is an abbreviation for point of service. Warehouse Warehouses (here we are talking about point of services of warehouse type) are used to attach an adress to an exisiting warehouse. Warning! Don’t get point of service of type warehouse and warehouse mixed up! 31 2.9. WAREHOUSE To create a new point of service of type warehouse: INSERT_UPDATE PointOfService;name[unique=true];displayName;warehouses(code);address(&addrID);latitude ⌋ ֒→ ֒→ ;longitude;geocodeTimestamp[dateformat=dd.MM.yyyy];type(code)[default=WAREHOUSE] ;warehouse_s;Warehouse South;warehouse_s;warehouse_s;33,5933317;130,3515247;11.12.2025 Store Stores are an essential component for the click and collect functionality, customers want to know if a given product is available at the physical store and if it is they might also want to pick the product directly there. Stores are also a way for you to publish your stores opening hours and few extra informations. To create a new point of service of type store: INSERT_UPDATE PointOfService;name[unique=true];address(&addrID);latitude;longitude;geocodeTi ⌋ mestamp[dateformat=dd-MM-yyyy];openingSchedule(code);basestore(uid)[default=$storeUid]; ⌋ ֒→ features(code);type(code)[default=STORE] ֒→ ;WSI-Nakano;WSI-Nakano;35,6894875;139,6917064;29-04-2011;$standardHours Warehouse Figure 2.2: Warehouse item type 32 CHAPTER 2. WCMS Warehouses are attached to one or more stores, and to stock levels and consignments. They are used by the stock level service to return a stock level status for a given product and a given base store. To define a new warehouse: $vendor=electro # Create some Warehouses for the Vendor INSERT_UPDATE ֒→ Warehouse;code[unique=true];vendor(code)[default=$vendor];default[default=’false’] ;Nakano To define a new stock level: INSERT_UPDATE StockLevel;available;warehouse(code)[unique=true];inStockStatus(code);maxPreOr ⌋ ֒→ der;maxStockLevelHistoryCount;overSelling;preOrder;productCode[unique=true];reserved ;6;warehouse_1;notSpecified;0;-1;0;0;product1;0 Example on how to call the stock level service (if the out-of-the-box electronic store is available, this should print "inStock"): def def def def commerceStockService = spring.getBean("commerceStockService") baseStoreDao = spring.getBean("baseStoreDao") catalogService = spring.getBean("catalogService") productService = spring.getBean("productService") def elecStore = baseStoreDao.findBaseStoresByUid("electronics").get(0) def elecCatalog = catalogService.getCatalogVersion("electronicsProductCatalog", "Online") def product = productService.getProductForCode(elecCatalog, "5897548") def stockLevel = commerceStockService .getStockLevelStatusForProductAndBaseStore(product, elecStore) println stockLevel Chapter 3 Backoffice 33 34 CHAPTER 3. BACKOFFICE The backoffice (or next generation cockpit) replaces the cockpit framework. In future versions, all existing cockpits (hmc, cs, wcms...) will be replaced by a new one using the backoffice framework. The key feature of the backoffice framework is the concept of widget. Each widget is an independent component that can be reused and connected to other widgets. An application orchestrator allows you to build a new backoffice from the current backoffice itself. Create a new Backoffice application To create a new backoffice application run the ant target ant extgen and select the template ybackoffice. Then, follow the instructions and select a name, a package and ask the template to create a sample widget. A backoffice extension has : • backoffice configuration inside its extensioninfo.xml <meta key="backoffice-module" value="true" /> Any backoffice extension has a backoffice folder with the following structure: • src folder for your backoffice components code (for example, the controllers), • resources/cng for all static files, • resources/widgets. Each widget has its own directory with a definition.xml file, a zul a template, a css style sheet folders for images an localization. The widgets folder may also contain sub directories for actions and editors. Tip 6: Usage For more details, generate a new custom extension using the extgen ant target and use the ybackoffice template (don’t forget to generate an example widget). The backoffice has one shared configuration file where users have different business roles. They would see different perspectives and have different rights. The admin can see all perspectives and reorganizes them from the orchestrator. To configure a new perspective, you need to edit file hybhubbackoffice/resources/hybhubbackoffice-backoffice-config.xml (where hybhubbackoffice is the name of my custom extension built from the ybackoffice template). Tip 7: Usage During backoffice development activate the following property: backoffice.cockpitng.hotDeployment.enabled=true to be able to reload the backoffice application without restarting your server. Run ant build and click on redeploy from the backoffice admin perspective. 3.2. CREATE AND CONNECT A NEW WIDGET 35 To create a new backoffice perspective, you need to : 1. create a new BackofficeRole group; 2. create a new UserGroup member of the new BackofficeRole group and the employeegroup group; 3. create users member of the new group; 4. update your backoffice configuration, add a new perspective by configuring the principal attribute of contexts to your new backoffice role. Create and connect a new widget Tip 8: To speed up widgets development deactivate all cache backoffice.cockpitng.additionalResourceLoader.enabled=true backoffice.cockpitng.uifactory.cache.enabled=false backoffice.cockpitng.widgetclassloader.resourcecache.enabled=false backoffice.cockpitng.resourceloader.resourcecache.enabled=false 36 CHAPTER 3. BACKOFFICE We will Create a widget to filter reviews by status, to do so create a new folder customerreviewselectorbackofficewidget inside your custom backoffice extension (the one created with the ybackoffice template). This folder will contain our widget configuration, create a file definition.xml with the following content : <widget-definition xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.hybris.com/schema/cockpitng/widget-definit ⌋ ion.xsd" ֒→ id="com.hybhub.backoffice.widgets.customerreviewselectorbackofficewidget"> <name>Customer Review Selector</name> <description>Customer Review Selector - Select pending reviews, denied reviews or ֒→ approved reviews </description> <defaultTitle>Customer Review Selector</defaultTitle> <author>Hybhub</author> <version>1.0</version> <view src="customerreviewselectorwidget.zul" /> <keywords> <keyword>customerreviewselector</keyword> </keywords> <controller class="com.hybhub.backoffice.widgets.CustomerreviewbackofficeController" ֒→ /> <settings> <setting key="type" type="ENUM(Pending,Approved,Rejected)"/> <setting key="name" type="String"/> </settings> <sockets> <output type="com.hybris.cockpitng.search.data.pageable.Pageable" ֒→ id="reviews"/> </sockets> </widget-definition> 3.2. CREATE AND CONNECT A NEW WIDGET And a view hybhubbackofficewidget.zul in the same folder with the following content : <?xml version="1.0" encoding="UTF-8"?> <widget xmlns="http://www.zkoss.org/2005/zul" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:w="http://www.zkoss.org/2005/zk/client" xmlns:zk="http://www.zkoss.org/2005/zk" xsi:schemaLocation="http://www.zkoss.org/2005/zul ֒→ http://www.hybris.com/schema/cockpitng/zul/zul.xsd" height="100%"> <div height="100%" style="background: #ccc;"> <div> <button id="reviews" label="Pending Reviews"/> </div> <widgetchildren slotID="additionalArea" id="additionalArea" type="tab" ֒→ width="100%" height="100%"/> </div> </widget> 37 38 CHAPTER 3. BACKOFFICE Create a controller inside your backoffice extension backoffice/src folder : package com.hybhub.backoffice.widgets; import de.hybris.platform.customerreview.enums.CustomerReviewApprovalType; import de.hybris.platform.customerreview.model.CustomerReviewModel; import import import import org.zkoss.zk.ui.Component; org.zkoss.zk.ui.event.Events; org.zkoss.zk.ui.select.annotation.WireVariable; org.zkoss.zul.Button; import import import import com.hybhub.services.CustomerReviewSearchService; com.hybris.cockpitng.annotations.ViewEvent; com.hybris.cockpitng.search.data.pageable.PageableList; com.hybris.cockpitng.util.DefaultWidgetController; public class CustomerreviewbackofficeController extends DefaultWidgetController { private Button reviews; @WireVariable private CustomerReviewSearchService customerReviewSearchService; @Override public void initialize(final Component comp) { super.initialize(comp); reviews.setLabel(this.getWidgetSettings().getString("name")); } @ViewEvent(componentID = "reviews", eventName = Events.ON_CLICK) public void sendReviews() { final CustomerReviewApprovalType approvalType = CustomerReviewApprovalType .valueOf(this.getWidgetSettings().getString("type")); ֒→ sendOutput("reviews", new PageableList<CustomerReviewModel>(customerReviewSe ⌋ archService.getAllCustomerReviews(approvalType), 10, CustomerReviewModel._TYPECODE)); } } Widget application context The widgets have their own spring application context (child of the web application context) with auto-reload capabilities during runtime. 3.3. DYNAMIC FORM 39 Tip 9: Usage Remember that in order to override a bean you need to load your extensions in the correct order using extension dependencies within the extensioninfo.xml. In order to load a bean from a widget controller you can use: @WireVariable private UserService userService; If you need to load a spring bean outside of a widget controller use: com.hybris.cockpitng.util.BackofficeSpringUtil.getBean(String name, Class<? extends C> clz) Dynamic form Dynamic form is a new feature for backoffice applications. They bring more control and interactivity over the data entered from the backoffice. They can be configured directly from the backoffice itself or from the backoffice configuration files (myextension/resources/myextension-backofficeconfig.xml). Dynamic forms can perform action on attributes, sections and tabs. You can write action in Groovy, Bean Shell, Javascript and spel. To customize dynamic forms directly from the Backoffice, access the application orchestrator (press f4), then select show cockpit-config.xml from the top right corner. Filter components with editorAreaDynamicForms. Tip 10: Dynamic forms Dynamic forms can execute actions on attributes, sections and tabs. Example how to update the approval status Here we would like a product to have a disaproved status whenever somebody update any model attribute (this shouldn’t be applied for the user responsible for approving products). To do so, we customize one of the -backoffice-config.xml file or we update the cockpit-config.xml directly from the ordchestrator. 40 CHAPTER 3. BACKOFFICE <context type="Product" component="editorAreaDynamicForms" merge-by="module"> <df:dynamicForms xmlns:df="http://www.hybris.com/cockpitng/component/dynamicForms"> <df:attribute id="uniqueIdForTheAction" triggeredOn="}" ֒→ qualifier="approvalStatus" computedValue="T(de.hybris.platform.catalog.enums.ArticleApprovalStatus).UNAPPROVED" /> </df:dynamicForms> </context> ֒→ • triggeredOn="*", get triggered for any change on the model • qualifier="approvalStatus", the targeted attribute • computedValue=...., the value of the targeted attribute Warning! Now every time you update a product, the approval status is automatically set to unapproved. ArticleApprovalStatus is used for products approval status on Hybris 6.2, this is confusing but the enum name has no impacts. Backoffice and tenants Out of the box tenant selection is not available for backoffice applications. Here is how you can add a junit context path for the backoffice: Create a file named : local_tenant_junit.properties under your configuration folder, it should contain : backoffice.webroot=/backoffice_junit Create a file for customization inside your config folder customize/ext-backoffice/backoffice/web/webroot/WEB-INF/backoffice-spring-filter.xml. Copy the content of the original file and update the backofficeFilterChain bean. We want to use the dynamicTenantActivationFilter instead of the tenantActivationFilter): 3.4. BACKOFFICE AND TENANTS 41 <bean id="backofficeFilterChain" ֒→ class="de.hybris.platform.servicelayer.web.PlatformFilterChain"> <constructor-arg> <list> <ref bean="log4jFilter"/> <ref bean="dynamicTenantActivationFilter"/> <ref bean="backofficeRedirectFilter"/> <ref bean="sessionFilter"/> <ref bean="backofficeDataSourceSwitchingFilter"/> <ref bean="backofficeCatalogVersionActivationFilter"/> <ref bean="backofficeContextClassloaderFilter"/> <ref bean="backofficeSecureMediaFilter" /> </list> </constructor-arg> 1. Execute ant clean all customize. 2. Check that in bin/platform/tomcat/conf/server.xml you now have a new context backoffice_junit. 3. Start your server, you can now access the backoffice application for master and junit tenant. 42 CHAPTER 3. BACKOFFICE Chapter 4 Cockpit 43 44 CHAPTER 4. COCKPIT Sap Hybris has a cockpit framework used to build backend applications. The framework is based on ZK a RIA framework (rich internet application). ZK has a large set of UI components that can be used across all backend applications to speed up the development. The cockpit framework is provided by the cockpit extension. The out-of-the-box available cockpits are: • Customer Service Cockpit (order and customer management); • Hybris Management Console (administration cockpit); • Import Cockpit (import data from CSV files); • Hybris Print Cockpit (export your catalog for printing); • Hybris Product Cockpit (manafe your products); • Report Cockpit (create and view dashboards); • WCMS Cockpit (web content management cockpit). Warning! The legacy cockpit framework is slowly being removed by the next generation cockpit framework, but it won’t be done before mid 2018, for more information have a look at the deprecation status page on SAP Hybris wiki. Therefore it’s really important to know how to work with the cockpit framework. Create a new cockpit To create a new cockpit, use the available template ycockpit with the extgen ant target. For example: {12:02}~/bin/platform:master -> ant extgen ... ycockpit ... hybhubcockpit ... com.hybhub.cockpit Then, add the new extension into your localextensions.xml file (here hybhubcockpit). Compile and start your server. Your new cockpit is available at http://localhost:9001/hybhubcockpit. By default, you will have one perspective for Product models. 4.2. COCKPIT MODIFICATION 45 You can easily add new perspectives by adding a bean reference into the availablePerspectives property of the UICockpitSession bean (open the file resources/extensionName/extensionNameweb-spring.xml): <property name="availablePerspectives"> <list> <ref bean="HybhubcockpitPerspective"/> <ref bean="HybhubcockpitMediaPerspective" /> <!-- Custom perspective for ֒→ Medias --> </list> </property> <bean id="HybhubcockpitMediaPerspective" scope="session" parent="BasePerspective"> <property name="uid" value="hybhubcockpit.perspective.hybhubmediacockpit" /> <property name="label" value="perspective.hybhubmediacockpit" /> <property name="customCsaURI" value="/hybhubcockpit/hybhubcockpitCSA.zul" /> <property name="navigationArea"> <ref bean="HybhubcockpitNavigationArea" /> </property> <property name="browserArea"> <bean class="com.hybhub.cockpit.session.impl.HybhubcockpitBrowserArea"> <property name="rootSearchTypeCode" value="Media"/> <property name="viewURI" value="/cockpit/baseSearchBrowserArea.zul"/> </bean> </property> <property name="editorArea"> <bean id="HybhubcockpitMediaEditorAreaModel" parent="BaseEditorAreaModel"> <property name="editorAreaController"> <ref bean="HybhubcockpitEditorAreaController"/> </property> </bean> </property> <property name="popupEditorArea"> <bean id="HybhubcockpitMediaPopupEditor" parent="BasePopupEditor"> <property name="editorAreaController"> <ref bean="HybhubcockpitPopupEditorAreaController"/> </property> </bean> </property> <property name="cockpitTypeService" ref="cockpitTypeService"/> </bean> Cockpit modification Within a cockpit, you have different key elements: • Navigation area, where can find tree views, links, history... • Browser area, where you see a list or grid of elements. • Editor area, displays an item and provides the option to edit it. 46 CHAPTER 4. COCKPIT To import cockpit configuration you have two choices: • automatically if you respect the out of the box naming convention; • manually (not recommended). To automatically import cockpit configuration, the convention requires: • a folder resources/<extensionName>-config • the main folder cannot be empty and must contain at least: – a XML configuration that affects all users and user groups – a folder with XML configuration inside. The folder name needs to be user id or a group id (validated during the import). It can contain only XML files and no subfolders; inside this folder you have configuration files: ∗ they must match this pattern contextID_ObjectTemplateCode.xml ∗ available context IDs are defined within the bean ctxID2FactoryMappings ∗ ObjectTemplateCode are item types and are validated by the type service. Chapter 5 Commerce and Accelerator 47 48 CHAPTER 5. COMMERCE AND ACCELERATOR Create a new addon Addons are used to extend the functionalities of SAP Hybris platform without changing its source code. They are standard extensions and are loaded during the build phase. Therefore, they can be used to customize item types using their own items.xml file or beans using their own beans.xml file. From an addon you can do the following: • add frontend files (JSP, HTML, CSS and Javascript); • Generate or customize Facade transferred objects (DTO); • Generate or customize the data model; • Declare new Spring Services or Facades or customize existing; • Declare new Controllers or customize existing; • Declare new Converters or populators or customize existing. An extension is an addon when it requires the addonsupport extension and when the targeted extension requires the given addon. 5.1. CREATE A NEW ADDON 49 First you need to generate a new extension based on the yaddon template. I called my extension hybhubaddon and chose com.hybhub.hybhubaddon as a package name: Buildfile: ֒→ /Users/j.doe/Workspace/workspace-perso/hybris/hybris-6.2.0/hybris/bin/platform/build.xml [echo] /Workspace/hybris/hybris-6.2.0/hybris/bin/platform/tomcat/bin ..... [echo] ----[echo] hybris Platform Environment [echo] ----[echo] OS family: unix [echo] Java platform: Java(TM) SE Runtime Environment, 1.8.0_60-b27 [echo] Java compiler: org.eclipse.jdt.core.JDTCompilerAdapter 1.8 [echo] Build target: [echo] Compilation mode: strict [echo] hybris Platform directory: /Workspace/hybris/hybris-6.0.0/hybris/bin/platform 6.0.0.0-SNAPSHOT [echo] hybris Platform version: [echo] Ant version: Apache Ant(TM) version 1.9.1 compiled on May 15 2013 [echo] Ant memory settings: -Xmx512m -Dfile.encoding=UTF-8 [echo] ----..... extgen: [input] [input] Please choose a template for generation. [input] Press [Enter] to use the default value (ywebservices, ysmarteditmodule, yhacext, [yempty], ycmssmartedit, yatddtests, yscala, ygroovy, ycockpit, yoccaddon, yaddon, ֒→ ycommercewebservices, ycommercewebservicestest, ycommercewebserviceshmc, ֒→ ֒→ ychinaacceleratorstorefront, yacceleratorordermanagement, yacceleratorstorefront, yacceleratorfractusfulfilmentprocess, yacceleratorfulfilmentprocess, ybackoffice) ֒→ yaddon [input] [input] Please choose the name of your extension. It has to start with a letter followed by letters and/or numbers. ֒→ [input] Press [Enter] to use the default value [training] hybhubaddon [input] [input] Please choose the package name of your extension. It has to fulfill java package ֒→ name convention. [input] Press [Enter] to use the default value [org.training] com.hybhub.hybhubaddon ....... [echo] Next steps: [echo] [echo] 1) Add your extension to your ֒→ /Workspace/hybris/hybris-6.0.0/hybris/config/localextensions.xml [echo] <extension [echo] ֒→ dir="/Workspace/hybris/hybris-6.0.0/hybris/bin/custom/hybhubaddon"/> [echo] [echo] 2) Please remove all template extensions (again) before you proceed. [echo] [echo] 3) Make sure the applicationserver is stopped before you build the extension the first time. ֒→ [echo] [echo] 4) Perform ’ant’ in your hybris/platform directory. [echo] [echo] 5) Restart the applicationserver [echo] [echo] BUILD SUCCESSFUL Total time: 29 seconds 50 CHAPTER 5. COMMERCE AND ACCELERATOR After adding your addon to your eclipse environment and to your localextensions.xml file, you will notice that it looks like any other extension except for the acceleratoraddon folder. This folder structure is a copy of the usual structure of a frontend extension. Figure 5.1: Addon accelerator folder structure Next step is to install your addon into the designated store front extension. For this, we use ant: ant addoninstall -Daddonnames="AddOnName1,AddOnName2" ֒→ -DaddonStorefront.yacceleratorstorefront="B2CStorefront1,B2CStorefront2" ֒→ -DaddonStorefront.yb2bacceleratorstorefront="B2BStorefront1,B2BStorefront2" 5.1. CREATE A NEW ADDON 51 In my case my addon’s name is hybhubaddon and the storefront extension I’m using is yacceleratorstorefront so I need to execute : ant addoninstall -Daddonnames="hybhubaddon" ֒→ -DaddonStorefront.yacceleratorstorefront="yacceleratorstorefront" ..... addoninstall: [echo] Storefront templates found in command line : yacceleratorstorefront, [delete] Deleting: /Workspace/hybris/hybris-6.0.0/hybris/bin/platform/null2070331373 [echo] AddOn name list: hybhubaddon [echo] ------------------Instaling addon : hybhubaddon--------------[echo] Generate project.properties file from template ֒→ /Workspace/hybris/hybris-6.0.0/hybris/bin/custom/hybhubaddon/project.properties.template [copy] Copying 1 file to /Workspace/hybris/hybris-6.0.0/hybris/bin/custom/hybhubaddon [echo] Add additionalWebSpringConfigs line for storefrontTemplate ֒→ ’yacceleratorstorefront’ and storefronts ’yacceleratorstorefront’ [propertyfile] Updating property file: ֒→ /Workspace/hybris/hybris-6.0.0/hybris/bin/custom/hybhubaddon/project.properties [echo] Adding addon ’hybhubaddon’ to extensioninfo.xml for ’yacceleratorstorefront’ [echo] Adding addon ’hybhubaddon’ to addons.less for ’yacceleratorstorefront’ BUILD SUCCESSFUL Total time: 2 seconds The addoninstall target does a couple of things: • Generate a new project.properties file from the project.properties.template inside your addon. • Configure the hybhubaddon web spring configuration hybhubaddonweb-spring.xml into yacceleratorstorefront.additionalWebSpringConfigs • Add hybhubaddon into extensioninfo.xml for yacceleratorstorefront • Add hybhubaddon to addons.less for yacceleratorstorefront If you need to uninstall an Addon, you can use: ant addonuninstall -Daddonnames="hybhubaddon" ֒→ -DaddonStorefront.yacceleratorstorefront="yacceleratorstorefront" ..... addonuninstall: [echo] Storefront templates found in command line : yacceleratorstorefront, [delete] Deleting: /Workspace/hybris/hybris-6.0.0/hybris/bin/platform/null583120343 [echo] AddOn name list: hybhubaddon [echo] ------------------Uninstaling addon : hybhubaddon--------------[echo] Removing addon ’hybhubaddon’ from extensioninfo.xml for ’yacceleratorstorefront’ [echo] Removing addon ’hybhubaddon’ from addons.less for ’yacceleratorstorefront’ BUILD SUCCESSFUL Total time: 2 seconds 52 CHAPTER 5. COMMERCE AND ACCELERATOR Your new Addon extension has few files under its acceleratoraddon folder: • acceleratoraddon/web/webroot/_ui/responsive/common/css/hybhubaddon.css • acceleratoraddon/web/webroot/WEB-INF/_ui-src/responsive/less/hybhubaddon.less Create a new JSP file under the folder acceleratoraddon/web/webroot/WEB-INF/views/responsive/pages/layout, hybhubAddonLayout.jsp: <%@ page trimDirectiveWhitespaces="true"%> <%@ taglib prefix="template" tagdir="/WEB-INF/tags/responsive/template"%> <template:page pageTitle="${pageTitle}"> <h1>From Hybhub Addon</h1> </template:page> Create a new controller under the package com.hybhub.hybhubaddon.controllers.pages (you may need to add the folder acceleratoraddon/web/src under your java build path source): package com.hybhub.hybhubaddon.controllers.pages; import de.hybris.platform.addonsupport.controllers.page.AbstractAddOnPageController; import de.hybris.platform.cms2.exceptions.CMSItemNotFoundException; import de.hybris.platform.cms2.model.pages.ContentPageModel; import import import import org.springframework.stereotype.Controller; org.springframework.ui.Model; org.springframework.web.bind.annotation.RequestMapping; org.springframework.web.bind.annotation.RequestMethod; @Controller @RequestMapping(value = "/hybhub") public class HybHubAddonPageController extends AbstractAddOnPageController { @RequestMapping(method = RequestMethod.GET) public String hybhub(final Model model) throws CMSItemNotFoundException { final ContentPageModel page = getContentPageForLabelOrId(null); storeCmsPageInModel(model, page); setUpMetaDataForContentPage(model, page); return "addon:/hybhubaddon/pages/layout/hybhubAddonLayout"; } } 53 5.2. OCC WEBSERVICES Update the file hybhubaddon/resources/hybhubaddon/web/spring/hybhubaddon-web-spring.xml to add a context component-scan element: <context:annotation-config/> <context:component-scan base-package="com.hybhub.hybhubaddon.controllers"/> Build your platform and start your server, now when you access https://localhost:9002/yacceleratorstorefront/ electronics/en/hybhub?site=electronics you should see : Figure 5.2: Hybhub Addon Page Tip 11: Addons What have we done? We have customized the store front extension without changing its sources. We could easily inject our changes on any other project or future Hybris releases. OCC Webservices The Omni Commerce Connect module is a set of API that expose services and data from Hybris. All webservices are Restful web services and support both JSON and XML format. Web services are build with the Spring MVC framework, using controllers, facades and converters. 54 CHAPTER 5. COMMERCE AND ACCELERATOR The OCC module contains the following extensions: • commercewebservicescommons • ycommercewebservices Authentication uses the OAuth2 framework. Object can be cached using the Ehcache, cache is configured for each controller and method with the @Cacheable annotation • ycommercewebserviceshmc • ycommercewebservicestest Tip 12: Extension templates Remember that all extensions starting with a y are templates. For example to add new webservices or to customize existing one, you need to generate a new commerce web service extensions from the ycommercewebservices template. Currently 2 versions of the web services coexists: 1. V1 (/v1/) which is the old version released with Hybris V5.1. The key features of version 1 are: • Stateful • Response format (XML or JSON). 2. V2 (/v2/) which is the current version of the web services released with Hybris 5.4. The key features of version 2 are: • Stateless; • Restful • Data creation. Type of requests: • GET a request to get data • POST a request to create data • PUT a request to update data • HEAD a request to get information about the data (number of elements for example) Tip 13: auth access Remember that OCC doesn’t use normal users. It has its own data type to manage credentials. Have a look at OAuthClientDetails. 5.2. OCC WEBSERVICES <itemtype code="OAuthClientDetails" generate="true" autocreate="true" > <deployment table="OAuthClientDetails" typecode="6500" /> ֒→ <attributes> <attribute qualifier="clientId" type="java.lang.String"> <description>Client Id</description> <modifiers unique="true" optional="false" initial="true" write="false" /> <persistence type="property" /> </attribute> <attribute qualifier="resourceIds" type="StringSet"> <description>Set of Resource Id’s</description> <persistence type="property" /> </attribute> <attribute qualifier="clientSecret" type="java.lang.String"> <description>Client Secret</description> <persistence type="property" /> </attribute> <attribute qualifier="scope" type="StringSet"> <description>Set of client scopes</description> <persistence type="property" /> </attribute> <attribute qualifier="authorizedGrantTypes" type="StringSet"> <description>Set of grant types for client</description> <persistence type="property" /> </attribute> <attribute qualifier="registeredRedirectUri" type="StringSet"> <description>Set of redirect Uri for client</description> <persistence type="property" /> </attribute> <attribute qualifier="authorities" type="StringSet"> <description>Set of authorities granted to client</description> <persistence type="property" /> </attribute> <attribute qualifier="accessTokenValiditySeconds" type="java.lang.Integer"> <description>Set of authorities granted to client</description> <persistence type="property" /> </attribute> <attribute qualifier="refreshTokenValiditySeconds" type="java.lang.Integer"> <description>Set of authorities granted to client</description> <persistence type="property" /> </attribute> <attribute qualifier="autoApprove" type="StringSet"> <description>Set of auto approve scopes of client</description> <persistence type="property" /> </attribute> </attributes> <indexes> <index name="clientIdIdx" unique="true"> <key attribute="clientId" /> </index> </indexes> </itemtype> 55 56 CHAPTER 5. COMMERCE AND ACCELERATOR Install OCC To activate and use the Omni Channel Connect module, you need to have these extensions activated: • ycommercewebservices; • ycommercewebserviceshmc; • ycommercewebservicestest; • commercewebservicescommons; • webservicescommonsbackoffice; • cmswebservices. Warning! If you have created your own web services extensions from the y templates (using modulegen with commercewebservices), then delete the reference to the web services templates from your localextensions.xml file. Once you have updated your Hybris installation with the required extensions, you will be able to manage the OAuthClientDetails and see the current OAuthAccessToken from the backoffice. Figure 5.3: Backoffice OAuth OAUTH 2 Hybris web services authorization mechanism is based on (https://oauth.net/2/) which is the industry standard protocol. Each call to the OCC web services have to go through a complex authorization mechanism. First the user will be authenticated (verifying credentials); then the user is authorized (decide whether or not the user can perform a given action). Authorization happens within the HTTP layer and the Service layer. Each of them applies its own set of verification. 57 5.2. OCC WEBSERVICES OCC user roles Authentification type Anonymous Client Customers Guest Role A non-authenticated principal is assigned a built-in ANONYMOUS role by default. Every client application that was authenticated using an OAuth2 token in the client credentials flow is assigned a specific role depending on the client definition. When defining the clients remember to assign either the ROLE_CLIENT or ROLE_TRUSTED_CLIENT to them, because these roles allow client access to the ycommercewebservices extension Users who were authenticated using the OAuth2 token in the password flow, are assigned a list of roles that are received from a service layer in the same way as it works in the whole application. By default, CUSTOMERGROUP and CUSTOMERMANAGERGROUP roles are used. Anonymous users who provided their own e-mail address. It can be done by calling /customers/current/guestlogin in v1 or /users/anonymous/carts/guid/email in v2. For such users, a built-in GUEST role is assigned. How to get a token ? First you need to have OAuth clients configured. You can check this from the backoffice (System -> OAuth -> OAuth Clients). If you don’t have any, you can import the one provided with the cmswebservices extension inside the impex file sampledata-oauthclients.impex. INSERT_UPDATE OAuthClientDetails;clientId[unique=true];resourceIds;scope;authorizedGrantType ⌋ s;authorities;clientSecret;registeredRedirectUri ֒→ ;client-side;hybris;basic;implicit,client_credentials;ROLE_CLIENT;secret;http://localhost:90 ⌋ 01/authorizationserver/oauth2_implicit_callback; ֒→ ;mobile_android;hybris;basic;authorization_code,refresh_token,password,client_credentials;RO ⌋ LE_CLIENT;secret;http://localhost:9001/authorizationserver/oauth2_callback; ֒→ ;trusted_client;hybris;extended;authorization_code,refresh_token,password,client_credentials ⌋ ֒→ ;ROLE_TRUSTED_CLIENT;secret;; 58 CHAPTER 5. COMMERCE AND ACCELERATOR Curl curl -k -X POST https://localhost:9002/authorizationserver/oauth/token\?client_id\=trusted_c ⌋ ֒→ lient&client_secret\=secret&grant_type=client_credentials { "access_token" : "16582579-a158-4c31-ad3a-438c9e587dae", "token_type" : "bearer", "scope" : "extended", "expires_in" : 41780 }% Tip 14: Usage -k: Allow connections to SSL sites without certs -X POST : –request COMMAND Specify request command to use Postman Post Request: https://localhost:9002/authorizationserver/oauth/token?client_id=trusted_client& client_secret=secret&grant_type=client_credentials Figure 5.4: Postman Create / customize webservices When you want to add multiple webservices or customize multiple existing web services the best solution is to generate a new extension from the ycommercewebservices template, like what you do when you want to develop a new storefront you use the yacceleratorstorefront template to generate a new extension from where you start your development. 5.2. OCC WEBSERVICES Tip 15: Usage The alternative is to create a new addon to extend existing API or add new one, this could be a valid solution in some cases. 59 60 CHAPTER 5. COMMERCE AND ACCELERATOR The first step is to generate a new extension from the ycommercewebservices extension: : platform ant extgen Buildfile: /Workspace/hybris/hybris-6.0.0/hybris/bin/platform/build.xml [echo] /Workspace/hybris/hybris-6.0.0/hybris/bin/platform/tomcat/bin [echo] /Workspace/hybris/hybris-6.0.0/hybris/bin/platform/ext/core/web/webroot/WEB-INF/ ⌋ external-dependencies.xml was not ֒→ found! ֒→ [ysetplatformproperties] Starting platform in extgen mode... ... ... [echo] commercesearchbackoffice->(backoffice,cms2,commercesearch,commercefacades) ֒→ 6.0.0.0-SNAPSHOT [cib] ֒→ path:/Workspace/hybris/hybris-6.0.0/hybris/bin/ext-backoffice/commercesearchbackoffice [echo] mcc->(impex,processing,cockpit) 6.0.0.0-SNAPSHOT [ciw] ֒→ path:/Workspace/hybris/hybris-6.0.0/hybris/bin/ext-cockpit/mcc [echo] ----[echo] extgen: [input] [input] Please choose a template for generation. [input] Press [Enter] to use the default value (ywebservices, ysmarteditmodule, yhacext, ֒→ [yempty], ycmssmartedit, yatddtests, yscala, ygroovy, ycockpit, yoccaddon, yaddon, ֒→ ycommercewebservices, ycommercewebservicestest, ycommercewebserviceshmc, ֒→ ychinaacceleratorstorefront, yacceleratorordermanagement, yacceleratorstorefront, ֒→ yacceleratorfractusfulfilmentprocess, yacceleratorfulfilmentprocess, ybackoffice) ycommercewebservices [input] [input] Please choose the name of your extension. It has to start with a letter followed ֒→ by letters and/or numbers. [input] Press [Enter] to use the default value [training] mywebservices [input] [input] Please choose the package name of your extension. It has to fulfill java package ֒→ name convention. [input] Press [Enter] to use the default value [org.training] com.hybhub.webservices [echo] Using extension template source: ֒→ /Workspace/hybris/hybris-6.0.0/hybris/bin/ext-template/ycommercewebservices [mkdir] Created dir: /Workspace/hybris/hybris-6.0.0/hybris/temp/hybris/extgen [echo] Next steps: [echo] 1) Add your extension to your ֒→ /Users/workspace-perso/hybris/hybris-6.0.0/hybris/config/localextensions.xml [echo] <extension ֒→ dir="/Workspace/hybris/hybris-6.0.0/hybris/bin/custom/mywebservices"/> [echo] 2) Please remove all template extensions (again) before you proceed. [echo] 3) Make sure the applicationserver is stopped before you build the extension the ֒→ first time. [echo] 4) Perform ’ant’ in your hybris/platform directory. [echo] 5) Restart the applicationserver BUILD SUCCESSFUL Total time: 27 seconds : platform 5.2. OCC WEBSERVICES 61 Once you have generated the new extension, you need to remove the ycommercewebservices from the localextensions.xml file and add your new mywebservices into it. <hybrisconfig xmlns:xsi=’http://www.w3.org/2001/XMLSchema-instance’ ֒→ xsi:noNamespaceSchemaLocation=’../bin/platform/resources/schemas/extensions.xsd’> <extensions> <path dir=’\${HYBRIS_BIN_DIR}’ autoload=’false’ /> ... <extension name="mywebservices"/> ... </extensions> </hybrisconfig> Tip 16: Check current build Run ant clean all to check that the build is not broken before you start developing anything. We are going to add a new web service under the version 2 structure. Our web service will simply give a list of String of all running cronjob codes. To do so, we need to create a new class CronJobsController under web/src source folder and inside the com.hybhub.webservices.v2.controller package: 62 CHAPTER 5. COMMERCE AND ACCELERATOR package com.hybhub.webservices.v2.controller; import import import import import import import import import de.hybris.platform.servicelayer.cronjob.CronJobService; java.util.stream.Collectors; javax.annotation.Resource; org.springframework.security.access.annotation.Secured; org.springframework.stereotype.Controller; org.springframework.web.bind.annotation.RequestMapping; org.springframework.web.bind.annotation.RequestMethod; org.springframework.web.bind.annotation.ResponseBody; com.hybhub.webservices.dto.cronjob.CronJobListWsDTO; @Controller @RequestMapping(value = "/{baseSiteId}/cronjobs") public class CronJobsController extends BaseController { @Resource CronJobService cronJobService; ֒→ @Secured("ROLE_TRUSTED_CLIENT") @RequestMapping(value = "/running", method = RequestMethod.GET) @ResponseBody public CronJobListWsDTO getRunningCronJobCode() { final CronJobListWsDTO cronJobList = new CronJobListWsDTO(); cronJobList.setCronJobsCode( cronJobService.getRunningOrRestartedCronJobs(). parallelStream().map(c -> c.getCode()).collect(Collectors.toList())); return cronJobList; } } We need to create the java bean CronJobListWsDTO using mywebservices-beans.xml file: <bean class="com.hybhub.webservices.dto.cronjob.CronJobListWsDTO"> <property name="cronJobsCode" type="java.util.List&lt;java.lang.String>"/> </bean> Run ant clean all then start your server. Check that you have at least one running cronjob otherwise your list will be empty, then you can call our newly created webservice: Warning! Don’t forget that you first to generate a token as ROLE_TRUSTED_CLIENT in order to call the new webservice since we have declared it with @Secured("ROLE_TRUSTED_CLIENT") Why do we use CronJobListWsDTO and not simply a List of String ? HTTP message converters operate only on the dedicated DTO objects. Marshalers operate mainly on properties and, 63 5.2. OCC WEBSERVICES Figure 5.5: Postman Cronjobs because of this, marshaling performed with commonly used classes such as java.util.ArrayList or java.lang.String is not possible. The simplest solution is to wrap them using a dedicated DTO object. How to cache a webservice ? First we need to create a new cache configuration for our cronjob controller, open the file mywebservices/web/webroot/WEB-INF/cache/ehcache.xml and add : <cache name="cronjobCache" maxElementsInMemory="1" eternal="false" overflowToDisk="true" timeToLiveSeconds="30" diskPersistent="false" maxElementsOnDisk="100" memoryStoreEvictionPolicy="LRU"/> Then update our controller to be Cachable. This example is really basic we only cache the answer for one key and for 30 seconds, You can find more complex examples in other controllers: @Controller @CacheControl(directive = CacheControlDirective.PUBLIC, maxAge = 30) @RequestMapping(value = "/{baseSiteId}/cronjobs") public class CronJobsController extends BaseController ... @Secured("ROLE_TRUSTED_CLIENT") @RequestMapping(value = "/running", method = RequestMethod.GET) @Cacheable(value = "cronjobCache", key = "T(de.hybris.platform.commercewebservicescommons.ca ⌋ che.CommerceCacheKeyGenerator).generateKey(false,false,’getRunningCronJobCode’)") ֒→ @ResponseBody public CronJobListWsDTO getRunningCronJobCode() Now it should take 30 seconds before any changes to cronjobs status appear when you call our cronjob. 64 CHAPTER 5. COMMERCE AND ACCELERATOR CMS navigation bar SAP Hybris Accelerator CMS extension offers items to configure the top navigation nodes: • NavigationBarCollectionComponent, an item used to display top navigation menu, it holds a collection of NavigationBarComponents • NavigationBarComponents, main item type, this represents a section of the top navigation menu, can hold a CMSNavigationNodes (to display sub link) and a CMSLinkComponents • CMSNavigationNodes, represents the architecture of the navigation menu, each node can have parent nodes and children nodes • CMSLinkComponents, represents a link, with a name and a URL For example, to create a new top navigation menu with: • a node with only a home link • a node with the camera category; • a node with various external links. 5.4. ORDER SPLITTING 65 $contentCatalog=electronicsContentCatalog $contentCatalogName=Electronics Content Catalog $contentCV=catalogVersion(CatalogVersion.catalog(Catalog.id[default=$contentCatalog]),Catalo ⌋ gVersion.version[default=Online])[default=$contentCatalog:Online] ֒→ $productCatalog=electronicsProductCatalog $productCatalogName=Electronics Product Catalog $productCV=catalogVersion(catalog(id[default=$productCatalog]),version[default=’Staged’])[un ⌋ ֒→ ique=true,default=$productCatalog:Staged] $category=category(code, $productCV) #First we create a Root node INSERT_UPDATE CMSNavigationNode;uid[unique=true];$contentCV[unique=true];name;children(uid,$ ⌋ ֒→ contentCV)[mode=append] ;rootNode;;rootNode;; #We create the needed links INSERT_UPDATE CMSLinkComponent;$contentCV[unique=true];uid[unique=true];name;url;&linkRef;\& ⌋ ֒→ componentRef;target(code)[default=’sameWindow’];$category; ;;HomepageNavLink;Home Page Nav Link;/;HomepageNavLink;HomepageNavLink;;; ;;CamerasCategoryLink;Cameras Category Link;;CamerasCategoryLink;CamerasCategoryLink;;571; ;;HackerNewsNavLink;Hacker News Page Nav ֒→ Link;https://news.ycombinator.com/news;HackerNewsNavLink;HackerNewsNavLink;;; ;;GitHubNavLink;Git Hub Page Nav Link;https://github.com;GitHubNavLink;GitHubNavLink;;; #We Create the nodes INSERT_UPDATE CMSNavigationNode;uid[unique=true];$contentCV[unique=true];name;parent(uid, $contentCV);links(&linkRef);&nodeRef ֒→ ;ElectronicsNavNode;;Electronics Site;rootNode;HomepageNavLink;ElectronicsNavNode ;CamerasNavNode;;Cameras Category;rootNode;CamerasCategoryLink;CamerasNavNode ;VariousNavNode;;Various Links;rootNode;HackerNewsNavLink, GitHubNavLink;VariousNavNode #We create the navigation bar components INSERT_UPDATE NavigationBarComponent;$contentCV[unique=true];uid[unique=true];navigationNode ⌋ ֒→ (\&nodeRef);&componentRef;link(uid, ֒→ $contentCV) ;;ElectronicsNavComponent;ElectronicsNavNode;ElectronicsNavComponent;HomepageNavLink ;;CamerasNavComponent;CamerasNavNode;CamerasNavComponent;CamerasCategoryLink ;;VariousNavComponent;VariousNavNode;VariousNavComponent;HomepageNavLink #We create the navigation bar collection INSERT_UPDATE NavigationBarCollectionComponent;$contentCV[unique=true];uid[unique=true];name ⌋ ֒→ ;components(uid, ֒→ $contentCV);&componentRef ;;NavBarComponent;Navigation Bar Collection Componeent;ElectronicsNavComponent,CamerasNavCom ⌋ ponent,VariousNavComponent;NavBarComponent ֒→ Run this Impex query on the electronic store to update the top navigation menu. Order splitting Order splitting is part of the Order Management Module and is responsible of splitting orders into different consignments based on the implemented splitting strategy. The service responsible for splitting orders into different consignments is de.hybris.platform.ordersplitting.orderSplittingServ 66 CHAPTER 5. COMMERCE AND ACCELERATOR This service contains a list of splitting strategies to apply on a given order. A strategy needs to implement de.hybris.platform.ordersplitting.strategy.SplittingStrategy. The out of the box strategies are: • splitByAvailableCount; • splitByDeliveryMode; • splitByPoS; • splitByNamedDeliveryDate; • splitByEntryDeliveryAddress; • splitByWarehouse. Tip 17: Usage Implement a splitting strategy To implement a new strategy use de.hybris.platform.ordersplitting.strategy.AbstractSplittingStrategy, this abstract class is doing most of the work, all you have to do is to implement getGroupingObject (to select the attribute to group order entries) and afterSplitting to create consignment. Promotion SAP Hybris V6 introduced a major change with the new Promotion Engine (Running with the new rule engine). Unlike the now legacy and deprecated Promotion Module which was still using the deprecated Jalo Layer, the new Promotion Engine uses the services layer and is highly flexible. Warning! For backward compatibility reasons under SAP Hybris V6.2 both legacy Promotion Module and new Promotion Engine are available, but all new promotion implementation should be done using the Promotion Engine. Promotion Module Remember this module is deprecated under SAP Hybris V6.2 Promotions can apply at two different levels: • product promotion • order promotion Promotion are implemeted using the Jalo layer. It means that each promotion template would have its own Jalo implementation. Your implemetation will need to implement either ProductPromotion or OrderPromotion, both promotion type extends AbstractPromotion. For example, have a look at the promotions-items.xml file. Here is an extract of the ProductFixedPricePromotion: 5.5. PROMOTION <itemtype code="ProductFixedPricePromotion" extends="ProductPromotion" jaloclass="de.hybris.platform.promotions.jalo.ProductFixedPricePromotion" autocreate="true" generate="true"> <attributes> ֒→ ֒→ ֒→ <attribute qualifier="productFixedUnitPrice" autocreate="true" type="PromotionPriceRowCollectionType"> <description>Fixed price to sell the product at in specific currencies.</description> <persistence type="property" /> <modifiers read="true" write="true" search="false" initial="false" optional="true" partof="true"/> </attribute> <attribute qualifier="messageFired" type="localized:java.lang.String"> <description>The message to show when the promotion has fired.</description> <modifiers read="true" write="true" optional="true" /> <persistence type="property"> <columntype> <value>HYBRIS.LONG_STRING</value> </columntype> </persistence> </attribute> </attributes> </itemtype> 67 68 CHAPTER 5. COMMERCE AND ACCELERATOR Now have a look at the class de.hybris.platform.promotions.jalo.ProductFixedPricePromotion: public class ProductFixedPricePromotion extends GeneratedProductFixedPricePromotion { private static final Logger LOG = Logger.getLogger(ProductFixedPricePromotion.class); public ProductFixedPricePromotion() { } public void remove(SessionContext ctx) throws ConsistencyCheckException { ... } ֒→ ֒→ ֒→ private boolean hasPromotionPriceRowForCurrency(AbstractOrder order, Collection<PromotionPriceRow> promotionPriceRows) { ... } public List<PromotionResult> evaluate(SessionContext ctx, PromotionEvaluationContext promoContext) { ... } public String getResultDescription(SessionContext ctx, PromotionResult promotionResult, Locale locale) { ... } protected void buildDataUniqueKey(SessionContext ctx, StringBuilder builder) { ... } protected void deepCloneAttributes(SessionContext ctx, Map values) { ... } } Your promotion implementation extends: GeneratedProductFixedPricePromotion ProductPromotion GeneratedProductPromotion AbstractPromotion de.hybris.platform.promotions.jalo.AbstractPromotion defines two abstract methods that all promotions will need to implement : public abstract List<PromotionResult> evaluate(SessionContext var1, ֒→ PromotionEvaluationContext var2); public abstract String getResultDescription(SessionContext var1, PromotionResult var2, ֒→ Locale var3); 69 5.5. PROMOTION The major flaw of this approach and it’s why it is now deprecated, is that you can’t access the service layer in a trivial manner. Promotion Engine Rule Engine The rule engine is based on [Drools](http://www.drools.org), a Business Rules Management System (BRMS). It provides a mechanism to define business rules with conditions and actions (for example if you buy 2 products of the same type you will get a discount of 10 The rule engine is only used by the promotion engine out of the box but it could be useful for other features that could benefit from a rules/actions engine like the business process framework used by Hybris or order sourcing or btg. It is made from two independent components: • rule builder (accessible from the backoffice, Rule Engine -> Source Rule -> + Promotion Rule) • rule processor Figure 5.6: RuleEngine Actions and conditions are used to create rules. They are the building blocks of the rule engine. Rules are compiled using the IR (Intermediate Representation) into executables. Rule Aware Object Rule aware objects (RAO) are plain java objects (like java beans) they are used by the rule engine to determine if a condition is fulfilled or not. When running the rule engine we need to carry data from the application into the rule context as a set of facts. Out of the box RAO objects: • OrderRAO • CartRAO • UserRAO • RuleEngineResultRAO • DeliveryModeRAO 70 CHAPTER 5. COMMERCE AND ACCELERATOR RAO objects are defined using beans.xml files, and therefore are generated at compile time, you can extend existing one or create new one. Let’s extend de.hybris.platform.ruleengineservices.rao.AbstractOrderRAO with an additional value dayOfWeek represented by an Integer (from 1 to 7). First we need to update the bean definition: <bean class="de.hybris.platform.ruleengineservices.rao.AbstractOrderRAO" ֒→ template="resources/coderao-template.vm" extends="de.hybris.platform.ruleengineservices.rao.AbstractActionedRAO"> <property name="dayOfWeek" type="Integer" /> </bean> Now we need to create a new populator for our new field : package de.hybris.ruleenginetrail.converters.populator; import import import import de.hybris.platform.core.model.order.AbstractOrderModel; de.hybris.platform.ruleengineservices.converters.populator.CartRaoPopulator; de.hybris.platform.ruleengineservices.rao.CartRAO; de.hybris.platform.servicelayer.dto.converter.ConversionException; import java.time.LocalDate; public class ExtendedCartRaoPopulator extends CartRaoPopulator { ֒→ @Override public void populate(AbstractOrderModel source, CartRAO target) throws ConversionException { super.populate(source, target); target.setDayOfWeek(LocalDate.now().getDayOfWeek().getValue()); } } And finally update your spring configuration: <alias alias="cartRaoPopulator" name="extendedCartRaoPopulator" /> <bean id="extendedCartRaoPopulator" class="de.hybris.ruleenginetrail.converters.populator.ExtendedCartRaoPopulator" ֒→ parent="defaultCartRaoPopulator" /> 71 5.5. PROMOTION Create a new condition Warning! This requires you to have extended the cart rao. Let’s create a new condition to be fulfilled only on week end days (Saturday and Sunday). First you need an implementation of de.hybris.platform.ruleengineservices.compiler.RuleConditionTranslator. package de.hybris.ruleenginetrail.rule.evaluation.conditions.impl; import import import import de.hybris.platform.ruleengineservices.compiler.}; de.hybris.platform.ruleengineservices.rao.CartRAO; de.hybris.platform.ruleengineservices.rule.data.RuleConditionData; de.hybris.platform.ruleengineservices.rule.data.RuleConditionDefinitionData; public class RuleCartWeekEndConditionTranslator implements RuleConditionTranslator { ֒→ ֒→ @Override public RuleIrCondition translate(RuleCompilerContext context, RuleConditionData ruleConditionData, RuleConditionDefinitionData ruleConditionDefinitionData) throws RuleCompilerException { final String cartRaoVariable = context.generateVariable(CartRAO.class); final RuleIrAttributeCondition weekEndDayCondition = new RuleIrAttributeCondition(); weekEndDayCondition.setVariable(cartRaoVariable); weekEndDayCondition.setAttribute("dayOfWeek"); weekEndDayCondition.setOperator(RuleIrAttributeOperator.GREATER_THAN); weekEndDayCondition.setValue(Integer.valueOf(5)); return weekEndDayCondition; } } Then you need to create a new Spring bean for your condition: <alias alias="ruleCartWeekEndConditionTranslator" ֒→ name="defaultRuleCartWeekEndConditionTranslator" /> <bean name="defaultRuleCartWeekEndConditionTranslator" class="de.hybris.ruleenginetrail.rule ⌋ ֒→ .evaluation.conditions.impl.RuleCartWeekEndConditionTranslator" ֒→ /> 72 CHAPTER 5. COMMERCE AND ACCELERATOR Finally creating the new condition and mapping within SAP Hybris : $lang=en INSERT_UPDATE RuleConditionDefinition;id[unique=true];name[lang=$lang];priority;breadcrumb[l ⌋ ang=$lang];allowsChildren;translatorId;translatorParameters;categories(id) ֒→ ;cart_weekend;Cart week end;301;Cart week end;false;ruleCartWeekEndConditionTranslator;;cart INSERT_UPDATE RuleConditionDefinitionParameter;definition(id)[unique=true];id[unique=true];p ⌋ ֒→ riority;name[lang=$lang];description[lang=$lang];type;value;required[default=true] #We don’t insert anything here, not need for extra parameters INSERT_UPDATE RuleConditionDefinitionRuleTypeMapping;definition(id)[unique=true];ruleType(co ⌋ de)[unique=true] ֒→ ;cart_weekend;PromotionSourceRule Warning! RuleConditionDefinition translatorId must be the Spring bean name. Update your system, and you should be able to select Cart Week End from the rule builder. Available conditions Your translator will return a condition for the rule engine. So the rule engine can compile and optimize your rules for runtime. Out of the box, you have the following available conditions: true de.hybris.platform.ruleengineservices.compiler.RuleIrTrueCondition false de.hybris.platform.ruleengineservices.compiler.RuleIrFalseCondition condition de.hybris.platform.ruleengineservices.compiler.RuleIrGroupCondition RuleIrFalseCondition irFalseCondition = new RuleIrFalseCondition(); RuleIrTrueCondition irTrueCondition = new RuleIrTrueCondition(); RuleIrGroupCondition irCondition = new RuleIrGroupCondition(); irCondition.setOperator(RuleIrGroupOperator.OR); irCondition.setChildren(Arrays.asList(irFalseCondition, irTrueCondition)); exists condition de.hybris.platform.ruleengineservices.compiler.RuleIrExistsCondition 5.5. PROMOTION 73 String productRaoVariable = context.generateVariable(ProductRAO.class); RuleIrTypeCondition irTypeCondition = new RuleIrTypeCondition(); irTypeCondition.setVariable(productRaoVariable); RuleIrExistsCondition irCondition = new RuleIrExistsCondition(); irCondition.setChildren(Collections.singletonList(irTypeCondition)); not condition de.hybris.platform.ruleengineservices.compiler.RuleIrNotCondition String productRaoVariable = context.generateVariable(ProductRAO.class); RuleIrTypeCondition irTypeCondition = new RuleIrTypeCondition(); irTypeCondition.setVariable(productRaoVariable); RuleIrNotCondition irCondition = new RuleIrNotCondition(); irCondition.setChildren(Collections.singletonList(irTypeCondition)); type condition de.hybris.platform.ruleengineservices.compiler.RuleIrTypeCondition String cartRaoVariable = context.generateVariable(CartRAO.class); RuleIrTypeCondition irCondition = new RuleIrTypeCondition(); irCondition.setVariable(cartRaoVariable); attribute condition de.hybris.platform.ruleengineservices.compiler.RuleIrAttributeCondition String cartRaoVariable = context.generateVariable(CartRAO.class); RuleIrAttributeCondition irCondition = new RuleIrAttributeCondition(); irCondition.setVariable(cartRaoVariable); irCondition.setAttribute("total"); irCondition.setOperator(RuleIrAttributeOperator.GREATER_THAN_OR_EQUAL); irCondition.setValue(BigDecimal.valueOf(100)); 74 CHAPTER 5. COMMERCE AND ACCELERATOR Create a new action Let’s create a new type of action. This will add a discount on the order based on how many previous orders the customer fullfiled, for example: A customer A has 3 orders in his history; his new order will be discounted by 3 value factor B with a maximum of discount of C. First, we need to create a new RAO action: 5.5. PROMOTION package de.hybris.ruleenginetrail.rule.evaluation.actions.impl; import com.google.common.base.Preconditions; import de.hybris.platform.ruleengineservices.rao.}; import de.hybris.platform.ruleengineservices.rule.evaluation.RuleActionContext; import de.hybris.platform.ruleengineservices.rule.evaluation.actions.AbstractRuleExecutableS ⌋ ֒→ upport; import de.hybris.platform.ruleengineservices.rule.evaluation.actions.RAOAction; import org.apache.commons.collections.MapUtils; import java.math.BigDecimal; import java.util.Map; import java.util.Optional; public class RuleOrderHistoryDiscountRAOAction extends AbstractRuleExecutableSupport ֒→ implements RAOAction { static private String VALUE = "value"; static private String CART_SIZE = "max_discount"; ֒→ ֒→ ֒→ @Override public void performAction(RuleActionContext context, Map<String, Object> parameters) { Preconditions.checkArgument(MapUtils.isNotEmpty(parameters), "Properties passed as a method argument must not be empty"); this.validateRule(context); final Optional<BigDecimal> value = this.extractAmountForCurrency(context,parameters.get(VALUE)); final Optional<BigDecimal> maxDiscount = this.extractAmountForCurrency(context,parameters.get(CART_SIZE)); if(value.isPresent() \&& maxDiscount.isPresent()){ this.performAction(context, value.get(), maxDiscount.get()); } this.trackRuleGroupExecutions(context); this.trackRuleExecution(context); } ֒→ ֒→ ֒→ ֒→ } protected void performAction(RuleActionContext context, BigDecimal amount, BigDecimal maxDiscount) { CartRAO cartRao = context.getCartRao(); final int numOfOrders = cartRao.getUser().getOrders().size(); final BigDecimal calculatedDiscount = amount.multiply(BigDecimal.valueOf(numOfOrders)); final BigDecimal discount = maxDiscount.compareTo(calculatedDiscount) == 1 ? calculatedDiscount : maxDiscount; DiscountRAO discountRao = this.getRuleEngineCalculationService().addOrderLevelDiscount(cartRao, false, discount); RuleEngineResultRAO result = context.getRuleEngineResultRao(); result.getActions().add(discountRao); this.setRAOMetaData(context, new AbstractRuleActionRAO[]{discountRao}); context.updateFacts(new Object[]{cartRao, result}); context.insertFacts(new Object[]{discount}); } 75 76 CHAPTER 5. COMMERCE AND ACCELERATOR We now need to create create two Spring beans for our new action: <alias alias="ruleOrderHistoryDiscountRAOAction" ֒→ name="defaultRuleOrderHistoryDiscountRAOAction" /> <bean id="defaultRuleOrderHistoryDiscountRAOAction" parent="abstractRuleExecutableSupport" class="de.hybris.ruleenginetrail.rule.evaluation.actions.impl.RuleOrderHistoryDiscountR ⌋ ֒→ ֒→ AOAction"/> <alias alias="ruleOrderHistoryDiscountAction" name="defaultRuleOrderHistoryDiscountAction" /> <bean id="defaultRuleOrderHistoryDiscountAction" ֒→ class="de.hybris.platform.ruledefinitions.actions.DefaultRuleExecutableAction"> <property name="raoAction" ref="ruleOrderHistoryDiscountRAOAction"/> </bean> Warning! We use DefaultRuleExecutableAction for backward compatibility, see the following topic: Promotion actions. Last step is to define the rule action, its parameter and mapping: $lang=en INSERT_UPDATE RuleActionDefinition;id[unique=true];name[lang=$lang];priority;breadcrumb[lang ⌋ =$lang];translatorId;translatorParameters;categories(id) ֒→ ;order_history_discount;Order percentage discount base on how many orders;1200;Apply {value}% discount on the multiply by how many orders customer has, max of ֒→ {max_discount}%;ruleExecutableActionTranslator;actionId->ruleOrderHistoryDiscountAction ⌋ ֒→ ֒→ ;cart_discounts INSERT_UPDATE RuleActionDefinitionParameter;definition(id)[unique=true];id[unique=true];prio ⌋ rity;name[lang=$lang];description[lang=$lang];type;value;required[default=true] ֒→ ;order_history_discount;value;1000;Percentage discount value factor;Percentage discount that ֒→ multiplied by order numbers;java.math.BigDecimal;; ;order_history_discount;max_discount;1001;Maximum discount;Max discount;java.math.BigDecimal;; ֒→ INSERT_UPDATE RuleActionDefinitionRuleTypeMapping;definition(id)[unique=true];ruleType(code) ⌋ ֒→ [unique=true] ;order_history_discount;PromotionSourceRule You can create as many RuleActionDefinitionParameter as you need they would be available from the backoffice and inside you RAO action parameters map. Payment SAP Hybris provides a module to integrate external payment gateways such as CyberSource. The module is designed to seamlessly integrate any PSP into your checkout flow. 5.6. PAYMENT 77 Since SAP Hybris V6, CyberSource payment integration is not part of the Commerce Accelerator, you would need to add the CyberSource extension. When you integrate a new payment provider, you need to integrate the following steps: • Authorization, Authorized amount of money remains locked on card’s account until it is captured or authorization is reversed (canceled) or authorization is expired • Capture, Capturing an authorization means the authorized amount of money is actually transferred from the card holder account to the merchant account. Capture operation requires a previous successful authorization that has not yet expired • Partial Capture, Capture only a fragment of the reserved amount • Void, cancel an ongoing authorization • Follow on refund, refund money to the customer’s bank account for an order • Standalone refund, refund money to the customer’s bank account when not attached to any transactions or orders • Is applicable, command to check if the payment provider can execute a given command When you have implemented all commands for your payment service provider, create a new bean using de.hybris.platform.payment.commands.factory.impl.DefaultCommandFactoryImpl, and inject all your commands in it. Tip 18: Payment providers SAP Hybris can have multiple PSP configured at the same time, it’s up to the checkout flow to give the customer the option to choose the final PSP. HOP Out of the box, SAP Hybris provides an example of a hosted order page. A HOP is a page hosted by the payment provider where the customer will enter all his payment information. In a production environment the flow would be as follows: • customer enters the checkout flow; • customer reaches the payment step and click on pay; • customer is redirected to the payment provider site where he will enter all required payment information; • customer is redirected to SAP Hybris with a uid of his transaction so SAP Hybris can check that the transaction was successful. During the entire transaction SAP Hybris didn’t handle any sensitive payment data from the customer, all SAP Hybris knows is the uid of the transaction 78 CHAPTER 5. COMMERCE AND ACCELERATOR SOP Out of the box, SAP Hybris provides an example of a silent order page. A SOP is page hosted by SAP Hybris with a from sent directly to the payment provider. A SOP can be entirely customized unlike a HOP (hosted by the payment provider). In a production environment the flow would be as follows: • customer enters the checkout flow; • customer reaches the payment step and click on pay; • customer fill a form hosted by SAP Hybris but redirecting to the payment provider; • the payment provider receive the submited form and process the payment; • customer is redirected to SAP Hybris with a uid of his transaction so SAP Hybris can check that the transaction was successful. To host a SOP you need to have a SSL certificate During the entire transaction SAP Hybris didn’t handle any sensitive payment data from the customer, all SAP Hybris knows is the uid of the transaction PCI compliance The payment card industry security standard enforce strict rules for your platform in order for you to accept payment by credit card ([What is PCI compliance](http://www.onlinetech.com/resources/references/what is-pci-compliance)). In both mocked implementation SAP Hybris will only know a transaction uid, this is great for PCI compliance Internationalization SAP Hybris supports out of the box the following: • Languages (localizations of business objects); • Countries and Regions (tax regulations); • Currencies (currency formats, rounding); • Time Zones (online / offline dates, order dates); • Number Formats (separation characters); • Language fallbacks; • Tenant-specific settings; • Localized API for the service layer; • Compatible UI to manage localized attributes. The following services to work with internationalization and localization are available: 5.8. REQUEST HANDLING 79 • de.hybris.platform.servicelayer.i18n.I18NService, service to work with Java internationalization objects(Locale, Currency, TimeZone) • de.hybris.platform.servicelayer.i18n.CommonI18NService, service to work with SAP Hybris localization objects (LanguageModel, CurrencyModel, CountryModel, RegionModel), those items can be used to create a Java Locale object • de.hybris.platform.servicelayer.i18n.L10NService, provides methods localized resources bundles • de.hybris.platform.commerceservices.i18n.CommerceCommonI18NService, provides localization objects for the current base site internationalization is making your software compatible with different currencies, countries.... localization is providing the appropriate resources for a specific language Request handling SAP Hybris accelerators are built on top of Spring MVC. This will handle all HTTP requests you make to SAP Hybris. Below a simplified representation of how Spring MVC handles requests: ![Spring MVC - Request Handling](images/spring-mvc-requesthandling.png) The dispatcherServlet (org.springframework.web.servlet.DispatcherServlet )is responsible of handling and dispatching requests for all incoming requests it receives. A unique entry point is configured for each extension web module. By default it’s the name of the extension. You can change from your configuration (they are intentionally empty): hybhubstorefront.webroot= storefrontContextRoot= Request filters request filters are handled by a DelegatingFilterProxy, a special servlet filter that, by itself, doesn’t do much. Instead, it delegates to an implementation of javax.servlet.Filter that’s registered as a bean in the Spring application context. The accelerators are configured with different filters out-of-the-box: • cmsSiteFilter • storefrontFilter • urlEncoderFilter • springSecurityFilterChain • anonymousCheckoutFilter • cartRestorationFilter 80 CHAPTER 5. COMMERCE AND ACCELERATOR • customerLocationRestorationFilter • .... The filter entry point is configured inside your web application web.xml file: <filter> <description> Spring configured chain of spring filter beans in tenant scope </description> <filter-name>storefrontTenantFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> The bean storefrontTenantFilterChain is configured inside your extension spring-filter-config.xml: <bean id="storefrontTenantFilterChain" class="com.hybhub.storefront.filters.UrlPathFilter" > <property name="defaultFilter" ref="storefrontTenantDefaultFilterChain"/> <property name="urlPathHelper"> <bean class="org.springframework.web.util.UrlPathHelper"/> </property> <property name="urlPathMapping"> <map> <entry key="/integration/" value-ref="integrationTenantFilterChain"/> </map> </property> </bean> Create a new request filter Create a new class: 5.8. REQUEST HANDLING package com.hybhub.storefront.filters; import java.io.IOException; import import import import import javax.servlet.FilterChain; javax.servlet.ServletException; javax.servlet.ServletRequest; javax.servlet.ServletResponse; javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.filter.GenericFilterBean; public class HybhubFilter extends GenericFilterBean { Logger LOG = LoggerFactory.getLogger(HybhubFilter.class); ֒→ ֒→ ֒→ @Override public void doFilter(final ServletRequest paramServletRequest, final ServletResponse paramServletResponse, final FilterChain paramFilterChain) throws IOException, ServletException { final HttpServletRequest httpRequest = (HttpServletRequest) paramServletRequest; LOG.debug("Request received by the filter " + this.getFilterName()); LOG.debug("Requested path was " + httpRequest.getPathInfo()); paramFilterChain.doFilter(paramServletRequest, paramServletResponse); } } Then update your storefront spring-filter-config.xml file with: <bean id="hybhubFilter" class="com.hybhub.storefront.filters.HybhubFilter" /> <alias name="defaultStorefrontTenantDefaultFilterChainList" ֒→ alias="storefrontTenantDefaultFilterChainList" /> <util:list id="defaultStorefrontTenantDefaultFilterChainList"> <!-- other filters --> <ref bean="hybhubFilter"/> <!-- other filters --> </util:list> The order you place your filter matters as they are executed in the list order! 81 82 CHAPTER 5. COMMERCE AND ACCELERATOR Hot folders SAP Hybris provides different ways to import data; one of them is a hot folder. A hot folder means that when you transfer a file into a specific folder with matching name it will automatically be processed by SAP Hybris. The hot folder functionality is located inside the acceleratorservices extension, it’s based on the Spring Integration Framework When you transfer a file into a hot folder, the following actions are executed: 1. an inbound channel detects a new file matching a pattern (file:inbound-channel-adapter) 2. an outbound channel moves the file to a new folder (file:outbound-gateway) 3. activators setup the headers (int:service-activator) 4. resolve the mapping between the file name and the type to import 5. generate the impex 6. run the impex 7. cleanup Create a new import configuration Let’s assume the out of the box customer import flow is not enough for us. We need a new one with a different header and more attributes. 5.9. HOT FOLDERS 83 Create a inbound, outbound, mapping and converter <file:inbound-channel-adapter id="hybhubBatchFilesCustomer" ֒→ directory="#{baseDirectoryTenant}" filename-regex="^customers_full\-(.*)-(\d+)\.csv" comparator="fileOrderComparator"> <int:poller fixed-rate="1000" /> </file:inbound-channel-adapter> <file:outbound-gateway request-channel="hybhubBatchFilesCustomer" ֒→ reply-channel="hybhubBatchFilesCustomerProc" directory="#{baseDirectoryTenant}/processing" delete-source-files="true" /> <bean id="hybhubCustomersHeaderSetupTask" ֒→ class="de.hybris.platform.acceleratorservices.dataimport.batch.task.HeaderSetupTask"> <property name="catalog" value="electronicsProductCatalog" /> <property name="net" value="false" /> <property name="storeBaseDirectory" ref="baseDirectoryTenant" /> </bean> <int:service-activator input-channel="hybhubBatchFilesCustomerProc" ֒→ output-channel="batchFilesHeaderInit" ref="hybhubCustomersHeaderSetupTask" method="execute" /> <bean id="hybhubBatchCustomerConverterMapping" class="de.hybris.platform.acceleratorservices.dataimport.batch.converter.mapping.i ⌋ mpl.DefaultConverterMapping" ֒→ p:mapping="customers_full" p:converter-ref="hybhubBatchCustomerConverter"/> <bean id="hybhubBatchCustomerConverter" class="de.hybris.platform.acceleratorservices.dataim ⌋ ֒→ port.batch.converter.impl.DefaultImpexConverter"> <property name="header"> <value>#{defaultImpexProductHeader} # Insert Customer \$defaultPassword=1234 \$setName=name[cellDecorator=de.hybris.platform.acceleratorservices. ⌋ ֒→ dataimport.batch.decorator.CustomerNameDecorator] INSERT_UPDATE Customer;uid[unique=true];\$setName;title(code);sessio ⌋ ֒→ nLanguage(isocode);sessionCurrency(isocode);groups(uid) </value> </property> <property name="impexRow"> <value>;{+0};{1} {2};{3};{4};{5};{6}</value> </property> </bean> Here we are using headerInitTask as a batchFilesHeaderInit. This is expecting your file to match the following pattern to extract the language code: -(\w{2})-(\d+)\.csv 84 CHAPTER 5. COMMERCE AND ACCELERATOR And this to extract the sequence: -(\d+)\.csv Create a file under data/acceleratorservices/import/master named customers_full-en-0.csv with the following content : serge.d@hybhub.com,Serge,Dupont,mr,en,USD,regulargroup This file will be automatically executed by SAP Hybris, and a new customer will be created. The mapping is made between the name of your file and the string property of the ConverterMapping implementation Impex row definition uses a strange syntax, try to remember that + means mandatory and ’S’ means the current sequence ID Data Modeling Figure 5.7: Data model Create new types SAP Hybris gives you the ability to extend the existing model definition, this way you can completely customize the SAP Hybris platform with your own types, your own attributes for existing types and your own relations between types. All types definition are done using XML item files, from extensions with a core module: 5.10. DATA MODELING 85 <coremodule generated="true" manager="de.hybris.platform.jalo.extension.GenericManager" ֒→ packageroot="com.hybhub.myextension"/> The definitions are located under resources/myextension-items.xml, example of an empty definition : 86 CHAPTER 5. COMMERCE AND ACCELERATOR <?xml version="1.0" encoding="ISO-8859-1"?> <!-~ [y] hybris Platform ~ ~ Copyright (c) 2000-2016 SAP SE ~ All rights reserved. ~ ~ This software is the confidential and proprietary information of SAP ~ Hybris ("Confidential Information"). You shall not disclose such ~ Confidential Information and shall use it only in accordance with the ~ terms of the license agreement you entered into with SAP Hybris. --> <!-ATTENTION: This is just an example file. You have to edit it according to your needs. --> <items xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="items.xsd"> <itemtypes> <!-<itemtype generate="true" code="MyProduct" jaloclass="yemptypackage.jalo.MyProduct" extends="Product" autocreate="true" > ֒→ ֒→ <attributes> <attribute qualifier="myExampleInitialStringField" type="java.lang.String"> <description>My Example Initial String Value</description> <modifiers initial="true"/> <persistence type="property"/> </attribute> <attribute qualifier="myExampleBooleanField" ֒→ ֒→ type="java.lang.Boolean"> <description>Example Initial Boolean Field</description> <persistence type="property"/> </attribute> <attribute qualifier="media" type="Media"> <persistence type="property"/> </attribute> </attributes> </itemtype> --> </itemtypes> </items> From an }-items.xml file you can declare different item types : • Atomic types, the most basic types available on the SAP Hybris platform, they most likely 5.10. DATA MODELING 87 represent Numbers, Strings, Dates.... they have a direct correspondence with a database type. You probably won’t need to define your own Atomic type, following is an example extracted from core-items.xml : <atomictype class="java.lang.Double" extends="java.lang.Number" autocreate="true" generate="false"/> • Collection types, contains a typed number of instances. This can be a Collection (Unordered), a List (Ordered) or a Set (Unique) of elements. The collection is kept in the database by saving a csv list of instance pks. For Example if I create a wish list set of Products for Customers : <collectiontypes> <collectiontype code="ProductSet" elementtype="Product" type="set" autocreate="true" ֒→ generate="true" /> </collectiontypes> <itemtypes> <itemtype code="Customer" autocreate="false" generate="false"> <attributes> <attribute qualifier="wishList" type="ProductSet"> <persistence type="property"/> </attribute> </attributes> </itemtype> </itemtypes> After updating my system the User table would look like this: |p_wishlist|text|YES||NULL|| If I query a Customer who has 3 products in his wish list : mysql> select p_uid, p_wishlist from users where p_uid = "hybris"; +--------+------------------------------------------------+ | | p_uid | p_wishlist +--------+------------------------------------------------+ | ,#2,8796093087745,8796093120513,8796093054977, | | user +--------+------------------------------------------------+ 1 row in set (0.00 sec) 88 CHAPTER 5. COMMERCE AND ACCELERATOR You can see the size of the Set (3) and the 3 Products PKs. Use relation types whenever possible, collection types have severe limitation (maximum length as for example in Mysql maximum text size is 65,535 characters, not cached by Hybris). • Enumeration types, they are like java Enumerations, they declare a collection of constants, within SAP Hybris Enumeration is a composed type and their value represents their instance. Within SAP Hybris enumerations can be dynamic, meaning you can add new values during runtime (unlike enumerations in Java), following is an example extracted from core-items.xml : <enumtype code="OrderStatus" autocreate="true" generate="true" dynamic="true"> <value code="CREATED"/> <value code="ON_VALIDATION"/> <value code="COMPLETED"/> <value code="CANCELLED"/> </enumtype> • Map types, they are type collections of key/value pairs, they are intensively used under SAP Hybris for localized elements, for example when you use localized:java.lang.String, following is an example extracted from core-items.xml : <maptype code="localized:java.lang.String" argumenttype="Language" returntype="java.lang.String" autocreate="true" generate="false"/> • Relation types, they are the way to go to represent n:m relations, internally SAP Hybris links both element via an instance of an helper type called LinkedItem. LinkedItem holds 2 attributes, SourceItem and TargetItem. When SAP Hybris runs a search query on either side of the relation it returns a java Collection that contain all values, following is an example extracted from core-items.xml : <relation code="Country2RegionRelation" generate="true" localized="false" autocreate="true"> <sourceElement type="Country" qualifier="country" cardinality="one"> <modifiers read="true" write="true" search="true" optional="false" unique="true"/> </sourceElement> <targetElement type="Region" qualifier="regions" cardinality="many"> <modifiers read="true" write="true" search="true" partof="true"/> </targetElement> </relation> 5.10. DATA MODELING 89 • Item types, also called composed types are the main type used to build SAP Hybris platform, all types are derived of a ComposedType. It holds metadata about the types like the item’s type code, the JNDI deployment location, the database table deployment and the type’s java class. The following is an example extracted from core-items.xml: <itemtype code="Title" extends="GenericItem" jaloclass="de.hybris.platform.jalo.user.Title" autocreate="true" generate="true"> <!-- deployment="de.hybris.platform.persistence.user.Title" --> <deployment table="Titles" typecode="24"/> <attributes> <attribute autocreate="true" qualifier="code" type="java.lang.String"> <persistence type="property"/> <modifiers read="true" write="true" search="true" initial="true" ֒→ optional="false" unique="true"/> <custom-properties> <property name="hmcIndexField"> <value>"thefield"</value> </property> </custom-properties> </attribute> <attribute autocreate="true" qualifier="name" type="localized:java.lang.String"> <modifiers read="true" write="true" search="true" optional="true"/> <persistence type="property"/> <custom-properties> <property name="hmcIndexField"> <value>"thefield"</value> </property> </custom-properties> </attribute> </attributes> <indexes> <index name="codeIdx"> <key attribute="code"/> </index> </indexes> </itemtype> Exercise design your own data types Our main objective here is to model a blog as we want to add the ability for our employees to blog about new products and the company: We have posts. They represent the content of each blog entry. Each post is linked to a blog linked to one or more CMSSites. Each post can have comments linked to users. First we need enum types : • for post status (draft, published, deleted) • for comment status (pending, approved, blocked) 90 CHAPTER 5. COMMERCE AND ACCELERATOR Figure 5.8: UML blog <enumtypes> <enumtype code="PostStatus"> <value code="DRAFT"/> <value code="PUBLISHED"/> <value code="DELETED"/> </enumtype> <enumtype code="CommentStatus"> <value code="PENDING"/> <value code="APPROVED"/> <value code="BLOCKED"/> </enumtype> </enumtypes> Then we create the new item types : • Post • Blog • PostComment When you create new item types it is mandatory to specify a deployment table, or the build would fail (you can deactivate this check with build.development.mode=false) [ycheckdeployments] No deployment defined for non-abstract itemType Blog in file: /Users/b.vanalderweireldt/.../resources/hybhubaddon-items.xml 5.10. DATA MODELING <itemtypes> <itemtype code="Blog" extends="GenericItem" autocreate="true" generate="true"> <deployment table="blog" typecode="11001" /> <attributes> <attribute qualifier="code" type="java.lang.String"> <modifiers initial="true" read="true" write="false" ֒→ optional="false" unique="true" /> <persistence type="property" /> </attribute> <attribute qualifier="active" type="boolean"> <persistence type="property" /> </attribute> </attributes> </itemtype> ֒→ <itemtype code="Post" extends="GenericItem" autocreate="true" generate="true"> <deployment table="post" typecode="11002" /> <attributes> <attribute qualifier="blog" type="Blog"> <persistence type="property"/> </attribute> <attribute qualifier="date" type="java.util.Date"> <modifiers optional="false"/> <persistence type="property" /> </attribute> <attribute qualifier="author" type="Employee"> <modifiers optional="false"/> <persistence type="property" /> </attribute> <attribute qualifier="title" type="localized:java.lang.String"> <persistence type="property" /> </attribute> <attribute qualifier="content" type="localized:java.lang.String"> <persistence type="property"> <columntype> <value>HYBRIS.LONG_STRING</value> </columntype> </persistence> <modifiers search="false"/> </attribute> <attribute qualifier="keywords" type="StringCollection"> <persistence type="property" /> </attribute> <attribute qualifier="status" type="PostStatus"> <persistence type="property" /> <defaultvalue>em().getEnumerationValue("PostStatus", "DRAFT")</defaultvalue> </attribute> </attributes> </itemtype> <itemtype code="PostComment" extends="GenericItem" autocreate="true" generate="true"> <deployment table="postcomment" typecode="11003" /> <attributes> <attribute qualifier="author" type="User"> <persistence type="property"/> </attribute> <attribute qualifier="content" type="java.lang.String"> <persistence type="property"> <columntype> <value>HYBRIS.LONG_STRING</value> </columntype> </persistence> <modifiers search="false" optional="false"/> </attribute> <attribute qualifier="status" type="CommentStatus"> <persistence type="property" /> </attribute> </attributes> </itemtype> </itemtypes> 91 92 CHAPTER 5. COMMERCE AND ACCELERATOR We finally need to create relations between Blog and CMSSIte. <relation localized="false" code="BlogCMSSiteRelation"> <deployment table="blogcmssiterelation" typecode="1201"/> <sourceElement type="Blog" qualifier="blogs" cardinality="many"/> <targetElement type="CMSSite" qualifier="sites" cardinality="many"/> </relation> When you run ant all the SAP Hybris platform, the build framework will: • Generate Model classes for the service layer (for example BlogModel) • Abstract Jalo layer classes, starting with Generated (for example GeneratedBlog) • Non abstract Jalo layer classes, with the same name as the item type (for example Blog) Jalo is deprecated since Hybris V4.3, however not all logic have been moved to the service layer. If you don’t plan to use the Jalo layer with your item types you can use generate="false" Once you have build the platform successfully, you will need to update your system so SAP Hybris can update the database with the new item types definition (using the HAC or ant initialize). Check that all new types, relations and enumtypes are available from the backoffice (filter types per extension, in my case they are under hybhubaddon extension): ![Backoffice types](images/backoffice-types.png) We can see that all newly defined items are available in our current system. Import test data INSERT_UPDATE Blog;code[unique=true];active[default=true] ;myBlog; INSERT_UPDATE Post;date[unique=true][dateformat=dd-MM-yyyy];Blog(code);author(uid) ;01-01-2016;myBlog;admin INSERT_UPDATE PostComment;content[unique=true];author(uid);status(code); ;This is the content;admin;PENDING Extend existing types SAP Hybris not only gives you the ability to create new types, but also gives you the ability to extend the core item types (User, Product,...) without changing any of the core extensions so you would be able to update SAP Hybris without breaking anything. This is one of the greatest features of SAP Hybris the possibility of building your e-commerce platform not from SAP Hybris but on top of the existing code. Let’s do two examples of how we can extend existing item types: 5.10. DATA MODELING 93 • Users will have a birthday attribute • Products will have a boolean to activate (or not) an export to Google Shopping <itemtype code="Product" autocreate="false" generate="false"> <attributes> <attribute qualifier="exportToGoogleShopping" type="java.lang.Boolean"> <persistence type="property"/> </attribute> </attributes> </itemtype> <itemtype code="Customer" autocreate="false" generate="false"> <attributes> <attribute qualifier="birthday" type="java.util.Date"> <persistence type="property"/> </attribute> </attributes> </itemtype> Update your system and check from the backoffice that new attributes have been added to your types: ![Product new attribute](images/backoffice-product-newAttr.png) ![Customer new attribute](images/backoffice-customer-newAttr.png) New attributes are also available from the model service. Localized Attributes Localized attribute type is an essential type under SAP Hybris platform, it gives you the ability to dynamically server different content (text, image, link...) based on the user language. An example would be the product name: <attribute autocreate="true" qualifier="name" type="localized:java.lang.String"> <modifiers read="true" write="true" search="true" optional="true"/> <persistence type="property"/> <custom-properties> <property name="hmcIndexField"> <value>"thefield"</value> </property> </custom-properties> </attribute> When an item type has a localized attribute, SAP Hybris will automatically create an extra database table ending with lp, for example Product type has the table productslp : 94 CHAPTER 5. COMMERCE AND ACCELERATOR MariaDB [database]> explain productslp; +-----+--------------+------+-----+---------+-------+ | Type | Null | Field +-----+--------------+------+-----+---------+-------+ | ITEMPK | bigint(20) | NO | bigint(20) | YES | ITEMTYPEPK | bigint(20) | NO | LANGPK | p_name | varchar(255) | YES | text | YES | p_description | p_manufacturertypedescription | varchar(255) | YES | p_segment | varchar(255) | YES | longblob | YES | p_articlestatus | text | YES | p_summary | p_directionofuse | text | YES | text | YES | p_specialpricedescription | varchar(255) | YES | p_style | p_size | varchar(255) | YES | varchar(255) | YES | p_color +-----+--------------+------+-----+---------+-------+ 14 rows in set (0.01 sec) | Key | Default | Extra | | PRI | 0 | | NULL | PRI | 0 | | NULL | | NULL | | NULL | | NULL | | NULL | | NULL | | NULL | | NULL | | NULL | | NULL | | NULL | | | | | | | | | | | | | | | | | | | | | | | | | | | | Each localized attribute of an item is grouped in this table and linked to a language through LANGPK. The model service will create getters and setters like this : /** * <i>Generated method</i> - Getter of the <code>Product.description</code> attribute defined at extension <code>core</code>. ֒→ * @return the description */ @Accessor(qualifier = "description", type = Accessor.Type.GETTER) public String getDescription() { return getDescription(null); } /** * <i>Generated method</i> - Getter of the <code>Product.description</code> attribute defined at extension <code>core</code>. ֒→ * @param loc the value localization key * @return the description * @throws IllegalArgumentException if localization key cannot be mapped to data language */ @Accessor(qualifier = "description", type = Accessor.Type.GETTER) public String getDescription(final Locale loc) { return getPersistenceContext().getLocalizedValue(DESCRIPTION, loc); } You can get the product description using a Locale object or not, if you don’t provide any SAP Hybris will load the default one (see de.hybris.platform.servicelayer.model.ItemModelContextImpl). 5.10. DATA MODELING 95 Create a localized attribute localized attributes are maps, they are declared under core-items.xml : <maptype code="localized:java.lang.String" argumenttype="Language" returntype="java.lang.String" autocreate="true" generate="false"/> <maptype code="localized:java.lang.Integer" argumenttype="Language" returntype="java.lang.Integer" autocreate="true" generate="false"/> <maptype code="localized:java.lang.Boolean" argumenttype="Language" returntype="java.lang.Boolean" autocreate="true" generate="false"/> .... Therefore to declare an attribute as localized, first check if the type has a declared localized equivalent (SAP Hybris 6 has all atomic types, bigInteger, bigDecimal, date range and media). If not you could create your own localized type, for example lets say you need to localize groups : <maptype code="localized:Group" argumenttype="Language" returntype="UserGroup" autocreate="true" generate="false"/> Enumerations SAP Hybris gives you the possibility to use attributes with a predefined list of possible values. This works exactly like Java enumeration but at a database level. Enumeration types within SAP Hybris could be dynamic, it means you could add new values during runtime. If you configure your enumeration not to be dynamic SAP Hybris will use standard Java enumeration instead of SAP Hybris enumerations How to create a new enumeration ? Add it under the targeted items.xml file: 96 CHAPTER 5. COMMERCE AND ACCELERATOR <enumtype code="OrderStatus" autocreate="true" generate="true" dynamic="true"> <value code="CREATED"/> <value code="ON_VALIDATION"/> <value code="COMPLETED"/> <value code="CANCELLED"/> </enumtype> You can add new values from a different extension if needed, for example the OrderStatus enumeration is defined within the core extension, from my extension hybhubaddon I’m adding a new value : <enumtype code="OrderStatus" autocreate="false" generate="false" dynamic="true"> <value code="MYSTATUS"/> </enumtype> Enumeration Service SAP Hybris provides a service to deal with enumeration, de.hybris.platform.enumeration.EnumerationService, here is an example (Groovy compatible) to get the new OrderStatus we just created (you need to build and update your SAP Hybris platform) : import de.hybris.platform.enumeration.EnumerationService; import de.hybris.platform.core.enums.OrderStatus; EnumerationService es = spring.getBean("enumerationService"); OrderStatus os = es.getEnumerationValue("ORDERSTATUS", "MYSTATUS"); println os.getCode(); Dynamic attributes In some cases, you will need dynamic attributes, for example under the address model you have one field for each address characteristics, it would be nice to have directly under your model object a getter for a formatted address string, right? That’s when you use a dynamic attribute when you need attributes on your data model objects which aren’t persisted to the database. Use dynamic attributes to: • Format data like addresses or names • Create a calculated field • Combine multiple non dynamic attributes 5.10. DATA MODELING 97 Don’t use a dynamic attribute to: • Integrate your model with any external systems or database • Trigger any intensive operation, it would slow down the platform as getters and setters are frequently called by the cache mechanism • Implement your own SQL query Implement a dynamic attribute You will need to: • Create a value provider, it musts implement ‘DynamicAttributeHandler’ interface • Write the logic for the getter not the setter • Write the value provider using Spring • Add the value provider under your item xml definition file, setting the persistence to dynamic with the attribute handler parameter equal to your spring bean • If you just need the getter and not the setter you can customize the modifier attribute Let’s add a dynamic attribute to our PostComment item type, we want to have a summary of each comment with something like "PENDING - Luke T - Hi, I recently bought a new camera to start doing prof..." : <itemtype code="PostComment" extends="GenericItem" autocreate="true" generate="true"> <deployment table="postcomment" typecode="11003" /> <attributes> <attribute qualifier="author" type="User"> <persistence type="property"/> </attribute> <attribute qualifier="content" type="java.lang.String"> <persistence type="property"> <columntype> <value>HYBRIS.LONG_STRING</value> </columntype> </persistence> <modifiers search="false" optional="false"/> </attribute> <attribute qualifier="status" type="CommentStatus"> <persistence type="property" /> </attribute> <attribute qualifier="summary" type="java.lang.String"> <persistence type="dynamic" ֒→ attributeHandler="dynamicAttributesPostCommentSummaryHandler"/> </attribute> </attributes> </itemtype> Attribute Handler value is the name of the Spring bean that is responsible for the dynamic attribute. 98 CHAPTER 5. COMMERCE AND ACCELERATOR Next step is to create the class to handle the dynamic attribute, we will call it ‘DynamicAttributesPostCommentSummaryHandler’: package com.hybhub.hybhubaddon.servicelayer.dynamicattributes; import de.hybris.platform.servicelayer.model.attribute.DynamicAttributeHandler; import com.hybhub.hybhubaddon.model.PostCommentModel; public class DynamicAttributesPostCommentSummaryHandler implements ֒→ DynamicAttributeHandler<String, PostCommentModel> { final static String SEP = " - "; final static int MAX_CONTENT = 100; ֒→ ֒→ @Override public String get(final PostCommentModel postComment) { final StringBuilder sb = new StringBuilder(postComment.getStatus().getCode()); sb.append(SEP); sb.append(postComment.getAuthor().getDisplayName()); sb.append(SEP); sb.append(postComment.getContent().length() > MAX_CONTENT ? postComment.getContent().substring(0, MAX_CONTENT) : postComment.getContent()); return sb.toString(); } @Override public void set(final PostCommentModel postComment, final String str) { throw new IllegalAccessError("Cannot set that attribute [Summary]"); } } This class needs to implement ‘DynamicAttributeHandler’ interface, it will provide two methods, a getter and a setter, here we will just use the getter and throw an exception if the setter is used. Next step is to register this newly created class in a bean context: <bean id="dynamicAttributesPostCommentSummaryHandler" class="com.hybhub.hybhubaddon.servicel ⌋ ayer.dynamicattributes.DynamicAttributesPostCommentSummaryHandler" ֒→ /> ֒→ And then build and run an update on your system, to try it we will use a groovy script, go to http://localhost:9001/console/scripting/ : 5.10. DATA MODELING import import import import import 99 com.hybhub.hybhubaddon.model.PostCommentModel de.hybris.platform.servicelayer.model.ModelService de.hybris.platform.servicelayer.search.FlexibleSearchService java.util.List de.hybris.platform.servicelayer.user.UserService UserService us = spring.getBean("userService") ModelService ms = spring.getBean("modelService") FlexibleSearchService fss = spring.getBean("flexibleSearchService") PostCommentModel pcm = ms.create(PostCommentModel.class) pcm.setAuthor(us.getAdminUser()) List<PostCommentModel> pcms = fss.getModelsByExample(pcm) println pcms.get(0).getSummary() If you open the output tab you should see : PENDING - Administrator - This is the content of the PostComment Deployment SAP Hybris item types are stored inside database’s tables, each instance is stored as one row within the database. The database table where instances of a type are stored is called the type deployment and is specified within items.xml files. Every type must have a deployment table to store its instances. Types inherits deployment tables from their super types. The deployment that is active for a given type is the deployment specified closest to the type in the type’s hierarchy. The topmost deployment is GenericItem, which is therefore the default deployment. This means if a type has no explicit specification of deployment, that type’s instances are deployed in the same table as GenericItem. This means that the default deployment of any subtype, which you extend from GenericItem, is the deployment of GenericItem. In other words: if you do not specify a deployment for a subtype of GenericItem, the instances of that subtype are stored in the same table as instances of GenericItem. For example, the User and Product types in SAP Hybris Commerce are subtypes of GenericItem. If there were no deployment specified for User and Product, all Users and Products instances would be written into one single database table. Firstly, this is not intuitive. Secondly, storing instances of many different types in one single database table causes that database table to have quite a lot of columns to store all attributes from all these types (a User has different attributes than a Product, and both types needs to store attribute values). Not declaring a deployment table for a new item type (extending GenericItem) would lead the build to fail : [ycheckdeployments] No deployment defined for relation <RELATIONNAME> in file: <FILENAME> If you want to force this behavior besides the performance and schema complexity bottlenecks you could use : build.development.mode=true To specify your own deployment table: 100 CHAPTER 5. COMMERCE AND ACCELERATOR <itemtype code="Blog" extends="GenericItem" autocreate="true" generate="true"> <deployment table="blog" typecode="11001" /> ... </itemtype> The deployment attribute also has a propertytable attribute, this is to configure different advanced database features (database attribute types). Indices SAP Hybris gives you the ability to add database indices on item types, indices have a name and one or more keys (composite indices), here is an example of an index on the blog type: <indexes> <index name="blogIDX" unique="true"> <key attribute="code" lower="true"/> <key attribute="active"/> </index> </indexes> SAP Hybris will run the following SQL command (may vary upon the targeted database, here we use HSQL): CREATE UNIQUE INDEX blogIDX_11001 ON BLOG (P_CODE, P_ACTIVE); Types in DB When SAP Hybris generates the SQL commands to initialize the system, it will select the right destination types based on the current database, there is a out of the box mapping for this, have a look at the file core-advanced-deployment.xml, for example for Mysql : 5.10. DATA MODELING 101 <database-schema database="mysql" primary-key="primary key" null="" not-null="not null" > <type-mapping type="java.lang.String" persistence-type="varchar(255)" /> <type-mapping type="String" persistence-type="varchar(255)" /> <type-mapping <type-mapping <type-mapping <type-mapping <type-mapping <type-mapping <type-mapping <type-mapping type="java.lang.Float" persistence-type="float(20,5)" /> type="java.lang.Double" persistence-type="double" /> type="java.lang.Byte" persistence-type="smallint" /> type="java.lang.Character" persistence-type="smallint" /> type="java.lang.Short" persistence-type="integer" /> type="java.lang.Boolean" persistence-type="tinyint(1)" /> type="java.lang.Long" persistence-type="bigint" /> type="java.lang.Integer" persistence-type="integer" /> <type-mapping <type-mapping <type-mapping <type-mapping <type-mapping <type-mapping <type-mapping <type-mapping type="float" persistence-type="float(20,5) DEFAULT 0" /> type="double" persistence-type="double DEFAULT 0" /> type="byte" persistence-type="smallint DEFAULT 0" /> type="char" persistence-type="smallint DEFAULT 0" /> type="short" persistence-type="integer DEFAULT 0" /> type="boolean" persistence-type="tinyint(1) DEFAULT 0" /> type="long" persistence-type="bigint DEFAULT 0" /> type="int" persistence-type="integer DEFAULT 0" /> <type-mapping type="java.util.Date" persistence-type="datetime" /> <type-mapping type="java.math.BigDecimal" persistence-type="DECIMAL(30,8)" /> <type-mapping type="java.io.Serializable" persistence-type="LONGBLOB" /> <type-mapping type="HYBRIS.LONG_STRING" persistence-type="TEXT" /> <type-mapping type="HYBRIS.COMMA_SEPARATED_PKS" persistence-type="TEXT" /> <type-mapping type="HYBRIS.PK" persistence-type="BIGINT" /> </database-schema> To see what commands SAP Hybris uses to create your item types you can use the dry-run functionality ([Dry Run](http://localhost:9001/platform/dryrun)), for example for our Post type on HSQL SAP Hybris will execute : 102 CHAPTER 5. COMMERCE AND ACCELERATOR CREATE CACHED TABLE post ( hjmpTS BIGINT, createdTS TIMESTAMP, modifiedTS TIMESTAMP, TypePkString BIGINT, OwnerPkString BIGINT, PK BIGINT NOT NULL, p_blog BIGINT, p_date TIMESTAMP, p_author BIGINT, p_keywords LONGVARBINARY, p_status BIGINT, aCLTS BIGINT DEFAULT 0, propTS BIGINT DEFAULT 0, PRIMARY KEY (PK) ); CREATE CACHED TABLE postlp ( ITEMPK BIGINT, ITEMTYPEPK BIGINT, LANGPK BIGINT, p_title NVARCHAR(255), p_content LONGVARCHAR, PRIMARY KEY (ITEMPK, LANGPK) ); Flexible Search Query FSQL or flexible search query language helps you looking for items and types using a SQL-like syntax. Why is SAP Hybris doing this? SAP Hybris is compatible with various Databases (HSQL, Mysql, Oracle and Microsoft SQL Server) but each of them implements a different SQL version, SAP Hybris needs to have a database abstraction layer, allowing you to write a FSQL query that would run on any compatible database (SAP Hana, Mysql, SQL Server, HSQL, Postgres). It abstracts from you details such as column and table names and it is translated into native SQL statements on execution. Allowing nearly every aspect of SQL SELECT statements (but no data manipulation is possible, update or delete are not available). Warning! Flexible Search Queries are only for search, they cannot do any SQL DML or SQL DDL queries. Warning! Flexible Search Queries are executed on the database trough a SQL query, therefore it is not possible to execute flexible search queries on Jalo or dynamic attributes. 5.11. FLEXIBLE SEARCH QUERY 103 Cache SAP Hybris is caching flexible search query results, that is the reason why you should always deactivate the query cache on your database when you go on production. On Mysql it’s deactivated by default, but if you are not sure simply add this line to your my.cnf configuration: query_cache_size=0 Session and restrictions Flexible search queries are executed in a session context, different factors can influence the number of search results: • The session user, by default the session is assigned the anonymous user, you could assign the session to any available user (including admin user) • Restrictions, they are fragments of the SQL where clause that are applied for a given item type. No restrictions are applied when you run a flexible search query with an admin session. Restrictions Do not get flexible search query restrictions mixed up with CMS restrictions ! On is only adding filters in the SQL where clause while the other has its own java implementation. Restrictions are manageable from the SearchRestrictionService: 104 CHAPTER 5. COMMERCE AND ACCELERATOR package de.hybris.platform.search.restriction; import import import import import de.hybris.platform.core.model.security.PrincipalModel; de.hybris.platform.core.model.type.ComposedTypeModel; de.hybris.platform.core.model.type.SearchRestrictionModel; de.hybris.platform.search.restriction.session.SessionSearchRestriction; java.util.Collection; public interface SearchRestrictionService { void addSessionSearchRestrictions(Collection<SessionSearchRestriction> arg0); void addSessionSearchRestrictions(SessionSearchRestriction... arg0); void clearSessionSearchRestrictions(); void disableSearchRestrictions(); void enableSearchRestrictions(); Collection<SearchRestrictionModel> getActiveSearchRestrictions(PrincipalModel arg0, ֒→ boolean arg1, Collection<ComposedTypeModel> arg2); Collection<SearchRestrictionModel> getInactiveSearchRestrictions(PrincipalModel ֒→ arg0, boolean arg1, Collection<ComposedTypeModel> arg2); Collection<SearchRestrictionModel> getSearchRestrictions(PrincipalModel arg0, ֒→ boolean arg1, Collection<ComposedTypeModel> arg2); Collection<SearchRestrictionModel> getSearchRestrictionsForType(ComposedTypeModel arg0); ֒→ Collection<SessionSearchRestriction> getSessionSearchRestrictions(); Collection<SessionSearchRestriction> getSessionSearchRestrictions(ComposedTypeModel ֒→ arg0); boolean hasRestrictions(PrincipalModel arg0, boolean arg1, ComposedTypeModel arg2); boolean isSearchRestrictionsEnabled(); void removeSessionSearchRestrictions(Collection<SessionSearchRestriction> arg0); void removalAllSessionSearchRestrictions(); } You could activate or deactivate the search restriction from the restriction service, or create/update/delete existing restrictions. Another way to create restrictions is Impex: INSERT_UPDATE SearchRestriction;code[unique=true];name[lang=en];query;principal(UID);restric ⌋ tedType(code);active;generate ֒→ ;Restriction_Code;Restriction Name;{Active} = true;anonymous;Language;true;true Here we create a restriction for anonymous users, they would see only the active languages. For example when you run this flexible search query (anonymous user): 5.11. FLEXIBLE SEARCH QUERY 105 select {name} from {Language} SAP Hybris will execute this SQL query (pay attention to the WHERE clause where our restriction is added: SELECT lp_t0.p_name FROM languages item_t0 JOIN languageslp lp_t0 ON item_t0.PK = lp_t0.ITEMPK AND lp_t0.LANGPK =? WHERE (item_t0.TypePkString=? AND ( item_t0.p_active = true)) You could also create restriction from the hmc or the backoffice, they are called Personalization Rule Syntax Flexible search queries are very similar to SQL queries, although it is dedicated only for search. It abstracts all the database details (columns and tables name), it is translated into native SQL query on execution. The basic syntax of a flexible search query looks like this: SELECT <selects> FROM <types> ( WHERE <conditions> )? ( ORDER BY <order> )? • Select is mandatory • types is mandatory • conditions is optional • order is optional All standard select SQL keywords are available (ORDER BY,AS,IS NULL,...) and you also need to remember this: • By default SAP Hybris will search for the given type and any subtype, if you want the query only to search for the given item type you need to use an exclamation mark, select {code} from {Product!} • JOIN can be made inside the curly braces, {Product as prd join PriceRow as price on {prd:code} = {price:productId}} • WHERE clauses can execute sub queries using double curly braces • To work with localized attributes you need to specify the language {Name[en]} to select an attribute. If you want to match a localized attribute inside a WHERE clause you can use {Desc[de]} to match exactly one language or {Desc[ANY} to match any language 106 CHAPTER 5. COMMERCE AND ACCELERATOR • Parameters are passed using the question mark, where {date} > ?startDate • Outer join parameter, it is used to include matches with missing rows inside the localized table (xxxxxLP), for example : SELECT {p:PK} FROM {Product AS p} WHERE {p:description[en]:o} LIKE ’%text%’ OR {p:description[de]:o} LIKE ’%text%’ If you use ! after a type SAP Hybris won’t load any sub types, for example from {product!} will load only Product type items. Examples All users select * from {User} Category by catalog version select {c:pk} from {Category as c} where{c:catalogVersion}=?catalogVersion All Users ordered by desc name select {name[en]} from {User} order by {name} DESC Select distinct products select distinct {name[en]} from {Product} order by {name[en]} asc Select products and order them by price 5.11. FLEXIBLE SEARCH QUERY select distinct {prd.name[en]},{price.price} from {Product as prd join PriceRow as price on ֒→ {prd:code} = {price:productId}} order by {price.price} asc Select all products with an online date past 1st January select {name[en]}, {OnlineDate} from {Product} where {code} in ( {{ select {code} from ֒→ {Product} where {OnlineDate} > ’2016-01-01 00:00:00.0’ }} ) Tip 19: Usage You can test all flexible search queries directly from the HAC. Flexible Search Service package de.hybris.platform.servicelayer.search; import import import import import import import de.hybris.platform.core.model.ItemModel; de.hybris.platform.servicelayer.search.FlexibleSearchQuery; de.hybris.platform.servicelayer.search.RelationQuery; de.hybris.platform.servicelayer.search.SearchResult; de.hybris.platform.servicelayer.search.TranslationResult; java.util.List; java.util.Map; public interface FlexibleSearchService { <T> T getModelByExample(T arg0); <T> List<T> getModelsByExample(T arg0); <T> SearchResult<T> search(FlexibleSearchQuery arg0); <T> SearchResult<T> search(String arg0); <T> SearchResult<T> search(String arg0, Map<String, ? extends Object> arg1); <T> SearchResult<T> searchRelation(ItemModel arg0, String arg1, int arg2, int arg3); <T> SearchResult<T> searchRelation(RelationQuery arg0); <T> T searchUnique(FlexibleSearchQuery arg0); TranslationResult translate(FlexibleSearchQuery arg0); } 107 108 CHAPTER 5. COMMERCE AND ACCELERATOR Create a Flexible Search query import import import import de.hybris.platform.servicelayer.search.FlexibleSearchService de.hybris.platform.servicelayer.search.FlexibleSearchQuery de.hybris.platform.servicelayer.search.SearchResult de.hybris.platform.core.model.product.ProductModel FlexibleSearchService fsqs = spring.getBean("flexibleSearchService") String q = "select {code[en]} from {Product} where {code[ANY]} like ?code" FlexibleSearchQuery myFlexibleSearchQuery = new FlexibleSearchQuery(q) myFlexibleSearchQuery.addQueryParameter(’code’, ’109%’) SearchResult result = fsqs.<ProductModel>search(myFlexibleSearchQuery) println result.getTotalCount() This would print 4 on my system. Product Variants & Category Variants Product Variants When you work with products you often need to model variants, a variant represent a variation of a base product. It has attributes from the base product, and defines its own variation attributes. For example let’s define laptops, they are Products with specific attributes like the screen size and variants with specific attributes like the disk capacity. 5.12. PRODUCT VARIANTS & CATEGORY VARIANTS 109 <enumtypes> <enumtype code="ScreenSize"> <value code="THIRTEEN" /> <value code="FOURTEEN" /> <value code="FIFTEEN" /> </enumtype> </enumtypes> <itemtypes> <itemtype code="Laptop" extends="Product" autocreate="true" generate="true"> <attributes> <attribute qualifier="ScreenSize" type="ScreenSize"> <persistence type="property" /> </attribute> </attributes> </itemtype> <itemtype code="LaptopVariant" extends="VariantProduct" autocreate="true" generate="true" jaloclass="com.hybhub.jalo.LaptopVariant"> <attributes> <attribute qualifier="diskCapacity" type="int" ֒→ metatype="VariantAttributeDescriptor"> <persistence type="property"/> </attribute> </attributes> </itemtype> </itemtypes> ֒→ To mark an attribute as a variant descriptor, you need to use metatype="VariantAttributeDescriptor" This has one major downside, you won’t be able to access the screen size attribute from your product variant ! Why, because variant product has a direct line of inheritance to Product and our screen size attribute is defined in the laptop subtype. You are probably thinking that you could simply move the screen size attribute into the laptop variant item. <itemtype code="LaptopVariant" extends="VariantProduct" autocreate="true" generate="true" jaloclass="com.hybhub.jalo.Car"> <attributes> <attribute qualifier="diskCapacity" type="int" ֒→ metatype="VariantAttributeDescriptor"> <persistence type="property"/> </attribute> <attribute qualifier="ScreenSize" type="ScreenSize"> <persistence type="property" /> </attribute> </attributes> </itemtype> This is a bad idea, since it is described on the variant level, you need to define a screen size for all 110 CHAPTER 5. COMMERCE AND ACCELERATOR your variants, even if they all have the same. Why not define the screen size directly on the product type ? well it’s also a bad design since you might have products that are not laptops ! Diagram showing what the problem with product variant is: Figure 5.9: Product Variant Diagram Category Variants A different approach is to build category variants with specific attributes. For example our laptop category has a screen size attribute: <itemtype code="LaptopCategory" extends="VariantValueCategory"> <attributes> <attribute qualifier="ScreenSize" type="ScreenSize"> <persistence type="property" /> </attribute> </attributes> </itemtype> <itemtype code="LaptopVariant" extends="VariantProduct" autocreate="true" generate="true"> <attributes> <attribute qualifier="diskCapacity" type="int" ֒→ metatype="VariantAttributeDescriptor"> <persistence type="property"/> </attribute> </attributes> </itemtype> How to create a new product : You cannot create your first VariantCategory from an impex unless you skip the service layer VariantValueCategoryValidateInterceptor. You could also manually create one from the HMC and then use it as the super category 5.13. CLASSIFICATION ATTRIBUTES 111 $productCatalog=electronicsProductCatalog $productCatalogName=Electronics Product Catalog $catalogVersion=catalogversion(catalog(id[default=$productCatalog]),version[default=’Staged’ ⌋ ])[unique=true,default=$productCatalog:Staged] ֒→ $supercategories=supercategories(code, $catalogVersion) $baseProduct=baseProduct(code,$catalogVersion) $approved=approvalstatus(code)[default=’check’] # Insert Categories INSERT_UPDATE LaptopCategory;code[unique=true];name[lang=en];ScreenSize(code);$supercategori ⌋ ֒→ es;$catalogVersion ;000100;Laptop;THIRTEEN;1 ;000101;Laptop;FIFTEEN;1 INSERT_UPDATE Product;code[unique=true];$supercategories;manufacturerName;manufacturerAID;un ⌋ ֒→ it(code);variantType(code);$catalogVersion;$approved ;000010;000100;Apple;MBP-13;pieces;LaptopVariant ;000011;000100;Apple;MBP-15;pieces;LaptopVariant INSERT_UPDATE LaptopVariant;code[unique=true];$baseProduct;unit(code);diskCapacity;approvals ⌋ tatus(code)[default=’approved’];$catalogVersion ֒→ ;000100_256;000010;pieces;256; ;000100_512;000010;pieces;512; ;000101_256;000011;pieces;256; ;000101_512;000011;pieces;512; We now have a base laptop which holds the screen size through the category variant, and a variant attribute disk capacity. We did all this without changing the product model ! The downside of this is that the value of the screen size is held by the category and not the product. Classification attributes SAP Hybris gives you the possibility to classify your product, what is the difference between classification and categorization ? • You can categorize your laptop with : Electronic -> Computer -> Laptop. • You can classify your laptop with its color, its screen size, its disk capacity... The difference is that categorization is vertical while classification is horizontal. For example to classify our laptop : 112 CHAPTER 5. COMMERCE AND ACCELERATOR $productCatalog=electronicsProductCatalog $productCatalogName=Electronics Product Catalog $catalogVersion=catalogversion(catalog(id[default=$productCatalog]),version[default=’Staged’ ⌋ ])[unique=true,default=$productCatalog:Staged] ֒→ $classCatalogVersion=catalogversion(catalog(id[default=’ElectronicsClassification’]),version ⌋ [default=’1.0’])[unique=true,default=’ElectronicsClassification:1.0’] ֒→ $classSystemVersion=systemVersion(catalog(id[default=’ElectronicsClassification’]),version[d ⌋ ֒→ efault=’1.0’])[unique=true] $class=classificationClass(ClassificationClass.code,\$classCatalogVersion)[unique=true] $supercategories=supercategories(code, $catalogVersion) $supercategoriesRel=source(code, $classCatalogVersion)[unique=true] $categories=target(code, $catalogVersion)[unique=true] $attribute=classificationAttribute(code,$classSystemVersion)[unique=true] $unit=unit(code,$classSystemVersion) $approved=approvalstatus(code)[default=’check’] $baseProduct=baseProduct(code,$catalogVersion) $clAttrModifiers=system=’ElectronicsClassification’,version=’1.0’,translator=de.hybris.platf ⌋ ֒→ orm.catalog.jalo.classification.impex.ClassificationAttributeTranslator,lang=en # Insert Classifications INSERT_UPDATE ClassificationClass;$classCatalogVersion;code[unique=true];name[lang=en];allow ⌋ ֒→ edPrincipals(uid)[default=’customergroup’] ;;2002;Laptop # Insert Classification Attributes INSERT_UPDATE ClassificationAttribute;$classSystemVersion;code[unique=true];name[lang=en] ;;screen size;Screen Size INSERT_UPDATE ClassAttributeAssignment;$class;$attribute;position;$unit;attributeType(code[d ⌋ efault=string]);multiValued[default=false];range[default=false];localized[default=false] ֒→ ;2002;screen size;1;16;number;false;false INSERT_UPDATE CategoryCategoryRelation;$categories;$supercategoriesRel ;1;2002 $feature1=@screen size[$clAttrModifiers]; INSERT_UPDATE Product;code[unique=true];$supercategories;manufacturerName;manufacturerAID;un ⌋ it(code);variantType(code);$feature1;$catalogVersion;$approved ֒→ ;000010;1;Apple;MBP-13;pieces;LaptopVariant;13 ;000011;1;Apple;MBP-15;pieces;LaptopVariant;15 INSERT_UPDATE LaptopVariant;code[unique=true];$baseProduct;unit(code);diskCapacity;approvals ⌋ ֒→ tatus(code)[default=’approved’];$catalogVersion ;000100_256;000010;pieces;256; ;000100_512;000010;pieces;512; ;000101_256;000011;pieces;256; ;000101_512;000011;pieces;512; The same product could be classified from two different classification trees ! It offers a lot of flexibility and it can be modified without changing the SAP Hybris model type! Chapter 6 Order management 113 114 CHAPTER 6. ORDER MANAGEMENT Through this chapter, you will learn how to work with orders within a SAP Hybris e-commerce system. Business process SAP Hybris has an out-of-the-box "process engine" (yacceleratorfulfilmentprocess extension). Business processes help you define a list of actions that depend on another, and terminate with a status that decides the action, which will follow. For example, the first steps of the order-process.xml business process is as follow : ![Order Business Process](images/order-business-process.png) Let’s create our own business process. We won’t do anything else to create a few actions. First we need to define our business process: <itemtype code="MyProcess" autocreate="true" generate="true" extends="BusinessProcess"> <attributes> <attribute qualifier="fail" type="boolean"> <persistence type="property" /> </attribute> <attribute qualifier="error" type="boolean"> <persistence type="property" /> </attribute> </attributes> </itemtype> You now need to define your process, create a new file named myprocess-process.xml under /resources/hybhubaddon/process/, my extension is hybhubaddon : 6.1. BUSINESS PROCESS <?xml version="1.0" encoding="utf-8"?> <process xmlns="http://www.hybris.de/xsd/processdefinition" start="firstStep" ֒→ name="myProcess" processClass="com.hybhub.hybhubaddon.model.MyProcessModel"> <action id="firstStep" bean="firstStepAction"> <transition name="OK" to="secondStep"/> <transition name="NOK" to="abortStep"/> </action> <action id="secondStep" bean="secondStepAction"> <transition name="OK" to="success"/> <transition name="NOK" to="error"/> </action> <action id="abortStep" bean="abortStepAction"> <transition name="OK" to="failed"/> </action> <end id="error" state="ERROR">All went wrong.</end> <end id="failed" state="FAILED">Couldn’t run the process.</end> <end id="success" state="SUCCEEDED">All good.</end> </process> There are 4 different types of nodes: • Wait nodes - wait for a subprocess or an external process result; • Notify nodes - inform a user or user group about the state of a process; • Action nodes - carry out process logic and permit alternative actions to be carried out; • End nodes - end the process and store state in a process item. First step, will decide what to do based on the fail flag 115 116 CHAPTER 6. ORDER MANAGEMENT package com.hybhub.hybhubaddon.actions.myprocess; import de.hybris.platform.processengine.action.AbstractSimpleDecisionAction; import de.hybris.platform.task.RetryLaterException; import com.hybhub.hybhubaddon.model.MyProcessModel; public class FirstStepAction extends AbstractSimpleDecisionAction<MyProcessModel> { @Override public Transition executeAction(final MyProcessModel myProcess) throws ֒→ RetryLaterException, Exception { if (myProcess.isFail()) { return Transition.NOK; } return Transition.OK; } } Second step, will return success or error based on the error flag package com.hybhub.hybhubaddon.actions.myprocess; import de.hybris.platform.processengine.action.AbstractSimpleDecisionAction; import de.hybris.platform.task.RetryLaterException; import com.hybhub.hybhubaddon.model.MyProcessModel; public class SecondStepAction extends AbstractSimpleDecisionAction<MyProcessModel> { @Override public Transition executeAction(final MyProcessModel myProcess) throws ֒→ RetryLaterException, Exception { if (myProcess.isError()) { return Transition.NOK; } return Transition.OK; } } Abort step will log something and return OK 6.1. BUSINESS PROCESS package com.hybhub.hybhubaddon.actions.myprocess; import de.hybris.platform.processengine.action.AbstractSimpleDecisionAction; import de.hybris.platform.task.RetryLaterException; import org.apache.log4j.Logger; import com.hybhub.hybhubaddon.model.MyProcessModel; public class AbortStepAction extends AbstractSimpleDecisionAction<MyProcessModel> { private final static Logger LOG = Logger.getLogger(AbortStepAction.class); ֒→ @Override public Transition executeAction(final MyProcessModel myProcess) throws RetryLaterException, Exception { LOG.error("The process " + myProcess.getCode() + " failed !"); return Transition.OK; } } Now we need to wire them all using Spring xml configuration: <bean id="myProcess" ֒→ class="de.hybris.platform.processengine.definition.ProcessDefinitionResource" > <property name="resource" ֒→ value="classpath:/hybhubaddon/process/myprocess-process.xml"/> </bean> <bean id="firstStepAction" class="com.hybhub.hybhubaddon.actions.myprocess.FirstStepAction" ֒→ parent="abstractAction" /> <bean id="secondStepAction" ֒→ class="com.hybhub.hybhubaddon.actions.myprocess.SecondStepAction" ֒→ parent="abstractAction" /> <bean id="abortStepAction" class="com.hybhub.hybhubaddon.actions.myprocess.AbortStepAction" ֒→ parent="abstractAction" /> Let’s test our new business process using a Groovy script: 117 118 CHAPTER 6. ORDER MANAGEMENT import com.hybhub.hybhubaddon.model.MyProcessModel import de.hybris.platform.servicelayer.model.ModelService import de.hybris.platform.processengine.BusinessProcessService ModelService modelService = spring.getBean("modelService") BusinessProcessService bpService = spring.getBean("businessProcessService") MyProcessModel myprocess = bpService.createProcess("myProcess" + System.currentTimeMillis(), "myProcess") myprocess.setFail(true) myprocess.setError(true) modelService.save(myprocess) bpService.startProcess(myprocess) modelService.refresh(myprocess) println myprocess.state You can play around with the two flags and check the result in the HMC. You can see the process state, and the log for each step. Fulfillment process In this chapter, we study the order fulfillment process. This process is triggered when the customer triggers the place order action, then SAP Hybris will execute a set of actions to fulfill the order. Here is the list of all possible transitions for the order fulfillment process : 1. Check order, verify required data. 2. Check authorize payment, check that the current order has a payment info attached. 3. Reserve amount, change the status of the order to PAYMENT_AMOUNT_RESERVED. 4. Send payment failed notification, publish an event for the failed authorization. 5. Check transaction review status, check that the transaction has been authorized. 6. Fraud check, check couple of criteria to detect potential fraud (excessive amount, black listed customer,....). 7. Notify customer about fraud, contact the customer about the fraudulent order. 8. Manual order check CSA, prepare the order to be manually checked by a customer agent. 9. Order manual checked, take the decision to follow the fulfillment process after the manual check or not. 10. Schedule for cleanUp, try to cleanup a fraudulent or failed order. 11. Cancel order. 6.3. SHOPPING CART HANDLING 119 12. Send order placed notification, send a notification to the customer after his order has been successfully placed. 13. Take payment action, capture the payment. 14. Send payment failed notification, send a message to the customer after a failed capture attempt. 15. Split order, try to split the order in multiple consignment. 16. Is process completed, watch for the waitForWarehouseSubprocessEnd to end before moving on with the fulfillment. 17. Send order completed notification, order has been shipped and is a success. you can find the order fulfillment process under /yacceleratorfulfilmentprocess/resources/yacceleratorfulfilm process.xml Shopping cart handling First it’s important to understand that Carts and Orders both extend Abstract Orders. They contain equivalent date but a cart is a temporary and volatile object created when a customer is shopping online. When the session times out and when the clean cart job is processed, carts are discarded. Orders are created from carts but they are persistent once they have been placed by a customer. It’s important to understand that orders are created by making a copy of the cart, they are different objects within SAP Hybris, even the order entries are copied. 1. A customer access the store front, Hybris will create a new session, at that point no carts have been created yet. 2. The customer adds a product. (a) The cart service (cartService) will try to load the cart from the session. In our case the customer has no cart yet, so the cart factory (cartFactory) will handle the cart creation (generate a code, add user, add currency, set the date). It could be a standard persisted cart or a in memory cart (not persisted), for this you need to configure default.session.cart.type (Cart, InMemoryCart or your own implementation). (b) The commerce add to cart strategy (CommerceAddToCartStrategy) will add a new entry into the cart entry collection, then it will mark the cart as not calculated. (c) The calculation service (CalculationService) will calculate the total price of the cart. (a) Before the checkout process, the customer will log-in or create a new account (guest account for example). During this log-in phase the cart will be attached to the customer account. (b) During the checkout process the customer will add or select a delivery address, attach payment info... (c) When the Customer places the order, the order is created by making a copy of the cart (order entries as well). 120 CHAPTER 6. ORDER MANAGEMENT Checkout flow SAP Hybris gives you the possibility to configure the entire checkout flow independently for each store (since V5.2). All configuration is done using Spring beans and interface implementation. SAP Hybris checkout is configured with: 1. Checkout steps, they hold information about the next, previous and current link, about the validator to use (oftenly the controller is marked with a PreValidateStep annotation). 2. A validate result map, based on the different validation success or fail the checkout flow will often need to redirect the process to a given step, to hold the different redirection the checkout group has a string map with entry like REDIRECT_TO_DELIVERY_ADDRESS that would point to a spring MVC redirection redirect:/checkout/multi/delivery-address/add 3. The checkout flow progress bar, it is a map with each step ordered by the number in which they are populated under the progress bar To create a new checkout flow you need to create a new bean from the class de.hybris.platform.acceleratorstorefrontcom this is an example of the out of the box checkout flow configured with an extra step. Since multiple checkout flows can live under the same system instead of updating the existing flow we declare a new one named defaultHybhubCheckoutGroup with a alias hybhubCheckoutGroup: 6.4. CHECKOUT FLOW 121 <bean id="REDIRECT_TO_HYBHUB" class="java.lang.String"> <constructor-arg value="redirect:/checkout/multi/hybhub"/> </bean> <alias name="defaultHybhubCheckoutGroup" alias="hybhubCheckoutGroup" /> <bean id="defaultHybhubCheckoutGroup" ֒→ class="de.hybris.platform.acceleratorstorefrontcommons.checkout.steps.CheckoutGroup"> <property name="groupId" value="hybhubCheckoutGroup"/> <property name="checkoutStepMap"> <map merge="true"> <entry key="multi" value-ref="hybhubMultiStepCheckout"/> <entry key="hybhub" value-ref="hybhubStep"/> <entry key="delivery-address" ֒→ value-ref="hybhubDeliveryAddressCheckoutStep"/> <entry key="delivery-method" value-ref="deliveryMethodCheckoutStep"/> <entry key="pickup-location" value-ref="pickupLocationCheckoutStep"/> <entry key="payment-method" value-ref="paymentMethodCheckoutStep"/> <entry key="summary" value-ref="summaryCheckoutStep"/> </map> </property> <property name="validationResultsMap"> <map merge="true"> <entry key="FAILED" value-ref="REDIRECT_TO_CART"/> <entry key="REDIRECT_TO_DELIVERY_ADDRESS" ֒→ value-ref="REDIRECT_TO_DELIVERY_ADDRESS"/> <entry key="REDIRECT_TO_PICKUP_LOCATION" ֒→ value-ref="REDIRECT_TO_PICKUP_LOCATION"/> <entry key="REDIRECT_TO_CART" value-ref="REDIRECT_TO_CART"/> <entry key="REDIRECT_TO_PAYMENT_METHOD" ֒→ value-ref="REDIRECT_TO_PAYMENT_METHOD"/> <entry key="REDIRECT_TO_DELIVERY_METHOD" ֒→ value-ref="REDIRECT_TO_DELIVERY_METHOD"/> </map> </property> <property name="checkoutProgressBar"> <map merge="true"> <entry key="1" value-ref="hybhubStep"/> <entry key="2" value-ref="deliveryAddressCheckoutStep"/> <entry key="3" value-ref="deliveryMethodCheckoutStep"/> <entry key="4" value-ref="paymentMethodCheckoutStep"/> <entry key="5" value-ref="defaultSummaryCheckoutStep"/> </map> </property> </bean> Out of the box Hybris already has these steps : • deliveryMethodCheckoutStep • pickupLocationCheckoutStep • paymentMethodCheckoutStep • summaryCheckoutStep It also has multiStepCheckout and deliveryAddressCheckoutStep; but because we need to update their transitions, we declare new beans. Here are the 3 steps we need: 122 CHAPTER 6. ORDER MANAGEMENT <alias name="defaultHybhubStep" alias="hybhubStep" /> <bean id="defaultHybhubStep" parent="checkoutStep"> <property name="checkoutGroup" ref="hybhubCheckoutGroup"/> <property name="checkoutStepValidator" ref="hybhubValidator"/> <property name="transitions"> <map merge="true"> <entry key="previous" value-ref="REDIRECT_MULTI_STEP_CHECKOUT"/> <entry key="current" value-ref="REDIRECT_TO_HYBHUB"/> <entry key="next" value-ref="REDIRECT_TO_DELIVERY_ADDRESS"/> </map> </property> <property name="progressBarId" value="hybhubStep"/> </bean> <alias name="defaultHybhubMultiStepCheckout" alias="hybhubMultiStepCheckout" /> <bean id="defaultHybhubMultiStepCheckout" parent="checkoutStep"> <property name="checkoutGroup" ref="hybhubCheckoutGroup"/> <property name="checkoutStepValidator" ֒→ ref="defaultResponsiveMultiStepCheckoutValidator"/> <property name="transitions"> <map> <entry key="previous" value-ref="REDIRECT_TO_CART"/> <entry key="current" value-ref="REDIRECT_MULTI_STEP_CHECKOUT"/> <entry key="next" value-ref="REDIRECT_TO_HYBHUB"/> </map> </property> <property name="progressBarId" value="multi"/> </bean> <alias name="defaultHybhubDeliveryAddressCheckoutStep" ֒→ alias="hybhubDeliveryAddressCheckoutStep" /> <bean id="defaultHybhubDeliveryAddressCheckoutStep" parent="checkoutStep"> <property name="checkoutGroup" ref="hybhubCheckoutGroup"/> <property name="checkoutStepValidator" ֒→ ref="defaultResponsiveDeliveryAddressCheckoutValidator"/> <property name="transitions"> <map merge="true"> <entry key="previous" value-ref="REDIRECT_TO_HYBHUB"/> <entry key="current" value-ref="REDIRECT_TO_DELIVERY_ADDRESS"/> <entry key="next" value-ref="REDIRECT_TO_DELIVERY_METHOD"/> </map> </property> <property name="progressBarId" value="deliveryAddress"/> </bean> You may have noticed that our Hybhub step is validated by hybhubValidator; it’s an implementation of de.hybris.platform.acceleratorstorefrontcommons. checkout.steps.validation.AbstractCheckoutStepValidator which lets you validate the step controller on enter and on exit. Here is our implementation for the hybhub step: 6.4. CHECKOUT FLOW package com.hybhub.storefront.checkout.steps.validation.impl; import de.hybris.platform.acceleratorstorefrontcommons.checkout.steps.validation.AbstractChe ⌋ ckoutStepValidator; ֒→ import de.hybris.platform.acceleratorstorefrontcommons.checkout.steps.validation.ValidationR ⌋ esults; ֒→ import de.hybris.platform.commercefacades.order.data.CartData; import org.apache.log4j.Logger; import org.springframework.web.servlet.mvc.support.RedirectAttributes; public class DefaultHybhubValidator extends AbstractCheckoutStepValidator { final static Logger LOG = Logger.getLogger(DefaultHybhubValidator.class); ֒→ ֒→ @Override public ValidationResults validateOnEnter(final RedirectAttributes redirectAttributes) { final CartData cartData = getCheckoutFacade().getCheckoutCart(); LOG.info("Validating on enter with Hybhub Validator cart : " + cartData.getCode()); return ValidationResults.SUCCESS; } @Override public ValidationResults validateOnExit() { final CartData cartData = getCheckoutFacade().getCheckoutCart(); LOG.info("Validating on exit with Hybhub Validator cart : " + cartData.getCode()); return ValidationResults.SUCCESS; } } We now need to implement a controller to handle our step: 123 124 CHAPTER 6. ORDER MANAGEMENT package com.hybhub.storefront.controllers.pages.checkout.steps; import de.hybris.platform.acceleratorstorefrontcommons.annotations.PreValidateCheckoutStep; import de.hybris.platform.acceleratorstorefrontcommons.annotations.RequireHardLogIn; import de.hybris.platform.acceleratorstorefrontcommons.checkout.steps.CheckoutStep; import de.hybris.platform.acceleratorstorefrontcommons.constants.WebConstants; import de.hybris.platform.acceleratorstorefrontcommons.controllers.pages.checkout.steps.Abst ⌋ ractCheckoutStepController; ֒→ import de.hybris.platform.cms2.exceptions.CMSItemNotFoundException; import import import import import org.springframework.stereotype.Controller; org.springframework.ui.Model; org.springframework.web.bind.annotation.RequestMapping; org.springframework.web.bind.annotation.RequestMethod; org.springframework.web.servlet.mvc.support.RedirectAttributes; @Controller @RequestMapping(value = "/checkout/multi/hybhub") public class HybhubStepController extends AbstractCheckoutStepController { private static final String HYBHUB = "hybhub"; ֒→ ֒→ @RequestMapping(method = RequestMethod.GET) @RequireHardLogIn @Override @PreValidateCheckoutStep(checkoutStep = HYBHUB) public String enterStep(final Model model, final RedirectAttributes redirectAttributes) throws CMSItemNotFoundException { this.prepareDataForPage(model); storeCmsPageInModel(model, getContentPageForLabelOrId(HYBHUB)); setUpMetaDataForContentPage(model, getContentPageForLabelOrId(HYBHUB)); model.addAttribute(WebConstants.BREADCRUMBS_KEY, getResourceBreadcrumbBuilder().getBreadcrumbs("checkout.mult ⌋ i.deliveryMethod.breadcrumb")); model.addAttribute("metaRobots", "noindex,nofollow"); setCheckoutStepLinksForModel(model, getCheckoutStep()); return "pages/checkout/multi/hybhubPage"; } @RequestMapping(value = "/back", method = RequestMethod.GET) @RequireHardLogIn @Override public String back(final RedirectAttributes redirectAttributes) { return getCheckoutStep().previousStep(); } @RequestMapping(value = "/next", method = RequestMethod.GET) @RequireHardLogIn @Override public String next(final RedirectAttributes redirectAttributes) { return getCheckoutStep().nextStep(); } protected CheckoutStep getCheckoutStep() { return getCheckoutStep(HYBHUB); } } 6.4. CHECKOUT FLOW 125 To handle the view you need to create pages/checkout/multi/hybhubPage.jsp: <%@ <%@ <%@ <%@ <%@ <%@ <%@ <%@ <%@ page trimDirectiveWhitespaces="true"%> taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> taglib prefix="template" tagdir="/WEB-INF/tags/responsive/template"%> taglib prefix="cms" uri="http://hybris.com/tld/cmstags"%> taglib prefix="spring" uri="http://www.springframework.org/tags"%> taglib prefix="form" uri="http://www.springframework.org/tags/form"%> taglib prefix="formElement" tagdir="/WEB-INF/tags/responsive/formElement" %> taglib prefix="multi-checkout" tagdir="/WEB-INF/tags/responsive/checkout/multi"%> taglib prefix="ycommerce" uri="http://hybris.com/tld/ycommercetags" %> <template:page pageTitle="\${pageTitle}" hideHeaderLinks="true"> <div class="row"> <div class="col-sm-6"> <div class="checkout-headline"> <span class="glyphicon glyphicon-lock"></span> <spring:theme code="checkout.multi.secure.checkout" text="Secure Checkout"></spring:theme> ֒→ </div> <multi-checkout:checkoutSteps checkoutSteps="\${checkoutSteps}" ֒→ progressBarId="hybhubStep"> <jsp:body> <ycommerce:testId code="hybhub"> <a href=’<spring:url value="\${nextStepUrl}"/>’ ֒→ type="button" class="btn btn-primary btn-block checkout-next"><spring:theme ֒→ code="checkout.multi.hybhub.continue" text="Next"/></a> </ycommerce:testId> </jsp:body> </multi-checkout:checkoutSteps> </div> <div class="col-sm-6 hidden-xs"> <h1>This is the Hybhub step</h1> </div> </div> </template:page> Finally you need to create a new page. We choose the MultiStepCheckoutSummaryPageTemplate to make our life easier but it could be another template: $contentCatalog=electronicsContentCatalog $contentCV=catalogVersion(CatalogVersion.catalog(Catalog.id[default=$contentCatalog]),Catalo ⌋ ֒→ gVersion.version[default=Staged])[default=$contentCatalog:Staged] INSERT_UPDATE ContentPage;$contentCV[unique=true];uid[unique=true];name;masterTemplate(uid,$ ⌋ contentCV);label;defaultPage[default=’true’];approvalStatus(code)[default=’approved’];h ⌋ ֒→ omepage[default=’false’]; ֒→ ;;hybhub;Hybhub Page;MultiStepCheckoutSummaryPageTemplate;hybhub Don’t forget to run a synchronization or to change the catalog to the Online one, 126 CHAPTER 6. ORDER MANAGEMENT otherwise you won’t be able to see the hybhub page ! Now you need to change the checkout flow group of your store. You can do it from the HMC or from an impex query: INSERT_UPDATE BaseStore;uid[unique=true];checkoutFlowGroup; ;electronics;hybhubCheckoutGroup; Go to the store front, and checkout your cart. You should now see your new step! ASM SAP Hybris provides an Assister Service Module. It gives the customer service employees the ability to assist customers in real time using the same interface as the end customer - the store front. The ASM module is compatible with all SAP Hybris accelerator store front (B2C, B2B and Telco). In order to install the ASM module you need to add the asm extensions into your localextensions.xml file: <extension name="assistedservicefacades"/> <extension name="assistedservicestorefront"/> You also need to install the assistedservicestorefront addon into your store front extension. Here my store front is named hybhubstorefront; remember to use your store front name. From SAP Hybris platform/bin folder run : ant addoninstall -Daddonnames="assistedservicestorefront" ֒→ -DaddonStorefront.yacceleratorstorefront="hybhubstorefront" Now start your server and run a system update (with assistedservicefacades and assistedservicestorefront selected), when it’s finished, access your store front with this URL parameter asm=true, for example to access my electronic store front on my localhost for development I use : <a>http://localhost:9001/hybhubstorefront/?site=electronics&asm=true</a>. The default user login / password is asagent / 123456. You should see this : You need to remember that from the ASM perspective you can: • Find a customer’s account and session or an anonymous cart; • Map an anonymous cart to an existing customer; • Help a customer to create a new account; 6.6. CS COCPKIT 127 • Support a customer during add to cart and checkout flow processes; • Help a customer to manage his/her account. CS Cocpkit SAP Hybris provides a Customer Service Cockpit. It uses the cockpit framework (and not the next generation cockpit framework). This cockpit is loaded from the cscockpit cockpit extension and available from : <a>http://localhost:9001/cscockpit/</a>. It is meant to be used by operators in a call center environment for customers, pre-sales and post sales operations. We recall that from the customer service cockpit, operators can do the following : • Manage customers (create or edit); • Manage orders on behalf of a customer (create, cancel, update, refund, return); • Manage a customer’s cart; • Take payment; • Manage order’s delivery mode and address; • Manage subscription products. The current customer cockpit will probably be reimplemented by SAP Hybris using the NG Cockpit framework 128 CHAPTER 6. ORDER MANAGEMENT Figure 6.1: Order business Process 129 6.6. CS COCPKIT Figure 6.2: Order Model Classes Figure 6.3: Electronic ASM 130 CHAPTER 6. ORDER MANAGEMENT Chapter 7 Search and navigation 131 132 CHAPTER 7. SEARCH AND NAVIGATION SAP Hybris provides fast and customizable search and navigation with the help of an Apache SOLR server ([More information](http://lucene.apache.org/solr/)). Solr index A SOLR index is a fast text search database for products (or any other kind of data). In order to use a SOLR index you need: • A SolrServerConfig, which defines the connections between SAP Hybris and the Apache SOLR node(s). – Standalone or Embedded (standalone is now the default and it’s the preferred way of running SOLR as it’s how most systems in production run) – Embedded Master, to decide if only one SOLR server should take care of all index operations – Alive check interval, interval between check for a running system – Connection timeout, timeout before SAP Hybris considers a connection attempt a failure – Socket timeout, timeout before SAP Hybris considers a socket access a failure – Total connections, total open connections to SOLR – Total connections per host, total connections per SOLR host – List of endpoints, representing all the SOLR server – Use Master node exclusively for indexing, will force SAP Hybris to access only non master nodes and to reserve master nodes for indexing operations only • one or more SolrEndpointUrl. – A url to access Apache solr (for example : http://solr.local:8983) – A boolean to flag the node as a master • A SolrIndexConfig, which defines the way SAP Hybris will index documents within Apache SOLR. – Batch size, how many products SAP Hybris will index at the same time, this configuration can have a massive impact on the performance – Number of threads, number of threads within SAP Hybris to handle the indexing process – Indexer mode, DIRECT means SAP Hybris will execute operation on the running SOLR Index. This means that the search result would be inconsistent during the indexing operation this mode should be used only during development. The TWO_PHASE mode will create a new index where SAP Hybris will inject the new indexing result, once the operation is done SAP Hybris will switch the active index to be the newly created one – Commit mode, Apache SOLR data are not reachable until you commit them, there are different commit modes, never, after_index, after_batch, and mixed – Optimize mode, Apache SOLR can optimize its index by defragmenting it, this operation could be resource expensive, you can configure when it would be executed, never, after an index operation or after a full index operation 7.2. SOLR INDEXED PROPERTY 133 – Error handling, you can configure the behavior of the indexing process when an error is raised • A SolrSearchConfig. – Result page size – Restrict field in response – Include all facets in response • A SolrIndexedType, defines the type of item to index and what attributes. – Composed type, the type of item to index – A list of properties to index – A list of indexed queries – The default property value provider – Model fields value provider – Result converter Solr Indexed Property A Solr indexed type defines a list of properties from an item type that needs to be indexed, each of those properties are defined with : • A name, this name will be used under the Solr index; • A type, the type of the attribute under the Solr index; • A sortable type; • A localized flag; • A currency flag; • A multi-value flag; • A used for spell checking flag; • A include in response flag; • A range and range sets; • A property value provider; • Value provider parameter(s); • Category field flag; • Free text settings; • Facet settings; • Boost rules. 134 CHAPTER 7. SEARCH AND NAVIGATION Example, with the following customized product model : <itemtype code="Product" autocreate="false" generate="false"> <attributes> <attribute qualifier="exportToGoogleShopping" type="java.lang.Boolean"> <persistence type="property" /> </attribute> <attribute qualifier="lastCustomerReview" type="java.lang.String"> <persistence type="property" /> </attribute> </attributes> </itemtype> We will create 2 new properties for our product index: one facet property from the exportToGoogleShopping and the other free text search property from lastCustomerReview: exportToGoogleShopping facet : INSERT_UPDATE SolrIndexedProperty;solrIndexedType(identifier)[unique=true];name[unique=true] ⌋ ;type(code);sortableType(code);currency[default=false];localized[default=false];multiVa ⌋ ֒→ lue[default=false];facet[default=true];facetType(code);facetSort(code);priority;visible ⌋ ֒→ ;useForSpellchecking[default=false];useForAutocomplete[default=false];fieldValueProvide ⌋ ֒→ r;valueProviderParameter;facetDisplayNameProvider;customFacetSortProvider;topValuesProv ⌋ ֒→ ider;rangeSets(name) ֒→ ;electronicsProductType;exportToGoogleShopping;boolean;;;;false;;MultiSelectAnd;;100;true;;; ⌋ ֒→ springELValueProvider;getExportToGoogleShopping();; lastCustomerReview free text search : INSERT_UPDATE SolrIndexedProperty;solrIndexedType(identifier)[unique=true];name[unique=true] ⌋ ;type(code);sortableType(code);currency[default=false];localized[default=false];multiVa ⌋ ֒→ lue[default=false];facet[default=false];facetType(code);facetSort(code);priority;visibl ⌋ ֒→ e;useForSpellchecking[default=false];useForAutocomplete[default=false];fieldValueProvid ⌋ ֒→ er;valueProviderParameter;ftsQuery;ftsQueryBoost;ftsQueryMinTermLength;ftsWildcardQuery ⌋ ֒→ ֒→ ;ftsWildcardQueryBoost;ftsWildcardQueryMinTermLength;includeInResponse ;electronicsProductType;lastCustomerReview;string;;;;;;;;100;true;;;springELValueProvider;ge ⌋ ֒→ tLastCustomerReview();true;500;5;true;100;10;false In order to add a new free text attribute you need to configure the solr search configuration with legacy mode to false, or to update the commerceSearchTextPopulator bean and add your new field, in our case lastCustomerReview : 7.2. SOLR INDEXED PROPERTY 135 INSERT_UPDATE SolrSearchConfig;description[unique=true];pageSize;legacyMode ;electronicsPageSize;20;false After running a full index job, and adding data both for exportToGoogleShopping and lastCustomerReview look for products from your store front, you should see the new facets and results from the two attribute providers. Combinations are limitless Implement your own value resolver The recommended way to create a new value provider is to create a new implementation of the abstract class de.hybris.platform.solrfacetsearch.provider.impl.AbstractValueResolver<T extends ItemModel, MDATA, QDATA>, the only abstract method that you have to implement is addFieldValues, here is an example of a simple value provider for our exportToGoogleShopping attribute : package com.hybhub.hybhubaddon.search.solrfacetsearch.provider.impl; import import import import import import de.hybris.platform.core.model.product.ProductModel; de.hybris.platform.solrfacetsearch.config.IndexedProperty; de.hybris.platform.solrfacetsearch.config.exceptions.FieldValueProviderException; de.hybris.platform.solrfacetsearch.indexer.IndexerBatchContext; de.hybris.platform.solrfacetsearch.indexer.spi.InputDocument; de.hybris.platform.solrfacetsearch.provider.impl.AbstractValueResolver; public class ProductExportToGS extends AbstractValueResolver<ProductModel, Object, Object> { ֒→ ֒→ ֒→ ֒→ @Override protected void addFieldValues(final InputDocument inputDocument, final IndexerBatchContext IndexerBatchContext, final IndexedProperty indexedProperty, final ProductModel productModel, final ValueResolverContext<Object, Object> valueResolverContext) throws FieldValueProviderException { inputDocument.addField(indexedProperty, productModel.getExportToGoogleShopping() == null ? Boolean.FALSE : productModel.getExportToGoogleShopping(), valueResolverContext.getFieldQualifier()); } } We need to create a Spring bean for our value provider; it must have abstractValueResolver as a parent bean definition: 136 CHAPTER 7. SEARCH AND NAVIGATION <bean id="productExportToGS" ֒→ class="com.hybhub.hybhubaddon.search.solrfacetsearch.provider.impl.ProductExportToGS" parent="abstractValueResolver"/> Build your platform and run the following impex query: INSERT_UPDATE SolrIndexedProperty;solrIndexedType(identifier)[unique=true];name[unique=true] ⌋ ;type(code);sortableType(code);currency[default=false];localized[default=false];multiVa ⌋ ֒→ lue[default=false];facet[default=true];facetType(code);facetSort(code);priority;visible ⌋ ֒→ ;useForSpellchecking[default=false];useForAutocomplete[default=false];fieldValueProvide ⌋ ֒→ r;valueProviderParameter;facetDisplayNameProvider;customFacetSortProvider;topValuesProv ⌋ ֒→ ider;rangeSets(name) ֒→ ;electronicsProductType;exportToGoogleShopping;boolean;;;;false;;MultiSelectAnd;;100;true;;; ⌋ ֒→ productExportToGS;;; Run a full indexation, you should now see that all products have a exportToGoogleShopping facet, since we set it to Boolean.FALSE by default. Solr Indexed Query Indexer queries are used by the system to get the list of PKs it needs to work on. There are different scenarios for the indexing process: • Full query, it runs a full indexation, SAP Hybris will get all products pks from this query, by default it runs : SELECT {PK} FROM {Product} Don’t forget that the query is executed by the anonymous user, therefore the Frontend_ProductApprovalStatus search restriction applies ({approvalStatus} = approved) • Update query, runs an update indexation on the existing SOLR index. SAP Hybris will load the products that have been modified since the last index time, therefore only products that have been modified (modifiedtime attribute) since the last update (date is injected as an argument by the indexing service) would be indexed. 7.3. INDEXING LISTENERS 137 SELECT DISTINCT tbl.pk, tbl.code FROM ( {{ SELECT DISTINCT {p:PK} AS pk, {p:code} AS code FROM {Product AS p LEFT JOIN CustomerReview AS cr ON {cr:product}={p:PK} } WHERE ֒→ {p:varianttype} IS NULL AND ({p:modifiedtime} >= ?lastIndexTime OR ֒→ ֒→ {cr:modifiedtime} >= }} UNION {{ SELECT {p:PK} AS ֒→ ?lastIndexTime) pk, {p:code} AS code FROM {Product AS p} WHERE {p:code} IN ֒→ ( {{ SELECT DISTINCT ֒→ {sl:productCode} FROM {StockLevel AS sl} WHERE {sl:modifiedtime} >= ֒→ ?lastIndexTime }} ) }}) tbl ORDER BY ֒→ tbl.code ֒→ ֒→ Don’t forget that the query is executed by the anonymous user, therefore the Frontend_ProductApprovalStatus search restriction applies ({approvalStatus} = approved) • Partial update, this operation is similar to the update process but you can select the field you want to update. This kind of operation, unlike the others, can’t be launched from a wizard. You would need to create a different job for this. Here is an example of how to create one job. INSERT_UPDATE SolrExtIndexerCronJob;code[unique=true];job(code)[unique=true];sessionLa ⌋ nguage(isocode);sessionCurrency(isoCode);indexedType(identifier)[unique=true];que ⌋ ֒→ ry;facetSearchConfig(name);indexerOperation(code);sessionUser(uid);queryParameter ⌋ ֒→ Provider;indexedProperties ֒→ ;partialUpdate-fromImpex-cronJob;solrExtIndexerJob;en;USD;Product_Product;set in ֒→ solrIndexerQuery;electronicsIndex;partial_update;anonymous;ParameterProvider;name The indexedProperties attribute is a collection of String. Here you can list the attribute you want to update. This is where you configure the partial behavior, if you don’t configure any properties all attributes are updated • Delete query this deletes documents from the index. For example you can select all products that are not authorized or have been discontinued. Indexing listeners During the indexing process you can use listeners to intercept and customize a piece of the indexing operation. Each listeners can implement three kind of actions, before, after and afterError. SAP Hybris provides three kind of listeners : • de.hybris.platform.solrfacetsearch.indexer.IndexerListener, will be executed around indexations; 138 CHAPTER 7. SEARCH AND NAVIGATION • de.hybris.platform.solrfacetsearch.indexer.IndexerBatchListener, will be executed around batch executions; • de.hybris.platform.solrfacetsearch.indexer.IndexerQueryListener, will be executed around query executions; First let’s create an Abstract class for our listeners : package com.hybhub.hybhubaddon.search.solrfacetsearch.indexer.impl; import org.springframework.beans.factory.annotation.Required; public abstract class AbstractHybhubListener { private String name; public String getName() { return name; } @Required public void setName(final String name) { this.name = name; } } Example how to implement an IndexerListener listener : 7.3. INDEXING LISTENERS 139 package com.hybhub.hybhubaddon.search.solrfacetsearch.indexer.impl; import de.hybris.platform.solrfacetsearch.indexer.IndexerContext; import de.hybris.platform.solrfacetsearch.indexer.IndexerListener; import de.hybris.platform.solrfacetsearch.indexer.exceptions.IndexerException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HybhubIndexerListenerImpl extends AbstractHybhubListener implements ֒→ IndexerListener { private final Logger LOG = LoggerFactory.getLogger(HybhubIndexerListenerImpl.class); @Override public void beforeIndex(final IndexerContext arg0) throws IndexerException { LOG.info("Before Index : " + getName()); } @Override public void afterIndex(final IndexerContext arg0) throws IndexerException { LOG.info("After Index : " + getName()); } @Override public void afterIndexError(final IndexerContext arg0) throws IndexerException { LOG.info("After Index Error : " + getName()); } } Example how to declare a bean for the listener and configure it as global : ֒→ <bean id="globaltHybhubIndexerListener" class="com.hybhub.hybhubaddon.search.solrfac ⌋ etsearch.indexer.impl.HybhubIndexerListenerImpl"> <property name="name" value="globaltHybhubIndexerListener" /> </bean> <bean id="globaltHybhubIndexerListenerDefinition" parent="solrListenerDefinition"> <property name="listener" ref="globaltHybhubIndexerListener" /> </bean> Listeners are called based on their priority, from the highest to the lowest (reversed order for the after method): • Global listeners first, ordered by their priority index (default is 100) 140 CHAPTER 7. SEARCH AND NAVIGATION • Listeners configured in the Facet Search Config • Listeners configured in the Indexed Type Solr Facet The difference between normal search and facet search is that when you do a normal search you follow a determined path, like a category path. It is fixed and you need to know all the details about your product. Quite the opposite with a facet search when you don’t need to know anything about the product, you narrow down the results by applying filters to the list of products. SAP Hybris faceted search gives the customer the ability to navigate through a collection of products using facets. A facet can be anything from colors, prices, weigh, internal components, classified as, warranty time, available in store... To see this with a real world example, you can go to any of the big online retailers. Here, you will notice that when searching for a product by entering its name, you see a list of facets (usually) on the left. In order to create a new facet, you only need to declare a new SolrIndexedProperty and flag it as a facet. We will update the existing pickupAvailableFlag and make a facet from it: INSERT_UPDATE SolrIndexedProperty;solrIndexedType(identifier)[unique=true];name[unique=true] ⌋ ;type(code);fieldValueProvider;facet;facetType(code);priority; ֒→ ;electronicsProductType;pickupAvailableFlag;boolean;productPickupAvailabilityValueProvider;t ⌋ rue;MultiSelectAnd;50; ֒→ facetType could be: • Refine, meaning that this would be the only facet used if selected; • MultiSelectAnd, it filters all search; • MultiSelectOr, it adds products based on the facet. After running a full SOLR indexation you should see the new facet under your product search page. Solr Facet Range In order to have efficient facets in some cases you would need to create ranges. The simplest example is for prices. Without ranges SAP Hybris would create one facet perspective per price, that would be inefficient and resource consuming. To achieve efficient facets you will need to create ranges. The simplest way to understand it is to have a look at the out-of-the-box price ranges available under SAP Hybris. In the example below, we create different ranges for prices in USD, from 0 to 50... For each facet we have a UI string to display "$0-$49.99"; the following is an example. 7.6. AUTO SUGGESTION 141 # Define price range set INSERT_UPDATE ֒→ SolrValueRangeSet;name[unique=true];qualifier;type;solrValueRanges(\&rangeValueRefID) ;electronicsPriceRangeUSD;USD;double;rangeRefUSD1,rangeRefUSD2,rangeRefUSD3,rangeRefUSD4,ran ⌋ geRefUSD5 ֒→ # Define price ranges INSERT_UPDATE SolrValueRange;\&rangeValueRefID;solrValueRangeSet(name)[unique=true];name[uni ⌋ ֒→ que=true];from;to ;rangeRefUSD1;electronicsPriceRangeUSD; $0-$49.99; 0; 49.99 ;rangeRefUSD2;electronicsPriceRangeUSD; $50-$199.99; 50; 199.99 ;rangeRefUSD3;electronicsPriceRangeUSD; $200-$499.99; 200; 499.99 ;rangeRefUSD4;electronicsPriceRangeUSD; $500-$999.99; 500; 999.99 ;rangeRefUSD5;electronicsPriceRangeUSD;$1,000-$100,000;1000; 100000 When you create a SolrIndexedProperty you can specify rangeSets. Auto suggestion If a SOLR indexed property is configured to be auto complete, its content becomes available for a search from the Search Box Component. To activate it from an indexed property, execute the following impex query : INSERT_UPDATE SolrIndexedProperty;solrIndexedType(identifier)[unique=true];name[unique=true] ⌋ ;useForAutocomplete ֒→ ;electronicsProductType;code;true The Search box component is configurable: • delay before auto complete; • maximum number of products; • minimum of characters before sending a query; • display images. Keywords Keywords are configured within the solr facet search configuration. They provide you with the ability to redirect the customer to a page for a given query. For example, if a customer enters the word cart under the search component, you can redirect him or her to the cart page. First you need a SolrFacetSearchKeywordRedirect. This holds the following: 142 CHAPTER 7. SEARCH AND NAVIGATION • the language; • the keyword; • match type (EXACT, START_WITH, ENDS_WITH, CONTAINS, REGEX); • ignore case flag; • a link to a facet search configuration; • a link to a redirect object (Abstract redirect). It can be one of the following: – a category redirect; – a direct url; – a page redirect; – a product redirect. Below is an example of how to create a new redirect for a product: $productCatalog=electronicsProductCatalog $productCatalogVersion=catalogVersion(CatalogVersion.catalog(Catalog.id[default=$productCata ⌋ ֒→ log]),CatalogVersion.version[default=Staged])[default=$productCatalog:Staged] INSERT_UPDATE SolrProductRedirect;redirectItem(code,$productCatalogVersion)[unique=true];\&r ⌋ ֒→ edirectRefID ;726510;contentCatalogName-redirectRefID-726510 INSERT_UPDATE SolrFacetSearchKeywordRedirect;facetSearchConfig(name)[unique=true];language(i ⌋ socode)[unique=true];keyword[unique=true];matchType(code)[unique=true];redirect(&redire ⌋ ֒→ ֒→ ctRefID);ignoreCase ;electronicsIndex;en;"the best camera";EXACT;contentCatalogName-redirectRefID-726510;true Try a search using the best camera as a keyword. Stopwords Stopwords are words that SAP Hybris is ignoring during a search. Because you don’t want to search words like the or a, a list needs to be created and maintained for each language. Happily SAP Hybris and SOLR provide a standard stopword list for most languages. You can find it under your configuration folder config/solr/instances/default/ configsets/default/conf/lang/stopwords_en.txt (the example illustrates stopwords for English, but other languages are available). To manage stopwords from SAP Hybris, one first creates them from Impex queries: INSERT_UPDATE SolrStopWord;facetSearchConfig(name)[unique=true];language(isocode)[unique=tru ⌋ ֒→ e];stopword[unique=true] ;electronicsIndex;en;the 7.9. SYNONYMS 143 Synonyms Synonyms help you to deal with customers typo, abbreviations or requests. They simply translate a word into something you know your SOLR index would easily understand. Let us say, a customer is looking for a disk capacity and he enters ’256 gigabytes’; you need to translate ’gigabytes’ into ’GB’ because that’s how you have indexed your products. In order to create a new synonym: INSERT_UPDATE SolrSynonymConfig;facetSearchConfig(name)[unique=true];language(isocode)[uniqu ⌋ e=true];synonymFrom[unique=true];synonymTo ֒→ ;electronicsIndex;en;"gigabytes";"GB" Hero products & boost rules Hero products SAP Hybris gives the possibility to prioritize which products should appear on top of the category listing by having hero products. Hero products work on a category based level. It means that if you don’t navigate inside a given category but instead do a normal search, hero products have no influence on the result sorting. For example, when a customer navigates to the Camera top category through the top menu link (Accelerator store front), we want to display the product 478828 (10.2 Megapixel D-SLR with Standard Zoom Lens) first. Below is the Impex query: $productCatalog=electronicsProductCatalog $productCatalogVersion=catalogVersion(CatalogVersion.catalog(Catalog.id[default=$productCata ⌋ ֒→ log]),CatalogVersion.version[default=Online])[default=$productCatalog:Online] INSERT_UPDATE SolrHeroProductDefinition;$productCatalogVersion[unique=true];category(code, ֒→ $productCatalogVersion);code[unique=true];indexedType(identifier);products(code, ֒→ $productCatalogVersion) ;;575;575_electronicsProductType;electronicsProductType;478828 INSERT_UPDATE Product;code[unique=true];$productCatalogVersion[unique=true];solrHeroProductD ⌋ efinitions(code) ֒→ ;478828;;575_electronicsProductType Now navigate to home -> open catalog -> cameras -> digital cameras. You should see your product on top of the search result. Boost rules Boost rules help you adjust the SOLR server answers in the way that for example product in stock shows up before out of stock products. Based on different factors, you will artificially boost the 144 CHAPTER 7. SEARCH AND NAVIGATION results. SAP Hybris implemented boosting at query time, meaning that you do not need to re-index to see the changes! For example, for the product 1312564 to always be on top, you can execute this Impex query below: $solrIndexedType=electronicsProductType INSERT_UPDATE GlobalSolrSearchProfile;code[unique=true];indexedType(identifier) ;globalcatalog-srch-profile0000000001;$solrIndexedType INSERT_UPDATE SolrBoostRule;propertyValue[unique=true];solrIndexedProperty(name);operator(co ⌋ de)[unique=true];boostFactor;solrSearchProfiles(code) ֒→ ;1312564;code;EQUAL_TO;100;globalcatalog-srch-profile0000000001 Now when you navigate to the Power Supplies category (one of the default top navigation link), you see that our product 1312564 is listed on top. Solr boost rule operators are: • EQUAL_TO; • CONTAIN; • GREATER_THAN; • GREATER_THAN_OR_EQUAL_TO; • LESS_THAN; • LESS_THAN_OR_EQUAL_TO. Now if you type battery into your search box component you’d expect to see your product 1312564 on top of the list, but it’s not! The reason is that the boost factor we entered 100 is not enough to push it to the top. A lot of other factors are adding their boost rules. Below the SOLR query executed when you search for battery : Now if you type "battery"" into your search box component, you may expect to see your product 1312564 on top of the list; but it’s not! The reason is that the boost factor we entered (100 ) is not enough to push it to the top. A lot of other factors are adding to their boost rules. Below SOLR query is executed when you search for battery: 7.10. HERO PRODUCTS & BOOST RULES 145 localhost:8983/solr/master_electronics_Product/select? q=....’((code_string:battery^90.0)+ OR+(keywords_text_en:battery^20.0)+ OR+(manufacturerName_text:battery^40.0)+ OR+(categoryName_text_en_mv:battery^20.0)+ OR+(lastCustomerReview_string:battery^100.0)+ OR+(ean_string:battery^100.0)+ OR+(name_text_en:battery^50.0))+ OR+((keywords_text_en:battery~^10.0)+ OR+(manufacturerName_text:battery~^20.0)+ OR+(categoryName_text_en_mv:battery~^10.0)+ OR+(lastCustomerReview_string:battery~^50.0)+ OR+(name_text_en:battery~^25.0))+ OR+((code_string:battery*^45.0)+ OR+(ean_string:battery*^50.0))+ OR+((keywords_text_en:"battery"^40.0)+ OR+(manufacturerName_text:"battery"^80.0)+ OR+(categoryName_text_en_mv:"battery"^40.0)+ OR+(name_text_en:"battery"^100.0))’})+ // // This is our custom boost query ! // AND+({!func+v="sum(map(query({!v=code_string:1312564}),0,0,0,1000.0))"}) // &sort=score+desc,inStockFlag_boolean+desc,score+desc &start=0&rows=20 &facet.field=Resolution,+80_string &facet.field=Mounting,+1867_en_string &facet.field=categoryPath_string_mv &facet.field=Megapixel,+63_string &facet.field=exportToGoogleShopping_boolean &facet.field=availableInStores_string_mv &facet.field=pickupAvailableFlag_boolean &facet.field=Colour+of+product,+1766_en_string &facet.field=price_usd_string &facet.field=allPromotions_string_mv &facet.field=allCategories_string_mv &facet.field=Lens+type,+472_en_string_mv &facet.field=category_string_mv &facet.field=brand_string_mv &facet=true &facet.sort=count &facet.mincount=1 &facet.limit=50 &spellcheck=true &spellcheck.q=battery &spellcheck.dictionary=en &spellcheck.collate=true &fq=(catalogId:"electronicsProductCatalog"+AND+catalogVersion:"Online") If you want to see the SOLR query you need to activate the debug for de.hybris.platform.solrfacetsearch package : 146 CHAPTER 7. SEARCH AND NAVIGATION log4j2.logger.search.name=de.hybris.platform.solrfacetsearch log4j2.logger.search.level = debug log4j2.logger.search.appenderRef.stdout.ref = STDOUT Having a boost of 100 is not enough. So why not putting more to be sure that our product is always showing on top? By default, SAP Hybris limits the maximum boost factor to 100. If you want to increase it, you need to change the out-of-the-box BoostRuleValidator behavior. Unfortunately SAP Hybris does not "think"" that this needs to be easily updated and made all beans around it without aliases. So in order to change the maxBoostFactorValue attribute, you have 3 options: • change it directly under commercesearch/resources/commercesearch-spring.xml, but changing SAP Hybris source files means it’s hard to update to newer versions....; • import boost factor rules in legacy mode (without service layer interceptors); • using Spring AOP to update the existing bean, the preferred solution by far. How to change boostRuleValidator bean using AOP, first you need to create a class to handle the change. This is called around the validate method call from the interceptor: package com.hybhub.hybhubaddon.commercesearch.searchandizing.boost.interceptors; import ֒→ de.hybris.platform.commercesearch.searchandizing.boost.interceptors.BoostRuleValidator; import org.aspectj.lang.ProceedingJoinPoint; import org.springframework.beans.factory.annotation.Required; public class HybhubBoostRuleValidatorAspect { private int maxBoostFactorValue; public void around(final ProceedingJoinPoint joinPoint) throws Throwable { final BoostRuleValidator boostRuleValidator = (BoostRuleValidator) ֒→ joinPoint.getTarget(); boostRuleValidator.setMaxBoostFactorValue(maxBoostFactorValue); joinPoint.proceed(); } @Required public void setMaxBoostFactorValue(final int maxBoostFactorValue) { this.maxBoostFactorValue = maxBoostFactorValue; } } Now you need to configure your aspect with Spring. We use an around aspect to be able to inject our customized max boost factor into the existing bean: 7.10. HERO PRODUCTS & BOOST RULES 147 <bean id="hybhubBoostRuleValidatorMethodInterceptor" class="com.hybhub.hybhubaddon.commercesearch.searchandizing.boost.interceptors.Hybhu ⌋ ֒→ bBoostRuleValidatorAspect"> <property name="maxBoostFactorValue" value="500" /> </bean> <aop:config> <aop:aspect id="boostValidatorAspect" ֒→ ref="hybhubBoostRuleValidatorMethodInterceptor"> <aop:pointcut id="boostValidatorPoincut" expression="execution(* de.hybris.platform.commercesearch.searchandi ⌋ zing.boost.interceptors.BoostRuleValidator.onValidate(..))" ֒→ /> ֒→ <aop:around method="around" pointcut-ref="boostValidatorPoincut"/> </aop:aspect> </aop:config> You can now have boost factor value up to 500. If you run this Impex query your product 1312564 will always be on top of search: INSERT_UPDATE SolrBoostRule;propertyValue[unique=true];solrIndexedProperty(name);operator(co ⌋ ֒→ de)[unique=true];boostFactor;solrSearchProfiles(code) ;1312564;code;EQUAL_TO;500;globalcatalog-srch-profile0000000001 148 CHAPTER 7. SEARCH AND NAVIGATION Chapter 8 Platform basics 149 150 CHAPTER 8. PLATFORM BASICS SAP Hybris commerce platform is composed of a set of essential features: • persistence; • caching; • security; • transactions; • clustering; • i18n; • import/export; • search; • cronjobs; • task queue. Initialization Initialization creates the database and inject the data from scratch following these steps: • Put together all items.xml files to create the type system definition. • Aborts all running cronjobs. • Remove all tables existing under the type system definition, orphaned data stay intact. • Hybris goes through all extension manager and call: initialize Remove Objects, initialize Create Types, initialize Modify Types and initialize Create Objects. • Prepare the DDL (Data Definition Language) and the DML (Data Modification Language). • Clears cache. • Creates media folders. • Sets license. • Always creates essential data and if enabled project data. Hybris no longer removes all tables, it removes only those that are declared in its current items.xml files, to reactivate the legacy behavior use initialization.legacy.mode=true in local.properties To run the initialization, you have 2 options: • from the SAP Hybris HAC (Hybris Administration Console); • from the build framework, executing ant initialize, one option is to select the tenant with -Dtenant=master. On a production system it is a good idea to lock the initialization from the HAC, configure system.unlocking.disabled=true 151 8.2. UPDATE Update During the update process SAP Hybris updates the item types to match the new items.xml definition. Unlike the initialization process, there are no loss of data during an update because update process: • doesn’t rename tables. • doesn’t rename attributes. • doesn’t change an attribute type. • doesn’t drop any table. • doesn’t delete any data. • change indices by recreating them. • doesn’t modify an attribute from optional to required. During the update process SAP Hybris will (in order of execution): • put together all items.xml files to create the type system definition; • update existing type system according to the new items.xml definition (if possible, see rules above); • add new type system; • create essential data and project data (if enabled). On a production system it is a good idea to lock the initialization from the HAC, configure system.unlocking.disabled=true Update and Initialization lock Whenever you start an update or an initialization, SAP Hybris will get a lock to be sure no one is starting a similar process before it ends. The lock is obtained for a special database table SYSTEMINIT. Here is an example of a lock during an update: MariaDB [hybris]> select * from SYSTEMINIT; id locked tenantId clusterNode lockdate process globalID 1 master 0 20... System update locked = 1 means you cannot start an update or an initialization. instanceId ... 152 CHAPTER 8. PLATFORM BASICS Update and Initialization hooks You can control what an extension is doing during the initialization and update process. SAP Hybris provides annotation hooks for you to control the execution. To create your own hook, you need to create a class that uses the annotation SystemSetup. Here is an example using AbstractSystemSetup from commerceservices as a starting point: 8.4. UPDATE AND INITIALIZATION HOOKS package com.hybhub.setup; import import import import import import import de.hybris.platform.commerceservices.setup.AbstractSystemSetup; de.hybris.platform.core.initialization.SystemSetup; de.hybris.platform.core.initialization.SystemSetup.Process; de.hybris.platform.core.initialization.SystemSetup.Type; de.hybris.platform.core.initialization.SystemSetupContext; de.hybris.platform.core.initialization.SystemSetupParameter; de.hybris.platform.core.initialization.SystemSetupParameterMethod; import java.util.ArrayList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.hybhub.constants.HybhubhookConstants; @SystemSetup(extension = HybhubhookConstants.EXTENSIONNAME) public class HybhubHookSystemSetup extends AbstractSystemSetup { final static Logger LOG = LoggerFactory.getLogger(HybhubHookSystemSetup.class); @SystemSetupParameterMethod @Override public List<SystemSetupParameter> getInitializationOptions() { final List<SystemSetupParameter> params = new ArrayList<>(); params.add(createBooleanSystemSetupParameter("key", "Import ?", false)); return params; } ֒→ ֒→ ֒→ ֒→ } @SystemSetup(type = Type.ESSENTIAL, process = Process.ALL) public void createEssentialData(final SystemSetupContext context) { final boolean doImport = getBooleanSystemSetupParameter(context, "key"); if (doImport) { logInfo(context, "Starting importing essential data for " + HybhubhookConstants.EXTENSIONNAME); importImpexFile(context, "/hybhubcore/essential.impex"); synchCatalogs(); } else { logError(context, "Did not import essential data !", null); } } @SystemSetup(type = Type.PROJECT, process = Process.ALL) public void createProjectData(final SystemSetupContext context) { final boolean doImport = getBooleanSystemSetupParameter(context, "key"); if (doImport) { logInfo(context, "Starting importing project data for " + HybhubhookConstants.EXTENSIONNAME); importImpexFile(context, "/hybhubcore/project.impex"); synchCatalogs(); } else { logError(context, "Did not import project data !", null); } } private void synchCatalogs() { getSetupSyncJobService().createContentCatalogSyncJob("electronicsContentCata ⌋ log"); getSetupSyncJobService().createProductCatalogSyncJob("electronicsProductCata ⌋ log"); } 153 154 CHAPTER 8. PLATFORM BASICS You need to declare the system setup class as a bean: <bean id="hybhubHook" class="com.hybhub.setup.HybhubHookSystemSetup" ֒→ parent="abstractCoreSystemSetup"/> Have a look at coreDataImportService, sampleDataImportService and event services, to find an example you could open ElectronicsStoreSystemSetup Essential & project data Import by convention During the initialization and update processes, the platform looks for ImpEx files under <extension_name>/resources/impex folder. In particular: • For essential data: The platform scans the <extension_name>/resources/impex folders for files with names that match the regular expressionessentialdata.impex and imports the files during the essential data creation. • For project data: The platform scans the <extension_name>/resources/impex folders for files with names that match the regular expressionprojectdata.impex and imports the files during the project data creation. The ImpEx directory does not exist by default. You must create it and copy files to it. Import by configuration If you have special folder structures or want to use another folder in the resources, you must override the configuration in your local.properties file: • For essential data, add the property <extension_name>.essentialdata-impex-pattern. • For project data, use <extension_name>.projectdata-impex-pattern. For example, assume that you have the following folder structure: • resources/test1.impex • resources/subfolder/test2.impex • resources/impex/subfolder/subfolder/test3.impex In this structure, only the test1.impex file has the pattern <extension_name>.essentialdata-impexpattern=impex. In contrast to the example above, the pattern <extension_name>.essentialdata-impex-pattern=/.impex includes test1.impex, test2.impex and test3.impex. If you want your configuration to work as the default does, you must set the pattern to <extension_name>.essentialdata-impex-pattern=impex/essentialdataimpex 8.6. SESSIONS HANDLING 155 Control the order of import If you need to control in what order the Impex files are being imported, you can create a file that match the import pattern, and from this files you will import all the other Impex files you need in the wanted order: "#% impex.includeExternalData(SampleDataManager.class.getResourceAsStream(""/firstFileToBeIm ⌋ ֒→ ported.csv""), ""utf-8"", 0, 0 ֒→ );"; "#% impex.includeExternalData(SampleDataManager.class.getResourceAsStream(""/secondFileToBeI ⌋ mported.csv""), ""utf-8"", 0, 0 ֒→ );"; ֒→ Sessions handling All browser requests made to SAP Hybris are bind to a session. There are 2 kind of sessions: • the HTTP session which is by default held by Tomcat; • the JaloSession held by SAP Hybris. The layer Jalo is deprecated, but JaloSessions are not going to be replaced by the service layer, they just use a legacy name. The JaloSession is a wrapper around the HTTP session to hold information about the current SAP Hybris user: • user (anonymous by default); • language; • currency; • price factory; • locale; • timezone. Jalo Sessions are also used for cronjobs, they exist only within SAP Hybris memory and are by default never persisted. Example how to get your current Jalo Session from the SessionService (Groovy script): def sessionService = spring.getBean("sessionService") def jaloSession = sessionService.getCurrentSession().getJaloSession() def sessionContext = jaloSession.getSessionContext() println jaloSession.getHttpSessionId() println sessionContext.getLanguage() println sessionContext.getLocale() 156 CHAPTER 8. PLATFORM BASICS Extensions structures SAP Hybris is made of different extensions, like the set of extensions that constitute the so called platform. Extensions are made to be independent from each other, so you could migrate SAP Hybris platform without changing your customized extensions. Each extensions are represented by a Java project, even the out of the box extensions can be opened as Java projects. Below the folder’s structure of all extensions: • lib folder for external libraries; • resources folder for configuration and localization; • src folder for source code; • testsrc folder for the tests; • web folder for the we application; • buildcallbacks.xml file for build call backs configuration; • extensioninfo.xml extension configuration; • project.properties extension properties; • external-dependencies.xml libraries managed by maven. extensioninfo.xml Within the extensioninfo.xml file, you can configure: • list of dependencies, this will be used by the build framework to know what extensions need to be included within the build and in what order they should be compiled. • activated modules. – core module, if defined the extension will be used to define and localize item types trough the }items.xml file. – web module, if activated the extension will be used as a web application. – hmc, if activated the extension will have hmc configuration. meta, there are also different meta keys that can be used to configure extensions, for backoffice or templates for example. <meta key="backoffice-module" value="true"/> <meta key="extgen-template-extension" value="true"/> <meta key="modulegen-name" value="accelerator,b2baccelerator,chinaaccelerator"/> 8.7. EXTENSIONS STRUCTURES 157 localextensions.xml SAP Hybris consists of a number of extensions. You can configure which extensions are used by modifying the localextensions.xml file located under your configuration folder. Extensions located under ${HYBRIS_BIN_DIR}/platform/ext are automatically loaded Extensions are loaded when: • you configure a directory (relative to ${HYBRIS_BIN_DIR} or not); • you configure a name, if it can be found within a configured path; • by dependency, if it can be found within a configured path; • by being inside a folder configured as auto-loaded. For examples : <extension dir="${HYBRIS_BIN_DIR}/custom/hybhubaddon"/> <path dir=’${HYBRIS_BIN_DIR}/custom’ autoload=’false’ /> <extension name="hybhubaddon"/> <path dir=’${HYBRIS_BIN_DIR}/custom’ autoload=’true’ /> It’s also possible to load external web application (war files for example), using the webapp tag : <webapp context="/path/to/your/context.xml" path="/mypath"/> During the build phase you can read what extensions are being loaded directly, and what extensions are being lazy loaded (through dependencies and configured path): ... [echo] [echo] [echo] [echo] [echo] [echo] [echo] [echo] [echo] .... ------- Extensions in dependency order ( options: --- @deprecated: is deprecated, p: platform extension,*: auto-required --- ?: lazy-loaded, i: got items.xml, b: got beans.xml, c: got core module --- w: got web module, h: got HMC module ) ----core 6.0.0.0-SNAPSHOT [p*cib] testweb 6.0.0.0-SNAPSHOT [p*w] scripting 6.0.0.0-SNAPSHOT [p*ci] 158 CHAPTER 8. PLATFORM BASICS Create a new extension You can create new extensions based on an existing template. The example below creates a new extension based on the yempty template: $ platform ant extgen ... ... [input] [input] Please choose a template for generation. [input] Press [Enter] to use the default value (ywebservices, ysmarteditmodule, yoccaddon, yhacext, [yempty], ycommercewebservices, ycommercewebservicestest, ֒→ ֒→ ycommercewebserviceshmc, ycmssmartedit, ychinaacceleratorstorefront, yatddtests, ֒→ yaddon, yacceleratorstorefront, yacceleratorordermanagement, ֒→ yacceleratorfulfilmentprocess, yacceleratorfractusfulfilmentprocess, yscala, ygroovy, ֒→ ycockpit, ybackoffice, hybhubstorefront, hybhubfulfilmentprocess) yempty [input] [input] Please choose the name of your extension. It has to start with a letter followed ֒→ by letters and/or numbers. [input] Press [Enter] to use the default value [training] myextension [input] [input] Please choose the package name of your extension. It has to fulfill java package ֒→ name convention. [input] Press [Enter] to use the default value [org.training] com.hybhub ... There are different available templates to create new extensions, the convention is that all extensions starting with a y are templates, the most basic one is the yempty template which is an empty shell with the SAP Hybris folder structure. Maven SAP Hybris is currently in between Ant and Maven. The whole platform build is tightly coupled with Ant but dependency management can be used for any extension. Also for some modules (CIS and Datahub for example) the full lifecycle is done with Maven. If you want to activate dependency management for one of your extension, open the extensioninfo.xml file and configure it with usemaven=”true”: 8.8. CONFIGURATION 159 <?xml version="1.0" encoding="UTF-8" standalone="no"?> <extensioninfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ֒→ xsi:noNamespaceSchemaLocation="extensioninfo.xsd"> <extension abstractclassprefix="Generated" classprefix="Hybhubhook" ֒→ managername="HybhubhookManager" managersuperclass="de.hybris.platform.jalo.extension.Extension" ֒→ name="hybhubhook" usemaven="true"> <requires-extension name="commerceservices" /> <coremodule generated="true" manager="com.hybhub.jalo.HybhubhookManager" ֒→ packageroot="com.hybhub" /> <webmodule jspcompile="false" webroot="/hybhubhook" /> </extension> </extensioninfo> Then you can manage your dependencies inside external-dependencies.xml: <project xmlns="http://maven.apache.org/POM/4.0.0" ֒→ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> ֒→ <modelVersion>4.0.0</modelVersion> <groupId>de.hybris.platform</groupId> <artifactId>hybhubhook</artifactId> <version>5.0.0.0-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>4.1</version> </dependency> </dependencies> </project> When you build your project, Hybris will automatically download all libraries and update your project. If you want to update your project classpath you can use ant classpathgen within your extension. You need to have Maven installed! Configuration SAP Hybris uses simple property files for its configuration. All variables would be either injected into Tomcat when you execute ant server, or injected into the running system as configuration variables (you can configure them lively directly from the HAC). There are different files where you can configure SAP Hybris behavior, in order of priority from high to low: 160 CHAPTER 8. PLATFORM BASICS • config/local.properties • your extensions project.properties files • platform directory project.properties file Configuration properties are defined using a key and a value; for example: #A comment the.key=myValue Use tab for tabulation, t is ignored. The recommended way of configuring SAP Hybris is to configure extensions with default configuration within their own project.properties files, and configure your project for a given environment within your local.properties file. Never change the configuration directly into out of the box extensions of within the platform project.properties file. This would make your it harder to migrate to newer versions. Restarting your server is enough for your configuration to changes to be loaded you don’t need to re-build the platform. Configuration Service An example of how to get a configuration key from your code is below: def configurationService = spring.getBean("configurationService") def value = configurationService.getConfiguration().getProperty("spring.profiles.active") You can read or edit the current configuration. Have a look at the interface org.apache.commons.configuration.Config Environment variables When configuration files may contain sensible data such as logins, passwords, urls, hash..., it’s a good idea not to store them within a file. SAP Hybris gives you the possibility to configure special properties value that need to be overridden by environment variables using <CHANGE_ME> keyword. For example within my configuration, I have : hybhub.config=<CHANGE_ME> Then from my environment I execute (note that it needs to start with y_ and you replace dots by underscore, double underscore to replace single underscores): 8.9. BUILD 161 export y_hybhub_config=fromConsole Then from the HAC: def configurationService = spring.getBean("configurationService") def value = configurationService.getConfiguration().getProperty("hybhub.config") Will print fromConsole. Runtime optional configuration It becomes difficult to maintain configuration for various environment from a single repository. Usually the config folder is duplicated for each environment, and every few weeks a developer would forget to copy the new configuration he added to the pre-production environment to the production environment leading to half a day of debugging. SAP Hybris version 6 introduced a new concept of an optional configuration directory. To specify host specific properties without interfering with provided configuration file. To do so, you need to configure an extra directory: • from your local.properties using hybris.optional.config.dir key • from an environment variable using HYBRIS_OPT_CONFIG_DIR In this folder files would be loaded in they match [1-9][0-9]-local.properties (example : 10-local.properties), the number specifies the order in which they are loaded and merged. Build SAP Hybris build framework is based on: • Apache Ant, automation framework build in Java; • Eclipse IDE. When you execute the command ant, the build framework would call the default ant target (all -> build + server): • check directories (data, temp and log), it creates the folders if they don’t exist; • check the config directory, and prompt for the configuration template selection if none exist; • resolves extensions dependencies; • you cannot predict the order extensions are being built; 162 CHAPTER 8. PLATFORM BASICS • extensions that depends on another are built only after all their dependencies are built; • generates and compiles source files (according to the definitions in the items.xml files); • collects localization property files (locales_XY.properties); • collects layout configuration files (hmc.xml); • updates the hybris Server. You can use ant with different scopes: • platform, will execute the target for all extensions; • extension, will execute the target for a single extension. To see all ant targets, you can use ant -p. Unless you execute ant clean Java classes are not compiled every time you call ant all, because the javac command will compare timestamp of the source and target and compile the source only if its timestamp is newer than the target Callbacks In each extensions you will find a file named buildcallbacks.xml, within this file you can extend the behavior of the build framework for a given extension adding custom actions around each ant targets. For example, before my extension is built: <macrodef name="hybhubhook_before_build"> <sequential> <echo message="Hybhub is being build !!" level="info"/> </sequential> </macrodef> Tenant SAP Hybris can run in a multi tenant mode, which means each tenant is using a different set of data. Each tenant has: • isolated data; • option to have a separate database; • option to use a different time zone and locales; • option to use different extensions. 8.11. CACHE 163 To declare new tenants: installed.tenants=junit,foo,t1,t2,hybhub The master tenant is always present To have specific tenant configuration you need to create as tenant_tenantID.properties under your configuration folder. Under our tenant specific configuration, you can configure extensions to use using the following two properties: allowed.extensions=core;promotions forbidden.extensions=b2bapprovalprocess;hybhubaddon Cache SAP Hybris cache helps reduce the number of database queries. It caches three type of objects: • Flexible Search Queries results; • Item instances; • Item attributes. The cache is used within the Service Layer. It is completely transparent to be used. When you call a getter on an item attribute, if it hasn’t been loaded yet, it will be then loaded from the database and cached for the next time. The cache is independent for each node meaning that each nodes have their local cache. Data are removed from the cache when: • Cache is full and the replacement strategy is removing an entry; • Cache is no longer valid: – item is deleted; – item is modified. When a node invalidates a cache entry, the invalidation is shared with other nodes by sending a UDP request to all cluster nodes. Region cache SAP Hybris cache is divided into regions. Each region has its own configuration, caches different kind of objects, and has a maximum size. Regions can have different kinds of eviction strategy: 164 CHAPTER 8. PLATFORM BASICS • Least Recently Used (LRU), objects are evicted based on their last used timestamp; • Least Frequently Used (LFU), every time an object is read the cache increment a hit counter, object are evicted based on this counter; • First In First Out (FIFO), objects are evicted based on their age. By default, SAP Hybris has a region for type systems. The region is called ehCacheEntityCacheRegion and uses LRU eviction strategy. To create new cache region, you need to create a new Spring Beans within the global Spring context. First we configure for our extension named hybhubhook a new global spring context file: hybhubhook.global-context=hybhubhook-cache-spring.xml Then we create a new file under the resources folder hybhubhook-cache-spring.xml: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" ֒→ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ֒→ xmlns:aop="http://www.springframework.org/schema/aop" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd" default-lazy-init="true"> <bean id="productCacheRegionRegistrar" ֒→ class="de.hybris.platform.regioncache.region.CacheRegionRegistrar" c:region-ref="productCacheRegion" /> ֒→ <alias name="defaultProductCacheRegion" alias="productCacheRegion" /> <bean name="defaultProductCacheRegion" ֒→ class="de.hybris.platform.regioncache.region.impl.EHCacheRegion" lazy-init="false"> <constructor-arg name="name" value="productCacheRegion" /> <constructor-arg name="maxEntries" value="1" /> <constructor-arg name="evictionPolicy" value="LFU" /> <constructor-arg name="statsEnabled" value="true" /> <constructor-arg name="exclusiveComputation" value="true" /> <property name="handledTypes"> <array> <value>1</value> </array> </property> </bean> </beans> handledTypes is the type code of the item type, one is for products. It could also be ALL_TYPES, QUERY_CACHE or NO_QUERY. For more examples have a look at the SAP Hybris default regions cache within core-cache.xml. Now if you go into the HAC, under /monitoring/cache, you should see your new cache region productCacheRegion. 8.12. INTERCEPTORS 165 Interceptors The service layer gives the ability to activate interceptor when life cycle of a model reaches certain steps: • load interceptor (de.hybris.platform.servicelayer.interceptor.LoadInterceptor) is loaded when the service layer loads an item. You may use this interceptor to change or check values loaded by the service layer. • init default interceptor (de.hybris.platform.servicelayer.interceptor.InitDefaultsInterceptor) is called when you create a new item from the model service. You may use this interceptor to fill the item with default value you couldn’t configure from the }items.xml. • prepare interceptor (de.hybris.platform.servicelayer.interceptor.PrepareInterceptor) is called before an item is saved by the service layer. You may use this interceptor to add or update attribute values before they are saved. • validate interceptor (de.hybris.platform.servicelayer.interceptor.ValidateInterceptor) is called before an item is saved and after it has been prepared. You may use this interceptor to validate the integrity of the data. • remove interceptor (de.hybris.platform.servicelayer.interceptor.RemoveInterceptor) is called before an item is being removed by the service layer. You may use this interceptor to prevent removal of data or to delete related data. An example of how to create an interceptor for product items follows. 166 CHAPTER 8. PLATFORM BASICS package com.hybhub.hybhubaddon.servicelayer.interceptor; import import import import de.hybris.platform.core.model.product.ProductModel; de.hybris.platform.servicelayer.interceptor.InterceptorContext; de.hybris.platform.servicelayer.interceptor.InterceptorException; de.hybris.platform.servicelayer.interceptor.RemoveInterceptor; import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ProductRemoveInterceptor implements RemoveInterceptor<ProductModel> { ֒→ final private static Logger LOG = LoggerFactory.getLogger(ProductRemoveInterceptor.class); final private static Pattern PROTECTED_PRODUCTS = Pattern.compile("^[0-9]+$"); ֒→ ֒→ @Override public void onRemove(final ProductModel prd, final InterceptorContext ctx) throws InterceptorException { LOG.info("Removing product : " + prd.getCode()); if (PROTECTED_PRODUCTS.matcher(prd.getCode()).find()) { throw new InterceptorException("Product is protected, cannot remove it !"); } LOG.info("Product : " + prd.getCode() + " removed"); } } You need to declare the interceptor as a Spring bean and to configure the type mapping : <alias name="defaultProductRemoveInterceptor" alias="productRemoveInterceptor" /> <bean id="defaultProductRemoveInterceptor" ֒→ class="com.hybhub.hybhubaddon.servicelayer.interceptor.ProductRemoveInterceptor" /> <bean id="productRemoveInterceptorMapping" ֒→ class="de.hybris.platform.servicelayer.interceptor.impl.InterceptorMapping"> <property name="interceptor" ref="productRemoveInterceptor" /> <property name="typeCode" value="Product" /> </bean> Now try to delete a product with a numeric code. The interceptor should block you from removing it. The InterceptorMapping can be also configured with : • a replacedInterceptors collection, if you want to replace existing interceptors; 8.13. TRANSACTIONS 167 • an order of priority to run them in a sequence if there are multiple interceptors. You can choose to disable interceptors from an Impex query, by types [disable.interceptor.types=validate] or by bean names [disable.interceptor.beans=’productRemoveInterceptor’] You can choose to call the service layer in a local view with interceptor deactivated : final Map<String, Object> params = ֒→ ImmutableMap.of(InterceptorExecutionPolicy.DISABLED_INTERCEPTOR_TYPES, ImmutableSet.of(InterceptorExecutionPolicy.DisabledType.VALIDATE)); sessionService.executeInLocalViewWithParams(params, new SessionExecutionBody() { @Override public void executeWithoutResult() { modelService.save(item); // executed without any validate interceptors } }); Transactions Transactions are used to keep the data consistent at any time. SAP Hybris provides a wrapper to easily work with transactions. A simple groovy example follows below. def transaction = de.hybris.platform.tx.Transaction.current() transaction.begin() //commit all changes transaction.commit() //rollback all changes transaction.rollback() A single thread can only have one transaction, therefore it doesn’t matter how many times you get the current transaction, if it’s from the same thread you will always get the same transaction ! Remember to execute commit and rollback within a finally block ! Errors are not caught by Exception blocks. SAP Hybris also has a more advanced wrapper to include all steps of a transactions. The advantage of TransactionBody is that you don’t need to manually handle the transactions over and over: 168 CHAPTER 8. PLATFORM BASICS Transaction.current().execute( new TransactionBody() { public <Object> Object execute() { //Everything here is contained within a single transaction return something; } } ); SAP Hybris offers a delayed store mechanism, if activated statements will be executed only when the transaction is committed, the pro is that performance would be better in big transactions but data won’t be updated and available until the transaction is committed. To activate it Transaction.current().enableDelayedStore(true); Tasks SAP Hybris provides a task service. Tasks are scheduled actions like cronjob a simplified version; they have the following features: • Scheduling, time and event based; • Cluster aware, a task may be executed by any of the available node; • pool of workers, to avoid to overload the cluster with too many tasks, there is a maximum number of running workers (see project.properties from the processing extension). Each task can be executed on any cluster node. The first cluster to fetch and lock the task would run it. If you need to deactivate the task engine on a given node, you can do it from the configuration file: task.engine.loadonstartup=false A task can be scheduled based on: • time, holds its execution time; • event, holds at least one task condition item to specify when to trigger the task. Chapter 9 Platform core 169 170 CHAPTER 9. PLATFORM CORE Events The Event System is a framework provided by the service layer allowing you to send and receive events within the SAP Hybris. • One software component acts as a source and publishes an event that is received by registered listeners. • Event listeners are objects that are notified of events and perform business logic corresponding to the event that occurred. • Events can be published locally or across cluster nodes. • SAP Hybris Event System is based on the Spring event system. To create your own event, you need to extend the class de.hybris.platform.servicelayer.event.events.AbstractEvent: package com.hybhub.event; import de.hybris.platform.servicelayer.event.events.AbstractEvent; public class HybhubEvent extends AbstractEvent { private final String name; public HybhubEvent(final String name) { this.name = name; } @Override public String toString() { return "Hybhub Event : " + this.name; } } Then to create a listener you extend de.hybris.platform.servicelayer.event.impl.AbstractEventListener : 9.1. EVENTS 171 package com.hybhub.event; import de.hybris.platform.servicelayer.event.impl.AbstractEventListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HybhubListener extends AbstractEventListener<HybhubEvent> { private static final Logger LOG = LoggerFactory.getLogger(HybhubListener.class); @Override protected void onEvent(final HybhubEvent hybhubEvent) { LOG.info("Received event(Hybhub Event) : " + hybhubEvent); } } Finally you need to create a Spring bean for your listener : <bean id="hybhubListener" class="com.hybhub.event.HybhubListener" parent="abstractEventListener" /> To try your event run this Groovy script: import com.hybhub.event.HybhubEvent def eventService = spring.getBean("eventService") eventService.publishEvent(new HybhubEvent("Event published from Groovy console !")) You should be able to read the following message on SAP Hybris console: INFO [hybrisHTTP8] [HybhubListener] Received event(Hybhub Event) : Hybhub Event : Event published from Groovy console ! The only flaw here is that everything is executed synchronously by the same thread! To process them in an asynchronous way, you can: • make your event cluster aware by implementing de.hybris.platform.servicelayer.event.ClusterAwareEvent, when you implement this interface your events are always processed asynchronously even if they are executed on the same node they were created. 172 CHAPTER 9. PLATFORM CORE package com.hybhub.event; import de.hybris.platform.servicelayer.event.ClusterAwareEvent; import de.hybris.platform.servicelayer.event.events.AbstractEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HybhubEvent extends AbstractEvent implements ClusterAwareEvent { private static Logger LOG = LoggerFactory.getLogger(HybhubEvent.class); private final String name; public HybhubEvent(final String name) { LOG.info(Thread.currentThread().getName()); this.name = name; } @Override public String toString() { LOG.info(Thread.currentThread().getName()); return "Hybhub Event : " + this.name; } @Override public boolean publish(final int sourceClusterId, final int targetClusterId) { return true; //Returning true means your event will be broadcast on all nodes } } • update the Spring bean platformClusterEventSender with an executor: <bean id="platformClusterEventSender" ֒→ class="de.hybris.platform.servicelayer.event.impl.PlatformClusterEventSender"> <property name="serializationService" ref="serializationService"/> <property name="tenant" ref="tenantFactory"/> <property name="executor"> <bean class="java.util.concurrent.Executors" ֒→ factory-method="newCachedThreadPool"/> </property> </bean> 9.2. IMPEX 173 Impex SAP Hybris needs to exchange a lot of data with external systems and it needs to be easily configurable without having to create complex SQL query. Impex offers a way to easily store and exchange. That means *import and export*, which is an out-of-the-box CSV-based import framework. From an impex file you can do the following: • import data; • update data; • import / update data; • remove data; • export data. Impex are useful for: • initial data injection (stores definitions, initial catalogs, cms components creation, catalogs creation...); • update data during runtime; • test datas / configurations during development; • migrate data between systems; • backups. An Impex query has: • a header defining the mapping between an item type and the value lines; • line value(s) defining the date; • comments; • macro definition; • BeanShells directive; • user rights definition. Eclipse Neon (impexeditor Feature, with live Impex validation if your server is started) and IntelliJ (Hybris integration) have a special editors available for Impex files. Header Header is a single line preceding the value lines. A header has: • a mode which defines what kind of operations you are executing, it can be: – INSERT, creates a new item. – UPDATE, update an existing item based on unique identifiers. 174 CHAPTER 9. PLATFORM CORE – INSERT_UPDATE, update an existing item if it can find the unique identifier otherwise create a new entry. – REMOVE, try to remove an item based on unique attributes, log a warning if it can’t find any elements. • a item type, like User or Product; • attributes of the related item type, like code, name; • attribute modifiers, gives additional processing instructions of a given attribute: – alias for export ([alias=theAlias]). – allownull ([allownull=true]), not compatible with the service layer. – cellDecorator ([cellDecorator=de.hybris.platform.catalog.jalo.classification.eclass.EClassSuperCategoryDe – collection-delimiter, , is the default delimiter ( [collection-delimiter=;]). – dateformat ([dateformat=dd-MM-yyyy]). – default ([default=’default value’]). – forceWrite ([forceWrite=true), not compatible with the service layer. – ignoreKeyCase ([ignoreKeyCase=true]). – ignorenull ([ignorenull=true]), ignore null for collection type imports. – key2value-delimiter ([key2value-delimiter=->]), specifies the operator to delimit keys and values. – lang ([lang=en]). – map-delimiter ([map-delimiter=|]). – mode ([mode=append]) for collections import append the collections, replace or remove elements. – numberformat ([numberformat=#.###,##]). – path-delimiter ([path-delimiter=:]). – pos ([pos=3]) change positions of values, not recommended. – translator ([translator=de.hybris.platform.impex.jalo.translators.ItemPKTranslator]). – unique ([unique=true]) used to mark this attribute as unique, can be configured on multiple attributes if needed. – virtual ([virtual=true,default=value]) needs to have a default modifier as well. • header modifiers, are configured with the item type, for example : INSERT MyType[headerModifier=something – batchmode ([batchmode=true]), used with an update or remove it allows to modify more than one item that matches the query. – cacheUnique ([cacheUnique=true]). – processor ([processor=de.hybris.platform.impex.jalo.imp.DefaultImportProcessor]). – impex.legacy.mode ([impex.legacy.mode=true]). 9.2. IMPEX 175 Impex header syntax is case sensitive An example with Title item type follows below: INSERT Title;code;name[lang=en] UPDATE Title;code[unique=true];name[lang=en] INSERT_UPDATE Title;code[unique=true];name[lang=en] REMOVE Title;code[unique=true];name[lang=en] You can also specify a subtype of the Header item type on the value line, for example : INSERT User;uid[unique=true] Customer;aCustomer Employee;anEmployee The header item type could be abstract, but the value line item type obviously can’t Setting an attribute value for atomic types is straightforward, a string attribute for example can be directly entered within the value (see example with *User* item type above). For reference attributes, Impex expects you to enter the primary key of the referenced item. This is not possible to know items primary key before they are created; so this is not the right way to create relations. To efficiently and without external dependencies insert relations between items, you need to look them up based on their own attributes. Add a comment to this line INSERT Product;code;unit(code);catalogVersion(catalog(id), version) ;product;pieces;electronic:Staged To localized attributes you need to use a modifier within the header attributes like [lang=en] to localize an attribute in English: INSERT Type;localizedAttribute[lang=en] en is defined with the Language items and correspond to unique ISO code of the language, have a look at the internationalization tab under the HMC or HAC Comment A commented line starts with a dash # and is completely ignored during import: 176 CHAPTER 9. PLATFORM CORE INSERT_UPDATE Product;code #Begin importing products ;product1; #Finished importing products Macro Impex files can easily get large; it’s not occasional to see Impex import files of more than a thousand lines! If you need to update one common attribute for each of the value lines, Macros come in handy. A Macro definition starts with a $ sign: #Macro definition $unit=pieces $variantType=LaptopVariant $aid=MBP$manufacturer=Apple $catalogVersion=catalogversion(catalog(id[default=$productCatalog]),version[default=’Staged’ ⌋ ֒→ ])[unique=true,default=$productCatalog:Staged] $supercategories=supercategories(code, $catalogVersion) INSERT_UPDATE Product;code[unique=true];$supercategories;manufacturerName;manufacturerAID;un ⌋ it(code);variantType(code);$catalogVersion;$approved ֒→ ;000010;000100;$manufacturer;$aid13;$unit;$variantType ;000011;000100;$manufacturer;$aid15;$unit;$variantType Abbreviations Another way to shorten Impex files is to use abbreviations. You may use system wide regex replacement for you Impex headers. For example, out of the box there is an header replacement for the classification header: impex.header.replacement.1=C@(\\w+) ... @$1[system=’$systemName’,version=$systemVersion’,translator=’de.hybris.platform.catalog ⌋ ֒→ ֒→ .jalo.classification.impex.ClassificationAttributeTranslator’] That’s the reason why during classification import you might encounter: 177 9.2. IMPEX # Classification: Technical details (4593) $feature1=@Motor diameter, 6752[$clAttrModifiers]; # Motor diameter in millimeter (mm) Document ID Sometimes item references lookup method trough attributes does not suffice and you need to be able to have a reference to an object within an Impex file. That’s when you use the & operator to create references: INSERT_UPDATE Customer;groups(uid);uid[unique=true];name;defaultPaymentAddress( \&addrID ) ;customergroup;andrew@hybhub.com;Andrew;addr1 INSERT Address;\&addrID;owner(Customer.uid); ;addr1;andrew@hybhub.com; Translators For some attributes, you need a special translator to import the object. The most obvious example is for the media objects. In those cases you need to use a special translator: INSERT_UPDATE Media;code[unique=true];realfilename;@media[translator=de.hybris.platform.impe ⌋ ֒→ x.jalo.media.MediaDataTranslator];mime[default=’image/jpeg’];$catalogVersion ;/img/low_pic/574-4346.jpg;574-4346.jpg;$siteResource/images/img/low_pic/574-4346.jpg; Have a look at the class de.hybris.platform.impex.jalo. translators.AbstractSpecialValueTranslator and check all subclasses to find out what translators are available out of the box To insert map attributes, the default MapValueTranslator will be used. An example importing a saved query follows below. INSERT_UPDATE SavedQuery;code[unique=true];resulttype(code);query;params(key,value(code)) ;savedQuery;Customer;SELECT {pk} FROM {Product} where {code} = ?code;code->java.lang.String Alternative pattern When an item type relation is set to a basic item type such as ItemType for example, you can find the referenced item from multiple attributes. You can, in this case, give the Impex attribute 178 CHAPTER 9. PLATFORM CORE different options. Below is an example with the address owner type which is set to ItemType: INSERT_UPDATE Address;lastname;owner( Principal.uid | AbstractOrder.code ) ;Robert;robert@hybhub.com ;Dupre;00000001 Distributed Impex SAP Hybris V6 introduced a new concept of distributed Impex to share the Impex import between nodes. It speeds up the execution of queries by splitting them into batches that are executed in different nodes. This doesn’t need to be configured since it’s the new default Impex engine. Importing data using the new distributed mode consists of three steps: • prepare then split; • single import execution; • finishing. Access properties from Impex If you need to access your .properties configuration from an Impex file there is away to do it with the ConfigPropertyImportProcessor class. /** * Impex ImportProcessor that injects all config properties as impex definitions. * All defined configuration properties are added as impex macro definitions with * the prefix of "config-". For example the config key <tt>mail.smtp.server</tt> * can be accessed via the macro <tt>$config-mail.smtp.server</tt>. * In order to use this import processor and to load the configuration properties * the following must be added to the top of the impex file: * * <tt>UPDATE GenericItem[processor=de.hybris.platform.commerceservices.impex.impl.ConfigPro ⌋ pertyImportProcessor];pk[unique=true]</tt> ֒→ */ So for example, in your local.properties you have the following configuration: my.default.language=French In your Impex, you access it using the following syntax: 9.3. SPRING CONTEXT 179 UPDATE GenericItem[processor=de.hybris.platform.commerceservices.impex.impl.ConfigPropertyIm ⌋ portProcessor];pk[unique=true] ֒→ $defaultLanguage=$config-my.default.language INSERT_UPDATE Country;isocode[unique=true];name[lang=en];active[default=true] ;FR;$defaultLanguage You can access any properties using the prefix "$config-" Spring context SAP Hybris is heavily using Spring. It offers developers the flexibility to implement their work on top of the out of the box extensions. Spring offers the ability to contain beans within different contexts. SAP Hybris provides different application contexts: • a global static application context shared by all tenants. – to configure it you need to configure an extra property within your extension configuratio extname.global-context=spring.xml. • a core application context for each tenant. – its parent the global application context. – to configure it within your extension. ∗ use the convention resources/extname-spring.xml. ∗ configure a different path extname.application-context=spring.xml. • a web application context for each web application. – configured inside the web.xml file of your extension (your extension needs to have a web module defined within its extension.xml configuration file). – the listener org.springframework.web.context.ContextLoaderListener configured within the web.xml automatically configured the core application as parent of the web application context. – web application context can use core beans but cannot modify them. The order of loading Spring configuration is the same as in the build process, so to override a Spring bean defined in extension A from extension B, add a dependency in extension B for extension A You can configure more than one Spring configuration file, simply separate them with a coma, for example : extname.application-context=spring1.xml,spring2.xml To manually load a bean, it is recommended to use Registry.getApplicationContext() as this will first try to load it from a web application context then from the core context and finally from the global context. 180 CHAPTER 9. PLATFORM CORE Cronjobs A cronjob is an automated task performed at a certain time (every day at 2 am for example), or in fixed intervals (every hour for example). It can be used for: • data backups; • Catalog synchronization; • importing or exporting data. A cronjob is made of: • a job to do; • a trigger to start the job (not mandatory); • a cronjob item which link the job and the trigger and configure the environment the job will be performed in. To create a new Cronjob first you need to create a new item which extend the Cronjob item type. We will create a cronjob that deactivate products that haven’t been updated since a given time: <typegroup name="hybhub"> <itemtype code="DeactivateOutdatedProductCronJob" extends="CronJob"> <description>Job to deactivate out-dated products</description> <attributes> <attribute type="java.util.Date" qualifier="minLastUpdate"> <persistence type="property" /> </attribute> </attributes> </itemtype> </typegroup> Then we need to create the actual job that deactivate products based on the minLastUpdate attribute: 181 9.4. CRONJOBS package com.hybhub.job; import import import import import import import import import import de.hybris.platform.catalog.CatalogVersionService; de.hybris.platform.catalog.enums.ArticleApprovalStatus; de.hybris.platform.core.model.product.ProductModel; de.hybris.platform.cronjob.enums.CronJobResult; de.hybris.platform.cronjob.enums.CronJobStatus; de.hybris.platform.servicelayer.cronjob.AbstractJobPerformable; de.hybris.platform.servicelayer.cronjob.PerformResult; de.hybris.platform.servicelayer.model.ModelService; de.hybris.platform.servicelayer.search.FlexibleSearchQuery; de.hybris.platform.servicelayer.search.FlexibleSearchService; import java.util.List; import javax.annotation.Resource; import com.hybhub.model.DeactivateOutdatedProductCronJobModel; public class DeactivateOutdatedProductJob extends ֒→ AbstractJobPerformable<DeactivateOutdatedProductCronJobModel> { @Resource private FlexibleSearchService flexibleSearchService; @Resource private CatalogVersionService catalogVersionService; @Resource private ModelService modelService; ֒→ @Override public PerformResult perform(final DeactivateOutdatedProductCronJobModel cronjob) { final FlexibleSearchQuery fsq = new FlexibleSearchQuery("SELECT {pk} FROM {Product} " + "WHERE {modifiedtime} <= ?minLastUpdate " + " AND {catalogVersion} = ?catalogVersion"); ֒→ fsq.addQueryParameter("minLastUpdate", cronjob.getMinLastUpdate()); fsq.addQueryParameter("catalogVersion", catalogVersionService.getCatalogVersion("electronicsProductCatalog", "Staged")); ֒→ final List<ProductModel> productsToUpdate = flexibleSearchService.<ProductModel> search(fsq).getResult(); ֒→ productsToUpdate.stream().forEach((prd) -> { prd.setApprovalStatus(ArticleApprovalStatus.UNAPPROVED); modelService.save(prd); }); return new PerformResult(CronJobResult.SUCCESS, CronJobStatus.FINISHED); } } 182 CHAPTER 9. PLATFORM CORE You now need to create a Spring bean: <bean id="deactivateOutdatedProductJob" class="com.hybhub.job.DeactivateOutdatedProductJob" parent="abstractJobPerformable" /> You need to update your system for SAP Hybris to find the new Job Let’s create a cronjob from a Groovy script (You could do this from the HMC or from an Impex query), we create a trigger but also we start the cron manually: Don’t forget to switch the commit mode to on, if you execute this more than once it will fail since cronjob codes needs to be unique ! import com.hybhub.model.DeactivateOutdatedProductCronJobModel import de.hybris.platform.cronjob.model.JobModel import de.hybris.platform.cronjob.model.TriggerModel def modelService = spring.getBean("modelService") def cronJobService = spring.getBean("cronJobService") def flexibleSearchService = spring.getBean("flexibleSearchService") JobModel job = new JobModel(); job.setCode("deactivateOutdatedProductJob") job = flexibleSearchService.getModelByExample(job) DeactivateOutdatedProductCronJobModel cron = modelService.create(DeactivateOutdatedProductCronJobModel.class) cron.setMinLastUpdate(new Date()) cron.setCode("deleteOutdatedProductsss") cron.setJob(job) modelService.save(cron) TriggerModel trigger = modelService.create(TriggerModel.class) trigger.setSecond(0) trigger.setMinute(1) trigger.setHour(0) trigger.setDay(0) trigger.setMonth(-1) trigger.setYear(-1) trigger.setCronJob(cron) cronJobService.performCronJob(cron) Be careful this will change the status of all your products under the staged electronic product catalog ! Abortable Cronjob Sometimes when the jobs you are performing are time consuming you need to be able to abort a running cronjob. By default a cronjob will run until it’s done; but you can implement your own 9.5. CLUSTER 183 logic to give it a chance to abort itself. First you need to create a job to be aware of this new functionality. To do so, there are two methods: 1. override isAbortable() @Override public boolean isAbortable() { return true; } 2. configure the Spring bean <bean id="MyJob" class="de.hybhub.cronjobs.MyJob" parent="abstractJobPerformable" > <property name="abortable" value="true"/> </bean> Secondly you need to add within your perform method implementation an exit door like this: if (clearAbortRequestedIfNeeded(theJob)) { return new PerformResult(CronJobResult.ERROR, CronJobStatus.ABORTED); } Cluster When you deploy SAP Hybris into production you would need to configure a cluster of SAP Hybris nodes, a cluster offers: • node specific configuration; • cronjobs / events for specific nodes; • session fail over; • load balancing compatibility (load balancing needs to be done by a third party component); • choice of UDP (unicast or multicast) or TCP (Jgroup) for nodes communication. All nodes are using the same database 184 CHAPTER 9. PLATFORM CORE Cache invalidation Each SAP Hybris nodes have their own local cache. When one node update an item in the cache, it needs to tell the other nodes to invalidate the entry in their local cache for consistency purpose. To do so the node sends a request (TCP or UDP depends on your cluster configuration) so that other nodes discards the item and reloads it from the database the next time it needs to access that object. Configuration Node specific configuration can be done by using: #cluster.<nodeid>.my.property=value cluster.1.my.property=value Nodes need to have a unique identifier. Prior to SAP Hybris V6, you had to manually configure an id for each node: clustermode=true cluster.id=0 This could make deployment more complex as you needed to provision a unique identifier for each node. Since SAP Hybris V6, you can activate auto discovery mode; this keeps track of each nodes within the database: clustermode=true cluster.nodes.autodiscovery=true Cluster configuration is available under your platform project.properties file, if you want to change the out of the box configuration remember to do it under your config local.properties file Testing SAP Hybris uses Junit to run unit tests and integration tests, which are located within the testsrc and web/testsrc folders. You can execute tests from your IDE or from ant. 9.6. TESTING 185 #All tests ant alltests #All web tests ant allwebtests #Integration tests ant integrationtests #Unit tests ant unittests There are other ant commands (manual tests, performance tests...) to see all available tests execute "ant -p" Unit tests Unit tests are simple tests that do not need any access to SAP Hybris platform (database, services...). They focus on testing the correct behavior of a single java class. When necessary, you can isolate the class by using Mockito to mock anything you need (interfaces, pojo...). An example of a Unit test class generated by SAP Hybris follows: 186 CHAPTER 9. PLATFORM CORE /** * JUnit Tests for the Ruleenginetrail extension. */ public class RuleenginetrailTest extends HybrisJUnit4TransactionalTest { /** Edit the local|project.properties to change logging behavior (properties ֒→ log4j2.logger.*). */ @SuppressWarnings("unused") private static final Logger LOG = LoggerFactory.getLogger(RuleenginetrailTest.class); @Before public void setUp() { // implement here code executed before each test } @After public void tearDown() { // implement here code executed after each test } /** * This is a sample test method. */ @Test public void testRuleenginetrail() { final boolean testTrue = true; assertThat(testTrue).isTrue(); } } An example of a unit test using Mockito is below: 9.6. TESTING 187 @Test public void testThrowAnExceptionWhenNotEmptyContentCatalogs() throws BusinessException { final ContentCatalogModel contentCatalogOne = ֒→ Mockito.mock(ContentCatalogModel.class); final CatalogVersionModel catalogVersion = Mockito.mock(CatalogVersionModel.class); ֒→ ֒→ ֒→ ֒→ BDDMockito.given(siteModel.getStores()).willReturn(Collections.EMPTY_LIST); BDDMockito.given(siteModel.getContentCatalogs()).willReturn(Arrays.asList(contentCat ⌋ alogOne)); BDDMockito.given(contentCatalogOne.getActiveCatalogVersion()).willReturn(catalogVers ⌋ ion); final Throwable expected = new IllegalStateException("blah"); Mockito.doThrow(expected).when(catalogVersionService) .setSessionCatalogVersions(Mockito.argThat(new CollectionArgumentMatcher<CatalogVersionModel>(catalogVersion))); try { strategy.activate(siteModel); Assert.fail("Should call a setSessionCatalogVersions with content catalogs from site "); } catch (final BaseSiteActivationException ike) { Assert.assertEquals(expected, ike.getCause()); // } } Integration tests Integration tests let you interact with SAP Hybris (database, services...) using a dedicated tenant junit. The easiest way to create integration tests is to extend: • ServicelayerTest, this way you can import any service using the @Resource annotation and gain access to methods too create basic data (createDefaultUsers, createHardwareCatalog, createDefaultCatalog...); • ServiceLayerTransactionalTest is only adding transaction-based isolation logic to your tests. When you integration test is done the transaction is rolled back. Other tests • @DemoTest; • @PerformanceTest; • @ManualTest. 188 CHAPTER 9. PLATFORM CORE Chapter 10 PCM and price 189 190 CHAPTER 10. PCM AND PRICE Price calculation SAP Hybris provides a flexible, scalable and configurable way to handle prices, all price informations are contained with the PriceRow item type, a price row has : • a price • a currency • a unit factor • a unit • a minimum quantity • a catalog version (not mandatory) • a time frame (not mandatory) • a customer or a groups of customers (not mandatory) • a product or a group of products (not mandatory) • a net flag (not mandatory) Under SAP Hybris V6 the out of the box price factory implementation is called Europe1PriceFactory this is a legacy name, when the SAP Hybris team implemented their first price factory they thought they would implement different price factories for each regions which in fact was not needed, but they kept this legacy name The price factory is still using the deprecated jalo layer, therefore within the next version Europe1PriceFactory might be migrated to something like DefaultPriceFactoryService To be selected by the price factory a price row needs (following the order of execution) : • match the customer / customer group or be empty • match the product / product group • within the date range if any configured • quantity is equal or bigger than the minimum quantity if any configured Currency conversion is done automatically if no price are defined for the current currency, all conversions are done based on the base currency and the conversion attribute of the targeted currency, have a look at currencies from the HMC After the price factory has loaded all matching prices it needs to select the final one, ordered by highest priority : • product and customer • product group and customer • product and customer group • product group and customer group 10.2. TAXES 191 • product and all customers • product group and all customers • all products and customer • all products and customer group • all products and all customers When the price factory returns more than one price if they are more than one match the PriceService will select the lowest one. Taxes Taxes are represented by tax rows, they define : • an absolute flag • a currency • a date range • a product tax group • a product • a product match qualifier • a tax object • a user tax group • a user • a user match qualifier • a value Tax rows are very similar to price rows, but unlike price rows you accumulate them, for one product you could apply multiple taxes unlike prices where you need to select only one instance To match tax rows the Europe1PriceFactory will look for : • customer / customer group or empty field • product / product group or empty field • date range 192 CHAPTER 10. PCM AND PRICE Discounts Discounts are represented by discount rows, they define : • an absolute flag • a currency • a date range • a product discount group • a product • a product match qualifier • a discount object • a user discount group • a user • a user match qualifier • a value Discount rows are very similar to price rows, but unlike price rows you accumulate them, for one product you could apply multiple discounts unlike prices where you need to select only one instance To match discount rows the Europe1PriceFactory will look for : • customer / customer group or empty field • product / product group or empty field • date range Media object SAP Hybris supports media objects, a media item is a reference to any sort of physical files stored on locally or remotely. It can be a picture, a text file, a flash file, a zip archive and so on. A media is linked to only one file but a file could be linked to more than one media. A media item is defined by : • a location on the disk or remote location • a unique identifier • a catalog version • a mime type • a URL 10.4. MEDIA OBJECT 193 • a folder • a format • a media container • a location hash • metadatas • security rules Out of the box Media type has multiple sub types for specialized objects like Impex, Log and Email. Medias are not localized, therefore to have a localized reference to medias you need to declare an attribute this way : localized:Media SAP Hybris offers out the box two MediaStorageStrategy : • de.hybris.platform.media.storage.impl.LocalFileMediaStorageStrategy, default out of the box strategy, files are stored along with Hybris under the data directory • de.hybris.platform.amazon.media.storage.S3MediaStorageStrategy, files are stored within Amazon AWS S3 Buckets Depending on the media storage strategy you are using you would need to select the appropriate MediaURLStrategy, out of the box you have : • de.hybris.platform.media.url.impl.LocalMediaWebURLStrategy • de.hybris.platform.amazon.media.url.S3MediaURLStrategy Media formats and Media container Media formats help you to server the same image in different but for different contexts, for example on the product listing page (PLP) you need to get a small image of the products but on the product detail page (PDP) you need a bigger image, all those formats are loaded within a media container which is a container for multiple medias, inside a product media data object and you can simply select the appropriate format on your front end. The out of the box formats are : <alias name="acceleratorImageFormatMapping" alias="imageFormatMapping"/> <bean id="acceleratorImageFormatMapping" parent="defaultImageFormatMapping"> <property name="mapping"> <map> <entry key="superZoom" value="1200Wx1200H"/> <entry key="zoom" value="515Wx515H"/> <entry key="store" value="365Wx246H"/> <entry key="product" value="300Wx300H"/> <entry key="thumbnail" value="96Wx96H"/> <entry key="cartIcon" value="65Wx65H"/> <entry key="styleSwatch" value="30Wx30H"/> </map> </property> </bean> 194 CHAPTER 10. PCM AND PRICE Media contexts SAP Hybris is a multi channel platform therefore you could be loading medias in many different contexts from a mobile storefront to a print cockpit, therefore you need to be able to adequate images format for each context, a media context is a mapping container that would map a source format to a target format so the proper image is loaded. Secured media by default media access is not secured. media.default.secured=false To secure all media turn configure it to true, if you need to secure only one folder you can use : media.folderName.secured=true Permissions need to be configured for each media of the secured folder Synchronization jobs SAP Hybris has a concept of catalog and catalog versions, all item types which are catalog aware (have an attribute of type CatalogVersion) can exist in multiple catalogs. This gives you the flexibility to have a backstage where you prepare your changes before you push it into production. Out of the box a SAP Hybris system is configured with three type of catalogs, a content catalog for CMS items, (paragraph, menu, pages...) medias, a product catalog and a classification catalog. Synchronizing catalogs means that you push the content of a catalog into another, usually the Staged catalog into the Online catalog. You can either run a synchronization as a cronjob or run manual synchronization from the catalog, a CatalogVersionSyncJob is defined by : • synchronization source and target (usually Staged to Online) • create new elements flag • remove missing elements flag • synchronization languages • root item types (item types to synchronize) Warning! A catalog can have multiple versions and a Synchronization could merge two Staged catalog into one Online catalog. 10.6. WORKFLOW 195 To create a new CatalogVersionSyncJob from an Impex query : $productCatalog=productCatalog $defaultCurrency=EUR $languages=en $defaultLanguage=en $prices=europe1prices[translator=de.hybris.platform.europe1.jalo.impex.Europe1PricesTranslat ⌋ ֒→ or] $stagedProduct=catalogVersion(catalog(id[default=$productCatalog]),version[default=’Staged’] ⌋ ֒→ )[unique=true,default=’$productCatalog:Staged’] $onlineProduct=catalogVersion(catalog(id[default=$productCatalog]),version[default=’Online’] ⌋ ֒→ )[unique=true,default=’$productCatalog:Online’] $sourceProductCV=sourceVersion(catalog(id[default=$productCatalog]),version[default=’Staged’ ⌋ ])[unique=true,default=’$productCatalog:Staged’] ֒→ $targetProductCV=targetVersion(catalog(id[default=$productCatalog]),version[default=’Online’ ⌋ ֒→ ])[unique=true,default=’$productCatalog:Online’] INSERT_UPDATE CatalogVersionSyncJob;code[unique=true];$sourceProductCV;$targetProductCV; ;sync productCatalog:Staged->Online;;;; Dependent Synchronization To avoid having references to a staged catalog in your online catalog version when two catalogs depend on each other (for example a product catalog and category catalog) you can run a dependent synchronization. The CatalogVersionSyncJob item type has two attributes for this : • dependentSyncJobs • dependsOnSyncJobs You can use one or the other, what’s important is to have dependency set between the two synchronization jobs. Workflow SAP Hybris workflows help you modeling business processes like writing and validating a product description in multiple languages, a workflow is defined by : • workflow template, it has a sequence of action templates • workflow action template, define a step of a workflow, it produces an option • Option, possible result of an action it also defines the next step to follow • workflow, an instance of a workflow template, created by making a copy of the template • workflow action, an instance of a workflow action template, created by making a copy of the template 196 CHAPTER 10. PCM AND PRICE • decision, stores the option result of an action • comment, a note left on a workflow action • visible for principals, attached to the workflow template it gives users the ability to create new workflows A workflow action can have multiple statuses : • pending, action not reached yet • in progress • completed • disabled, deprecated • ended through end of workflow A workflow action template can be a : • start action, meaning it’s the first step of a workflow • normal step, meaning it’s neither the first or last step • end step, meaning it ends the workflow Start action are automatically marked as pending when the workflow is started If no action are marked as start actions then all actions are set to pending If a workflow is ended without reaching all actions, then the status of untouched actions is set to ended through end of workflow SAP Hybris V6 has a fully functional workflow example under productcockpitsampledata extension, have a look at projectdata_workflow.impex. Chapter 11 User Management 197 198 CHAPTER 11. USER MANAGEMENT User rights All groups and users within SAP Hybris are based on the principal item type, this is the foundation of all users and groups (unique identifier). A user can be either a Customer or an Employee : • Customers are users designed to visit your store front and place orders. • Employees are users designed to access the back office There are three special principals within SAP Hybris that can’t be modified or updated, the admin and anonymous users and the admin group Access rights SAP Hybris has a permission framework that let you define your own access rules to item types and attributes. An access right can be defined : • globally for a user or user group • for an item type • for an instance of an an item type • for an attribute rights apply from the most general rules to most specific rules Access right definitions could be either positive (granted) or negative (denied) If two rules collide, the most restrictive one would be applied, so if one rule grants read right and another denies read right, the user won’t have read right To assign permissions you have two possibilities : • Legacy Impex scripts that are only compatible with legacy permissions (READ, CHANGE, CREATE, DELETE) • PermissionManagementService is the new recommended way of managing permissions, it allows you to create new permission types Changing permissions takes effect immediately Impex script To define access rights using the legacy Impex script, you need to use $START_USERRIGHTS and $END_USERRIGHTS to indicate that every line in between those two statements are not Impex but user rights definition. Example : $START_USERRIGHTS Type;UID;MemberOfGroups;Password;Target;read;change;create;delete;change_perm UserGroup;employeegroup;; ;;;;Product;+;-;-;-;;;;;CMSItem.name;+;-;-;-;$END_USERRIGHTS 11.2. SEARCH RESTRICTIONS 199 Permission service The out of the box de.hybris.platform.servicelayer.security.permissions.PermissionManagementService lets you : • manage item type, item, attribute and global permissions • create new permissions Example how to get and set permissions : import de.hybris.platform.servicelayer.security.permissions.PermissionAssignment def def def def userService = spring.getBean("userService") pcs = spring.getBean("permissionCheckingService") typeService = spring.getBean("typeService") pms = spring.getBean("permissionManagementService") def productmanager = userService.getUserForUID("productmanager") println pcs.checkTypePermission("Product", productmanager, "read").getCheckValue() PermissionAssignment permission = new PermissionAssignment("read", productmanager, true) pms.addTypePermission(typeService.getComposedTypeForCode("Product") , permission) println pcs.checkTypePermission("Product", productmanager, "read").getCheckValue() Remember: • de.hybris.platform.servicelayer. security.permissions.PermissionCheckingService is used to check permissions • de.hybris.platform.servicelayer. security.permissions.PermissionManagementService is used to configure permissions • de.hybris.platform.servicelayer. security.permissions.PermissionCRUDService is used to check CRUD operations (read, change, create, remove, change rights) Search restrictions Search restrictions are where clauses automatically added to flexible search queries for specific users and item types. Restrictions do not apply for : • SOLR search • admin group users • direct item type access though the model service 200 CHAPTER 11. USER MANAGEMENT To deactivate search restriction use de.hybris.platform.search. restriction.SearchRestrictionService.e Since restrictions are linked to a session you have access to sessions attributes within the restriction user = ?session.user. To create a restriction you can : • use the service layer to create a restriction item • execute an impex query Service layer example def typeService = spring.getBean("typeService") def userService = spring.getBean("userService") def modelService = spring.getBean("modelService") def restrictedType = typeService.getComposedTypeForCode("Product"); def principal = userService.getUserForUID("anonymous"); def searchRestriction = modelService.create("SearchRestriction"); searchRestriction.setCode("OnlyPublicProduct"); searchRestriction.setActive(Boolean.TRUE); searchRestriction.setQuery("{code} like ’11%’"); searchRestriction.setRestrictedType(restrictedType); searchRestriction.setPrincipal(principal); searchRestriction.setGenerate(Boolean.TRUE); modelService.save(searchRestriction); Impex example INSERT_UPDATE SearchRestriction;code[unique=true];name[lang=en];query;principal(UID);restric ⌋ tedType(code);active;generate ֒→ ;OnlyPublicProduct;Only Public Product;"{code} like ’11%’";anonymous;Product;true;true B2B hierarchy SAP Hybris B2B accelerator gives you the ability to manage company organizations : • Unit, basic block of an organization. A unit could represent a part (location, department) or an entire organization • Users, each user is attached to a unit and must have at least one of these roles : • administrator, can manage a unit 11.3. B2B HIERARCHY 201 • manager, can access reports • approver, can approve orders and define user’s monetary limit • customer, place orders (automatically approved if under the approval threshold) • Cost center, assigned to units, orders are charged against a cost center • Budget, assigned to units, if a unit exceed its budgets then orders are sent to an approver • Credit limit, credit limit are assigned by the merchant to a unit, it exceeds the limit a manual approval is needed When you run SAP Hybris with the B2B accelerator you have access to a new panel under your account to manage your company organization directly from the store front under My Company, to see this your user need to be B2B administrator. 202 CHAPTER 11. USER MANAGEMENT Chapter 12 Study tips 203 204 CHAPTER 12. STUDY TIPS This covers suggestions and recommendations for SAP Hybris developers who have been working on few implementations, have completed all trails from the wiki and are starting to look at the certification proposed by SAP. If you have never attempted any certification exams this will be stressful, all people at work keep asking you when you finally pass that certification! Study plan Before you even sign up for the test you first need to plan how you envision it : 1. I setup a V6.2 local environment 2. I learn how to use the Groovy console to quickly debug and understand SAP Hybris 3. I configure my IDE with SAP Hybris and learn how to be comfortable with the console 4. I pass the assessment exam 5. I study each chapter and play with SAP Hybris and all code fragments provided 6. I pass the first mocked exam in real condition and list all unclear concepts and wrong answers 7. I study again point listed from the first mocked exam 8. I pass the second mocked exam in real condition and list all unclear concepts and wrong answers 9. I study again point listed from the second mocked exam 10. The days before the exam I randomly pick questions and check if I know the answer Play with SAP Hybris To be ready for the exam you need to be able to create a new B2C implementation, customize the initial data, create new pages and controllers and customize the backoffice. While you are doing this you need not to only follow the trails, try to remember things you would love to see within SAP Hybris and implement then yourself (image compression, attribute automatic translation, customized checkout...). Most important is to learn how to tackle issues while developing, understand why an impex file is not being imported or learn the difference between the Jalo layer and service layer! Identify your weakest point After passing the first mock exam you should be able to know what part of the exam will cause you troubles! Focus on that part if it’s Spring configuration start from scratch and read the Spring documentation, once you are comfortable with it come back to SAP Hybris. 12.4. UNDERSTAND THE QUESTIONS 205 Understand the Questions Before you start answering a question look at all possible answers, you would most likely be able to get context clues or understand more deeply the question. Also do not waste time on a question you don’t understand even after reading all possible answers! Simply move on and come back to it at the end of the exam, often you might find help in other questions. Although you might not immediately know the correct answer to a question, if you can reduce the question from four answers down to two, your odds of guessing the correct answer will be markedly improved. Checking the answer After passing a mocked exam the first step should be to look for the answer within SAP Hybris, by looking for the answer yourself you would most likely remember it next time you encounter a similar question. If you can’t find the answer each question has a quick link to see the answer and each answer has a quick link to navigate back to the question. 206 CHAPTER 12. STUDY TIPS Chapter 13 Mock exam 1 207 208 CHAPTER 13. MOCK EXAM 1 Questions Mock 1 - Question 1 What assessments are correct about modules and extensions ? 1. A module is a synonym of template 2. An extension is never used alone 3. A module includes a set of functionalities fulfilled by one or more extensions 4. An extension holds source code and configuration files −→ Go to the solution. Mock 1 - Question 2 What is a saved query ? 1. A query that has been cached by the service layer 2. A way of reusing a query from the flexible search service 3. A flexible search query saved inside the SavedQuery item type and reusable from the HMC 4. There is no such query −→ Go to the solution. Mock 1 - Question 3 Why do you need to execute setantenv.sh** or **setantenv.bat before calling the build platform ? 1. To preload ant classes 2. To configure your PLATFORM_HOME environment variable 3. If you already have ant installed you do need to call this script 4. TO configure ant home and opts environment variable −→ Go to the solution. Mock 1 - Question 4 Choose the correct definition for the bean scope singleton 1. One shared instance, which will be returned by all calls to getBean with the given id 2. One shared instance per thread, which will be returned by all calls to getBean with the given id 3. Independent instance resulting from each call to getBean 4. One shared instance per tenant, which will be returned by all calls to getBean with the given id −→ Go to the solution. 13.1. QUESTIONS 209 Mock 1 - Question 5 What is the "batch=true" Impex header modifier ? 1. creates an individual import context for each value line 2. Allows modifying more than one item for a combination of all unique attributes 3. inserts line by batches, default batch size is 20 4. There is no such header modifier −→ Go to the solution. Mock 1 - Question 6 What are the steps in order to create a backoffice widget ? 1. create a widget view 2. create a widget model hook 3. create a widget definition 4. execute ant widgetGen −→ Go to the solution. Mock 1 - Question 7 What is correct definition of the LTU eviction strategy ? 1. The oldest element would be deleted first 2. The youngest element would be deleted first 3. The most unused element would be deleted first 4. This is not a valid eviction strategy −→ Go to the solution. Mock 1 - Question 8 Which of the following statements are not true regarding the use of inheritance in SAP Hybris Item type definitions ? 1. All new item types must extend Object 2. All new item types must be declared within the Core extension 3. You can extend another item type only if it has been defined within the same extension 4. Item types are not compatible with inheritance −→ Go to the solution. 210 CHAPTER 13. MOCK EXAM 1 Mock 1 - Question 9 Which one of the following design patterns has this as its primary goal ; Provides a single, simplified interface to a complex system of mutually dependent interacting objects ? 1. Strategy 2. Model View Controller 3. Service Layer 4. Facade −→ Go to the solution. Mock 1 - Question 10 If you are performing a flexible search query as the user admin which of the following is true ? 1. all restrictions on the search are ignored 2. you cannot perform a flexible search being admin 3. you will only see in the result for the admin catalog 4. at least one restriction must be configured or no search will be performed −→ Go to the solution. Mock 1 - Question 11 When executing an Impex import, which of the following is true ? 1. you can only import one item type per Impex file 2. each line under header is a potential value line 3. you need to specify the location of the CSV value lines file 4. if you don’t specify a header the Impex framework will find a matching item type −→ Go to the solution. Mock 1 - Question 12 OAUTH2 is 1. a library built by SAP Hybris to handle authentication 2. a library built by Spring to handle authentication 3. an application protocol like HTTP but for authorization 4. an authorization protocol −→ Go to the solution. 13.1. QUESTIONS Mock 1 - Question 13 To load a localized resource bundle, what service should you use ? 1. L10NService 2. I18NService 3. CommonI18NService 4. CommerceCommonI18NService −→ Go to the solution. Mock 1 - Question 14 How can you activate the cluster mode ? 1. cluster mode is on by default 2. cluster mode is always on 3. configure clustermode=true 4. configure clustering=true −→ Go to the solution. Mock 1 - Question 15 What are the logging framework shipped with Hybris ? 1. LOG4J 2. LOG4J2 3. LOG4J3 4. SLF4J −→ Go to the solution. Mock 1 - Question 16 What happen when you run the following Impex script when the currency doesn’t exist yet ? UPDATE Currency;isocode[unique=true];conversion;digits;symbol ;GBP;1;2;£ 1. the import fails, log an error and move on to the next file 2. the import logs a warning and continue to resolve the remaining value lines 3. the import logs a warning, creates a temporary file, and try to import it later on 4. the import ignores non matching value lines when doing only UPDATE −→ Go to the solution. 211 212 CHAPTER 13. MOCK EXAM 1 Mock 1 - Question 17 Why is it considered bad practice to call directly CartService.getSessionCart().getEntries().isEmpty() to test if a customer already has a cart ? 1. this will create a cart if none is yet existing and the cart creation is expensive 2. the customerCartService wrapper is preferred to the raw cart service 3. getSessionCart() is deprecated, use getInMemoryCart() instead 4. getEntries() could be null −→ Go to the solution. Mock 1 - Question 18 What is the correct definition of the Spring MVC dispatcher servlet ? 1. receives incoming API requests and dispatch them to the right extension 2. process all incoming requests made to the root path 3. servlet responsible to find a matching controller and view to generate a response 4. servlet responsible to load data from the database, load the JSP view and delegate the request handling to a matching controller −→ Go to the solution. Mock 1 - Question 19 What template do you use do create a new cockpit ? 1. ycockpit 2. cockpit 3. ybackoffice 4. backoffice −→ Go to the solution. Mock 1 - Question 20 Which of the following cockpit import by convention files have a correct path and name ? 1. cockpitextension/resources/cockpitextension-admin/cscockpit/all/listViewContentBrowser_CatalogVersion.xm 2. cockpitextension/resources/cockpitextension/cscockpit/cockpitgroup/listViewContentBrowser_CatalogVersion 3. cockpitextension/resources/cockpitextension-admin/cscockpit/cockpitgroup/listViewContentBrowser.xml 4. cockpitextension/resources/cockpitextension-config/cscockpit/cockpitgroup/listViewContentBrowser_CatalogV −→ Go to the solution. 13.1. QUESTIONS 213 Mock 1 - Question 21 Which methods are valid to create and save a new product item from the service layer ? 1. ModelService modelService = spring.getBean("modelService"); ProductModel prd = new ProductModel(); //Fill the product object with valid data modelService.save(prd); 2. ModelService modelService = spring.getBean("modelService"); ProductModel prd = modelService.create("Product"); //Fill the product object with valid data modelService.save(prd); 3. ModelService modelService = spring.getBean("modelService"); ProductService productService = spring.getBean("productService"); //Assuming we load a valid product ProductModel prdTemplate = productService.getProductForCode("000001") ProductModel prd = modelService.clone(prdTemplate) //Set all unique data modelService.save(prd); −→ Go to the solution. Mock 1 - Question 22 What is the correct definition of the ant target updatesystem ? 1.type system definitions are modified to match the new type system definition in the items.xml files, the update mechanism makes sure that all data that existed in the system before the update is still accessible after the update 2.type system definitions are recreated to match the new type system definition in the items.xml files, the update mechanism makes sure that all data that existed in the system before the update is still accessible after the update 3.type system definitions are modified to match the new type system definition in the items.xml files, the update mechanism makes sure that all data that existed in the system are replaced by new data 4.type system definitions are modified to match the new type system definition in the springitems.xml files, the update mechanism makes sure that all data that existed in the system before the update is still accessible after the update −→ Go to the solution. Mock 1 - Question 23 How many currency items would be inserted from the following impex query, assuming they don’t exist in SAP Hybris yet ? INSERT_UPDATE Currency;isocode[unique=true];conversion;digits;symbol ;DKK;1;2;DKK 214 CHAPTER 13. MOCK EXAM 1 INSERT Currency;isocode[unique=true];conversion;digits;symbol ;DKK;7;2;DKK INSERT_UPDATE Currency;isocode[unique=true];conversion;digits ;VND;7;2;VND 1. none 2. one 3. two 4. three −→ Go to the solution. Mock 1 - Question 24 Which of the following application contexts are valid ? 1. tenant service application context 2. tenant core application context 3. global web application context 4. hac web application context −→ Go to the solution. Mock 1 - Question 25 Is this a valid item type definition ? <itemtype code="DeactivateOutdatedProductCronJob"> <description>Job to deactivate out-dated products</description> <attributes> <attribute type="java.util.Date" qualifier="minLastUpdate"> <persistence type="memory" /> </attribute> </attributes> </itemtype> 1. no, a cronjob must extends the Cronjob item type 2. no, persistence can’t be of type memory 3. no, the jalo class definition is missing 4. yes −→ Go to the solution. 13.1. QUESTIONS 215 Mock 1 - Question 26 What does the ant target customize do ? 1. inject all properties into tomcat configuration 2. execute all call back scripts 3. copies all files from /config/customize folder to /bin folder recursively 4. customize the tomcat configuration folder from the config folder −→ Go to the solution. Mock 1 - Question 27 What SQL query could be generated from the following flexible search query ? select {pk} from {Product as prd} 1. SELECT prd.PK FROM products prd WHERE (prd.TypePkString IN (?,?)) 2. SELECT item_t0.PK FROM products item_t0 WHERE (item_t0.TypePkString IN (?,?)) 3. SELECT PK FROM products WHERE (TypePkString IN (?,?)) 4. SELECT item_t0.PK FROM products item_t0 WHERE (item_t0.TypePkString IN (?,?)) −→ Go to the solution. Mock 1 - Question 28 Which of the following item type attribute default value definition valid ? 1. <defaultvalue>java.lang.Boolean.FALSE</defaultvalue> 2. enumerationService().getEnumerationValue("validEnumeration", "validValue") 3. <defaultvalue>Boolean.FALSE</defaultvalue> 4. <defaultvalue>com.hybhub.core.ExistingClass.EXISTING_STATIC_STRING</defaultvalue> −→ Go to the solution. Mock 1 - Question 29 The SAP Hybris out of the box model service is used to ? 1. refresh the status of a data object 2. lock a model object 3. remove a model object 4. create new item type definition −→ Go to the solution. 216 CHAPTER 13. MOCK EXAM 1 Mock 1 - Question 30 What statements about the backoffice application orchestrator are wrong ? 1. it’s already visible when you are logged as an admin 2. you use it to create and modify back office application 3. you use it to import new data 4. you need to press F4 to activate it −→ Go to the solution. Mock 1 - Question 31 What is cached by SAP Hybris ? 1. result of flexible search queries 2. media images 3. item attributes 4. user sessions −→ Go to the solution. Mock 1 - Question 32 Choose the correct definition of an interface based architecture ? 1. defines an application as a collection of components, in which API calls between components are made through abstract interfaces, not concrete classes 2. defines an application as a collection of components, in which API calls between components are made through concrete classes, not abstract interfaces 3. defines an application as a collection of static components, in which API calls between components are made static references 4. defines an application as a collection of RESTFUL API entry points, calls between components are made through HTTP requests −→ Go to the solution. Mock 1 - Question 33 What steps are required in order to create a new SAP Hybris cron job ? 1. create a new trigger for the job 2. create a job implementation of JobPerformable 3. Update your system 4. create a new item that extends cronjob −→ Go to the solution. 13.1. QUESTIONS 217 Mock 1 - Question 34 How do you declare an attribute dynamic ? 1. <attribute type="dynamic" attributeHandler="myAttributeHandlerBean" /> 1. <persistence type="dynamic" attributeHandler="myAttributeHandlerBean" /> 1. <dynamic attributeHandler="myAtttibuteHandlerBean" /> 1. <persistence dynamic="myAtttibuteHandlerBean" /> −→ Go to the solution. Mock 1 - Question 35 How is Spring configured in SAP Hybris ? 1. Explicit configuration in XML 2. Explicit configuration in Java 3. Implicit bean discovery and automatic wiring 4. Explicit configuration in XML, implicit bean discovery and automatic wiring −→ Go to the solution. Mock 1 - Question 36 How could you enable single product catalog synchronization from the PCM ? 1. catalog synchronization cannot be done from the PCM, it’s meant to be done by administrators only 2. configure expressUpdateCatalogVersions=catalog:version 3. each product can be synchronized individually out of the box 4. PCM is not for products −→ Go to the solution. Mock 1 - Question 37 What is the default authorization framework for the OCC web services ? 1. spring-security 2. oauth 2.0 3. java-security 4. sso −→ Go to the solution. 218 CHAPTER 13. MOCK EXAM 1 Mock 1 - Question 38 In order to test a dao implementation what test should you write ? 1. @UnitTest 2. @DatabaseTest 3. @SqlTest 4. @IntegrationTest −→ Go to the solution. Mock 1 - Question 39 How can you use environment variables in your Hybris configuration ? 1. from a call back scripts 2. using hybhub.config=$NAME assuming the env variable exists and is named NAME 3. using hybhub.config=<CHANGE_ME> assuming the env variable exists and is name y_hybhub_config 4. using hybhub.config= ? assuming the env variable exists and is name hybhub_config −→ Go to the solution. Mock 1 - Question 40 What statements are correct about he following impex ? INSERT_UPDATE MediaFolder;qualifier[unique=true];path[unique=true] ;images;images ;email-body;email-body ;email-attachments;email-attachments 1. only creates new MediaFolder 2. generates a unique database index from the qualifier and the path 3. contains 3 value lines 4. doesn’t use any macro −→ Go to the solution. Mock 1 - Question 41 Which one of the following flexible correct is valid ? 1. select name[de] from product 2. select name.de from product 3. select name[de] from product 4. select name from product,lang where lang = ’de’ −→ Go to the solution. 13.1. QUESTIONS 219 Mock 1 - Question 42 Choose the right definition for a payment capture ? 1. authorize an amount of money after it has been reserved 2. transfer money back for a given order 3. transfer money from after it has been authorized 4. lock an amount of money on the card’s holder account −→ Go to the solution. Mock 1 - Question 43 Using the out of the box impex import convention, what files would be imported during the update process (only create essential data checked) ? 1. extension/resources/impex/0_essentialdata.impex 2. extension/resources/impex/essentialdata_0.impex 3. extension/resources/impex/core/essentialdata_0.impex 4. extension/resources/impex/projectdata_users.impex −→ Go to the solution. Mock 1 - Question 44 "A navigation technique for accessing a collection of available information by narrowing down long lists of objects to a manageable size. The reduction is achieved through attribute filters that can by applied in flexible combinations of any order." best describes ? 1. facet search 2. solr 3. category search 4. flexible search −→ Go to the solution. Mock 1 - Question 45 How many catalog versions can be attached to a catalog ? 1. zero, a catalog defines its own version 2. one, the version can be shared across different catalogs, for example Online 3. two, catalogs have their own Staged and Online catalog versions 4. as many as needed, there are no limits at how many versions a catalog can have −→ Go to the solution. 220 CHAPTER 13. MOCK EXAM 1 Mock 1 - Question 46 How many transactions would be created by the following Groovy script ? def transaction = de.hybris.platform.tx.Transaction.current() transaction.begin() transaction.begin() //Do something transaction.commit() transaction.begin() //Do something transaction.commit() transaction.commit() 1. code execution will fail 2. one transaction would be created 3. two transactions would be created 4. three transaction would be created −→ Go to the solution. Mock 1 - Question 47 What is the main entry servlet of Spring MVC application ? 1. SpringMvcServlet 2. DispatcherServlet 3. JaloServlet 4. RequestContextServlet −→ Go to the solution. Mock 1 - Question 48 What statements are true about CMSItem and CMSRelation item types ? 1. their sub items are catalog version aware 2. it’s used by all tag components 3. every CMS types extend one or the other 4. they are defined under the core extension −→ Go to the solution. 13.1. QUESTIONS 221 Mock 1 - Question 49 How can you configure a new checkout flow ? 1. from an impex query 2. from a java implementation of de.hybris.platform.acceleratorstorefrontcommons.checkout.CheckoutFLow 3. from a spring configuration 4. from JSP tag file −→ Go to the solution. Mock 1 - Question 50 What application context is valid to create a new region cache for an item type ? 1. core tenant 2. master tenant 3. core static 4. global static −→ Go to the solution. Mock 1 - Question 51 Choose the best definition for the following exception : de.hybris.platform.servicelayer.exceptions.AmbiguousIdentifierExcep ? 1. the service layer can’t find any matching identifier 2. the service layer found more than one matching item and expected only one 3. the service layer doesn’t know what attribute is used as the unique identifier 4. the service layer doesn’t know the item type −→ Go to the solution. Mock 1 - Question 52 What informations can you extract from a PK ? 1. type code 2. last modified time 3. created time 4. user who created the item −→ Go to the solution. 222 CHAPTER 13. MOCK EXAM 1 Mock 1 - Question 53 The Spring Integration Framework is used for ? 1. OCC webservices 2. Datahub 3. Hot Folder 4. Service Layer −→ Go to the solution. Mock 1 - Question 54 Where can you specify an index for an item type ? 1. resources/extensionName-indexes.xml 2. resources/extensionName-items.xml 3. resources/extensionName-indices.xml 4. resources/database/index.sql −→ Go to the solution. Mock 1 - Question 55 Choose the right definition for the order splitting service ? 1. split an order into sets of order entries, for each set a consignment is then created 2. split an order into multiple orders, for each orders a consignment is then created 3. split order entries into sets of order entries, for each set a consignment is then created 4. split a consignment into sets of consignments, for each set a new consignment is then created −→ Go to the solution. Mock 1 - Question 56 When designing a new web service what HTTP header should you choose to implement a create object endpoint ? 1. GET 2. PUT 3. CREATE 4. POST −→ Go to the solution. 13.1. QUESTIONS 223 Mock 1 - Question 57 What is the downside of product variants ? 1. none 2. you cannot have common attributes between variant objects without changing the base product definition 3. products are virtual 4. all variant objects and the base product share the same unique code which makes any search not trivial −→ Go to the solution. Mock 1 - Question 58 How could you group solr attribute values in a single set to avoid excessive number of values ? 1. use a group by statement 2. use a paginated solr search query 3. use a facet range 4. use a facet sort −→ Go to the solution. Mock 1 - Question 59 Which of the following interceptors are valid ? 1. load 2. init 3. prepare 4. remove −→ Go to the solution. Mock 1 - Question 60 What statements are wrong about the build framework ? 1. it compiles extension in a pre-defined order 2. it generates and compiles sources from items.xml files 3. every ant targets compile the source files 4. it is runnable only from your platform folder −→ Go to the solution. 224 CHAPTER 13. MOCK EXAM 1 Mock 1 - Question 61 A Media can be ? 1. a image 2. a zip file 3. a flat file 4. a string −→ Go to the solution. Mock 1 - Question 62 Which one of the following service is best suited to load a resource bundle for a specific language ? 1. I18NService 2. CommonI18NService 3. CommerceCommonI18NService 4. L10NService −→ Go to the solution. Mock 1 - Question 63 If I add the following search restriction for product and for user anonymous, what will happen when anonymous users execute a product search ? {name} is not null 1. this will fail, where is missing 2. this will replace any where clause for anonymous and product 3. this will be added to the where clause for anonymous and product 4. this will be added to the where clause for anonymous and product during solr indexing −→ Go to the solution. Mock 1 - Question 64 What happened when you execute the following Groovy script ? import java.util.HashSet import de.hybris.platform.core.model.user.UserGroupModel def modelService = spring.getBean("modelService") def group = modelService.create("userGroup") group.setUid("newGroup") modelService.save(group) def groups = new HashSet<UserGroupModel>() 13.1. QUESTIONS groups.add(group) def customer = modelService.create("customer") customer.setUid("newCustomer") customer.setName("newCustomer") customer.setGroups(groups) modelService.save(customer) 1. a service layer exception is thrown 2. only the customer is created 3. only the user group is created 4. both the customer and the user group are created −→ Go to the solution. Mock 1 - Question 65 What statements are true about the promotion engine ? 1. runs on the rule engine 2. runs on the jalo layer 3. uses rule aware object (RAO) actions 4. runs when the promotion strategy is called during cart calculation −→ Go to the solution. Mock 1 - Question 66 How can you create a new socket for a custom backoffice widget ? 1. open your widget definition.xml file 2. use the HMC 3. run the widget wizard from OCC 4. create a new socket.zul file −→ Go to the solution. Mock 1 - Question 67 What statements are true about Apache SOLR ? 1. SAP Hybris uses an embedded SOLR server 2. SOLR uses the same database as SAP Hybris 3. all products are cached inside SOLR 4. SOLR can have multiple index −→ Go to the solution. 225 226 CHAPTER 13. MOCK EXAM 1 Mock 1 - Question 68 Why would run the following flexible search query ? select {pk} from {Product!} 1. you need only to load product subtype items 2. you need only to load product type items 3. you need only to load product variants 4. you need to tell SAP Hybris not to cache the result −→ Go to the solution. Mock 1 - Question 69 What is the right way to create a localized string attribute ? 1. <attribute qualifier="name" type="java.lang.String"> <persistence type="property" localized="true"/> </attribute> 2. <attribute qualifier="name" type="localized:String"> <persistence type="property"/> </attribute> 3. <attribute qualifier="name" type="localized:java.lang.String"> <persistence type="property" /> </attribute> 4. <attribute qualifier="name" type="localized:java.lang.String"> </attribute> −→ Go to the solution. Mock 1 - Question 70 What can you define permissions for (using the permission framework) ? 1. OCC access 2. item type 3. instance of an item type 4. attribute −→ Go to the solution. 13.1. QUESTIONS Mock 1 - Question 71 What is the purpose of the following class ? /* * [y] hybris Platform * * Copyright (c) 2000-2016 SAP SE * All rights reserved. * * This software is the confidential and proprietary information of SAP * Hybris ("Confidential Information"). You shall not disclose such * Confidential Information and shall use it only in accordance with the * terms of the license agreement you entered into with SAP Hybris. */ package com.hybhub.setup; import ... @SystemSetup(extension = HybhubhookConstants.EXTENSIONNAME) public class HybhubHookSystemSetup extends AbstractSystemSetup { ֒→ ֒→ ֒→ final static Logger LOG = LoggerFactory.getLogger(HybhubHookSystemSetup.class); @SystemSetupParameterMethod @Override public List<SystemSetupParameter> getInitializationOptions() { final List<SystemSetupParameter> params = new ArrayList<>(); params.add(createBooleanSystemSetupParameter("key", "Choice", false)); return params; } @SystemSetup(type = Type.ALL, process = Process.ALL) public void createEssentialData(final SystemSetupContext context) { final boolean choice = getBooleanSystemSetupParameter(context, "choice"); if (choice) { //Do something } else { //Do something else } } 227 228 CHAPTER 13. MOCK EXAM 1 } 1. customize a HMC wizard 2. customize the impex import service 3. customize the update and initialization 4. customize the available import choices when running an update from the HMC −→ Go to the solution. Mock 1 - Question 72 Is this a valid impex import ? INSERT_UPDATE Title;code #% beforeEach: print(line[1]); ;Doctor 1. yes 2. yes only if bean shell execution is activated 3. yes only if the title Doctor doesn’t exist yet 4. no −→ Go to the solution. Mock 1 - Question 73 What is the recommended way of setting cluster ids ? 1. use cluster.node.autodiscovery=true 2. create a subnet and configure nodes to listen to the broadcast address 3. manually configure a unique id on each node 4. activate the Spring Cluster functionality −→ Go to the solution. Mock 1 - Question 74 Which are valid steps of the ant all target ? 1. Resolving extensions dependencies 2. Restart the application server 3. Delete all previously compiled classes 4. Generate sources for all extensions −→ Go to the solution. 13.1. QUESTIONS 229 Mock 1 - Question 75 Which statements are correct about widget definitions ? 1. widget definition is as simple as creating a Spring bean under your core extension spring configuration 2. A widget needs to extend another widget id in order to be available 3. Each widget ids must be unique otherwise they wouldn’t be available 4. Widgets definition are located under myextension/backoffice/resources/widgets/widgetName/definition.xml −→ Go to the solution. Mock 1 - Question 76 What is the best way to create a new addon for your store front ? 1. generate a new extension from yempty 2. generate a new extension from yoccaddon 3. generate a new extension from yaddon 4. generate a new extension from addon −→ Go to the solution. Mock 1 - Question 77 Is this a valid business process definition ? <?xml version="1.0" encoding="utf-8"?> <process> <action id="action1" bean="action1"> <transition name="OK" to="success"/> <transition name="NOK" to="action2"/> </action> <action id="action2" bean="action2"> <transition name="OK" to="error"/> </action> <end id="error" state="ERROR">All went wrong.</end> <end id="success" state="SUCCEEDED">Order placed.</end> </process> 1. yes 2. yes if you had a new action with start=true 3. yes if action1 implements StartProcess 4. no −→ Go to the solution. 230 CHAPTER 13. MOCK EXAM 1 Mock 1 - Question 78 What are the required steps to create a new CMSComponent ? 1. create an item that extends AbstractCMSComponent or one of its subtype 2. create an item that extends CMSItem or one of its subtype 3. a Spring MVC controller that extends AbstractCMSComponentController 4. a JSP (or other front end technology) to render the component −→ Go to the solution. Mock 1 - Question 79 When should you run a full Apache SOLR indexation ? 1. when you restart the SOLR server 2. when you update a product 3. when you update prices 4. every day to recreate a clean index (not defragmented) −→ Go to the solution. Mock 1 - Question 80 How many websites could you have per tenant ? 1. only one because orders and customers are tenant dependent 2. websites are not tenant aware 3. as many as you need 4. as many as you want if you are running a cluster Solutions Mock 1 - Solution 1 3,4 are correct. A module is made of one or more extensions to provide a set of related functionalities, like the WCMS module made from different extensions : cms2lib, cmscockpit, cm2. −→ Go back to the question. Mock 1 - Solution 2 3 is correct. A saved query can be used from the HMC to search a given item type from a customized and possibly parameterized flexible search query. 13.2. SOLUTIONS 231 −→ Go back to the question. Mock 1 - Solution 3 2,4 are correct. The setantenv scrips configure : • PLATFORM_HOME • ANT_OPTS • ANT_HOME • add ANT_HOME to your path −→ Go back to the question. Mock 1 - Solution 4 1 is correct. 3 is the definition of prototype scope, 4 is correct only if you are inside different tenant application context. −→ Go back to the question. Mock 1 - Solution 5 2 is correct. When you need to update more than one item from the same value line use the batch mode. −→ Go back to the question. Mock 1 - Solution 6 1,3 are correct. −→ Go back to the question. Mock 1 - Solution 7 4 is correct. The available eviction strategy are : • LRU (Least Recently Used) • LFU (Least Frequently Used) • FIFO (First In First Out) −→ Go back to the question. Mock 1 - Solution 8 *1,2,3,4 are correct. 1. All new item types extends GenericItem by default 2. Any extension with a core module defined could declare new item types 3. You can extend any item type from your extensions as long as you have a dependency to the extension where the item type you inherit from is defined 4. All item types extend one item type (except AbstractItemModel) −→ Go back to the question. 232 CHAPTER 13. MOCK EXAM 1 Mock 1 - Solution 9 4 is correct. −→ Go back to the question. Mock 1 - Solution 10 1 is correct. −→ Go back to the question. Mock 1 - Solution 11 2 is correct. After a header all lines would be imported (unless they are empty, contain comments or macro). −→ Go back to the question. Mock 1 - Solution 12 4 is correct. SAP Hybris OCC uses OAUTH2, the Java implementation of the secured protocol is provided by Spring under Spring-security-oauth2. −→ Go back to the question. Mock 1 - Solution 13 1 is correct. • I18NService is to load Java Locale objects • CommonI18NService is to load SAP Hybris internationalization objects (CountryModel, LangageModel...) • CommerceCommonI18NService is to load SAP Hybris internationalization objects (CountryModel, LangageModel...) for the current base site −→ Go back to the question. Mock 1 - Solution 14 3 is correct. −→ Go back to the question. Mock 1 - Solution 15 2 is correct. SLF4J is a simple logging facade for logging frameworks. −→ Go back to the question. Mock 1 - Solution 16 3 is correct. If there is no item that matches the values of all key attributes, the value line cannot be resolved. The value line is dumped into a temporary file and the ImpEx extension tries to resolve the value line later on. −→ Go back to the question. Mock 1 - Solution 17 1 is correct. Make sure your system does not create a new cart when the user does not need it. This has massive impact to the system performance since for every session a new entry in the Carts table is created. 13.2. SOLUTIONS 233 In SAP Hybris, if using cartService.getSessionCart(), a new cart object is created implicitly if none is yet existing for this session. You should always use cartService.hasSessionCart() before. −→ Go back to the question. Mock 1 - Solution 18 3 is correct. A dispatcher servlet is the entry point of the Spring MVC framework, it loads the right controller from its mapping, call the controller, use the view name answered by the controller to load a view, and use the view and its model (loaded inside the controller) to generate a a response. −→ Go back to the question. Mock 1 - Solution 19 1 is correct. −→ Go back to the question. Mock 1 - Solution 20 4 is correct. Remember extensionName/resources/extensionName-config/userGroup/context_itemType.xml −→ Go back to the question. Mock 1 - Solution 21 1,2,3 are correct −→ Go back to the question. Mock 1 - Solution 22 1 is correct. −→ Go back to the question. Mock 1 - Solution 23 2 is correct. INSERT Currency;isocode[unique=true];conversion;digits ;DKK;7;2;DKK would fail since because it’s conflicting with an existing item (de.hybris.platform.impex.jalo.imp.ItemConflictException) −→ Go back to the question. Mock 1 - Solution 24 2,4 are correct. −→ Go back to the question. Mock 1 - Solution 25 2 is correct. To create a new SAP Hybris cronjob you need to extend the cronjob item type, but it doesn’t make the item definition invalid. −→ Go back to the question. Mock 1 - Solution 26 3 is correct. −→ Go back to the question. Mock 1 - Solution 27 2 is correct. 234 CHAPTER 13. MOCK EXAM 1 −→ Go back to the question. Mock 1 - Solution 28 1,3,4 are correct. The correct way to load enumeration default values is to call the enumeration manager using em(). −→ Go back to the question. Mock 1 - Solution 29 2,3 are correct. have a look at de.hybris.platform.servicelayer.model.ModelService. −→ Go back to the question. Mock 1 - Solution 30 1,3 are correct. −→ Go back to the question. Mock 1 - Solution 31 1,3 are correct. −→ Go back to the question. Mock 1 - Solution 32 1 is correct. −→ Go back to the question. Mock 1 - Solution 33 2,3,4 are correct. −→ Go back to the question. Mock 1 - Solution 34 2 is correct. −→ Go back to the question. Mock 1 - Solution 35 4 is correct. −→ Go back to the question. Mock 1 - Solution 36 3 is correct. −→ Go back to the question. Mock 1 - Solution 37 2 is correct. −→ Go back to the question. Mock 1 - Solution 38 4 is correct. −→ Go back to the question. Mock 1 - Solution 39 3 is correct. 13.2. SOLUTIONS 235 −→ Go back to the question. Mock 1 - Solution 40 3,4 are correct. −→ Go back to the question. Mock 1 - Solution 41 1 is correct. −→ Go back to the question. Mock 1 - Solution 42 3 is correct. Capture, is the action of transferring money into the merchant account after it has been authorized. −→ Go back to the question. Mock 1 - Solution 43 2 is correct. 1,2 don’t respect the conveniont (essentialdata*.impex) 3 project data are not being imported in this scenario. −→ Go back to the question. Mock 1 - Solution 44 1 is correct. −→ Go back to the question. Mock 1 - Solution 45 4 is correct. −→ Go back to the question. Mock 1 - Solution 46 2 is correct. Remember that only one transaction per thread can be created. −→ Go back to the question. Mock 1 - Solution 47 2 is correct. −→ Go back to the question. Mock 1 - Solution 48 1,3 are correct. −→ Go back to the question. Mock 1 - Solution 49 3 is correct. −→ Go back to the question. Mock 1 - Solution 50 4 is correct. −→ Go back to the question. Mock 1 - Solution 51 2 is correct. 236 CHAPTER 13. MOCK EXAM 1 −→ Go back to the question. Mock 1 - Solution 52 1 is correct. −→ Go back to the question. Mock 1 - Solution 53 3 is correct. −→ Go back to the question. Mock 1 - Solution 54 2 is correct. −→ Go back to the question. Mock 1 - Solution 55 1 is correct. −→ Go back to the question. Mock 1 - Solution 56 4 is correct. −→ Go back to the question. Mock 1 - Solution 57 2 is correct. Product variants have an attribute baseProduct of type Product, so any to add shared attributes between variants object you need to update the base product definition. −→ Go back to the question. Mock 1 - Solution 58 3 is correct. −→ Go back to the question. Mock 1 - Solution 59 1,2,3,4 are correct. −→ Go back to the question. Mock 1 - Solution 60 1,3,4 are correct. −→ Go back to the question. Mock 1 - Solution 61 1,2,3 are correct. −→ Go back to the question. Mock 1 - Solution 62 4 is correct. −→ Go back to the question. Mock 1 - Solution 63 3 is correct. 13.2. SOLUTIONS 237 −→ Go back to the question. Mock 1 - Solution 64 4 is correct. −→ Go back to the question. Mock 1 - Solution 65 1,3 are correct. −→ Go back to the question. Mock 1 - Solution 66 1 is correct. −→ Go back to the question. Mock 1 - Solution 67 4 is correct. 1 is wrong since SAP Hybris V6 Apache SOLR is running in its own process. −→ Go back to the question. Mock 1 - Solution 68 2 is correct. −→ Go back to the question. Mock 1 - Solution 69 3 is correct. 4 is wrong, persistence is mandatory. −→ Go back to the question. Mock 1 - Solution 70 2,3,4 are correct. −→ Go back to the question. Mock 1 - Solution 71 3 is correct. −→ Go back to the question. Mock 1 - Solution 72 4 is correct. A bean shell script can be executed from an impex script but an update need a unique identifier. −→ Go back to the question. Mock 1 - Solution 73 1 is correct. −→ Go back to the question. Mock 1 - Solution 74 1,2,4 are correct. −→ Go back to the question. Mock 1 - Solution 75 3,4 are correct. 238 CHAPTER 13. MOCK EXAM 1 −→ Go back to the question. Mock 1 - Solution 76 3 is correct. −→ Go back to the question. Mock 1 - Solution 77 4 is correct. Missing start="startAction" name="processName" processClass="Process.Class" −→ Go back to the question. Mock 1 - Solution 78 1,4 are correct. You don’t have to provide a controller, if none is provided the DefaultCMSComponentController is used. −→ Go back to the question. Mock 1 - Solution 79 4 is correct. −→ Go back to the question. Mock 1 - Solution 80 3 is correct. Chapter 14 Mock exam 2 239 240 CHAPTER 14. MOCK EXAM 2 Questions Mock 2 - Question 1 What is/are the best definition(s) of the JALO Layer ? 1. means Jakarta Logic 2. mix data and business logic 3. it’s a piece of the service layer 4. JALO classes are generated during runtime −→ Go to the solution. Mock 2 - Question 2 What changes need to be done in the following query to be imported successfully ? \$productCatalog=apparelProductCatalog \$productCatalogName=Apparel Product Catalog INSERT_UPDATE ֒→ ApparelProduct;code;\$catalogVersion;unit(code);supercategories(code,\$catalogVersion) ;300441142;;pieces;Blue Tomato,caps 1. add a macro for catalog version 2. change the attribute header INSERT_UPDATE to INSERT_UNIQUE 3. mark code with [unique=true] 4. use the type Product instead of ApparelProduct −→ Go to the solution. Mock 2 - Question 3 Choose the correct file to localize HMC entries in English (extension’s name is hybhubcore)? 1. hybhubcore/resources/localization/hybhubcore-locales_en.properties 2. hybhubcore/resources/localization/hmc-locales_en.properties 3. hybhubcore/hmc/localization/hybhubcore-locales_en.properties 4. hybhubcore/resources/localization/english/hybhubcore.properties −→ Go to the solution. 14.1. QUESTIONS 241 Mock 2 - Question 4 Select the right definition for the tenant concept ? 1. a single SAP Hybris installation with multiple localextensions.xml 2. a clustered SAP Hybris installation with one database 3. a single SAP Hybris installation with distinct sets of data 4. a single SAP Hybris installation with a specific database for each store −→ Go to the solution. Mock 2 - Question 5 When do you use cluster aware events ? 1. when you run a cluster all events need to be cluster aware 2. since SAP Hybris V6 all events are cluster aware 3. when you need to run an event from a specific node 4. when you need to broadcast an event to all nodes −→ Go to the solution. Mock 2 - Question 6 Why does the rule engine (used by the promotion engine) include a versioning and archiving mechanism ? 1. to keep an history of all promotions that ever existed on the system 2. to have a consistent representation of the applied rules when promotions were applied 3. to be able to work on different time zone 4. all item types in SAP Hybris are archived and versioned −→ Go to the solution. Mock 2 - Question 7 What is wrong with the following item type definition ? <itemtype code="Subscription" extends="GenericItem"> <attributes> <attribute type="java.lang.String" qualifier="code"> <persistence type="property" /> <modifiers unique="true"/> </attribute> </attributes> <indexes> <index name="codeIdx"> <key attribute="code"/> </index> </indexes> </itemtype> 242 CHAPTER 14. MOCK EXAM 2 1. A Jalo class definition is missing 2. An index needs to be on at least two attributes 3. GenericItem is not a valid item to extend from 4. A deployment table specification is missing −→ Go to the solution. Mock 2 - Question 8 Out of the box, what CMS items are used to build the top navigation menu ? 1. NavigationBarComponent 2. TopMenuNavigationComponent 3. CMSNavigationNode 4. CMSLinkComponent −→ Go to the solution. Mock 2 - Question 9 What steps do you need to fulfill in order to create a new cronjob ? 1. create a new cron job expression in your Spring XML 2. implement a job in Java 3. create a new item type extending CronJob 4. add the new cronjob in your local.properties −→ Go to the solution. Mock 2 - Question 10 What statements are wrong about catalog synchronization ? 1. only existing items can be updated 2. no items can be deleted 3. all item types are being synchronized 4. the same catalog version can be a target and a source −→ Go to the solution. 14.1. QUESTIONS 243 Mock 2 - Question 11 Cached data are removed from the cache when ? 1. the cache is full 2. a cache invalidation notification is received from another node 3. an item is created 4. an item is removed −→ Go to the solution. Mock 2 - Question 12 What statements are true about classification ? 1. classification is horizontal unlike categorization which is vertical 2. a product can be classified by only one classification attribute 3. classification is not being used by SAP Hybris because its model type definition is flexible 4. classification is not compatible with SOLR −→ Go to the solution. Mock 2 - Question 13 What can you do from an addon ? 1. add front end files (JSP, HTML, CSS and Javascript) 2. generate or customize the data model 3. declare new Spring Services or Facades or customize existing 4. declare new Controllers or customize existing −→ Go to the solution. Mock 2 - Question 14 What configuration file has the highest priority ? 1. project.properties from the platform extension 2. advanced.properties from the platform extension 3. local.properties from the config extension 4. local.properties from the custom extension −→ Go to the solution. 244 CHAPTER 14. MOCK EXAM 2 Mock 2 - Question 15 When you create the following item type A what classes are being generated ? <itemtype code="A" extends="ItemB" generate="false"> <attributes> <attribute type="java.lang.String" qualifier="code"> <persistence type="property" /> <modifiers unique="true"/> </attribute> </attributes> </itemtype> 1. A.java 2. GeneratedA.java 3. AJalo.java 4. AModel.java −→ Go to the solution. Mock 2 - Question 16 When configuring the Apache SOLR indexer mode what does DIRECT means ? 1. access the embedded Apache SOLR server 2. send request to Apache SOLR using its REST API 3. all index operations would be made directly on the index 4. all index operations would be made on a temporary index before indexes are switch −→ Go to the solution. Mock 2 - Question 17 Is it possible for two tenants to use different extensions ? 1. yes using two different localextensions.xml, one for each tenant 2. yes using allowed.extensions and forbidden.extensions inside a tenant specific configuration file 3. yes using <tenantid>.allowed.extensions and <tenantid>.forbidden.extensions inside your configuration 4. no, SAP Hybris is not compatible with this −→ Go to the solution. 14.1. QUESTIONS Mock 2 - Question 18 How do you install a new addon ? 1. add the addon inside your localextensions.xml 2. add the addon under external-dependencies.xml 3. use the ant target addoninstall 4. use the ant target syncaddons −→ Go to the solution. Mock 2 - Question 19 Is this a valid Flexible Search Query ? select {p.description[fr]:o} from {Product as p} where {p:pk} in ( {{ select {p:pk} from {product as p} where {p:code} like ’%0%’ }} ) 1. no 2. yes if we delete :o 3. yes if we delete the whole where statement 4. yes −→ Go to the solution. Mock 2 - Question 20 Out of the box how can you access the assisted service module functionalities ? 1. from the targeted storefront add a request parameter asm equal true 2. from the backoffice open the asm perspective 3. go to the /asmcockpit 4. go to the /cscockpit −→ Go to the solution. Mock 2 - Question 21 How can you initialize SAP Hybris ? 1. using ant initialize 2. from the HAC 3. from the HMC 4. from the backoffice −→ Go to the solution. 245 246 CHAPTER 14. MOCK EXAM 2 Mock 2 - Question 22 What is highest B2B organization block ? 1. company 2. unit 3. division 4. region −→ Go to the solution. Mock 2 - Question 23 In order to force a product to be listed on top of a given category, what out of the box feature(s) should you use ? 1. boost rules 2. hero rules 3. hero products 4. top products −→ Go to the solution. Mock 2 - Question 24 What payment steps are described by the following definition ? Transfer back money to a customer account, the transfer not being associated with ֒→ any order or previous transactions. 1. capture 2. stand alone refund 3. capture 4. refund −→ Go to the solution. Mock 2 - Question 25 For what of the following things can the BTG module be used? 1. personalize a web applications based on customers 2. business to groups 3. provides a upgraded version of the CMS cockpit 4. add business targets accessible from the backoffice −→ Go to the solution. 14.1. QUESTIONS 247 Mock 2 - Question 26 What ant target should you use to start working on a new B2C implementation ? 1. ant installer -r b2c_acc 2. ant extgen 3. ant modulegen 4. ant accelerator −→ Go to the solution. Mock 2 - Question 27 What Spring context has the largest scope ? 1. web context 2. core context 3. shared context 4. international context −→ Go to the solution. Mock 2 - Question 28 What will happen during an ImpEx Import when the import process can’t find a reference to an item type ? 1. import process will fail and move onto the next file 2. the value line would be ignored 3. the value line would be saved and a new attempt would be made 4. the value line would be saved and attached to an error log entry −→ Go to the solution. Mock 2 - Question 29 When loading available prices what is the price factory trying to match ? 1. customer / customer group 2. product / product group 3. date range if any configured 4. promotion / promotion group −→ Go to the solution. 248 CHAPTER 14. MOCK EXAM 2 Mock 2 - Question 30 What is the problem with the following business process action ? package com.hybhub.core.process.action; import import import import import de.hybris.platform.core.model.order.OrderModel; de.hybris.platform.orderprocessing.model.OrderProcessModel; de.hybris.platform.processengine.action.AbstractSimpleDecisionAction; de.hybris.platform.servicelayer.model.ModelService; de.hybris.platform.task.RetryLaterException; import javax.annotation.Resource; public class HybhubSimpleAction extends ֒→ AbstractSimpleDecisionAction<OrderProcessModel> { @Resource private ModelService modelService; ֒→ @Override public Transition executeAction(final OrderProcessModel orderProcess) throws RetryLaterException, Exception { OrderModel order = orderProcess.getOrder(); if(order!= null){ order.setNet(Boolean.TRUE); modelService.save(order); return Transition.OK; } return null; } } 1. it doesn’t always return a transition 2. it has not setter for the modelService attribute 3. it should implement Action<T> 4. the package is wrong −→ Go to the solution. Mock 2 - Question 31 What is true about the DefaultCMSComponentController controller ? 1. it will inject all attributes into the model 14.1. QUESTIONS 2. it will inject all front-end attributes (non system) into the model 3. it is used by default if no specific controller are implemented 4. it should be avoided −→ Go to the solution. Mock 2 - Question 32 What is true about the following log extracted from the build process ? [echo] catalog->(validation,commons) 6.0.0.0-SNAPSHOT [p}cib] 1. the catalog extension was automatically required 2. the catalog extension has a core module 3. the catalog extension is deprecated 4. the catalog extension is a platform extension −→ Go to the solution. Mock 2 - Question 33 Choose the correct user rights definition to complete the following import query. \$START_USERRIGHTS Type;UID;MemberOfGroups;Password;Target;read;change;create;delete;change_perm UserGroup;employeegroup;; <Choose the right answer to complete this> \$END_USERRIGHTS 1. ;;;;Media;true+;false;false;false;false 2. ;;;;Media;1;0;0;0;0 3. ;;;;Media;+;-;-;-;4. ;;;;Media;granted;denied;denied;denied;denied −→ Go to the solution. Mock 2 - Question 34 Which of the following components are deprecated ? 1. cockpit framework 2. Jalo layer 3. task engine 4. service layer −→ Go to the solution. 249 250 CHAPTER 14. MOCK EXAM 2 Mock 2 - Question 35 What is the correct way of configuring a new logger for the package com.hybhub.hybhubaddon ? 1. log4j.logger.com.hybhub.hybhubaddon = info 2. log4j.logger.hybhub.name = com.hybhub.hybhubaddon log4j.logger.hybhub.level = info log4j.logger.hybhub.appenderRef.stdout.ref = STDOUT 3. log4j2.logger.hybhub.name = com.hybhub.hybhubaddon log4j2.logger.hybhub.level = info log4j2.logger.hybhub.appenderRef.stdout.ref = STDOUT 4. log4j2.logger.com.hybhub.hybhubaddon = info log4j2.logger.com.hybhub.hybhubaddon = STDOUT −→ Go to the solution. Mock 2 - Question 36 The OCC webservices are ? 1. restful API 2. restless API 3. soap API 4. oAuth API −→ Go to the solution. Mock 2 - Question 37 What statements are wrong about converters ? 1. they all have the same bean parent abstractPopulatingConverter 2. they contain a list of attributes to convert 3. they contain a list a populators to call 4. they are un-aware of the object type they are converting −→ Go to the solution. 14.1. QUESTIONS 251 Mock 2 - Question 38 What is wrong with the following extensioninfo.xml file ? <extensioninfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ֒→ xsi:noNamespaceSchemaLocation="extensioninfo.xsd"> <extension abstractclassprefix="Generated" classprefix="HybhubFacades" ֒→ name="hybhubfacades"> <requires-extension name="hybhubfacades"/> <requires-extension name="hybhubcore"/> <coremodule generated="true" ֒→ manager="de.hybris.platform.jalo.extension.GenericManager" packageroot="com.hybhub.facades"/> ֒→ </extension> </extensioninfo> 1. missing web module declaration 2. missing core module declaration 3. missing hmc module declaration 4. dependency issue −→ Go to the solution. Mock 2 - Question 39 Is the following dynamic attribute handler correct ? package com.hybhub.core.model.dynamic; import de.hybris.platform.core.model.product.ProductModel; import de.hybris.platform.servicelayer.model.attribute.DynamicAttributeHandler; public class HybhubDynamicHandler implements DynamicAttributeHandler<String, ֒→ ProductModel> { @Override public String get(ProductModel product) { return product.getCode() + " - " + product.getDescription(); } } 1. no, it must extends ServiceLayerItems 2. no, it must also implement a setter 3. no, it must be in a different package 4. yes −→ Go to the solution. 252 CHAPTER 14. MOCK EXAM 2 Mock 2 - Question 40 What statements are true about collections under item type definitions ? 1. a collection is comma-separated list of PKs 2. collections are preferred over relations 3. at runtime collections are loaded into a collection of objects 4. collections can store as many objects as needed −→ Go to the solution. Mock 2 - Question 41 Which of the following definition is the correct one for unit tests ? 1. requires access to the database and need a running SAP Hybris 2. requires access to the database and don’t need a running SAP Hybris 3. does not require access to the database and don’t need a running SAP Hybris 4. require access to the database and starts a transaction for each test −→ Go to the solution. Mock 2 - Question 42 What is the following Spring configuration doing ? <context:annotation-config/> <context:component-scan base-package="...,..." scope-resolver="..."/> 1. add a new XML Spring configuration 2. load all detected components from the given packages 3. create a new bean scope 4. configure a request filter −→ Go to the solution. Mock 2 - Question 43 When you synchronize a Media object from a Staged to an Online catalog what happens to the media file ? 1. the Media file is referenced by the Online and Staged Media 2. the Media file is copied to the Online data folder and referenced by the Online Media 3. the Media file is moved to a shared folder and referenced by both Media 4. the Media file is embedded inside the file attribute and synchronize like all other attributes −→ Go to the solution. 14.1. QUESTIONS Mock 2 - Question 44 Select the right definition for the facade layer. 1. builds a complex object using simple objects and using a step by step approach 2. separates application’s concerns 3. hides the complexities of the system and provides a simplified interface to the client 4. add new functionality to an existing object without altering its structure −→ Go to the solution. Mock 2 - Question 45 Select the existing Impex header mode. 1. INSERTED 2. UPDATE_INSERT 3. REMOVE 4. DELETE −→ Go to the solution. Mock 2 - Question 46 How could you configure a new hot folder ? 1. from the HAC 2. from the HMC 3. from the import cockpit 4. from a Spring config file −→ Go to the solution. Mock 2 - Question 47 How do you create a new interceptor ? 1. declare it along with the item type definition 2. provide an implementation of one of the Interceptor interface and create a bean from it. 3. run a system update 4. add a Spring bean mapping the item type and the Interceptor implementation −→ Go to the solution. 253 254 CHAPTER 14. MOCK EXAM 2 Mock 2 - Question 48 A checkout step has ? 1. checkoutGroup reference 2. checkoutStepValidator reference 3. transitions map 4. progressBarId string −→ Go to the solution. Mock 2 - Question 49 What are are valid legacy cockpit areas ? 1. navigation 2. item 3. browser 4. editor −→ Go to the solution. Mock 2 - Question 50 When running an initialization on one tenant SAP Hybris will ? 1. delete all tables 2. delete all known tables 3. delete all known tables starting with the tenant table prefix 4. delete and recreate the database −→ Go to the solution. Mock 2 - Question 51 When creating a new item how do you generate a new primary key ? 1. using java.util.UUID.randomUUID() 2. using the primaryKeyService 3. primary keys creation is automatically handled by the database 4. primary keys creation is automatically handled by the service layer −→ Go to the solution. 14.1. QUESTIONS Mock 2 - Question 52 How could you declare configure an attribute to be encrypted ? 1. <attribute qualifier="sensible" autocreate="true" type="java.lang.String"> <persistence type="property"/> <modifiers encrypted="true"/> </attribute> 2. <attribute qualifier="sensible" autocreate="true" ֒→ type="encrypted:java.lang.String"> <persistence type="property"/> </attribute> 3. <attribute qualifier="sensible" autocreate="true" type="java.lang.String"> <persistence type="property" encrypted="true"/> </attribute> 4. <attribute qualifier="sensible" autocreate="true" type="java.lang.String"> <modifiers type="property" encrypted="true"/> </attribute> −→ Go to the solution. Mock 2 - Question 53 Which item types exist ? 1. collectiontype 2. enumtype 3. atomictype 4. listtype −→ Go to the solution. Mock 2 - Question 54 A customer segment could be executed in ? 1. optimized processing 2. full processing 3. jalo processing 4. complete processing −→ Go to the solution. 255 256 CHAPTER 14. MOCK EXAM 2 Mock 2 - Question 55 Why a Spring MCV controller needs to return a String ? 1. to return it as an HTML page 2. to load a view 3. to load a servlet 4. to load a model −→ Go to the solution. Mock 2 - Question 56 What features are available through the Order Management Module (formally OMS) ? 1. order splitting 2. payment 3. sourcing 4. promotion engine −→ Go to the solution. Mock 2 - Question 57 Why do you need to define stop words for for your search configuration ? 1. to ignore configured words while searching 2. to boost configured words while searching 3. to stop the query after a configured word 4. to split the query in two queries −→ Go to the solution. Mock 2 - Question 58 What are the two main components of the rule engine ? 1. rule processor 2. rule matcher 3. rule executor 4. rule builder −→ Go to the solution. 14.1. QUESTIONS 257 Mock 2 - Question 59 How could you improve the following code (groovy) ? import de.hybris.platform.processengine.enums.ProcessState def businessProcessService = spring.getBean("businessProcessService") def modelService = spring.getBean("modelService") def process = businessProcessService.createProcess(UUID.randomUUID().toString(), ֒→ "myProcess") modelService.save(process) businessProcessService.startProcess(process) modelService.refresh(process) if(ProcessState.SUCCEEDED != process.getProcessState()){ businessProcessService.startProcess(process) } 1. use the method <T extends BusinessProcessModel> T startProcess(String arg0, String arg1); to create a process 2. delete the second call trying to start the business process 3. delete the useless call to save 4. generate a unique identifier for the business process −→ Go to the solution. Mock 2 - Question 60 What statements are true about the following table (SQL DDL for HSQL) ? CREATE CACHED TABLE b2bbudgetslp ( ITEMPK BIGINT, ITEMTYPEPK BIGINT, LANGPK BIGINT, p_name NVARCHAR(255), PRIMARY KEY (ITEMPK, LANGPK) ); 1. table prefix is b2b 2. the item b2bbudgets has only one attribute 3. contains localized attributes for b2bbudgets item 4. it has 4 primary keys −→ Go to the solution. 258 CHAPTER 14. MOCK EXAM 2 Mock 2 - Question 61 Why would you use classification ? 1. to optimize Apache SOLR requests 2. to have dynamic product features 3. to speed up search 4. to unify product and category attributes −→ Go to the solution. Mock 2 - Question 62 Which facet search configuration attributes give you the ability to redirect a user for a specific search request ? 1. stopwords 2. keywords 3. redirectwords 4. matchwords −→ Go to the solution. Mock 2 - Question 63 What statements are wrong about extensions ? 1. they have to be inside the bin folder 2. they need to have a dependency to yempty 3. they can written using Groovy 4. they are always automatically loaded −→ Go to the solution. Mock 2 - Question 64 What statements are true about the SAP Hybris server ? 1. based on Apache Tomcat 2. good for production 3. compatible with EJB 4. can be started from the command line −→ Go to the solution. 14.1. QUESTIONS 259 Mock 2 - Question 65 When you run an impex import query from the HAC with the default configuration what statements are true ? 1. you are running distributed impexes 2. you are using the service layer 3. you are using the jalo layer 4. import relaxed is activated −→ Go to the solution. Mock 2 - Question 66 Which of the following would be good uses for a SAP Hybris CronJob ? 1. synchronizing the stock level with an external warehouse once per day 2. notify the System Administrator when the peak load on the server passes a critical point 3. perform an inventory once per week at midnight 4. any task that isn’t database related, cronjobs are designed to perform operating System tasks, not database system tasks −→ Go to the solution. Mock 2 - Question 67 Content slots for page template are ? 1. place holder for CMS component 2. place holder for JSP tags 3. shared across all pages using the page template 4. configured on the JSP view page −→ Go to the solution. Mock 2 - Question 68 Which bean would be loaded from getBean("bean") ? <bean name="stringBean" class="java.lang.String" > <constructor-arg><value>my String Name</value></constructor-arg> </bean> <bean id="stringBean" class="java.lang.String" > <constructor-arg><value>my String ID</value></constructor-arg> </bean> <alias name="stringBean" alias="bean" /> 260 CHAPTER 14. MOCK EXAM 2 1. my String Name 2. my String ID 3. a new String 4. none −→ Go to the solution. Mock 2 - Question 69 Is it possible to add new enumeration values by runtime ? 1. no 2. yes if you are using JRebel 3. yes if the enumeration is dynamic 4. yes if the enumeration is a subtype of the HybrisEnumValue class −→ Go to the solution. Mock 2 - Question 70 What a POS is ? 1. a warehouse 2. a physical store 3. a web store 4. a geolocalization service −→ Go to the solution. Mock 2 - Question 71 What statements are wrong about Backoffice widgets ? 1. widget can extend each other 2. widget ids must be unique or the compilation fails 3. views are implemented using JSP tags 4. widgets can have parameters −→ Go to the solution. 14.1. QUESTIONS 261 Mock 2 - Question 72 When writing unit tests how should you simulate pojo dependencies to external services, facades... ? 1. the test class should extend HybrisUnitTests 2. the test class should extend ServicelayerTest 3. use Mockito to stub dependencies 4. wrap all tests inside a transaction −→ Go to the solution. Mock 2 - Question 73 What is the notification framework used for ? 1. notify cockpit users 2. notify customers 3. notify administrators 4. facade for the event service −→ Go to the solution. Mock 2 - Question 74 What statements are true about stock levels ? 1. the stock service can check stock levels across multiple warehouses 2. a single warehouse can contain multiple stock levels for the same product 3. a base store is attached to a single warehouse 4. a product can be sold even if it has no stock −→ Go to the solution. Mock 2 - Question 75 The CS cockpit was designed for ? 1. customer segment management 2. order life cycles management 3. css rendering management 4. customer security management −→ Go to the solution. 262 CHAPTER 14. MOCK EXAM 2 Mock 2 - Question 76 SAP Hybris semi-session failover mechanism allows to ? 1. automatically redirected users to the login page of a new node when on node goes down 2. to transfer all sessions of the failing node to other available nodes without loss of data 3. to always have all sessions replicated on every nodes 4. to automatically redirect users to a temporary page −→ Go to the solution. Mock 2 - Question 77 Choose the flexible query which has generated the following SQL query ? SELECT item_t0.PK, item_t1.p_code FROM products item_t0 LEFT JOIN medias item_t1 ON item_t1.PK = item_t0.p_logo WHERE ((item_t0.TypePkString=? AND (item_t1.TypePkString IS NULL OR ( item_t1.TypePkString=? ) ))) ֒→ 1. select prd.pk, md.code from Product as prd left join Media as md on md.pk = prd.logo 2. select prd.pk, md.code from Product! as prd join Media! as md on md.pk = prd 3. select prd.pk, md.code from Product! as prd left join Media! as md on md.pk = prd.logo 4. select md.code from Product as prd left join Media! as md on md.pk = prd.logo −→ Go to the solution. Mock 2 - Question 78 What service should you use to save an model ? 1. persistenceService 2. jaloService 3. serviceLayerService 4. modelService −→ Go to the solution. Mock 2 - Question 79 If you are testing a Flexible Search Restriction, the user you are running the query as ? 1. should be admin 2. should be flexiblesearchquery 3. should be a member of the searchquery group 4. should not be a member of the admin group −→ Go to the solution. 14.2. SOLUTIONS 263 Mock 2 - Question 80 With default configuration when you load a model item with the model service (for examples product model).What is automatically loaded inside the object ? 1. nothing, all attributes are lazy loaded 2. only literal values 3. only reference values 4. all −→ Go to the solution. Solutions Mock 2 - Solution 1 1 and 2 are correct. −→ Go back to the question. Mock 2 - Solution 2 1 and 3 are correct. −→ Go back to the question. Mock 2 - Solution 3 1 is correct. −→ Go back to the question. Mock 2 - Solution 4 3 is correct. −→ Go back to the question. Mock 2 - Solution 5 3 and 4 are correct. When you implement the interface ClusterAwareEvent you need to implement the method publish(int sourceNodeId, int targetNodeId), but you are free to decide whether your code should run on a very specific node (answer 3) or on all nodes (answer 4). In order to be able to broadcast and process it to all nodes you need to return true in the publish method like so: 264 CHAPTER 14. MOCK EXAM 2 package com.hyhub.core; import de.hybris.platform.servicelayer.event.ClusterAwareEvent; import de.hybris.platform.servicelayer.event.impl.AbstractEventListener; public class AnyEventListener extends AbstractEventListener<AnyEvent> implements ֒→ ClusterAwareEvent { @Override protected void onEvent(AnyEvent event) { // your logic } ֒→ ֒→ ֒→ /** * Decide weather this event should be broadcasted/received. Examples are * <ul> * <li><code>return (sourceNodeId==targetNodeId );</code> => will only publish to local cluster</li> * <li><code>return true;</code> => will publish to all nodes</li> * <li><code>return (targetNodeId==14);</code> => publish to dedicated node regardless of source</li> * </ul> * @return <code>true</code> if event should be published from source cluster node to target cluster node */ @Override public boolean publish(int sourceNodeId, int targetNodeId) { // publish event on all cluster nodes return true; } } −→ Go back to the question. Mock 2 - Solution 6 2 is correct. −→ Go back to the question. Mock 2 - Solution 7 4 is correct. −→ Go back to the question. Mock 2 - Solution 8 1,3,4 are correct. −→ Go back to the question. Mock 2 - Solution 9 2,3 are correct. −→ Go back to the question. Mock 2 - Solution 10 1,2,3 are correct. 14.2. SOLUTIONS 265 −→ Go back to the question. Mock 2 - Solution 11 1,2,4 are correct. −→ Go back to the question. Mock 2 - Solution 12 1 is correct. −→ Go back to the question. Mock 2 - Solution 13 1,2,3,4 are correct. −→ Go back to the question. Mock 2 - Solution 14 3 is correct. −→ Go back to the question. Mock 2 - Solution 15 4 is correct. generate="false" means that Jalo classes won’t be generated. −→ Go back to the question. Mock 2 - Solution 16 3 is correct. −→ Go back to the question. Mock 2 - Solution 17 2 is correct. −→ Go back to the question. Mock 2 - Solution 18 3 is correct. −→ Go back to the question. Mock 2 - Solution 19 4 is correct. The :o in the SELECT clause means that an OUTER JOIN is used in order to get the localizations. This would result in a ResultSet containing also items where the selected localization is null. −→ Go back to the question. Mock 2 - Solution 20 1 is correct. −→ Go back to the question. Mock 2 - Solution 21 1,2 are correct. −→ Go back to the question. Mock 2 - Solution 22 2 is correct. 266 CHAPTER 14. MOCK EXAM 2 −→ Go back to the question. Mock 2 - Solution 23 3 is correct. −→ Go back to the question. Mock 2 - Solution 24 2 is correct. −→ Go back to the question. Mock 2 - Solution 25 1 is correct. −→ Go back to the question. Mock 2 - Solution 26 3 is correct. The installer is not an ant target. extgen would generate a single extension while here we need to generate a whole new project. −→ Go back to the question. Mock 2 - Solution 27 2 is correct. Web contexts are contained within a core context (one per tenant), the two other contexts are not valid. −→ Go back to the question. Mock 2 - Solution 28 3 is correct. −→ Go back to the question. Mock 2 - Solution 29 1,2,3 are correct. −→ Go back to the question. Mock 2 - Solution 30 1 is correct. −→ Go back to the question. Mock 2 - Solution 31 2,3 are correct. −→ Go back to the question. Mock 2 - Solution 32 1,2,4 are correct. −→ Go back to the question. Mock 2 - Solution 33 3 is correct. −→ Go back to the question. Mock 2 - Solution 34 1,2 are correct. 14.2. SOLUTIONS −→ Go back to the question. Mock 2 - Solution 35 3 is correct. −→ Go back to the question. Mock 2 - Solution 36 1 is correct. −→ Go back to the question. Mock 2 - Solution 37 2,4 are correct. −→ Go back to the question. Mock 2 - Solution 38 4 is correct. −→ Go back to the question. Mock 2 - Solution 39 2 is correct. −→ Go back to the question. Mock 2 - Solution 40 1,3 is correct. −→ Go back to the question. Mock 2 - Solution 41 3 is correct. −→ Go back to the question. Mock 2 - Solution 42 2 is correct. −→ Go back to the question. Mock 2 - Solution 43 1 is correct. −→ Go back to the question. Mock 2 - Solution 44 3 is correct. −→ Go back to the question. Mock 2 - Solution 45 3 is correct. −→ Go back to the question. Mock 2 - Solution 46 4 is correct. −→ Go back to the question. Mock 2 - Solution 47 2,3 are correct. 267 268 −→ Go back to the question. Mock 2 - Solution 48 1,2,3,4 are correct. −→ Go back to the question. Mock 2 - Solution 49 1,3,4 are correct. −→ Go back to the question. Mock 2 - Solution 50 3 is correct. −→ Go back to the question. Mock 2 - Solution 51 4 is correct. −→ Go back to the question. Mock 2 - Solution 52 1 is correct. −→ Go back to the question. Mock 2 - Solution 53 1,2,3 are correct. −→ Go back to the question. Mock 2 - Solution 54 1,2 are correct. −→ Go back to the question. Mock 2 - Solution 55 2 is correct. −→ Go back to the question. Mock 2 - Solution 56 1,3 are correct. −→ Go back to the question. Mock 2 - Solution 57 1 is correct. −→ Go back to the question. Mock 2 - Solution 58 1,4 are correct. −→ Go back to the question. Mock 2 - Solution 59 1,2,3 are correct. −→ Go back to the question. Mock 2 - Solution 60 3 is correct. CHAPTER 14. MOCK EXAM 2 14.2. SOLUTIONS −→ Go back to the question. Mock 2 - Solution 61 2 is correct. −→ Go back to the question. Mock 2 - Solution 62 2 is correct. −→ Go back to the question. Mock 2 - Solution 63 3 is correct. Have a look at the extension template ygroovy. −→ Go back to the question. Mock 2 - Solution 64 1,2,4 are correct. −→ Go back to the question. Mock 2 - Solution 65 2 is correct. −→ Go back to the question. Mock 2 - Solution 66 1,3 are correct. −→ Go back to the question. Mock 2 - Solution 67 1,3 are correct. −→ Go back to the question. Mock 2 - Solution 68 4 is correct. −→ Go back to the question. Mock 2 - Solution 69 3,4 are correct. −→ Go back to the question. Mock 2 - Solution 70 2 is correct. −→ Go back to the question. Mock 2 - Solution 71 2 and 3 are correct. −→ Go back to the question. Mock 2 - Solution 72 3 is correct. −→ Go back to the question. Mock 2 - Solution 73 2 is correct. 269 270 CHAPTER 14. MOCK EXAM 2 −→ Go back to the question. Mock 2 - Solution 74 1,2,4 are correct. −→ Go back to the question. Mock 2 - Solution 75 2 is correct. −→ Go back to the question. Mock 2 - Solution 76 2 is correct. −→ Go back to the question. Mock 2 - Solution 77 3 is correct. −→ Go back to the question. Mock 2 - Solution 78 4 is correct. −→ Go back to the question. Mock 2 - Solution 79 4 is correct. −→ Go back to the question. Mock 2 - Solution 80 1 is correct. Look at the servicelayer.prefetch property inside the advanced.properties file inside the platform. There are four available modes : • none • literal • all • default (equal to none) −→ Go back to the question. Chapter 15 Mock exam 3 271 272 CHAPTER 15. MOCK EXAM 3 Questions Mock 3 - Question 1 Dynamic forms are used to ? 1. Automatically generates html forms from an Hybris model object 2. Executes dynamic actions on attributes, sections and tabs when a model property changes 3. Executes dynamic actions on dynamic attributes based on the spring actions attached to the model 4. Automatically saves input data when the user fills a form from the accelerator −→ Go to the solution. Mock 3 - Question 2 When you use advanced personalization what are the valid scopes for segment rule results? 1. Singleton 2. Session 3. Web 4. Permanent −→ Go to the solution. Mock 3 - Question 3 Choose the right definition for the modulegen ant task 1. Generate sets of dependent extensions to quickly start new projects or create new applications 2. Generate a single dependent extension to quickly start new projects or create new applications 3. Generate new extensions based on extension templates 4. Generate new empty extensions −→ Go to the solution. Mock 3 - Question 4 Which of the following are out of the box point of services ? 1. Factory 2. ServiceDesk 3. Store 4. Warehouse −→ Go to the solution. 15.1. QUESTIONS Mock 3 - Question 5 What attribute from the stock level item are mandatory? 1. A product model 2. A product code 3. A point of service 4. A warehouse −→ Go to the solution. Mock 3 - Question 6 What is the ant command used to generate a new backoffice extension ? 1. ant modulegen 2. ant ybackofficegen 3. ant extgen 4. ant extensiongen −→ Go to the solution. Mock 3 - Question 7 Which of the following language are available to write dynamic form scripts? 1. beanshell 2. groovy 3. spel 4. javascript −→ Go to the solution. Mock 3 - Question 8 What is the correct way to create a new cockpit ? 1. add a new line in the xml config file for cockpit (cockpits.xml) 2. create a new cockpit model from the hac (impex) or the hmc 3. generate a new extension from ycockpit 4. run ant cockpitgen −→ Go to the solution. 273 274 CHAPTER 15. MOCK EXAM 3 Mock 3 - Question 9 Inside the configuration folder you have a file called local_tenant_all.properties, what is it ? 1. a property file for all tenants 2. a property file for the all tenant 3. a property file for the master tenant 4. none of the above −→ Go to the solution. Mock 3 - Question 10 Why would you use a widget virtual socket rather than a widget regular socket 1. to add a socket at runtime 2. to connect more than one output or input 3. to to override an existing socket 4. none of the above −→ Go to the solution. Mock 3 - Question 11 What statements are true about the widget application context ? 1. it belongs to the CS cockpit 2. it offers auto-reload capabilities 3. it is a child of the web application context 4. none of the above −→ Go to the solution. Mock 3 - Question 12 Dynamic forms can perform actions on which of the following elements ? 1. spring bean 2. enumeration 3. backoffice orchestrator 4. none of the above −→ Go to the solution. 15.1. QUESTIONS Mock 3 - Question 13 What statements are true about a new extension created from the ycockpit template ? 1. it has only one perspective 2. it has no perspective 3. it creates a new context path based on the name of your extension 4. it creates an admin user to access your cockpit −→ Go to the solution. Mock 3 - Question 14 A cockpit perspective is composed of which of the following elements ? 1. a navigation area 2. a browser area 3. an editor area 4. a product area −→ Go to the solution. Mock 3 - Question 15 What statement(s) are true about cockpit wizards ? 1. it guides users through complex item creation 2. it is used by the admin user only 3. wizards can be configured with a ZK editor 4. wizards are part of the backoffice framework not the cockpit framework −→ Go to the solution. Mock 3 - Question 16 When implementing a new payment provider, what is the most important ? 1. requests should be done using SSL encryption 2. sensitive attribute should be encrypted 3. enforce PCI compliance rules 4. use HOP over SOP −→ Go to the solution. 275 276 CHAPTER 15. MOCK EXAM 3 Mock 3 - Question 17 What are the new features of Hybris V6 compared to V5? 1. backoffice 2. Yaas integration 3. rule engine 4. smart edit −→ Go to the solution. Mock 3 - Question 18 What makes a B2B implementation different than a B2C? 1. B2B needs to manage customer credit 2. B2C needs to integrate customers organization 3. B2B most likely will ask customer to login before they can access the catalog 4. B2B doesn’t integrate payment −→ Go to the solution. Mock 3 - Question 19 What is de the default password encryption? 1. none 2. md5 3. sha-512 4. pbkdf2 −→ Go to the solution. Mock 3 - Question 20 How can you create a new promotion rule? 1. from the backoffice by using a template 2. from the hmc using the appropriate wizard 3. from the backoffice from scratch 4. from ant using the promogen target −→ Go to the solution. 15.1. QUESTIONS 277 Mock 3 - Question 21 What are OCC v2 new features? 1. stateless 2. stateful 3. restful 4. xml or json response format −→ Go to the solution. Mock 3 - Question 22 In SAP Hybris 6.2 what is the recommended to have promotions 1. the promotion engine 2. the promotion module 3. the jalo layer 4. none of the above −→ Go to the solution. Mock 3 - Question 23 When 2 promotions (promotion engine) have the same priority how does SAP Hybris order them 1. alphabetically 2. can’t be determined 3. date of creation 4. uuid −→ Go to the solution. Mock 3 - Question 24 What statements are true about the rule compiler ? 1. when compiling it uses the IR data structure 2. it is used to transform promotion rules into rules 3. it turns source rules into executables 4. none of the above −→ Go to the solution. 278 CHAPTER 15. MOCK EXAM 3 Mock 3 - Question 25 How do you define a new RAO object ? 1. create new item type in a items.xml file 2. create new rao type in a items.xml file 3. create new bean in a beans.xml file 4. create new rao in a rao.xml file −→ Go to the solution. Mock 3 - Question 26 When creating a new condition translator what are the available conditions ? 1. RuleIrTrueCondition 2. RuleIrFalseCondition 3. RuleIrGroupCondition 4. RuleIrAttributeCondition −→ Go to the solution. Mock 3 - Question 27 Where should you manage the visibility restrictions of an existing CMS page ? 1. hac 2. hmc 3. CMS cockpit 4. none of the above −→ Go to the solution. Mock 3 - Question 28 What statements are true about flexible search restrictions ? 1. they only apply for anonymous users 2. they never apply for admingroup users 3. you cannot disable restrictions 4. they target one item type −→ Go to the solution. 15.1. QUESTIONS 279 Mock 3 - Question 29 Tenants let you choose a specific ? 1. set of extensions 2. time zone 3. currency 4. database −→ Go to the solution. Mock 3 - Question 30 Can you use SOLR to index something else than products ? 1. yes, if you have at least 2 solr servers 2. yes, if you have at least 2 tenants 3. yes, if you create a new index 4. no, it’s not possible −→ Go to the solution. Mock 3 - Question 31 To create a new CMS restriction type (for example loyal customers with at least 3 past orders) what do you need to do ? 1. create a new item type for your restriction extending AbstractRestriction 2. create an implementation of de.hybris.platform.cms2.servicelayer.services.evaluator.CMSRestrictionEvaluator with the restriction logic 3. create a new spring bean for my restriction evaluator 4. create a new spring bean to map my evaluator with my item type −→ Go to the solution. Mock 3 - Question 32 What could be improved with the following DAO ? package de.hybris.dao; import de.hybris.platform.core.model.ItemModel; import de.hybris.platform.core.model.product.ProductModel; import de.hybris.platform.servicelayer.internal.dao.DefaultGenericDao; import java.util.List; import java.util.Set; public class MyDao extends DefaultGenericDao<ItemModel> { 280 CHAPTER 15. MOCK EXAM 3 public MyDao(String typecode) { super(ProductModel._TYPECODE); } ֒→ public List<ProductModel> searchProduct(String code){ return this.getFlexibleSearchService().<ProductModel>search("select {pk} from {Product} where id=’"+code+"’").getResult(); } } 1. use args instead of concatenating the string 2. avoid unused import (java.util.set) 3. extends CachedDao<ProductModel> 4. use a direct SQL query −→ Go to the solution. Mock 3 - Question 33 How many item types can a single cache region handle ? 1. only one 2. as many as you need 3. cache regions only cache flexible search queries 4. none of the above −→ Go to the solution. Mock 3 - Question 34 Choose the best definition for the FIFO eviction strategy ? 1. objects are evicted based on their age, newest first 2. objects are evicted based on their io use, lowest first 3. objects are evicted based on their size, biggest first 4. none of the above −→ Go to the solution. Mock 3 - Question 35 What can you do with the Assised Service Module (ASM) ? 1. create a cart in behalf of a customer 2. create a new product from a responsive storefront 15.1. QUESTIONS 3. manage a customer account from my account section in behalf of a customer 4. assign an anonymous cart to a customer −→ Go to the solution. Mock 3 - Question 36 What is best approach to implement the following requirements ? From my custom backoffice perspective, I want the product description to be ֒→ filled with the product name automatically if empty. 1. create a new prepare interceptor 2. create a new dynamic form for the backoffice 3. customize the ProductModel constructor 4. create a cron job to scan products −→ Go to the solution. Mock 3 - Question 37 How does SAP Hybris redirect a URL is redirect to the correct store front ? 1. using different port 2. stores have URL patterns 3. websites have URL patterns 4. using different ips −→ Go to the solution. Mock 3 - Question 38 Where can you define a new socket for a widget ? 1. myextension-widgets.xml 2. myextension-backoffice.xml 3. mywidget/definition.xml 4. mywidget/config.xml −→ Go to the solution. Mock 3 - Question 39 What functionality should you use to periodically check for product out of stock ? 1. task service 2. cronjob service 3. stockCheck service 4. productSentinel service −→ Go to the solution. 281 282 CHAPTER 15. MOCK EXAM 3 Mock 3 - Question 40 Can you override a content slot defined in a page template when you are editing a page ? 1. yes, if the slot is empty 2. yes, if you are editing another page template extending the first one 3. yes 4. no, page template slots are meant to be shared across pages −→ Go to the solution. Mock 3 - Question 41 What statements are true about macrodefs ? 1. they are part of the build framework 2. they can add build custom logic logic before or after each build step 3. they can be defined in the extensioninfo.xml 4. none of the above −→ Go to the solution. Mock 3 - Question 42 What version(s) of OCC is activated by default ? 1. V1 2. V2 3. V1 and V2 4. none −→ Go to the solution. Mock 3 - Question 43 When call OCC API, request filters are executed before or after the controllers ? 1. always before 2. always after 3. controllers are responsible for running filters 4. none of the above −→ Go to the solution. 15.1. QUESTIONS Mock 3 - Question 44 What statements are true about the following relation definition ? <relation code="Product2KeywordRelation" autocreate="true" generate="true" ֒→ localized="true"> <deployment table="Prod2KeywordRel" typecode="604"/> <sourceElement qualifier="keywords" type="Keyword" cardinality="many" ֒→ ordered="true" collectiontype="list"> <description>Keywords</description> <modifiers read="true" write="true" search="true" ֒→ optional="true"/> </sourceElement> <targetElement qualifier="products" type="Product" cardinality="many" ֒→ ordered="false"> <description>Products</description> <modifiers read="true" write="true" search="true" ֒→ optional="true"/> </targetElement> </relation> 1. defines a new item type Product2KeywordRelation 2. will create a new database table Prod2KeywordRel 3. a relation can’t be localized 4. a new attribute products will be visible from the Keyword item type −→ Go to the solution. Mock 3 - Question 45 Choose the right definition for a converter ? 1. create new instance of model objects and call populators to populate them 2. create new instance of data objects and call populators to populate them 3. populate a data object attributes from a model object 4. populate a model object attributes from a data object −→ Go to the solution. Mock 3 - Question 46 Is this a correct flexible search query ? delete from {Product} where {code} like ’prd123’ 1. no, because it’s missing the semi colon 2. no, because you can’t use like in a where clause 3. no, because flexible search query can’t execute delete statements 4. no, because you need the primary key to delete an item −→ Go to the solution. 283 284 CHAPTER 15. MOCK EXAM 3 Mock 3 - Question 47 If one item attribute contains sensitive data what should you do ? 1. use password encrypted attribute 2. use xss encoder 3. use secured attribute protection 4. use transparent attribute encryption −→ Go to the solution. Mock 3 - Question 48 In a multi tenant system how could you be sure that a Spring bean is unique across different tenants ? 1. use the tenant scope 2. use the default singleton scope 3. use the prototype scope 4. none of the above −→ Go to the solution. Mock 3 - Question 49 facets could be of which type ? 1. MultiSelectAnd 2. MultiSelectOr 3. Refine 4. Define −→ Go to the solution. Mock 3 - Question 50 Which of the following item types are valid B2B item types ? 1. B2BUnit 2. B2BBudget 3. B2BCostCenter 4. B2BCustomer −→ Go to the solution. 15.1. QUESTIONS Mock 3 - Question 51 Give the correct definition of cross site scripting ? 1. malicious code is sent to and executed by end users’ web browsers 2. malicious code is sent to and executed by SAP Hybris 3. malicious code is sent to and executed by the database 4. malicious code is sent to and executed by the JSP page −→ Go to the solution. Mock 3 - Question 52 How can you install an SAP license for SAP Hybris ? 1. ant -install /home/hybris/license.txt 2. ant licenceInstall /home/hybris/license.txt 3. ./license.sh -install /home/hybris/license.txt 4. ./licenseInstall.sh /home/hybris/license.txt −→ Go to the solution. Mock 3 - Question 53 Why would you run a dependent synchronization ? 1. your catalogs extend each other 2. you want to avoid cross references between catalog versions 3. you want to have cross references between catalog versions 4. none of the above −→ Go to the solution. Mock 3 - Question 54 What happen when you update SAP Hybris ? 1. it updates items type code according to items.xml files 2. it imports all impex files 3. it drops database tables if item type are not defined anymore 4. none of the above −→ Go to the solution. 285 286 CHAPTER 15. MOCK EXAM 3 Mock 3 - Question 55 After adding a new attribute into the product item type definition, what is needed to forward this attribute into the SOLR index ? 1. nothing the build process would generate a value resolver based on the attribute type and map it with SOLR 2. you must create a new value resolver 3. you must create a new SolrIndexedProperty item 4. none of the above −→ Go to the solution. Mock 3 - Question 56 Which of the following are available accelerators within SAP Hybris 6.2 ? 1. China accelerator 2. B2B accelerator 3. Telco accelerator 4. Travel accelerator −→ Go to the solution. Mock 3 - Question 57 What is the best way to start a new project from an accelerator ? 1. ant extgen nameOfTheAccelerator 2. ant modulegen nameOfTheAccelerator 3. ./install.sh -i nameOfTheAccelerator 4. ./install.sh -p nameOfTheAccelerator −→ Go to the solution. Mock 3 - Question 58 What steps are required to create a new wizard for a legacy cockpit ? 1. defines a new Spring bean for the wizard de.hybris.platform.cockpit.wizards.Wizard 2. defines a new Spring bean for the wizard configuration de.hybris.platform.cockpit.wizards.generic.CreateWizard 3. create zul files for each wizard’s pages 4. defines a custom WizardPageController −→ Go to the solution. 15.1. QUESTIONS 287 Mock 3 - Question 59 How should you implement the following requirement ? We need all products within our SAP Hybris system to carry a new uid generated ֒→ from an external system integrated with our platform. 1. use a classification attribute 2. use a categorization attribute 3. use a product variant attribute 4. none of the above −→ Go to the solution. Mock 3 - Question 60 How should you implement the following requirement ? We need to have a different product catalog for each season. Most important we ֒→ need to be able to work on any catalog at any moment even if they are not ֒→ online. 1. create 4 different product catalogs 2. create 4 different catalog versions 3. create 5 different catalog versions 4. create 4 different stores −→ Go to the solution. Mock 3 - Question 61 How should you install a the captcha addon into your store front extension ? 1. ant addoninstall -Daddonnames="captchaaddon" -DaddonStorefront.myStoreFront="myStoreFront" 2. ant addoninstall -Daddonnames="captchaaddon" myStoreFront 3. add <requires-extension name="captchaaddon"/> into your store front extensioninfo.xml 4. add myStoreFront.additionalWebSpringConfigs.captchaaddon=classpath :/captchaaddon/web/spring/captchaaddon-web-spring.xml into your property file −→ Go to the solution. Mock 3 - Question 62 Choose the appropriate term defined by ? User interface where you can create and maintain rules and rule templates 1. Rule Engine 2. Rule Cockpit 288 CHAPTER 15. MOCK EXAM 3 3. Rule Design 4. Rule Builder −→ Go to the solution. Mock 3 - Question 63 How could SAP Hybris decide to create multiple consignments for the same order ? 1. if you have implemented a custom business process for order fulfillment 2. if you have matching order splitting strategy 3. if you use the B2B accelerator 4. whenever size of a product is too big for one shipment −→ Go to the solution. Mock 3 - Question 64 Is this a valid impex file ? INSERT_UPDATE Employee;UID;name;groups(uid);description;password ;CustomerSupportAdministrator;Customer Support ֒→ Administrator;customersupportadministratorgroup;This generic Customer Support ֒→ Administrator.;1234 1. yes 2. no 3. yes, if you use the legacy mode 4. yes, if you add a password encryption [password=md5] −→ Go to the solution. Mock 3 - Question 65 How can you be sure your property file are compatible with every existing characters (French, Chinese, Vietnamese, Hindi...) ? 1. each language should have a different encoding (UTF-FR for French, ISO-HINDI for Indian,...) 2. enforce encoding all your property files in ASCII 3. enforce encoding all your property files in UTF-1 4. none of the above −→ Go to the solution. 15.1. QUESTIONS 289 Mock 3 - Question 66 What is the correct definition of the service layer ? 1. interconnects data objects with the client (api, controllers...) and hold logic to perform business processes 2. interconnects external service with SAP Hybris hold logic to perform business processes 3. interconnects model objects with the client (api, controllers...) hold logic to perform business processes 4. interconnects model objects with the the dao layer −→ Go to the solution. Mock 3 - Question 67 What media storage strategy does SAP Hybris 6.2 offers out of the box ? 1. nfs media storage strategy 2. Amazon S3 media storage strategy 3. Google Cloud media storage strategy 4. Windows Azure Blob media storage strategy −→ Go to the solution. Mock 3 - Question 68 When creating a new cms component item type what item type should yours extend from ? 1. CMSItem 2. SimpleCMSComponent 3. AbstractCMSComponent 4. CMSComponentType −→ Go to the solution. Mock 3 - Question 69 How to you access the backoffice orchestrator ? 1. Login as an admin into the backoffice and open the orchestrator perspective 2. Login as an admin into the hmc and open the orchestrator perspective 3. Login as an admin into the hac and open the orchestrator tab 4. none of the above −→ Go to the solution. 290 CHAPTER 15. MOCK EXAM 3 Mock 3 - Question 70 In a widget zul file what slot(s) can you use to embed another widget ? 1. <widget .../> 2. <widgetslot .../> 3. <widgetchild .../> 4. <widgetchildren .../> −→ Go to the solution. Mock 3 - Question 71 How does SAP Hybris provides to secure the platform ? 1. attribute encryption 2. xss protection 3. access rights 4. password encoding −→ Go to the solution. Mock 3 - Question 72 How does SOLR handle localized attributes ? 1. it creates a copy of the each product document for each language 2. it creates a map for each localized attribute (the key being the language code) 3. it creates one attribute for each language (name ends with _en, _de...) 4. it doesn’t store localized attributes −→ Go to the solution. Mock 3 - Question 73 When connecting two widgets through a socket, what statements are true ? 1. the data type must be serializable 2. output and input must be compatible (String->String, Integer->Number) 3. output and input must be of the same class 4. you can’t mix virtual and non virtual socket −→ Go to the solution. 15.1. QUESTIONS Mock 3 - Question 74 What do you need to provide to create a search restriction ? 1. targeted attribute(s) 2. targeted catalog(s) 3. targeted user or user group 4. targeted tenant −→ Go to the solution. Mock 3 - Question 75 Which of the following are price row attributes ? 1. currency 2. price group 3. user price group 4. minimum quantity −→ Go to the solution. Mock 3 - Question 76 Which of the following are valid workflow action statuses ? 1. new 2. pending 3. deleted 4. disabled −→ Go to the solution. Mock 3 - Question 77 What is the correct definition of a B2B approver ? 1. to provide customer approval for an order 2. to view the reports of the organization’s expenditure 3. to be able to administer the organization structure 4. to be able to place order −→ Go to the solution. 291 292 CHAPTER 15. MOCK EXAM 3 Mock 3 - Question 78 Is it possible to abort a cron job ? 1. yes, if your job performable extends AbortJobPerformable 2. yes, if isAbortable() returns true 3. yes, if you implement the abortable logic within the PerformResult method of your job 4. none of the above −→ Go to the solution. Mock 3 - Question 79 Which of the following user / user group can’t be deleted ? 1. anonymous 2. cockpitgroup 3. backofficegroup 4. anonymousgroup −→ Go to the solution. Mock 3 - Question 80 When writing a new integration test class, what already existing class should you extend to assure isolation ? 1. ServiceLayerIsolatedTest 2. ServiceLayerTest 3. ServiceLayerRollbackTransactionalTest 4. ServiceLayerTransactionalTest Solutions Mock 3 - Solution 1 2 are correct. Dynamic forms are used to add logic within a wizard or editor area. −→ Go back to the question. Mock 3 - Solution 2 2 and 4 are correct. −→ Go back to the question. Mock 3 - Solution 3 1 is correct. 15.2. SOLUTIONS −→ Go back to the question. Mock 3 - Solution 4 3 and 4 are correct. −→ Go back to the question. Mock 3 - Solution 5 2 and 4 are correct. −→ Go back to the question. Mock 3 - Solution 6 3 is correct. −→ Go back to the question. Mock 3 - Solution 7 1,2,3 and 4 are correct. −→ Go back to the question. Mock 3 - Solution 8 3 is correct. −→ Go back to the question. Mock 3 - Solution 9 2 is correct. −→ Go back to the question. Mock 3 - Solution 10 1 is correct. −→ Go back to the question. Mock 3 - Solution 11 2 and 3 are correct. −→ Go back to the question. Mock 3 - Solution 12 4 is correct. −→ Go back to the question. Mock 3 - Solution 13 1 and 3 are correct. −→ Go back to the question. Mock 3 - Solution 14 1, 2 and 3 are correct. −→ Go back to the question. Mock 3 - Solution 15 1 is correct. −→ Go back to the question. Mock 3 - Solution 16 3 is correct. 293 294 −→ Go back to the question. Mock 3 - Solution 17 1,2,3 and 4 are correct. −→ Go back to the question. Mock 3 - Solution 18 1 and 3 are correct. −→ Go back to the question. Mock 3 - Solution 19 4 is correct. −→ Go back to the question. Mock 3 - Solution 20 1 and 3 are correct. −→ Go back to the question. Mock 3 - Solution 21 1 and 3 are correct. −→ Go back to the question. Mock 3 - Solution 22 1 is correct. −→ Go back to the question. Mock 3 - Solution 23 2 is correct. −→ Go back to the question. Mock 3 - Solution 24 1 and 3 are correct. −→ Go back to the question. Mock 3 - Solution 25 3 is correct. −→ Go back to the question. Mock 3 - Solution 26 1,2,3 and 4 are correct. −→ Go back to the question. Mock 3 - Solution 27 3 is correct. −→ Go back to the question. Mock 3 - Solution 28 2 and 4 are correct. −→ Go back to the question. Mock 3 - Solution 29 1,2,3 and 4 are correct. CHAPTER 15. MOCK EXAM 3 15.2. SOLUTIONS −→ Go back to the question. Mock 3 - Solution 30 3 is correct. −→ Go back to the question. Mock 3 - Solution 31 1,2,3 and 4 are correct. −→ Go back to the question. Mock 3 - Solution 32 1,2 and 3 are correct. −→ Go back to the question. Mock 3 - Solution 33 3 is correct. −→ Go back to the question. Mock 3 - Solution 34 4 is correct. −→ Go back to the question. Mock 3 - Solution 35 1,3 and 4 are correct. −→ Go back to the question. Mock 3 - Solution 36 2 is correct. −→ Go back to the question. Mock 3 - Solution 37 2 is correct. −→ Go back to the question. Mock 3 - Solution 38 1 and 3 are correct. −→ Go back to the question. Mock 3 - Solution 39 2 is correct. −→ Go back to the question. Mock 3 - Solution 40 3 is correct. −→ Go back to the question. Mock 3 - Solution 41 1 and 2 are correct. −→ Go back to the question. Mock 3 - Solution 42 2 is correct. 295 296 CHAPTER 15. MOCK EXAM 3 −→ Go back to the question. Mock 3 - Solution 43 1 is correct. −→ Go back to the question. Mock 3 - Solution 44 2 and 4 are correct. −→ Go back to the question. Mock 3 - Solution 45 2 is correct. −→ Go back to the question. Mock 3 - Solution 46 3 is correct. −→ Go back to the question. Mock 3 - Solution 47 4 is correct. −→ Go back to the question. Mock 3 - Solution 48 4 is correct. Each tenant has its own application context and therefore beans are unique across different tenants. −→ Go back to the question. Mock 3 - Solution 49 1,2 and 3 are correct. −→ Go back to the question. Mock 3 - Solution 50 1,2,3 and 4 are correct. −→ Go back to the question. Mock 3 - Solution 51 1 is correct. −→ Go back to the question. Mock 3 - Solution 52 3 is correct. −→ Go back to the question. Mock 3 - Solution 53 2 is correct. −→ Go back to the question. Mock 3 - Solution 54 4 is correct. −→ Go back to the question. Mock 3 - Solution 55 3 is correct. 15.2. SOLUTIONS 297 −→ Go back to the question. Mock 3 - Solution 56 1,2,3 and 4 are correct. −→ Go back to the question. Mock 3 - Solution 57 2 is correct. −→ Go back to the question. Mock 3 - Solution 58 1,2 and 3 are correct. 4 is wrong because it’s not required to define a custom WizardPageController, and you can choose to use the default de.hybris.platform.cockpit.wizards.impl.DefaultPageController −→ Go back to the question. Mock 3 - Solution 59 4 is correct. 4 is correct because in this case you should add a new attribute within the Product item type itself. −→ Go back to the question. Mock 3 - Solution 60 3 is correct. One catalog version for each season plus one Online version. −→ Go back to the question. Mock 3 - Solution 61 1 is correct. −→ Go back to the question. Mock 3 - Solution 62 4 is correct. −→ Go back to the question. Mock 3 - Solution 63 2 is correct. −→ Go back to the question. Mock 3 - Solution 64 2 is correct. When running update you need to have [unique=true] on at least one attribute header. −→ Go back to the question. Mock 3 - Solution 65 4 is correct. By default SAP Hybris use UTF-8 for property files, make sure that every people involve in translating content use UTF-8. −→ Go back to the question. Mock 3 - Solution 66 3 is correct. 298 CHAPTER 15. MOCK EXAM 3 −→ Go back to the question. Mock 3 - Solution 67 2 and 4 are correct. −→ Go back to the question. Mock 3 - Solution 68 2 and 3 are correct. −→ Go back to the question. Mock 3 - Solution 69 4 is correct. You 1 is partially correct, there is no orchestrator perspective instead you simply need to to press F4. −→ Go back to the question. Mock 3 - Solution 70 2 and 4 are correct. −→ Go back to the question. Mock 3 - Solution 71 1,2,3 and 4 are correct. −→ Go back to the question. Mock 3 - Solution 72 3 is correct. −→ Go back to the question. Mock 3 - Solution 73 1 and 2 are correct. −→ Go back to the question. Mock 3 - Solution 74 3 is correct. −→ Go back to the question. Mock 3 - Solution 75 1,2,3 and 4 are correct. −→ Go back to the question. Mock 3 - Solution 76 2 and 4 are correct. −→ Go back to the question. Mock 3 - Solution 77 1 is correct. −→ Go back to the question. Mock 3 - Solution 78 4 is correct. In order for your cronjob to be abortable you would need 2 and 3. You need the job to be aware of the fact that it can abort itself, and you need to implement the logic to abort it, something like : 15.2. SOLUTIONS if (clearAbortRequestedIfNeeded(cronJob)) { return new PerformResult(CronJobResult.ERROR, CronJobStatus.ABORTED); } −→ Go back to the question. Mock 3 - Solution 79 1 is correct. −→ Go back to the question. Mock 3 - Solution 80 4 is correct. 299 300 CHAPTER 15. MOCK EXAM 3 Alphabetical Index OAuth 2, 56 OCC, 53 301