source: contrib/MailArchiver/sources/src/serpro/mailarchiver/service/web/DefaultZipMessagesOperation.java @ 6785

Revision 6785, 12.2 KB checked in by rafaelraymundo, 12 years ago (diff)

Ticket #2946 - Liberado codigo do MailArchiver?. Documentação na subpasta DOCS.

Line 
1/**
2 * MailArchiver is an application that provides services for storing and managing e-mail messages through a Web Services SOAP interface.
3 * Copyright (C) 2012  Marcio Andre Scholl Levien and Fernando Alberto Reuter Wendt and Jose Ronaldo Nogueira Fonseca Junior
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19/******************************************************************************\
20*
21*  This product was developed by
22*
23*        SERVIÇO FEDERAL DE PROCESSAMENTO DE DADOS (SERPRO),
24*
25*  a government company established under Brazilian law (5.615/70),
26*  at Department of Development of Porto Alegre.
27*
28\******************************************************************************/
29
30package serpro.mailarchiver.service.web;
31
32import java.io.IOException;
33import java.io.StringReader;
34import java.io.UnsupportedEncodingException;
35import java.nio.file.Files;
36import java.security.MessageDigest;
37import java.security.NoSuchAlgorithmException;
38import java.util.ArrayList;
39import java.util.Collections;
40import java.util.Comparator;
41import java.util.List;
42
43import javax.jdo.annotations.PersistenceAware;
44import javax.xml.stream.XMLInputFactory;
45import javax.xml.stream.XMLStreamException;
46
47import org.codehaus.jettison.mapped.Configuration;
48import org.codehaus.jettison.mapped.MappedXMLInputFactory;
49import org.codehaus.staxmate.SMInputFactory;
50import org.codehaus.staxmate.in.SMHierarchicCursor;
51import org.codehaus.staxmate.in.SMInputCursor;
52
53import org.springframework.beans.factory.annotation.Autowired;
54
55import com.ctc.wstx.stax.WstxInputFactory;
56
57import de.schlichtherle.truezip.file.TFile;
58import de.schlichtherle.truezip.nio.file.TPath;
59import de.schlichtherle.truezip.fs.FsSyncException;
60
61import serpro.mailarchiver.domain.metaarchive.Folder;
62import serpro.mailarchiver.domain.metaarchive.Message;
63import serpro.mailarchiver.service.BaseService;
64import serpro.mailarchiver.service.find.FFolder;
65import serpro.mailarchiver.service.find.FMessage;
66import serpro.mailarchiver.util.Logger;
67import serpro.mailarchiver.util.UserAppConfig;
68import serpro.mailarchiver.util.transaction.WithReadOnlyTx;
69
70@PersistenceAware
71public class DefaultZipMessagesOperation
72    extends BaseService
73    implements ZipMessagesOperation
74{
75    private static final Logger log = Logger.getLocalLogger();
76
77    @Autowired
78    private FMessage findMessage;
79
80    @Autowired
81    private FFolder findFolder;
82
83    @Autowired
84    private UserAppConfig userAppConfig;
85
86    @WithReadOnlyTx
87    @Override
88    public String apply(String zipConfig) throws ServiceFault {
89
90        /*
91         * zipConfig:
92         *
93         *  <zip format="zip|jar|tar|tar.gz|tgz|tar.bz2|tb2|tbz">
94         *
95         *      <message id="id"/>
96         *
97         *      <folder id="id"/>
98         *      <folder id="id" recursive="true|false"/>
99         *
100         *  </zip>
101         */
102
103        if(zipConfig.isEmpty()) {
104            ServiceFault.invalidZipConfig()
105                    .setActor("zipMessages")
106                    .setMessage("Zip config is null or empty.")
107                    .raise();
108        }
109
110        XMLInputFactory inf = null;
111        switch(zipConfig.charAt(0)) {
112            case '<':
113                inf = new WstxInputFactory();
114                break;
115
116            case '{':
117                Configuration config = new Configuration();
118                config.setIgnoreNamespaces(true);
119                inf = new MappedXMLInputFactory(config);
120                break;
121
122            default:
123                ServiceFault.invalidZipConfig()
124                        .setActor("zipMessages")
125                        .setMessage("Invalid zip config.")
126                        .raise();
127        }
128
129        String guid = null;
130
131        SMInputFactory sminf = new SMInputFactory(inf);
132        StringReader strReader = new StringReader(zipConfig);
133
134        try {
135            SMHierarchicCursor cursor0 = sminf.rootElementCursor(strReader);
136            cursor0.advance();
137            String localName0 = cursor0.getLocalName();
138            if(localName0.equalsIgnoreCase("zip")) {
139                String format = "zip";
140                for(int i = 0; i < cursor0.getAttrCount(); i++) {
141                    String attrLocalName0 = cursor0.getAttrLocalName(i);
142                    if(attrLocalName0.equalsIgnoreCase("format")) {
143                        format = cursor0.getAttrValue(i);
144                    }
145                    else {
146                        //ignore ?
147                    }
148                }
149
150                if((!format.equalsIgnoreCase("zip"))
151                && (!format.equalsIgnoreCase("jar"))
152                && (!format.equalsIgnoreCase("tar"))
153                && (!format.equalsIgnoreCase("tar.gz"))
154                && (!format.equalsIgnoreCase("tgz"))
155                && (!format.equalsIgnoreCase("tar.bz2"))
156                && (!format.equalsIgnoreCase("tb2"))
157                && (!format.equalsIgnoreCase("tbz"))) {
158
159                    ServiceFault.unsupportedArchiveFormat()
160                            .setActor("zipMessages")
161                            .setMessage("Unsupported archive format.")
162                            .addValue("format", format)
163                            .raise();
164                }
165
166                List<Message> messages = new ArrayList<Message>();
167
168                SMInputCursor cursor1 = cursor0.childElementCursor();
169                while(cursor1.getNext() != null) {
170                    String localName1 = cursor1.getLocalName();
171                    if(localName1.equalsIgnoreCase("message")) {
172                        String messageId = "";
173                        for(int i = 0; i < cursor1.getAttrCount(); i++) {
174                            String attrLocalName1 = cursor1.getAttrLocalName(i);
175                            if(attrLocalName1.equalsIgnoreCase("id")) {
176                                messageId = cursor1.getAttrValue(i);
177                            }
178                            else {
179                                //ignore ?
180                            }
181                        }
182                        addMessage(messages, messageId);
183                    }
184                    else if(localName1.equalsIgnoreCase("folder")) {
185                        String folderId = "";
186                        boolean recursive = false;
187                        for(int i = 0; i < cursor1.getAttrCount(); i++) {
188                            String attrLocalName1 = cursor1.getAttrLocalName(i);
189                            if(attrLocalName1.equalsIgnoreCase("id")) {
190                                folderId = cursor1.getAttrValue(i);
191                            }
192                            else if(attrLocalName1.equalsIgnoreCase("recursive")) {
193                                recursive = cursor1.getAttrBooleanValue(i);
194                            }
195                            else {
196                                //ignore ?
197                            }
198                        }
199                        addFolder(messages, folderId, recursive);
200                    }
201                    else {
202                        //ignore ?
203                    }
204                }
205
206                Collections.sort(messages, new Comparator<Message>() {
207                    @Override
208                    public int compare(Message m1, Message m2) {
209                        return m1.getRelativePath().compareTo(m2.getRelativePath());
210                    }
211                });
212
213                try {
214                    MessageDigest md = MessageDigest.getInstance("MD5");
215
216                    md.update(format.getBytes("UTF-8"));
217
218                    for(Message message : messages) {
219                        md.update(message.getOid().getBytes("UTF-8"));
220                    }
221
222                    char[] hexDigest = new char[32];
223                    int i = 0;
224                    for(byte b : md.digest()) {
225                        hexDigest[i++] = Character.forDigit((char)((b & 0xf0) >>> 4), 16);
226                        hexDigest[i++] = Character.forDigit((char)(b & 0x0f), 16);
227                    }
228
229                    String digest = new String(hexDigest);
230
231                    guid = digest.substring(0, 8) +
232                        "-" + digest.substring(8, 12) +
233                        "-" + digest.substring(12, 16) +
234                        "-" + digest.substring(16, 20) +
235                        "-" + digest.substring(20, 32);
236                }
237                catch(NoSuchAlgorithmException ex) {
238                }
239                catch(UnsupportedEncodingException ex) {
240                }
241
242                TPath zip = new TPath(userAppConfig.SERVER.getArchiveDir().resolve("temp").resolve("mails_" + guid + "." + format));
243                if(Files.notExists(zip)) {
244                    try {
245                        for(Message message : messages) {
246                            TFile.cp(message.getAbsolutePath().toFile(), zip.resolve(message.getRelativePath()).toFile());
247                        }
248                    }
249                    catch(IOException ex) {
250                        ServiceFault.fileSystemFailure()
251                                .setActor("zipMessages")
252                                .setMessage("Filesystem archive message failure.")
253                                .setCause(ex)
254                                .raise();
255                    }
256                    finally {
257                        try {
258                            TFile.umount(zip.toFile(), true);
259                        }
260                        catch(FsSyncException ex) {
261                            ServiceFault.fileSystemFailure()
262                                    .setActor("zipMessages")
263                                    .setMessage("Archive umount failure.")
264                                    .setCause(ex)
265                                    .raise();
266                        }
267                    }
268                }
269            }
270            else {
271                //ignore ?
272            }
273        }
274        catch(XMLStreamException ex) {
275            ServiceFault.runtimeException()
276                    .setActor("zipMessages")
277                    .setMessage("Zip config parse exception.")
278                    .setCause(ex)
279                    .raise();
280        }
281
282        return guid;
283    }
284
285    private void addMessage(List<Message> messages, String messageId) throws ServiceFault {
286
287        if(messageId.isEmpty()) {
288            ServiceFault.invalidMessageId()
289                    .setActor("zipMessages")
290                    .setMessage("Message id is empty or null.")
291                    .raise();
292        }
293
294        Message message = findMessage.byId(messageId);
295
296        if(message == null) {
297            ServiceFault.messageNotFound()
298                    .setActor("zipMessages")
299                    .setMessage("Message not found.")
300                    .addValue("messageId", messageId)
301                    .raise();
302        }
303
304        if( ! messages.contains(message)) {
305            messages.add(message);
306        }
307    }
308
309    private void addFolder(List<Message> messages, String folderId, boolean recursive) throws ServiceFault {
310
311        Folder folder = findFolder.byId(folderId);
312
313        if(folder == null) {
314            ServiceFault.folderNotFound()
315                    .setActor("zipMessages")
316                    .setMessage("Folder not found.")
317                    .addValue("folderId", folderId)
318                    .raise();
319        }
320
321        addFolder(messages, folder, recursive);
322    }
323
324    private void addFolder(List<Message> messages, Folder folder, boolean recursive) {
325
326        for(Message message : folder.getMessages()) {
327            if( ! messages.contains(message)) {
328                messages.add(message);
329            }
330        }
331
332        if(recursive) {
333            for(Folder child : folder.getChildren()) {
334                addFolder(messages, child, recursive);
335            }
336        }
337    }
338
339}
Note: See TracBrowser for help on using the repository browser.