• About

On Technology

~ SOA and Integration blog

On Technology

Category Archives: Java

Enterprise Integration Patterns (EIP) with OpenESB Part 2 : Dynamic Router

01 Friday Jun 2012

Posted by Padmarag Lokhande in BPEL, Integration, Java, SOA

≈ 3 Comments

This is second of the series Enterprise Integration Patterns (EIP) with OpenESB where we will cover Enterprise Integration Patterns using OpenESB.


According to Enterprise Integration Patterns,

The Dynamic Router is a Router that can self-configure based on special configuration messages from participating destinations.

Dynamic Router

We can realize this pattern in OpenESB using Dynamic Addressing which uses WS-Addressing to dynamically determine the service location at runtime.

To check this in action we need 3 Projects –

  • EJB Module implementing the Web Service – you can use any WSDL based web service, not necessary to use EJB.
  • BPEL – This will contain the actual code that determines the endpoint at runtime.
  • JBI / CASA – This is a composite application necessary to deploy the BPEL.

Step 1 – We create a web service with 2 operations – addition and subtraction. Both operations take 2 numbers as input and return result.
Here’s the xsd –

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://xml.aurorite.com/schema/numberBase"
    xmlns:tns="http://xml.aurorite.com/schema/numberBase"
    elementFormDefault="qualified">

    <xsd:element name="NumbersAdditionRequest" type="tns:AdditionType"/>
    <xsd:element name="NumbersAdditionResponse" type="tns:OperationResultType"/>
    <xsd:element name="NumbersSubtractionRequest" type="tns:SubtractionType"/>
    <xsd:element name="NumbersSubtractionResponse" type="tns:OperationResultType"/>

    <xsd:complexType name="AdditionType">
        <xsd:sequence>
            <xsd:element name="number1" type="xsd:int"/>
            <xsd:element name="number2" type="xsd:int"/>
        </xsd:sequence>
    </xsd:complexType>

    <xsd:complexType name="SubtractionType">
        <xsd:sequence>
            <xsd:element name="number1" type="xsd:int"/>
            <xsd:element name="number2" type="xsd:int"/>
        </xsd:sequence>
    </xsd:complexType>

    <xsd:complexType name="OperationResultType">
        <xsd:sequence>
            <xsd:element name="result" type="xsd:int"/>
            <xsd:element name="processor" type="xsd:string"/>
        </xsd:sequence>
    </xsd:complexType>
</xsd:schema>

and here’s the wsdl –

<?xml version="1.0" encoding="UTF-8"?>
<definitions name="NumbersOperations"
    targetNamespace="http://websvcs.aurorite.com/wsdl/NumberOperationSvcs/NumbersOperations"
    xmlns="http://schemas.xmlsoap.org/wsdl/"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:tns="http://websvcs.aurorite.com/wsdl/NumberOperationSvcs/NumbersOperations"
    xmlns:ns="http://xml.aurorite.com/schema/numberBase"
    xmlns:plnk="http://docs.oasis-open.org/wsbpel/2.0/plnktype"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
    <types>
        <xsd:schema targetNamespace="http://websvcs.aurorite.com/wsdl/NumberOperationSvcs/NumbersOperations">
            <xsd:import namespace="http://xml.aurorite.com/schema/numberBase" schemaLocation="numberBase.xsd"/>
        </xsd:schema>
    </types>
    <message name="additionRequest">
        <part name="part1" element="ns:NumbersAdditionRequest"/>
    </message>
    <message name="additionResponse">
        <part name="part1" element="ns:NumbersAdditionResponse"/>
    </message>
    <message name="subtractionRequest">
        <part name="part1" element="ns:NumbersSubtractionRequest"/>
    </message>
    <message name="subtractionResponse">
        <part name="part1" element="ns:NumbersSubtractionResponse"/>
    </message>
    <portType name="NumbersOperationsPortType">
        <operation name="addition">
            <input name="input1" message="tns:additionRequest"/>
            <output name="output1" message="tns:additionResponse"/>
        </operation>
        <operation name="subtraction">
            <input name="input2" message="tns:subtractionRequest"/>
            <output name="output2" message="tns:subtractionResponse"/>
        </operation>
    </portType>
    <binding name="NumbersOperationsBinding" type="tns:NumbersOperationsPortType">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
        <operation name="addition">
            <soap:operation/>
            <input name="input1">
                <soap:body use="literal"/>
            </input>
            <output name="output1">
                <soap:body use="literal"/>
            </output>
        </operation>
        <operation name="subtraction">
            <soap:operation/>
            <input name="input2">
                <soap:body use="literal"/>
            </input>
            <output name="output2">
                <soap:body use="literal"/>
            </output>
        </operation>
    </binding>
    <service name="NumbersOperationsService">
        <port name="NumbersOperationsPort" binding="tns:NumbersOperationsBinding">
            <soap:address location="http://localhost:8080/NumbersOperationsService/NumberService"/>
        </port>
    </service>
    <plnk:partnerLinkType name="NumbersOperations">
        <!-- A partner link type is automatically generated when a new port type is added. Partner link types are used by BPEL processes. 
