1
18 package net.bull.javamelody.internal.model;
19
20 import java.io.BufferedInputStream;
21 import java.io.BufferedOutputStream;
22 import java.io.File;
23 import java.io.FileInputStream;
24 import java.io.FileOutputStream;
25 import java.io.FilenameFilter;
26 import java.io.IOException;
27 import java.io.ObjectInputStream;
28 import java.io.ObjectOutputStream;
29 import java.io.OutputStream;
30 import java.util.Arrays;
31 import java.util.Calendar;
32 import java.util.Collections;
33 import java.util.List;
34 import java.util.zip.GZIPInputStream;
35 import java.util.zip.GZIPOutputStream;
36
37 import net.bull.javamelody.Parameter;
38 import net.bull.javamelody.internal.common.LOG;
39 import net.bull.javamelody.internal.common.Parameters;
40
41
45 public class CounterStorage {
46 private static final int DEFAULT_OBSOLETE_STATS_DAYS = 365;
47 private static boolean storageDisabled;
48 private final Counter counter;
49
50
51 private static class CounterOutputStream extends OutputStream {
52 int dataLength;
53 private final OutputStream output;
54
55 CounterOutputStream(OutputStream output) {
56 super();
57 this.output = output;
58 }
59
60 @Override
61 public void write(int b) throws IOException {
62 output.write(b);
63 dataLength++;
64 }
65
66 @Override
67 public void write(byte[] b) throws IOException {
68 output.write(b);
69 dataLength += b.length;
70 }
71
72 @Override
73 public void write(byte[] b, int off, int len) throws IOException {
74 output.write(b, off, len);
75 dataLength += len;
76 }
77
78 @Override
79 public void flush() throws IOException {
80 output.flush();
81 }
82
83 @Override
84 public void close() throws IOException {
85 output.close();
86 }
87 }
88
89
93 CounterStorage(Counter counter) {
94 super();
95 assert counter != null;
96 this.counter = counter;
97 }
98
99
104 int writeToFile() throws IOException {
105 if (storageDisabled) {
106 return -1;
107 }
108 final File file = getFile();
109 if (counter.getRequestsCount() == 0 && counter.getErrorsCount() == 0 && !file.exists()) {
110
111
112 return -1;
113 }
114 final File directory = file.getParentFile();
115 if (!directory.mkdirs() && !directory.exists()) {
116 throw new IOException("JavaMelody directory can't be created: " + directory.getPath());
117 }
118 return writeToFile(counter, file);
119 }
120
121 static int writeToFile(Counter counter, File file) throws IOException {
122 try (FileOutputStream out = new FileOutputStream(file)) {
123 final CounterOutputStream counterOutput = new CounterOutputStream(
124 new GZIPOutputStream(new BufferedOutputStream(out)));
125 try (ObjectOutputStream output = new ObjectOutputStream(counterOutput)) {
126 output.writeObject(counter);
127
128 }
129
130
131 return counterOutput.dataLength;
132 }
133 }
134
135
140 Counter readFromFile() throws IOException {
141 if (storageDisabled) {
142 return null;
143 }
144 final File file = getFile();
145 if (file.exists()) {
146 return readFromFile(file);
147 }
148
149 return null;
150 }
151
152 static Counter readFromFile(File file) throws IOException {
153 try (FileInputStream in = new FileInputStream(file)) {
154 try (ObjectInputStream input = TransportFormat
155 .createObjectInputStream(new GZIPInputStream(new BufferedInputStream(in)))) {
156
157 return (Counter) input.readObject();
158
159 }
160 } catch (final ClassNotFoundException e) {
161 throw new IOException(e.getMessage(), e);
162 } catch (final IllegalStateException | ClassCastException e) {
163 LOG.warn("could not deserialize " + file.getName()
164 + " , corrupted file will be deleted.", e);
165 file.delete();
166 return null;
167 }
168 }
169
170 private File getFile() {
171 final File storageDirectory = Parameters.getStorageDirectory(counter.getApplication());
172 return new File(storageDirectory, counter.getStorageName() + ".ser.gz");
173 }
174
175 static long deleteObsoleteCounterFiles(String application) {
176 final Calendar nowMinusOneYearAndADay = Calendar.getInstance();
177 nowMinusOneYearAndADay.add(Calendar.DAY_OF_YEAR, -getObsoleteStatsDays());
178 nowMinusOneYearAndADay.add(Calendar.DAY_OF_YEAR, -1);
179
180 long diskUsage = 0;
181 for (final File file : listSerGzFiles(application)) {
182 boolean deleted = false;
183 if (file.lastModified() < nowMinusOneYearAndADay.getTimeInMillis()) {
184 deleted = file.delete();
185 }
186 if (!deleted) {
187 diskUsage += file.length();
188 }
189 }
190
191
192 return diskUsage;
193 }
194
195
199 private static int getObsoleteStatsDays() {
200 final String param = Parameter.OBSOLETE_STATS_DAYS.getValue();
201 if (param != null) {
202
203 final int result = Integer.parseInt(param);
204 if (result <= 0) {
205 throw new IllegalStateException(
206 "The parameter obsolete-stats-days should be > 0 (365 recommended)");
207 }
208 return result;
209 }
210 return DEFAULT_OBSOLETE_STATS_DAYS;
211 }
212
213 private static List<File> listSerGzFiles(String application) {
214 final File storageDir = Parameters.getStorageDirectory(application);
215
216 final FilenameFilter filenameFilter = new FilenameFilter() {
217
218 @Override
219 public boolean accept(File dir, String fileName) {
220 return fileName.endsWith(".ser.gz");
221 }
222 };
223 final File[] files = storageDir.listFiles(filenameFilter);
224 if (files == null) {
225 return Collections.emptyList();
226 }
227 return Arrays.asList(files);
228 }
229
230
231 public static void disableStorage() {
232 storageDisabled = true;
233 }
234 }
235