blob: 052586047fccac90f50bac90737fa30d4b0800d0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 2018 Rapicorp, Inc and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Rapicorp, Inc - initial API and implementation
*******************************************************************************/
package org.eclipse.equinox.p2.internal.repository.tools;
import java.io.*;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import org.eclipse.equinox.internal.p2.metadata.repository.Messages;
import org.eclipse.osgi.util.NLS;
import org.tukaani.xz.*;
/**
* A class taking care of creating the XZ'ed version of a repository. It takes
* as input the folder where a p2 repository is stored and produces the new
* files in this folder.
*
* The files expected in to be found as input are content.xml or content.jar, or
* artifacts.xml or artifacts.jar. Note that the tool does not require both the
* a metadata repository and an artifact repository. The output will be
* <fileName>.xml.xz, and a p2.index
*
*/
public class XZCompressor {
private static final String CONTENT_XML_XZ = "content.xml.xz"; //$NON-NLS-1$
private static final String ARTIFACTS_XML_XZ = "artifacts.xml.xz"; //$NON-NLS-1$
private static final String ARTIFACTS2 = "artifacts"; //$NON-NLS-1$
private static final String CONTENT = "content"; //$NON-NLS-1$
private static final String JAR = ".jar"; //$NON-NLS-1$
private static final String XML = ".xml"; //$NON-NLS-1$
private String repoFolder;
private boolean preserveOriginalFile = true;
private ArrayList<File> filesToDelete = new ArrayList<>();
public String getRepoFolder() {
return repoFolder;
}
public void setRepoFolder(String repoFolder) {
this.repoFolder = repoFolder;
}
public boolean isPreserveOriginalFile() {
return preserveOriginalFile;
}
public void setPreserveOriginalFile(boolean preserveOriginalFile) {
this.preserveOriginalFile = preserveOriginalFile;
}
private File uncompressJar(File jarFile, String fileToExtract) throws IOException {
try (JarInputStream jarStream = new JarInputStream(new FileInputStream(jarFile))) {
JarEntry jarEntry = jarStream.getNextJarEntry();
while (jarEntry != null && (!fileToExtract.equals(jarEntry.getName()))) {
jarEntry = jarStream.getNextJarEntry();
}
// if there is a jar but the entry is missing or invalid, treat this as an
// invalid repository
if (jarEntry == null) {
jarStream.close();
throw new IOException(NLS.bind(Messages.repoMan_invalidLocation, jarFile.getAbsolutePath()));
}
File extractedFile = new File(jarFile.getAbsoluteFile().getParent(), fileToExtract);
try (BufferedInputStream input = new BufferedInputStream(jarStream);
OutputStream output = new BufferedOutputStream(new FileOutputStream(extractedFile))) {
byte[] buffer = new byte[8192];
int bytesRead = 0;
while ((bytesRead = input.read(buffer)) != -1)
output.write(buffer, 0, bytesRead);
}
filesToDelete.add(extractedFile);
return extractedFile;
}
}
private File getMetadataFile(String prefix) throws IOException {
File candidate = new File(repoFolder, prefix + XML);
if (candidate.exists()) {
if (!preserveOriginalFile) {
filesToDelete.add(candidate);
}
return candidate;
}
candidate = new File(repoFolder, prefix + JAR);
if (candidate.exists()) {
if (!preserveOriginalFile) {
filesToDelete.add(candidate);
}
return uncompressJar(candidate, prefix + XML);
}
return null;
}
public void compressRepo() throws IOException {
File metadata = getMetadataFile(CONTENT);
if (metadata != null)
compressFile(metadata, new File(repoFolder, CONTENT_XML_XZ));
File artifacts = getMetadataFile(ARTIFACTS2);
if (artifacts != null)
compressFile(artifacts, new File(repoFolder, ARTIFACTS_XML_XZ));
createP2Index(metadata != null, artifacts != null);
deleteFiles();
}
private void deleteFiles() {
for (Iterator<File> iterator = filesToDelete.iterator(); iterator.hasNext();) {
iterator.next().delete();
}
}
private void compressFile(File input, File output) throws IOException {
LZMA2Options options = new LZMA2Options();
try {
options.setDictSize(LZMA2Options.DICT_SIZE_DEFAULT);
options.setLcLp(3, 0);
options.setPb(LZMA2Options.PB_MAX);
options.setMode(LZMA2Options.MODE_NORMAL);
options.setNiceLen(LZMA2Options.NICE_LEN_MAX);
options.setMatchFinder(LZMA2Options.MF_BT4);
options.setDepthLimit(512);
} catch (UnsupportedOptionsException e) {
// Can't happen
}
try (XZOutputStream out = new XZOutputStream(new FileOutputStream(output), options);
FileInputStream is = new FileInputStream(input)) {
byte[] buf = new byte[8192];
int size;
while ((size = is.read(buf)) != -1)
out.write(buf, 0, size);
}
}
private void createP2Index(boolean metadata, boolean artifacts) throws IOException {
Properties p2Index = new Properties();
if (metadata) {
if (preserveOriginalFile)
p2Index.setProperty("metadata.repository.factory.order", "content.xml.xz,content.xml,!"); //$NON-NLS-1$//$NON-NLS-2$
else
p2Index.setProperty("metadata.repository.factory.order", "content.xml.xz,!"); //$NON-NLS-1$//$NON-NLS-2$
}
if (artifacts) {
if (preserveOriginalFile)
p2Index.setProperty("artifact.repository.factory.order", "artifacts.xml.xz,artifacts.xml,!"); //$NON-NLS-1$//$NON-NLS-2$
else
p2Index.setProperty("artifact.repository.factory.order", "artifacts.xml.xz,!"); //$NON-NLS-1$//$NON-NLS-2$
}
p2Index.setProperty("version", "1"); //$NON-NLS-1$//$NON-NLS-2$
try (OutputStream output = new FileOutputStream(new File(repoFolder, "p2.index"));) { //$NON-NLS-1$
// $NON-NLS-1$
p2Index.store(output, null);
}
}
}