In a BPEL process, a partner link represents the interaction between the BPEL process and a partner service. Each partner link is associated with a partner link type.
A partner link type characterizes the conversational relationship between two services. The partner link type can have one or two roles.-->
        <plnk:role name="NumbersOperationsPortTypeRole" portType="tns:NumbersOperationsPortType"/>
    </plnk:partnerLinkType>
</definitions>

Here is the implementation of the webservice operations –

@WebService(serviceName = "NumbersOperationsService", portName = "NumbersOperationsPort", endpointInterface = "com.aurorite.websvcs.wsdl.numberoperationsvcs.numbersoperations.NumbersOperationsPortType", targetNamespace = "http://websvcs.aurorite.com/wsdl/NumberOperationSvcs/NumbersOperations", wsdlLocation = "META-INF/wsdl/NumberService/NumbersOperations.wsdl")
@Stateless
public class NumberService {

    Logger logger = Logger.getLogger("soa.aurorite.com.NumberService");

    public com.aurorite.xml.schema.numberbase.OperationResultType addition(com.aurorite.xml.schema.numberbase.AdditionType part1) throws UnknownHostException {
        logger.info("Running this service at " + InetAddress.getLocalHost().getHostName() + " on port 8080");
        com.aurorite.xml.schema.numberbase.OperationResultType result = new com.aurorite.xml.schema.numberbase.OperationResultType();
        result.setProcessor(InetAddress.getLocalHost().getHostName());
        try {
            result.setResult(part1.getNumber1() + part1.getNumber2());
        } catch (Exception e){
            result.setResult(0);
        }

        return result;
        //throw new UnsupportedOperationException("Not implemented yet.");
    }

    public com.aurorite.xml.schema.numberbase.OperationResultType subtraction(com.aurorite.xml.schema.numberbase.SubtractionType part1) throws UnknownHostException {
        logger.info("Running this service at " + InetAddress.getLocalHost().getHostName() + " on port 8080");
        com.aurorite.xml.schema.numberbase.OperationResultType result = new com.aurorite.xml.schema.numberbase.OperationResultType();
        result.setProcessor(InetAddress.getLocalHost().getHostName());
        try {
            result.setResult(part1.getNumber1() - part1.getNumber2());
        } catch (Exception e){
            result.setResult(0);
        }
        return result;
        //throw new UnsupportedOperationException("Not implemented yet.");
    }

}

Note that I’m returning the machine name in the response. This’ll help us to determine who actually processed a particular operation.

Now to the BPEL. We’ll create a simple process looking like this –
Dynamic Router BPEL

In the BPEL, we add these 2 namespace references –

xmlns:sref="http://docs.oasis-open.org/wsbpel/2.0/serviceref" 
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
    <import namespace="http://docs.oasis-open.org/wsbpel/2.0/serviceref" location="ws-bpel_serviceref.xsd" importType="http://www.w3.org/2001/XMLSchema"/>
    <import namespace="http://schemas.xmlsoap.org/ws/2004/08/addressing" location="addressing.xsd" importType="http://www.w3.org/2001/XMLSchema"/>

And now the most important part – Adding the Service Endpoint Reference

