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.monitoring.snapshot;
018    
019    import java.io.File;
020    import java.io.FileOutputStream;
021    import java.util.ArrayList;
022    
023    import javax.xml.parsers.DocumentBuilder;
024    import javax.xml.parsers.DocumentBuilderFactory;
025    import javax.xml.parsers.ParserConfigurationException;
026    
027    import org.apache.commons.logging.Log;
028    import org.apache.commons.logging.LogFactory;
029    import org.apache.xmlbeans.XmlCursor;
030    import org.apache.xmlbeans.XmlObject;
031    import org.w3c.dom.Document;
032    import org.w3c.dom.Element;
033    import org.w3c.dom.Node;
034    import org.w3c.dom.NodeList;
035    
036    /**
037     * In charge of dealing with the XML processing of the snapshot's data.
038     */
039    public class SnapshotConfigXMLBuilder {
040        private static Log log = LogFactory.getLog(SnapshotConfigXMLBuilder.class);
041        
042        private static final String pathToXML = 
043            System.getProperty("org.apache.geronimo.home.dir") + "/var/monitoring/snapshot-config.xml";
044        
045        private static final String SNAPSHOT_CONFIG = "snapshot-config";
046        private static final String DURATION = "duration";
047        private static final String RETENTION = "retention";
048        private static final String MBEAN = "mbean"; 
049        
050        /**
051         * @return A list of all mbean names that have been previously saved.
052         * These mbean names are those to keep track of for per snapshot.
053         */
054        public static ArrayList<String> getMBeanNames() {
055            ArrayList<String> mbeanList = new ArrayList<String>();
056            // get an instance of the document
057            Document doc = openDocument();
058            // get the root element node
059            Element rootElement = doc.getDocumentElement();
060            // get all children in the root node (i.e. all config properties)
061            NodeList configNodes = rootElement.getChildNodes();
062            // find the duration node and save it
063            for(int i = 0; i < configNodes.getLength(); i++) {
064                if(MBEAN.equals(configNodes.item(i).getNodeName())) {
065                    mbeanList.add( configNodes.item(i).getTextContent() );
066                }
067            }
068            return mbeanList;
069        }
070        
071        /**
072         * Adds to the snapshot-config.xml another configuration element <mbean>
073         * in order to persistently keep track of all user requested statistics.
074         * If there is a duplicate, nothing will be done.
075         */
076        public static boolean removeMBeanName(String mbeanName) {
077            ArrayList<String> mbeanList = getMBeanNames();
078            // operate on the snapshot-config.xml if there exists the mbean name
079            if(mbeanList.contains(mbeanName)) {
080                // get an instance of the document
081                Document doc = openDocument();
082                // get the root element node
083                Element rootElement = doc.getDocumentElement();
084                // find the Node that represents the mbeanName
085                NodeList list = rootElement.getChildNodes();
086                for(int i = 0; i < list.getLength(); i++) {
087                    // check the Node's text context for a match with mbeanName
088                    if(list.item(i).getTextContent().equals(mbeanName)) {
089                        // remove the node from rootElement
090                        Node toRemoveNode = list.item(i);
091                        rootElement.removeChild(toRemoveNode);
092                        break;
093                    }
094                }
095                // save the document
096                saveDocument(doc, pathToXML);
097                return true;
098            } else {
099                return false;
100            }
101        }
102    
103        /**
104         * Removes from the snapshot-config.xml a configuration element <mbean>
105         * in order to persistently keep track of all user requested statistics.
106         * If there does not exist an instance of the mbeanNAme, nothing will be done.
107         */
108        public static boolean addMBeanName(String mbeanName) {
109            ArrayList<String> mbeanList = getMBeanNames();
110            if(mbeanList.contains(mbeanName)) {
111                return false;
112            } else {
113                // get an instance of the document
114                Document doc = openDocument();
115                // get the root element node
116                Element rootElement = doc.getDocumentElement();
117                // create <mbean> element
118                Element mbeanElement = doc.createElement(MBEAN);
119                mbeanElement.setTextContent(mbeanName);
120                // add <mbean> element to the rootElement
121                rootElement.appendChild(mbeanElement);
122                try {
123                    Thread.sleep(1000);
124                } catch(Exception e) {
125                    
126                }
127                // save the document
128                saveDocument(doc, pathToXML);
129                return true;
130            }
131        }
132        
133        /**
134         * Saves the duration of the snapshot as a configuration attribute
135         * @param duration
136         */
137        public static void saveDuration(long duration) {
138            saveAttribute(DURATION, duration);
139        }
140    
141        /**
142         * Saves the retention of the snapshot as a configuration attribute
143         * @param retention
144         */
145        public static void saveRetention(int retention) {
146            saveAttribute(RETENTION, retention);
147        }
148        
149        /**
150         * Saves a generic attribute value into the node with text = attribute name.
151         * Creates one if there is not an instance of one.
152         * @param attrName
153         * @param attributeValue
154         */
155        private static void saveAttribute(String attrName, long attributeValue) {
156            Document doc = openDocument();
157            // get the root node        
158            Element rootElement = doc.getDocumentElement();
159            // get all children in the root node (i.e. all config properties)
160            NodeList configNodes = rootElement.getChildNodes();
161            // find the duration node and save it
162            boolean foundNode = false;
163            for(int i = 0; i < configNodes.getLength() && !foundNode; i++) {
164                Node configNode = configNodes.item(i);
165                if(attrName.equals(configNode.getNodeName())) {
166                    // found a match
167                    configNode.setTextContent(attributeValue + "");
168                    foundNode = true;
169                }
170            }
171            // if there was not a duration node, make one
172            if(!foundNode) {
173                Element element = doc.createElement(attrName);
174                element.setTextContent(attributeValue + "");
175                rootElement.appendChild(element);
176            }
177            try {
178                Thread.sleep(1000);
179            } catch(Exception e) {
180                
181            }
182            log.info("***saving:  " + attrName + " = " + attributeValue);
183            // save the document to file
184            saveDocument(doc, pathToXML);
185        }
186        
187        /**
188         * Returns the value of the configuration attribute, defined by the key
189         * @param key
190         * @return
191         * @throws Exception
192         */
193        public static String getAttributeValue(String key) throws Exception {
194            // ensure that there exists the 'monitor' directory
195            ensureMonitorDir();
196            // get an instance of the document
197            Document doc = openDocument();
198            // get the root element node
199            Element rootElement = doc.getDocumentElement();
200            // get all children in the root node (i.e. all config properties)
201            NodeList configNodes = rootElement.getChildNodes();
202            // find the duration node and save it
203            for(int i = 0; i < configNodes.getLength(); i++) {
204                if(key.equals(configNodes.item(i).getNodeName())) {
205                    return configNodes.item(i).getTextContent();
206                }
207            }
208            throw new Exception("[WARNING] " + key + " is not found in " + SNAPSHOT_CONFIG);
209        }
210        
211        /**
212         * Ensures that there is an existing XML file. Creates one if there
213         * does not exist one already. 
214         */
215        public static void checkXMLExists() {
216            File docFile = new File(pathToXML);
217            // create an XML document if it does not exist
218            if(!docFile.exists()) {
219                Document doc = setUpDocument( createDocument() );
220                saveDocument(doc, pathToXML);
221            }
222        }
223        
224        /**
225         * Prepares the root element for a document.
226         */
227        public static Document setUpDocument(Document document) {
228            // add <snapshot-config> tag as the root
229            Element rootElement = document.createElement("snapshot-config");
230            document.appendChild(rootElement);
231            return document;
232        }
233        
234        /**
235         * Creates an instance of a Document and returns it
236         */
237        public static Document createDocument() {
238            // get an instance of factory
239            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
240            try {
241                // get an instance of builder
242                DocumentBuilder db = dbf.newDocumentBuilder();
243                // create an instance of DOM
244                return db.newDocument();
245            } catch(ParserConfigurationException pce) {
246                log.error("Error while trying to instantiate DocumentBuilder", pce);
247            }
248            return null;
249        }
250        
251        /**
252         * Write the document object to the file location specified by
253         * the path.
254         */
255        public static void saveDocument(Document document, String path) {
256            try {
257                // before saving, make sure the directory is present
258                ensureMonitorDir();
259    
260                //TODO GERONIMO-3719.  Hack to use xmlbeans to write out xml instead of sun specific classes.
261                XmlObject xmlObject = XmlObject.Factory.parse(document.getDocumentElement());
262                xmlObject.save(new File(path));
263    
264                // formatting the doc
265                // generate a file output
266            } catch(Exception e) {
267                log.error(e.getMessage(), e);
268            }
269        }
270    
271        /**
272         * Parses the XML document specified by the private member 'pathToXML'
273         * and stores the information in the a Document object
274         */
275        public static Document openDocument() {
276            // ensure that the XML file is there
277            checkXMLExists();
278            // get the factory
279            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
280            // continue to attempt to parse
281            while(true) {
282                try {
283                    // Using factory get an instance of document builder
284                    DocumentBuilder db = dbf.newDocumentBuilder();
285                    // parse using builder to get DOM representation of the XML file
286                    Document doc = db.parse(pathToXML);
287                    return doc;
288                } catch(Exception e) {
289                    // Either this file is being read/written to by snapshot thread
290                    // or there is an UNKNOWN error
291                    log.error(e.getMessage(), e);
292                }
293            }
294        }
295        
296        /**
297         * Checks to see if the GERONIMO_HOME/var/monitoring/ directory was made.
298         * If not, the method creates it.
299         */
300        public static void ensureMonitorDir() {
301            final String pathToDir = 
302                System.getProperty("org.apache.geronimo.home.dir") + "/var/monitoring/";
303            File dir = new File(pathToDir);
304            if(dir.exists() && dir.isDirectory()) {
305                // all good
306                return;
307            } else {
308                // make a directory
309                if(dir.mkdir()) {
310                    // directory was successfully created
311                    log.info("/var/monitoring directory created.");
312                    return;
313                } else {
314                    log.error("Could not make the directory " + pathToDir);
315                }
316            }
317        }
318    }