I've spent hours trying to solve my problem which seems to be caused by synchronous Until Successful Scope in Mule ESB v3.5.0. It seems to modify message payload when sending an outbound HTTP requests.
I need to continue in my flow after outbound HTTP request successfully returns from a HTTP server (which sometimes has connection problems). Thus I need the sync variant of Until Successful. For now I use just a simple Logger after the Until Successful block.
The body of my HTTP request is a XML file. When there is no problem at my server and the Until Successful doesn't need to make another HTTP request again, I receive the XML which I sent.
However, when there is a connectivity problem so the Until Successful repeats requesting a few times and then the server goes back online, on my server I receive an instance of org.apache.commons.httpclient.methods.PostMethod instead of the sent XML in the request body!
So no more XML on my server. It seems this sync Until Successful simply discards the original message payload...
The standard async variant of Until Successful works as intended - getting XML in requests all the time.
Here is a minimal sample of HTTP outbound endpoint with Until Successful:
<flow name="perform" doc:name="performHTTP">
<until-successful maxRetries="${repeater.retries}" millisBetweenRetries="${repeater.period}" failureExpression="#[exception != null && (exception.causedBy(java.net.ConnectException) || exception.causedBy(java.net.SocketTimeoutException)) || message.inboundProperties['http.status'] != 200]" doc:name="Until Successful - Repeater" synchronous="true">
<http:outbound-endpoint exchange-pattern="request-response" host="${https.outbound.address}" port="${https.outbound.port}" path="${https.outbound.path}" method="POST" mimeType="text/xml" transformer-refs="Custom_Outbound_HTTPS_Header" contentType="text/xml" doc:name="HTTPS - Outbound" doc:description="Outcoming HTTPS connection" responseTimeout="15000"/>
</until-successful>
<logger message="#['Sending done']" level="INFO" doc:name="Logger - Done"/>
</flow>
Long story short:
synchronous Until Successful: XML -> HTTP request - { NET } - HTTP request -> org.apache.commons.httpclient.methods.PostMethod
asynchronous Until Successful: XML -> HTTP request - { NET } - HTTP request -> XML
I had the same problem and fixed it by saving my payload and retrieving on each retry something like this
<set-variable value="#[payload]" variableName="paloadbeforecall" doc:name="Variable" />
<until-successful maxRetries="${repeater.retries}" millisBetweenRetries="${repeater.period}" failureExpression="#[exception != null && (exception.causedBy(java.net.ConnectException) || exception.causedBy(java.net.SocketTimeoutException)) || message.inboundProperties['http.status'] != 200]" doc:name="Until Successful - Repeater" synchronous="true">
<processor-chain>
<set-payload value="#[flowVars.?paloadbeforecall]" doc:name="Variable" />
<http:outbound-endpoint exchange-pattern="request-response" host="${https.outbound.address}" port="${https.outbound.port}" path="${https.outbound.path}" method="POST" mimeType="text/xml" transformer-refs="Custom_Outbound_HTTPS_Header" contentType="text/xml" doc:name="HTTPS - Outbound" doc:description="Outcoming HTTPS connection" responseTimeout="15000"/>
</processor-chain>
</until-successful>
Sounds like a bug. It would be interesting to report this as an issue. Anyhow, there is a simple workaround for this, just wrap the until-successful in a wire-tap. This will create a copy of the message (not necessarily the payload) and given that the payload is inmmutable (String) the oubound-endpoint will just change the reference without affecting the flow after the wire-tap.
Related
I am using Apigee as gateway to our application. Several applications will hit Apigee and Apigee will in-turn route the request to backend servers. Every incoming request will have a JWT token.
I want Apigee to pass that token to a auth server and auth server will validate if the token is valid or not.
If token is invalid(if auth server return any status other then 200) , I want Apigee to return 403 error as response to request else pass the request to backend server.
How can I implement this kind of shared flow? Is it even possible with Apigee ? Is there any better way to achieve this?
You can do that.
Create a shared flow for Authentication/Authorization which includes ServiceCallout policy which will make a call to auth server.
Based on result for Unauthorized/Bad request you can raise a fault response with help of RaiseFault.
If the response is OK it will proceed smooth to backend.
Sample shared flow.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<SharedFlowBundle revision="1" name="Auth">
<Policies>
<Policy>AssignVariableJwks</Policy> <!-- Assign Input values if needed via AssignMessage policy -->
<Policy>RequestAuthServer</Policy> <!-- Extrnal auth server call using ServiceCallout policy -->
<Policy>TokenNotFoundValidation</Policy> <!-- Validate response and raise fault if needed using RaiseFault policy -->
</Policies>
<Resources/>
<Spec/>
<subType>SharedFlow</subType>
<SharedFlows>
<SharedFlow>default</SharedFlow>
</SharedFlows>
</SharedFlowBundle>
For above shared flow create & attach required policies with logic and you're good to go.
I am new to Mule and i am working on a company project. I have many inbound-endpoints defined in XML in a single flow. I am trying to get the processor "scbProcessor" that processes the response to work asynchronously in the inbound-endpoint.
I tried to just wrap the processor in <async></async> but it gives the error: "Invalid content was found starting with element 'async'". Then I tried to make it sub-flow then reference to flow, which gave the same error.
Code:
<flow name="scb-services">
<composite-source doc:name="Composite Source inbounds">
<https:inbound-endpoint exchange-pattern="request-response" host="${httpInbound.secure.host}"
port="${httpInbound.secure.port}" doc:name="HTTPS QWE Service (Deprecated)"
path="${httpInbound.qwe.contextRoot}/${auto.external.serviceName}"
responseTimeout="${service.qwe.auto.timeout}" connector-ref="httpsConnector">
<mule-ss:http-security-filter realm="mule-realm"/>
<mule-ss:authorization-filter requiredAuthorities="PUBLIC"/>
<response>
<set-variable variableName="outgoingFromESBToClientDate"
value="#[new org.mule.el.datetime.DateTime()]"
doc:name="Register Outgoing date from ESB to Client"/>
<processor ref="scbProcessor" doc:name="SCB Logging Processor"/>
</response>
</https:inbound-endpoint>
.
.
</composite-source>
</flow>
.
.
You can change exchange-pattern="request-response" to exchange-pattern="one-way"to send the messages asynchronously. You can see it in the official documentation: https://docs.mulesoft.com/mule-runtime/3.6/http-transport-reference
Alternatively, you can use a queue in between. Send the message to queue as soon as it is received and then process it after reading it from the queue.
My Requirement is to get data from TCP, after getting the data from TCP convert into java object and send on ActiveMQ. Post that after doing some processing need to send the acknowledgement/response code on same channel on TCP.
To fulfill this requirement I am using tcp-outbound-gateway as bidirectional communication is required.
Problem is I am not able to send acknowledgement with ActiveMQ. If I comment out ActiveMQ part and write a dummy string on replyChannel it is visible, but the moment I send the object on Active MQ queue it is giving me a message "null reply received for nothing to send".
I am using a new queue to get the acknowledgement and trying to put the response on reply channel of tcp-outbound-gate, but error message is no output-channel or replyChannel header available.
I got the MessageHeaders details via Incoming message and sending it via queue to use copyHeader. I am able to set the headers and see paylod in Message object, verified the same by applying Interceptos on reply channel, but still getting the same error no output-channel or replyChannel header available.
Code is :
<int:gateway id="gw" default-reply-channel="replyChannel" default-reply-timeout="10000" service-interface= "com.telnet.core.integration.connection.ParseTcpConfiguration$SimpleGateway"
default-request-channel="${server.inboundChannel}"/>
<int:channel id="telnetLandingChannel" />
<ip:tcp-connection-factory id="serverFactory" type="server" host="${server.host}" port="${server.port}" single-use="false"
serializer="${server.serializer}" deserializer="${server.serializer}" task-executor="serverFactoryTaskExecutor"/>
<ip:tcp-inbound-gateway id="serverInboundAdpater" request-channel="telnetLandingChannel" reply-channel="replyChannel"
connection-factory="serverFactory" error-channel="errorChannel" reply-timeout="1000000" auto-startup="false"/>
<int:channel id="replyChannel"></int:channel>
<beans:bean id="acknowledgementHandler" class= "com.telnet.core.integration.AcknowledgementHandler">
</beans:bean>
<int:channel id="incidentDispatchMessageChannel" datatype="${incident.interaction.dispatch.response.datatype}"></int:channel>
<int-jms:message-driven-channel-adapter id="incidentDispatchMessageChannelAdapter" error-channel="errorChannel"
connection-factory="mqConnectionFactory"
destination-name="${incident.processing.tcp.dispatch.response.queues}"
channel="incidentDispatchMessageChannel"/>
<int:transformer id="incidentMessageActivator"
input-channel="incidentDispatchMessageChannel"
output-channel="replyChannel"
ref="acknowledgementHandler" method="incidentAck">
</int:transformer>
public Message incidentAck(final DefaultIncidentAcknowledgeMessage defaultIncidentAcknowledgeMessage){
MessageHeaders ms = (MessageHeaders)defaultIncidentAcknowledgeMessage.getProperties().get("MessageHeader");
Message<String> message = MessageBuilder.withPayload("1").copyHeaders(ms).build();
return message;
}
Need to see your Integration configuration though, but let me guess that you are loosing TemporaryReplyChannel object in the replyChannel header because it isn't Serializable. Consider to use:
<int:header-enricher>
<int:header-channels-to-string/>
</int:header-enricher>
somewhere before sending to the ActveMQ.
See Reference Manual for more information.
UPDATE
Looks like this is a continuation of Receive the acknowledgement from TCP server to our application using spring Integration. And I see you still use the same replyChannel for many places. That isn't going to work properly. The replyChannel header from the gateway can accept only one reply. Even if we figure out what to do with the reply from ActiveMQ, the TemporaryReplyChannel will be fulfilled with the reply from the TCP Outbound Gateway.
If I understand you correctly, alongside with the reply from the TCP you need also get some message from ActiveMQ. And send everything as a reply to the gateway call. For this purpose I suggest you to consider to use Aggregator and figure out some custom correlation strategy to match the reply from TCP to that acknowledge from the ActiveMQ. After aggregation you really can just use the existing replyChannel header to reply to the gateway.
The use case seeing this error is as follows:
There is an Apigee API Proxy that has been configured for a service. A second API Proxy has a JavaScript policy that makes a call out to the first configured Apigee API Proxy to get back a response and process it. Running this second API Proxy gives the following error from time to time:
"fault": {
"detail": {
"errorcode": "steps.javascript.ScriptExecutionFailed"
},
"faultstring": "Execution of getlocationserviceresponse failed with error: Javascript runtime exceeded limit of 200ms"
}
There are other JavaScript policies attached to this second Proxy so that the total JavaScript is chopped up into small modules but this runtime exceeded limit error persists from time to time. What can be done to avoid this?
You should check the configuration of the Apigee javascript policy. Here is an example of a policy definition:
<Javascript async="false" continueOnError="false" enabled="true"
timeLimit="200" name="validate-email">
<DisplayName>validate-email</DisplayName>
<FaultRules/>
<Properties/>
<ResourceURL>jsc://validate-email.js</ResourceURL>
</Javascript>
The timeLimit attribute can be updated to raise the execution limit. It's value is in ms.
I am facing a Tomcat Server configuration problem.
I have requested a certificate from GoDaddy and have configured the same with Apache Tomcat 5.x. Following is the snippet from tomcat5.x\conf\server.xml for HTTPS configuration.
<Connector port="8443" maxHttpHeaderSize="8192" protocol="HTTP/1.1"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" disableUploadTimeout="true"
acceptCount="100" scheme="https" secure="true"
keystoreFile="/path/to/tomcat.keystore" keystorePass="xxxxxxxxx"
clientAuth="false" sslProtocol="TLS" SSLEnabled="false"/>
I am able to access the Servlet deployed on my server using https://www.exampleserver.com:8443/servletname successfully
Now I am trying HTTP POST request with JSON data in HTTP Body to the same Servlet and I am trying to log the POST data in the servlet but the POST data is empty or not available.
The following is the code snippet to Log POST data in doPost method of Servlet
// Respond with OK and status 200 in a timely fashion to prevent redelivery
response.setContentType("text/html");
Writer writer = response.getWriter();
writer.append("OK");
writer.close();
// Get the notification object from the request body (into a string so we
// can log it)
BufferedReader notificationReader =
new BufferedReader(new InputStreamReader(request.getInputStream()));
String notificationString = "";
// Count the lines as a very basic way to prevent Denial of Service attacks
int lines = 0;
while (notificationReader.ready()) {
notificationString += notificationReader.readLine();
lines++;
// No notification would ever be this long. Something is very wrong.
if (lines > 1000) {
throw new IOException("Attempted to parse notification payload that was unexpectedly long.");
}
}
LOG.info("got raw notification " + notificationString);
The Log.info statement always prints "got raw notification " which means no data was available.
Previous
Interesting part is it works when the Servlet is deployed or accesses using HTTP not HTTPS
Update
I tried curl (JSON data) to the same Servlet with HTTP as well as HTTPS but it's NOT working for both. On Local server it's working.
To be honest I don't have much experience in configuring Apache Tomcat Server and SSL certificates. I followed the configuration steps provided by GoDaddy and of course with the help of Google.