<assign name="assign_endpoint">
    <copy>
                        <!--<from>ns2:doXslTransform('urn:stylesheets:wrap2serviceref.xsl', $EPRVariable.eprValue)</from>-->
        <from>
            <literal>
                <sref:service-ref>
                    <wsa:EndpointReference>
                        <wsa:Address>http://machine2:8080/NumbersOperationsService/NumberService</wsa:Address>
                        <wsa:ServiceName PortName="NumbersOperationsPort"
                                        xmlns:serv="http://websvcs.aurorite.com/wsdl/NumberOperationSvcs/NumbersOperations">serv:NumbersOperationsService
                        </wsa:ServiceName>
                    </wsa:EndpointReference>
                </sref:service-ref>
            </literal>
        </from>
        <to partnerLink="NumberService"/>
    </copy>
</assign>

The portname and Service name must match to the ones in target service.

Advertisement

Share this:

  • Click to share on Twitter (Opens in new window)
  • Click to share on Facebook (Opens in new window)
  • Click to share on Tumblr (Opens in new window)
  • Click to share on LinkedIn (Opens in new window)
  • Click to share on Reddit (Opens in new window)
  • Click to share on Pinterest (Opens in new window)
  • Click to email a link to a friend (Opens in new window)
  • Click to print (Opens in new window)

Like this:

Like Loading...

Enterprise Integration Patterns (EIP) with OpenESB Part 1 : Content-Based Router

30 Wednesday May 2012

Posted by Padmarag Lokhande in BPEL, Integration, Java, SOA

≈ 1 Comment

This is first of the series Enterprise Integration Patterns with OpenESB where we will cover Enterprise Integration Patterns using OpenESB.


According to Enterprise Integration Patterns,

The Content-Based Router examines the message content and routes the message onto a different channel based on data contained in the message. The routing can be based on a number of criteria such as existence of fields, specific field values etc. When implementing a Content-Based Router, special caution should be taken to make the routing function easy to maintain as the router can become a point of frequent maintenance. In more sophisticated integration scenarios, the Content-Based Router can take on the form of a configurable rules engine that computes the destination channel based on a set of configurable rules.

Content Based Router

It can be achieved in OpenESB using “If” component.

In this example it simply checks for the value in “num:operation” element.

<soapenv:Envelope xsi:schemaLocation="http://schemas.xmlsoap.org/soap/envelope/ http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:num="http://xml.aurorite.com/schema/NumberOperationSchema">
  <soapenv:Body>
    <num:OperationRequest>
      <num:operation>ADDITION</num:operation>
      <num:data>
        <num:number1>20</num:number1>
        <num:number2>30</num:number2>
      </num:data>
    </num:OperationRequest>
  </soapenv:Body>
</soapenv:Envelope>     

Share this:

  • Click to share on Twitter (Opens in new window)
  • Click to share on Facebook (Opens in new window)
  • Click to share on Tumblr (Opens in new window)
  • Click to share on LinkedIn (Opens in new window)
  • Click to share on Reddit (Opens in new window)
  • Click to share on Pinterest (Opens in new window)
  • Click to email a link to a friend (Opens in new window)
  • Click to print (Opens in new window)

Like this:

Like Loading...

Enterprise Integration Patterns (EIP) with OpenESB

07 Wednesday Sep 2011

Posted by Padmarag Lokhande in Integration, Java, SOA

≈ 2 Comments

Tags

enterprise integration patterns openesb soa

This is a series which’ll cover Enterprise Integration Patterns (EIP) using OpenESB.

Part 1:Enterprise Integration Patterns (EIP) with OpenESB Part 1 : Content-Based Router

Share this:

  • Click to share on Twitter (Opens in new window)
  • Click to share on Facebook (Opens in new window)
  • Click to share on Tumblr (Opens in new window)
  • Click to share on LinkedIn (Opens in new window)
  • Click to share on Reddit (Opens in new window)
  • Click to share on Pinterest (Opens in new window)
  • Click to email a link to a friend (Opens in new window)
  • Click to print (Opens in new window)

Like this:

Like Loading...

Siebel CRM On Demand Web Services – Contact Search using SoapUI

