How to navigate to the next page based on the return value from the method called inside the action attribute of the command button.
<af:button id="tt_b2"
rendered="#{attrs.nextRendered}"
partialSubmit="true"
action="#{attrs.backingBean.nextAction}"
text="Next"
disabled="#{attrs.nextDisabled}"/>
private static final String NEXT_NAVIGATION_ACTION = "controllerContext.currentViewPort.taskFlowContext.trainModel.getNext";
public String nextAction() {
if (validate()) {
updateModel();
return NEXT_NAVIGATION_ACTION;
}
return null;
}
The use case is done for train model, which is implemented based on this blog : http://javacollectibles.blogspot.co.uk/2014/10/adf-train-template.html
We need to define a generic next action in the template but the action should be called conditionally, based on whether all the validation checks has been passed on not.
Try using ADFUtils.invokeEl
public String nextAction() {
if (validate()) {
updateModel();
return (String)ADFUtils.invokeEL(NEXT_NAVIGATION_ACTION);
}
return null;
}
Its ain't necessary to hardcode any steps, you can query TaskFlowTrainModel
/**
* Navigates to the next stop in a train
* #return outcome string
*/
public String navigateNextStop() {
String nextStopAction = null;
ControllerContext controllerContext = ControllerContext.getInstance();
ViewPortContext currentViewPortCtx = controllerContext.getCurrentViewPort();
TaskFlowContext taskFlowCtx = currentViewPortCtx.getTaskFlowContext();
TaskFlowTrainModel taskFlowTrainModel = taskFlowCtx.getTaskFlowTrainModel();
TaskFlowTrainStopModel currentStop = taskFlowTrainModel.getCurrentStop();
TaskFlowTrainStopModel nextStop = taskFlowTrainModel.getNextStop(currentStop);
//is either null or has the value of outcome
return nextStopAction;
}
Full code of the sample can be found on the ADF Code Corner.
To navigate by taskflow outcomes you just need to provide exact outcome String as return of your method:
private static final String NEXT_NAVIGATION_ACTION = "next";
public String nextAction() {
if (validate()) {
updateModel();
return NEXT_NAVIGATION_ACTION;
}
return null;
}
Can you verify, you can do it in through phase listener.
Verify you condition in the phase listener and allow it to move ahead if it validates else stop the thread execution.
Below is the sample phase listener code.
public class MyPhaseListener implements PagePhaseListener{
public MyPhaseListener() {
super();
}
#Override
public void afterPhase(PagePhaseEvent pagePhaseEvent) {
if (pagePhaseEvent.getPhaseId() == Lifecycle.PREPARE_RENDER_ID ) {
// DO your logic here
}
}
#Override
public void beforePhase(PagePhaseEvent pagePhaseEvent) {
}
}
Related
I'm trying to make Solr search phone numbers which are stored like this +79876543210 using a query like these:
+79876543210
79876543210
89876543210 <-- '+7' is replaced with region specific code '8'
9876543210 <-- '+7' entirely removed
This is just an example. Another one is wired line phone numbers:
+78662123456 <-- '+78662' is a specific region code
78662123456
88662123456
8662123456
123456 <-- region code entirely removed
One way I could manage this is using a separate field which is filled with these variants and used solely during search.
But this has issues with highlighting (it returns <em>123456</em> to be highlighted whereas the real value shown to user is +78662123456).
I thought that maybe it's best to make these indices using just Solr, but how?
First thought was to use managed synonyms filter and pass them along with each added record. But the docs explicitly states:
Changes made to managed resources via this REST API are not applied to the active Solr components until the Solr collection (or Solr core in single server mode) is reloaded.
So reloading a core every time after adding a record is not the way to go.
Other issues involve keeping these synonyms up to date with records.
Could there be another way to solve this?
Thanks to this comment (by MatsLindh) I've managed to assemble a simple filter based on bult-in EdgeNGramTokenFilter:
package com.step4;
import org.apache.lucene.analysis.TokenFilter;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ReverseCustomFilter extends TokenFilter {
private static final PatternReplacementPair[] phonePatterns = {
new PatternReplacementPair("\\+7", "7"),
new PatternReplacementPair("\\+7", "8"),
new PatternReplacementPair("\\+7", ""),
new PatternReplacementPair("\\+78662", ""),
new PatternReplacementPair("\\+78663", ""),
};
private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class);
private final PositionIncrementAttribute posIncrAtt = addAttribute(PositionIncrementAttribute.class);
private int curPatternIndex;
private int curPosIncr;
private State curState;
public ReverseCustomFilter(TokenStream input) {
super(input);
}
#Override
public final boolean incrementToken() throws IOException {
while (true) {
if (curPatternIndex == 0) {
if (!input.incrementToken()) {
return false;
}
curState = captureState();
curPosIncr += posIncrAtt.getPositionIncrement();
curPatternIndex = 1;
}
if (curPatternIndex <= phonePatterns.length) {
PatternReplacementPair replacementPair = phonePatterns[curPatternIndex - 1];
curPatternIndex++;
restoreState(curState);
Matcher matcher = replacementPair.getPattern().matcher(termAtt);
if (matcher.find()) {
posIncrAtt.setPositionIncrement(curPosIncr);
curPosIncr = 0;
String replaced = matcher.replaceFirst(replacementPair.getReplacement());
termAtt.setEmpty().append(replaced);
return true;
}
}
else {
restoreState(curState);
posIncrAtt.setPositionIncrement(0);
curPatternIndex = 0;
return true;
}
}
}
#Override
public void reset() throws IOException {
super.reset();
curPatternIndex = 0;
curPosIncr = 0;
}
#Override
public void end() throws IOException {
super.end();
posIncrAtt.setPositionIncrement(curPosIncr);
}
private static class PatternReplacementPair {
private final Pattern pattern;
private final String replacement;
public PatternReplacementPair(String pattern, String replacement) {
this.pattern = Pattern.compile(pattern);
this.replacement = replacement;
}
public Pattern getPattern() {
return pattern;
}
public String getReplacement() {
return replacement;
}
}
}
I need to create an action in my custom business process that must be executed every 10 minutes until an specific action is returned, is there any way to customize the polling interval of an action in hybris order business process? I know that you can configure a timeout but not a polling interval:
<wait id='waitForOrderConfirmation' then='checkOrder' prependProcessCode='true'>
<event>confirm</event>
<timeout delay='PT12H' then='asmCancelOrder'/>
Need custom Implementation to achieve this and need to use BusinessProcessParameterModel.
Below are the steps to Make Retry based on Dealy.
Create RepeatableAction.
import de.hybris.platform.processengine.model.BusinessProcessModel;
import de.hybris.platform.processengine.model.BusinessProcessParameterModel;
import de.hybris.platform.servicelayer.model.ModelService;
import de.hybris.platform.warehousing.process.BusinessProcessException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Optional;
public interface RepeatableAction<T extends BusinessProcessModel>
{
int COUNTER_STARTER = 1;
ModelService getModelService();
Optional<Integer> extractThreshold(T process);
String getParamName();
default void increaseRetriesCounter(final T process)
{
final Collection<BusinessProcessParameterModel> contextParameters = process.getContextParameters();
final Optional<BusinessProcessParameterModel> paramOptional = extractCounter(contextParameters);
paramOptional.ifPresent(this::incrementParameter);
if (!paramOptional.isPresent())
{
final Collection<BusinessProcessParameterModel> newContextParameters = new ArrayList<>(contextParameters);
final BusinessProcessParameterModel counter = getModelService().create(BusinessProcessParameterModel.class);
counter.setName(getParamName());
counter.setValue(COUNTER_STARTER);
counter.setProcess(process);
newContextParameters.add(counter);
process.setContextParameters(newContextParameters);
getModelService().save(process);
}
}
default Optional<BusinessProcessParameterModel> extractCounter(
final Collection<BusinessProcessParameterModel> contextParameters)
{
//#formatter:off
return contextParameters.stream().filter(p -> getParamName().equals(p.getName())).findFirst();
//#formatter:on
}
default void incrementParameter(final BusinessProcessParameterModel parameter)
{
final Object value = parameter.getValue();
if (value instanceof Integer)
{
parameter.setValue((Integer) value + 1);
getModelService().save(parameter);
}
else
{
//#formatter:off
final String message = MessageFormat.format("Wrong process parameter '{}' type. {} expected, {} actual.", getParamName(),
Integer.class.getSimpleName(), value.getClass().getSimpleName());
//#formatter:on
throw new BusinessProcessException(message);
}
}
default boolean retriesCountThresholdExceeded(final T process)
{
//#formatter:off
final Optional<Integer> counterOptional = extractCounter(process.getContextParameters())
.map(BusinessProcessParameterModel::getValue).filter(Integer.class::isInstance).map(Integer.class::cast);
//#formatter:on
final Optional<Integer> thresholdOptional = extractThreshold(process);
final boolean counterSet = counterOptional.isPresent();
final boolean thresholdSet = thresholdOptional.isPresent();
boolean thresholdExceeded = false;
if (counterSet && thresholdSet)
{
final int counter = counterOptional.get();
final int threshold = thresholdOptional.get();
thresholdExceeded = counter > threshold;
}
return counterSet && thresholdSet && thresholdExceeded;
}
}
Then Go to Custom Action and Implement This Interface and based on some condition make custom Transition as RETRY, something like this.
public class CustomAction extends AbstractAction<OrderProcessModel>
implements RepeatableAction<OrderProcessModel>
{
private static final int MAX_RETRIES = 3;//make it configurable it's your choice
#Override
public Transition prepare(OrderModel order, OrderProcessModel process)
{
if (!retriesCountThresholdExceeded(process))
{
if (custom condition)
{
return Transition.OK;
}
getModelService().refresh(order);
increaseRetriesCounter(process);
return Transition.RETRY;
}
return Transition.NOK;
}
}
#Override
public Optional<Integer> extractThreshold(OrderProcessModel process)
{
return Optional.of(MAX_RETRIES);
}
Then in process.xml Action entries should be like this.
<action id="customAction" bean="customAction">
<transition name="OK" to="nextStep"/>
<transition name="RETRY" to="waitForOrderConfirmation"/>
<transition name="NOK" to="cancelOrderAction"/>
</action>
<wait id='waitForOrderConfirmation' then='checkOrder' prependProcessCode='true'>
<event>confirm</event>
<timeout delay='PT12H' then='asmCancelOrder'/>
<wait>
NOTE: Please set dealy as per requirement as of new 12hr seems to be too much
i want select element when i click button.i use IExternalEventHandler ,but i cant use
method: pickobject/pickobjects ,i change the method to pickPoint the hander run success.
event
public class ExecuteEvent : IExternalEventHandler
{
public string ElementId { get; set; }
public void Execute(UIApplication app)
{
UIDocument uidoc = app.ActiveUIDocument;
Autodesk.Revit.DB.Document doc = uidoc.Document;
Autodesk.Revit.UI.Selection.Selection sel = uidoc.Selection;
Autodesk.Revit.DB.Reference re = sel.PickObject(Autodesk.Revit.UI.Selection.ObjectType.Element);
Autodesk.Revit.DB.XYZ point = sel.PickPoint("select");
ElementId = re.GetType().Name;
}
public string GetName()
{
return "ExecuteEvent";
}
}
hander
Exc = new ExecuteEvent();
ExternalHander = ExternalEvent.Create(Exc);
button click
private void Button_Click(object sender, RoutedEventArgs e)
{
ExternalHander.Raise();
SetLabelText(Exc.ElementId);
}
Apparently, the external event handler does not provide you with a valid user interface context. To get such a context, you might want to subscribe to the Idling event instead. That event is called when Revit has nothing else to do and hence is free to interact with the user.
I have a Trade class which contains a property currentPrice, which downloads price data from a website using getPricedata() method. The Trade object will show up as a table row in TableView. Now, my task: is to
use the getPricedata() method to grab data from internet, populate the currentPrice cell, whenever the object is created.
relaunch the getPricedata() method to every 1 minute after the object has been created and update table cell.
Below is the basic structure of my code. But I have no idea how to implement this ?
Which package do I need ? Task ? Service ? ScheduledService ?
public class Trade{
private DoubleProperty currentPrice;
// need thread here
public double getPricedata(){
.......
}
}
Use a ScheduledService<Number>, whose Task<Number>'s call() method retrieves and returns the value. Then you can either register an onSucceeded handler with the service, or just bind the Trade's currentPrice to service.lastValue(). Call setPeriod(..) on the service (once) to configure it to run every minute.
Since the currentPrice is being set from the service, you should only expose a ReadOnlyDoubleProperty from your Trade class (otherwise you might try to call currentPriceProperty().set(...) or setCurrentPrice(...), which would fail as it's bound).
I would do something like
public class Trade {
private final ReadOnlyDoubleWrapper currentPrice ;
private final ScheduledService<Number> priceService = new ScheduledService<Number>() {
#Override
public Task<Number> createTask() {
return new Task<Number>() {
#Override
public Number call() {
return getPriceData();
}
};
}
};
public Trade() {
priceService.setPeriod(Duration.minutes(1));
// in case of errors running service:
priceService.setOnFailed(e -> priceService.getException().printStackTrace());
currentPrice = new ReadOnlyDoubleWrapper(0);
currentPrice.bind(priceService.lastValueProperty());
startMonitoring();
}
public final void startMonitoring() {
priceService.restart();
}
public final void stopMonitoring() {
priceService.cancel();
}
public ReadOnlyDoubleProperty currentPriceProperty() {
return currentPrice.getReadOnlyProperty();
}
public final double getCurrentPrice() {
return currentPriceProperty().get();
}
private double getPriceData() {
// do actual retrieval work here...
}
}
(Code just typed in here without testing, but it should give you the idea.)
I'm trying to get windows workflows working, and I've become a little stumped.
I've gotten a single workflow working, but now I am trying to do something a little more complex: start a workflow, where each activity itself contains a workflow. (Picture something like the main program starts the activities "Input, logic, and output", and then each of those have additional activities like "prompt user, get input, etc.")
I've had it working fine, with the example from here (http://msdn.microsoft.com/en-us/magazine/gg535667.aspx), when I am not passing any parameters from the main program to the activites. My question is, how exactly does the 'Variables' and 'metadata.SetVariablesCollection' work in the NativeActivity, and how to I get the parameters to the low level activities?
This is what I am currently trying:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Activities;
using System.Collections.ObjectModel;
using System.Activities.Statements;
namespace Project1
{
internal class MainProgram
{
internal static void Main(string[] args)
{
try
{
var act = new SimpleSequence();
act.Activities.Add((Activity)(new WriteSomeText()));
act.Activities.Add((Activity)(new WriteSomeText()));
act.Activities.Add((Activity)(new WriteSomeText()));
act.Variables.Add(new Variable<string> ("stringArg", "TEXT"));
WorkflowInvoker.Invoke(act);
}
catch (Exception ex)
{
System.Console.WriteLine("EXCEPTION: {0}", ex);
}
}
public class WriteSomeText : CodeActivity
{
[RequiredArgument]
public InArgument<string> stringArg { get; set; }
protected override void Execute(CodeActivityContext context)
{
string output = context.GetValue(stringArg);
System.Console.WriteLine(output);
}
}
public class SimpleSequence : NativeActivity
{
Collection<Activity> activities;
Collection<Variable> variables;
Variable<int> current = new Variable<int> { Default = 0 };
public Collection<Activity> Activities
{
get
{
if (this.activities == null)
this.activities = new Collection<Activity>();
return this.activities;
}
set
{
this.activities = value;
}
}
public Collection<Variable> Variables
{
get
{
if (this.variables == null)
this.variables = new Collection<Variable>();
return this.variables;
}
set
{
this.variables = value;
}
}
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
metadata.SetChildrenCollection(this.activities);
metadata.SetVariablesCollection(this.variables);
metadata.AddImplementationVariable(this.current);
}
protected override void Execute(NativeActivityContext context)
{
if (this.Activities.Count > 0)
context.ScheduleActivity(this.Activities[0], onChildComplete);
}
void onChildComplete(NativeActivityContext context, ActivityInstance completed)
{
int currentExecutingActivity = this.current.Get(context);
int next = currentExecutingActivity + 1;
if (next < this.Activities.Count)
{
context.ScheduleActivity(this.Activities[next], this.onChildComplete);
this.current.Set(context, next);
}
}
}
}
}
This ends up throwing the following exception:
EXCEPTION: System.Activities.InvalidWorkflowException: The following errors were encountered while processing the workflow tree:
'WriteSomeText': Value for a required activity argument 'stringArg' was not supplied.
'WriteSomeText': Value for a required activity argument 'stringArg' was not supplied.
'WriteSomeText': Value for a required activity argument 'stringArg' was not supplied.
at System.Activities.Validation.ActivityValidationServices.ThrowIfViolationsExist(IList`1 validationErrors)
at System.Activities.Hosting.WorkflowInstance.ValidateWorkflow(WorkflowInstanceExtensionManager extensionManager)
at System.Activities.Hosting.WorkflowInstance.RegisterExtensionManager(WorkflowInstanceExtensionManager extensionManager)
at System.Activities.WorkflowApplication.EnsureInitialized()
at System.Activities.WorkflowApplication.RunInstance(WorkflowApplication instance)
at System.Activities.WorkflowApplication.Invoke(Activity activity, IDictionary`2 inputs, WorkflowInstanceExtensionManager extensions, TimeSpan timeout)
at System.Activities.WorkflowInvoker.Invoke(Activity workflow, TimeSpan timeout, WorkflowInstanceExtensionManager extensions)
at System.Activities.WorkflowInvoker.Invoke(Activity workflow)
at Project1.MainProgram.Main(String[] args) in c:\users\user\documents\visual studio 2010\Projects\ModelingProject1\Project1\MainProgram.cs:line 25
I know, I only pass 1 parameter, but the exception still says that I am missing 3 parameters. I am missing something as to how to do this properly.
You're correctly declaring stringArg as an InArgument but you're not passing any value to it when calling it inside SimpleSequence.
You can pass something using the constructor, while constructing the all activity itself, like this:
public class WriteSomeText : CodeActivity
{
[RequiredArgument]
public InArgument<string> stringArg { get; set; }
public WriteSomeText(string stringArg)
{
this.stringArg = stringArg;
}
protected override void Execute(CodeActivityContext context
{
string output = context.GetValue(stringArg);
System.Console.WriteLine(output);
}
}
// Calling the activity like this:
internal static void Main(string[] args)
{
var act = new SimpleSequence()
{
Activities =
{
new WriteSomeText("hello"),
new WriteSomeText("world"),
new WriteSomeText("!")
}
};
WorkflowInvoker.Invoke(act);
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
Also notice that is a best practice to use the constructor to initialize collections:
public SimpleSequence()
{
activities = new Collection<Activity>();
variables = new Collection<Variable>();
}
This way is even more intuitive to initialize the activity:
var act = new SimpleSequence()
{
Activities =
{
new WriteSomeText("hello"),
new WriteSomeText("world"),
new WriteSomeText("!")
},
Variables =
{
new Variable<int>("myNewIntVar", 10),
// ....
}
};
EDIT:
There are a couple of other ways to approach the problem. This is your best friend while starting in the WF4 world.
Check WF\Basic\CustomActivities\Code-Bodied for a little push with this particular case.