CQ5 - displaying a CQ.Notification in the frontend, when a workflow finished - aem

I implemented workflows, but it would be nice to know if there are hooks provided by the client library which allow to hook in. When a workflow was triggered and finished, a CQ.Notification should be displayed. Or do i need to implement a polling library by myself?

As far as I know, there is no built-in CQ area to see when something is done, aside from looking here:
http://yoursite.com:port/libs/cq/workflow/content/console.html
Once there, you can go to the 'Instances' tab and see what's happening.
In one application that I worked on, we ended up writing our own method that sends notifications to us based on one of our workflows (our workflow ties into it - from the workflow models area, you can set your process to be a servlet that you've put into CQ). Here is the main piece of code from our servlet that catches the process being active, and then calls our methods to email us based on what it finds:
import com.day.cq.workflow.WorkflowException;
import com.day.cq.workflow.WorkflowSession;
import com.day.cq.workflow.exec.WorkItem;
import com.day.cq.workflow.exec.WorkflowData;
import com.day.cq.workflow.exec.WorkflowProcess;
import com.day.cq.workflow.metadata.MetaDataMap;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.osgi.framework.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import java.util.Arrays;
public class YourServletName implements WorkflowProcess {
#Override
public void execute(WorkItem workItem, WorkflowSession workflowSession, MetaDataMap args) throws WorkflowException {
session = workflowSession.getSession();
final WorkflowData data = workItem.getWorkflowData();
String type = data.getPayloadType();
String[] argStrings = args.get("PROCESS_ARGS", ARG_UPDATED).split(",");
String reason = argStrings[0];
String baseUrl = argStrings[1];
try {
if (type.equals(TYPE_JCR_PATH) && data.getPayload() != null) {
String resourcePath = data.getPayload().toString();
logger.info("Send Notification that {} has been {}.", resourcePath, reason.toLowerCase());
if (resourcePath != null && !resourcePath.isEmpty()) {
ResourceInfo resourceInfo = new ResourceInfo(resourcePath, baseUrl);
sendEmail(resourceInfo, reason);
}
}
} catch (EmailException ex) {
logger.warn("Failed to send Email");
throw new WorkflowException(ex);
} catch (MailingException ex) {
logger.warn("Failed to send Email");
throw new WorkflowException(ex);
}
}
}
You can find more info in the documentation for Extending Workflow Functionality.
Look at the first code block on that page, and that will give you the best idea of how you can implement a custom workflow handler.
EDIT
If you want to see it on the front-end, you could do an AJAX call to get the JSON list of currently running workflows - you can hit this url:
http://localhost:4502/etc/workflow/instances.RUNNING.json
Then you could loop through them and see if yours is in there. This isn't very nice though, since they are all just listed by IDs. I would instead suggest using the querybuilder, or again, just doing an AJAX GET. This is one example:
1_group.0_path=/etc/workflow/instances
2_group.0_type=cq:Workflow
0_group.property.2_value=COMPLETED
0_group.property=status
0_group.property.and=true
3_group.property=modelId
3_group.property.2_value=/etc/workflow/models/your-model-name/jcr:content/model
3_group.property.and=true
Then the URL would look something like this:
http://yoursiteurl:port/libs/cq/search/content/querydebug.html?_charset_=UTF-8&query=http%3A%2F%2Fyoursiteurl%3Aport%3F%0D%0A1_group.0_path%3D%2Fetc%2Fworkflow%2Finstances%0D%0A2_group.0_type%3Dcq%3AWorkflow%0D%0A0_group.property.2_value%3DRUNNING%0D%0A0_group.property%3Dstatus%0D%0A0_group.property.and%3Dtrue%0D%0A3_group.property%3DmodelId%0D%0A3_group.property.2_value%3D%2Fetc%2Fworkflow%2Fmodels%2Fyour-model-name%2Fjcr%3Acontent%2Fmodel%0D%0A3_group.property.and%3Dtrue
It's ugly, but it gets you the results you need, and then you can parse them to get further information you need.

Related

Catching AEM Content installation event

The use case here is to get the information of content with type cq:Page and dam:Asset that is being installed/updated in the JCR using package manager in AEM.
I would like to know if there are any event listener or API's that can be used for this purpose.
Is there a way to know if the content was installed/updated using the package manager?
AEM 6.2 is being used here.
Thanks!!
To detect Resource changes, you need to create a ResourceChangeListener
Here is an example based on ACS commons SampleResourceChangeListener but restricted to ADDED and CHANGED events.
import org.apache.felix.scr.annotations.*;
import org.apache.sling.api.resource.observation.ExternalResourceChangeListener;
import org.apache.sling.api.resource.observation.ResourceChange;
import org.apache.sling.api.resource.observation.ResourceChangeListener;
import org.apache.sling.event.jobs.JobManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* The Sling Resource Change Listener is the preferred method for listening for Resource Change events in AEM.
* This is preferred over the Sling Resource Event Listener, or the JCR Event Handler approaches.
*
* ResourceChangeListener Javadoc:
* - https://docs.adobe.com/docs/en/aem/6-2/develop/ref/javadoc/org/apache/sling/api/resource/observation/ResourceChangeListener.html
*
* Note: To listen for External events, implements the ExternalResourceChangeListener. If ONLY local events are in scope, implement only the ResourceChangeListener.
*/
#Component(
label = "Sample - Resource Change Listener",
description = "A sample implementation of the Sling Resource Change Listener",
metatype = true
)
#Properties({
// Scope the paths as tightly as possible based on your use-case.
#Property(
label = "Paths",
description = "[ Required ] A list of resource paths this listener will listen for change events.",
name = ResourceChangeListener.PATHS,
value = {"/content"}
),
// Scope the types as tightly as possible based on your use-case.
// If This property is not provided, ALL ChangeTypes will be accepted.
// Available values are defined on: ResourceChange.ChangeType
#Property(
label = "Change Types",
description = "[ Optional ] The change event types this listener will listener for. ",
name = ResourceChangeListener.CHANGES,
value = {"ADDED", "CHANGED"}
)
})
#Service
public class SampleResourceChangeListener implements ResourceChangeListener, ExternalResourceChangeListener {
private static final Logger log = LoggerFactory.getLogger(SampleResourceChangeListener.class);
#Reference
private JobManager jobManager;
public void onChange(#Nonnull List<ResourceChange> changes) {
// Iterate over the ResourceChanges and process them
for (final ResourceChange change : changes) {
// Process each change quickly; Do not do long-running work in the Resource Change Listener.
// If expensive/long-running work is required to handle the event, create a Sling Job to perform that work.
if (change.isExternal()) {
// Since this implements BOTH the ResourceChangeListener AND ExternalResourceChangeListener
// we can conditionally handle local vs external events.
}
switch (change.getType()) {
case ADDED:
log.debug("Change Type ADDED: {}", change);
if (change.getAddedPropertyNames().contains("someProperty")) {
// Do some work
// In this case we will pass some some data from the Event to a custom job via a custom Job topic.
final Map<String, Object> props = new HashMap<String, Object>();
props.put("path", change.getPath());
props.put("userId", change.getUserId());
jobManager.addJob("com/adobe/acs/commons/samples/somePropertyAdded", props);
}
break;
case CHANGED:
log.debug("Change Type CHANGED: {}", change);
if (change.getChangedPropertyNames().contains("someOtherProperty")) {
// Do some other work
}
break;
default:
// Do nothing
}
}
}
}
Alternatively, you can use a JcrEventListener, again ACS has a good example here: SampleJcrEventListener.java
I don't know of a way to detect if the cause of the add/update is package manager. However, if you want to execute some code before a package is installed, you can use InstallHook. I have not tried this before.

