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.module.files;
35
36 import info.magnolia.jcr.util.NodeTypes;
37 import info.magnolia.jcr.util.NodeUtil;
38 import info.magnolia.jcr.util.PropertyUtil;
39
40 import java.io.File;
41 import java.io.FileInputStream;
42 import java.io.IOException;
43 import java.io.InputStream;
44 import java.io.OutputStream;
45 import java.security.DigestInputStream;
46 import java.security.DigestOutputStream;
47 import java.security.MessageDigest;
48 import java.security.NoSuchAlgorithmException;
49
50 import javax.jcr.Node;
51 import javax.jcr.RepositoryException;
52 import javax.jcr.Session;
53
54 import org.apache.commons.codec.binary.Hex;
55 import org.apache.commons.io.IOUtils;
56 import org.apache.commons.lang3.StringUtils;
57
58
59
60
61
62
63
64
65
66
67 class MD5CheckingFileExtractorOperation extends BasicFileExtractorOperation {
68 private final FileExtractionLogger log;
69 private final Session session;
70
71 MD5CheckingFileExtractorOperation(FileExtractionLogger log, Session session, String resourcePath, String absoluteTargetPath) {
72 super(resourcePath, absoluteTargetPath);
73 this.log = log;
74 this.session = session;
75 }
76
77 @Override
78 protected InputStream checkInput() throws IOException {
79 return wrap(super.checkInput());
80 }
81
82
83 @Override
84 protected File checkOutput() throws IOException {
85 final File out = super.checkOutput();
86
87 if (out.exists()) {
88
89 final InputStream stream = new FileInputStream(out);
90 try {
91 final String existingFileMD5 = calculateMD5(stream);
92 final String repoMD5 = PropertyUtil.getString(getOrCreateNode(), "md5");
93
94
95 if (StringUtils.isNotBlank(repoMD5)) {
96 if (existingFileMD5.equals(repoMD5)) {
97
98 return out;
99 }
100 log.error("Can't extract " + resourcePath + " as this file was probably modified locally: expected MD5 [" + repoMD5 + "] but current MD5 is [" + existingFileMD5 + "].");
101 return null;
102 }
103 } finally {
104 IOUtils.closeQuietly(stream);
105 }
106
107 } else {
108
109 }
110 return out;
111 }
112
113 @Override
114 protected OutputStream openOutput(File outFile) throws IOException {
115 final OutputStream outputStream = super.openOutput(outFile);
116 final MessageDigest md5 = getMessageDigest();
117 return new DigestOutputStream(outputStream, md5);
118 }
119
120 @Override
121 protected void copyAndClose(InputStream in, OutputStream out) throws IOException {
122 super.copyAndClose(in, out);
123
124 final DigestInputStream md5Stream = (DigestInputStream) in;
125 final String newMD5 = retrieveMD5(md5Stream);
126
127 try {
128 getOrCreateNode().setProperty("md5", newMD5);
129 } catch (RepositoryException e) {
130 throw new RuntimeException(e);
131 }
132
133 }
134
135 protected Node getOrCreateNode() {
136 try {
137 final String repoPath = getRepositoryPath(resourcePath);
138 final Node node;
139 if (!session.nodeExists(repoPath)) {
140 node = NodeUtil.createPath(session.getRootNode(), repoPath, NodeTypes.Content.NAME);
141 } else {
142 node = session.getNode(repoPath);
143 }
144 return node;
145 } catch (RepositoryException e) {
146 throw new RuntimeException(e);
147 }
148 }
149
150
151
152
153
154 protected String getRepositoryPath(String resourcePath) {
155 return "/server/install" + resourcePath;
156 }
157
158
159
160
161
162 protected String calculateMD5(InputStream stream) throws IOException {
163 final DigestInputStream md5Stream = wrap(stream);
164 byte[] buffer = new byte[1024];
165 while (md5Stream.read(buffer) != -1) {
166 }
167
168 return retrieveMD5(md5Stream);
169 }
170
171 protected DigestInputStream wrap(InputStream stream) {
172 final MessageDigest md5 = getMessageDigest();
173 return new DigestInputStream(stream, md5);
174 }
175
176 protected String retrieveMD5(DigestInputStream md5Stream) {
177 final byte[] digInBytes = md5Stream.getMessageDigest().digest();
178 return String.valueOf(Hex.encodeHex(digInBytes));
179 }
180
181 protected MessageDigest getMessageDigest() {
182 try {
183 return MessageDigest.getInstance("MD5");
184 } catch (NoSuchAlgorithmException e) {
185 throw new RuntimeException("Can't check files with md5: " + e.getMessage(), e);
186 }
187 }
188 }