SessionAsSigner & restoreWrappedDocument() - xpages

Faced with the following issue: I am actively use DominoDocument class (wrapped Document) in my projects, particularly as basis for my business model objects.
Very often I have a need to access / iterate my business model objects as Anonymous user thus underlying lotus.domino.Document retrieved based on SessionAsSigner session object (for example in case of some REST Services, or in case of xAgent, etc).
The behavior of restoreWrappedDocument() method in such cases really breaks all flexibility of using such architecture: this method tries to restore wrapped document based on current execution environment access rights, and of course that causes errors with ACL.
Let’s consider the following code snippet as example:
public void test3() {
try {
System.out.println(">>>>>");
System.out.println(">>>>> START");
lotus.domino.Database db = AppBean.getSessionAsSigner().getDatabase(AppBean.getInstance().getContactsDBserverName(), AppBean.getInstance().getContactsDBname(), false);
Document notesDoc = db.getAllDocuments().getFirstDocument();
String dbName = notesDoc.getParentDatabase().getServer() + "!!" + notesDoc.getParentDatabase().getFilePath();
DominoDocument ds = DominoDocument.wrap(dbName, notesDoc, null, "exception", false, "UseWeb", null);
System.out.println(">> 1 >> " + ds.getValue("form"));
ds.getDocument().recycle();
try {
ds.restoreWrappedDocument();
}catch(Throwable e2){
System.out.println(">> 2 - exception - >> " + e2.toString());
e2.printStackTrace();
}
try {
System.out.println(">> 3 >> " + ds.getValue("form"));
}catch(Throwable e3){
System.out.println(">> 3 - exception - >> " + e3.toString());
}
System.out.println(">>>>> END");
System.out.println(">>>>>");
}catch(Exception e){
e.printStackTrace();
}
}
1) Scenario 1: executing this code by authenticated user that has access to target DB gives the following result:
So method works as expected and everything perfect.
2) Scenario 2: executing this code by Anonymous user causes Exception (generally, what is expected):
You can clearly see that restoreWrappedDocument() executes some helper methods in order to get DB, and of course that is done with current user access level (Anonymous).
Possible solutions:
The obvious solution is to add custom logic to my business object model, which will perform custom restore (basically based on Server&DB names and document UNID or NoteID).
What I am very curious whether there is any more smart or built-in method exist for restoring wrapped documents with SessionAsSigner rights?
Thanks!

I don't think there's a proper way to do this, other than your option 1, for better or for worse.
However, and I'm not saying this is a good idea, it seems like DominoDocument likely gets to its session through the current request map. If you want to be tricky, you could try temporarily swapping session out for sessionAsSigner in the request scope, calling restoreWrappedDocument, and then swapping it back.

A solution with a Helper class using Java Reflection:
(Incomplete, missing some parts)
package ch.hasselba.xpages;
import java.lang.reflect.Field;
import lotus.domino.Database;
import lotus.domino.Document;
import lotus.domino.NotesException;
import com.ibm.xsp.FacesExceptionEx;
import com.ibm.xsp.model.domino.DominoUtils;
import com.ibm.xsp.model.domino.wrapped.DominoDocument;
public class DominoDocumentUtil {
private static final long serialVersionUID = 1L;
private transient final Field wrappedObj;
private transient final DominoDocument dominoDoc;
public DominoDocumentUtil(DominoDocument doc) throws SecurityException,
NoSuchFieldException {
dominoDoc = doc;
wrappedObj= doc.getClass().getDeclaredField("_wrappedObject");
wrappedObj.setAccessible(true);
}
public void restoreWrappedDocument(Database db)
throws IllegalArgumentException, IllegalAccessException {
try {
Document doc = DominoUtils.getDocumentById(db, dominoDoc
.getDocumentId(), dominoDoc.isAllowDeletedDocs());
this.wrappedObj.set(dominoDoc, doc);
} catch (NotesException ne) {
throw new FacesExceptionEx(ne.getMessage());
}
}
}
To use the class you can call the restoreWrappedDocument method with a database opened with sessionAsSigner:
DominoDocumentUtil util = new DominoDocumentUtil(ds);
util.restoreWrappedDocument(db);

Related

ArchUnit: Verify method only calls one outside method

