1 /*
2  * Copyright 2008-2019 by Emeric Vernat
3  *
4  *     This file is part of Java Melody.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18 package net.bull.javamelody.internal.model;
19
20 import java.io.File;
21 import java.io.FileInputStream;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.OutputStream;
26 import java.io.Serializable;
27 import java.text.ParseException;
28 import java.text.SimpleDateFormat;
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.Comparator;
32 import java.util.Date;
33 import java.util.HashMap;
34 import java.util.LinkedHashMap;
35 import java.util.List;
36 import java.util.Locale;
37 import java.util.Map;
38 import java.util.Properties;
39
40 import net.bull.javamelody.internal.common.LOG;
41 import net.bull.javamelody.internal.common.Parameters;
42
43 /**
44  * Records the versions of the webapp.
45  * @author Emeric Vernat
46  */

47 class WebappVersions {
48     private static final Comparator<Map.Entry<String, Date>> WEBAPP_VERSIONS_VALUE_COMPARATOR = Collections
49             .reverseOrder(new MapValueComparator<String, Date>());
50     private static final String VERSIONS_FILENAME = "versions.properties";
51     private static final String VERSIONS_DATE_PATTERN = "yyyy/MM/dd";
52
53     /**
54      * Les versions de l'applications avec pour chacune la date de déploiement.
55      */

56     private final Map<String, Date> datesByVersions;
57     private final File versionsFile;
58
59     private static class MapValueComparator<K, V extends Comparable<V>>
60             implements Comparator<Map.Entry<K, V>>, Serializable {
61         private static final long serialVersionUID = 1L;
62
63         MapValueComparator() {
64             super();
65         }
66
67         @Override
68         public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
69             return o1.getValue().compareTo(o2.getValue());
70         }
71     }
72
73     WebappVersions(String application) {
74         super();
75         assert application != null;
76         final File storageDirectory = Parameters.getStorageDirectory(application);
77         this.versionsFile = new File(storageDirectory, VERSIONS_FILENAME);
78         this.datesByVersions = readDatesByVersions();
79     }
80
81     @SuppressWarnings("unchecked")
82     private Map<String, Date> readDatesByVersions() {
83         final Map<String, Date> result = new HashMap<>();
84         if (versionsFile.exists()) {
85             final Properties versionsProperties = new Properties();
86             try {
87                 try (InputStream input = new FileInputStream(versionsFile)) {
88                     versionsProperties.load(input);
89                 }
90                 final List<String> propertyNames = (List<String>) Collections
91                         .list(versionsProperties.propertyNames());
92                 final SimpleDateFormat dateFormat = new SimpleDateFormat(VERSIONS_DATE_PATTERN,
93                         Locale.US);
94                 for (final String version : propertyNames) {
95                     try {
96                         final Date date = dateFormat.parse(versionsProperties.getProperty(version));
97                         result.put(version, date);
98                     } catch (final ParseException e) {
99                         continue;
100                     }
101                 }
102             } catch (final IOException e) {
103                 // lecture échouée, tant pis
104                 LOG.warn("exception while reading versions in " + versionsFile, e);
105             }
106         }
107         return result;
108     }
109
110     Map<String, Date> getDatesByVersions() {
111         final List<Map.Entry<String, Date>> entries = new ArrayList<>(datesByVersions.entrySet());
112         Collections.sort(entries, WEBAPP_VERSIONS_VALUE_COMPARATOR);
113         final Map<String, Date> map = new LinkedHashMap<>();
114         for (final Map.Entry<String, Date> entry : entries) {
115             map.put(entry.getKey(), entry.getValue());
116         }
117         return Collections.unmodifiableMap(map);
118     }
119
120     void addVersionIfNeeded(String webappVersion) throws IOException {
121         if (webappVersion == null || datesByVersions.containsKey(webappVersion)) {
122             return;
123         }
124
125         final Properties versionsProperties = new Properties();
126         if (versionsFile.exists()) {
127             try (InputStream input = new FileInputStream(versionsFile)) {
128                 versionsProperties.load(input);
129             }
130         }
131         assert versionsProperties.getProperty(webappVersion) == null;
132
133         final SimpleDateFormat dateFormat = new SimpleDateFormat(VERSIONS_DATE_PATTERN, Locale.US);
134         versionsProperties.setProperty(webappVersion, dateFormat.format(new Date()));
135
136         final File directory = versionsFile.getParentFile();
137         if (!directory.mkdirs() && !directory.exists()) {
138             throw new IOException("JavaMelody directory can't be created: " + directory.getPath());
139         }
140         try (OutputStream output = new FileOutputStream(versionsFile)) {
141             versionsProperties.store(output, "Application deployments with versions and dates");
142         }
143         datesByVersions.put(webappVersion, new Date());
144         LOG.debug("New application version added: " + webappVersion);
145     }
146 }
147