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
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 am doing a simple time slot (planning entity) -> team (planning var) assignment task. All work fine while using only one thread. Also multithreading works fine if one custom move I have is not involved, but after it's used the solver gets stuck forever with no message. All the custom moves are created before every STEP. Full assert doesn't say anything. It looks like deadlock for me. I am using optaplanner 8.25.0.Final with Java 17
This is the move factory:
package pl.medaxtrans.pre.solver;
import org.optaplanner.core.impl.heuristic.move.Move;
import org.optaplanner.core.impl.heuristic.selector.move.factory.MoveListFactory;
import pl.medaxtrans.common.domain.Team;
import pl.medaxtrans.pre.domain.HalfHourTimeslot;
import pl.medaxtrans.pre.domain.ShiftsSolution;
import java.time.DayOfWeek;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class FarSlotChangeMoveFactory implements MoveListFactory<ShiftsSolution> {
private static Predicate<HalfHourTimeslot> equalTime(HalfHourTimeslot slot2) {
return slot -> slot.compareTo(slot2) == 0;
}
private static Predicate<HalfHourTimeslot> sameDayAndTeam(DayOfWeek day, Team team) {
return slot -> slot.getDayOfWeek().equals(day) && slot.getTeam().equals(team);
}
#Override
public List<? extends Move<ShiftsSolution>> createMoveList(ShiftsSolution solution) {
List<FarSlotChangeMove> moves = new ArrayList<>();
List<HalfHourTimeslot> slots = solution.getSlots();
List<Team> teams = solution.getTeams();
for (Team fromTeam : teams) {
for (Team toTeam : teams) {
if (fromTeam != toTeam) {
for (DayOfWeek day : DayOfWeek.values()) {
List<HalfHourTimeslot> fromTeamSlots = slots.stream().filter(sameDayAndTeam(day, fromTeam)).collect(Collectors.toList());
List<HalfHourTimeslot> toTeamSlots = slots.stream().filter(sameDayAndTeam(day, toTeam)).collect(Collectors.toList());
Optional<HalfHourTimeslot> fromMinOpt = fromTeamSlots.stream().min(Comparator.naturalOrder());
Optional<HalfHourTimeslot> fromMaxOpt = fromTeamSlots.stream().max(Comparator.naturalOrder());
Optional<HalfHourTimeslot> toMaxOpt = toTeamSlots.stream().max(Comparator.naturalOrder());
Optional<HalfHourTimeslot> toMinOpt = toTeamSlots.stream().min(Comparator.naturalOrder());
if (fromMinOpt.isPresent() && fromMaxOpt.isPresent() && toMaxOpt.isPresent() && toMinOpt.isPresent())
{
HalfHourTimeslot fromMin = fromMinOpt.get();
HalfHourTimeslot fromMax = fromMaxOpt.get();
HalfHourTimeslot toMax = toMaxOpt.get();
HalfHourTimeslot toMin = toMinOpt.get();
if (toMin.compareTo(fromMin) < 0 && toMax.compareTo(fromMin) > 0 && toTeamSlots.stream().noneMatch(equalTime(fromMin)))
{
moves.add(new FarSlotChangeMove(fromMin, toTeam));
}
if (toMin.compareTo(fromMax) < 0 && toMax.compareTo(fromMax) > 0 && toTeamSlots.stream().noneMatch(equalTime(fromMax)))
{
moves.add(new FarSlotChangeMove(fromMax, toTeam));
}
}
}
}
}
}
return moves;
}
}
And this is the problematic move:
package pl.medaxtrans.pre.solver;
import org.optaplanner.core.api.score.director.ScoreDirector;
import org.optaplanner.core.impl.heuristic.move.AbstractMove;
import pl.medaxtrans.common.domain.Team;
import pl.medaxtrans.pre.domain.HalfHourTimeslot;
import pl.medaxtrans.pre.domain.ShiftsSolution;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
public class FarSlotChangeMove extends AbstractMove<ShiftsSolution> {
private final HalfHourTimeslot slot;
private final Team toTeam;
private final Team fromTeam;
public FarSlotChangeMove(HalfHourTimeslot slot, Team toTeam) {
this.slot = slot;
this.toTeam = toTeam;
this.fromTeam = slot.getTeam();
}
#Override
protected AbstractMove<ShiftsSolution> createUndoMove(ScoreDirector<ShiftsSolution> scoreDirector) {
return new FarSlotChangeMove(slot, fromTeam);
}
#Override
protected void doMoveOnGenuineVariables(ScoreDirector<ShiftsSolution> scoreDirector) {
scoreDirector.beforeVariableChanged(slot, "team");
slot.setTeam(toTeam);
scoreDirector.afterVariableChanged(slot, "team");
}
#Override
public boolean isMoveDoable(ScoreDirector<ShiftsSolution> scoreDirector) {
// we don't check overlaps here, because it is already checked by the move factory
return !toTeam.equals(fromTeam);
}
#Override
public FarSlotChangeMove rebase(ScoreDirector<ShiftsSolution> destinationScoreDirector) {
return new FarSlotChangeMove(destinationScoreDirector.lookUpWorkingObject(slot), destinationScoreDirector.lookUpWorkingObject(toTeam));
}
#Override
public Collection<?> getPlanningEntities() {
return Collections.singletonList(slot);
}
#Override
public Collection<?> getPlanningValues() {
return Collections.singletonList(toTeam);
}
#Override
public String toString() {
return slot + " {" + fromTeam + "->" + toTeam + "}";
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final FarSlotChangeMove other = (FarSlotChangeMove) o;
return Objects.equals(fromTeam, other.fromTeam) &&
Objects.equals(toTeam, other.toTeam) &&
Objects.equals(slot, other.slot);
}
#Override
public int hashCode() {
return Objects.hash(slot, toTeam, fromTeam);
}
}
After using this move and right after this trace message (after cachedMoveList is empty):
2022-09-14 20:13:28,568 [main] TRACE Created cachedMoveList: size (0), moveSelector (MoveListFactory(class pl.medaxtrans.pre.solver.FarSlotChangeMoveFactory)).
All solver threads get blocked in "WAIT" mode and they never stop waiting. What are they waiting for and what did I do wrong? This is expected that this move list ends at some point and I want solver to stop solving then. Why is it not happening as opposed to single thread solving? On the other hand while solving with one thread only I get:
No doable selected move at step index ... Terminating phase early.
And it ends. I want the exact same behavior with multithreading.
I am solving a tough problem in OptaPlanner. The best algorithm I found so far is to use a custom move factory, a computationally intensive one. After noticing that I was utilising a single CPU core, I discovered that OptaPlanner only spreads on multiple threads the score calculation, while it performs the move generation in a single thread.
To mitigate the problem, I implemented the multi-threading in my move factory via the following abstract class, which I then extend with the actual logic (I did this because I actually have three computationally expensive custom move factories):
package my.solver.move;
import lombok.AllArgsConstructor;
import lombok.NonNull;
import org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor;
import org.optaplanner.core.impl.heuristic.move.CompositeMove;
import org.optaplanner.core.impl.heuristic.move.Move;
import org.optaplanner.core.impl.heuristic.selector.move.factory.MoveIteratorFactory;
import org.optaplanner.core.impl.score.director.ScoreDirector;
import java.util.Iterator;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
public abstract class MultiThreadedMoveFactory<T> implements MoveIteratorFactory<T> {
private final ThreadPoolExecutor threadPoolExecutor;
public MultiThreadedMoveFactory(
#NonNull String threadPrefix
) {
int availableProcessorCount = Runtime.getRuntime().availableProcessors();
int resolvedThreadCount = Math.max(1, availableProcessorCount);
ThreadFactory threadFactory = new SolverThreadFactory(threadPrefix);
threadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(resolvedThreadCount, threadFactory);
}
#AllArgsConstructor
public class MoveGeneratorData {
T solution;
SolutionDescriptor<T> solutionDescriptor;
Random random;
BlockingQueue<Move<T>> generatedMoves;
}
protected abstract int getNumMoves();
#Override
public long getSize(ScoreDirector<T> scoreDirector) {
return getNumMoves();
}
protected class MovesIterator implements Iterator<Move<T>> {
private final BlockingQueue<Move<T>> generatedMoves = new ArrayBlockingQueue<>(getNumMoves());
public MovesIterator(
#NonNull T solution,
#NonNull SolutionDescriptor<T> solutionDescriptor,
#NonNull Random random,
#NonNull Function<MoveGeneratorData, Runnable> moveGeneratorFactory
) {
MoveGeneratorData moveGeneratorData = new MoveGeneratorData(solution, solutionDescriptor, random, generatedMoves);
for (int i = 0; i < getNumMoves(); i++) {
threadPoolExecutor.submit(moveGeneratorFactory.apply(moveGeneratorData));
}
}
#Override
public boolean hasNext() {
if (!generatedMoves.isEmpty()) {
return true;
}
while (threadPoolExecutor.getActiveCount() > 0) {
try {
//noinspection BusyWait
Thread.sleep(50);
} catch (InterruptedException e) {
return false;
}
}
return !generatedMoves.isEmpty();
}
#Override
public Move<T> next() {
//noinspection unchecked
return Objects.requireNonNullElseGet(generatedMoves.poll(), CompositeMove::new);
}
}
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private static class SolverThreadFactory implements ThreadFactory {
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
public SolverThreadFactory(String threadPrefix) {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
namePrefix = "MyPool-" + poolNumber.getAndIncrement() + "-" + threadPrefix + "-";
}
#Override
public Thread newThread(#NonNull Runnable r) {
Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
if (t.isDaemon()) {
t.setDaemon(false);
}
if (t.getPriority() != Thread.NORM_PRIORITY) {
t.setPriority(Thread.NORM_PRIORITY);
}
return t;
}
}
#Override
public Iterator<? extends Move<T>> createOriginalMoveIterator(ScoreDirector<T> scoreDirector) {
return createMoveIterator(scoreDirector, new Random());
}
#Override
public Iterator<? extends Move<T>> createRandomMoveIterator(ScoreDirector<T> scoreDirector, Random workingRandom) {
return createMoveIterator(scoreDirector, workingRandom);
}
public abstract Iterator<? extends Move<T>> createMoveIterator(ScoreDirector<T> scoreDirector, Random random);
}
However, the solver seems to hang after a while. The debugger tells me that it's waiting on an innerQueue.take() in OrderByMoveIndexBlockingQueue. This is caused by my move factory: if I revert the above and only use the previous implementation, which was single-threaded, the problem goes away.
I do not quite understand where the problem is, so the question is: how can I fix it?
No, no, no. This approach is doomed. I think. (Prove me wrong.)
JIT selection
First learn about Just In Time selection (see docs) of moves.
Instead of generating all moves (which can be billions) at the beginning of each step, only generate those that will actually be evaluated. Most LS algorithms will only evaluate a few moves per step.
Watch the TRACE log to see how many milliseconds it takes to start a step. Typically you want to do evaluate 10000 moves per second, so it should take 0 or 1 milliseconds to start a step (the log only shows in milliseconds).
Multithreaded solving
Then learn about moveThreadCount to enable multithreaded solving. See this blog post. Know that this still does the move selection on 1 thread, for reproducibility reasons. But the move evaluation is spread across threads.
Caching for move selection
But your custom moves are smart, so the move selection must be smart?
First determine what "solution state" query information you need to generate the moves - for example a Map<Employee, List<Shift>> - then cache that:
either calculate that map at the beginning of each step, if it doesn't take too long (but this won't scale because it doesn't do deltas)
or use a shadow variable (#InverseRelationShadowVariable works fine in this case), because these are updated through deltas. But it does do the delta's for every move and undo move too...
Or hack in an actual new MoveSelector, which can listen to stepEnded() events and actually apply the delta of the last step on that Map, without doing any of the deltas of every move and undo move. We should probably standardize this approach and make it part of our public API some day.
I was able to make the factory work by removing any trace of JIT-ing from hasNext: block the method until all moves have been generated, and only then return true, and keep returning true until all moves have been consumed.
#Override
public boolean hasNext() {
while (!generationComplete && generatedMoves.size() < getNumMoves()) {
try {
// We get a warning because the event we are waiting for could happen earlier than the end of sleep
// and that means we would be wasting time, but that is negligible so we silence it
//noinspection BusyWait
Thread.sleep(50);
} catch (InterruptedException e) {
return false;
}
}
generationComplete = true;
return !generatedMoves.isEmpty();
}
To the best of my understanding, the solution I am using not only works, but it is the best I found in a few months of iterations.
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) {
}
}
I am working on a j2me application which contain a class to find the location of mobile using GPS.I need to include gauge while the location provider API is called and it finds the location.I am new to j2me so still not clear with all the concepts.I am pasting my code below.Please help me through this.Thanks in advance..
package org.ets.utils;
import javax.microedition.lcdui.*;
import javax.microedition.location.*;
import javax.microedition.io.*;
import java.io.*;
import org.ets.midlet.ETS_infozech;
import javax.microedition.midlet.*;
public class Locfinder {
public Locfinder(ETS_infozech midlet)
{
this.midlet = midlet;
}
public static String ex()
{
try {
checkLocation();
} catch (Exception ex)
{
ex.printStackTrace();
}
//System.out.println(string);
return string;
}
public static void checkLocation() throws Exception
{
Location l;
LocationProvider lp;
Coordinates c;
// Set criteria for selecting a location provider:
// accurate to 500 meters horizontally
Criteria cr= new Criteria();
cr.setHorizontalAccuracy(500);
// Get an instance of the provider
lp= LocationProvider.getInstance(cr);
//Request the location, setting a one-minute timeout
l = lp.getLocation(60);
c = l.getQualifiedCoordinates();
if(c != null ) {
// Use coordinate information
double lat = c.getLatitude();
double lon = c.getLongitude();
string = " LAT-" + lat + " LONG-" + lon;
}
}
}
There's no way you can link a Gauge to some task.
You have to set values to the Gauge manually. So you'd create a Gauge and add it to your Form. Then start your code to perform the look-up.
In between your lines of code, you'd add myGauge.setValue(some_value); to increase the indicator.
Of course, this becomes difficult when most of the task is contained in a single line of code, like e.g. lp.getLocation(60);.
I think, in that case, I would create a Thread that automatically increases the value on the Gauge in the 60 seconds, but can be stopped/overridden by a manual setting.
class Autoincrementer implements Runnable {
private boolean running;
private Gauge gauge;
private int seconds;
private int secondsElapsed;
public Autoincrementer(Gauge gauge) {
this.gauge = gauge;
this.seconds = gauge.getMaxValue();
this.running = true;
this.secondsElapsed = 0;
}
public void run() {
if (running) {
secondsElapsed++;
gauge.setValue(secondsElapsed);
if (secondsElapsed>=gauge.getMaxValue()) running = false; // Stop the auto incrementing
try {
Thread.sleep(1000); // Sleep for 1 second
} catch (Exception e) {}
}
}
public void stop() {
running = false;
}
}
You would then create a Gauge and add it to your Form
myGauge = new Gauge("Process", false, 60, 0);
myForm.append(myGauge);
Then start the auto-increment.
myIncrementer = new Autoincrementer(myGauge);
new Thread(myIncrementer).start();
And then call your look-up code.
checkLocation();
Inside your look-up code, add code to stop the auto-incrementing and set the Gauge object to 100%, if the look-up was successful (meaning before the timeout).
myIncrementer.stop();
myGauge.setValue(60);
LWUIT 1.5 can help you in this. Am not sure for Location API which you are using.
But you will get Gauge using LWUIT 1.5. Use Lwuit instead of LCDUI.
http://www.oracle.com/technetwork/java/javame/javamobile/download/lwuit/index.html