In a Controller-Service-Datalayer architecture, I'm searching for a way to verify that my controller methods perform exactly one call to the service layer like this:
#DeleteMapping(value = "/{id}")
public ResponseEntity<String> deleteBlubber(#PathVariable("id") long blubberId) {
service.deleteBlubber(blubberId);
return new ResponseEntity<>("ok", HttpStatus.OK);
}
This should not be allowed:
#DeleteMapping(value = "/{id}")
public ResponseEntity<String> deleteBlubber(#PathVariable("id") long blubberId) {
service.deleteOtherStuffFirst(); // Opens first transaction
service.deleteBlubber(blubberId); // Opens second transaction - DANGER!
return new ResponseEntity<>("ok", HttpStatus.OK);
}
As you can see from the comments, the reason for this is to make sure that each request is handled in one transaction (that is started in the service layer), not multiple transactions.
It seems that ArchUnit can only check meta-data from classes and methods and not what's actually going on in a method. I would have to be able to count the request to the service classes, which seems to not be possible in ArchUnit.
Any idea if this might be possible? Thanks!
With JavaMethod.getMethodCallsFromSelf() you have access to all methods calls of a given method. This could be used inside a custom ArchCondition like this:
methods()
.that().areDeclaredInClassesThat().areAnnotatedWith(Controller.class)
.should(new ArchCondition<JavaMethod>("call exactly one service method") {
#Override
public void check(JavaMethod item, ConditionEvents events) {
List<JavaMethodCall> serviceCalls = item.getMethodCallsFromSelf().stream()
.filter(call -> call.getTargetOwner().isAnnotatedWith(Service.class))
.toList();
if (serviceCalls.size() != 1) {
String message = serviceCalls.stream().map(JavaMethodCall::getDescription).collect(joining(" and "));
events.add(SimpleConditionEvent.violated(item, message));
}
}
})

Filtering out soft deletes with AutoQuery

I'm using ServiceStack with OrmLite, and having great success with it so far. I'm looking for a way to filter out 'soft deleted' records when using AutoQuery. I've seen this suggestion to use a SqlExpression, but I'm not sure where you would place that. In the AppHost when the application starts? I did that, but the deleted records still return. My QueryDb request object in this case is as follows:
public class QueryableStore : QueryDb<StoreDto>
{
}
Other SqlExpressions I've used are in the repository class itself, but being that I'm using QueryDb and only the message itself (not leveraging my repository class) I don't have any other code in place to handle these messages and filter out the 'deleted' ones.
I've also tried using a custom service base as suggested by this approach as well, using the following:
public abstract class MyCustomServiceBase : AutoQueryServiceBase
{
private const string IsDeleted = "F_isdeleted";
public override object Exec<From>(IQueryDb<From> dto)
{
var q = AutoQuery.CreateQuery(dto, Request);
q.And("{0} = {1}", IsDeleted, 0);
return AutoQuery.Execute(dto, q);
}
public override object Exec<From, Into>(IQueryDb<From, Into> dto)
{
var q = AutoQuery.CreateQuery(dto, Request);
q.And("{0} = {1}", IsDeleted, 0);
return AutoQuery.Execute(dto, q);
}
}
This code gets called, but when the Execute call happens I get an error:
System.ArgumentException: 'Conversion failed when converting the varchar value 'F_isdeleted' to data type int.'
The F_isdeleted column is a 'bit' in SQL Server, and represented as a bool in my POCO.
Any ideas on what would work here? I'm kind of at a loss that this seems this difficult to do, yet the docs make it look pretty simple.
The {0} are placeholders for db parameters, so your SQL should only be using placeholders for DB parameters, e.g:
var q = AutoQuery.CreateQuery(dto, Request);
q.And(IsDeleted + " = {0}", false);
Otherwise if you want to use SQL Server-specific syntax you can use:
q.And(IsDeleted + " = 0");

How can I execute code from the Release / Release All buttons in the Release AR Documents screen

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.

Allow only one user to access a page at a time in struts application

