How to retrieve the process's unique workflow number, while launching it through C# API? - filenet-p8

I'm struggling for 4 days now.
There's this C# Process Engine API:
https://www.ibm.com/support/knowledgecenter/en/SSNW2F_5.2.1/com.ibm.p8.pe.dev.doc/web_services/ws_reference.htm
What I need to do is to retrieve the WorkflowNumber when launching the workflow, so later I can find that process in the system.
The issue here is that when you launch it - it returns the LaunchStep (the first step in the workflow) which doesn't have that ID assigned yet - it's null. The only thing available is the LaunchStep's WOBNumber.
In order to assign the Workflow ID to the step, you need to dispatch the step, so I do that:
UpdateStepRequest request = new UpdateStepRequest();
UpdateFlagEnum flag = UpdateFlagEnum.UPDATE_DISPATCH;
request.updateFlag = flag;
request.stepElement = element; // add the launch step
session.Client.updateStep(request);
And here the funny part happens. From this point, there is complately no option to retrieve that, because StepElements are stateless, updateStep() returns nothing and the best part - the LaunchStep is now destroyed in the system, because it's a LaunchStep - it just gets destroyed after the launch.
Any tips would be appreciated!

Related

Epicor 10 - Changing the plant for the current session

We are starting to use Epicor 10 and we are wanting to leverage the DLLs/Services to talk to Epicor. That way we can add/update info from a custom app. So far things are going fine but we ran into an issue when we wanted to, lets say, add a job for a specific plant. It seems to always want to save to the plant the user last logged into via the client app.
So for example -- Lets say the user's last plant was plant "A". I want my custom app to log into Epicor (creating a session) and create a job for plant "B". I can add the job fine, but it will put it under plant "A" and not "B", even though I logged into plant "B" when I created the session.
We are calling SetPlant and passing in the right plant we want, but Epicor seems to always override what plant to save it as. Has anyone else run into this case? We are also having this issue with the Company. SetCompany doesnt seem to work at all.
More info: - We are using the net.tcp:///ERP/ICE/Lib/SessionMod.svc service. - We can login fine with Login() and get a SessionId back - Even calling GetValues() on the Session object says we are logged into plant "B" even though Epicor will still use plant "A".
The only work around we can come up with, which we do not want to do, is to have an app user by company and by plant so we can guarantee which company and plant the data gets saved to. This will work but it isnt ideal.
Anyone have any suggestions on how to get the system to take the new Company or Plant?
As user463132 points out, you can wrap your service connection with the temporary session context:
using (CallContext.Current.TemporarySessionCreator.SetCompanyID("YourCompanyHere").SetPlantID("B").Create())
{
}
Haso Keric Article Reference
I'll also add that if you are using the UI adapters, you can simply grab the session from the oTrans object instance and set the properties here which governs how oTrans interacts with your data.
Ice.Core.Session s1 = (Ice.Core.Session)this.oTrans.Session;
s1.CompanyID = "YourCompanyHere";
s1.PlantID = "B";
The plantID can be updated in the session using the SetPlant() method. It must be called after the SetCompany():
sessionModImpl = NetTcp_Helper.ClassAttributHelper.CreateBusObj<SessionModImpl>(Guid.Empty, SessionModImpl.UriPath, Settings);
sessionId = sessionModImpl.Login();
sessionModImpl.SessionID = sessionId;
sessionModImpl.SetCompany(epicorCompanyID, out companyName, out plantID, out plantName, out workstationID, out workstationDescription, out employeeID,
out countryGroupCode, out countryCode, out tenantID);
sessionModImpl.SetPlant(newSiteID, out plantName);
The first thing to understand is that a Server Session is not a single
instance but rather a .NET Stack of Session Instances. The
‘CallContext.Current.Session’ variable is just a pointer to the top of
the Stack. In most cases, there is just a single Session instance in
the CallContext stack. But when you need to iterate over Companies to
process something the Session Stack get pushed and popped around.
That’s where ‘TemporarySessions’ comes in.
Read about it at:
https://www.linkedin.com/pulse/snippet-epicor-change-companyplantuser-bpm-haso-keric/

