1   package com.trendmicro.grid.acl.ds.bclog;
2   
3   import com.trendmicro.grid.acl.l0.datatypes.ProcessPackageDataSet;
4   import net.sf.tinyjee.streams.ByteBufferOutputStream;
5   import net.sf.tinyjee.streams.PositionOutputStream;
6   import net.sf.tinyjee.util.FileUtils;
7   
8   import java.io.File;
9   import java.io.IOException;
10  import java.io.OutputStream;
11  import java.io.FilterOutputStream;
12  import java.util.LinkedHashMap;
13  import java.util.List;
14  import java.util.Map;
15  import java.util.jar.JarOutputStream;
16  import java.util.jar.Manifest;
17  import java.util.jar.Attributes;
18  import java.util.zip.CRC32;
19  import java.util.zip.CheckedOutputStream;
20  import java.util.zip.ZipEntry;
21  import java.util.zip.ZipOutputStream;
22  
23  /**
24   * Implements a writer for the binary changelog format.
25   *
26   * @author juergen_kellerer, 2010-05-28
27   * @version 1.0
28   */
29  public class BinaryChangeLogFileWriter {
30  
31  	static final int ENTRIES_PER_SECTION = 1000;
32  
33  	private static final Map<String, byte[]> schemaFilesMap = new LinkedHashMap<String, byte[]>(8);
34  
35  	static {
36  		try {
37  			File tempFolder = File.createTempFile("~process-schema", ".temp");
38  			if (!tempFolder.delete() || !tempFolder.mkdir())
39  				throw new RuntimeException("Failed creating temp folder " + tempFolder);
40  
41  			try {
42  				ByteBufferOutputStream buffer = new ByteBufferOutputStream();
43  				List<File> schemaFiles = ProcessPackageDataSet.getXmlSerializer().generateSchema(tempFolder);
44  				for (File schemaFile : schemaFiles) {
45  					FileUtils.copy(schemaFile, buffer, true);
46  					schemaFilesMap.put(schemaFile.getName(), buffer.toByteArray());
47  					buffer.reset();
48  				}
49  			} finally {
50  				if (FileUtils.removeFiles(tempFolder))
51  					tempFolder.delete();
52  			}
53  
54  
55  		} catch (Exception e) {
56  			throw new RuntimeException(e);
57  		}
58  	}
59  
60  	int entryIndex, sectionIndex;
61  	CRC32 checksum = new CRC32();
62  	ByteBufferOutputStream buffer = new ByteBufferOutputStream();
63  	ZipOutputStream sectionOut, logOut;
64  
65  	public BinaryChangeLogFileWriter(OutputStream binaryLogStream, String...references) throws IOException {
66  		if (references.length > 0) {
67  			StringBuilder refs = new StringBuilder();
68  			for (String reference : references) {
69  				if (refs.length() > 0)
70  					refs.append(",");
71  				refs.append(reference);
72  			}
73  
74  			Manifest mf = new Manifest();
75  			mf.getMainAttributes().put(Attributes.Name.EXTENSION_LIST, refs.toString());
76  			logOut = new JarOutputStream(binaryLogStream, mf);
77  		} else
78  			logOut = new ZipOutputStream(binaryLogStream);
79  	}
80  
81  	public synchronized void flush() throws IOException {
82  		if (sectionOut != null)
83  			sectionOut.flush();
84  		logOut.flush();
85  	}
86  
87  	public synchronized void close() throws IOException {
88  		if (sectionOut != null)
89  			sectionOut.flush();
90  		logOut.closeEntry();
91  		logOut.close();
92  	}
93  
94  	public synchronized void writeNextSection() throws IOException {
95  		if (sectionOut != null) {
96  			sectionOut.close();
97  			logOut.closeEntry();
98  		}
99  
100 		logOut.putNextEntry(new ZipEntry(String.format("section-%08d.zip", ++sectionIndex)));
101 		sectionOut = new ZipOutputStream(new PositionOutputStream(logOut, false));
102 
103 		// Copy the schema files into the section file.
104 		for (Map.Entry<String, byte[]> entry : schemaFilesMap.entrySet()) {
105 			sectionOut.putNextEntry(createStoredEntry(entry.getKey(), 0, entry.getValue()));
106 			sectionOut.write(entry.getValue());
107 			sectionOut.closeEntry();
108 		}
109 
110 		entryIndex = 0;
111 	}
112 
113 	/**
114 	 * Writes the next data set into the section.
115 	 *
116 	 * @param dataSet the dataset to write.
117 	 * @throws IOException in case of the write operation failed.
118 	 */
119 	public synchronized void writeNext(ProcessPackageDataSet dataSet) throws IOException {
120 		if (sectionOut == null || entryIndex >= ENTRIES_PER_SECTION)
121 			writeNextSection();
122 
123 		buffer.reset();
124 		checksum.reset();
125 		try {
126 			CheckedOutputStream cOut = new CheckedOutputStream(buffer, checksum);
127 			ProcessPackageDataSet.getXmlSerializer().save(dataSet, cOut, true, 0);
128 			cOut.close();
129 		} catch (IOException e) {
130 			throw e;
131 		} catch (Exception e) {
132 			throw new IOException(e);
133 		}
134 
135 		entryIndex++;
136 
137 		sectionOut.putNextEntry(createStoredEntry("%08d.xml", entryIndex, checksum.getValue(), buffer.getLength()));
138 		buffer.writeTo(sectionOut);
139 		sectionOut.closeEntry();
140 		buffer.reset();
141 	}
142 
143 	private ZipEntry createStoredEntry(String namePattern, int nameIndex, byte[] data) {
144 		checksum.reset();
145 		checksum.update(data);
146 		return createStoredEntry(namePattern, nameIndex, checksum.getValue(), data.length);
147 	}
148 
149 	private ZipEntry createStoredEntry(String namePattern, int nameIndex, long crc32, long length) {
150 		ZipEntry ze = new ZipEntry(String.format(namePattern, nameIndex));
151 		ze.setMethod(ZipEntry.STORED);
152 		ze.setCrc(crc32);
153 		ze.setSize(length);
154 		ze.setCompressedSize(length);
155 		ze.setTime(System.currentTimeMillis());
156 		return ze;
157 	}
158 }