src/share/classes/org/openjdk/jigsaw/ModuleFile.java
Print this page
*** 31,40 ****
--- 31,41 ----
import java.util.*;
import java.util.jar.*;
import java.util.zip.*;
import static org.openjdk.jigsaw.FileConstants.ModuleFile.*;
+ import static org.openjdk.jigsaw.FileConstants.ModuleFile.SectionType.*;
public final class ModuleFile {
/**
* Return the subdir of a section in an extracted module file.
*/
*** 66,76 ****
private File natlibs;
private File natcmds;
private File configs;
private static class CountingInputStream extends FilterInputStream {
! int count;
public CountingInputStream(InputStream stream, int count) {
super(stream);
this.count = count;
}
--- 67,77 ----
private File natlibs;
private File natcmds;
private File configs;
private static class CountingInputStream extends FilterInputStream {
! private int count;
public CountingInputStream(InputStream stream, int count) {
super(stream);
this.count = count;
}
*** 112,122 ****
--- 113,128 ----
long skipped = super.skip(n);
if (n > 0)
count-=skipped;
return skipped;
}
+
+ public void close() throws IOException {
+ // Do nothing, CountingInputStream is used to wrap (sub)section
+ // content. We never want to close the underlying stream.
}
+ }
public Reader(DataInputStream stream) {
hashtype = HashType.SHA256;
// Ensure that mark/reset is supported
if (stream.markSupported()) {
*** 125,144 ****
this.stream =
new DataInputStream(new BufferedInputStream(stream));
}
}
- private void checkHashMatch(byte[] expected, byte[] computed)
- throws IOException
- {
- if (!MessageDigest.isEqual(expected, computed))
- throw new IOException("Expected hash "
- + hashHexString(expected)
- + " instead of "
- + hashHexString(computed));
- }
-
private ModuleFileHeader fileHeader = null;
private MessageDigest fileDigest = null;
private MessageDigest sectionDigest = null;
private DataInputStream fileIn = null;
private byte[] moduleInfoBytes = null;
--- 131,140 ----
*** 145,154 ****
--- 141,151 ----
private Integer moduleSignatureType = null;
private byte[] moduleSignatureBytes = null;
private final int MAX_SECTION_HEADER_LENGTH = 128;
private List<byte[]> calculatedHashes = new ArrayList<>();
private boolean extract = true;
+ private List<String> contents; // list of the module-file contents
/*
* Reads the MODULE_INFO section and the Signature section, if present,
* but does not write any files.
*/
*** 165,175 ****
fileHeader.write(new DataOutputStream(baos));
sectionDigest.update(baos.toByteArray());
calculatedHashes.add(sectionDigest.digest());
fileIn = new DataInputStream(dis);
! if (readSection(fileIn) != SectionType.MODULE_INFO)
throw new IOException("First module-file section"
+ " is not MODULE_INFO");
assert moduleInfoBytes != null;
// Read the Signature Section, if present
--- 162,172 ----
fileHeader.write(new DataOutputStream(baos));
sectionDigest.update(baos.toByteArray());
calculatedHashes.add(sectionDigest.digest());
fileIn = new DataInputStream(dis);
! if (readSection(fileIn) != MODULE_INFO)
throw new IOException("First module-file section"
+ " is not MODULE_INFO");
assert moduleInfoBytes != null;
// Read the Signature Section, if present
*** 198,215 ****
this.deflate = deflate;
this.destination = dst != null ? dst.getCanonicalFile() : null;
this.natlibs = natlibs != null ? natlibs : new File(destination, "lib");
this.natcmds = natcmds != null ? natcmds : new File(destination, "bin");
this.configs = configs != null ? configs : new File(destination, "etc");
try {
if (extract)
Files.store(moduleInfoBytes, computeRealPath("info"));
// Module-Info and Signature, if present, have been consumed
// Read rest of file until all sections have been read
stream.mark(1);
! while (-1 != stream.read()) {
stream.reset();
readSection(fileIn);
stream.mark(1);
}
--- 195,215 ----
this.deflate = deflate;
this.destination = dst != null ? dst.getCanonicalFile() : null;
this.natlibs = natlibs != null ? natlibs : new File(destination, "lib");
this.natcmds = natcmds != null ? natcmds : new File(destination, "bin");
this.configs = configs != null ? configs : new File(destination, "etc");
+ contents = new ArrayList<>();
+
try {
if (extract)
Files.store(moduleInfoBytes, computeRealPath("info"));
+ contents.add("module-info.class");
// Module-Info and Signature, if present, have been consumed
// Read rest of file until all sections have been read
stream.mark(1);
! while (stream.read() != -1) {
stream.reset();
readSection(fileIn);
stream.mark(1);
}
*** 254,278 ****
byte[] getSignatureNoClone() {
return moduleSignatureBytes;
}
! private JarOutputStream contentStream = null;
!
! private JarOutputStream contentStream() throws IOException {
! if (contentStream == null) {
! if (extract) {
! FileOutputStream fos
! = new FileOutputStream(computeRealPath("classes"));
! contentStream
! = new JarOutputStream(new BufferedOutputStream(fos));
! } else {
! contentStream = new JarOutputStream(new NullOutputStream());
}
- }
- return contentStream;
- }
public void close() throws IOException {
try {
try {
if (contentStream != null) {
--- 254,269 ----
byte[] getSignatureNoClone() {
return moduleSignatureBytes;
}
! public List<String> getContents() throws IOException {
! if (contents == null)
! readModule();
! Collections.sort(contents);
! return contents;
}
public void close() throws IOException {
try {
try {
if (contentStream != null) {
*** 306,332 ****
private void readSignatureSection(DataInputStream stream,
DigestInputStream dis)
throws IOException
{
-
// Turn off digest computation before reading Signature Section
dis.on(false);
// Mark the starting position
stream.mark(MAX_SECTION_HEADER_LENGTH);
if (stream.read() != -1) {
stream.reset();
SectionHeader header = SectionHeader.read(stream);
! if (header != null &&
! header.getType() == SectionType.SIGNATURE) {
readSectionContent(header, stream);
! } else {
! // Revert back to the starting position
! stream.reset();
}
- }
// Turn on digest computation again
dis.on(true);
}
--- 297,319 ----
private void readSignatureSection(DataInputStream stream,
DigestInputStream dis)
throws IOException
{
// Turn off digest computation before reading Signature Section
dis.on(false);
// Mark the starting position
stream.mark(MAX_SECTION_HEADER_LENGTH);
if (stream.read() != -1) {
stream.reset();
SectionHeader header = SectionHeader.read(stream);
! if (header != null && header.getType() == SIGNATURE)
readSectionContent(header, stream);
! else
! stream.reset(); // No signature, reset back to start
}
// Turn on digest computation again
dis.on(true);
}
*** 343,422 ****
throws IOException
{
SectionType type = header.getType();
Compressor compressor = header.getCompressor();
int csize = header.getCSize();
- short subsections =
- type.hasFiles() ? header.getSubsections() : 1;
CountingInputStream cs = new CountingInputStream(stream, csize);
sectionDigest.reset();
DigestInputStream dis = new DigestInputStream(cs, sectionDigest);
DataInputStream in = new DataInputStream(dis);
for (int subsection = 0; subsection < subsections; subsection++)
! readFile(in, compressor, type, csize);
byte[] headerHash = header.getHashNoClone();
checkHashMatch(headerHash, sectionDigest.digest());
! if (header.getType() != SectionType.SIGNATURE) {
calculatedHashes.add(headerHash);
}
}
! public void readFile(DataInputStream in,
! Compressor compressor,
! SectionType type,
! int csize)
throws IOException
{
switch (compressor) {
case NONE:
! if (type == SectionType.MODULE_INFO) {
! moduleInfoBytes = readModuleInfo(in, csize);
! } else if (type == SectionType.SIGNATURE) {
! // Examine the Signature header
! moduleSignatureType = (int)in.readShort();
! int length = in.readInt();
! moduleSignatureBytes = readModuleSignature(in, csize - 6);
! if (length != moduleSignatureBytes.length) {
! throw new IOException("Invalid Signature length");
}
- } else {
- readUncompressedFile(in, type, csize);
}
! break;
! case GZIP:
! readGZIPCompressedFile(in, type);
! break;
case PACK200_GZIP:
! readClasses(
! new DataInputStream(new CountingInputStream(in, csize)));
! break;
default:
! throw new IOException("Unsupported Compressor for files: " +
! compressor);
}
}
! public void readClasses(DataInputStream in) throws IOException {
! unpack200gzip(in);
}
private File currentPath = null;
! private OutputStream openOutputStream(SectionType type,
! String path)
throws IOException
{
if (!extract)
return new NullOutputStream();
currentPath = null;
! assert type != SectionType.CLASSES;
! if (type == SectionType.RESOURCES)
! return Files.newOutputStream(contentStream(), path);
currentPath = computeRealPath(type, path);
File parent = currentPath.getParentFile();
if (!parent.exists())
Files.mkdirs(parent, currentPath.getName());
return new BufferedOutputStream(new FileOutputStream(currentPath));
--- 330,540 ----
throws IOException
{
SectionType type = header.getType();
Compressor compressor = header.getCompressor();
int csize = header.getCSize();
CountingInputStream cs = new CountingInputStream(stream, csize);
sectionDigest.reset();
DigestInputStream dis = new DigestInputStream(cs, sectionDigest);
DataInputStream in = new DataInputStream(dis);
+ if (type.hasFiles()) {
+ short subsections = header.getSubsections();
for (int subsection = 0; subsection < subsections; subsection++)
! readSubSection(in, compressor, type);
! } else if (type == CLASSES) {
! readClassesContent(in, compressor);
! } else {
! readSectionBytes(in, compressor, type);
! }
+ // ## appropriate exception ??
+ if (cs.read() != -1)
+ throw new IllegalArgumentException("All section content not read");
+
byte[] headerHash = header.getHashNoClone();
checkHashMatch(headerHash, sectionDigest.digest());
! if (header.getType() != SIGNATURE) {
calculatedHashes.add(headerHash);
}
}
! // module-info OR module signature
! public void readSectionBytes(DataInputStream in, Compressor compressor,
! SectionType type)
throws IOException
{
+ Decompressor decompressor = Decompressor.newInstance(in, compressor);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ decompressor.extractTo(baos);
+
+ if (type == MODULE_INFO)
+ moduleInfoBytes = baos.toByteArray();
+ else if (type == SIGNATURE) {
+ SignatureSection sh = SignatureSection.read(new DataInputStream(
+ new ByteArrayInputStream(baos.toByteArray())));
+ moduleSignatureType = sh.getSignatureType();
+ moduleSignatureBytes = sh.getSignature();
+ } else
+ throw new IllegalArgumentException(
+ "Unsupported raw bytes section type: " + type);
+ }
+
+ public void readClassesContent(DataInputStream in, Compressor compressor)
+ throws IOException
+ {
+ ClassesDecompressor decompressor =
+ ClassesDecompressor.newInstance(in, compressor, deflate);
+ decompressor.extractTo(contentStream());
+ }
+
+ // subsections/files (resources, libs, cmds, configs)
+ public void readSubSection(DataInputStream in, Compressor compressor,
+ SectionType type)
+ throws IOException
+ {
+ assert type == RESOURCES || type == NATIVE_LIBS ||
+ type == NATIVE_CMDS || type == CONFIG;
+
+ SubSectionFileHeader header = SubSectionFileHeader.read(in);
+ CountingInputStream cs = new CountingInputStream(in, header.getCSize());
+ Decompressor decompressor = Decompressor.newInstance(cs, compressor);
+ String path = header.getPath();
+ try (OutputStream sink = openOutputStream(type, path)) {
+ decompressor.extractTo(sink);
+ }
+
+ String prefix = type == RESOURCES ? "" :
+ getSubdirOfSection(type) + File.separator;
+ contents.add(prefix + path);
+ // post processing for executable and files outside the module dir
+ if (extract)
+ postExtract(type, currentPath);
+ }
+
+ static class Decompressor {
+ protected InputStream source;
+ protected Decompressor() { }
+ protected Decompressor(InputStream source) {
+ // no decompression
+ this.source = source;
+ }
+
+ void extractTo(OutputStream sink) throws IOException {
+ copyStream(source, sink);
+ }
+
+ static Decompressor newInstance(InputStream source,
+ Compressor compressor)
+ throws IOException
+ {
switch (compressor) {
case NONE:
! return new Decompressor(source);
! case GZIP:
! return new GZIPDecompressor(source);
! default:
! throw new IllegalArgumentException(
! "Unsupported compressor type: " + compressor);
! }
! }
! }
! static class GZIPDecompressor extends Decompressor {
! GZIPDecompressor(InputStream source) throws IOException {
! this.source = new GZIPInputStream(source) {
! public void close() throws IOException {}
! };
}
}
!
! static abstract class ClassesDecompressor {
! protected InputStream source;
!
! abstract void extractTo(JarOutputStream sink) throws IOException;
!
! static ClassesDecompressor newInstance(InputStream source,
! Compressor compressor,
! boolean deflate)
! throws IOException
! {
! switch (compressor) {
case PACK200_GZIP:
! return new Pack200GZIPDecompressor(source, deflate);
default:
! throw new IllegalArgumentException(
! "Unsupported compressor type: " + compressor);
}
}
+ }
! static class Pack200GZIPDecompressor extends ClassesDecompressor {
! private Pack200.Unpacker unpacker;
!
! Pack200GZIPDecompressor(InputStream source, boolean deflate)
! throws IOException
! {
! this.source = new GZIPInputStream(source) {
! public void close() throws IOException {}
! };
! unpacker = Pack200.newUnpacker();
! if (deflate) {
! Map<String,String> p = unpacker.properties();
! p.put(Pack200.Unpacker.DEFLATE_HINT, Pack200.Unpacker.TRUE);
}
+ }
+ void extractTo(JarOutputStream sink) throws IOException {
+ unpacker.unpack(source, sink);
+ }
+ }
+
+ private TrackingJarOutputStream contentStream = null;
+
+ // Used to retrieved archive contents
+ private static class TrackingJarOutputStream extends JarOutputStream {
+ private final List<String> contents;
+ TrackingJarOutputStream(OutputStream out, List<String> contents)
+ throws IOException
+ {
+ super(out);
+ this.contents = contents;
+ }
+
+ public void putNextEntry(ZipEntry ze) throws IOException {
+ super.putNextEntry(ze);
+ contents.add(ze.getName());
+ }
+ }
+
+ private JarOutputStream contentStream() throws IOException {
+ if (contentStream != null)
+ return contentStream;
+
+ OutputStream sink;
+ if (extract)
+ sink = new BufferedOutputStream(
+ new FileOutputStream(computeRealPath("classes")));
+ else
+ sink = new NullOutputStream();
+
+ return contentStream = new TrackingJarOutputStream(sink, contents);
+ }
+
private File currentPath = null;
! private OutputStream openOutputStream(SectionType type, String path)
throws IOException
{
if (!extract)
return new NullOutputStream();
+
currentPath = null;
! assert type != CLASSES;
! if (type == RESOURCES)
! return Files.newOutputStream(contentStream(), deflate, path);
currentPath = computeRealPath(type, path);
File parent = currentPath.getParentFile();
if (!parent.exists())
Files.mkdirs(parent, currentPath.getName());
return new BufferedOutputStream(new FileOutputStream(currentPath));
*** 429,504 ****
public void write(byte[] b) throws IOException {}
@Override
public void write(byte[] b, int off, int len) throws IOException {}
}
! public void readGZIPCompressedFile(DataInputStream in,
! SectionType type)
throws IOException
{
! SubSectionFileHeader header = SubSectionFileHeader.read(in);
! int csize = header.getCSize();
!
! // Splice off the compressed file from input stream
! ByteArrayOutputStream baos = new ByteArrayOutputStream();
! copyStream(new CountingInputStream(in, csize), baos, csize);
!
! byte[] compressedfile = baos.toByteArray();
! ByteArrayInputStream bain
! = new ByteArrayInputStream(compressedfile);
! try (GZIPInputStream gin = new GZIPInputStream(bain);
! OutputStream out = openOutputStream(type, header.getPath())) {
! copyStream(gin, out);
}
- if (extract)
- postExtract(type, currentPath);
- }
-
- public void readUncompressedFile(DataInputStream in,
- SectionType type,
- int csize)
- throws IOException
- {
- assert type != SectionType.MODULE_INFO;
- SubSectionFileHeader header = SubSectionFileHeader.read(in);
- csize = header.getCSize();
- try (OutputStream out = openOutputStream(type, header.getPath())) {
- CountingInputStream cin = new CountingInputStream(in, csize);
- byte[] buf = new byte[8192];
- int n;
- while ((n = cin.read(buf)) >= 0)
- out.write(buf, 0, n);
- }
- if (extract) {
- postExtract(type, currentPath);
- }
- }
-
- public byte[] readModuleInfo(DataInputStream in, int csize)
- throws IOException
- {
- CountingInputStream cin = new CountingInputStream(in, csize);
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- byte[] buf = new byte[8192];
- int n;
- while ((n = cin.read(buf)) >= 0)
- out.write(buf, 0, n);
- return out.toByteArray();
- }
-
- public byte[] readModuleSignature(DataInputStream in, int csize)
- throws IOException
- {
- return readModuleInfo(in, csize); // signature has the same format
- }
-
// Track files installed outside the module library. For later removal.
// files are relative to the modules directory.
private PrintWriter filesWriter;
! private void trackFiles(SectionType type, File file)
throws IOException
{
if (file == null || file.toPath().startsWith(destination.toPath()))
return;
--- 547,571 ----
public void write(byte[] b) throws IOException {}
@Override
public void write(byte[] b, int off, int len) throws IOException {}
}
! private static void checkHashMatch(byte[] expected, byte[] computed)
throws IOException
{
! if (!MessageDigest.isEqual(expected, computed))
! throw new IOException("Expected hash "
! + hashHexString(expected)
! + " instead of "
! + hashHexString(computed));
}
// Track files installed outside the module library. For later removal.
// files are relative to the modules directory.
private PrintWriter filesWriter;
! private void trackFiles(File file)
throws IOException
{
if (file == null || file.toPath().startsWith(destination.toPath()))
return;
*** 541,555 ****
return excs;
}
// Returns the absolute path of the given section type.
private File getDirOfSection(SectionType type) {
! if (type == SectionType.NATIVE_LIBS)
return natlibs;
! else if (type == SectionType.NATIVE_CMDS)
return natcmds;
! else if (type == SectionType.CONFIG)
return configs;
// resolve sub dir section paths against the modules directory
return new File(destination, ModuleFile.getSubdirOfSection(type));
}
--- 608,622 ----
return excs;
}
// Returns the absolute path of the given section type.
private File getDirOfSection(SectionType type) {
! if (type == NATIVE_LIBS)
return natlibs;
! else if (type == NATIVE_CMDS)
return natcmds;
! else if (type == CONFIG)
return configs;
// resolve sub dir section paths against the modules directory
return new File(destination, ModuleFile.getSubdirOfSection(type));
}
*** 576,621 ****
}
private static void markNativeCodeExecutable(SectionType type,
File file)
{
! if (type == SectionType.NATIVE_CMDS
! || (type == SectionType.NATIVE_LIBS
! && System.getProperty("os.name").startsWith("Windows")))
! {
file.setExecutable(true);
}
- }
private void postExtract(SectionType type, File path)
throws IOException
{
markNativeCodeExecutable(type, path);
! trackFiles(type, path);
}
-
- private void unpack200gzip(DataInputStream in) throws IOException {
- GZIPInputStream gis = new GZIPInputStream(in) {
- public void close() throws IOException {}
- };
- Pack200.Unpacker unpacker = Pack200.newUnpacker();
- if (deflate) {
- Map<String,String> p = unpacker.properties();
- p.put(Pack200.Unpacker.DEFLATE_HINT, Pack200.Unpacker.TRUE);
}
- unpacker.unpack(gis, contentStream());
- }
- }
-
private static void checkCompressor(SectionType type,
Compressor compressor) {
! if ((SectionType.MODULE_INFO == type &&
! Compressor.NONE != compressor)
! || (SectionType.CLASSES == type &&
! Compressor.PACK200_GZIP != compressor))
throw new IllegalArgumentException(type
+ " may not use compressor "
+ compressor);
}
--- 643,670 ----
}
private static void markNativeCodeExecutable(SectionType type,
File file)
{
! if (type == NATIVE_CMDS || (type == NATIVE_LIBS &&
! System.getProperty("os.name").startsWith("Windows")))
file.setExecutable(true);
}
private void postExtract(SectionType type, File path)
throws IOException
{
markNativeCodeExecutable(type, path);
! trackFiles(path);
}
}
private static void checkCompressor(SectionType type,
Compressor compressor) {
! if ((MODULE_INFO == type && Compressor.NONE != compressor) ||
! (CLASSES == type && Compressor.PACK200_GZIP != compressor))
throw new IllegalArgumentException(type
+ " may not use compressor "
+ compressor);
}
*** 627,675 ****
+ subsections);
else if (type.hasFiles() && subsections == 0)
throw new IllegalArgumentException(type + " subsection count is 0");
}
! private static void copyStream(InputStream in, DataOutput out)
throws IOException
{
!
! byte[] buffer = new byte[1024 * 8];
! for (int b_read = in.read(buffer);
! -1 != b_read;
! b_read = in.read(buffer))
! out.write(buffer, 0, b_read);
}
! private static void copyStream(InputStream in, OutputStream out)
throws IOException
{
! copyStream(in, (DataOutput) new DataOutputStream(out));
}
- private static void copyStream(InputStream in, DataOutput out,
- int count)
- throws IOException
- {
- byte[] buffer = new byte[1024 * 8];
-
- while(count > 0) {
- int b_read = in.read(buffer, 0, Math.min(count, buffer.length));
- if (-1 == b_read)
- return;
- out.write(buffer, 0, b_read);
- count-=b_read;
- }
- }
-
- private static void copyStream(InputStream in, OutputStream out,
- int count)
- throws IOException
- {
- copyStream(in, (DataOutput) new DataOutputStream(out), count);
- }
-
private static void ensureNonNegativity(long size, String parameter) {
if (size < 0)
throw new IllegalArgumentException(parameter + "<0: " + size);
}
--- 676,700 ----
+ subsections);
else if (type.hasFiles() && subsections == 0)
throw new IllegalArgumentException(type + " subsection count is 0");
}
! private static void copyStream(InputStream source, DataOutput sink)
throws IOException
{
! byte[] buf = new byte[8192];
! int b_read = 0;
! while((b_read = source.read(buf)) > 0)
! sink.write(buf, 0, b_read);
}
! private static void copyStream(InputStream source, OutputStream sink)
throws IOException
{
! copyStream(source, (DataOutput) new DataOutputStream(sink));
}
private static void ensureNonNegativity(long size, String parameter) {
if (size < 0)
throw new IllegalArgumentException(parameter + "<0: " + size);
}
*** 784,794 ****
}
private static byte[] readHashBytes(DataInputStream in, short hashLength)
throws IOException
{
-
final byte[] hash = new byte[hashLength];
in.readFully(hash);
return hash;
}
--- 809,818 ----
*** 798,808 ****
}
private static byte[] readFileHash(DigestInputStream dis)
throws IOException
{
-
DataInputStream in = new DataInputStream(dis);
final short hashLength = readHashLength(in);
// Turn digest computation off before reading the file hash
--- 822,831 ----
*** 1052,1061 ****
--- 1075,1130 ----
final String path = in.readUTF();
return new SubSectionFileHeader(csize, path);
}
}
+
+ public final static class SignatureSection {
+ private final int signatureType; // One of FileConstants.ModuleFile.HashType
+ private final int signatureLength; // Length of signature
+ private final byte[] signature; // Signature bytes
+
+ public int getSignatureType() {
+ return signatureType;
+ }
+
+ public int getSignatureLength() {
+ return signatureLength;
+ }
+
+ public byte[] getSignature() {
+ return signature;
+ }
+
+ public SignatureSection(int signatureType, int signatureLength,
+ byte[] signature) {
+ ensureNonNegativity(signatureLength, "signatureLength");
+
+ this.signatureType = signatureType;
+ this.signatureLength = signatureLength;
+ this.signature = signature.clone();
+ }
+
+ public void write(DataOutput out) throws IOException {
+ out.writeShort(signatureType);
+ out.writeInt(signatureLength);
+ out.write(signature);
+ }
+
+ public static SignatureSection read(DataInputStream in)
+ throws IOException
+ {
+ final short signatureType = in.readShort();
+ ensureMatch(signatureType, SignatureType.PKCS7.value(), "SignatureType.PKCS7");
+ final int signatureLength = in.readInt();
+ ensureNonNegativity(signatureLength, "signatureLength");
+ final byte[] signature = new byte[signatureLength];
+ in.readFully(signature);
+ return new SignatureSection(signatureType, signatureLength,
+ signature);
+ }
+ }
private static void writeHash(DataOutput out, byte[] hash)
throws IOException
{
out.writeShort(hash.length);