1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.dexgen.util; 18 19import java.io.BufferedInputStream; 20import java.io.BufferedOutputStream; 21import java.io.File; 22import java.io.FileInputStream; 23import java.io.FileOutputStream; 24import java.io.IOException; 25import java.util.jar.Attributes; 26import java.util.jar.JarEntry; 27import java.util.jar.JarOutputStream; 28import java.util.jar.Manifest; 29 30/** 31 * Helper class used to encapsulate generated .dex file into .jar 32 * so that it fits {@code DexClassLoader} constructor. 33 */ 34public class DexJarMaker { 35 36 /** indicates name of the dex file added to jar */ 37 public static final String DEX_FILE_NAME_IN_JAR = "classes" + PathHolder.DEX_FILE_EXTENSION; 38 39 /** {@code non-null;} storage for all the paths related to current dex file */ 40 private final PathHolder pathHolder; 41 42 public DexJarMaker(PathHolder pathHolder) { 43 this.pathHolder = pathHolder; 44 } 45 46 /** Packs previously added files into a single jar archive. */ 47 public void create() throws DexClassLoadingException { 48 Manifest manifest = new Manifest(); 49 manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); 50 JarOutputStream target = null; 51 try { 52 target = new JarOutputStream( 53 new BufferedOutputStream(new FileOutputStream(pathHolder.getJarFilePath())), 54 manifest); 55 add(new File(pathHolder.getDexFilePath()), target); 56 57 } catch (IOException e) { 58 throw new DexClassLoadingException(e); 59 } 60 finally { 61 try { 62 if (target != null) { 63 target.close(); 64 } 65 } catch(IOException e) { 66 // Ignoring deliberately in order to keep the original exception clear. 67 } 68 } 69 } 70 71 /** 72 * Adds indicated file to the requested archive. 73 * 74 * @param source {@code non-null;} dex file to add 75 * @param target {@code non-null;} target jar archive 76 * @throws IOException 77 */ 78 private void add(File source, JarOutputStream target) throws IOException { 79 80 if (!source.isFile()) { 81 throw new IllegalArgumentException("Wrong source dex file provided"); 82 } 83 84 BufferedInputStream in = new BufferedInputStream(new FileInputStream(source)); 85 JarEntry entry = new JarEntry(DEX_FILE_NAME_IN_JAR); 86 entry.setTime(source.lastModified()); 87 target.putNextEntry(entry); 88 89 int curr = -1; 90 while ((curr = in.read()) != -1) { 91 target.write(curr); 92 } 93 target.closeEntry(); 94 } 95}