11 Monday Apr 2011

Posted by Padmarag Lokhande in Camel, Integration, Java, Siebel, SOA

≈ 10 Comments

Tags

integration, siebel, soa, soapui, web service

Before we begin using Siebel CRM On Demand’s web services, we require the WSDL files. The WSDL files are specific to the account and can be downloaded by logging in to Siebel COD.

Create a new SoapUI project and add the WSDL as initial WSDL. SoapUI will create a new project with placeholder requests for all operations. Let’s open ContactQueryPage operation from SoapUI.

The endpoint path will be something like – https://secure-ausoxxxxx.crmondemand.com/Services/Integration
This path is  unique to your instance of Siebel COD.

Next lets remove the body of input and replace it with –

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body>
     <con:ContactQueryPage_Input xmlns:con="urn:crmondemand/ws/ecbs/contact/" xmlns:quer="urn:/crmondemand/xml/Contact/Query">
        <quer:ListOfContact startrownum="0" pagesize="100" recordcountneeded="true">
            <quer:Contact searchspec="[ContactFirstName] LIKE 'John'">
                <quer:Id/>
                <quer:ContactFirstName/>
                <quer:ContactLastName/>
                <quer:AccountId/>
            </quer:Contact>
        </quer:ListOfContact>
    </con:ContactQueryPage_Input>
</soapenv:Body>
</soapenv:Envelope>

This results in Soap Fault response with Siebel error code SBL-ODU-01006 and message : “Internal Error: Session is not available. Aborting.”
This is because we still haven’t added username/password for authentication. There is another option to first get the session and then pass that value in header of the request. However I prefer to use the Stateless version above, as it separates the responsibility of handling session.

Let’s make 2 changes to fix this error.
1) Replace first line of the SOAP request with –

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/07/secext">

2) Add these lines just below the SOAP Envelope element –

<soapenv:Header>
   <wsse:Security>
      <wsse:UsernameToken>
         <wsse:Username>your_siebel_username</wsse:Username>
         <wsse:Password Type="wsse:PasswordText">your_siebel_password</wsse:Password>
      </wsse:UsernameToken>
   </wsse:Security>
</soapenv:Header>

What we’ve done here is added WS-SECURITY headers to the SOAP request.
Now the request should run just fine.

Note –
1) You need to add any fields you need to the request before they can be returned by Siebel CRM. e.g., <ContactFirstName/>
2) Its better to confirm data actually exists by logging through web-console.

Share this:

  • Click to share on Twitter (Opens in new window)
  • Click to share on Facebook (Opens in new window)
  • Click to share on Tumblr (Opens in new window)
  • Click to share on LinkedIn (Opens in new window)
  • Click to share on Reddit (Opens in new window)
  • Click to share on Pinterest (Opens in new window)
  • Click to email a link to a friend (Opens in new window)
  • Click to print (Opens in new window)

Like this:

Like Loading...

Reading WSDL

11 Friday Mar 2011

Posted by Padmarag Lokhande in Java, SOA

≈ Leave a comment

Tags

soa, web service, wsdl

While SOA offers all the goodies of loose-coupling, it does require understanding of some basic concepts and formats.
SOAP and WSDL are the prime candidates (or culprits, depending on your view)

SOAP is just a protocol for passing information, important is WSDL.

A WSDL(Web Service Description Language) file defines the contract for web service. It is just like the menu in a restaurant or a table of contents of a book. It tells about what is offered.

Without wasting more time, lets just get to work – understanding WSDL.

A WSDL file may be provided to you or it can generally be accessed by appending “?wsdl” at the end of a web service address.

A WSDL file is best-read bottom-up. It consists of abstract as well as concrete parts. Important parts to focus are (concrete)-

  • wsdl:service – this is the tag which tells you about the name of the service. Consider it synonymous with class file.
  • wsdl:port or port – this tag tells about the place where you can connect to. There can be multiple ports. Each port will have an “address” element. The location attribute specifies the endpoint address of the service. Pay attention the binding attribute of the port.
  • wsdl:binding – this tag refers to the actual implementation of the service. It’ll be further up in the wsdl document. It contains the methods of the service.
  • wsdl:operation – this tag refers to the operations provided by the port. wsdl:operation will contain soap:operation or similar tag depending on the binding.
  • wsdl:input and wsdl:output – specifies the form of input/output. This is because it can be either literal or encoded.

