1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.about.app;
35
36 import static info.magnolia.about.app.AboutView.*;
37
38 import info.magnolia.cms.beans.config.ServerConfiguration;
39 import info.magnolia.cms.pddescriptor.ProductDescriptorExtractor;
40 import info.magnolia.context.MgnlContext;
41 import info.magnolia.i18nsystem.SimpleTranslator;
42 import info.magnolia.init.MagnoliaConfigurationProperties;
43 import info.magnolia.objectfactory.Components;
44
45 import java.io.File;
46 import java.sql.Connection;
47 import java.sql.DatabaseMetaData;
48 import java.sql.DriverManager;
49 import java.sql.PreparedStatement;
50 import java.sql.ResultSet;
51 import java.sql.SQLException;
52
53 import javax.inject.Inject;
54 import javax.jcr.Repository;
55 import javax.jcr.RepositoryException;
56 import javax.naming.Context;
57 import javax.naming.InitialContext;
58 import javax.naming.NamingException;
59 import javax.sql.DataSource;
60 import javax.xml.parsers.DocumentBuilder;
61 import javax.xml.parsers.DocumentBuilderFactory;
62 import javax.xml.parsers.SAXParserFactory;
63 import javax.xml.xpath.XPath;
64 import javax.xml.xpath.XPathConstants;
65 import javax.xml.xpath.XPathFactory;
66
67 import org.apache.commons.lang3.StringUtils;
68 import org.apache.jackrabbit.commons.JcrUtils;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
71 import org.w3c.dom.Document;
72 import org.xml.sax.Attributes;
73 import org.xml.sax.SAXException;
74 import org.xml.sax.helpers.DefaultHandler;
75
76 import com.vaadin.data.Item;
77 import com.vaadin.data.util.ObjectProperty;
78 import com.vaadin.data.util.PropertysetItem;
79
80
81
82
83 public class AboutPresenter {
84
85 private static final Logger log = LoggerFactory.getLogger(AboutPresenter.class);
86
87 static final String COMMUNITY_EDITION_I18N_KEY = "about.app.main.communityEdition";
88 static final String INSTANCE_AUTHOR_I18N_KEY = "about.app.main.instance.author";
89 static final String INSTANCE_PUBLIC_I18N_KEY = "about.app.main.instance.public";
90 static final String UNKNOWN_PROPERTY_I18N_KEY = "about.app.main.unknown";
91
92 protected final SimpleTranslator i18n;
93 private final String unknownProperty;
94
95 private final AboutView view;
96 private final ProductDescriptorExtractor productDescriptorExtractor;
97 private final ServerConfiguration serverConfiguration;
98 private final MagnoliaConfigurationProperties magnoliaProperties;
99
100
101
102
103 @Deprecated
104 protected Item viewData = new PropertysetItem();
105
106 @Inject
107 public AboutPresenter(AboutView view, ServerConfiguration serverConfiguration, MagnoliaConfigurationProperties magnoliaProperties, SimpleTranslator i18n, ProductDescriptorExtractor productDescriptorExtractor) {
108 this.view = view;
109 this.productDescriptorExtractor = productDescriptorExtractor;
110 this.serverConfiguration = serverConfiguration;
111 this.magnoliaProperties = magnoliaProperties;
112 this.i18n = i18n;
113 this.unknownProperty = i18n.translate(UNKNOWN_PROPERTY_I18N_KEY);
114 }
115
116
117
118
119 @Deprecated
120 public AboutPresenter(AboutView view, ServerConfiguration serverConfiguration, MagnoliaConfigurationProperties magnoliaProperties, SimpleTranslator i18n) {
121 this(view, serverConfiguration, magnoliaProperties, i18n, Components.getComponent(ProductDescriptorExtractor.class));
122 }
123
124 public AboutView start() {
125 Item viewData = getInstallationInfo();
126
127 if (!this.viewData.getItemPropertyIds().isEmpty()) {
128 this.viewData.getItemPropertyIds()
129 .forEach(propertyId -> viewData.addItemProperty(propertyId, AboutPresenter.this.viewData.getItemProperty(propertyId)));
130 }
131 view.setDataSource(viewData);
132 return view;
133 }
134
135 protected Item getInstallationInfo() {
136 PropertysetItem viewData = new PropertysetItem();
137
138 String mgnlEdition = getEditionName();
139 String mgnlVersion = productDescriptorExtractor.get(ProductDescriptorExtractor.VERSION_NUMBER);
140 String authorInstance = serverConfiguration.isAdmin() ?
141 i18n.translate(INSTANCE_AUTHOR_I18N_KEY) :
142 i18n.translate(INSTANCE_PUBLIC_I18N_KEY);
143
144
145 String osInfo = String.format("%s %s (%s)",
146 magnoliaProperties.getProperty("os.name"),
147 magnoliaProperties.getProperty("os.version"),
148 magnoliaProperties.getProperty("os.arch"));
149 String javaInfo = String.format("%s (build %s)",
150 magnoliaProperties.getProperty("java.version"),
151 magnoliaProperties.getProperty("java.runtime.version"));
152 String serverInfo = MgnlContext.getWebContext().getServletContext().getServerInfo();
153
154 String dbInfo = null;
155 String dbDriverInfo = null;
156 Connection connection = null;
157 try {
158 String connectionString[] = getConnectionString();
159
160 String repoHome = magnoliaProperties.getProperty("magnolia.repositories.home");
161 String repoName = getRepoName();
162 connectionString[0] = StringUtils.replace(connectionString[0], "${wsp.home}", repoHome + "/" + repoName + "/workspaces/default");
163 if (connectionString[0].startsWith("jdbc:")) {
164
165 connection = DriverManager.getConnection(connectionString[0], connectionString[1], connectionString[2]);
166 } else if (connectionString[0].startsWith("java:")) {
167
168 Context initialContext = new InitialContext();
169 DataSource datasource = (DataSource) initialContext.lookup(connectionString[0]);
170 if (datasource != null) {
171 connection = datasource.getConnection();
172 } else {
173 log.debug("Failed to lookup datasource.");
174 }
175 }
176 if (connection != null) {
177 DatabaseMetaData meta = connection.getMetaData();
178 dbInfo = meta.getDatabaseProductName() + " " + meta.getDatabaseProductVersion();
179 if (dbInfo.toLowerCase().contains("mysql")) {
180 String engine = getMySQLEngineInfo(connection, connectionString);
181 if (engine != null) {
182 dbInfo += engine;
183 }
184 }
185 dbDriverInfo = meta.getDriverName() + " " + meta.getDriverVersion();
186 } else {
187 dbInfo = i18n.translate(UNKNOWN_PROPERTY_I18N_KEY);
188 }
189 } catch (NamingException e) {
190 log.debug("Failed obtain DB connection through JNDI with {}", e.getMessage(), e);
191 } catch (SQLException e) {
192 log.debug("Failed to read DB and driver info from connection with {}", e.getMessage(), e);
193 } catch (IllegalArgumentException e) {
194 log.debug("Failed to understand DB connection URL with {}", e.getMessage(), e);
195 } finally {
196 if (connection != null) {
197 try {
198 connection.close();
199 } catch (SQLException e) {
200
201 }
202 }
203 }
204
205 String jcrInfo;
206 try {
207 Repository repo = JcrUtils.getRepository();
208 jcrInfo = String.format("%s %s",
209 repo.getDescriptor("jcr.repository.name"),
210 repo.getDescriptor("jcr.repository.version"));
211 } catch (RepositoryException e) {
212 log.debug("JCR repository information is not available", e);
213 jcrInfo = "-";
214 }
215
216
217 addItemProperty(viewData, MAGNOLIA_EDITION_KEY, mgnlEdition);
218 addItemProperty(viewData, MAGNOLIA_VERSION_KEY, mgnlVersion);
219 addItemProperty(viewData, MAGNOLIA_INSTANCE_KEY, authorInstance);
220 addItemProperty(viewData, OS_INFO_KEY, osInfo);
221 addItemProperty(viewData, JAVA_INFO_KEY, javaInfo);
222 addItemProperty(viewData, SERVER_INFO_KEY, serverInfo);
223 addItemProperty(viewData, JCR_INFO_KEY, jcrInfo);
224 addItemProperty(viewData, DB_INFO_KEY, dbInfo);
225 addItemProperty(viewData, DB_DRIVER_INFO_KEY, dbDriverInfo);
226
227 return viewData;
228 }
229
230
231
232
233 protected void addItemProperty(Item viewData, String key, String value) {
234 viewData.addItemProperty(key, new ObjectProperty<>(StringUtils.defaultIfBlank(value, unknownProperty)));
235 }
236
237
238
239
240 @Deprecated
241 protected void addViewProperty(String key, String value) {
242 viewData.addItemProperty(key, new ObjectProperty<>(StringUtils.defaultIfBlank(value, unknownProperty)));
243 }
244
245 protected AboutView getView() {
246 return view;
247 }
248
249
250
251
252 protected String getEditionName() {
253
254 return i18n.translate(COMMUNITY_EDITION_I18N_KEY);
255 }
256
257 private String getMySQLEngineInfo(Connection connection, String[] connectionString) {
258 PreparedStatement statement = null;
259 ResultSet resultSet = null;
260 try {
261 statement = connection.prepareStatement("SHOW TABLE STATUS FROM `" + StringUtils.substringAfterLast(connectionString[0], "/") + "`;");
262 resultSet = statement.executeQuery();
263 if (resultSet.next()) {
264 String engine = resultSet.getString("Engine");
265 return " (" + engine + ")";
266 }
267 } catch (SQLException e) {
268
269 } finally {
270 try {
271 if (resultSet != null) {
272 resultSet.close();
273 }
274 if (statement != null) {
275 statement.close();
276 }
277 } catch (SQLException ignored) {
278 }
279 }
280 return null;
281 }
282
283 String[] getConnectionString() {
284 File config = null;
285
286
287 String configuredPath = magnoliaProperties.getProperty("magnolia.repositories.jackrabbit.config");
288 if (configuredPath != null) {
289 if (configuredPath.startsWith("WEB-INF")) {
290 config = new File(magnoliaProperties.getProperty("magnolia.app.rootdir") + "/" + configuredPath);
291 } else {
292 config = new File(configuredPath);
293 }
294 }
295
296
297
298 final String[] connectionString = new String[]{"", "", ""};
299 try {
300 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
301 DocumentBuilder builder = factory.newDocumentBuilder();
302 Document doc = builder.parse(config);
303 XPathFactory xPathfactory = XPathFactory.newInstance();
304 XPath xpath = xPathfactory.newXPath();
305 String url = (String) xpath.compile("/Repository/Workspace/PersistenceManager/param[@name='url']/@value").evaluate(doc, XPathConstants.STRING);
306 if (StringUtils.isNotBlank(url)) {
307 connectionString[0] = url;
308 } else {
309 connectionString[0] = (String) xpath.compile("/Repository/DataSources/DataSource/param[@name='url']/@value").evaluate(doc, XPathConstants.STRING);
310 connectionString[1] = (String) xpath.compile("/Repository/DataSources/DataSource/param[@name='user']/@value").evaluate(doc, XPathConstants.STRING);
311 connectionString[2] = (String) xpath.compile("/Repository/DataSources/DataSource/param[@name='password']/@value").evaluate(doc, XPathConstants.STRING);
312 }
313 } catch (Exception e) {
314 log.debug("Failed to obtain DB connection info with {}", e.getMessage(), e);
315 }
316 return connectionString;
317 }
318
319 String getRepoName() {
320 String repoConfigPath = magnoliaProperties.getProperty("magnolia.repositories.config");
321 File config = new File(magnoliaProperties.getProperty("magnolia.app.rootdir") + "/" + repoConfigPath);
322 final String[] repoName = new String[1];
323 try {
324 SAXParserFactory.newInstance().newSAXParser().parse(config, new DefaultHandler() {
325 private boolean inRepo;
326
327 @Override
328 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
329 super.startElement(uri, localName, qName, attributes);
330 if ("RepositoryMapping".equals(qName)) {
331 inRepo = true;
332 }
333 if (inRepo && "Map".equals(qName)) {
334 if ("config".equals(attributes.getValue("name"))) {
335 repoName[0] = attributes.getValue("repositoryName");
336 }
337 }
338 }
339
340 @Override
341 public void endElement(String uri, String localName, String qName) throws SAXException {
342 super.endElement(uri, localName, qName);
343 if ("RepositoryMapping".equals(localName)) {
344 inRepo = false;
345 }
346 }
347 });
348 return repoName[0];
349 } catch (Exception e) {
350 log.debug("Failed to obtain repository configuration info with {}", e.getMessage(), e);
351 }
352 return null;
353 }
354 }