Friday, August 31, 2007

EmbeddedNode

We've seen how to setup an EmbeddedDomain. Now we'll see the client class:
EmbeddedNode, used for a distributed node.
An EmbeddedNode has :

  1. a node name.
  2. a local domain.
  3. a distributed domain.
  4. a domain name.
  5. a managment domain, in which i load node manager components.

The first thing that it does is staring management domain (creating an EmbeddedDomain for management) and activating its components.
In order to complete an its init fase in the client class i should:
* attach a local domain this is done by:
     public SCADomain attachDomain(DistributedSCADomain distributedDomain)
97 throws ActivationException {
98 this.distributedDomain = (DistributedSCADomainImpl) distributedDomain;
99 domainName = distributedDomain.getDomainName();
100
101 try {
102 ClassLoader cl = EmbeddedNode.class.getClassLoader();
103
104 // create and start the local domain
105 domain = new EmbeddedSCADomain(cl, domainName);
106 domain.start();
107 } catch(ActivationException ex) {
108 throw ex;
109 } catch(Exception ex) {
110 throw new ActivationException(ex);
111 }
112
113 // add local information into the distributed domain
114 this.distributedDomain.setNodeName(nodeName);
115 this.distributedDomain.setManagementDomain(management);
116
117 // add domain information into the management components that need it
118
119
120 return domain;
This method attach a new local domain to the node starting it, and setting the nodename in the distributed domain and setting its management domain.
Then you should activate it and loading artifacts. We'll see it later.

EmdeddedSCADomain : booting your SCA Domain

Today we'll see the class EmbeddedSCADomain

The first thing I inizialize it:

68 public EmbeddedSCADomain(ClassLoader runtimeClassLoader,
69 String domainURI) {
70 this.uri = domainURI;
71
72 // Create a runtime
73 runtime = new ReallySmallRuntime(runtimeClassLoader);
74 }

I set Domain's URI and I create a new SCA Runtime, providing to it a classloader in order to load all needed artifacts.
Now a client code should start the domain, i.e:

EmbeddedSCADomain myDomain = new EmbeddedSCADomain(myClassloader,"http://www.di.unipi.it");
myDomain.start();

What the start() does?

public void start() throws ActivationException {
77
78 // Start the runtime
79 runtime.start();
80
81 // Create an in-memory domain level composite
82 AssemblyFactory assemblyFactory = runtime.getAssemblyFactory();
83 domainComposite = assemblyFactory.createComposite();
84 domainComposite.setName(new QName(Constants.SCA10_NS, "domain"));
85 domainComposite.setURI(uri);
86
87 getCompositeActivator().setDomainComposite(domainComposite);
88
89 }

It does some simple things:

  1. Starts the SCA runtime.
  2. Create an empty composition, set its namespace and the domain URI.
  3. Get from the runtime the activator and set a composite for this domain.

The setDomainComposite in the file CompositeActivatorImpl;

public void setDomainComposite(Composite domainComposite) {
699 this.domainComposite = domainComposite;
700 }

What happens inside ReallySmallRuntime?

The first thing , that Tuscany does, is to create a DefaultExtensionPointRegistry which holds a Map for each extension to the Tuscany Runtime. Then Tuscany loads the core extensions with the method getExtensionPoint. If the extension is not present in the core, it gets a Java Classloader, then it gets all classes for that extension, and create their objects. The following extension are loaded as default:
* WorkerScheduler
* InterfaceContractMapper
* Model Registry Factory
* Context Registry Factory
* Proxy Factory
* Assembly Factory
* SCA Binding Factory
Then the runtime has the task of initializing the following core parts, loading as extensions:
* Make a SCA definition processor
* Make a Contribution Service
* Make a Scope Registry
* Make a Composite Builder
* Make a Composite Activator
Then it has to load all Class file, instancing their objects by reflection and adding to a listo of ModuleActivator.

That's all for now! Next time i'll continue...

Thursday, August 30, 2007

Understanding Asynchronous Invoke for WS-Binding

The core asynchronous invoker in Apache Tuscany for ws-binding is the Axis2OneWayBindingInvoker.java file. The message on the wire is done in the following way:

27   * Represents a request, response, or exception flowing through a wire
28 *
29 * @version $Rev $Date
30 */
31 public interface Message {
32
33 /**
34 * Returns the body of the message, which will be the payload or parameters
associated with the wire

35 * @return The body of the message
36 */
37 T getBody();
38
39 /**
40 * Sets the body of the message.
41 * @param body The body of the message
42 */
43 void setBody(T body);
44
45 /**
46 * Get the conversation id
47 * @return The conversation ID
48 */
49 String getConversationID();
50
51 /**
52 * Set the conversation id
53 * @param conversationId The conversation ID
54 */
55 void setConversationID(String conversationId);
56
57 /**
58 * Get the end point reference of the source reference
59 * @return The end point reference of the reference originating the message
60 */
61 EndpointReference getFrom();
62
63 /**
64 * Set the end point reference of the reference originating the message
65 * @param from The end point reference of the reference originating the message
66 */
67 void setFrom(EndpointReference from);
68
69 /**
70 * Get the end point reference of target service
71 * @return The end point reference of the service that the message targets
72 */
73 EndpointReference getTo();
74
75 /**
76 * Set the end point reference of target service
77 * @param to The end point reference of the service that the message targets
78 */
79 void setTo(EndpointReference to);
80
81 /**
82 * Returns the id of the message
83 * @return The message Id
84 */
85 Object getMessageID();
86
87 /**
88 * Sets the id of the message
89 * @param messageId The message ID
90 */
91 void setMessageID(Object messageId);
92
93 /**
94 * Returns the correlation id of the message or null if one is not available. Correlation ids are used by transports
95 * for message routing.
96 * @return The correlation Id
97 */
98 Object getCorrelationID();
99
100 /**
101 * Sets the correlation id of the message. Correlation ids are used by transports for message routing.
102 * @param correlationId The correlation Id
103 */
104 void setCorrelationID(Object correlationId);
105
106 /**
107 * Determines if the message represents a fault/exception
108 *
109 * @return true If the message body is a fault object, false if the body is a normal payload
110 */
111 boolean isFault();
112
113 /**
114 * Set the message body with a fault object. After this method is called, isFault() returns true.
115 *
116 * @param fault The fault object represents an exception
117 */
118 void setFaultBody(T fault);
119
120 /**
121 * Returns the conversational sequence the message is associated
with, NONE, START, CONTINUE, or END on TargetInvoker}

122 *
123 * @return The conversational sequence the message is associated with
124 */
125 ConversationSequence getConversationSequence();
126
127 /**
128 * Sets the conversational sequence the message is
associated with, NONE, START, CONTINUE, or END

129 *
130 * @param sequence The conversational sequence
131 */
132 void setConversationSequence(ConversationSequence sequence);
133
134 /**
135 * Returns the operation that created the message.
136 *
137 * @return The operation that created the message
138 */
139 Operation getOperation();
140
141 /**
142 * Sets the operation that created the message.
143 *
144 * @param op The operation that created the message
145 */
146 void setOperation(Operation op);
147
148 /**
149 * Get the associated callable reference
150 * @param
151 * @return The callable reference
152 */
153 CallableReference getCallableReference();
154
155 /**
156 * Set the callable reference
157 * @param
158 * @param callableReference
159 */
160 void setCallableReference(CallableReference callableReference);
161
162 }

From the code we see that a Message has a :

  • a message identificator
  • a correlation id
  • a conversation id
  • a body
  • an operation
  • two endpoint reference (from, to)
I assume to have a factory, called MessageFactory in order to create messages.

Now we'll look how a message is sent on the wire in the asynchronous case file Axis2OneWayBindingInvoker.java :


protected Object invokeTarget(Message msg) throws InvocationTargetException {
47
try {

We'll speak about AxisOperationClient later:

1) Create an OperationClient with the incoming Message

48 OperationClient operationClient = createOperationClient(msg);

49
50
// ensure connections are tracked so that they can be closed by the reference binding
51 MessageContext requestMC = operationClient.getMessageContext(WSDLConstants.MESSAGE_LABEL_OUT_VALUE);
52
53
requestMC.getOptions().setProperty(HTTPConstants.REUSE_HTTP_CLIENT, Boolean.TRUE);

It's asynchronous so it should not block, so execute(false) means : DON'T BLOCK!
54 operationClient.execute(false);
55
56
// REVIEW it seems ok to return null
57
return null;
58
59 }
catch (AxisFault e) {
60
throw new InvocationTargetException(e);
61 }

What does operation client ?

You can use Operation Client if you want more control during the invocation. Typically you need to work with the Message Context. Axis2 creates an instance of Message Context for each and every SOAP message handled inside the engine. Message Context encapsulates a SOAP message and it has most of the information required to process the message. Axis2 engine is a stateless piece of code which executes according to the information found inside the Message Context.

Operation Client enables user to directly access or pass-in a Message Context so that user has the ability to set numerous parameters to be used during an invocation. User can even set a complete SOAP envelope to the message context, created by any means he likes. (Service Client API has means to set the content of body and it has addHeader()) method to add headers to the SOAP messages. So Service Client user can also customize the SOAP message, to some extent, just like the Operation Client user.)

You need to know a bit about the concept of MEP (Message Exchange Pattern) to use Operation Client API.

Message Exchange Pattern (MEP)

During a request-response invocation, we first send a SOAP message to the Web service and it then returns back another SOAP message to us. From the Web service's point of view, it receives one incoming message (IN message) and sends out one message (OUT message). This is a pattern and we name this message exchange as IN-OUT MEP. Similarly, there can be another Web service which simply accepts messages from client. For example, you can register with a new alert system by sending a subscription request message (assume, you will become a member as soon as you send this request and server doesn't send a confirmation message). In this case you are working with an IN-only MEP.

In simple terms, MEP is a template that establishes a pattern for the exchange of messages between two communicating parties.

Then in superclass there's the method for creating an operation client:

protected OperationClient createOperationClient(Message msg) throws AxisFault {

Create a default SOAP envelope:

109 SOAPEnvelope env = soapFactory.getDefaultEnvelope();

Fill the envelope with items using AXIOM:

110 Object[] args = (Object[])msg.getBody();
111 if (args != null && args.length > 0) {
112 SOAPBody body = env.getBody();
113 for (Object bc : args) {
114 if (bc instanceof OMElement) {
115 body.addChild((OMElement)bc);
116 } else {
117 throw new IllegalArgumentException(
118 "Can't handle mixed payloads betweem OMElements and other types.");
119 }
120 }
121 }

After ending the creation of a SOAP Envelope, create a MessageContext
122 MessageContext requestMC = new MessageContext();
123 requestMC.setEnvelope(env);
124
125 // Axis2 operationClients can not be shared so create a new one for each request
126 OperationClient operationClient = serviceClient.createClient(wsdlOperationName);
127
128 String conversationId = msg.getConversationID();
129 if (conversationId != null && conversationId.length() != 0) {
130 EndpointReference fromEPR = new EndpointReference(AddressingConstants.Final.WSA_ANONYMOUS_URL);
131 fromEPR.addReferenceParameter(CONVERSATION_ID_REFPARM_QN, conversationId);
132 options.setFrom(fromEPR);
133 requestMC.setFrom(fromEPR); //who knows why two ways ?
134
135 //For now do this the brute force method. Need to figure out how to do axis addressing .. configure mar in flow.
136 SOAPEnvelope sev = requestMC.getEnvelope();
137 SOAPHeader sh = sev.getHeader();
138 OMElement el =
139 fromEPR.toOM(AddressingConstants.Final.WSA_NAMESPACE,
140 AddressingConstants.WSA_FROM,
141 AddressingConstants.WSA_DEFAULT_PREFIX);
142 sh.addChild(el);
143 }
144
145 operationClient.setOptions(options);
146 // if target endpoint was not specified when this invoker was created,
147 // use dynamically specified target endpoint passed in on this call
148 if (options.getTo() == null) {
149 org.apache.tuscany.sca.runtime.EndpointReference ep = msg.getTo();
150 if (ep != null) {
151 requestMC.setTo(new EndpointReference(ep.getURI()));
152 } else {
153 throw new RuntimeException("Unable to determine destination endpoint");
154 }
155 }
156 //FIXME: need to consolidate the following code with similar code above for
157 // conversations, so that conversations and callbacks work when used together
158 if (options.getFrom() != null) {
159 requestMC.setFrom(options.getFrom());
160 //FIXME: is there any way to use the Axis2 addressing support for this?
161 SOAPEnvelope sev = requestMC.getEnvelope();
162 SOAPHeader sh = sev.getHeader();
163 OMElement el =
164 options.getFrom().toOM(AddressingConstants.Final.WSA_NAMESPACE,
165 AddressingConstants.WSA_FROM,
166 AddressingConstants.WSA_DEFAULT_PREFIX);
167 sh.addChild(el);
168 } else if (msg.getFrom() != null) {
169 EndpointReference fromEpr = new EndpointReference(msg.getFrom().getURI());
170 requestMC.setFrom(fromEpr);
171 SOAPEnvelope sev = requestMC.getEnvelope();
172 SOAPHeader sh = sev.getHeader();
173 OMElement el = fromEpr.toOM(AddressingConstants.Final.WSA_NAMESPACE,
174 AddressingConstants.WSA_FROM,
175 AddressingConstants.WSA_DEFAULT_PREFIX);
176 sh.addChild(el);
177 } else {
178 // the from field remains blank
179 }
180 operationClient.addMessageContext(requestMC);
181
182 return operationClient;
183 }

Axis2OneWayBindingInvoker is called by Axis2Client. For each operation It creates an invoker.

protected Invoker createInvoker(Operation operation) {
139 Options options = new Options();
140 EndpointReference epTo = getPortLocationEPR(wsBinding);

141 if (epTo != null) {

142 options.setTo(epTo);
143 }
144 if (callbackBinding != null) {
145 options.setFrom(getPortLocationEPR(callbackBinding));
146 }
147 options.setProperty(HTTPConstants.CHUNKED, Boolean.FALSE);
148
149 String operationName = operation.getName();
150
151 String soapAction = getSOAPAction(operationName);
152 if (soapAction != null && soapAction.length() > 1) {
153 options.setAction(soapAction);
154 }
155
156 options.setTimeOutInMilliSeconds(30 * 1000); // 30 seconds
157
158 SOAPFactory soapFactory = OMAbstractFactory.getSOAP11Factory();
159 QName wsdlOperationQName = new QName(operationName);
160
161 // Axis2BindingInvoker invoker;
162 if (operation.isNonBlocking()) {
163 invoker = new Axis2OneWayBindingInvoker(serviceClient, wsdlOperationQName, options, soapFactory);
164 } else {
165 invoker = new Axis2BindingInvoker(serviceClient, wsdlOperationQName, options, soapFactory);
166 }
167 return invoker;
168 }