WIP
[hibernate4-maven-plugin] / src / main / java / de / juplo / plugins / hibernate / MD5ModificationTracker.java
1 package de.juplo.plugins.hibernate;
2
3
4 import static de.juplo.plugins.hibernate.AbstractSchemaMojo.SCRIPT;
5 import java.io.File;
6 import java.io.FileInputStream;
7 import java.io.FileOutputStream;
8 import java.io.IOException;
9 import java.io.InputStream;
10 import java.io.ObjectInputStream;
11 import java.io.ObjectOutputStream;
12 import java.math.BigInteger;
13 import java.security.MessageDigest;
14 import java.security.NoSuchAlgorithmException;
15 import java.time.ZonedDateTime;
16 import java.util.HashMap;
17 import java.util.HashSet;
18 import java.util.Map;
19 import java.util.Properties;
20 import java.util.Set;
21 import org.apache.maven.plugin.logging.Log;
22
23
24
25 /**
26  *
27  * @author Kai Moritz
28  */
29 public class MD5ModificationTracker implements ModificationTracker
30 {
31   private Map<String,String> properties;
32   private Map<String,String> classes;
33
34   private final Set<String> propertyNames;
35   private final Set<String> classNames;
36
37   private boolean modified = false;
38   private boolean failed = false;
39
40   private final File output;
41   private final File saved;
42   private final MessageDigest digest;
43   private final Log log;
44
45
46   MD5ModificationTracker(String buildDirectory, String filename, Log log)
47       throws
48         NoSuchAlgorithmException
49   {
50     propertyNames = new HashSet<String>();
51     classNames = new HashSet<String>();
52     output = new File(filename);
53     File tmp = new File(filename + ".md5s");
54     if (tmp.isAbsolute())
55     {
56       saved = tmp;
57     }
58     else
59     {
60       // Interpret relative file path relative to build directory
61       saved = new File(buildDirectory, tmp.getPath());
62       log.debug("Adjusted relative path, resulting path is " + saved.getPath());
63     }
64     digest = java.security.MessageDigest.getInstance("MD5");
65     this.log = log;
66   }
67
68
69   private String calculate(InputStream is)
70       throws
71         IOException
72   {
73     byte[] buffer = new byte[1024*4]; // copy data in 4MB-chunks
74     int i;
75     while((i = is.read(buffer)) > -1)
76       digest.update(buffer, 0, i);
77     is.close();
78     byte[] bytes = digest.digest();
79     BigInteger bi = new BigInteger(1, bytes);
80     return String.format("%0" + (bytes.length << 1) + "x", bi);
81   }
82
83   private boolean check(Map<String,String> values, String name, String value)
84   {
85     if (!values.containsKey(name) || !values.get(name).equals(value))
86     {
87       values.put(name, value);
88       return true;
89     }
90     else
91       return false;
92   }
93
94
95   @Override
96   public boolean track(String name, InputStream is) throws IOException
97   {
98     boolean result = check(classes, name, calculate(is));
99     classNames.add(name);
100     modified |= result;
101     return result;
102   }
103
104   @Override
105   public boolean check(String name, String property)
106   {
107     propertyNames.add(name);
108     return check(properties, name, property);
109   }
110
111   @Override
112   public boolean track(String name, String property)
113   {
114     boolean result = check(name, property);
115     modified |= result;
116     return result;
117   }
118
119   @Override
120   public boolean track(Properties properties)
121   {
122     boolean result = false;
123     for (String name : properties.stringPropertyNames())
124       result |= track(name, properties.getProperty(name));
125     return result;
126   }
127
128   @Override
129   public void track() throws IOException
130   {
131     if (output.exists())
132       track(SCRIPT, new FileInputStream(output));
133     else
134       track(SCRIPT, ZonedDateTime.now().toString());
135   }
136
137
138   @Override
139   public void touch()
140   {
141     modified = true;
142   }
143
144   @Override
145   public boolean modified()
146   {
147     for (String property : new HashSet<String>(properties.keySet()))
148       if (!propertyNames.contains(property))
149       {
150         modified = true;
151         properties.remove(property);
152       }
153      for (String clazz : new HashSet<String>(classes.keySet()))
154       if (!classNames.contains(clazz))
155       {
156         modified = true;
157         classes.remove(clazz);
158       }
159     return modified;
160   }
161
162
163   @Override
164   public void failed()
165   {
166     failed = true;
167   }
168
169
170   @Override
171   public void load()
172   {
173     if (saved.isFile() && saved.length() > 0)
174     {
175       try
176       {
177         FileInputStream fis = new FileInputStream(saved);
178         ObjectInputStream ois = new ObjectInputStream(fis);
179         properties = (HashMap<String,String>)ois.readObject();
180         classes = (HashMap<String,String>)ois.readObject();
181         ois.close();
182       }
183       catch (Exception e)
184       {
185         properties = new HashMap<String,String>();
186         classes = new HashMap<String,String>();
187         log.warn("Cannot read md5s from saved: " + e);
188       }
189     }
190     else
191     {
192       properties = new HashMap<String,String>();
193       classes = new HashMap<String,String>();
194       try
195       {
196         saved.createNewFile();
197       }
198       catch (IOException e)
199       {
200         log.debug("Cannot create file \"" + saved.getPath() + "\" for md5s: " + e);
201       }
202     }
203   }
204
205   @Override
206   public void save()
207   {
208     if (failed)
209     {
210       saved.delete();
211       return;
212     }
213
214     if (!modified)
215       return;
216
217     /** Write md5-sums for annotated classes to file */
218     try
219     {
220       FileOutputStream fos = new FileOutputStream(saved);
221       ObjectOutputStream oos = new ObjectOutputStream(fos);
222       oos.writeObject(properties);
223       oos.writeObject(classes);
224       oos.close();
225       fos.close();
226     }
227     catch (Exception e)
228     {
229       log.error("Cannot write md5-sums to file: " + e);
230     }
231   }  
232
233
234   @Override
235   public void turncat() throws IOException
236   {
237     new FileOutputStream(output).getChannel().truncate(0).close();
238   }
239 }