[wso2][APIM 3.2] conditional engagement of handler does not work - handler

I try to write my own WSO2 3.2 APIM Handler.
My purpose is :
engage Handler when a dedicated property is set on API
consume the request and use the binary response to decrypt part of it
change the content type of the response after decryption
I successfuly write my handler and modify the velocity_template.xml as follow (following the documentation):
#if($apiObj.additionalProperties.get('encrypted') == "true"))
<handler class="org.rudi.wso2.mediation.EncryptedMediaHandler">
<property name="encryptedMimeType" value="$apiObj.additionalProperties.get('encrypted_mime_type')"/>
<property name="mimeType" value="$apiObj.additionalProperties.get('mime_type')"/>
<property name="providerUuid" value="dummy"/>
</handler>
#end
But this does not work and the handler is not engaged.
If I remove the #if condition the handler is engaged but the properties are not interpreted (for exemple the "mimeType" field is explicitly set to "$apiObj.additionalProperties.get('mime_type')" and not to the value of the additionnalProperty set on API.
What is wrong ?
How could I use the additionnal properties added on API ?
Next when the Handler is called, I did not find any way to read the response of the endpoint.
I find code to change the response ou the response status code to write fault for exemple
But I did not find a way to read the binary response send by my endpoint to work on it.
Help will be appreciated!
UPDATED
For the second part of the question, I create a method as follow:
private void replaceBody(SOAPBody body) throws IOException {
OMElement element = body.getFirstElement();
if (element.getLocalName().equalsIgnoreCase(BINARY_LOCAL_NAME)) {
OMNode subChild = element.getFirstOMChild();
if (subChild instanceof OMText && ((OMText) subChild).isBinary()) {
OMText textNode = ((OMText) subChild);
DataHandler originalDataHandler = (DataHandler) textNode.getDataHandler();
InputStream modifiedInputStream = modify(originalDataHandler.getInputStream());
DataHandler newDataHandler = new DataHandler(new StreamingOnRequestDataSource(modifiedInputStream ));
OMText newTextNode = body.getOMFactory().createOMText(newDataHandler, true);
textNode.insertSiblingBefore(newTextNode);
textNode.detach();
}
}
}
And I lookup for SOAPBody as follow :
org.apache.axis2.context.MessageContext axis2MC = ((Axis2MessageContext) messageContext)
.getAxis2MessageContext();
RelayUtils.buildMessage(axis2MC, true);
axis2MC.setProperty(RelayConstants.FORCE_RESPONSE_EARLY_BUILD, Boolean.TRUE);
SOAPBody body = axis2MC.getEnvelope().getBody();
I test the behaviour on small file but I think we need to do more stuff to handle big file ou chunked api.
Any advice?

Regarding velocity_template.xml & API Properties Configurations
The velocity_template.xml file is used to construct the API Synapse Artifacts to deploy them in the Gateway. As it is a common template file, you have to place your conditions and customizations in the correct sections of the template to reflect in the Synapse Artifact.
If you are trying to publish a REST API, then place your code block after this section in the velocity_template.xml
#foreach($handler in $handlers)
<handler xmlns="http://ws.apache.org/ns/synapse" class="$handler.className">
#if($handler.hasProperties())
#set ($map = $handler.getProperties() )
#foreach($property in $map.entrySet())
<property name="$!property.key" value="$!property.value"/>
#end
#end
</handler>
#end
<!-- place your block here -->
This makes sure, that your handler is engaged after all mandatory Handlers of the API Managers are engaged. If you want to engage your handler in the middle, then add a condition within #foreach block to append your handler. You can follow this doc for more detailed information.
Once the velocity_template.xml changes are made, re-publish the API by selecting the Gateway environments to deploy the updated Synapse Artifacts. If it is a distributed environment, make sure to update the velocity_template.xml of the Publisher node.
Also, check for any typos in your code block: I see an extra ) at the end of the #if condition.
#if($apiObj.additionalProperties.get('encrypted') == "true"))
...

Related

FLOWABLE: Executing Service Tasks from java code without using xml