Now focus on the Type attribute of the wsdl:binding, and trace it up in the document. It leads you to portType element. portType is the abstract part of the wsdl.

  • portType – is something like an Interface, in fact it is called as interface in wsdl 2.0. It’ll have a no. of operations.
  • operation – specifies input and output message variable types. These types are specified further up in the wsdl.

Share this:

  • Click to share on Twitter (Opens in new window)
  • Click to share on Facebook (Opens in new window)
  • Click to share on Tumblr (Opens in new window)
  • Click to share on LinkedIn (Opens in new window)
  • Click to share on Reddit (Opens in new window)
  • Click to share on Pinterest (Opens in new window)
  • Click to email a link to a friend (Opens in new window)
  • Click to print (Opens in new window)

Like this:

Like Loading...

Call Oracle PL/SQL procedure with XMLType from Java using JDBC

11 Friday Mar 2011

Posted by Padmarag Lokhande in Database, Integration, Java, Oracle

≈ 2 Comments

Tags

jdbc, oracle, oracleaq, plsql, stored procedure, xmltype

Recently I needed to call a stored procedure which had Oracle’s XMLType as IN and OUT parameters.

The first thing to do is add xdb.jar and xmlparserv2.jar file to your application lib. These contain the required class files for Oracle XML API. the jars can be found under your installation of oracle client lib folders. Also don’t forget to add oracle jdbc driver files – ojdbc6 or ojdbc14.

The code I setup was this –

XMLType reqInXml;
XMLType reqOutXML;
String atpInStr = "";
OpResponse output = null;

try {
   reqInStr = jaxbMarshalRequestToString(input);
   System.out.println("Input : " + reqInStr);
} catch (JAXBException ex) {
   logger.log(Level.SEVERE, null, ex);
} catch (IOException ex) {
   logger.log(Level.SEVERE, null, ex);
}

//input.getHeader().setNotes("THIS VALUE RETURNED BY FACADE : " + XXDS);
try {
   Connection con = XXDS.getConnection();
   //The IN parameter for stored proc is Oracle XDB XMLType
   reqInXml = XMLType.createXML(con, reqInStr);
   OracleCallableStatement stmt = (OracleCallableStatement) con.prepareCall("call DEMO_PROC.ProcessXMLRequest(?, ?, ?, ?)");
   stmt.setObject(1, reqInXml);

   //set out parameters
   stmt.registerOutParameter (2, OracleTypes.OPAQUE,"SYS.XMLTYPE");
   stmt.registerOutParameter(3, Types.INTEGER);
   stmt.registerOutParameter(4, Types.VARCHAR);

   stmt.executeQuery();
   int resultCode = stmt.getInt(3);
   String resultMsg = stmt.getString(4);
   System.out.println("result code : " + resultCode);
   System.out.println("result msg : " + resultMsg);
   if (resultCode == 101 || resultCode == 100){
      reqOutXML = XMLType.createXML(stmt.getOPAQUE(2));
      System.out.println("Output from ERP :" + reqOutXML.getStringVal());
      output = jaxbUnmarshalFromString(reqOutXML.getStringVal());
   }
}
....

This gave me “java.lang.ClassCastException: com.sun.gjc.spi.jdbc40.ConnectionHolder40 cannot
be cast to oracle.jdbc.OracleConnection” Exception. The connection was being returned from GlassFish JDBC ConnectionPool and was instance of OracleConnectionPooldataSource.
The FIX proved tricky, but in the end it was simple –
Do this –

OracleConnection oraCon = con.unwrap(OracleConnection.class);
//The IN parameter for stored proc is Oracle XDB XMLType
atpInXml = XMLType.createXML(oraCon, atpInStr);

Pass the cast instance of OracleConnection to the XMLType API.

