I have an Xpage application that uses the extension library where the xsp.extlib.convstate is 'null' for one of three users until they manually refresh page. All three users access application via RDP using Citrix and internet options are the same for all three. Trying to figure out why this would be happening. The application is only on one 9.0.1 server.
From the looks of the source code, if there hasn't been a conversationState initialised yet, the conversationState would not be initialised until either:
after the Render Response phase (in the phase listener: com.ibm.xsp.extlib.component.layout.impl.ApplicationPhaseListener)
#SuppressWarnings("unchecked") // $NON-NLS-1$
public void afterPhase(PhaseEvent event) {
if(event.getPhaseId()==PhaseId.RENDER_RESPONSE) {
// After the render phase, we save the conversion state
ConversationState.saveInSession(event.getFacesContext());
}
}
in the setParent method of the UIApplicationLayout, and this seems to be guarded by a 'isRestoringState' condition, which means I don't think this would run on the first view of a page as there wouldn't be any state to restore.
#Override
public void setParent(UIComponent parent) {
super.setParent(parent);
if( null == parent ){ // removing parent
return;
}
// TODO should move this initialization to initBeforeContents instead
FacesContextEx context = (FacesContextEx) getFacesContext();
if(null != context && !context.isRestoringState()) {
ConversationState cs = ConversationState.get(context, FacesUtil.getViewRoot(this), true);
// Initialize the conversation state
// Set the current navigation path to the UserBean
ApplicationConfiguration conf = findConfiguration();
if(conf!=null) {
String navPath = conf.getNavigationPath();
if(StringUtil.isEmpty(navPath)) {
// If there isn't a navigation path that is defined, the use the default one
if(StringUtil.isEmpty(cs.getNavigationPath())) {
navPath = conf.getDefaultNavigationPath();
}
}
if(StringUtil.isNotEmpty(navPath)) {
cs.setNavigationPath(navPath);
}
}
}
}
So this might explain why it wouldn't be initialised until the 2nd page view.
You could try forcing an initialisation of the ConversationState before you try to use it, maybe in beforePageLoad, by calling one of the com.ibm.xsp.extlib.component.layout.ConversationState's get() methods.
Note the boolean parameter tells the method to create the ConversationState if it does not exist.
I don't do much ServerSide Javascript but I guess this works? The sentiment is correct.
#{javascript: com.ibm.xsp.extlib.component.layout.ConversationState.get(facesContext, true); }
If you are doing it in java then:
ConversationState.get(FacesContext.getInstance(), true);
Does this sound like an explanation of why you are seeing your behaviour?
Related
Regarding the 'Run Allocations by Projects' process - I have a customization where I'd like to add conditions to the Allocation process so that it doesn't execute unless those conditions are met. I've added a checkbox user field to the 'Allocation Rules' screen (PM207500), and I'd like that field to be used (in combination with other criteria) to determine whether or not to actually execute the Allocation for that PMTask row.
I've overriden the PMAllocator.Execute method as follows, but it doesn't seem to be working properly. Here is the code I've come up with in an extension of the PMAllocator graph:
[PXOverride]
public virtual void Execute(List<PMTask> tasks)
{
Base.PreselectAccountGroups();
if (Base.PreSelectTasksTransactions(tasks))
{
foreach (PMTask task in tasks)
{
//Get the allocation id for the task...
var pmalloc = (PMAllocation)PXSelect<PMAllocation,
Where<PMAllocation.allocationID, Equal<Required<PMAllocation.allocationID>>>>.Select(Base, task.AllocationID);
//if (pmalloc == null) return;
//Get the cache extension / user field...
var pmallocext = PXCache<PMAllocation>.GetExtension<PMAllocationExt>(pmalloc);
if (pmallocext.UsrRunAfterProjectCompletion == true)
{
//Get the project...
var pmproj = (PMProject)PXSelect<PMProject,
Where<PMProject.contractID, Equal<Required<PMProject.contractID>>>>.Select(Base, task.ProjectID);
if (pmproj.ExpireDate > DateTime.Today || pmproj.ExpireDate == null)
{
//do nothing..
}
else
{
Base.Execute(task, false);
}
}
else
{
Base.Execute(task, false);
}
}
}
}
But I'm not sure if this is the correct way to do it. It seems to be adding an extra allocation line. I'm not really even sure if the base execute method is NOT being called if I don't explicitly call it here.
Can someone point out the best way of accomplishing this?
When you use just [PXOverride] - base Execute(...) method will be called before yours.
To replace the base method you should specify additional parameter - a delegate. In your case it can be like that:
public delegate void ExecuteDelegate(List<PMTask> tasks);
[PXOverride]
public virtual void Execute(List<PMTask> tasks, ExecuteDelegate BaseExecute)
{
... your code
}
I've got a customization to the Invoice & Memo screen where I execute some custom code (web service calls) when the Release action is activated. This works fine - I knew how to replace the PXAction code and proceeded from there. Now I want to use the Release AR Documents processing screen to do the same thing, but I'm having trouble understanding where / what to override, or where to place my code.
I see the ARDocumentRelease graph constructor with the SetProcessDelegate in the source code, but I'm not sure how to proceed - whether this is where I need to be looking or not. I need to execute my code for each line being released, using the RefNbr in my code.
Since it's an static method, you can't override it. Also, you can't do like it's done in the T300, because you are in processing graph and you can't override the release button with your own. I was able to achieve it by passing callback for each AR document that have been processed.
You can call the Initialize method of the ARDocumentRelease graph to override the logic like you said. After you just have to call ReleaseDoc that uses a callback parameter instead of using the default one.
Here's the code that I came with:
public class ARDocumentRelease_Extension : PXGraphExtension<ARDocumentRelease>
{
public override void Initialize()
{
ARSetup setup = Base.arsetup.Current;
Base.ARDocumentList.SetProcessDelegate(
delegate (List<BalancedARDocument> list)
{
List<ARRegister> newlist = new List<ARRegister>(list.Count);
foreach (BalancedARDocument doc in list)
{
newlist.Add(doc);
}
AddAdditionalLogicToRelease(newlist);
}
);
Base.ARDocumentList.SetProcessCaption("Release");
Base.ARDocumentList.SetProcessAllCaption("Release All");
}
public delegate void PostPorcessing(ARRegister ardoc, bool isAborted);
private void AddAdditionalLogicToRelease(List<ARRegister> newlist)
{
ARDocumentRelease.ReleaseDoc(newlist, true, null, delegate(ARRegister ardoc, bool isAborted) {
//Add your logic to handle each document
//Test to check if it was not aborted
});
}
}
Please note that you must always call static methods from within long running process and create necessary objects there.
Processing delegate logic is implemented as long running process which creates worker thread to execute the processing logic.
You have AddAdditionalLogicToRelease() method which requires object instance in order to call and will fail during thread context switches and hence the issue. So, you must have create object instance inside the thread context and then call instance method.
In general, method that gets called from long running processes are declared static and required objects/graphs are created inside this static method to do some work. See below example how to properly override ARDocumentRelease graph for this purpose:
public class ARDocumentRelease_Extension : PXGraphExtension<ARDocumentRelease>
{
public override void Initialize()
{
Base.ARDocumentList.SetProcessDelegate(
delegate (List<BalancedARDocument> list)
{
List<ARRegister> newlist = new List<ARRegister>(list.Count);
foreach (BalancedARDocument doc in list)
{
newlist.Add(doc);
}
// use override that allows to specify onsuccess routine
ARDocumentRelease.ReleaseDoc(newlist, true, null, (ardoc, isAborted) =>
{
//Custom code here, such as create your GL
});
}
);
}
}
I think it's the function
public static void ReleaseDoc(List<ARRegister> list, bool isMassProcess, List<Batch> externalPostList, ARMassProcessDelegate onsuccess)
under ARDocumentRelease businesss logic.
I have an MVC application in which I have a controller that receives data from the user and then uploads a file to Azure blob storage. The application is using Unity IoC to handle dependency injection.
During the workflow I have isolated the following code as demonstrating the problem
public class MvcController : Controller
{
private IDependencyResolver _dependencyResolver;
public MvcController() : this(DependencyResolver.Current)
{
}
public MvcController(IDependencyResolver dependencyResolver)
{
this._dependencyResolver = dependencyResolver;
}
public GetService<T>()
{
T resolved = _dependencyResolver.GetService<T>()
if (resolved == null)
throw new Exception(string.Format("Dependency resolver does not contain service of type {0}", typeof(T).Name));
return resolved;
}
}
public class MyController : MvcController
{
[NoAsyncTimeout]
public async Task<ActionResult> SaveFileAsync(/* A bunch of arguments */)
{
/* A bunch of code */
//This line gets a concrete instance from HttpContext.Current successfully...
IMyObject o = GetService<IMyObject>();
await SaveFileToAzure(/* A bunch of parameters */);
.
.
/* Sometime later */
Method2(/* A bunch of parameters */);
}
private Method2(/* A bunch of parameters */)
{
//This line fails because HttpContext.Current is null
IMyObject o = GetService<IMyObject>();
/* A bunch of other code */
}
private async Task SaveFileToAzure(/* A bunch of parameters */)
{
//Grab a blob container to store the file data...
CloudBlobContainer blobContainer = GetBlobContainer();
ICloudBlob blob = blobContainer.GetBlockBlobReference(somePath);
Stream dataStream = GetData();
System.Threading.CancellationToken cancelToken = GetCancellationToken();
//All calls to DependencyResolver.GetService<T>() after this line of code fail...
response = await blob.UploadStreamAsync(dataStream, cancelToken);
/* A bunch of other code */
}
}
Unity has a registration for my object:
container.RegisterType<IMyObject, MyObject>(new HttpLifetimeManager());
My lifetime manager is defined as follows:
public sealed class HttpRequestLifetimeManager : LifetimeManager
{
public Guid Key { get; private set; }
public HttpRequestLifetimeManager()
{
this.Key = Guid.NewGuid();
}
public override object GetValue()
{
return HttpContext.Current.Items[(object)this.Key];
}
public override void SetValue(object newValue)
{
HttpContext.Current.Items[(object)this.Key] = newValue;
}
public override void RemoveValue()
{
HttpContext.Current.Items.Remove((object)this.Key);
}
}
Nothing complicated.
Stepping into the HttpRequestLifetimeManager on the failing GetService() calls shows that after the UploadStreamAsync() call HttpContext.Current is null...
Has anyone else come across this problem? If so, is this a bug? Is this expected behaviour? Am I doing something out of the ordinary? What should I do to resolve it?
I can hack around it by storing a reference to HttpContext.Current prior to the offending call and restoring it after, but that doesn't seem like the right approach.
Any ideas?
To echo #Joachim - http context may not be available to your async thread. Compare the current thread id where you can see httpcontext is available, to the thread id where you can see that it isn't - i'm assuming you will see they are 2 different threads. If my assumption is correct this may be a sign that your main thread (the one with httpcontext) does not have a "synchronizationcontext". (you can see http://blogs.msdn.com/b/pfxteam/archive/2012/01/20/10259049.aspx for more details of how that works) If so, it may mean that the code immediately after your await statement is actually not running on the same thread as the code prior to the await statement! So from your perspective, one moment you have http context and the next you don't because execution has actually been switched to another thread! You should probably look at implementing / setting a synchronizationcontext on your main thread if that's the case and then control will be returned to your original thread with http context and that should fix your problem, or alternatively you could retrieve your object from http context on the original thread and find a way to pass it as a parameter to the async method/s so that they don't need to access http context to get their state.
My application has a save and retrieve function. I have the save/retrieve working in that the objects are saved to a database and retrieved correctly. However, in my retrieve landing page, depending on the state of the saved application, I either want to validate some details with the user, or silently navigate to the last accessed view. The latter is where I'm having trouble.
We're using spring beans and in my SaveAndRetrieve page bean I have:
#PostConstruct
public void initialise() {
caseNotFound = false;
caseReference = saveAndRetrieveActionHandler.getRequestedCaseReference();
LOGGER.debug("Retrieve initialise. Case ref is {}", caseReference);
if (caseReference != null) {
try {
saveAndRetrieveActionHandler.retrieveApplicationByCaseRef();
LOGGER.debug("Retrieve initialise - case found");
final NavigationOutcome outcome = saveAndRetrieveActionHandler.getLastAccessedView();
if (outcome.getApplicationState() == ApplicationState.QUOTE) {
LOGGER.info("Quote retrieved, navigating to view");
// HERE IS WHERE THE TROUBLE LIES! THIS DOESNT WORK
FacesUtils.setNextViewNavigation(outcome.getViewId());
}
} catch (final FrameworkException fe) {
LOGGER.debug("Exception caught {}", fe);
caseNotFound = true;
}
}
}
outcome is an enumeration containing amongst other things the view I need to navigate to, and the application state (another enumeration). If applicationState is quote, I want to silently navigate. For all other applicationStates I want to challenge the user to verify them.
My facesUtils method is:
public static void setNextViewNavigation(final String p_lastAccessedViewId) {
if (p_lastAccessedViewId != null) {
getCurrentViewRoot().setViewId(p_lastAccessedViewId);
}
}
I've also tried calling this method
public static void navigateToOutcome(final String p_outcome) {
final FacesContext context = getFacesContext();
final NavigationHandler navigationHandler = context.getApplication().getNavigationHandler();
navigationHandler.handleNavigation(context, null, p_outcome);
}
Despite my efforts, I'm seeing the landing page wheras I want to silently navigate to the saved page
Basically I want to abort the current lifecycle and reset the viewroot to the saved view. (note I am not saving the component tree itself, just my business objects)
One more piece of information, this is jsf1.2, but with facelets. I cannot use any jsf2 specific functionality, nor can I use any third party JSF extenstions.
Help please!
We solved this by using a ui:include tag with the src attribute being a jsf method that determines the name of the page to navigate to.
I have the following (here simplified) code which I want to test with FakeItEasy.
public class ActionExecutor : IActionExecutor
{
public void TransactionalExecutionOf(Action action)
{
try
{
// ...
action();
// ...
}
catch
{
// ...
Rollback();
}
}
public void Commit()
{ }
public void Rollback()
{ }
}
public class Service : IService
{
private readonly IRepository _repository;
private readonly IActionExecutor _actionExecutor;
// ctor for CI
public void ServiceMethod(string name)
{
_actionExecutor.TransactionalExecutionOf(() =>
{
var item = _repository.FindByName(ItemSpecs.FindByNameSpec(name));
if (item == null) throw new ServiceException("Item not found");
item.DoSomething();
_actionExecutor.Commit();
}
}
}
I want to test that the ServiceException is thrown so i setup my test like that
var repo = A.Fake<IRepository>();
A.CallTo(() => repo.FindByName(A<ISpec<Item>>.Ignored))
.Returns(null);
var executor = A.Fake<IActionExecutor>();
executor.Configure()
.CallsTo(x => x.Rollback()).DoesNothing();
executor.Configure()
.CallsTo(x => x.Commit()).DoesNothing();
executor.Configure()
.CallsTo(x => x.TransactionalExecutionOf(A<Action>.Ignored))
.CallsBaseMethod();
With the following code
var service = new Service(executor, repo);
service.ServiceMethod("notExists")
.Throws(new ServiceException());
I get the following message
The current proxy generator can not intercept the specified method
for the following reason:
- Sealed methods can not be intercepted.
If I call the method directly on the service like
var service = new Service(executor, repo);
service.ServiceMethod("NotExists");
I get this message
This is a DynamicProxy2 error: The interceptor attempted to 'Proceed'
for method 'Void TransactionalExecutionOf(System.Action)' which has no
target. When calling method without target there is no implementation
to 'proceed' to and it is the responsibility of the interceptor to
mimic the implementation (set return value, out arguments etc)
Now I am a bit confused and don't know what to do next.
Problems comes from the way you create fake and what you later expect it to do:
var executor = A.Fake<IActionExecutor>();
// ...
executor.Configure()
.CallsTo(x => x.TransactionalExecutionOf(A<Action>.Ignored))
.CallsBaseMethod();
What base method? FakeItEasy has no idea what the base class is, and hence the DynamicProxy2 exception in your second case. You can create partial mock this way:
var executor = A.Fake<ActionExecutor>();
Note that we're basing on actual implementation, not interface anymore
This however introduces a new set of problems, as methods on ActionExecutor are not virtual and therefore interceptor cannot hook up to well - intercept them. To make your current setup work, you'll have to change your ActionExecutor and make (all) the methods virtual.
However, you may (or even should) want to avoid modifications of existing code (which sometimes might not even be an option). You could then set up your IActionExecutor fake like this:
var executor = A.Fake<IActionExecutor>();
A.CallTo(() => executor.TransactionalExecutionOf(A<Action>.Ignored))
.Invokes(f => new ActionExecutor()
.TransactionalExecutionOf((Action)f.Arguments.First())
);
This will allow you to work on faked object, with the exception of call to TransactionalExecutionOf which will be redirected to actual implementation.