I am using Flowable 6.4.1 in spring-boot to create processes and run from my java code, but requirement is to not use any xml, so due to this I have hit a blockade.
I have a user task, taking input from user, depending on that input, call to corresponding service task is made.
Below is a short example of what I am going to do:
basic-process.bpmn20.xml:
<process id="basicprocess" name="Basic Process" isExecutable="true">
<startEvent id="startEvent"/>
<sequenceFlow sourceRef="startEvent" targetRef="getInput"/>
<userTask id="getInput" name="Get input from user" />
<sequenceFlow sourceRef="getInput" targetRef="decision"/>
<exclusiveGateway id="decision"/>
<sequenceFlow sourceRef="decision" targetRef="firstServiceTask">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[
${number>100}
]]>
</conditionExpression>
</sequenceFlow>
<sequenceFlow sourceRef="decision" targetRef="secondServiceTask">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[
${number<=100}
]]>
</conditionExpression>
</sequenceFlow>
<serviceTask id="firstServiceTask" name="Number is greater than predefined target"
flowable:class="demo.service.tasks.FirstServiceTask"/>
<sequenceFlow sourceRef="firstServiceTask" targetRef="greaterEnd"/>
<serviceTask id="secondServiceTask" name="Number is less than predefined target"
flowable:class="demo.service.tasks.SecondServiceTask"/>
<sequenceFlow sourceRef="secondServiceTask" targetRef="lesserEnd"/>
<endEvent id="greaterEnd"/>
<endEvent id="lesserEnd"/>
</process>
Above, XML shows the process and I'm starting the process using REST API
Below is the controller:
DefinitionsController.java:
#RestController
#SuppressWarnings("rawtypes")
public class DefinitionsController {
#Autowired
private RepositoryService mRepositoryService;
#Autowired
private RuntimeService mRuntimeService;
#Autowired
private TaskService mTaskService;
#PostMapping("/start-service")
public String startService(#RequestBody String input) {
Integer request = Integer.parseInt(input);
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("number", request);
ProcessInstance instance = mRuntimeService.startProcessInstanceByKey("basicprocess", variables);
Task userTask = mTaskService.createTaskQuery().processInstanceId(instance.getId()).taskDefinitionKey("getInput").singleResult();
mTaskService.complete(userTask.getId());
return "ProcessInstance id is "+instance.getProcessInstanceId();
}
}
FirstServiceTask.java:
public class FirstServiceTask implements JavaDelegate{
#Override
public void execute(DelegateExecution execution) {
System.err.println("Came in first service task");
}
}
Same for SecondServiceTask.java except the sysout statement.
REST RESPONSE: I get the processInstance Id and sysout statement of respective service task gets printed in console..
Pretty easy to wire the Service Task classes from xml, however if I were to not use XML, I would need to create the same process using flowable-modeler api of FLOWABLE.
So, basically I want to have control over those service tasks from my java code and in order to do that how do I wire the Service Tasks that are created using flowable-modeler with my java code ?
I have gone through docs, but found the xml way only.
Configuring Service Tasks (created using flowable-modeler) with Java code can be done by the 4 ways shown here.
The delegate expression which is going to be configured should be either present on the classpath or should have a spring-bean created.
I created bean using a method in main class, and put the name of method in delegate expressionattribute in flowable-modeler/process api and that's what was needed to do that.
Attached image should clarify things, which shows the way to wire Service Tasks (created using flowable-modeler API) with Java classes in workspace.
firstServiceTask in highlighted field is the method which returns bean of FirstServiceTask
EDIT: Apart from above solution, we can also specify class name in class field alone and all configuration is done. Forex: I have a class called TestClass.java in package org.flowable.learning, so I'll just specify org.flowable.learning.TestClass in class field, which is just above highlighted Delegate expression field in attached screenshot

BizTalk 2010 WCF Remove processing instruction

