001    /*
002     @license.text@ 
003     */
004    package biz.hammurapi.jms.adapter.converters;
005    
006    import java.io.IOException;
007    import java.io.StringReader;
008    import java.io.StringWriter;
009    import java.net.MalformedURLException;
010    import java.net.URL;
011    import java.util.HashMap;
012    import java.util.Map;
013    
014    import javax.jms.Message;
015    import javax.jms.Session;
016    import javax.jms.TextMessage;
017    import javax.xml.parsers.DocumentBuilder;
018    import javax.xml.parsers.DocumentBuilderFactory;
019    import javax.xml.transform.Result;
020    import javax.xml.transform.Source;
021    import javax.xml.transform.Templates;
022    import javax.xml.transform.Transformer;
023    import javax.xml.transform.TransformerConfigurationException;
024    import javax.xml.transform.TransformerFactory;
025    import javax.xml.transform.dom.DOMSource;
026    import javax.xml.transform.stream.StreamResult;
027    import javax.xml.transform.stream.StreamSource;
028    
029    import org.w3c.dom.Document;
030    import org.w3c.dom.Element;
031    
032    import biz.hammurapi.config.ComponentBase;
033    import biz.hammurapi.config.ConfigurationException;
034    import biz.hammurapi.jms.adapter.Converter;
035    import biz.hammurapi.jms.adapter.JmsAdapter;
036    import biz.hammurapi.xml.dom.DOMUtils;
037    
038    /**
039     * This converter supports the following optional conversion properties:
040     * <UL>
041     * <LI>from-xml-style - URL of stylesheet to apply before XML -&gt; Object conversion. 
042     * If URL starts with <code>resource:</code> then it is loaded from classloader resource.</LI>
043     * <LI>to-xml-style - URL of stylesheet to apply after Object -&gt; XML conversion. 
044     * If URL starts with <code>resource:</code> then it is loaded from classloader resource.</LI>
045     * <LI>error-style - URL of stylesheet to apply after Exception -&gt; XML conversion. 
046     * If URL starts with <code>resource:</code> then it is loaded from classloader resource.</LI>
047     * </UL>
048     * @author Tatyana Konukova
049     *
050     */
051    public abstract class StylingXmlConverter extends ComponentBase implements Converter {
052    
053            private static final String RESOURCE_PREFIX = "resource:";
054            private Map templates = new HashMap();
055            
056            private DocumentBuilder documentBuilder;
057    
058            protected synchronized Document createDocument() {
059                    return documentBuilder.newDocument();
060            }
061            
062            /**
063             * Creates result to receive transformed input.
064             * @return Result instance.
065             */
066            protected abstract Result createResult();
067            
068            /**
069             * Converts reply result to object.
070             * @param result
071             * @return
072             */
073            protected abstract Object processResult(Result result) throws Exception;
074    //              return ((StringWriter) ((StreamResult) result).getWriter()).toString();
075            
076            /**
077             * Converts request object to Source for styling/serialization
078             * @param request
079             * @return
080             */
081            protected abstract Source convert(Object request) throws Exception; 
082                    
083            protected TransformerFactory transformerFactory;
084    
085            public void start() throws ConfigurationException {
086                    try {                   
087                            documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
088                            transformerFactory = TransformerFactory.newInstance();
089                    } catch (Exception e) {
090                            throw new ConfigurationException("Startup failed: "+e, e);
091                    }
092            }
093            
094            protected Transformer getTransformer(String url) throws TransformerConfigurationException, MalformedURLException, IOException {
095                    if (JmsAdapter.isBlank(url)) {
096                            return null;
097                    }
098                    
099                    synchronized (templates) {
100                            Templates template = (Templates) templates.get(url);
101                            if (template==null) {
102                                    if (url.startsWith(RESOURCE_PREFIX)) {
103                                            template = transformerFactory.newTemplates(
104                                                            new StreamSource(
105                                                                            getClass()
106                                                                            .getClassLoader()
107                                                                            .getResourceAsStream(
108                                                                                            url.substring(RESOURCE_PREFIX.length()))));
109                                    } else {
110                                            template = transformerFactory.newTemplates(new StreamSource(new URL(url).openStream()));
111                                    }
112                                    templates.put(url, template);
113                            }
114                            return template.newTransformer();
115                    }
116            }
117    
118            /**
119             * This method supports <code>from-xml-style</code> optional property - URL of stylesheet to apply before XML -&gt; Object conversion. 
120             * If URL starts with <code>resource:</code> then it is loaded from classloader resource.</LI>
121             */
122            public Object convert(Message message, Map properties) throws Exception {
123                    Result result = createResult();         
124                    Transformer transformer = getTransformer((String) properties.get("from-xml-style")); 
125                    if (transformer==null) {
126                            transformer = transformerFactory.newTransformer();
127                    }
128                    transformer.transform(new StreamSource(new StringReader(((TextMessage) message).getText())), result);
129                    return processResult(result);
130            }
131    
132            /**
133             * This method supports <code>to-xml-style</code> optional property - URL of stylesheet to apply after Object -&gt; XML conversion. 
134             * If URL starts with <code>resource:</code> then it is loaded from classloader resource.</LI>
135             * Correlates by message ID.
136             */
137            public Message convert(Object obj, Session session, Map properties, Message request) throws Exception {
138                    TextMessage ret = session.createTextMessage();
139                    Transformer transformer = getTransformer((String) properties.get("to-xml-style")); 
140                    if (transformer==null) {
141                            transformer = transformerFactory.newTransformer();
142                    }
143                    StringWriter sw = new StringWriter();
144                    transformer.transform(convert(obj), new StreamResult(sw));
145                    sw.close();
146                    ret.setText(sw.toString());
147                    if (request!=null) {
148                            ret.setJMSCorrelationID(request.getJMSMessageID());
149                    }
150                    return ret;
151            }
152    
153            /**
154             * This method supports <code>error-style</code> optional property - URL of stylesheet to apply after Exception -&gt; Conversion.
155             * If URL starts with <code>resource:</code> then it is loaded from classloader resource.</LI>
156             * This implementation uses ThrowableSerializer to convert exception to XML.
157             * Correlates by message ID.
158             */
159            public Message convert(Exception ex, Session session, Map properties, Message request) throws Exception {
160                    TextMessage ret = session.createTextMessage();
161                    Document doc = createDocument();
162                    Element root = doc.createElement("error");
163                    doc.appendChild(root);
164                    DOMUtils.toDom(ex, root);
165                    
166                    StringWriter sw = new StringWriter();
167                    Transformer transformer = getTransformer((String) properties.get("error-style")); 
168                    if (transformer==null) {
169                            DOMUtils.serialize(doc, sw);
170    //                      XMLSerializer xmlSerializer = new XMLSerializer(sw, new OutputFormat("xml","UTF-8",false));
171    //                      xmlSerializer.setNamespaces(true);
172    //                      xmlSerializer.serialize(doc);
173                    } else {
174                            transformer.transform(new DOMSource(doc), new StreamResult(sw));
175                    }
176                    sw.close();
177                    ret.setText(sw.toString());
178                    if (request!=null) {
179                            ret.setJMSCorrelationID(request.getJMSMessageID());
180                    }
181                    return ret;
182            }
183    
184            public void stop() throws ConfigurationException {
185                    // Nothing to do                
186            }
187    
188    }