001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    package org.apache.geronimo.console.jmsmanager.wizard;
018    
019    import java.io.File;
020    import java.io.FileInputStream;
021    import java.io.IOException;
022    import java.io.InputStream;
023    import java.io.Serializable;
024    import java.util.ArrayList;
025    import java.util.HashSet;
026    import java.util.Iterator;
027    import java.util.List;
028    import java.util.Properties;
029    import java.util.Set;
030    import java.util.zip.ZipEntry;
031    import java.util.zip.ZipInputStream;
032    import javax.portlet.PortletRequest;
033    import javax.xml.parsers.DocumentBuilder;
034    import javax.xml.parsers.DocumentBuilderFactory;
035    import javax.xml.parsers.ParserConfigurationException;
036    import org.apache.commons.logging.Log;
037    import org.apache.commons.logging.LogFactory;
038    import org.apache.geronimo.console.util.PortletManager;
039    import org.apache.geronimo.kernel.util.XmlUtil;
040    import org.w3c.dom.Document;
041    import org.w3c.dom.Element;
042    import org.w3c.dom.Node;
043    import org.w3c.dom.NodeList;
044    import org.xml.sax.SAXException;
045    
046    /**
047     * Loads data on JMS providers known to the console.  Reads from a properties
048     * file on the class path.
049     *
050     * @version $Rev: 476061 $ $Date: 2006-11-17 01:36:50 -0500 (Fri, 17 Nov 2006) $
051     */
052    public class JMSProviderData implements Serializable {
053        private final static Log log = LogFactory.getLog(JMSProviderData.class);
054        private String name;
055        private final String raURI;
056        private final String dependency;
057        private String defaultTransaction;
058        private ConfigPropertyData[] instanceConfigProperties;
059        private ConnectionDefinition[] connectionDefinitions;
060        private AdminObjectDefinition[] adminObjectDefinitions;
061    
062        public JMSProviderData(String name, String raURI, String dependency) {
063            this.name = name;
064            this.raURI = raURI;
065            this.dependency = dependency;
066        }
067    
068        public String getName() {
069            return name;
070        }
071    
072        public String getRaURI() {
073            return raURI;
074        }
075    
076        public String getDependency() {
077            return dependency;
078        }
079    
080        public String getDefaultTransaction() {
081            return defaultTransaction;
082        }
083    
084        public ConfigPropertyData[] getInstanceConfigProperties() {
085            return instanceConfigProperties;
086        }
087    
088        public ConnectionDefinition[] getConnectionDefinitions() {
089            return connectionDefinitions;
090        }
091    
092        public AdminObjectDefinition[] getAdminObjectDefinitions() {
093            return adminObjectDefinitions;
094        }
095    
096        public static class ConfigPropertyData implements Serializable {
097            private final String name;
098            private final String type;
099            private final String defaultValue;
100            private final String description;
101    
102            public ConfigPropertyData(String name, String type, String defaultValue, String description) {
103                this.name = name;
104                this.type = type;
105                this.defaultValue = defaultValue;
106                this.description = description;
107            }
108    
109            public String getName() {
110                return name;
111            }
112    
113            public String getType() {
114                return type;
115            }
116    
117            public String getDefaultValue() {
118                return defaultValue;
119            }
120    
121            public String getDescription() {
122                return description;
123            }
124        }
125    
126        public static class ConnectionDefinition implements Serializable {
127            private final String connectionFactoryInterface;
128            private final ConfigPropertyData[] configProperties;
129    
130            public ConnectionDefinition(String connectionFactoryInterface, ConfigPropertyData[] configProperties) {
131                this.connectionFactoryInterface = connectionFactoryInterface;
132                this.configProperties = configProperties;
133            }
134    
135            public String getConnectionFactoryInterface() {
136                return connectionFactoryInterface;
137            }
138    
139            public ConfigPropertyData[] getConfigProperties() {
140                return configProperties;
141            }
142        }
143    
144        public static class AdminObjectDefinition implements Serializable {
145            private final String adminObjectInterface;
146            private final String adminObjectClass;
147            private final ConfigPropertyData[] configProperties;
148    
149            public AdminObjectDefinition(String adminObjectInterface, String adminObjectClass, ConfigPropertyData[] configProperties) {
150                this.adminObjectInterface = adminObjectInterface;
151                this.adminObjectClass = adminObjectClass;
152                this.configProperties = configProperties;
153            }
154    
155            public String getAdminObjectInterface() {
156                return adminObjectInterface;
157            }
158    
159            public String getAdminObjectClass() {
160                return adminObjectClass;
161            }
162    
163            public ConfigPropertyData[] getConfigProperties() {
164                return configProperties;
165            }
166        }
167    
168    
169        // *************** Static methods to access the data ****************
170    
171        private static List all = null;
172        public static JMSProviderData[] getAllProviders() {
173            if(all == null) {
174                loadProviders();
175            }
176            return (JMSProviderData[]) all.toArray(new JMSProviderData[all.size()]);
177        }
178    
179        public static JMSProviderData getProviderByName(String name) {
180            if(all == null) {
181                loadProviders();
182            }
183            for (int i = 0; i < all.size(); i++) {
184                JMSProviderData data = (JMSProviderData) all.get(i);
185                if(data.getName().equals(name)) {
186                    return data;
187                }
188            }
189            return null;
190        }
191    
192        public static JMSProviderData getProviderData(String rar, PortletRequest request) throws IOException {
193            if(all == null) {
194                loadProviders();
195            }
196            for (int i = 0; i < all.size(); i++) {
197                JMSProviderData data = (JMSProviderData) all.get(i);
198                if(data.getRaURI().equals(rar)) {
199                    if(data.instanceConfigProperties == null) {
200                        loadRARData(data, request);
201                    }
202                    return data;
203                }
204            }
205            JMSProviderData data = new JMSProviderData(null, rar, null);
206            loadRARData(data, request);
207            all.add(data);
208            return data;
209        }
210    
211        private static void loadRARData(JMSProviderData data, PortletRequest request) throws IOException {
212            File url = PortletManager.getRepositoryEntry(request, data.getRaURI());
213            if(url == null) {
214                throw new IOException("Unable to locate entry "+data.getRaURI()+" in repository");
215            }
216            ZipInputStream in = new ZipInputStream(new FileInputStream(url));
217            ZipEntry entry;
218            Document doc = null;
219            try {
220                while((entry = in.getNextEntry()) != null) {
221                    if(entry.getName().equals("META-INF/ra.xml")) {
222                        DocumentBuilderFactory factory = XmlUtil.newDocumentBuilderFactory();
223                        factory.setValidating(false);
224                        DocumentBuilder builder = factory.newDocumentBuilder();
225                        doc = builder.parse(in);
226                        in.close();
227                        in = null;
228                        break;
229                    } else in.closeEntry();
230                }
231            } catch (ParserConfigurationException e) {
232                log.error("Unable to read META-INF/ra.xml in RAR file '"+data.getRaURI()+"'", e);
233            } catch (SAXException e) {
234                log.error("Unable to read META-INF/ra.xml in RAR file '"+data.getRaURI()+"'", e);
235            } finally {
236                if (in != null)
237                    try {
238                        in.close();        
239                    } catch (IOException ignore) {
240                    }
241            }
242            if(doc == null) {
243                throw new IOException("Unable to locate META-INF/ra.xml in RAR file '"+data.getRaURI()+"'");
244            }
245            Element root = doc.getDocumentElement();
246            if(data.getName() == null) {
247                NodeList displays = getChildren(root, "display-name");
248                if(displays != null && displays.getLength() > 0) {
249                    data.name = getText(displays.item(0));
250                }
251            }
252            Element ra = (Element) getChildren(root, "resourceadapter").item(0);
253            data.instanceConfigProperties = loadConfigs(ra);
254            Element outbound = (Element) getChildren(ra, "outbound-resourceadapter").item(0);
255            data.defaultTransaction = getTransactionSetting(getChildText(outbound, "transaction-support"));
256            data.connectionDefinitions = loadConnections(outbound);
257            data.adminObjectDefinitions = loadAdmins(ra);
258        }
259    
260        private static String getTransactionSetting(String text) {
261            if(text == null) {
262                return null;
263            }
264            if(text.equals("XATransaction")) return "xa";
265            if(text.equals("LocalTransaction")) return "local";
266            if(text.equals("NoTransaction")) return "none";
267            return null;
268        }
269    
270        private static ConfigPropertyData[] loadConfigs(Element parent) {
271            NodeList configs = getChildren(parent, "config-property");
272            if(configs == null || configs.getLength() == 0) {
273                return new ConfigPropertyData[0];
274            }
275            ConfigPropertyData[] results = new ConfigPropertyData[configs.getLength()];
276            for (int i = 0; i < results.length; i++) {
277                Element root = (Element) configs.item(i);
278                results[i] = new ConfigPropertyData(getChildText(root, "config-property-name"),
279                        getChildText(root, "config-property-type"), getChildText(root, "config-property-value"),
280                        getChildText(root, "description"));
281            }
282            return results;
283        }
284    
285        private static NodeList getChildren(Element parent, String child) {
286            final List list = new ArrayList();
287            NodeList nodes = parent.getChildNodes();
288            for(int i=0; i<nodes.getLength(); i++) {
289                Node node = nodes.item(i);
290                if(node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName().equals(child)) {
291                    list.add(node);
292                }
293            }
294            return new NodeList() {
295                public Node item(int index) {
296                    return (Node) list.get(index);
297                }
298    
299                public int getLength() {
300                    return list.size();
301                }
302            };
303        }
304    
305        private static ConnectionDefinition[] loadConnections(Element outbound) {
306            NodeList defs = getChildren(outbound, "connection-definition");
307            if(defs == null || defs.getLength() == 0) {
308                return new ConnectionDefinition[0];
309            }
310            ConnectionDefinition[] results = new ConnectionDefinition[defs.getLength()];
311            for (int i = 0; i < results.length; i++) {
312                Element def = (Element) defs.item(i);
313                results[i] = new ConnectionDefinition(getChildText(def, "connectionfactory-interface"), loadConfigs(def));
314            }
315            return results;
316        }
317    
318        private static AdminObjectDefinition[] loadAdmins(Element ra) {
319            NodeList defs = getChildren(ra, "adminobject");
320            if(defs == null || defs.getLength() == 0) {
321                return new AdminObjectDefinition[0];
322            }
323            AdminObjectDefinition[] results = new AdminObjectDefinition[defs.getLength()];
324            for (int i = 0; i < results.length; i++) {
325                Element def = (Element) defs.item(i);
326                results[i] = new AdminObjectDefinition(getChildText(def, "adminobject-interface"),
327                        getChildText(def, "adminobject-class"), loadConfigs(def));
328            }
329            return results;
330        }
331    
332        private static String getChildText(Element root, String name) {
333            NodeList list = getChildren(root, name);
334            if(list == null || list.getLength() == 0) {
335                return null;
336            }
337            return getText(list.item(0));
338        }
339    
340        private static String getText(Node node) {
341            StringBuffer buf = null;
342            NodeList list = node.getChildNodes();
343            if(list != null) {
344                for(int i=0; i<list.getLength(); i++) {
345                    Node current = list.item(i);
346                    if(current.getNodeType() == Node.TEXT_NODE) {
347                        if(buf == null) {
348                            buf = new StringBuffer();
349                        }
350                        buf.append(current.getNodeValue());
351                    }
352                }
353            }
354            return buf == null ? null : buf.toString();
355        }
356    
357    
358        private static void loadProviders() {
359            InputStream in = JMSProviderData.class.getResourceAsStream("/jms-resource-providers.properties");
360            if(in == null) {
361                log.error("Unable to locate JMS provider properties file");
362                return;
363            }
364            Properties props = new Properties();
365            try {
366                props.load(in);
367            } catch (IOException e) {
368                log.error("Unable to read JMS provider properties file", e);
369            } finally {
370                // load could fail, ensure stream is closed.
371                try {
372                    in.close();
373                } catch (IOException ignore) {
374                    // ignore
375                }
376            }
377            Set set = new HashSet();
378            // Find the names of the provider entries
379            for (Iterator it = props.keySet().iterator(); it.hasNext();) {
380                String key = (String) it.next();
381                int start = key.indexOf('.');
382                int end = key.indexOf('.', start+1);
383                if(start < 0 || end < 0) {
384                    continue;
385                }
386                set.add(key.substring(start+1, end));
387            }
388            List list = new ArrayList(set.size());
389            for (Iterator it = set.iterator(); it.hasNext();) {
390                String key = (String) it.next();
391                String name = props.getProperty("provider."+key+".name");
392                String rar = props.getProperty("provider."+key+".rar");
393                String dep = props.getProperty("provider."+key+".dependency");
394                list.add(new JMSProviderData(name, rar, dep));
395            }
396            all = list;
397        }
398    }