for GET Rest api call using spring-integration, how to read user defined headers? - spring

MessageHeaders has predefined headers like TIMESTAMP, ERROR_CHANNEL etc. but how to access user defined header?
My api has http://localhost:8082/load/1234567?source=ABC and headers like username:testuser
message.getPayload() gives me just this 1234567 so that header is not part of payload, but
Map<String, Object> headers = new HashMap<String, Object>();
Set<String> keys = message.getHeaders().keySet();
MessageHeaders msgHeader = message.getHeaders();
for(String key : keys) {
headers.put(key, msgHeader.get(key));
}
& headers.get("username") returns null.
could someone please help?

I hope that you mean you set a username HTTP header in request.
The HTTP Inbound Channel Adapter (or Gateway) come with a DefaultHttpHeaderMapper by default. This one does only standard HTTP Request headers mapping by default:
private static final String[] HTTP_REQUEST_HEADER_NAMES =
{
HttpHeaders.ACCEPT,
HttpHeaders.ACCEPT_CHARSET,
HttpHeaders.ACCEPT_ENCODING,
HttpHeaders.ACCEPT_LANGUAGE,
HttpHeaders.ACCEPT_RANGES,
HttpHeaders.AUTHORIZATION,
HttpHeaders.CACHE_CONTROL,
HttpHeaders.CONNECTION,
HttpHeaders.CONTENT_LENGTH,
HttpHeaders.CONTENT_TYPE,
HttpHeaders.COOKIE,
HttpHeaders.DATE,
HttpHeaders.EXPECT,
HttpHeaders.FROM,
HttpHeaders.HOST,
HttpHeaders.IF_MATCH,
HttpHeaders.IF_MODIFIED_SINCE,
HttpHeaders.IF_NONE_MATCH,
HttpHeaders.IF_RANGE,
HttpHeaders.IF_UNMODIFIED_SINCE,
HttpHeaders.MAX_FORWARDS,
HttpHeaders.PRAGMA,
HttpHeaders.PROXY_AUTHORIZATION,
HttpHeaders.RANGE,
HttpHeaders.REFERER,
HttpHeaders.TE,
HttpHeaders.UPGRADE,
HttpHeaders.USER_AGENT,
HttpHeaders.VIA,
HttpHeaders.WARNING
};
To include your custom header into a message this channel adapter produces, you just need to incorporate this configuration option:
/**
* Provide the pattern array for request headers to map.
* #param patterns the patterns for request headers to map.
* #return the current Spec.
* #see DefaultHttpHeaderMapper#setOutboundHeaderNames(String[])
*/
public S mappedRequestHeaders(String... patterns) {
and use, for example, just * to map all the headers, or if your requirements are strict only for your headers, then pass their names over there.
See more info in docs: https://docs.spring.io/spring-integration/docs/current/reference/html/http.html#http-header-mapping

Related

Implementing Request Response in Apache Kafka java

Please find the use case we need to implement.
First, we need to invoke a Kafka producer a message as a rest service, they will process and give back the response in another topic.
For us, It is a request-reply topic we need to reply back for the same request the response, using replykafka template is working fine, but we can set co-relation id in the header.
As a topic message metadata there are sending in attributes, is there any way to map the co-relation id with request topic message and reply topic message.
Explain to you better.
One microservice expects the payload as given below with correlationId in payload.
{
"operationDate": "2020-09-16T11:58:25",
"correlationId": "-5544538377183901824042719876882142227",
"birthDate": "2013-12-12",
"firstNameEn": "boby",
"firstNameAr": "الشيخ",
}
The microservice will process the payload and will give a response in another topic as.
{
"correlationId": -5544538377183901824042719876882142227,
"consumerId": null,
"userid": 123456,
"statusCode": "SUCCESS",
"errors": null
}
Now as this we need to implement using spring ReplyingKafkaTemplate.
As ReplyingKafkaTemplate will work with correlationId in the header only
Assuming you mean you want to include the topic(s) in the correlation id, see
/**
* Set a function to be called to establish a unique correlation key for each request
* record.
* #param correlationStrategy the function.
* #since 2.3
*/
public void setCorrelationIdStrategy(Function<ProducerRecord<K, V>, CorrelationKey> correlationStrategy) {
You can create your own correlation id, based on the ProducerRecord (which has the topic()).
You just need to make sure it is unique. If you manually set the KafkaHeaders.REPLY_TOPIC, it will be visible to the strategy.
EDIT
With the correlation id in the payload, use setCorrelationIdStrategy to extract the correlationId from the payload and add a RecordInterceptor to do the same on the reply side.
Thanks for the hint.
I have done as overriding the payload with Kafka header correlationId.
#Override
protected ListenableFuture<SendResult> doSend(ProducerRecord producerRecord) {
if(producerRecord.value()!=null){
// i have appeneded the header correlationId in th payload
}
return super.doSend(producerRecord);
}
And in Replay onMessage ,i have populated the response payload correlationId to the header.
#Override
public void onMessage(List<ConsumerRecord<K, R>> data) {
data.forEach(
krConsumerRecord -> //update each record header
);
super.onMessage(data);
}
In this way was successfully integrated request-response semantics with correlationId in the request and response payload.

Web API content negotiated formatters with accept header and url parameter

I have implemented content negotiation so that a specific serializer will be used based on the accept header:
XmlFormatter fmtXml = new XmlFormatter();
fmtXml.SupportedMediaTypes.Add(new
System.Net.Http.Headers.MediaTypeHeaderValue("text/xml"));
JsonFormatter fmtJson = new JsonFormatter();
fmtJson.SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
config.Formatters.Insert(0, fmtJson);
config.Formatters.Insert(0, fmtXml);
I need to allow a client to specify the desired format using a url parameter, which would take precedence over the accept header.
To do this, I've started subclassing the DefaultContentNegogiator (although I don't know that it's the best idea.:
public class CustomContentNegotiator : DefaultContentNegotiator
{
public override ContentNegotiationResult Negotiate(Type type, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters)
{
string sMimeType = HttpUtility.ParseQueryString(request.Url.Query).Get("_format");
if (!string.IsNullOrEmpty(sMimeType))
{
...
}
else
{
return base.Negotiate(type, request, formatters);
}
}
}
Then I replace the default content negotiator with mine:
GlobalConfiguration.Configuration.Services.Replace(typeof(IContentNegotiator), new CustomContentNegotiator());
The idea with the custom content negotiator is that if a content format has been specified as a url parameter, I would locate the formatter that matches, otherwise I would just fallback to the behavior of the DefaultContentNegotiator.
I'm just not sure how to match correctly on the supported media types, or if there is a better, simpler solution to this...
I determined that using a custom content negotiator was a red herring. Instead I was able to use a MediaTypeMapping which matches against a specific url parameter instead of the accept request header:
fmtJson.MediaTypeMappings.Add(new System.Net.Http.Formatting.QueryStringMapping("_format", "json", "application/json"));

NiFi SpringContextProcessor interrupts the flow

I added a SpringContextProcessor to my NiFi flow, it executes as expected and updates the FlowFile content and attributes. But in the data provenance section of NiFi instead of seeing SEND/RECEIVE, I am seeing
03/27/2017 11:47:57.164 MDT RECEIVE 42fa1c3f-edde-4cb7-8e73-ce752f7e3d66
03/27/2017 11:47:57.163 MDT DROP 667094a7-8eef-4657-981a-dc9fdc6c4056
03/27/2017 11:47:57.163 MDT SEND 667094a7-8eef-4657-981a-dc9fdc6c4056
Looks like the original message is being dropped and replaced by a new message. I haven't seen this behavior in other components, i.e. they all seem to preserve the original Flow File UUID. Simplified version of the Spring processor code:
#ServiceActivator(inputChannel = "fromNiFi", outputChannel = "toNiFi")
public Message<byte[]> process1(Message<byte[]> inMessage) {
String inMessagePayload = new String(inMessage.getPayload());
String userId = getUserIdFromDb(inMessagePayload);
String outMessagePayload = inMessagePayload + userId;
return MessageBuilder.withPayload(outMessagePayload.getBytes())
.copyHeaders(inMessage.getHeaders())
.setHeader("userId", userId)
.build();
}
Is there a way to preserve the original Flow File UUID in the outgoing message?
This is probably an oversight on our end, so yes please do raise a JIRA.
However as a workaround you can try to extract FlowFile attributes from the incoming Message headers and then propagate them back to the outgoing message.
public Message<byte[]> process1(Message<byte[]> inMessage) {
String myHeader = inMessage.getHeader("someHeader");
. . .
return MessageBuilder.withPayload(outMessagePayload.getBytes())
.copyHeaders(inMessage.getHeaders())
.setHeader("userId", userId)
.setHeader("someHeader", myHeader)
.build();
}

How to have WRO answer with a http 304 not modified?

We are serving javascript resources (and others) via wro in our webapp.
On the PROD environment, the browser gets (for example) the app.js angular webapp's content with an 'expires' headers one year in the future.
Meaning that for subsequent requests the browser takes it from cache without a request to the server.
If we deploy a new version of the webapp, the browser does not get the new version, as it takes it from the local cache.
The goal is to configure wro or/and spring so that the headers will be correctly set to have the browser perform the request each time, and the server return a 304 not modified. So we would have the clients automatically "updated" uppon new deployment.
Did someone already achieve this?
We use Spring's Java Configuration:
#Configuration
public class Wro4jConfiguration {
#Value("${app.webapp.web.minimize}")
private String minimize;
#Value("${app.webapp.web.disableCache}")
private String disableCache;
#Autowired
private Environment env;
#Bean(name = "wroFilter")
public WroFilter wroFilter() {
ConfigurableWroFilter filter = new ConfigurableWroFilter();
filter.setWroManagerFactory(new Wro4jManagerFactory());
filter.setWroConfigurationFactory(createProperties());
return filter;
}
private PropertyWroConfigurationFactory createProperties() {
Properties props = new Properties();
props.setProperty("jmxEnabled", "false");
props.setProperty("debug", String.valueOf(!env.acceptsProfiles(EnvConstants.PROD)));
props.setProperty("gzipResources", "false");
props.setProperty("ignoreMissingResources", "true");
props.setProperty("minimizeEnabled", minimize);
props.setProperty("resourceWatcherUpdatePeriod", "0");
props.setProperty("modelUpdatePeriod", "0");
props.setProperty("cacheGzippedContent", "false");
// let's see if server-side cache is disabled (DEV only)
if (Boolean.valueOf(disableCache)) {
props.setProperty("resourceWatcherUpdatePeriod", "1");
props.setProperty("modelUpdatePeriod", "5");
}
return new PropertyWroConfigurationFactory(props);
}
}
By default, WroFilter set the following headers: ETag (md5 checksum of the resource), Cache-Control (public, max-age=315360000), Expires (1 year since resource creation).
There are plenty of details about the significance of those headers. The short explanation is this:
When the server reads the ETag from the client request, the server can determine whether to send the file (HTTP 200) or tell the client to just use their local copy (HTTP 304). An ETag is basically just a checksum for a file that semantically changes when the content of the file changes. If only ETag is sent, the client will always have to make a request.
The Expires and Cache-Control headers are very similar and are used by the client (and proxies/caches) to determine whether or not it even needs to make a request to the server at all.
So really what you want to do is use BOTH headers - set the Expires header to a reasonable value based on how often the content changes. Then configure ETags to be sent so that when clients DO send a request to the server, it can more easily determine whether or not to send the file back.
If you want the client always to check for the latest resource version, you should not send the expires & cache-control headers.
Alternatively, there is a more aggressive caching technique: encode the checksum of the resource into its path. As result, every time a resource is changed, the path to that resource is changed. This approach guarantee that the client would always request the most recent version. For this approach, in theory the resources should never expire, since the checksum change every time a resource is changed.
Based on Alex's information and documentation reference, I ended up overriding WroFilter.setResponseHeaders to put appropriate expire values.
This is working fine. Wro already takes care of setting ETag, Date and others, so I only overwrite the expiration delay and date.
#Configuration
public class Wro4jConfiguration {
#Value("${app.webapp.web.browserCache.maxAgeInHours}")
private String maxAgeInHours;
#Bean(name = "wroFilter")
public WroFilter wroFilter() {
ConfigurableWroFilter filter = createFilter();
filter.setWroManagerFactory(new Wro4jManagerFactory());
filter.setWroConfigurationFactory(createProperties());
return filter;
}
private ConfigurableWroFilter createFilter() {
return new ConfigurableWroFilter() {
private final int BROWSER_CACHE_HOURS = Integer.parseInt(maxAgeInHours);
private final int BROWSER_CACHE_SECONDS = BROWSER_CACHE_HOURS * 60 * 60;
#Override
protected void setResponseHeaders(final HttpServletResponse response){
super.setResponseHeaders(response);
if (!getConfiguration().isDebug()) {
ZonedDateTime cacheExpires = ZonedDateTime.of(LocalDateTime.now(), ZoneId.of("GMT")).plusHours(BROWSER_CACHE_HOURS);
String cacheExpiresStr = cacheExpires.format(DateTimeFormatter.RFC_1123_DATE_TIME);
response.setHeader(HttpHeader.EXPIRES.toString(), cacheExpiresStr);
response.setHeader(HttpHeader.CACHE_CONTROL.toString(), "public, max-age=" + BROWSER_CACHE_SECONDS);
}
}
};
}
// Other config methods
}

InstanceAnnotation into OData Response

I defined follow own provider:
public class CustomEntityTypeSerializer : ODataEntityTypeSerializer
{
public CustomEntityTypeSerializer(ODataSerializerProvider serializerProvider)
: base(serializerProvider)
{}
public override Microsoft.OData.Core.ODataEntry CreateEntry(SelectExpandNode selectExpandNode, EntityInstanceContext entityInstanceContext)
{
Microsoft.OData.Core.ODataEntry entry = base.CreateEntry(selectExpandNode, entityInstanceContext);
entry.InstanceAnnotations.Add(new ODataInstanceAnnotation("org.test", new ODataPrimitiveValue("test")));
return entry;
}
}
and I can't get instance annotations above within http response from my webapi app.
Why it is occurs?
ps: I use System.Web.OData lib from latest release of master branch from aspnetwebstack.codeplex.com. Accept header with odata.metadata=full is enable
Can you check if header of response contains:
Preference-Applied: odata.include-annotations="*"
If not, try to pass the following Prefer header in your request header. Let service handle this header and return the response with above Preference-Applied header.
Prefer: odata.include-annotations="*"
ODL writer should write instance annotations with the setting of odata.include-annotations in Preference-Applied header

Resources