View Javadoc
1   /**
2    * This file Copyright (c) 2017 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.about.app;
35  
36  import info.magnolia.cms.beans.config.ServerConfiguration;
37  import info.magnolia.cms.pddescriptor.ProductDescriptorExtractor;
38  import info.magnolia.context.WebContext;
39  import info.magnolia.init.MagnoliaConfigurationProperties;
40  
41  import java.io.File;
42  import java.sql.Connection;
43  import java.sql.DatabaseMetaData;
44  import java.sql.DriverManager;
45  import java.sql.PreparedStatement;
46  import java.sql.ResultSet;
47  import java.sql.SQLException;
48  
49  import javax.inject.Inject;
50  import javax.inject.Provider;
51  import javax.jcr.Repository;
52  import javax.jcr.RepositoryException;
53  import javax.naming.Context;
54  import javax.naming.InitialContext;
55  import javax.naming.NamingException;
56  import javax.sql.DataSource;
57  import javax.xml.parsers.DocumentBuilder;
58  import javax.xml.parsers.DocumentBuilderFactory;
59  import javax.xml.parsers.SAXParserFactory;
60  import javax.xml.xpath.XPath;
61  import javax.xml.xpath.XPathConstants;
62  import javax.xml.xpath.XPathFactory;
63  
64  import org.apache.commons.lang3.StringUtils;
65  import org.apache.jackrabbit.commons.JcrUtils;
66  import org.slf4j.Logger;
67  import org.slf4j.LoggerFactory;
68  import org.w3c.dom.Document;
69  import org.xml.sax.Attributes;
70  import org.xml.sax.SAXException;
71  import org.xml.sax.helpers.DefaultHandler;
72  
73  /**
74   * Component for obtaining information about current installation and environment.
75   */
76  public class InstanceConfigurationProvider {
77  
78      private static final Logger log = LoggerFactory.getLogger(InstanceConfigurationProvider.class);
79  
80      private final Provider<WebContext> contextProvider;
81      private final ProductDescriptorExtractor productDescriptorExtractor;
82      private final MagnoliaConfigurationProperties magnoliaProperties;
83      private final ServerConfiguration serverConfiguration;
84  
85      @Inject
86      public InstanceConfigurationProvider(final Provider<WebContext> contextProvider,
87              final ServerConfiguration serverConfiguration, final MagnoliaConfigurationProperties magnoliaProperties,
88              final ProductDescriptorExtractor productDescriptorExtractor) {
89          this.contextProvider = contextProvider;
90          this.productDescriptorExtractor = productDescriptorExtractor;
91          this.serverConfiguration = serverConfiguration;
92          this.magnoliaProperties = magnoliaProperties;
93      }
94  
95      protected String[] getConnectionString() {
96          File config = null;
97          // Assuming, the path to the repository-config.-file is configured
98          // relative, starting with WEB-INF.
99          // Otherwise, assuming it's an absolute path for this config. (See JIRA
100         // MGNLUI-3163)
101         String configuredPath = magnoliaProperties.getProperty("magnolia.repositories.jackrabbit.config");
102         if (configuredPath != null) {
103             if (configuredPath.startsWith("WEB-INF")) {
104                 config = new File(magnoliaProperties.getProperty("magnolia.app.rootdir") + "/" + configuredPath);
105             } else {
106                 config = new File(configuredPath);
107             }
108         }
109         // No special handling here if the config (file) is null or not
110         // existing.
111         // If the path is wrong or not set, Magnolia won't start up properly and
112         // it won't be possible to launch the About-app.
113 
114         final String[] connectionString = new String[] { "", "", "" };
115         try {
116             DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
117             DocumentBuilder builder = factory.newDocumentBuilder();
118             Document doc = builder.parse(config);
119             XPathFactory xPathfactory = XPathFactory.newInstance();
120             XPath xpath = xPathfactory.newXPath();
121             String url = (String) xpath.compile("/Repository/Workspace/PersistenceManager/param[@name='url']/@value")
122                     .evaluate(doc, XPathConstants.STRING);
123             if (StringUtils.isNotBlank(url)) {
124                 connectionString[0] = url;
125             } else {
126                 connectionString[0] = (String) xpath.compile(
127                         "/Repository/DataSources/DataSource/param[@name='url']/@value").evaluate(doc,
128                         XPathConstants.STRING);
129                 connectionString[1] = (String) xpath.compile(
130                         "/Repository/DataSources/DataSource/param[@name='user']/@value").evaluate(doc,
131                         XPathConstants.STRING);
132                 connectionString[2] = (String) xpath.compile(
133                         "/Repository/DataSources/DataSource/param[@name='password']/@value").evaluate(doc,
134                         XPathConstants.STRING);
135             }
136         } catch (Exception e) {
137             log.debug("Failed to obtain DB connection info with {}", e.getMessage(), e);
138         }
139         return connectionString;
140     }
141 
142     protected String getRepositoryName() {
143         String repoConfigPath = magnoliaProperties.getProperty("magnolia.repositories.config");
144         File config = new File(magnoliaProperties.getProperty("magnolia.app.rootdir") + "/" + repoConfigPath);
145         final String[] repoName = new String[1];
146         try {
147             SAXParserFactory.newInstance().newSAXParser().parse(config, new DefaultHandler() {
148                 private boolean inRepo;
149 
150                 @Override
151                 public void startElement(String uri, String localName, String qName, Attributes attributes)
152                         throws SAXException {
153                     super.startElement(uri, localName, qName, attributes);
154                     if ("RepositoryMapping".equals(qName)) {
155                         inRepo = true;
156                     }
157                     if (inRepo && "Map".equals(qName)) {
158                         if ("config".equals(attributes.getValue("name"))) {
159                             repoName[0] = attributes.getValue("repositoryName");
160                         }
161                     }
162                 }
163 
164                 @Override
165                 public void endElement(String uri, String localName, String qName) throws SAXException {
166                     super.endElement(uri, localName, qName);
167                     if ("RepositoryMapping".equals(localName)) {
168                         inRepo = false;
169                     }
170                 }
171             });
172             return repoName[0];
173         } catch (Exception e) {
174             log.debug("Failed to obtain repository configuration info with {}", e.getMessage(), e);
175         }
176         return null;
177     }
178 
179     protected String getRepositoryHome() {
180         return magnoliaProperties.getProperty("magnolia.repositories.home");
181     }
182 
183     private Connection getConnection() throws SQLException, NamingException {
184         Connection connection = null;
185         String connectionString[] = getConnectionString();
186         String repoHome = getRepositoryHome();
187         String repoName = getRepositoryName();
188         connectionString[0] = StringUtils.replace(connectionString[0], "${wsp.home}", repoHome + "/" + repoName
189                 + "/workspaces/default");
190         if (connectionString[0].startsWith("jdbc:")) {
191             // JDBC url
192             connection = DriverManager.getConnection(connectionString[0], connectionString[1], connectionString[2]);
193         } else if (connectionString[0].startsWith("java:")) {
194             // JNDI url
195             Context initialContext = new InitialContext();
196             DataSource datasource = (DataSource) initialContext.lookup(connectionString[0]);
197             if (datasource != null) {
198                 connection = datasource.getConnection();
199             } else {
200                 log.debug("Failed to lookup datasource.");
201             }
202         }
203         return connection;
204     }
205 
206     private String getMySQLEngineInfo(Connection connection, String[] connectionString) {
207         PreparedStatement statement = null;
208         ResultSet resultSet = null;
209         try {
210             statement = connection.prepareStatement("SHOW TABLE STATUS FROM `"
211                     + StringUtils.substringAfterLast(connectionString[0], "/") + "`;");
212             resultSet = statement.executeQuery();
213             if (resultSet.next()) {
214                 String engine = resultSet.getString("Engine");
215                 return " (" + engine + ")";
216             }
217         } catch (SQLException e) {
218             // can't get extra info, oh well
219         } finally {
220             try {
221                 if (resultSet != null) {
222                     resultSet.close();
223                 }
224                 if (statement != null) {
225                     statement.close();
226                 }
227             } catch (SQLException ignored) {
228             }
229         }
230         return null;
231     }
232 
233     public String getDatabase() {
234         String dbInfo = null;
235         Connection connection = null;
236         try {
237             connection = getConnection();
238             if (connection != null) {
239                 DatabaseMetaData meta = connection.getMetaData();
240                 dbInfo = meta.getDatabaseProductName() + " " + meta.getDatabaseProductVersion();
241                 if (dbInfo.toLowerCase().contains("mysql")) {
242                     String engine = getMySQLEngineInfo(connection, getConnectionString());
243                     if (engine != null) {
244                         dbInfo += engine;
245                     }
246                 }
247             }
248         } catch (NamingException e) {
249             log.debug("Failed obtain DB connection through JNDI with {}", e.getMessage(), e);
250         } catch (SQLException e) {
251             log.debug("Failed to read DB and driver info from connection with {}", e.getMessage(), e);
252         } catch (IllegalArgumentException e) {
253             log.debug("Failed to understand DB connection URL with {}", e.getMessage(), e);
254         } finally {
255             if (connection != null) {
256                 try {
257                     connection.close();
258                 } catch (SQLException e) {
259                     // ignore, nothing we can do
260                 }
261             }
262         }
263         return dbInfo;
264     }
265 
266     public String getDatabaseDriver() {
267         String dbDriverInfo = null;
268         Connection connection = null;
269         try {
270             connection = getConnection();
271             if (connection != null) {
272                 DatabaseMetaData meta = connection.getMetaData();
273                 dbDriverInfo = meta.getDriverName() + " " + meta.getDriverVersion();
274             }
275         } catch (NamingException e) {
276             log.debug("Failed obtain DB connection through JNDI with {}", e.getMessage(), e);
277         } catch (SQLException e) {
278             log.debug("Failed to read DB and driver info from connection with {}", e.getMessage(), e);
279         } catch (IllegalArgumentException e) {
280             log.debug("Failed to understand DB connection URL with {}", e.getMessage(), e);
281         } finally {
282             if (connection != null) {
283                 try {
284                     connection.close();
285                 } catch (SQLException e) {
286                     // ignore, nothing we can do
287                 }
288             }
289         }
290         return dbDriverInfo;
291     }
292 
293     public String getDatabaseVersion() {
294         return null;
295     }
296 
297     public String getJcrName() {
298         try {
299             Repository repo = JcrUtils.getRepository();
300             return repo.getDescriptor("jcr.repository.name");
301         } catch (RepositoryException e) {
302             log.debug("JCR repository information is not available", e);
303         }
304         return null;
305     }
306 
307     public String getJcrVersion() {
308         try {
309             Repository repo = JcrUtils.getRepository();
310             return repo.getDescriptor("jcr.repository.version");
311         } catch (RepositoryException e) {
312             log.debug("JCR repository information is not available", e);
313         }
314         return null;
315     }
316 
317     public String getMagnoliaVersion() {
318         return productDescriptorExtractor.get(ProductDescriptorExtractor.VERSION_NUMBER);
319     }
320 
321     public boolean isAdmin() {
322         return serverConfiguration.isAdmin();
323     }
324 
325     public String getOSName() {
326         return magnoliaProperties.getProperty("os.name");
327     }
328 
329     public String getOSVersion() {
330         return magnoliaProperties.getProperty("os.version");
331     }
332 
333     public String getOSArch() {
334         return magnoliaProperties.getProperty("os.arch");
335     }
336 
337     public String getJavaVendor() {
338         return magnoliaProperties.getProperty("java.vendor");
339     }
340 
341     public String getJavaVersion() {
342         return magnoliaProperties.getProperty("java.version");
343     }
344 
345     public String getJavaRuntimeVersion() {
346         return magnoliaProperties.getProperty("java.runtime.version");
347     }
348 
349     public String getApplicationServer() {
350         return contextProvider.get().getServletContext().getServerInfo();
351     }
352 }