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