Having an huge customers profile page if two or more users start using same page and start editing big change will happen in my database so planing to implement Threads concept where only one user can use that customer page
i'm aware about threads concept but confused how to implement it
hope i need to use Singleton class as well
Any suggestion or Logic's will be helpful
I'm using Struts,Hibernate frame work
You may use application context to store a flag variable. Action will use its value to allow only one simultaneous execution.
public class TestAction extends ActionSupport implements ApplicationAware {
private static final String APP_BUSY_KEY = "APP_BUSY";
Map<String, Object> map;
#Override
public void setApplication(Map<String, Object> map) {
this.map = map;
}
#Override
public String execute() throws Exception {
if (map.containsKey(APP_BUSY_KEY)) {
return ERROR;
} else {
map.put(APP_BUSY_KEY, "1");
try {
// action logic here
} finally {
map.remove(APP_BUSY_KEY);
}
return SUCCESS;
}
}
}
If you plan to implement similar logic for two requests (lock after displaying values and release lock after submitting new values) then logic will be more complex and you will also need to handle lock release after timeout.

Geb Reporter / Extension to retrieve Test Status

I am attempting to replace some custom java selenium extensions by utilizing geb. I have hit a bit of a brick wall when I attempt to utilize a grid in the cloud (i.e. SauceLabs). When my tests complete, it'd be nice to send an update back to indicate whether or not the test has failed or succeeded. To utilize this, I need the sessionId from the RemoteWebDriver instance. This can be obtained in a custom Reporter, however I can't determine the success with this interface. Since I am extending the GebReportingSpec, I attempted to create my own custom version, which had a custom Junit rule to track success or failure:
public class TestSuccess extends TestWatcher {
boolean success;
String message;
#Override
protected void starting(Description d) {
message = d.getMethodName();
}
#Override
protected void succeeded(final Description description) {
System.out.println("Test Success [succeeded] " + description);
this.success = true;
}
#Override
protected void failed(final Throwable e, final Description description) {
System.out.println("Test Success [failed] " + description);
this.success = false;
}
public boolean isSuccess() {
return success;
}
#Override
public String toString() {
return message + " success: <" + success + ">.";
}
}
I then added that to my CustomReportingSpec:
class CustomReportingSpec extends GebReportingSpec {
/* I also tried creating this as a RuleChain with:
* #Rule TestRule chain = RuleChain.outerRule(
super._gebReportingSpecTestName).around(new TestSuccess());
* however, this results in a NPE. Placing the super rule in the around
* still results in a NPE.
*/
#Rule public TestSuccess _gebTestSuccesswatcher = new TestSuccess();
// I never see this called
void report() {
System.out.println("Custom Reporting Spec: " + _gebTestSuccesswatcher + "\t")
super.report()
}
}
I have also attempted to set this up in a custom reporter:
public CustomReporter extends ScreenshotAndPageSourceReporter implements Reporter {
#Rule
public TestSuccess _gebTestSuccesswatcher = new TestSuccess();
#Override
public void writeReport(Browser browser, String label, File outputDir) {
System.out.println("Custom Reporter: " + _gebTestSuccesswatcher);
super.writeReport(browser, label, outputDir)
}
}
However, regardless of whether or not my test fails, the success method on the watcher seems to be called. Here is my sample test:
class OneOff extends CustomReportingSpec {
def "Check One off"() {
when:
go "http://www.google.com"
then:
1 == 2
}
}
And the output:
Custom Reporter: null success: <false>.
Test Success [succeeded] Check One off(OneOff)
As you can see the success method is called upon completion of this failing test. If I modify the test to pass (i.e. 1 == 1), here is my output:
Custom Reporter: null success: <false>.
Test Success [succeeded] Check One off(OneOff)
Is there any way for me to get this Rule to work properly in the Custom Reporter? Or is there a way to get the browser instance in an extension? I've followed this guide to create a custom annotation and listener, but I can't access the Browser object. I have attempted adding an #Shared onto the declaration of the browser, but it isn't pulling the one in the Geb Configuration.
Your TestSuccess class doesn't work correctly due to a known limitation in Spock's TestRule support. Due to subtle differences between Spock's and JUnit's test execution model, calling base.evaluate() from a TestRule will not throw an exception in Spock, even if the test has failed. In many cases this won't make a difference, but for TestWatcher it will.
This is the only known limitation in Spock's rule support, and hopefully we'll find a way to overcome it at some point. There is no such semantic mismatch when using MethodRule.
If you want to implement your requirement with the help of a JUnit rule (which I think is fine), MethodRule is probably the better choice anyway. In contrary to TestRule, MethodRule provides access to the test instance, which will allow you to grab the session ID with browser.driver.sessionId.

Resources