I need to do download an XML file from a public website (http://www.tcmb.gov.tr/kurlar/201707/10072017.xml) to get exchange rates.
But I have a problem since the XML contains an xml-stylesheet processing instruction.
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="isokur.xsl"?>
<Tarih_Date Tarih="07.07.2017" Date="07/07/2017" Bulten_No="2017/131" >
I use a WCF-Custom port with webHttpBindng and BizTalk REST starter kit from bLogical. Everything works fine, but when I try to parse the incoming xml, I get an error on that processing instruction.
System.Xml.XmlException: Processing instructions (other than the XML declaration) and DTDs are not supported. Line 2, position 2.
I'm not sure what the best way would be to fix this. I tried to follow this guide WCF Errors on XML Deserialization but it still fails when I try to access the message content using the CreateBufferedCopy method.
using (var readStream = new System.IO.MemoryStream())
{
using (var buffer = reply.CreateBufferedCopy(int.MaxValue))
{
buffer.WriteMessage(readStream);
}
readStream.Position = 0;
xdoc.Load(readStream);
}
Does anybody know how I can access the content of my message without actually parsing the XML? I'm just trying to find a way to either remove that line or make the parser ignore it.
I found the solution myself in the end. Instead of a message inspector, I created a Message Encoder based on the CustomTextMessageEncoder that you can find online.
In the ReadMessage method I just added a little bit of code
public override Message ReadMessage(System.IO.Stream stream, int maxSizeOfHeaders, string contentType)
{
XmlReaderSettings xsettings = new XmlReaderSettings();
xsettings.IgnoreProcessingInstructions = true;
XmlReader reader = XmlReader.Create(stream,xsettings);
return Message.CreateMessage(reader, maxSizeOfHeaders, this.MessageVersion);
}

Understanding seam filter url-pattern and possible conflicts

I made a custom editor plugin, in a Seam 2.2.2 project, which makes file upload this way:
1) config the editor to load my specific xhtml upload page;
2) call the following method inside this page, and return a javascript callback;
public String sendImageToServer()
{
HttpServletRequest request = ServletContexts.instance().getRequest();
try
{
List<FileItem> items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
processItems(items);//set the file data to specific att
saveOpenAttachment();//save the file to disk
}
//build callback
For this to work I have to put this inside components.xml:
<web:multipart-filter create-temp-files="false"
max-request-size="1024000" url-pattern="*"/>
The attribute create-temp-files do not seems to matter whatever its value.
But url-pattern has to be "" or "/myUploadPage.seam", any other value makes the item list returns empty. Does Anyone know why?
This turns into a problem because when I use a url-pattern that work to this case, every form with enctype="multipart/form-data" in my application stops to submit data. So I end up with other parts of the system crashing.
Could someone help me?
To solve my problem, I changed the solution to be like Seam multipart filter handle requests:
ServletRequest request = (ServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
try
{
if (!(request instanceof MultipartRequest))
{
request = unwrapMultipartRequest(request);
}
if (request instanceof MultipartRequest)
{
MultipartRequest multipartRequest = (MultipartRequest) request;
String clientId = "upload";
setFileData(multipartRequest.getFileBytes(clientId));
setFileContentType(multipartRequest.getFileContentType(clientId));
setFileName(multipartRequest.getFileName(clientId));
saveOpenAttachment();
}
}
Now I handle the request like Seam do, and do not need the web:multipart-filter config that was breaking other types of request.

Is it possible to programmatically add a new Mule Flow after the context has been initialized?

I would like to programmatically add new RSS Connector flows while Mule is running (after the context has been initialized). When I try to do this, I get a Lifecycle Exception saying that the context is already initialized.
Is there a way to do this without restarting the whole context?
I figured out a solution on my own. It turned out that creating a new Mule context, adding my flow, and then starting the context worked just fine. In fact, this ended up being simpler, faster, and cleaner than the other path I was going down.
Creating a default Mule context worked just fine for me. You might need to add a ConfigurationBuilder to yours if you have special needs.
MuleContext newMuleContext = new DefaultMuleContextFactory().createMuleContext();
MuleRegistry registry = newMuleContext.getRegistry();
Flow flow = createFlow();
registry.registerFlowConstruct(flow);
newMuleContext.start();
Edit. Here's the createFlow method. Your specifics will be different based on the needs of your app.
protected Flow createFlow(MuleContext context, RssBean feed) throws Exception {
MuleRegistry registry = context.getRegistry();
String feedName = feed.getName();
HttpPollingConnector connector = getHttpPollingConnector(context, registry, feedName);
EndpointURIEndpointBuilder endpointBuilder = getEndpointBuilder(context, feed, registry, shortName, connector);
registry.registerEndpointBuilder(feedName + ".in", endpointBuilder);
MessagePropertiesTransformer transformer = getTransformer(context, feedName);
MessageProcessor mp = getOutboundFlowRef(context);
Flow flow = getFlow(context, shortName, endpointBuilder, transformer, mp);
registry.registerFlowConstruct(flow);
return flow;
}

WCF: (MTOM) is there any way to change the scheme used in xop:Content reference uris generated by WCF?

WCF uses http://tempuri/1/number for Content-ID uri references when handling streamed MTOM requests.
Is there any way how to force WCF to use a different Content-ID references for the xop:Include?
Background of the problem:
I am building a .NET client for MTOM enabled jax ws java web service that handles streamed large data uploads. I have hand crafted the service and data contacts (the WSDL generated contracts were not correct and did not allow streaming).
The problem is that the web service (jax ws) does not receive the request body containing the data.
It receives the data that is transferred in headers.
We have built a java client for the ws - this one works.
I have captured and compared the HTTP traffic when issuing requests from java and wcf, and the only difference is in how Content-ID reference is generated when posting the multipart data:
WCF uses http://tempuri/1/... Content-ID references which yield in encoded value, like href="cid:http%3A%2F%2Ftempuri.org%2F1%2F634019957020047928"
Java client uses "email-style" uris, like href="cid:3f3ec388-8cd9-47aa-a603-fb1bc17935b8#example.jaxws.sun.com"
These yield in the following xop-includes (Data is the only element in the soap body) (XOP includes specification)
//WCF:
<Data>
<xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:http%3A%2F%2Ftempuri.org%2F1%2F634019957020047928" />
</Data>
//JAVA:
<Data>
<xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:3f3ec388-8cd9-47aa-a603-fb1bc17935b8#example.jaxws.sun.com"/>
</Data>
later on, in the multipart data, the content is referred to by unencoded Content-ID:
--uuid:7e166bb7-042f-4ba3-b6ef-98fbbc21244b+id=1
Content-ID: <http://tempuri.org/1/634019957020047928>
Content-Transfer-Encoding: binary
Content-Type: application/octet-stream
I guess there may be a bug in the jax web service framework and it is not recognizing WCF-generated+urlencoded Content-ID uri references.
Is there any way how to force WCF to use a different Content-ID references for the xop:Include?
EDIT: I have found the XmlMtomWriter which has the GenerateUriForMimePart method, this is used to generate Content-IDs.
public static string GenerateUriForMimePart(int index)
{
return string.Format(CultureInfo.InvariantCulture,
"http://tempuri.org/{0}/{1}", new object[] { index, DateTime.Now.Ticks });
}
It does not seem that the ID generation is in any way overridable.
A similar issue is described here, the answer provided does not help: http://social.msdn.microsoft.com/Forums/en/wcf/thread/f90affbd-f431-4602-a81d-cc66c049e351
Asnwering to myself after long investigation: Not possible without reimplementing the whole XmlMtomWriter and other related layers and concerns in WCF - almost everything involved in the mtom implementation is internal.
I know it is an old question. But I'm faced the same problem two days ago.
I found a way which works BUT it is a VERY VERY dirty hack (I know that. I thought about not publishing it here but perhaps it would help somebody.) Hopefully you will not blame me for that.
The ContentId is formatted with the use of CultureInfo.InvariantCulture. I didn't find an official way for replacing it with a custom CultureInfo. But with the help of reflection I got it running. The following implementation is only for .Net 4.0.
public class NoTempUriInvariantCultureInfo : CultureInfo, ICustomFormatter
{
private static CultureInfo originalCulture;
private static object originalCultureLock;
private static int enableCounter;
private NoTempUriInvariantCultureInfo(CultureInfo invariantCulture)
: base(invariantCulture.Name)
{
originalCulture = invariantCulture;
}
public static void Enable()
{
if(originalCultureLock == null)
originalCultureLock = new object();
lock (originalCultureLock)
{
if (enableCounter == 0)
{
var mInvCultField = typeof (CultureInfo).GetField("s_InvariantCultureInfo", BindingFlags.NonPublic | BindingFlags.Static);
mInvCultField.SetValue(null, new NoTempUriInvariantCultureInfo(CultureInfo.InvariantCulture));
}
enableCounter++;
}
}
public static void Disable()
{
lock (originalCulture)
{
if (enableCounter == 0)
return;
enableCounter--;
if (enableCounter == 0)
{
var mInvCultField = typeof (CultureInfo).GetField("s_InvariantCultureInfo", BindingFlags.NonPublic | BindingFlags.Static);
mInvCultField.SetValue(null, NoTempUriInvariantCultureInfo.originalCulture);
}
}
}
public override object GetFormat(Type formatType)
{
var result = originalCulture.GetFormat(formatType);
return result ?? this;
}
public string Format(string format, object arg, IFormatProvider formatProvider)
{
if (format == null)
return System.Text.RegularExpressions.Regex.Replace(arg.ToString().Replace("http%3A%2F%2Ftempuri.org%2F1%2F", ""), "http[:][/][/]tempuri[.]org[/][0-9]+[/]*", "");
return String.Format("{0:" + format + "}", arg);
}
}
I enable my own "InvariantCulture" only before a WCF call.
NoTempUriInvariantCultureInfo.Enable();
try
{
// make your call
}
finally
{
NoTempUriInvariantCultureInfo.Disable();
}
CultureInfo.InvariantCulture is a global state object. Enabling my own InvariantCulture affects every other thread.
Again, it is a dirty hack. But it works.
Both of the XOP includes samples that you indicated are correct and acceptable according to the W3C. I refer to them as the URL format and the Email format respectively.
I am not a JAVA developer, but recall a similiar problem when interfacing with a particular JAVA web service. I recall there being a bug in a particular JAVA release and after they (the JAVA developers) upgraded to the next release version, this issue simply went away. I wish I could provide you more details, but at the time, there were enough problems for me to address from my end of the wire and I was just glad to have one less item on the defect log.
//WCF: using URL format
<Data>
<xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:http%3A%2F%2Ftempuri.org%2F1%2F634019957020047928" />
</Data>
//JAVA: using EMAIL format
<Data>
<xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:3f3ec388-8cd9-47aa-a603-fb1bc17935b8#example.jaxws.sun.com"/>
</Data>