I have a screen with a form contaning invoices I want to notify the user with. The screen is used as bodyScreenLocation for the EmailTemplate. Up to here, it works. I wanted to add a link for each invoice so that the user can open the document right from withing the message. How to construct the path? This way it does not work:
This field is created in a form, that is included in a screen. This screen is used in a template called using
<service-call name="org.moqui.impl.EmailServices.send#EmailTemplate" async="true">
Added 18.10.2016
I attempted to use your advice.
<actions>
<entity-find entity-name="mantle.account.invoice.Invoice" list="invoiceList"/>
<script>
import org.moqui.impl.context.WebFacadeImpl
def httpUrl = WebFacadeImpl.getWebappRootUrl('webroot',null,false,false,ec)
</script>
</actions>
This is the log:
21:47:18.996 WARN oquiWorker-2 o.moqui.i.c.TransactionFacadeImpl Transaction set rollback only. The rollback was originally caused by: Error rendering screen [component://HS-data/screen/Notification/PayablesDue.xml]
java.lang.NullPointerException: null
at org.moqui.impl.context.WebFacadeImpl.makeWebappHost(WebFacadeImpl.groovy:477) ~[moqui-framework-2.0.0.jar:2.0.0]
at org.moqui.impl.context.WebFacadeImpl.makeWebappRootUrl(WebFacadeImpl.groovy:520) ~[moqui-framework-2.0.0.jar:2.0.0]
at org.moqui.impl.context.WebFacadeImpl.getWebappRootUrl(WebFacadeImpl.groovy:467) ~[moqui-framework-2.0.0.jar:2.0.0]
at org.moqui.impl.context.WebFacadeImpl$getWebappRootUrl.call(Unknown Source) ~[?:?]
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48) ~[groovy-2.4.7.jar:2.4.7]
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113) ~[groovy-2.4.7.jar:2.4.7]
at PayablesDue_xml_screen_actions.run(PayablesDue_xml_screen_actions:11) ~[script:?]
What you're looking for is the WebFacade.getWebappRootUrl() method. Use a call that is something like this in your template to get the base URL:
ec.web.getWebappRootUrl(true, null)
When there is no WebFacade in place, such as when running in an async service, you'll have to drop a level lower and there is a static method in WebFacadeImpl for this (it's not so convenient when you don't have the standard interfaces to go through), here is the method signature:
String getWebappRootUrl(String webappName, String servletContextPath, boolean requireFullUrl, Boolean useEncryption, ExecutionContextImpl eci)
The standard webappName is 'webroot' and unless you're doing something fancy in your Moqui Conf XML file this can be a static String. The servletContextPath can be null or an empty String if your Moqui webapp is mounted on the root (commonly the case; without a request where we can get to the ServletContext to see where it is mounted this can't be determined automatically).
Related
I have Spring Boot API that uses spring-hateoas.
I have a domain class that is used for request and response in my controller. The domain class inherits from spring-hateoas class RepresentationModel, therefore it contains a "links" field.
For some reason (that I cannot control) a client is sending "_links": null in the request which is forcing links field to be null, after it had been properly initialized with an empty List. See RepresentationModel.java
Null links field in the request is causing issues in my application:
Caused by: java.lang.IllegalArgumentException: Links must not be null!
at org.springframework.util.Assert.notNull(Assert.java:201) ~[spring-core-5.3.8.jar:5.3.8]
at org.springframework.hateoas.Links.<init>(Links.java:54) ~[spring-hateoas-1.2.6.jar:1.2.6]
at org.springframework.hateoas.Links.of(Links.java.79) ~[spring-hateoas-1.2.6.jar:1.2.6]
at org.springframework.hateoas.RepresentationModel.getLinks(RepresentationModel.java:210) ~[spring-hateoas-1.2.6.jar:1.2.6]
at sun.reflect.GeneratedMethodAccesor158.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccesorImpl.invoke(DelegatingMethodAccesorImpl.java:43) ~[na:1.8.0_292]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_292]
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:689) ~[jackson-databind-2.11.4.jar:2.11.4]
at com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter.serializeAsField(SimpleBeanPropertyFilter.java:208) ~[jackson-databind-2.11.4.jar:2.11.4]
One thing I tried was configuring Jackson to ignore links field on marshalling (#JsonIgonoreProperties) but this is also affecting my response by removing HATEOAS links from it (this Jackson configuration is application wide)
Looking at RepresentationModel class from spring-hateoas, seems like links field is designed not to be handled directly. It is updated only through the corresponding methods in RepresentationModel after it was initialized.
I was able to reinitialize links field through reflection.
try {
Field field = obj.getClass().getSuperClass().getDeclaredField("links");
field.setAccessible(true);
field.set(obj, new ArrayList<>());
field.setAccessible(false);
} catch (Exception e) {
e.printStackTrace(e);
}
Is there a better way to reinitialize HATEOAS links field in a RepresenationModel object which already has null links?
Is there a better approach to prevent the client request from affecting the incoming object this way?
I have a route (with Camel 2.23.1) like:
from("file://not.existing.dir?autoCreate=false&startingDirectoryMustExist=true&consumer.bridgeErrorHandler=true")
.onException(Exception.class)
.handled(true)
.log(LoggingLevel.ERROR, "...exception text...")
.end()
.log(LoggingLevel.INFO, "...process text...")
...
(I tried it with just &bridgeErrorHandler, too, since according to the latest doc the consumer. prefix seems to be not necessary any longer.)
According to the doc of startingDirectoryMustExist:
| startingDirectoryMustExist | [...] Will thrown an exception if the directory doesn’t exist. |
the following exception is thrown:
org.apache.camel.FailedToCreateRouteException: Failed to create route route1:
Route(route1)[[From[file://not.existing.dir?autoCreate=false...
because of Starting directory does not exist: not.existing.dir
...
but, despite of the doc and the description of [consumer.]bridgeErrorHandler it's propagated to the caller, i.e neither "exception text" nor "process text" are printed.
There is a unit test FileConsumerBridgeRouteExceptionHandlerTest that covers consumer.bridgeErrorHandler, so I think this works basically. Can it be that [consumer.]bridgeErrorHandler doesn't work in conjunction with the exception thrown by startingDirectoryMustExist?
Do I have to write my own [consumer.]exceptionHandler as mentioned in this answer to "Camel - Stop route when the consuming directory not exists"?
There also a post on the mailing list from 2014 that reports similar behaviour with startingDirectoryMustExist and consumer.bridgeErrorHandler.
UPDATE
After TRACEing and debugging through the code I found that the exception is propagated as follows:
FileEndpoint.createConsumer()
throw new FileNotFoundException(...);
--> RouteService.warmUp()
throw new FailedToCreateRouteException(...)
--> DefaultCamelContext.doStart()
(re)throw e
--> ServiceSupport.start()
(re)throw e
I couldn't find any point where bridgeErrorHandler comes into play.
Setting breakpoints on BridgeExceptionHandlerToErrorHandler's constructor and all of its handleException() methods doesn't stop at any of them.
Am I still missing something?
You should use the directoryMustExist option instead, then you can have the error during polling, which is where the bridge error handler can be triggered. The startingDirectoryMustExist option is checked during creating the consumer and therefore before the polling and where the bridge error handler operates.
See also the JIRA ticket: https://issues.apache.org/jira/browse/CAMEL-13174
Currently I have test class for testing internal class using Pax-Exam 5.
#ProbeBuilder
public TestProbeBuilder probeConfiguration(TestProbeBuilder probe) {
probe.setHeader("Fragment-Host", "com.mycompany.abc");
return probe;
}
The reason I use Fragment-Host is to avoid exporting internal package for bundle com.mycompany.abc.
But I get this error
org.ops4j.pax.exam.TestContainerException: org.osgi.framework.BundleException: Invalid operation on a fragment.
at org.ops4j.pax.exam.nat.internal.NativeTestContainer.install(NativeTestContainer.java:135)
at org.ops4j.pax.exam.nat.internal.NativeTestContainer.install(NativeTestContainer.java:140)
at org.ops4j.pax.exam.nat.internal.NativeTestContainer.installProbe(NativeTestContainer.java:428)
at org.ops4j.pax.exam.spi.reactors.EagerSingleStagedReactor.setUp(EagerSingleStagedReactor.java:68)
at org.ops4j.pax.exam.spi.reactors.EagerSingleStagedReactor.beforeClass(EagerSingleStagedReactor.java:106)
at org.ops4j.pax.exam.spi.reactors.ReactorManager.beforeClass(ReactorManager.java:400)
at org.ops4j.pax.exam.junit.DriverExtension.beforeClassBlock(DriverExtension.java:130)
at org.ops4j.pax.exam.junit.ExtensibleRunner$1.evaluate(ExtensibleRunner.java:53)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.ops4j.pax.exam.junit.PaxExam.run(PaxExam.java:78)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)
Caused by: org.osgi.framework.BundleException: Invalid operation on a fragment.
at org.eclipse.osgi.container.Module.checkFragment(Module.java:520)
at org.eclipse.osgi.container.Module.start(Module.java:408)
at org.eclipse.osgi.internal.framework.EquinoxBundle.start(EquinoxBundle.java:428)
at org.eclipse.osgi.internal.framework.EquinoxBundle.start(EquinoxBundle.java:447)
at org.ops4j.pax.exam.nat.internal.NativeTestContainer.install(NativeTestContainer.java:131)
... 15 more
Is there any other solution?
Thanks
Pax Exam supports provisioning of fragments because you can call .noStart() on the provisioning Option to tell Pax Exam that the bundle/fragment must not be started:
CoreOptions.mavenBundle("gid", "aid", "version").noStart();
But what you are trying to achieve is different, you are trying to make your probe bundle into a fragment of the tested bundle.
You may still achieve your goal without having to make the probe a fragment as follows:
Use TinyBundles to create a fragment that exports the internal package(s) of the test bundle
Pass the fragment to Pax Exam as an url() option with .noStart()
see the following class for an example:
https://github.com/ops4j/org.ops4j.pax.exam2/blob/exam-reactor-3.2.0/itest/osgi/src/it/regression-multi/src/test/java/org/ops4j/pax/exam/regression/multi/fragment/FragmentTest.java#L61
In other words, rather than making the probe a fragment of the host, create on the fly an empty fragment just to export the packages of the host for the benefit of the probe.
Disclaimer: I did not try this, it's just an hypothesis.
I am trying to create a POST request that contains multipart-form-data that requires NT Credentials. The authentication request causes the POST to be resent and I get a unrepeatable entity exception.
I tried wrapping the MultipartContent entity that is produced with a BufferedHttpEntity but it throws NullPointerExceptions?
final GenericUrl sau = new GenericUrl(baseURI.resolve("Record"));
final MultipartContent c = new MultipartContent().setMediaType(MULTIPART_FORM_DATA).setBoundary("__END_OF_PART__");
final MultipartContent.Part p0 = new MultipartContent.Part(new HttpHeaders().set("Content-Disposition", format("form-data; name=\"%s\"", "RecordRecordType")), ByteArrayContent.fromString(null, "C_APP_BOX"));
final MultipartContent.Part p1 = new MultipartContent.Part(new HttpHeaders().set("Content-Disposition", format("form-data; name=\"%s\"", "RecordTitle")), ByteArrayContent.fromString(null, "JAVA_TEST"));
c.addPart(p0);
c.addPart(p1);
The documentation for ByteArrayContent says
Concrete implementation of AbstractInputStreamContent that generates repeatable input streams based on the contents of byte array.
Making all the parts repeatable does not solve the problem. Because this code
System.out.println("c.retrySupported() = " + c.retrySupported()); outputs c.retrySupported() = true.
I found the following documentation:
1.1.4.1. Repeatable entities An entity can be repeatable, meaning its content can be read more than once. This is only possible with self
contained entities (like ByteArrayEntity or StringEntity)
I have now converted my MultipartContent to a ByteArrayContent with a multi/part-form media type by extracting the string contents and still get the same error!
But I still get the following exception when I try and call request.execute().
Caused by: org.apache.http.client.NonRepeatableRequestException: Cannot retry request with a non-repeatable request entity.
So how do I go about convincing the ApacheHttpTransport to create a repeatable Entity?
I had to modify all the classes that inherited from HttpContent so that they would report back correctly with .retrySupported() so that the when the ApacheHttpTransport code was entered it would create repeatable content correctly.
The changes were made against version 1.20.0 because that is what I was using. I am submitting a pull request against dev branch HEAD so hopefully, this or some version of this will make it into the next release.
Here are the modifications that need to be merged in.
If content length of all parts in multipart entity is known (returned as a non negative value) the entity will be treated as repeatable. The easiest way to make multipart entity repeatable is to make all its parts repeatable.
I have a mediator that I've registered for a navigation page:
facade.registerMediator(new NavPageMediator(viewComponent));
I'm trying to retrieve that mediator on another page like so:
var navPageMediator:NavPageMediator = facade.retrieveMediator(NavPageMediator.NAME) as NavPageMediator;
However, that statement returns null. If I try to cast it using the NavPageMediator(facade.retrieveMediator(NavPageMediator.NAME)) syntax instead, I get a TypeError: Error #1034: Type Coercion failed: cannot convert com.website.mvc.view.page::NavPageMediator#237560a1 to com.website.mvc.view.page.NavPageMediator.`
I can't, for the life of me, understand why NavPageMediator#237560a1 would be unable to convert to NavPageMediator, nor what happened in between registering the mediator and retrieving it that caused this. Especially since trace(new NavPageMediator() as NavPageMediator); returns [object NavPageMediator].
Incidentally, and this may be part of my problem, I don't understand what the #hash at the end of the object is (#237560a1). Is it simply an internal identifier for that class instance?
Edit:
Left a bit of important info: The SWF in which I instantiate and register the mediator is separate from the SWF in which I try to retrieve it.
Figured it out. It turned out to be an ApplicationDomain issue. Assigning both SWFs (the registrant and the retriever) to the same domain solved the issue.
Additionally, I'm pretty sure the #hash at the end of the class name is an internal reference to the ApplicationDomain to which the class belongs. So NavPageMediator#237560a1 was in a different domain than NavPageMediator (why there was no hash on the second one I'm still not sure; that would have made things a bit clearer).