[ WSO2 Api Manager 4.1 / Custom Handlers] - wso2

I have developped a custom handler for my apim platform(4.1) where I call a custom class mediator in order to log MessageContext infos into a separated log file as shown below :
public void logUserRequest(MessageContext messageContext) {
Map headers = service.getTransportHeaders(messageContext);
String userId = (String) messageContext.getProperty(APIMgtGatewayConstants.USER_ID);
String api = (String) messageContext.getProperty(APIMgtGatewayConstants.API);
String apiKeyType = (String) messageContext.getProperty(AM_KEY_TYPE);
String appliName = (String) messageContext.getProperty(APIMgtGatewayConstants.APPLICATION_NAME);
String method = (String) messageContext.getProperty(APIMgtGatewayConstants.HTTP_METHOD);
String organisation = (String) messageContext.getProperty(APIMgtGatewayConstants.APPLICATION_NAME);
String version = (String) messageContext.getProperty(APIMgtGatewayConstants.VERSION);
successCallLog.info(userId + "|" + organisation + "|" + apiKeyType + "|" + method + "|" + api + "|" + version + "|" + appliName );
}
It works fine when the log mediator is called inside the handleResponse() method of my handler , but the problem with that is that I don't log the exact time of the request and it is used in another flow to calculate other statistics .
06-10-2022 10:49:00,072|admin#carbon.super|test|PRODUCTION|GET|PizzaShackAPI|1.0.0|test
in the other hand, when I call the log mediator inside the handleRequest() method of my handler , I get null values of the MessageContext properties I'm trying to log :
06-10-2022 10:46:34,311|null|null|null|null|null|null|null|null
PS:
I didn't have this problem using the same handler and mediator in apim version 3.2 .
Am I missing something ? or Did something change between the 2 versions ?

As #DushaniW explained in his comment, the MessageContext properties were not set at the moment my mediator was trying to log them , since the handler calling the mediator was engaged before the APIAuthenticationHandler, which explains null values.
I was able to correct the issue by making sure the default handler APIAuthenticationHandler is called before my custom handler.
I have updated my configuration in velocity_template.xml to engage my custom handler after the foreach loop that engage the default handlers (if my understanding is correct) .
Error :
<handlers xmlns="http://ws.apache.org/ns/synapse">
<!-- ############## CUSTOM : handler X-Correlation-ID ############### -->
<handler class="fr.XX.XX.XX.XX.handler.CustomAPIAuthenticationXCorrelationIDHandler"/>
<!-- ############## CUSTOM : handler X-Correlation-ID ############### -->
<!-- ############## Default handlers ############### -->
#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
Correction :
<handlers xmlns="http://ws.apache.org/ns/synapse">
<!-- ############## Default handlers ############### -->
#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
<!-- ############## CUSTOM : handler X-Correlation-ID ############### -->
<handler class="fr.XX.XX.XX.XX.handler.CustomAPIAuthenticationXCorrelationIDHandler"/>
<!-- ############## CUSTOM : handler X-Correlation-ID ############### -->

Related

Logging is not functioning properly after changing Logging Configuration via managemnt console in WSO2 EI 6.5.0

