Rest camel passing objects between endpoints - spring

Overview.
My camel setup calls two service methods. the response of the first one is passed into the second one and then output the final response as json webpage. Fairly simple nothing too complicated.
Further breakdown to give some more context.
Method_1. Takes in scanId. This works ok. It produces an object called ScheduledScan .class
Method_2. Takes in object previous instance of ScheduledScan .class and returns a list of ConvertedScans scans. Then would like to display said list
Description of the code
#Override
public void configure() throws Exception {
restConfiguration().bindingMode(RestBindingMode.json);
rest("/publish")
.get("/scheduled-scan/{scanId}")
.to("bean:SentinelImportService?method=getScheduledScan").outType(ScheduledScan .class)
.to("bean:SentinelImportService?method=convertScheduledScan");
}
The methods that are called look like the following
ScheduledScan getScheduledScan(#Header("scanId") long scanId);
List<ConvertedScans > convertScheduledScan(#Body ScheduledScan scheduledScans);
It is returning the the following error
No body available of type: path. .ScheduledScan but has value:
of type: java.lang.String on: HttpMessage#0x63c2fd04. Caused by: No type converter available
The following runs without the error, i.e. without method 2. So I think im almost there.
rest("/publish")
.get("/scheduled-scan/{scanId}")
.to("bean:SentinelImportService?method=getScheduledScan");
Now from reading the error it looks like im passing in a HttpMessage not the java object? I'm a bit confused about what to do next? Any advice much appreciated.
I have found some similar questions to this message. However I am looking to pass the java object directly into the service method.
camel-rest-bean-chaining
how-to-share-an-object-between-methods-on-different-camel-routes

You should setup the outType as the last output, eg what the REST response is, that is a List/Array and not a single pojo. So use .outTypeList(ConvertedScans.class) instead.

Related

Jersey #PathParam : contains multiple parameters with no annotation

The following is my method signature which I am using in Jersey , when I debug/run the program I am getting error as :
[[FATAL] Method public javax.ws.rs.core.Response com.xxxx.xxxxx.Xxxxx.xxxxx.xxxxxxxx(java.lang.String,java.lang.String,java.lang.String,javax.ws.rs.container.ContainerRequestContext) on resource class com.xxxxxx.xxxxx.xxxxxx.xxxxxx contains multiple parameters with no annotation.
My code:
#PUT
#Path("/user/{user}/{role}")
#Consumes({MediaType.APPLICATION_JSON,MediaType.TEXT_PLAIN})
#Produces("application/json")
public Response myFunction(#PathParam("user") String user,
#PathParam("role") String role,
String rawData,
#Context ContainerRequestContext crc) {
}
What I am doing wrong here.
Thank you
Edit: This answer helped me solve my error, but as Cássio Mazzochi Molin mentioned in the comment below: it wont help you (and the documentation is for the wrong version of Jersey..). A total miss on my part.
Please excuse my attempt to help you. I hope you already have solved your error :)
Ahoy there!
I'm really new to REST (so take my answer with a bucket of herb salt),
but I think I know where your error is coming from.
You have to bind your parameter rawData.
Example: #PathParam("rawdata") String rawData or
#HeaderParam("rawdata") String rawData
Depending on where you want to extract the parameter from you have to
write a #annotation to the parameter.
You can extract the following types of parameters for use in your
resource class:
Query
URI
Path
Form
Cookie
Header
Matrix
Text above is taken from the link:
http://docs.oracle.com/javaee/6/tutorial/doc/gilik.html You should
take a look and read a little about it if you haven't done it already
:)

ConstraintViolationException - extract field name which caused exception

I'm using hibernate-validator with a JAX-RS service to validate query parameters using #NotNull:
#GET
public Response doSomething(#NotNull #QueryParam("myParam") String myParam) {...}
This works as expected and throws a ConstraintViolationException if myParam is null. I'd like to extract the param name which is associated to the violation (e.g. myParam), and return that in the response message to the client but there does not appear to be an obvious way of extracting this from the exception. Can someone provide some insight?
As of BeanValidation 1.1 there is a ParameterNameProvider contract which makes parameter name extraction configurable. As mentioned in the other answer, with Java 8 you can get the parameter names in the byte code provided you compile with the -parameters flag. Use the ReflectionParameterNameProvider in this case. However, even with Java 7 you can get parameter names, for example by using the ParanamerParameterNameProvider. This parameter name provider is based on Paranamer and there are several ways to set it up.
This only works if you're using Java 8, as prior to Java 8 the actual parameter name was lost at compile time. Its now retained, assuming you compile and run at Java 8. See also http://docs.jboss.org/hibernate/validator/5.2/reference/en-US/html_single/#_java_8_support

AX2012 - Pre-Processed RecId parameter not found

I made a custom report in AX2012, to replace the WHS Shipping pick list. The custom report is RDP based. I have no trouble running it directly (with the parameters dialog), but when I try to use the controller (WHSPickListShippingController), I get an error saying "Pre-Processed RecId not found. Cannot process report. Indicates a development error."
The error is because in the class SrsReportProviderQueryBuilder (setArgs method), the map variable reportProviderParameters is empty. I have no idea why that is. The code in my Data provider runs okay. Here is my code for running the report :
WHSWorkId id = 'LAM-000052';
WHSPickListShippingController controller;
Args args;
WHSShipmentTable whsShipmentTable;
WHSWorkTable whsWorkTable;
clWHSPickListShippingContract contract; //My custom RDP Contract
whsShipmentTable = WHSShipmentTable::find(whsWorkTable.ShipmentId);
args = new Args(ssrsReportStr(WHSPickListShipping, Report));
args.record(whsShipmentTable);
args.parm(whsShipmentTable.LoadId);
contract = new clWHSPickListShippingContract();
controller = new WHSPickListShippingController();
controller.parmReportName(ssrsReportStr(WHSPickListShipping, Report));
controller.parmShowDialog(false);
controller.parmLoadFromSysLastValue(false);
controller.parmReportContract().parmRdpContract(contract);
controller.parmReportContract().parmRdpName(classStr(clWHSPickListShippingDP));
controller.parmReportContract().parmRdlContract().parmLanguageId(CompanyInfo::languageId());
controller.parmArgs(args);
controller.startOperation();
I don't know if I'm clear enough... But I've been looking for a fix for hours without success, so I thought I'd ask here. Is there a reason why this variable (which comes from the method parameter AifQueryBuilderArgs) would be empty?
I'm thinking your issue is with these lines (try removing):
controller.parmReportContract().parmRdpContract(contract);
controller.parmReportContract().parmRdpName(classStr(clWHSPickListShippingDP));
controller.parmReportContract().parmRdlContract().parmLanguageId(CompanyInfo::languageId());
The style I'd expect to see with your contract would be like this:
controller = new WHSPickListShippingController();
contract = controller.getDataContractObject();
contract.parmWhatever('ParametersHere');
controller.parmArgs(args);
And for the DataProvider clWHSPickListShippingDP, usually if a report is using a DataProvider, you don't manually set it, but the DP extends SRSReportDataProviderBase and has an attribute SRSReportParameterAttribute(...) decorating the class declaration in this style:
[SRSReportParameterAttribute(classstr(MyCustomContract))]
class MyCustomDP extends SRSReportDataProviderBase
{
// Vars
}
You are using controller.parmReportContract().parmRdpContract(contract); wrong, as this is more for run-time modifications. It's typically used for accessing the contract for preRunModifyContract overloads.
Build your CrossReference in a development environment then right click on \Classes\SrsReportDataContract\parmRdpContract and click Add-Ins>Cross-reference>Used By to see how that is generally used.
Ok, so now I feel very stupid for spending so much time on that error, when it's such a tiny thing...
The erronous line is that one :
controller.parmReportName(ssrsReportStr(WHSPickListShipping, Report));
Because WHSPickListShipping is the name of the AX report, but I renamed my custom report clWHSPickListShipping. What confused me was that my DataProvider class was executing as wanted.

Spring HATEOAS / MockMvc / JsonPath best practices

I'm writing unit tests for Spring HATEOAS backend using MockMvc and JsonPath.
To test the links contained in a response I'm doing something like:
#Test
public void testListEmpty() throws Exception {
mockMvc.perform(get("/rest/customers"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.links", hasSize(1))) // make sure links only contains self link
.andExpect(jsonPath("$.links[?(#.rel=='self')]", hasSize(1))) // make sure the self link exists 1 time
.andExpect(jsonPath("$.links[?(#.rel=='self')].href", contains("http://localhost/rest/customers{?page,size,sort}"))) // test self link is correct
.andExpect(jsonPath("$.links[?(#.rel=='self')][0].href", is("http://localhost/rest/customers{?page,size,sort}"))) // alternative to test self link is correct
.andExpect(jsonPath("$.content", hasSize(0))); // make sure no content elements exists
}
However I wonder if there are some best practices I should use to make it easier for myself like:
Testing link contains http://localhost does not feel right. Can I use some Spring MovkMvc helper to determine the host?
With JsonPath it's difficult to test if an array contains an element where 2 attributes have certain value.
Like that the array should contain a self link with a certain value.
Is there a better way to test that then above
This will also come into play when testing validation errors for fields with error messages.
I've see technique like below in some blog posts:
.andExpect(jsonPath("$.fieldErrors[*].path", containsInAnyOrder("title", "description")))
.andExpect(jsonPath("$.fieldErrors[*].message", containsInAnyOrder(
"The maximum length of the description is 500 characters.",
"The maximum length of the title is 100 characters.")));
But this does not guarantee at all that title has the specific error message.
It could also be that the title has incorrectly the "The maximum length of the description is 500 characters." but the test will succeed.
You may use Traverson (included in Spring HATEOAS) to traverse the links in tests.
If you are using Spring Boot, I'd consider using #WebIntegrationTest("server.port=0") rather than MockMvc, as in some cases I experienced behaviours slightly different from the actual application.
You may find some example in a post of mine: Implementing HAL hypermedia REST API using Spring HATEOAS.
Also look at the tests in the sample project.
One approach that addresses the http://localhost concern without sacrificing the need to test two attribute constraints on an array element is to use the org.hamcrest.CoreMatchers.hasItem(org.hamcrest.Matcher nestedMatcher) matcher. The test you showed above now becomes:
.andExpect(jsonPath("$.links[?(#.rel=='self')].href", hasItem(endsWith("/rest/customers{?page,size,sort}"))))
.andExpect(jsonPath("$.links[?(#.rel=='self')][0].href", hasItem(endsWith("/rest/customers{?page,size,sort}"))))

log4net on WebApi 2.1 using ExceptionLogger

How does one properly implement WebApi 2.1's ExceptionLogger so that log4net logs the correct values for method, location and line?
What I'm trying to achieve is a global exception logger to log all unhandled exceptions in a WebAPI 2.1 v5.1.2 app running .NET 4.5.1. I've read quite a few articles including the one linked below explaining how to implement the ExceptionLogger, and it works great, except that I can't get log4net to output the values I really want to record.
For example, if I log an exception from a controller, everything is correct. When I log from the ExceptionLogger, I'm getting the values from the Logger itself, and not the method that initiated the exception. I tried a few things listed in my code below, but they're not quite right. Here's what I get.
I know the text is small, but the table shows the different values log4net writes. The first log is from the controller, everything is great. The 2nd entry is from log.Logger.Log in the code snippet. The last entry is from log.Error in the snippet.
The final method in the snippet attempts to use a limiting type as I've read from other implementations of log4net wrappers, but it just throws an error, as described in the snippet.
So, HOW CAN I GET THE VALUES LIKE THE ONES I WOULD RECEIVE IF CALLING FROM A CONTROLLER, WHEN USING A GLOBAL ExceptionLogger ?
public class GlobalExceptionLogger: ExceptionLogger
{
//private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public override void Log(ExceptionLoggerContext context)
{
StackTrace stackTrace = new StackTrace(context.Exception);
Type methodDeclaringType = stackTrace.GetFrame(2).GetMethod().DeclaringType;
ILog log = LogManager.GetLogger(methodDeclaringType);
string message = context.ExceptionContext.Exception.Message;
//this methods works but writes the location, method name and line from this method, not the caller
//location: System.Web.Http.ExceptionHandling.ExceptionLogger.LogAsync(:0)
//method: LogAsync
//line: 0
log.Logger.Log(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType, log4net.Core.Level.Error, message, context.ExceptionContext.Exception);
//this methods works but writes the location, method name and line from this method, not the caller
//location: Company.AppName.Api.GlobalExceptionLogger.Log(c:\TFS\AppName\AppName.Api\GlobalExceptionLogger.cs:38)
//method: Log
//line: 38
log.Error(message, context.ExceptionContext.Exception);
//this method throws an error in the log4net debug: log4net:ERROR loggingEvent.LocationInformation.StackFrames was null or empty.
log.Logger.Log(methodDeclaringType, log4net.Core.Level.Error, message, context.ExceptionContext.Exception);
}
}
http://weblogs.asp.net/jongalloway//looking-at-asp-net-mvc-5-1-and-web-api-2-1-part-4-web-api-help-pages-bson-and-global-error-handling
Your method of getting the stacktrace is not recommended, because the code will behave differently on debug/release or precessor architecture. The method stackTrace.GetFrame(2).GetMethod() will give you the method on the real stack, with taking into consideration the optimalizations of the runtime for processor architecture, linq rewrites etc.
An alternative method of getting the member name:
public static string LogError(Exception ex, [CallerMemberName] string callerName = "")
You should have a look at this question:
stackframe-behaving-differently-in-release-mode

Resources