Java tutorial
/* * Copyright 2011-2012 Paddy Byers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.webinos.android.util; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.security.MessageDigest; import org.apache.http.HttpEntity; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.webinos.android.util.Constants; import android.content.Context; import android.util.Log; public class ModuleUtils { private static String TAG = "anode::ModuleUtils"; /* module types */ public static class ModuleType { public int type; public String extension; public Unpacker unpacker; public ModuleType(int type, String extension, Unpacker unpacker) { this.type = type; this.extension = extension; this.unpacker = unpacker; } } public static final int TYPE_JS = 0; public static final int TYPE_NODE = 1; public static final int TYPE_DIR = 2; public static final int TYPE_ZIP = 3; public static final int TYPE_TAR = 4; private static final ModuleType[] modTypes = new ModuleType[] { new ModuleType(TYPE_JS, ".js", null), new ModuleType(TYPE_NODE, ".node", null), new ModuleType(TYPE_DIR, "", null), new ModuleType(TYPE_ZIP, ".zip", new ZipExtractor()), new ModuleType(TYPE_TAR, ".tar.gz", new TarExtractor()), new ModuleType(TYPE_TAR, ".tgz", new TarExtractor()) }; public interface Unpacker { public void unpack(File src, File dest) throws IOException; } /* for hashing names and tmp files */ private static int counter = 0; private static final int HASH_LEN = 20; /* cache dir for tmp and downloaded resources */ private static File resourceDir = new File(Constants.RESOURCE_DIR); private static File moduleDir = new File(Constants.MODULE_DIR); public static void install(Context ctx, String module, String path) { moduleDir.mkdirs(); File moduleResource; boolean remove_tmp_resource = false; /* if no path was specified, it is an error */ if (path == null || path.isEmpty()) { Log.v(TAG, "install: no path specified"); return; } /* resolve expected module type from path */ ModuleType modType = guessModuleType(path); if (modType == null) { Log.v(TAG, "install: unable to determine module type: path = " + path); return; } /* guess the module name, if not already specified */ if (module == null || module.isEmpty()) { module = guessModuleName(path, modType); } /* download module if http or https */ if (path.startsWith("http://") || path.startsWith("https://")) { String filename = ModuleUtils.getResourceUriHash(path); try { moduleResource = downloadResource(new URI(path), filename); remove_tmp_resource = true; } catch (IOException e) { Log.v(TAG, "install: aborting (unable to download resource); exception: " + e + "; resource = " + path); return; } catch (URISyntaxException e) { Log.v(TAG, "install: aborting (invalid URI specified for resource); exception: " + e + "; resource = " + path); return; } /* extract to temp file if an asset */ } else if (path.startsWith(AssetUtils.ASSET_URI)) { try { String filename = ModuleUtils.getResourceUriHash(path); String destination = new File(resourceDir, filename).getAbsolutePath(); moduleResource = AssetUtils.writeAssetToFile(ctx, path.substring(AssetUtils.ASSET_URI.length()), destination); remove_tmp_resource = true; } catch (IOException e) { Log.v(TAG, "install: aborting (unable to extract resource); exception: " + e + "; resource = " + path); return; } } else { moduleResource = new File(path); } /* unpack if necessary */ if (modType.unpacker != null) { try { moduleResource = unpack(moduleResource, module, modType); remove_tmp_resource = true; } catch (IOException e) { Log.v(TAG, "install: aborting (unable to unpack resource); exception: " + e + "; resource = " + path); return; } } /* copy processed package to modules dir */ File installLocation = getModuleFile(module, modType); if (installLocation.exists()) { if (!deleteFile(installLocation)) { Log.v(TAG, "install: aborting (unable to delete old module version); resource = " + path + ", destination = " + installLocation.toString()); return; } } if (copyFile(moduleResource, installLocation)) { if (remove_tmp_resource) deleteFile(moduleResource); Log.v(TAG, "install: success; resource = " + path + ", destination = " + installLocation.toString()); return; } Log.v(TAG, "install: aborting (unable to copy resource); resource = " + path + ", destination = " + installLocation.toString()); } public static void uninstall(String module) { /* if no module was specified, it is an error */ if (module == null || module.isEmpty()) { Log.v(TAG, "uninstall: no module specified"); return; } File moduleLocation = locateModule(module, null); if (moduleLocation == null) { Log.v(TAG, "uninstall: specified module does not exist: " + module); return; } if (!deleteFile(moduleLocation)) { Log.v(TAG, "uninstall: unable to delete: " + module + "; attempting to delete " + moduleLocation.toString()); return; } Log.v(TAG, "uninstall: success; module = " + module + ", location = " + moduleLocation.toString()); } public static String guessModuleName(String path, ModuleType type) { int pathEnd = path.lastIndexOf('/') + 1; return path.substring(pathEnd, path.length() - type.extension.length()); } public static ModuleType guessModuleType(String filename) { /* guess by extension first */ for (ModuleType modType : modTypes) { if (!modType.extension.isEmpty() && filename.endsWith(modType.extension)) return modType; } /* if it's a local directory, then it's type DIR */ if ((new File(filename)).isDirectory()) return modTypes[TYPE_DIR]; return null; } public static File getModuleFile(String module, ModuleType modType) { if (modType.unpacker == null) module += modType.extension; return new File(moduleDir, module); } public static File locateModule(String module, ModuleType modType) { File installLocation = null, candidateLocation; if (modType == null) { for (ModuleType type : modTypes) { /* a small cheat, since all unpacked types here match as TYPE_DIR */ if ((candidateLocation = new File(moduleDir, module + type.extension)).exists()) { installLocation = candidateLocation; break; } } } else { candidateLocation = getModuleFile(module, modType); if (candidateLocation.exists()) installLocation = candidateLocation; } return installLocation; } public static boolean deleteFile(File location) { boolean result = true; if (location.exists()) { if (location.isDirectory()) { result = deleteDir(location); } else { result = location.delete(); } } return result; } private static boolean deleteDir(File location) { for (String child : location.list()) { boolean result = deleteFile(new File(location, child)); if (!result) { return false; } } return location.delete(); } public static boolean copyFile(File src, File dest) { boolean result = true; if (src.isDirectory()) { result = copyDir(src, dest); } else { FileInputStream fis = null; FileOutputStream fos = null; try { int count; byte[] buf = new byte[1024]; fis = new FileInputStream(src); fos = new FileOutputStream(dest); while ((count = fis.read(buf, 0, 1024)) != -1) fos.write(buf, 0, count); } catch (IOException e) { Log.v(TAG, "moveFile exception: aborting; exception: " + e + "; src = " + src.toString() + "; dest = " + dest.toString()); return false; } finally { try { if (fis != null) fis.close(); if (fos != null) fos.close(); } catch (IOException ieo) { } } } return result; } private static boolean copyDir(File src, File dest) { dest.mkdir(); for (String child : src.list()) { boolean result = copyFile(new File(src, child), new File(dest, child)); if (!result) { return false; } } return true; } public static File unpack(File moduleResource, String moduleName, ModuleType modType) throws IOException { /* create temp dir to unpack; assume no hash collision */ String tmpDirName = moduleName + '-' + String.valueOf(counter++) + "-tmp"; File result = new File(resourceDir, tmpDirName); if (!result.isDirectory() && !result.mkdir()) throw new IOException("Unable to create tmp directory to unpack: " + result.toString()); if (modType.unpacker != null) { modType.unpacker.unpack(moduleResource, result); } else { Log.v(TAG, "ModuleUtils.unpack(): aborting (internal error)"); result = null; } return result; } public static File getResource(URI httpUri, boolean download) throws IOException { String cacheFilename = getResourceUriHash(httpUri.toString()); File cachedFile = new File(resourceDir, cacheFilename); if (cachedFile.exists()) return cachedFile; if (download) return downloadResource(httpUri, cacheFilename); return null; } public static File downloadResource(URI httpUri, String filename) throws IOException { /* download */ HttpClient http = new DefaultHttpClient(); HttpGet request = new HttpGet(); request.setURI(httpUri); HttpEntity entity = http.execute(request).getEntity(); InputStream in = entity.getContent(); File destination = new File(resourceDir, filename); FileOutputStream out = new FileOutputStream(destination); byte[] buf = new byte[1024]; int read; while ((read = in.read(buf)) != -1) { out.write(buf, 0, read); } in.close(); out.flush(); out.close(); return destination; } public static String getResourceUriHash(String id) { try { MessageDigest sha = MessageDigest.getInstance("SHA-1"); sha.update(id.getBytes("iso-8859-1")); return digest2Hex(sha.digest()); } catch (Exception e) { return null; } } private static String digest2Hex(byte[] digest) { StringBuilder hash = new StringBuilder(HASH_LEN * 2); final String hexChars = "0123456789abcdef"; for (int i = 0; i < HASH_LEN; i++) { hash.append(hexChars.charAt((digest[i] & 0xF0) >> 4)).append(hexChars.charAt((digest[i] & 0x0F))); } return hash.toString(); } }