citrus waitFor().condition() statement not waiting when used with ftpServer

I'm trying to use the citrus-framework to test an integration that writes some files on a FTP server.
I need to wait until some file is uploaded to the ftp (I'm using waitFor().condition() statement to accomplish that) and then receive the messages sent and do some assertions.
import com.consol.citrus.annotations.CitrusTest;
import com.consol.citrus.condition.Condition;
import com.consol.citrus.context.TestContext;
import com.consol.citrus.dsl.testng.TestNGCitrusTestDesigner;
import com.consol.citrus.ftp.server.FtpServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ActiveProfiles;
import org.testng.annotations.Test;
import java.io.File;
#ActiveProfiles(value = "ftpTest")
#Test
public class FtpTest extends TestNGCitrusTestDesigner {
#Autowired
FtpServer ftpServer;
#Autowired
TestContext context;
#CitrusTest(name = "ftpTest")
public void ftpTest() {
// here I start my integration that uses a cron to upload the file
// this code is irrelevant for the example
Condition waitUntilFileIsUploaded = new Condition() {
#Override
public String getName () {
return "Check files on FTP";
}
#Override
public boolean isSatisfied (TestContext testContext){
return new File("/tmp/foo_dir").listFiles().length != 0;
}
#Override
public String getSuccessMessage (TestContext testContext){
return "Files found in FTP!";
}
#Override
public String getErrorMessage (TestContext testContext){
return "No file was found in FTP";
}
};
waitFor().condition(waitUntilFileIsUploaded).seconds(120L).interval(500L);
ftpServer.createConsumer().receive(context);
}
}
When I try to run this test looks like the waitFor() is never executed and ftpServer.createConsumer().receive(context); is executed before any file could be uploaded to the FTP.
This is the error that I'm getting:
ftpTest>TestNGCitrusTest.run:57->TestNGCitrusTest.run:111->TestNGCitrusTestDesigner.invokeTestMethod:73->TestNGCitrusTest.invokeTestMethod:133->ftpTest:49 ยป ActionTimeout
Any idea how I could fix this?
Also any complete example for using FTP Java DSL with Citrus would be more than welcome!
Please use test designer receive method instead of creating the consumer on your own.
receive(ftpServer)
.header("some-header", "some-value")
.payload("some payload");
Only then test designer can arrange the test actions in proper order. This is because test designer constructs the complete test action logic first and execution takes place at the very end of the test method.
As an alternative to that you could also use test runner instead of test designer. The runner will execute each test action immediately giving you the opportunity to add custom statements as you did before.

Drools Creating Rules(DRL) Programatically not working in drools-distribution-6.5.0.Final

I am working on putting some business rules in drool engine.We cannot use KIE workspace UI to author rules.So that is out.
Problem Statement:Create a application(front end angular UI) back end spring boot microservice to author rules.Those authored rules needs to be dynamically refreshed without having to restart the jvm and other micro services which want to use these rules,should use them.For e.g:granting credit or interest rates based dealer credit history ,duration with bank and any new rules which might be designed as per author.I started looking on this and theoretically one could build something like this by using API of drools compiler library.
There is code example here.
for real time refreshing,there is something called KnowledgeAgent.
https://docs.jboss.org/drools/release/5.2.0.Final/drools-guvnor-docs/html/ch09.html
What is the new accepted way of programmatically creating new drools rules in Drools 6?
My problem is I am not able to make this work.Code is running fine but I am not able to see the drl file getting written.In debug mode,I can see string object with proper drl structure.Has anyone encountered this problem before.?
I have seen some examples on github where people have done yoman job to integrate drools in spring boot.I can start with building my service,but I need to be sure that this something which is possible to do
Following code will help you create drool rule using code.It is not recommended way and most of people use kie-web interface to design and modify drool rules.Not sure about how we can modify already created .drl files.But this has given me start.Going
package com.sample.model;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import org.drools.compiler.lang.DrlDumper;
import org.drools.compiler.lang.api.DescrFactory;
import org.drools.compiler.lang.api.PackageDescrBuilder;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.Message;
import org.kie.api.builder.ReleaseId;
import org.kie.api.io.Resource;
import org.kie.api.io.ResourceType;
import org.kie.api.runtime.KieContainer;
//#SuppressWarnings("restriction")
public class GenerateRule {
public static void main(String[] args) {
// TODO Auto-generated method stub
KieContainer container=build(KieServices.Factory.get());
System.out.println(container.getReleaseId());
System.out.println(container.getKieBase());
}
public static KieContainer build(KieServices kieServices){
KieFileSystem fileSystem=kieServices.newKieFileSystem();
ReleaseId releaseId=kieServices.newReleaseId("com.example.rulesengine",
"model-test", "1.0-SNAPSHOT");
fileSystem.generateAndWritePomXML(releaseId);
//fileSystem.write("D:/workspace/DroolSamples/src/main/resources/rules/rules.drl", getResource(kieServices, "D:/workspace/DroolSamples/src/main/resources/rules/rules.drl"));
addRule(fileSystem);
KieBuilder kieBuilder = kieServices.newKieBuilder(fileSystem);
kieBuilder.buildAll();
if (kieBuilder.getResults().hasMessages(Message.Level.ERROR)) {
throw new RuntimeException("Build Errors:\n" +
kieBuilder.getResults().toString());
}
return kieServices.newKieContainer(releaseId);
}
#SuppressWarnings("restriction")
private static void addRule(KieFileSystem kieFileSystem) {
PackageDescrBuilder packageDescrBuilder = DescrFactory.newPackage();
packageDescrBuilder
.name("com.sample.model")
.newRule()
.name("Is of valid age")
.lhs()
.pattern("Person").constraint("age < 18")
.id("$a", true).end()
//.pattern().id("$a", false).end()
.end()
.rhs("$a.setShowBanner( false );")
//.rhs("insert(new Person())")
.end();
String rules = new DrlDumper().dump(packageDescrBuilder.getDescr());
KieFileSystem fileSystem=kieFileSystem.write("D:/newrule.drl", rules);
try{
// create new file
File file = new File("src/main/resources/rules/test.drl");
file.createNewFile();
FileWriter fw = new FileWriter(file.getAbsoluteFile());
BufferedWriter bw = new BufferedWriter(fw);
bw.write(rules);
// close connection
bw.close();
System.out.println("File Created Successfully");
}catch(Exception e){
System.out.println(e);
}
}
private static Resource getResource(KieServices kieServices, String resourcePath) {
try {
// InputStream is = com.google.common.io.Resources.getResource(resourcePath).openStream(); //guava
InputStream is=new FileInputStream(new File(resourcePath));
return kieServices.getResources()
.newInputStreamResource(is)
.setResourceType(ResourceType.DRL);
} catch (IOException e) {
throw new RuntimeException("Failed to load drools resource file.", e);
}
}
}

How to replace sling:resourceType value in bulk using query or script

How to replace sling:resourceType value in bulk using Query and Scipt.
For example I want to change sling:resourceType value
from app/component/linkButton to app/component/content/linkbutton1.
The component is being used on 20 pages, and I want change it using query rather than manually on each page.
the best choice for the purpose is groovy console .
Bellow script which do the job:
import javax.jcr.Node
getNode('/content/').recurse { resourceNode ->
if (resourceNode.hasProperty('sling:resourceType')) {
final def resourceType = resourceNode.getProperty('sling:resourceType').string
if (resourceType.equals('OLD_RESOURCE_TYPE')) {
println "changing " + resourceNode.path
resourceNode.setProperty('sling:resourceType', 'NEW_RESOURCE_TYPE')
resourceNode.save();
}
}
}
You can use the ACS AEM Tools open source project which includes AEM Fiddle. AEM Fiddle allows you to run scripts directly on the AEM instance without have to build.
If you use AEM Fiddle, navigate to http://localhost:4502/miscadmin#/etc/acs-tools/aem-fiddle, click the plus sign in the top right and select .java. Insert this code and run. Make sure you update the query's path.
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import javax.jcr.query.Query;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.ModifiableValueMap;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
public class fiddle extends SlingAllMethodsServlet {
#Override
protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException {
response.setStatus(HttpServletResponse.SC_OK);
PrintWriter out = response.getWriter();
ResourceResolver resolver = null;
out.println("starting...");
try {
resolver = request.getResourceResolver();
if (resolver != null) {
Iterator<Resource> resources = resolver.findResources("/jcr:root/content/mysite//*[#sling:resourceType='app/component/linkButton']", Query.XPATH);
while (resources.hasNext()) {
Resource resource = resources.next();
ModifiableValueMap properties = resource.adaptTo(ModifiableValueMap.class);
properties.put("sling:resourceType", "app/component/linkButton1");
resolver.commit();
out.println(resource.getPath());
}
}
} catch(Exception e) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
e.printStackTrace(out);
} finally {
if (resolver != null && resolver.isLive()) {
resolver.close();
resolver = null;
}
}
out.println("...finished");
}
}
If you'd rather use JSP as you've stated, the code is the same:
<%#include file="/libs/foundation/global.jsp"%><%
%><%#page session="false" contentType="text/html; charset=utf-8"
pageEncoding="UTF-8"
import="org.apache.sling.api.resource.*,
java.util.*,
javax.jcr.*,
com.day.cq.search.*,
com.day.cq.wcm.api.*,
com.day.cq.dam.api.*,
javax.jcr.query.Query,
org.apache.sling.api.resource.ModifiableValueMap"%><%
Iterator<Resource> resources = resourceResolver.findResources("/jcr:root/content/mysite//*[#sling:resourceType='app/component/linkButton']", Query.XPATH);
while (resources.hasNext()) {
Resource current = resources.next();
ModifiableValueMap props = current.adaptTo(ModifiableValueMap.class);
props.put("sling:resourceType", "app/component/linkButton1");
resourceResolver.commit();
%>
<%=current.getPath()%>
<%
}
%>
Another dirty method, but worked for me. :)
Package the path and download the zip file.
Extract to a folder.
Based on your operating system,
If using Windows, use Notepad++ to find an replace in all files under directory with your search pattern.
If linux, use find or sed commands to replace all occurrences inside a director
How about AEM ACS TOOLS?
It is bulk updating tool for sling:resourceType or cq:Template.
Click here for the article on Getting Started
Click here for the Github Repo
Good Luck...
You can also have a look at sling pipes.
https://sling.apache.org/documentation/bundles/sling-pipes.html
this is the perfect solution for your problem