Non helpfull error message Calabash with page objects pattern

I'm currently using Calabash framework to automate functional testing for a native Android and IOS application. During my time studying it, I stumbled upon this example project from Xamarin that uses page objects design pattern which I find to be much better to organize the code in a Selenium fashion.
I have made a few adjustments to the original project, adding a file called page_utils.rb in the support directory of the calabash project structure. This file has this method:
def change_page(next_page)
sleep 2
puts "current page is #{current_page_name} changing to #{next_page}"
#current_page = page(next_page).await(PAGE_TRANSITION_PARAMETERS)
sleep 1
capture_screenshot
#current_page.assert_info_present
end
So in my custom steps implementation, when I want to change the page, I trigger the event that changes the page in the UI and update the reference for Calabash calling this method, in example:
#current_page.click_to_home_page
change_page(HomePage)
PAGE_TRANSITION_PARAMETERS is a hash with parameters such as timeout:
PAGE_TRANSITION_PARAMETERS = {
timeout: 10,
screenshot_on_error: true
}
Just so happens to be that whenever I have a timeout waiting for any element in any screen during a test run, I get a generic error message such as:
Timeout waiting for elements: * id:'btn_ok' (Calabash::Android::WaitHelpers::WaitError)
./features/support/utils/page_utils.rb:14:in `change_page'
./features/step_definitions/login_steps.rb:49:in `/^I enter my valid credentials$/'
features/04_support_and_settings.feature:9:in `And I enter my valid credentials'
btn_ok is the id defined for the trait of the first screen in my application, I don't understand why this keeps popping up even in steps ahead of that screen, masking the real problem.
Can anyone help getting rid of this annoyance? Makes really hard debugging test failures, specially on the test cloud.
welcome to Calabash!
As you might be aware, you'll get a Timeout waiting for elements: exception when you attempt to query/wait for an element which can't be found on the screen. When you call page.await(opts), it is actually calling wait_for_elements_exist([trait], opts), which means in your case that after 10 seconds of waiting, the view with id btn_ok can't be found on the screen.
What is assert_info_present ? Does it call wait_for_element_exists or something similar? More importantly, what method is actually being called in page_utils.rb:14 ?
And does your app actually return to the home screen when you invoke click_to_home_page ?
Unfortunately it's difficult to diagnose the issue without some more info, but I'll throw out a few suggestions:
My first guess without seeing your application or your step definitions is that #current_page.click_to_home_page is taking longer than 10 seconds to actually bring the home page back. If that's the case, simply try increasing the timeout (or remove it altogether, since the default is 30 seconds. See source).
My second guess is that the element with id btn_ok is not actually visible on screen when your app returns to the home screen. If that's the case, you could try changing the trait definition from * id:'btn_ok' to all * id:'btn_ok' (the all operator will include views that aren't actually visible on screen). Again, I have no idea what your app looks like so it's hard to say.
My third guess is it's something related to assert_info_present, but it's hard to say without seeing the step defs.
On an unrelated note, I apologize if our sample code is a bit outdated, but at the time of writing we generally don't encourage the use of #current_page to keep track of a page. Calabash was written in a more or less stateless manner and we generally encourage step definitions to avoid using state wherever possible.
Hope this helps! Best of luck.

Basic Sidekiq Questions about Idempotency and functions

I'm using Sidekiq to perform some heavy processing in the background. I looked online but couldn't find the answers to the following questions. I am using:
Class.delay.use_method(listing_id)
And then, inside the class, I have a
self.use_method(listing_id)
listing = Listing.find_by_id listing_id
UserMailer.send_mail(listing)
Class.call_example_function()
Two questions:
How do I make this function idempotent for the UserMailer sendmail? In other words, in case the delayed method runs twice, how do I make sure that it only sends the mail once? Would wrapping it in something like this work?
mail_sent = false
if !mail_sent
UserMailer.send_mail(listing)
mail_sent = true
end
I'm guessing not since the function is tried again and then mail_sent is set to false for the second run through. So how do I make it so that UserMailer is only run once.
Are functions called within the delayed async method also asynchronous? In other words, is Class.call_example_function() executed asynchronously (not part of the response / request cycle?) If not, should I use Class.delay.call_example_function()
Overall, just getting familiar with Sidekiq so any thoughts would be appreciated.
Thanks
I'm coming into this late, but having been around the loop and had this StackOverflow entry appearing prominently via Google, it needs clarification.
The issue of idempotency and the issue of unique jobs are not the same thing. The 'unique' gems look at the parameters of job at the point it is about to be processed. If they find that there was another job with the same parameters which had been submitted within some expiry time window then the job is not actually processed.
The gems are literally what they say they are; they consider whether an enqueued job is unique or not within a certain time window. They do not interfere with the retry mechanism. In the case of the O.P.'s question, the e-mail would still get sent twice if Class.call_example_function() threw an error thus causing a job retry, but the previous line of code had successfully sent the e-mail.
Aside: The sidekiq-unique-jobs gem mentioned in another answer has not been updated for Sidekiq 3 at the time of writing. An alternative is sidekiq-middleware which does much the same thing, but has been updated.
https://github.com/krasnoukhov/sidekiq-middleware
https://github.com/mhenrixon/sidekiq-unique-jobs (as previously mentioned)
There are numerous possible solutions to the O.P.'s email problem and the correct one is something that only the O.P. can assess in the context of their application and execution environment. One would be: If the e-mail is only going to be sent once ("Congratulations, you've signed up!") then a simple flag on the User model wrapped in a transaction should do the trick. Assuming a class User accessible as an association through the Listing via listing.user, and adding in a boolean flag mail_sent to the User model (with migration), then:
listing = Listing.find_by_id(listing_id)
unless listing.user.mail_sent?
User.transaction do
listing.user.mail_sent = true
listing.user.save!
UserMailer.send_mail(listing)
end
end
Class.call_example_function()
...so that if the user mailer throws an exception, the transaction is rolled back and the change to the user's flag setting is undone. If the "call_example_function" code throws an exception, then the job fails and will be retried later, but the user's "e-mail sent" flag was successfully saved on the first try so the e-mail won't be resent.
Regarding idempotency, you can use https://github.com/mhenrixon/sidekiq-unique-jobs gem:
All that is required is that you specifically set the sidekiq option
for unique to true like below:
sidekiq_options unique: true
For jobs scheduled in the future it is possible to set for how long
the job should be unique. The job will be unique for the number of
seconds configured or until the job has been completed.
*If you want the unique job to stick around even after it has been successfully processed then just set the unique_unlock_order to
anything except :before_yield or :after_yield (unique_unlock_order =
:never)
I'm not sure I understand the second part of the question - when you delay a method call, the whole method call is deferred to the sidekiq process. If by 'response / request cycle' you mean that you are running a web server, and you call delay from there, so all the calls within the use_method are called from the sidekiq process, and hence outside of that cycle. They are called synchronously relative to each other though...

How to update Oracle BPMN flow diagram during execution

I'm working with Oracle BPMN (Fusion middleware), using JDeveloper to create BPMN processes, and writing Java code for a custom page to display the flow diagram for running processes. The problem being encountered is that the BPMN diagrams do not display/update until they hit certain trigger events (apparently asynchronous event points). So in many cases the diagrams do not even show up in a query until the BPMN process completes. Note we don't normally have user input tasks, which qualify as async events and also result in the diagram then showing up.
Our team has talked to Oracle about it and their solution was to wrap every BPMN call (mostly service calls) in asynchronous BPEL wrappers, so that the BPMN calls an async request/response (thus as two actions) that calls the service. Doing this does work, but it adds a huge overhead to the effort of developing BPMN processes, because every action has to be wrapped.
So I'm wondering if anyone else has explored or potentially solved this problem.
Some code snippets of what we're doing (partial code only):
To get the running instance IDs:
List<Column> columns = new ArrayList<Column>();
columns.add(...); // repeated for all relevant fields
Ordering ...
Predicate ...
IInstanceQueryInput input = new IInstanceQueryInput();
List<IProcessInstance> instances = client.getInstanceQueryService().queryProcessInstances(context, columns, predicate, ordering, input);
// however, instances doesn't return the instance until the first async event, or until completion
After that the AuditProcessDiagrammer is used to get the flow diagram, and DiagramEvents uesd to update / highlight the flow in progress. The instanceId does show up in the Oracle fusion control panel, so it must at least potentially be available. But trying to get an image for it results in a null image:
IProcessInstance pi = client.getInstanceQueryService().getProcessInstance(context, instance);
// HERE --> pi is null until the image is available (so the rest of this isn't run)
String compositeDn = pi.getSca().getCompositeDN();
String componentName = pi.getSca().getComponentName();
IProcessModelPackage package = client.getProcessModelService().getProcessModel(context, compositeDn, componentName);
ProcessDiagramInfo info = new ProcessDiagramInfo();
info.setModelPackage(package);
AuditProcessDiagrammer dg = new AuditProcessDiagrammer(info.getModelPackage().getProcessModel().getProcess());
List<IAuditInstance> audits = client.getInstanceQueryService().queryAuditInstanceByProcessId(context, instance);
List<IDiagramEvent> events = // function to get these
dg.highlight(events);
String base64image = dg.getImage();
See the HERE --> part. That's where I need instance to be valid.
If there are good alternatives (setting, config, etc...) that others have successfully used, I'd love to hear it. I'm really not interested in strange workarounds (already have that in the BPEL wrapper). I'm looking for a solution that allows the BPMN process flow to remain simple. Thanks.

Visual Studio project remains "stuck" when stopped

Currently developing a connector DLL to HP's Quality Center. I'm using their (insert expelative) COM API to connect to the server. An Interop wrapper gets created automatically by VStudio.
My solution has 2 projects: the DLL and a tester application - essentially a form with buttons that call functions in the DLL. Everything works well - I can create defects, update them and delete them. When I close the main form, the application stops nicely.
But when I call a function that returns a list of all available projects (to fill a combo box), if I close the main form, VStudio still shows the solution as running and I have to stop it.
I've managed to pinpoint a single function in my code that when I call, the solution remains "hung" and if I don't, it closes well. It's a call to a property in the TDC object get_VisibleProjects that returns a List (not the .Net one, but a type in the COM library) - I just iterate over it and return a proper list (that I later use to fill the combo box):
public List<string> GetAvailableProjects()
{
List<string> projects = new List<string>();
foreach (string project in this.tdc.get_VisibleProjects(qcDomain))
{
projects.Add(project);
}
return projects;
}
My assumption is that something gets retained in memory. If I run the EXE outside of VStudio it closes - but who knows what gets left behind in memory?
My question is - how do I get rid of whatever calling this property returns? Shouldn't the GC handle this? Do I need to delve into pointers?
Things I've tried:
getting the list into a variable and setting it to null at the end of the function
Adding a destructor to the class and nulling the tdc object
Stepping through the tester function application all the way out, whne the form closes and the Main function ends - it closes, but VStudio still shows I'm running.
Thanks for your assistance!
Try to add these 2 lines to post-build event:
call "$(DevEnvDir)..\Tools\vsvars32.bat"
editbin.exe /NXCOMPAT:NO "$(TargetPath)"
Have you tried manually releasing the List object using System.Runtime.InteropServices.Marshal.ReleaseComObject when you are finished with it ?
I suspect some dangling threads.
When this happens, pause the process in the debugger and see what threads are still around.
May be try to iterate the list manually using it's count and Item properties instead of using it's iterator, some thing like:
for (int i=1; i <= lst.Count ; ++i)
{
string projectName = lst.Item(i);
}
It might be the Iterator that keeps it alive and not the list object itself, if not using an iterator might not have a problem.

Resources