I have developed API which call the class mediator via sequence in WSO2 EI 6.5.0. Initially API logs are getting printed except class mediator logs in Server log.
To enable logs for class mediator as per this, I logged into management console Home> Configure> Logging section and went to section Configure Log4J Loggers , searched log keyword whatever i added inside class mediator to find out class mediator and changed class level to Debug
post this change, nothing is printed when i invoke service via postman, but API response getting. I just restarted server, post this management console url also not getting printed in server logs.
Below is the management console logging configuration image for reference.
Class Mediator:
package com.abc.in;
import org.apache.synapse.MessageContext;
import org.apache.synapse.mediators.AbstractMediator;
import org.apache.synapse.core.axis2.Axis2MessageContext;
/*import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;*/
import java.util.ArrayList;
import java.util.Map;
public class DuplicateHeadersMediator extends AbstractMediator {
// private static final Log logger = LogFactory.getLog(DuplicateHeadersMediator.class);
public boolean mediate(MessageContext messageContext) {
log.info("DuplicateHeadersMediator called********** : " );
trace.info("trace DuplicateHeadersMediator called********** :");
org.apache.axis2.context.MessageContext axis2MessageContext = ((Axis2MessageContext) messageContext)
.getAxis2MessageContext();
Map excessHeaders = (Map) axis2MessageContext.getProperty("EXCESS_TRANSPORT_HEADERS");
log.info("excessHeaders : " + excessHeaders.entrySet());
trace.info("trace excessHeaders : " + excessHeaders.entrySet());
Map transportHeaders = (Map) axis2MessageContext.getProperty("TRANSPORT_HEADERS");
log.info("transportHeaders : " + transportHeaders.entrySet());
trace.info("trace transportHeaders : " + transportHeaders.entrySet());
if (excessHeaders.size() != 0 && transportHeaders.size() != 0) {
for (Object key : transportHeaders.keySet()) {
addPropertiesForExcessHeaders((String)key,excessHeaders,messageContext);
}
}
return true;
}
// Add extra properties to the synapse message context for duplicated headers.
private void addPropertiesForExcessHeaders(String headerName, Map excessHeaders, MessageContext messageContext) {
if (excessHeaders.get(headerName) != null) {
ArrayList<String> list = (ArrayList) excessHeaders.get(headerName);
if (list.size() > 0) {
int i = 2;
for (String value : list) {
String propName = headerName + i;
messageContext.setProperty(propName, value);
log.info("propName : " + propName);
trace.info("trace propName : " + propName);
i += 1;
}
}
}
}
}
API:
<?xml version="1.0" encoding="UTF-8"?>
<api context="/readcookiesapi" name="ReadCookiesAPI" xmlns="http://ws.apache.org/ns/synapse">
<resource methods="POST">
<inSequence>
<log level="custom">
<property name="ReadCookiesAPI" value="is called *****"/>
</log>
<sequence key="HeaderMediatorCall_Sequecne"/>
<log level="custom">
<property expression="$trp:test" name="test1"/>
<property expression="$ctx:test2" name="test2"/>
<property expression="$ctx:test3" name="test3"/>
</log>
<respond/>
</inSequence>
<outSequence/>
<faultSequence/>
</resource>
</api>
Sequence:
<?xml version="1.0" encoding="UTF-8"?>
<sequence name="HeaderMediatorCall_Sequecne" trace="disable" xmlns="http://ws.apache.org/ns/synapse">
<log level="custom">
<property name="HeaderMediatorCall_Sequecne" value="B4 *****"/>
</log>
<class name="com.abc.in.DuplicateHeadersMediator"/>
<log level="custom">
<property name="HeaderMediatorCall_Sequecne" value="after *****"/>
</log>
</sequence>
Kindly clarify my doubts mentioned below.
how can I recover default logging mechanism since this change made product logging weird, so that artifacts like API, Sequence etc and server logs i'll get properly
why class mediator logs are not getting printed initially or how can i get those class mediators log in wso2 ei server 6.5.0
When you extend AbstractMediator you should already have an instance of the logger for you to use. Check here. So you don't have to instantiate a new Logger. You can simply use the existing logger.
I have used java.util.logging.Logger to reflect logger inside class mediator. Still not sure why org.apache.commons.logging.LogFactory not printing anything in log.
import java.util.logging.Logger;
public class ParseEmailBody extends AbstractMediator {
private static final Logger logger = Logger.getLogger(ParseEmailBody.class.getName());
public boolean mediate(MessageContext context) {
logger.info("===Inside ParseEmailBody Class Mediator===");
return true;
}

How to get Request payload content at Response path class mediator using OMElement - WSO2 APIM ver 3.2.0

I am using WSO2 APIM version 3.2.0.
I have a POST request with the request payload.
In the response message mediation of WSO2 APIM I have added the policy that contains the class mediator that tries to get the payload sent during the request.
OMElement element = (OMElement) mc.getEnvelope().getBody().getFirstOMChild();
log.info("payload: " + element.toString());
The above code snippet prints the response payload content but I need the request payload content at the response path.
Response message mediation with a policy added
Below is the sequence with class mediator
sequence with class mediator
Code snippet inside class mediator
OMElement element = (OMElement) mc.getEnvelope().getBody().getFirstOMChild();
log.info("payload: " + element.toString());
Pls let me know what changes to be done, to get the request payload content.
First, we have to store the request payload in a custom property in the Message Context. Then, we can use that property to retrieve the Request Payload in the Response path of the execution.
For example: You are invoking an API with JSON Payload. So, we have to first capture the sent payload and store it in a custom property in the Message Context. Given below is a sample sequence to perform the same
<?xml version="1.0" encoding="UTF-8"?>
<sequence xmlns="http://ws.apache.org/ns/synapse" name="admin--MockAPI:v1.0.0--In">
<property name="RequestPayload" expression="json-eval($)" />
<log level="custom">
<property name="RequestPayload" expression="$ctx:RequestPayload" />
</log>
</sequence>
Then, in the Response path, inside your custom class mediator, you have to access the RequestPayload property from the MessageContext to extract the stored payload. You can achieve this by using the following snippet
synapseContext.getProperty("RequestPayload");

Setting SOAP Envelope Payload in a Custom Mediator

I am writing a proxy service in WSO2 ESB that accepts a JSON payload and performs some transformations (to a SOAP message) that are quite complex, so we are writing the transformation logic in a Custom Mediator.
As you can see, my custom mediator class sets a property in the message context, and the proxy flow extracts this property and sets the payload (using a Javascript API that I could find).
This results in my SOAP message being "double wrapped" in two envelope tags, and I need to use an enricher with a XPath expression to remove the outer envelope/body tags.
Is it possible to set the XML payload within the custom mediator, thus avoiding to read a property and writing the XML payload in the proxy flow?
The proxy code is listed below:
<?xml version="1.0" encoding="UTF-8"?>
<proxy name="stackOverflowProxy" startOnLoad="true" trace="disable"
transports="jms" xmlns="http://ws.apache.org/ns/synapse">
<target>
<inSequence>
<!-- process incoming request in custom class. the payload is a JSON object -->
<class name="stackOverflow.CustomMediator"/>
<!-- set registration xml string as xml payload -->
<script language="js"><![CDATA[mc.setPayloadXML(new XML(mc.getProperty('mediatorPayload')));]]></script>
<enrich>
<source clone="true"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xpath="$body//soapenv:Envelope/soapenv:Body/*"/>
<target type="body"/>
</enrich>
</inSequence>
<outSequence/>
<faultSequence/>
</target>
</proxy>
And this is the Custom Mediator class:
package stackOverflow;
// imports ...
public class CustomMediator extends AbstractMediator {
public boolean mediate(MessageContext messageContext) {
//messageContext has a json object property
//message is processed and transformed to a SOAPEnvelope (namespaces omitted for simplicity)
//soapEnvelopeString = <Envelope><Body><tag>value</tag></Body></Envelope>
messageContext.setProperty("mediatorPayload", soapEnvelope.toString());
return true;
}
}
In your custom mediator you are setting entire soap message in to a property and in script mediator setting it as payload of the soap message. thats why you are getting double soap env.
in your class mediator set only the body payload to property and then insert that value as payload in main flow using script will solve your issue.
package stackOverflow;
// imports ...
public class CustomMediator extends AbstractMediator {
public boolean mediate(MessageContext messageContext) {
//messageContext has a json object property
//message is processed and transformed to a SOAPEnvelope (namespaces omitted for simplicity)
//**soapPayload = <tag>value</tag>**
messageContext.setProperty("mediatorPayload", soapPayload);
return true;
}
}

How can i have two separate web services with identical name space and local name requests be routed to different end points?

I'm attempting to create 2 separate web services, both within one spring deployment, both with the wsdl's being generated from the same xsd schemas, yet have them be routed to two separate end points so i can handle the requests differently in the separate contexts.
Ex:
Webservice 1: subset of access, lower privileges and security constraints
Webservice 2: higher privileges
<sws:dynamic-wsdl id="spml-readonly"
portTypeName="SpmlReadOnlyService"
locationUri="SpmlReadOnly">
<sws:xsd location="/WEB-INF/xsd/spml/pstc_spmlv2_core.xsd"/>
</sws:dynamic-wsdl>
<sws:dynamic-wsdl id="spml-crud"
portTypeName="SpmlCrudService"
locationUri="SpmlCrud">
<sws:xsd location="/WEB-INF/xsd/spml/pstc_spmlv2_core.xsd"/>
<sws:xsd location="/WEB-INF/xsd/spml/pstc_spmlv2_search.xsd"/>
<sws:xsd location="/WEB-INF/xsd/spml/pstc_spmlv2_batch.xsd"/>
</sws:dynamic-wsdl>
Now since both wsdls are based off of the same xsds, the 'namespace' and 'localPart" of the requests come across the wire identical, regardless of which web service i'm hitting (/SpmlReadOnly or /SpmlCrud).
Therefore, that's ruling out the deprecated PayloadRootQNameEndpointMapping since the localPart and namespace are still identical, etc,... and my current config simply routes the requests to the same endpoint method handler, and i have no way of distinguishing which web service was called:
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "lookupRequest")
#ResponsePayload
public Source handleLookupRequest(SoapMessage message) throws Exception {
...
}
Is what I'm able to do even possible? If the xsd's are shared and have identical namespaces at the root of the schema, and the same localPart method requests, will there ever be a way to distinguish between them and map to two different end points? Any information on this would be useful! I'm hoping i don't have to set up two separate .wars and deploy them separately with their own code bases on a server!
Thanks,
Damian
You need something that combines URI and PayloadRoot mapping. Unfortunately Spring-Ws doesn't have something like this. But because it's very extensible it's really easy to achieve this.
TL;DR
See This branch at GitHub for working example
Details
You need to create mapping of combined URI+QName to org.springframework.ws.server.endpoint.MethodEndpoint instances. Also you should minimize the code which would duplicate existing Spring-Ws functions.
So 1) You need to explicitly configure Spring-Ws annotations without using <sws:annotation-driven />:
This is your requirement (with my schemas):
<ws:dynamic-wsdl id="spml-readonly" portTypeName="SpmlReadOnlyService" locationUri="SpmlReadOnly">
<ws:xsd location="classpath:springws/model/schema.xsd" />
</ws:dynamic-wsdl>
<ws:dynamic-wsdl id="spml-crud" portTypeName="SpmlCrudService" locationUri="SpmlCrud">
<ws:xsd location="classpath:springws/model/schema.xsd" />
<ws:xsd location="classpath:springws/model/schema2.xsd" />
</ws:dynamic-wsdl>
This is all you need to do by hand which normally is configured by <sws:annotation-driven /> (one adapter with one JAXB marshaller):
<bean class="org.springframework.ws.server.endpoint.adapter.DefaultMethodEndpointAdapter">
<property name="methodArgumentResolvers">
<list>
<ref local="marshallingPayloadMethodProcessor"/>
</list>
</property>
<property name="methodReturnValueHandlers">
<list>
<ref local="marshallingPayloadMethodProcessor"/>
</list>
</property>
</bean>
<bean id="marshallingPayloadMethodProcessor" class="org.springframework.ws.server.endpoint.adapter.method.MarshallingPayloadMethodProcessor">
<property name="marshaller" ref="marshaller" />
<property name="unmarshaller" ref="marshaller" />
</bean>
<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="contextPaths">
<list>
<value>springws.model</value>
</list>
</property>
</bean>
This is custom mapping:
<bean class="springws.PathAndPayloadRootAnnotationEndpointMapping" />
And 2) You should create your own mapping
public class PathAndPayloadRootAnnotationEndpointMapping extends PayloadRootAnnotationMethodEndpointMapping
{
#Override
protected QName getLookupKeyForMessage(MessageContext messageContext) throws Exception
{
String urlPart = "";
QName payloadRootPart = super.getLookupKeyForMessage(messageContext);
TransportContext transportContext = TransportContextHolder.getTransportContext();
if (transportContext != null) {
WebServiceConnection connection = transportContext.getConnection();
if (connection != null && connection instanceof HttpServletConnection) {
String requestURI = ((HttpServletConnection)connection).getHttpServletRequest().getRequestURI();
String contextPath = ((HttpServletConnection)connection).getHttpServletRequest().getContextPath();
urlPart = requestURI.substring(contextPath.length());
}
}
return new QName(payloadRootPart.getNamespaceURI(), urlPart + "/" + payloadRootPart.getLocalPart());
}
#Override
protected List<QName> getLookupKeysForMethod(Method method)
{
List<QName> result = new ArrayList<QName>();
RequestMapping rm = AnnotationUtils.findAnnotation(method.getDeclaringClass(), RequestMapping.class);
String urlPart = rm == null || rm.value().length != 1 ? "" : rm.value()[0];
List<QName> methodPart = super.getLookupKeysForMethod(method);
for (QName qName : methodPart) {
result.add(new QName(qName.getNamespaceURI(), urlPart + "/" + qName.getLocalPart()));
}
return result;
}
}
which extends org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping. And all it does is extending the keys (QNames of payload root elements) of messages with the information extracted from the endpoint URI. I've used Spring's #org.springframework.web.bind.annotation.RequestMapping annotation for that, but someone thinking it's a hack may create his/her own annotation.
So for endpoint like this:
#org.springframework.ws.server.endpoint.annotation.Endpoint
#RequestMapping("/ws/SpmlReadOnly")
public class Endpoint1
{
#ResponsePayload
#PayloadRoot(namespace = "urn:test", localPart = "method1Request")
public Response2 method(#RequestPayload Request1 request) throws Exception
{
return new Response2("e1 m1");
}
}
the key is not:
namespace = urn:test
localName = method1Request
but this:
namespace = urn:test
localName = /ws/SpmlReadOnly/method1Request
The protected QName getLookupKeyForMessage(MessageContext messageContext) method ensures that the mapping URI is independent of the WAR context, the application is deployed at.

EntityManagerFactory not being injected using #PersistenceUnit

I'm a java beginner. I'm in trouble to configure a persistance unit using JTA transactions.
I need to use a PostgreSQL database that is already defined, configured and populated. Using netbeans, i created the persistance.xml and glassfish-resources.xml as fallows:
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="WellWatcherPU" transaction-type="JTA">
<jta-data-source>WellWatcherDB</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="eclipselink.logging.logger" value="org.eclipse.persistence.logging.DefaultSessionLog"/>
<property name="eclipselink.logging.level" value="FINE"/>
</properties>
</persistence-unit>
</persistence>
and
<resources>
<jdbc-connection-pool allow-non-component-callers="false" associate-with-thread="false" connection-creation-retry-attempts="0" connection-creation-retry-interval-in-seconds="10" connection-leak-reclaim="false" connection-leak-timeout-in-seconds="0" connection-validation-method="auto-commit" datasource-classname="org.postgresql.ds.PGSimpleDataSource" fail-all-connections="false" idle-timeout-in-seconds="300" is-connection-validation-required="false" is-isolation-level-guaranteed="true" lazy-connection-association="false" lazy-connection-enlistment="false" match-connections="false" max-connection-usage-count="0" max-pool-size="32" max-wait-time-in-millis="60000" name="post-gre-sql_geowellex_geowellexPool" non-transactional-connections="false" pool-resize-quantity="2" res-type="javax.sql.DataSource" statement-timeout-in-seconds="-1" steady-pool-size="8" validate-atmost-once-period-in-seconds="0" wrap-jdbc-objects="false">
<property name="serverName" value="localhost"/>
<property name="portNumber" value="5432"/>
<property name="databaseName" value="DBNAME"/>
<property name="User" value="USER"/>
<property name="Password" value="PASSWORD"/>
<property name="URL" value="jdbc:postgresql://localhost:5432/DBNAME"/>
<property name="driverClass" value="org.postgresql.Driver"/>
</jdbc-connection-pool>
<jdbc-resource enabled="true" jndi-name="WellWatcherDB" object-type="user" pool-name="post-gre-sql_geowellex_geowellexPool"/>
</resources>
And this is how i get the EntityManagerFactory and EntityManager (as used in the netBeans example)
public class EUserDao {
#Resource
private UserTransaction utx = null;
#PersistenceUnit(unitName = "WellWatcherPU")
private EntityManagerFactory emf = null;
public EntityManager getEntityManager() {
return emf.createEntityManager(); <-------- NullPointerException here
}
public EUser getOne(long userId){
EntityManager em = getEntityManager();
try {
return em.find(EUser.class, userId);
} finally {
em.close();
}
}
EDIT:
And here is my glassfish deploy log:
Informações: [EL Config]: 2012-05-10 12:01:13.534--ServerSession(2017352940)--Connection(1901223982)--Thread(Thread[admin-thread-pool-4848(5),5,grizzly-kernel])--connecting(DatabaseLogin(
platform=>DatabasePlatform
user name=> ""
connector=>JNDIConnector datasource name=>null
))
Informações: [EL Config]: 2012-05-10 12:01:13.534--ServerSession(2017352940)--Connection(1462281761)--Thread(Thread[admin-thread-pool-4848(5),5,grizzly-kernel])--Connected: jdbc:postgresql://localhost:5432/geowellex?loginTimeout=0&prepareThreshold=0
User: geowellex
Database: PostgreSQL Version: 9.1.3
Driver: PostgreSQL Native Driver Version: PostgreSQL 8.3 JDBC3 with SSL (build 603)
Informações: [EL Config]: 2012-05-10 12:01:13.534--ServerSession(2017352940)--Connection(766700859)--Thread(Thread[admin-thread-pool-4848(5),5,grizzly-kernel])--connecting(DatabaseLogin(
platform=>PostgreSQLPlatform
user name=> ""
connector=>JNDIConnector datasource name=>null
))
What's wrong?
Most likely problem is that your EUserDao is just regular class. Injection works only for container managed classes. Annotations like #PersistenceUnit and #Resource are not processed for normal classes.
Following types of classes are container managed classes (and in those #PersistenceUnit can be used):
Servlet: servlets, servlet filters, event listeners
JSP: tag handlers, tag library event listeners
JSF: scoped managed beans
JAX-WS: service endpoints, handlers
EJB: beans, interceptors
Managed Beans: managed beans
CDI: CDI-style managed beans, decorators
Java EE Platform: main class (static), login callback handler
I see that in your code declare:
private EntityManagerFactory emf = null;
but never create one... like this
emf = Persistence.createEntityManagerFactory("WellWatcherPU");
Thats why you get a Null Pointer Exception when use the object!
public EntityManager getEntityManager() {
return emf.createEntityManager(); <-------- NullPointerException here
}