Selenium WebDriver Stale Element Reference Exception for GWT

Ok I read all the other links, and i tried variants of the different solutions mentioned, however none of them work for me.
My Issue, I have the following Code:
package com.autotest.test.css;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.By;
import com.google.common.base.Predicate;
import java.util.concurrent.TimeUnit;
import cucumber.annotation.*;
import cucumber.annotation.en.*;
import static org.junit.Assert.assertEquals;
public class SaleStepsPre {
private WebDriver driver;
private String baseUrl;
#Before
public void setUp() {
System.setProperty("webdriver.chrome.driver", "/Users/AppData/Local/Google/Chrome/Application/chromedriver.exe");
driver = new ChromeDriver();
baseUrl = "http://xxxxx";
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
}
#Given("^I navigate to the css application$")
public void I_navigate_to_the_css_application() {
driver.get(baseUrl + "/care/a#brochureware-home");
}
#When("^I select the prepaid catalog$")
public void I_select_the_prepaid_catalog() {
driver.findElement(By.xpath("//div[#id='brochureware-home']/div/div/div/div[2]/div[2]/div/div")).click();
}
#When("^I select the add to basket for product$")
public void I_select_the_add_to_basket_for_product() {
driver.findElement(By.xpath("//*[#id='salesItem']/div[1]/div[1]/div/div[5]/div[1]/button")).click();
}
#When("^then I Click on the basket icon to go to basket$")
public void then_I_Click_on_the_basket_icon_to_go_to_basket() {
// times out after 5 seconds
// while the following loop runs, the DOM changes -
// page is refreshed, or element is removed and re-added
//driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
//WebElement searchBox;
//searchBox = driver.findElement(By.xpath("//input[#type='text']"));
//driver.findElement(By.xpath("html/body/div[2]/div[1]/nav/div[1]/div[3]/div[2]/div/ul[1]/li[5]/a/img")).click();
driver.findElement(By.cssSelector("c-menuimage")).click();
}
//#When("^then I click on the checkout button$")
//public void then_I_click_on_the_checkout_button() {
//driver.findElement(By.xpath("(//button[#type='button'])[9]")).click();
//}
#Then("^show product y$")
public void show_product_y() {
}
}
However I get following error:
For documentation on this error, please visit: http://seleniumhq.org/exceptions/no_such_element.html
B
For documentation on this error, please visit: http://seleniumhq.org/exceptions/no_such_element.html
This is the css path of basket icon, which is on a menu.
body > div:nth-child(3) > div:nth-child(1) > nav > div.container-fluid.c-wide > div.c-kill > div.collapse.navbar-collapse.c-2ndmenu > div > ul.nav.navbar-nav.navbar-left > li:nth-child(5) > a > img
The website is GWT, and the steps are:
1. Click on item add to basket
2. Adds to basket
3. Click on basket to go to basket.
However I cant seem to get this right.
I had problems, that the implicit wait only works for real page reloads, when the page is dynamicaly reloaded (like ajax) , then this will fail.
You can try expected conditions to wait for for items, they are nice and easy to use and robust. You can configure them to ignore certain exceptions,
so you can try to locate an element for a given time and then fail. This works even with ajax
In my case i have a small method like (it ignored the NoSuchElement exception):
protected <T> T waitForPageToLoad(ExpectedCondition<T> condition, String errorMessage) {
Wait<WebDriver> wait = new FluentWait<WebDriver>(driver).withTimeout(MAX_WAITING_TIME, SECONDS).ignoring(StaleElementReferenceException.class).ignoring(NoSuchElementException.class).pollingEvery(100, MILLISECONDS).withMessage(errorMessage);
T result = wait.until(condition);
return result;
}
MAX_WAITING_TIME is the time until this method throws an exception
Then you can use this with an expected condition like this:
public static final ExpectedCondition<Boolean> WAIT_FOR_BASKET = ExpectedConditions.visibilityOfElementLocated(By.cssSelector("c-menuimage"));
I have plenty of them in a utility class so they are static. you can do it like you want.
The full usage looks like:
WebElement elem = waitForPageToLoad(waitForPageToLoad);
elem.click();
This solution is originated here at stack overflow but i cant find the original question/answer, so kudos to the real guy who posted this