dynamically generate a crud admin panel with java annotations

56
Discussion document – Strictly Confidential & Proprietary Dynamically Generate a CRUD Admin Panel with Java Annotations

Upload: broadleaf-commerce

Post on 26-Jul-2015

372 views

Category:

Software


0 download

TRANSCRIPT

Page 1: Dynamically Generate a CRUD Admin Panel with Java Annotations

Discussion document – Strictly Confidential & Proprietary

Dynamically Generate a

CRUD Admin Panel with

Java Annotations

Page 2: Dynamically Generate a CRUD Admin Panel with Java Annotations

2

About me

• https://github.com/phillipuniverse, @phillipuniverse, Phillip Verheyden

• Architect at Broadleaf Commerce

• I like playground equipment and one time I saw Colbert

Page 3: Dynamically Generate a CRUD Admin Panel with Java Annotations

3

Customizing the Broadleaf Admin …

Overview

Annotations

– Basics

– Supported Field Types

– Broadleaf Enumerations

– Lookup Fields

– Collection Types

– Help, Tooltips

– Validation Support

– Overriding Annotations

Other Topics

• Security Model

• Persistence API View Layer

Page 4: Dynamically Generate a CRUD Admin Panel with Java Annotations

4

Broadleaf Open Admin

• Why?

– Broadleaf Commerce

broadleaf-admin-module vs broadleaf-open-admin-platform

– Extensible and generic at every level (frontend, backend, controller)

• Open Admin frontend history

– Open Admin v1 – Adobe Flex (~2010)

– Open Admin v2 – GWT (~2011)

– Open Admin v3 (current) – Spring MVC + Thymeleaf (~2013)

Page 5: Dynamically Generate a CRUD Admin Panel with Java Annotations

5

Admin Customizations … Overview …

Open Admin Benefits

• Quickly build and modify CRUD screens using metadata

• Rich, extensible security model

• Easy customizations are easy

– Hide / show fields

– Change labels, field ordering, and grouping

– Adding new managed fields and managed entities

– Add new actions, menu items, validations

Page 6: Dynamically Generate a CRUD Admin Panel with Java Annotations

6

Admin Annotation Basics

Page 7: Dynamically Generate a CRUD Admin Panel with Java Annotations

7

Admin Customizations … Annotation Basics …

Let’s start by looking at some basic annotations …

@Column(name = "FIRST_NAME")protected String firstName;

CustomerImpl.java

No annotations on firstName field …

Results in no input field on the customer form.

Page 8: Dynamically Generate a CRUD Admin Panel with Java Annotations

8

Admin Customizations … Annotation Basics …

Next, let’s add in an empty “AdminPresentation” annotation …

@Column(name = "FIRST_NAME")@AdminPresentation()protected String firstName;

CustomerImpl.java

Added @AdminPresentation annotation

Field was added to the form using the property

name in the default “group” on the default

“tab”

Page 9: Dynamically Generate a CRUD Admin Panel with Java Annotations

9

Admin Customizations … Annotation Basics …

Add a “friendlyName” to fix the label …

@Column(name = "FIRST_NAME")@AdminPresentation(friendlyName = “First Name”) protected String firstName;

CustomerImpl.java

Added friendlyName …

Label is now “First Name”

Note:

Broadleaf attempts to resolve the friendlyName from a messages file to allow for i18n labels.

Page 10: Dynamically Generate a CRUD Admin Panel with Java Annotations

10

Admin Customizations … Annotation Basics …

Finally, let’s position the field just before the Last Name field on the form …

@Column(name = "FIRST_NAME")@AdminPresentation( friendlyName = “First Name”, order = 2000, group = “Customer”) protected String firstName;

CustomerImpl.java

That’s what we want!

Why 2,000 for the order?

We looked at the emailAddress and lastName properties in CustomerImpl.java whose orders were set to 1,000 and 3,000 and chose a number in between the two.

We could have used 1,001 or 2,999.

Added “order” and “group”

Page 11: Dynamically Generate a CRUD Admin Panel with Java Annotations

11

Admin Customizations … Annotation Basics …

You can also annotate fields to show up on the Admin list grids …

List Grid Before

And After …

@Column(name = "FIRST_NAME")@AdminPresentation( friendlyName = “First Name”, prominent = true, gridOrder = “2000”) protected String firstName;

CustomerImpl.java

“prominent=true” means show on list grids

Page 12: Dynamically Generate a CRUD Admin Panel with Java Annotations

12

Supported Field Types

Page 13: Dynamically Generate a CRUD Admin Panel with Java Annotations

13

Admin Customizations … Supported Field Types …

The admin has support for common field types …

Related Entity LookupsMoney Fields

Radio Selectors Drop Down Selectors

Date Fields

Media LookupsBoolean Fields

Page 14: Dynamically Generate a CRUD Admin Panel with Java Annotations

14

Admin Customizations … Supported Field Types …

Supported Field Types (cont.)

• For simple types, the supported field type is derived from the property type (String, Integer, Date, etc.)

• Other field types require configuration and additional annotations. We’ll cover some of those on the upcoming slides …

• For a complete list of supported field types, see SupportedFieldType.java

Page 15: Dynamically Generate a CRUD Admin Panel with Java Annotations

15

Broadleaf Enumerations

Page 16: Dynamically Generate a CRUD Admin Panel with Java Annotations

16