Share this:

  • Click to share on Twitter (Opens in new window)
  • Click to share on Facebook (Opens in new window)
  • Click to share on Tumblr (Opens in new window)
  • Click to share on LinkedIn (Opens in new window)
  • Click to share on Reddit (Opens in new window)
  • Click to share on Pinterest (Opens in new window)
  • Click to email a link to a friend (Opens in new window)
  • Click to print (Opens in new window)

Like this:

Like Loading...

Connect to GlassFishMQ / OpenMQ from ActiveMQ using Camel configuration

06 Monday Dec 2010

Posted by Padmarag Lokhande in Camel, Integration, Java, JMS, Oracle, SOA

≈ Leave a comment

Tags

activemq, camel, glassfishmq, jms, openmq, soa

I was working on a solution based on ActiveMQ where we required to pull messages from GlassFishMQ (based on OpenMQ) and put them on OracleAQ.

Now, ActiveMQ uses Camel ESB for internal message routing, this proved useful. However try as I might, I couldn’t find a simple solution to what I wanted to achieve.

My target was to configure ActiveMQ broker which’ll act as a message router and decouple the 2 systems. Both would be unaware of its presence. Sounds really good in theory providing loose coupling.

But the difficult part was finding documentation. All the documentation available was about using ActiveMQ as JMS provider in GlassFish. Not what I was looking at.

So finally after much difficulty, I found the solution. It is simple but difficult to find.

Step 1) Copy imq.jar to ActiveMQ lib directory. The jar is found in GLASSFISH_HOME/imq/lib

Step 2) Add below lines to your activemq configuration file –

This line creates a default connectionFactory using host localhost and port 7676

<bean id="connectionFactoryOpenMQQueue"/>

 

This part creates a bean with credentials

<bean id="openMQQueueCredentials">
<property name="targetConnectionFactory">
<ref bean="connectionFactoryOpenMQQueue"/>
</property>
<property name="username">
<value>admin</value>
</property>
<property name="password">
<value>admin</value>
</property>
</bean>

This part created the actual scheme, namespace identifier for the connection.

<bean id="openMQQueue">
<property name="connectionFactory" ref="openMQQueueCredentials"/>
</bean>

Step 3) Add the routing instructions

This’ll consume message from GlassFishMQ and put it on ActiveMQ’s queue. You can check the messages at http://localhost:8161/admin

<route>
<from uri="openMQQueue:queue:q_user_info" />
<to  uri="activemq:queue:amq.temp.userinfo.queue"/>
</route>

Referred to this info on how to configure the ConnectionFactory – http://docs.sun.com/source/817-0355/adminobj.html

Share this:

  • Click to share on Twitter (Opens in new window)
  • Click to share on Facebook (Opens in new window)
  • Click to share on Tumblr (Opens in new window)
  • Click to share on LinkedIn (Opens in new window)
  • Click to share on Reddit (Opens in new window)
  • Click to share on Pinterest (Opens in new window)
  • Click to email a link to a friend (Opens in new window)
  • Click to print (Opens in new window)

Like this:

Like Loading...

Subscribe

  • Entries (RSS)
  • Comments (RSS)

Archives

  • April 2020
  • February 2019
  • April 2018
  • July 2015
  • July 2013
  • October 2012
  • June 2012
  • May 2012
  • September 2011
  • April 2011
  • March 2011
  • December 2010
  • August 2010

Categories

  • Camel
  • Database
  • Devops
    • Amazon AWS
    • Docker
    • Kubernetes
  • Integration
  • Java
  • JMS
  • MuleSoft
  • Oracle
  • Siebel
  • SOA
    • BPEL
    • REST
  • Uncategorized
  • Zapier

Meta

  • Register
  • Log in

Create a free website or blog at WordPress.com.

Privacy & Cookies: This site uses cookies. By continuing to use this website, you agree to their use.
To find out more, including how to control cookies, see here: Cookie Policy
  • Follow Following
    • On Technology
    • Already have a WordPress.com account? Log in now.
    • On Technology
    • Customize
    • Follow Following
    • Sign up
    • Log in
    • Report this content
    • View site in Reader
    • Manage subscriptions
    • Collapse this bar
 

Loading Comments...
 

    %d bloggers like this: