Tuesday, May 29, 2018
Cracks In The Paint
When I worked at Boeing Helicopters, I recall talking to an Engineer on the shop floor about a special paint that they used on the CH-47 (Chinook). The paint would bond to the underlying frame at a molecular level, to the point where a fracture deep within the frame would manifest itself as a crack in the paint, thereby providing all those involved with a clear indication that the frame needed repair. Rosanne Barr is the crack in the paint. We should be thankful for her signal, even as we detest her.
Wednesday, May 23, 2018
Truth
If one endeavors to speak the truth, then one must know what the truth is, and this goes far beyond the simple truths. It is one thing to know if you are lying by commission or omission. It is another thing to understand how you feel, recognize a situation for what it is, and speak the truth without undue friction. Whether you are dealing with a relative, a friend or a coworker, and whether you are grappling with how you feel about them or how they feel about you, you might not really know what the truth (your truth) is. And even if you do, is the ugly truth better than a polite lie that might smooth things over? I’ve seen depictions of how a succession of truths turns out to be disastrous (Everybody Loves Raymond) as well as depictions of how a succession of lies turns out to be equally disastrous (Seinfeld). To the extent that it is not always practical to prioritize authenticity over expediency, I wonder if it might be better to say nothing at all, rather than to fabricate an insidious lie or deliver an explosive truth. With regard to this particular question, I would accept either an honest or a polite answer, and that’s not the truth.
x
Tuesday, May 15, 2018
Salesforce: Apex: Generate from WSDL
Problem
.
When trying to generate Apex from a WSDL in Salesforce, you have to do a few things to make the WSDL suitable for use with the OOTB 'Generate from WSDL' option. It is possible that the Open Source WSDL2Apex Generator (here) does not require similar preprocessing, but, as of this writing, I have not tried it, so I do not know. In this post, I am only going to focus on the two actions that I had to take to get a WSDL into a suitable form, but I am relatively certain that this is only a small subset of what one might have to do along the spectrum of WSDLs..
Solution
.
Step 1: Inline the Imports
.
Let's say you have a WSDL that looks something like this:
.
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="ServiceName" targetNamespace="http://[domain]/[namespace]" xmlns:tns="http://[domain]/[namespace]" [...etc...]>
<types>
<xsd:schema>
<xsd:import schemaLocation="https://[domain]/[schema]" namespace="http://[domain]/[namespace]"/>
</xsd:schema>
</types>
<message name="SomeMessage">
<part name="parameters" element="tns:SomeMessage"/>
</message>
<portType name="ServiceName">
<operation name="SomeMessage">
<input message="tns:SomeMessage" wsam:Action="http://[domain]/[namespace]/[method]"/>
<output message="tns:SomeMessageResponse" wsam:Action="http://[domain]/[namespace]/[method]"/>
<fault name="ServiceException" message="tns:ServiceException" wsam:Action="http://[domain]/[namespace]/[method]/fault/ServiceException"/>
</operation>
<xs:complexType name="ServiceException">
<xs:sequence>
<xs:element name="errorCode" type="xs:int"/>
<xs:element name="message" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
[...etc...]
</definitions>
<definitions name="ServiceName" targetNamespace="http://[domain]/[namespace]" xmlns:tns="http://[domain]/[namespace]" [...etc...]>
<types>
<xsd:schema>
<xsd:import schemaLocation="https://[domain]/[schema]" namespace="http://[domain]/[namespace]"/>
</xsd:schema>
</types>
<message name="SomeMessage">
<part name="parameters" element="tns:SomeMessage"/>
</message>
<portType name="ServiceName">
<operation name="SomeMessage">
<input message="tns:SomeMessage" wsam:Action="http://[domain]/[namespace]/[method]"/>
<output message="tns:SomeMessageResponse" wsam:Action="http://[domain]/[namespace]/[method]"/>
<fault name="ServiceException" message="tns:ServiceException" wsam:Action="http://[domain]/[namespace]/[method]/fault/ServiceException"/>
</operation>
<xs:complexType name="ServiceException">
<xs:sequence>
<xs:element name="errorCode" type="xs:int"/>
<xs:element name="message" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
[...etc...]
</definitions>
.
The 'xsd:import' is not supported, so you have to actually inline the related schema into this WSDL, which we will refer to as the 'Consolidated_WSDL', for clarity.
.
Let's say that related schema looks something like this:
.
<?xml version='1.0' encoding='UTF-8'?><xs:schema xmlns:tns="http://[domain]/[namespace_1]" version="1.0" targetNamespace="http://[domain]/[namespace_1]">
<xs:import namespace="http://[domain]/[namespace_2" schemaLocation="https://[domain]/[namespace_2]"/>
<xs:import namespace="http://[domain]/[namespace_3" schemaLocation="https://[domain]/[namespace_3]"/>
<xs:element name="SomeException" type="tns:SomeException]"/>
<xs:element name="SomeService" type="tns:SomeService"/>
[...etc...]
</xs:schema>
.
You would inline this schema by replacing the entire <xsd:import> block in the Consolidated_WSDL with just the <schema> block from this file, as follows, in blue:
.
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="ServiceName" targetNamespace="http://[domain]/[namespace]" xmlns:tns="http://[domain]/[namespace]" [...etc...]>
<types>
<xs:schema xmlns:tns="http://[domain]/[namespace_1]" version="1.0" targetNamespace="http://[domain]/[namespace_1]">
<xs:import namespace="http://[domain]/[namespace_2" schemaLocation="https://[domain]/[namespace_2]"/>
<xs:import namespace="http://[domain]/[namespace_3" schemaLocation="https://[domain]/[namespace_3]"/>
<xs:element name="SomeException" type="tns:SomeException]"/>
<xs:element name="SomeService" type="tns:SomeService"/>
[...etc...]
</xs:schema> </types>
<message name="SomeMessage">
<part name="parameters" element="tns:SomeMessage"/>
</message>
<portType name="ServiceName">
<operation name="SomeMessage">
<input message="tns:SomeMessage" wsam:Action="http://[domain]/[namespace]/[method]"/>
<output message="tns:SomeMessageResponse" wsam:Action="http://[domain]/[namespace]/[method]"/>
<fault name="ServiceException" message="tns:ServiceException" wsam:Action="http://[domain]/[namespace]/[method]/fault/ServiceException"/>
</operation>
<xs:complexType name="ServiceException">
<xs:sequence>
<xs:element name="errorCode" type="xs:int"/>
<xs:element name="message" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
[...etc...]
</definitions>
<definitions name="ServiceName" targetNamespace="http://[domain]/[namespace]" xmlns:tns="http://[domain]/[namespace]" [...etc...]>
<types>
<xs:schema xmlns:tns="http://[domain]/[namespace_1]" version="1.0" targetNamespace="http://[domain]/[namespace_1]">
<xs:import namespace="http://[domain]/[namespace_2" schemaLocation="https://[domain]/[namespace_2]"/>
<xs:import namespace="http://[domain]/[namespace_3" schemaLocation="https://[domain]/[namespace_3]"/>
<xs:element name="SomeException" type="tns:SomeException]"/>
<xs:element name="SomeService" type="tns:SomeService"/>
[...etc...]
</xs:schema> </types>
<message name="SomeMessage">
<part name="parameters" element="tns:SomeMessage"/>
</message>
<portType name="ServiceName">
<operation name="SomeMessage">
<input message="tns:SomeMessage" wsam:Action="http://[domain]/[namespace]/[method]"/>
<output message="tns:SomeMessageResponse" wsam:Action="http://[domain]/[namespace]/[method]"/>
<fault name="ServiceException" message="tns:ServiceException" wsam:Action="http://[domain]/[namespace]/[method]/fault/ServiceException"/>
</operation>
<xs:complexType name="ServiceException">
<xs:sequence>
<xs:element name="errorCode" type="xs:int"/>
<xs:element name="message" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
[...etc...]
</definitions>
.
You may have noticed the additional 'xs:import' elements in the schema that we just inlined. These would have to be inlined as well. You would simply navigate to the schema location for each import, and then copy-paste the <schema> block for each import as peers to the schema we just copy-pasted, and remove the import elements, as follows, with the new schemas in bold-blue, and the removed import elements in strikethrough (e.g. strikethrough):
.
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="ServiceName" targetNamespace="http://[domain]/[namespace]" xmlns:tns="http://[domain]/[namespace]" [...etc...]>
<types>
<xs:schema xmlns:tns="http://[domain]/[namespace_1]" version="1.0" targetNamespace="http://[domain]/[namespace_1]">
<xs:import namespace="http://[domain]/[namespace_2" schemaLocation="https://[domain]/[namespace_2]"/>
<xs:import namespace="http://[domain]/[namespace_3" schemaLocation="https://[domain]/[namespace_3]"/>
<xs:element name="SomeException" type="tns:SomeException]"/>
<xs:element name="SomeService" type="tns:SomeService"/>
[...etc...]
</xs:schema> <xs:schema xmlns:tns="http://[domain]/[namespace_2]" version="1.0" targetNamespace="http://[domain]/[namespace_2]">
[...etc...]
</xs:schema>
<definitions name="ServiceName" targetNamespace="http://[domain]/[namespace]" xmlns:tns="http://[domain]/[namespace]" [...etc...]>
<types>
<xs:schema xmlns:tns="http://[domain]/[namespace_1]" version="1.0" targetNamespace="http://[domain]/[namespace_1]">
<xs:element name="SomeException" type="tns:SomeException]"/>
<xs:element name="SomeService" type="tns:SomeService"/>
[...etc...]
</xs:schema> <xs:schema xmlns:tns="http://[domain]/[namespace_2]" version="1.0" targetNamespace="http://[domain]/[namespace_2]">
[...etc...]
</xs:schema>
<xs:schema xmlns:tns="http://[domain]/[namespace_3]" version="1.0" targetNamespace="http://[domain]/[namespace_3]">
[...etc...]
</xs:schema>
[...etc...]
</xs:schema>
</types>
<message name="SomeMessage">
<part name="parameters" element="tns:SomeMessage"/>
</message>
<portType name="ServiceName">
<operation name="SomeMessage">
<input message="tns:SomeMessage" wsam:Action="http://[domain]/[namespace]/[method]"/>
<output message="tns:SomeMessageResponse" wsam:Action="http://[domain]/[namespace]/[method]"/>
<fault name="ServiceException" message="tns:ServiceException" wsam:Action="http://[domain]/[namespace]/[method]/fault/ServiceException"/>
</operation>
<xs:complexType name="ServiceException">
<xs:sequence>
<xs:element name="errorCode" type="xs:int"/>
<xs:element name="message" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
[...etc...]
</definitions>
<message name="SomeMessage">
<part name="parameters" element="tns:SomeMessage"/>
</message>
<portType name="ServiceName">
<operation name="SomeMessage">
<input message="tns:SomeMessage" wsam:Action="http://[domain]/[namespace]/[method]"/>
<output message="tns:SomeMessageResponse" wsam:Action="http://[domain]/[namespace]/[method]"/>
<fault name="ServiceException" message="tns:ServiceException" wsam:Action="http://[domain]/[namespace]/[method]/fault/ServiceException"/>
</operation>
<xs:complexType name="ServiceException">
<xs:sequence>
<xs:element name="errorCode" type="xs:int"/>
<xs:element name="message" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
[...etc...]
</definitions>
.
You need not get trapped in recursion during this process ;-)
.
If, for example, the schema for namespace_3 imported namespace_2, then you can rely on the already copy-pasted namespace_2 in the Consolidated_WSDL.
.
Step 2: Take Exception with 'Exception'
.
The Apex compiler does not like it when a class ends with the keyword 'Exception' unless it extends the Exception Class. In the example above, you will notice the 'ServiceException' complex type, element, fault, etc. This would need to be renamed in an identifiable way, so that you can locate the renamed artifacts in the generated Apex later (see below). I simply appeneded 'WSDL' to the end of all such 'Exception' references in a rather large WSDL, taking care to replace "ServiceException" with "ServiceExceptionWSDL" and "tns:ServiceException" with "tns:ServiceExceptionWSDL", etc. Take care to be very specific about your find-and-replace operations, so as not to accidentially replace an 'Exception' reference in a URL. I suggest you use fully double-quoted strings in your find-and-replace logic, so that you avoid URLs altogether.
.
The final Consolidated_WSDL would look something like this:
.
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="ServiceName" targetNamespace="http://[domain]/[namespace]" xmlns:tns="http://[domain]/[namespace]" [...etc...]>
<types>
<xs:schema xmlns:tns="http://[domain]/[namespace_1]" version="1.0" targetNamespace="http://[domain]/[namespace_1]">
<xs:element name="SomeExceptionWSDL" type="tns:SomeExceptionWSDL]"/>
<xs:element name="SomeService" type="tns:SomeService"/>
[...etc...]
</xs:schema>
<xs:schema xmlns:tns="http://[domain]/[namespace_2]" version="1.0" targetNamespace="http://[domain]/[namespace_2]">
[...etc...]
</xs:schema>
<definitions name="ServiceName" targetNamespace="http://[domain]/[namespace]" xmlns:tns="http://[domain]/[namespace]" [...etc...]>
<types>
<xs:schema xmlns:tns="http://[domain]/[namespace_1]" version="1.0" targetNamespace="http://[domain]/[namespace_1]">
<xs:element name="SomeExceptionWSDL" type="tns:SomeExceptionWSDL]"/>
<xs:element name="SomeService" type="tns:SomeService"/>
[...etc...]
</xs:schema>
<xs:schema xmlns:tns="http://[domain]/[namespace_2]" version="1.0" targetNamespace="http://[domain]/[namespace_2]">
[...etc...]
</xs:schema>
<xs:schema xmlns:tns="http://[domain]/[namespace_3]" version="1.0" targetNamespace="http://[domain]/[namespace_3]">
[...etc...]
</xs:schema>
[...etc...]
</xs:schema>
</types>
<message name="SomeMessage">
<part name="parameters" element="tns:SomeMessage"/>
</message>
<portType name="ServiceName">
<operation name="SomeMessage">
<input message="tns:SomeMessage" wsam:Action="http://[domain]/[namespace]/[method]"/>
<output message="tns:SomeMessageResponse" wsam:Action="http://[domain]/[namespace]/[method]"/>
<fault name="ServiceExceptionWSDL" message="tns:ServiceExceptionWSDL" wsam:Action="http://[domain]/[namespace]/[method]/fault/ServiceException"/>
</operation>
<xs:complexType name="ServiceExceptionWSDL">
<xs:sequence>
<xs:element name="errorCode" type="xs:int"/>
<xs:element name="message" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
[...etc...]
</definitions>
.
<message name="SomeMessage">
<part name="parameters" element="tns:SomeMessage"/>
</message>
<portType name="ServiceName">
<operation name="SomeMessage">
<input message="tns:SomeMessage" wsam:Action="http://[domain]/[namespace]/[method]"/>
<output message="tns:SomeMessageResponse" wsam:Action="http://[domain]/[namespace]/[method]"/>
<fault name="ServiceExceptionWSDL" message="tns:ServiceExceptionWSDL" wsam:Action="http://[domain]/[namespace]/[method]/fault/ServiceException"/>
</operation>
<xs:complexType name="ServiceExceptionWSDL">
<xs:sequence>
<xs:element name="errorCode" type="xs:int"/>
<xs:element name="message" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
[...etc...]
</definitions>
.
Generate from WSDL
.
When you generate new Apex code from the WSDL, you will be given the opportunity choose the same Apex class name for all inlined namespaces, or name them differently. Whatever you do, I suggest you pick a short name or names, just based on my experience of rather lengthy names in the WSDL that got truncated when I just accepted the defaults.
.
After you generate the Apex, you will need to refactor any 'Exception' classes and references to back to the original name, so that the final Apex conforms with the service contract. In the example we've been using so far, the ServiceExeption complex type renamed to ServiceExceptionWSDL would result in an Apex class that looks something like this:
.
public Integer errorCode;
public String message;
[...etc...]
.
After you generate the Apex, you will need to refactor any 'Exception' classes and references to back to the original name, so that the final Apex conforms with the service contract. In the example we've been using so far, the ServiceExeption complex type renamed to ServiceExceptionWSDL would result in an Apex class that looks something like this:
.
public class ServiceExceptionWSDL {
public Integer errorCode;
public String message;
[...etc...]
public Integer errorCode;
public String message;
[...etc...]
}
.
You need only remove whatever suffix (or prefix, or whatever) you added to the name (in this case, 'WSDL') and have the class extend the 'Exception' interface, as follows:
.
public class ServiceException extends Exception {public Integer errorCode;
public String message;
[...etc...]
}
.
In my particular case, I replaced 100s of references to ServiceException with ServiceExceptionWSDL in my Consolidated_WSDL, but all of those references amounted to a single, generated Apex class, so the refactoring was easy. Your mileage may vary.
.
In my particular case, I replaced 100s of references to ServiceException with ServiceExceptionWSDL in my Consolidated_WSDL, but all of those references amounted to a single, generated Apex class, so the refactoring was easy. Your mileage may vary.
.
References
.
Mr. Brent Schreibfeder, a Salesforce colleague, who graciously helped me with my general lack of understanding of WSDL structure and the process to inline imports.
.
.
Monday, May 14, 2018
Run with What You Brung
Today I had an experience that reminded me of one of Tony the Tenor's sayings: "Run with what you brung". It was a phrase originally pertaining to race cars - i.e. however it shakes out on race day, just race it - but he ported it over to singing, and used it as a way to counter the ever-present feeling that a singer has when it comes time to perform - i.e. that they are sub-par and not ready. In other words, just get into performance mode and deliver, and just let the voice do what the voice will do. You can't control everything... but you can control your thoughts.
Mother’s Day 2018
We judge our mothers by what we can remember, so I am struck by the wealth of love and attention I never committed to memory, just by the wealth of love and attention I see Val give to Nicholas every day. I think to myself, “did I come into the world that way? was my first caress like that? was I that helpless baby that cried in the night? was my only salvation in my mother’s arms? was I that toddler who needed constant company and supervision? was the totality of my experience so totally influenced by my mother?” The answer is yes, but it seems that appreciating it is a generational thing. My love and gratitude for Val grows each day, but even more so for the Mothers in my life, on Mother’s Day.
Childhood Radius
Since I was in the Scranton area, I decided to visit my childhood home. This is where I lived until I was 4. I barely remember the outside of the house (though I am sure it has changed considerably over the years), and I vaguely remember the inside of the house, but I remember the yard and the immediate neighborhood like it was yesterday. I had an almost ‘Skinnerian’ childhood in Scranton, minus a close friend. As I drove to the house, I was struck by how everything leading up to a 2 block radius of it was completely unfamiliar to me. But that is how it is. We live in the space we occupy. It imprints upon us. Anything beyond that boundary escapes us. I wonder what escapes me here and now?
Saturday, May 5, 2018
Memorable Childhood Lessons
I recall having a childhood debate in 1981 with my good friend Robert. It was about Lindsey Buckingham's new song, 'Trouble'. My contention was that Lindsey's intro to the song consisted of him saying "doo a dee a fow" or some phonetic equivalent, whereas Rob quite rationally and correctly contended that Lindesy was saying "two, three, four". I can remember walking with him from my house to his house, cutting through my neighbor's yard, and him being almost exasperated that I would not concede his point. It is one of those memories that resurfaces every time I feel myself clinging to an idea too dogmatically, or looking down my nose at someone who is doing the same. If only I could hold all such memorable childhood lessons in-memory simultaneously. As it stands, I can only fit the really sentimental ones.
Tuesday, May 1, 2018
Simple One
I’ve been following a one meal per day routine for the past three months, and I am astounded by the adjustment, in terms of mental clarity, energy level and, what I call, spiritual calmness. Our conscious minds sit atop a system of mental and physical processes that precede our self-awareness and our illusion of self and other control. I think that when we minimize our lives and strip away all of the noise, we can better grasp how our motion through this river of life has more to do the current than anything else. To me, any habit or routine that helps us pause long enough to stop swimming and start floating is a worthwhile endeavor.
Subscribe to:
Posts (Atom)