public class OfferDiscountType implements BroadleafEnumerationType {

private static final Map<String, OfferDiscountType> TYPES = new LinkedHashMap<String, OfferDiscountType>();

public static final OfferDiscountType PERCENT_OFF = new OfferDiscountType("PERCENT_OFF", "Percent Off"); public static final OfferDiscountType AMOUNT_OFF = new OfferDiscountType("AMOUNT_OFF", "Amount Off"); public static final OfferDiscountType FIX_PRICE = new OfferDiscountType("FIX_PRICE", "Fixed Price");

public static OfferDiscountType getInstance(final String type) { return TYPES.get(type); }

Admin Customizations … Broadleaf Enumerations …

Broadleaf provides support for extensible enumerations A Broadleaf Enumeration

– Is used for many of the radio and drop-down selection lists in the admin

– Allows the framework to provide enum like functionality in a way that can be extended by custom implementations

Example

Page 17: Dynamically Generate a CRUD Admin Panel with Java Annotations

17

Admin Customizations … Broadleaf Enumerations …

You can use an enumeration for String types

@Column(name = "OFFER_DISCOUNT_TYPE")@AdminPresentation( friendlyName = ”Discount Type”, fieldType=SupportedFieldType.BROADLEAF_ENUMERATION, broadleafEnumeration=”org...OfferDiscountType")protected String type;

public OfferDiscountType getDiscountType() { return OfferDiscountType.getInstance(type);}

public void setDiscountType(OfferDiscountType type) { this.type = type.getType();}

Example from OfferImpl.java

Getters and setters return the enumeration type

Specify the enumeration type and the class

Produces This …

By default, if less than 5 options, a radio is displayed. Otherwise the system shows a dropdown selector.

Page 18: Dynamically Generate a CRUD Admin Panel with Java Annotations

18

Admin Customizations … Broadleaf Enumerations …

Broadleaf also provides support for “data-driven” enumerations• Allow selection values in the admin to come from a database table

• Allows system to add new values without a deployment

• Used with the @AdminPresentationDataDrivenEnumeration annotation

• Values are stored in BLC_DATA_DRVN_ENUM and BLC_DATA_DRVN_ENUM_VALUE tables

@Column(name = ”TAX_CODE")@AdminPresentation(friendlyName = ”Tax Code”)@AdminPresentationDataDrivenEnumeration( optionFilterParams = { @OptionFilterParam( param = "type.key", value = "TAX_CODE", paramType = OptionFilterParamType.STRING) })

Example from SkuImpl.java

Page 19: Dynamically Generate a CRUD Admin Panel with Java Annotations

19

Lookup Fields

Page 20: Dynamically Generate a CRUD Admin Panel with Java Annotations

Admin Customizations … Lookup Fields …

Adding a lookup to a JPA ManyToOne related fields can be done with a simple annotation

@ManyToOne(targetEntity=CategoryImpl.class)Column(name = ”DEFAULT_CATEGORY_ID”)@AdminPresentation(friendlyName=‘Default Category’)@AdminPresentationToOneLookup() protected Category defaultCategory;

ProductImpl.java

Products have a category lookup to set the default category.

Produces This Field … Click lookup

Click here to show a popup with a read view of the entity detail

Page 21: Dynamically Generate a CRUD Admin Panel with Java Annotations

21

Admin Customizations … Lookup Fields …

Lookup fields can also show up on list grids …

When a lookup field (like defaultCategory) is marked as “prominent”, additional features surface …

List grids can be filtered by the corresponding related entity.

Page 22: Dynamically Generate a CRUD Admin Panel with Java Annotations

22

Collections

Page 23: Dynamically Generate a CRUD Admin Panel with Java Annotations

23

Admin Customizations … Collections …

The admin supports a wide variety of grid interactions with annotations …

Most @OneToMany and @ManyToMany JPA relationships can be modeled as functional list grids in the admin with annotations …

Examples (we’ll cover each of these)

• Add items to a list

• Create items and then add to a list

• Add items to a “Map” collection where a key must also be provided

• Add items to a list with additional mapping attributes

Page 24: Dynamically Generate a CRUD Admin Panel with Java Annotations

24

Admin Customizations … Collections … Product Options …

Adding product options to a product, shows an example of choosing from a list of existing items

@ManyToMany(targetEntity = ProductOptionImpl.class)@JoinTable(name=“BLC_PRODUCT_OPTION_XREF …) @AdminPresentationCollection( friendlyName = ”Product Options", manyToField = "products”, addType = AddMethodType.LOOKUP, operationTypes = @AdminPresentationOperationTypes( removeType = OperationType.NONDESTRUCTIVEREMOVE))protected List<ProductOption> productOptions;

ProductImpl.java

Indicates that we will be looking up existing values instead of creating new ones

If the option is deleted from the product, it will not also attempt to delete the option

HitAdd

Page 25: Dynamically Generate a CRUD Admin Panel with Java Annotations

25

Admin Customizations … Collections … Product Options …

When adding offer codes to an offer, we want to create the code first and then add it …

@OneToMany(targetEntity = OfferCodeImpl.class)@AdminPresentationCollection( friendlyName = ”Offer Codes”, addType = AddMethodType.PERSIST)protected List<OfferCode> offerCodes;

OfferImpl.java

Indicates that we will be creating new “Offer Codes”

HitAdd

Page 26: Dynamically Generate a CRUD Admin Panel with Java Annotations

26

Admin Customizations … Collections … Product Attributes …

For Map collections like Product Attributes, we need to also provide a key when adding the item …

@ManyToMany(targetEntity = ProductAttributeImpl.class)@MapKey(name=“name”)@AdminPresentationMap( friendlyName = ”Product Attributes”, deleteEntityOnRemove = true, forceFreeFormKeys = true, keyPropertyFriendlyName = “Key” addType = AddMethodType.PERSIST)protected Map<String, ProductAttribute>;

ProductImpl.java

Map properties introduce a few new properties to control delete behavior and how keys are managed.

HitAdd

Page 27: Dynamically Generate a CRUD Admin Panel with Java Annotations

27

Admin Customizations … Collections … Category Media Map …

The Map used for Category Media uses a bit more of the “Map” functionality …

@ManyToMany(targetEntity = MediaImpl.class)@JoinTable(…)@MapKeyColumn(name=“MAP_KEY”)@AdminPresentationMap( friendlyName = ”Media”, deleteEntityOnRemove = true, keyPropertyFriendlyName = “Key” , mediaField = “url”, keys = { @AdminPresentationMapKey( keyName = “primary”), @AdminPresentationMapKey( keyName = “alt1”), ... })protected Map<String, Media> categoryMedia;

CategorytImpl.javaCategory media, shows an example of explicitly defined Map keys.

Media fields

Key

Page 28: Dynamically Generate a CRUD Admin Panel with Java Annotations

28

Admin Customizations … Collections … Adorned Collections …

Some collections need additional properties as part of the join … referred to as “adorned” collections

@OneToMany(targetEntity = FeaturedProductImpl.class)@AdminPresentationAdornedTargetCollection ( friendlyName = ”Featured Products”, targetObjectProperty = product, maintainedAdornedTargetFields = { “promotionMessage”})protected List<FeaturedProduct> featuredProducts;

CategoryImpl.java

To add a featured product, we are looking up the product and providing values to additional fields on the FeaturedProduct class.

HitAdd

SelectProduct

Page 29: Dynamically Generate a CRUD Admin Panel with Java Annotations

29

Help and Tooltips

Page 30: Dynamically Generate a CRUD Admin Panel with Java Annotations

30

Admin Customizations … Help and Tooltips …

You can add contextual help to fields in the admin …

@Column(name = "FIRST_NAME")@AdminPresentation( friendlyName = “First Name”, helpText = "This is help text", hint = "This is a hint.", tooltip = "This is a tooltip.”)protected String firstName;

CustomerImpl.java

Page 31: Dynamically Generate a CRUD Admin Panel with Java Annotations

31

Validation

Page 32: Dynamically Generate a CRUD Admin Panel with Java Annotations

32

Admin Customizations … Validation …

There are several approaches to adding validation to fields managed in the admin …

Via @ValidationConfiguration Annotations

Broadleaf provides an admin annotation to add validations along with several out-of-box implementations

Using JSR 303 Style Validations

Broadleaf can leverage Spring MVC JSR-303 validations

By adding validation logic in a Custom Persistence Handler

More on Custom Persistence Handlers later

Page 33: Dynamically Generate a CRUD Admin Panel with Java Annotations

33

Admin Customizations … Validation … Required Fields …

A field can be marked as required via annotation

@Column(name = "FIRST_NAME")

@AdminPresentation(requiredOverride = RequiredOverride.REQUIRED)

protected String firstName;

CustomerImpl.java

ResultRequired fields are noted in the admin with an asterisk.

Normally, required or not-required is derived based on the database column (e.g. non-null = required).

You can override this as shown.

Page 34: Dynamically Generate a CRUD Admin Panel with Java Annotations

34

Admin Customizations … Validation … Example Annotation …

Example : Add a RegEx validator to customer name

@Column(name = "FIRST_NAME")@AdminPresentation( validationConfigurations = { @ValidationConfiguration( validationImplementation=“blRegExPropertyValidator”, configurationItems={ @ConfigurationItem(itemName="regularExpression", itemValue = "\\w+"), @ConfigurationItem(itemName=ConfigurationItem.ERROR_MESSAGE, itemValue = ”Only word chars are allowed.”) })protected String firstName;

CustomerImpl.java

Result

In this example, first name must be valid for this Regular Expression

Page 35: Dynamically Generate a CRUD Admin Panel with Java Annotations

35

Admin Customizations … Validation … Custom Validators …

You can create custom admin validators …

To create a custom property validator, implement the PropertyValidator interface …

public PropertyValidationResult validate(Entity entity,

Serializable instance, Map<String, FieldMetadata> entityFieldMetadata, Map<String, String> validationConfiguration, BasicFieldMetadata propertyMetadata, String propertyName, String value);

This interface looks a bit daunting but is easy to implement. See the JavaDocs or just go straight to an out of box implementation like …

org.broadleafcommerce.openadmin.server. service.persistence.validation.RegexPropertyValidator

Page 36: Dynamically Generate a CRUD Admin Panel with Java Annotations

36

Admin Customizations … Validation … JSR 303 …

You can add support for JSR-303 validation by modifying your application context

• Allows for @Email, @URL etc. from hibernate-validator

– Same structure as Spring MVC @Valid annotation

• Add two lines to applicationContext.xml to enable support for JSR-303

<bean id="blEntityValidatorService" class="org.broadleafcommerce.openadmin.server.service.persistence. validation.BeanValidationEntityValidatorServiceImpl" />

<bean class="org.springframework.validation.beanvalidation. LocalValidatorFactoryBean" />

applicationContext.xml

Page 37: Dynamically Generate a CRUD Admin Panel with Java Annotations

Admin Customizations ... View Layer

Frontend Validation

BLCAdmin.addPreValidationSubmitHandler(function($form) { // modify the form data prior to sending to the server});BLCAdmin.addValidationSubmitHandler(function($form) { // return false to stop the form from submitting});BLCAdmin.addPostValidationSubmitHandler(function($form) { // do work after receiving a response from the server});

Page 38: Dynamically Generate a CRUD Admin Panel with Java Annotations

38

Annotation Overrides

Page 39: Dynamically Generate a CRUD Admin Panel with Java Annotations

39

Admin Customizations … Annotation Overrides …

Broadleaf provides two methods for overriding annotations • In the examples so far, the annotations changes were directly made as part of

the @AdminPresentation

• Since you cannot modify Broadleaf classes, additional mechanisms are provided to allow you to override (or add to) the out of box annotations

• Method 1 : Override Using XML

- Add overrides to adminApplicationContext.xml

- Use the mo schema (see mo-3.0.xsd for info)

• Method 2 : Use the class level annotation “@AdminPresentationMergeOverride”

- Convenient when extending a Broadleaf class

Page 40: Dynamically Generate a CRUD Admin Panel with Java Annotations

40

Admin Customizations … Annotation Overrides … Using XML …

Override Using XML …

The example below makes the Customer firstName property required and adds help text.

<mo:override id="blMetadataOverrides"> <mo:overrideItem ceilingEntity = "org.broadleafcommerce…Customer"> <mo:field name=“firstName”> <mo:property name="requiredOverride” value="true"/> <mo:property name="helpText" value="This is help text"/> </mo:field> </mo:overrideItem></mo:override>

applicationContext.xml

Get IDE auto-completion by updating your applicationContext-admin.xml file beans tagto include …

• Update schemaLocations with http://schema.broadleafcommerce.org/mo and http://schema.broadleafcommerce.org/mo/mo-3.0.xsd

• Add the namespace … xmlns:mo="http://schema.broadleafcommerce.org/mo

Page 41: Dynamically Generate a CRUD Admin Panel with Java Annotations

41

Admin Customizations … Annotation Overrides … Using Extended Class Annotation …

Override using Extended Class Annotation …

The example below makes the Customer firstName property required and adds help text using annotations on a derived class

@AdminPresentationMergeOverrides( { @AdminPresentationMergeOverride(name = ”firstName", mergeEntries = { @AdminPresentationMergeEntry( propertyType=PropertyType.AdminPresentation.REQUIREDOVERRIDE, booleanOverrideValue = true) @AdminPresentationMergeEntry( propertyType=PropertyType.AdminPresentation.HELPTEXT, overrideValue = “This is help text”) } })public class MyCustomerImpl extends CustomerImpl {

MyCustomer.java

Page 42: Dynamically Generate a CRUD Admin Panel with Java Annotations

42

Demo

Page 43: Dynamically Generate a CRUD Admin Panel with Java Annotations

43

Admin Security

Page 44: Dynamically Generate a CRUD Admin Panel with Java Annotations

44

Admin Customizations ... Admin Security

Security Model

• Entity-based permissions – permission to perform a CRUD operation

• If the admin user has no permissions in a particular section, that section is not shown

• All permissions are rolled up into the Spring Security Principal’s GrantedAuthorities

Page 45: Dynamically Generate a CRUD Admin Panel with Java Annotations

45

Admin Customizations ... Admin Security

Role Management

Page 46: Dynamically Generate a CRUD Admin Panel with Java Annotations

Admin Customizations ... Admin Security

Invisible Modules/Sections

Page 47: Dynamically Generate a CRUD Admin Panel with Java Annotations

47

Admin Customizations ... Admin Security

Row-level Security

• Finer-grained control over security on a particular row vs an entity type as a whole

• Additional fetch criteria, readonly rows, prevent deletions of rows

• Javadocs for RowLevelSecurityProvider

@Componentpublic class ProductStoreRowSecurityProvider { public void addFetchRestrictions(AdminUser currentUser,

String ceilingEntity,List<Predicate> restrictions,Root root,CriteriaQuery criteria,CriteriaBuilder criteriaBuilder) {

Store adminStore = ((MyAdminuser) currentUser).getStore(); Predicate storeRestriction = criteriaBuilder.equal(root.get("store"), adminStore); restrictions.add(storeRestriction);}

Page 48: Dynamically Generate a CRUD Admin Panel with Java Annotations

48

Admin Customizations ... Admin Security

Other security features

• CSRF protection

– Token automatically generated and checked

• XSS protection

– Turned off by default for CMS functionality

– OWASP AntiSamy

Example Broadleaf Myspace AntiSamy configuration file

<bean id="blExploitProtectionService" class="org.broadleafcommerce.common.security.service.ExploitProtectionServiceImpl"> <property name="xsrfProtectionEnabled”

value="true" /> <property name="xssProtectionEnabled”

value="false" /> <property name="antiSamyPolicyFileLocation”

value="the_location_of_your_file" /> </bean>

Page 49: Dynamically Generate a CRUD Admin Panel with Java Annotations

49

Other Extension Points

Page 50: Dynamically Generate a CRUD Admin Panel with Java Annotations

50

Admin Spring MVC Controller

Admin Customizations ... Admin persistence APIs

Admin Persistence – Request Flow

DynamicEntityRemoteService

PersistenceManager

PersistenceModuleCustomPersistenceHandler

DynamicEntityDao

FieldMetadataProvider

FieldPersistenceProvider

Transaction boundary starts here

Database

EntityValidatorService

PersistenceEventHandler

Page 51: Dynamically Generate a CRUD Admin Panel with Java Annotations

51

Admin Customizations ... View Layer

Spring MVC

• AdminBasicEntityController

– Provides facilities for all CRUD operations

– Generic request mapping using path parameters

@RequestMapping("/{sectionKey:.+}")

– Custom controllers can override the request mapping with a specific URL

Generic Broadleaf admin controller

Specific customer controller (intercepts all methods to “/customer/”)…

@Controller("blAdminBasicEntityController")@RequestMapping("/{sectionKey:.+}”)public class AdminBasicEntityController extends AdminAbstractController { ...}

@Controller@RequestMapping("/customer”)public class AdminCustomerController extends AdminBasicEntityController { ...}

Page 52: Dynamically Generate a CRUD Admin Panel with Java Annotations

52

Admin Customizations ... View Layer

Admin Template Overrides

• Thymeleaf template resolution (TemplateResolver)

– Create custom templates in /WEB-INF/templates/admin

– Add custom resolvers to the blAdminWebTemplateResolvers list bean

– Example – override all strings entity fields to always load an HTML editor

/WEB-INF/templates/admin/fields/string.html

classpath:open_admin_style/templates/fields/string.html

classpath:/common_style/templates/fields/string.html

<div th:include=“fields/string.html” th:remove=“tag” />

locate fields/string.html

could not find

could not find

Page 53: Dynamically Generate a CRUD Admin Panel with Java Annotations

53

Admin Customizations ... View Layer

ListGrid

• Relationships (subgrids) as well as main grids

• Toolbar buttons

• ListGrid.Type

Page 54: Dynamically Generate a CRUD Admin Panel with Java Annotations

54

Admin Customizations ... View Layer

HTML Fields

• WYSIWYG editor by Redactor

• Redactor has its own extensible plugin API

• Additional extensions and/or customizations should add an initialization handler

Page 55: Dynamically Generate a CRUD Admin Panel with Java Annotations

55

Admin Customizations ... View Layer

Example frontend customizations

Page 56: Dynamically Generate a CRUD Admin Panel with Java Annotations

56

Broadleaf Commerce Admin Demo