Developer How-to

Info: Bloomreach provides Enterprise support for this feature to Bloomreach Experience customers. The release cycle for this feature may differ from the core product release cycle.

Subscribe to Image/Asset Creation and Update Events

The Gallery Asset Bulk Upload Plugin initializes and registers a com.onehippo.cms7.galleryasset.bulkupload.SynchronousEventBus service. This service posts gallery item creation and update events. Unlike the Hippo Event Bus, which uses Guava's AsyncEventBus and processes events asynchronously with a ThreadPoolExecutor, the SynchronousEventBus uses Guava's EventBus to execute event handlers synchronously. Synchronous execution is required when you need to create or update associated documents as part of the event handling.

To subscribe to gallery events and perform actions synchronously, register a listener with methods annotated by Guava's @Subscribe annotation to the SynchronousEventBus service.

Register a Listener with SynchronousEventBus

When Bloomreach Content starts, the plugin automatically registers the SynchronousEventBus service. To register your own listener, follow these steps in your web application module:

  1. In a Repository Addon (Daemon) Module, create a listener and register it during initialization.
  2. When an event is propagated, read the gallery event object (category, action, and subjectId). Use this information to implement your custom logic.

The following example shows a DaemonModule implementation that initializes and registers an event listener:

@RequiresService(types = { SynchronousEventBus.class }) public class GalleryEventListenerRegisteringDaemonModule extends AbstractReconfigurableDaemonModule { private DemoGalleryEventListener demoGalleryEventListener; private boolean registered; public GalleryEventListenerRegisteringDaemonModule() { demoGalleryEventListener = new DemoGalleryEventListener(); } @Override protected void doInitialize(Session session) throws RepositoryException { if (!registered) { SynchronousEventBus synchronousEventBus = HippoServiceRegistry.getService(SynchronousEventBus.class); if (synchronousEventBus != null) { synchronousEventBus.register(demoGalleryEventListener); registered = true; } } } @Override protected void doShutdown() { SynchronousEventBus synchronousEventBus = HippoServiceRegistry.getService(SynchronousEventBus.class); if (synchronousEventBus != null) { synchronousEventBus.unregister(demoGalleryEventListener); } } }

Implement your listener as shown below:

public class DemoGalleryEventListener { private static Logger log = LoggerFactory.getLogger(DemoGalleryEventListener.class); private static final Credentials SYSTEM_CREDENTIALS = new SimpleCredentials("system", new char[] {}); private static final String IMAGE_DOCUMENT_TYPE = "hippoaddongalleryassetbulkuploaddemo:imageDocument"; private static final String IMAGE_LINK_NODE_NAME = "hippoaddongalleryassetbulkuploaddemo:image"; private static final String ASSET_DOCUMENT_TYPE = "hippoaddongalleryassetbulkuploaddemo:assetDocument"; private static final String ASSET_LINK_NODE_NAME = "hippoaddongalleryassetbulkuploaddemo:asset"; private static final String LAST_MODIFIED_TIME_PROP_NAME = "hippoaddongalleryassetbulkuploaddemo:lastModified"; private boolean enabled; public DemoGalleryEventListener() { } public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } /** * Event handler method. * @param event gallery event */ @Subscribe public void handleEvent(GalleryEvent event) { if (!isEnabled()) { log.debug("Ignoring gallery event because the listener is disabled."); return; } // Process only 'gallery' category events with 'createGalleryItem' action. if ("gallery".equals(event.application()) && "createGalleryItem".equals(event.get("action"))) { createOrUpdateAssociatedDocument((String) event.get("subjectId")); } } /** * Creates or updates an associated document for the gallery image or asset document referenced by {@code subjectId}. * @param subjectId node id of a gallery image or asset document node, or a gallery folder node */ private void createOrUpdateAssociatedDocument(final String subjectId) { // Retrieve user-selected options from GalleryProcessingContext. final boolean bulkUploadMode = GalleryProcessingContext.isBulkUploadMode(); final boolean overwriteOption = GalleryProcessingContext.isOverwriteOption(); final boolean publishOption = GalleryProcessingContext.isPublishOption(); // Get the current CMS user's JCR session. Session jcrSession = UserSession.get().getJcrSession(); try { final Node subjectNode = jcrSession.getNodeByIdentifier(subjectId); if (subjectNode.isNodeType(HippoStdNodeType.NT_FOLDER)) { log.debug("Skipping the subject node because it is a folder."); return; } // Retrieve handle node from the gallery image or asset node. final Node handleNode = HippoNodeUtils.getHippoDocumentHandle(subjectNode); final String handleNodePath = handleNode.getPath(); boolean imageHandle; if (StringUtils.startsWith(handleNodePath, "/content/assets/")) { imageHandle = false; } else if (StringUtils.startsWith(handleNodePath, "/content/gallery/")) { imageHandle = true; } else { throw new IllegalArgumentException("Non image or asset handle path: " + handleNodePath); } // Create a DocumentManager (from Content-EXIM forge module). DocumentManager documentManager = new WorkflowDocumentManagerImpl(jcrSession); String documentLocation = getAssociatedDocumentLocation(handleNode, imageHandle); // Check if the associated document exists at the location. boolean existing = documentManager.documentExists(documentLocation); if (existing && !overwriteOption) { log.debug("Skipping document creation at {} because overwriting option is disabled.", documentLocation); } else { log.debug("{} associated document at {}.", (existing ? "Overwriting" : "Creating"), documentLocation); final String documentName = StringUtils.substringAfterLast(documentLocation, "/"); // Create a ContentNode object for document creation. ContentNode contentNode; if (imageHandle) { contentNode = createImageDocumentContentNode(documentName, handleNode.getIdentifier()); } else { contentNode = createAssetDocumentContentNode(documentName, handleNode.getIdentifier()); } DocumentVariantImportTask importTask = new WorkflowDocumentVariantImportTask(documentManager); documentLocation = importTask.createOrUpdateDocumentFromVariantContentNode(contentNode, contentNode.getPrimaryType(), documentLocation, "en", documentName); if (publishOption) { documentManager.publishDocument(documentLocation); } jcrSession.save(); } } catch (Exception e) { log.error("Failed to create or update associated document.", e); } } /** * Returns the path of the associated document for a gallery image or asset {@code handle} node. * For example: * ``` * /content/gallery/myhippoproject/samples/test.png * --> /content/documents/myhippoproject/images/samples/test.png * ``` * @param handle a gallery image or asset {@code handle} node * @param imageHandle true if image, false if asset * @return the path of the associated document * @throws RepositoryException if repository exception occurs */ private String getAssociatedDocumentLocation(final Node handle, final boolean imageHandle) throws RepositoryException { final String handlePath = handle.getPath(); String galleryRelPath = null; if (StringUtils.startsWith(handlePath, "/content/assets/")) { galleryRelPath = StringUtils.removeStart(handle.getPath(), "/content/assets/"); } else if (StringUtils.startsWith(handlePath, "/content/gallery/")) { galleryRelPath = StringUtils.removeStart(handle.getPath(), "/content/gallery/"); } else { throw new IllegalArgumentException("Non image or asset handle path: " + handlePath); } int offset = galleryRelPath.indexOf('/'); final String projectFolderName = galleryRelPath.substring(0, offset); final String documentLocation = "/content/documents/" + projectFolderName + (imageHandle ? "/images" : "/assets") + galleryRelPath.substring(offset); return documentLocation; } /** * Creates a {@link ContentNode} object for the image container document content. * @param name document name * @param imageHandleIdentifier image handle node identifier * @return a {@link ContentNode} object for use by {@link DocumentVariantImportTask} */ private ContentNode createImageDocumentContentNode(final String name, final String imageHandleIdentifier) { ContentNode contentNode = new ContentNode(name, IMAGE_DOCUMENT_TYPE); contentNode.addMixinType("mix:referenceable"); ContentNode linkNode = new ContentNode(IMAGE_LINK_NODE_NAME, "hippogallerypicker:imagelink"); linkNode.setProperty(new ContentProperty(HippoNodeType.HIPPO_FACETS, ContentPropertyType.STRING, true)); linkNode.setProperty(new ContentProperty(HippoNodeType.HIPPO_VALUES, ContentPropertyType.STRING, true)); linkNode.setProperty(new ContentProperty(HippoNodeType.HIPPO_MODES, ContentPropertyType.STRING, true)); linkNode.setProperty(HippoNodeType.HIPPO_DOCBASE, imageHandleIdentifier); contentNode.addNode(linkNode); ContentProperty lastModifiedProp = new ContentProperty(LAST_MODIFIED_TIME_PROP_NAME, ContentPropertyType.DATE); lastModifiedProp.setValue(ISO8601.format(Calendar.getInstance())); contentNode.setProperty(lastModifiedProp); return contentNode; } /** * Creates a {@link ContentNode} object for the asset container document content. * @param name document name * @param assetHandleIdentifier asset handle node identifier * @return a {@link ContentNode} object for use by {@link DocumentVariantImportTask} */ private ContentNode createAssetDocumentContentNode(final String name, final String assetHandleIdentifier) { ContentNode contentNode = new ContentNode(name, ASSET_DOCUMENT_TYPE); contentNode.addMixinType("mix:referenceable"); ContentNode linkNode = new ContentNode(ASSET_LINK_NODE_NAME, HippoNodeType.NT_MIRROR); linkNode.setProperty(HippoNodeType.HIPPO_DOCBASE, assetHandleIdentifier); contentNode.addNode(linkNode); ContentProperty lastModifiedProp = new ContentProperty(LAST_MODIFIED_TIME_PROP_NAME, ContentPropertyType.DATE); lastModifiedProp.setValue(ISO8601.format(Calendar.getInstance())); contentNode.setProperty(lastModifiedProp); return contentNode; } }

Bootstrap the DaemonModule

To activate the DaemonModule that registers your event listener, add the configuration before the next deployment cycle. Place the following configuration in repository-data/application/src/main/resources/hcm-config/configuration/modules/demo-gallery-eventhandler.yaml:

definitions: config: /hippo:configuration/hippo:modules/demo-gallery-eventhandler: jcr:primaryType: hipposys:module hipposys:className: com.onehippo.cms7.galleryasset.bulkupload.plugin.gallery.demo.event.GalleryEventListenerRegisteringDaemonModule /hippo:moduleconfig: jcr:primaryType: nt:unstructured enabled: true
Share Feedback
Page: /build/service-plugins/bulk-upload/developers-how-to
Section: Build
Category *