/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.felix.connect.launch; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import org.osgi.framework.Filter; import org.osgi.framework.FrameworkUtil; import org.apache.felix.connect.felix.framework.util.MapToDictionary; public class ClasspathScanner { public List<BundleDescriptor> scanForBundles() throws Exception { return scanForBundles(null, null); } public List<BundleDescriptor> scanForBundles(ClassLoader loader) throws Exception { return scanForBundles(null, loader); } public List<BundleDescriptor> scanForBundles(String filterString) throws Exception { return scanForBundles(filterString, null); } public List<BundleDescriptor> scanForBundles(String filterString, ClassLoader loader) throws Exception { Filter filter = (filterString != null) ? FrameworkUtil .createFilter(filterString) : null; loader = (loader != null) ? loader : getClass().getClassLoader(); List<BundleDescriptor> bundles = new ArrayList<BundleDescriptor>(); byte[] bytes = new byte[1024 * 1024 * 2]; for (Enumeration<URL> e = loader.getResources( "META-INF/MANIFEST.MF"); e.hasMoreElements(); ) { URL manifestURL = e.nextElement(); InputStream input = null; try { input = manifestURL.openStream(); int size = 0; for (int i = input.read(bytes); i != -1; i = input.read(bytes, size, bytes.length - size)) { size += i; if (size == bytes.length) { byte[] tmp = new byte[size * 2]; System.arraycopy(bytes, 0, tmp, 0, bytes.length); bytes = tmp; } } // Now parse the main attributes. The idea is to do that // without creating new byte arrays. Therefore, we read through // the manifest bytes inside the bytes array and write them back into // the same array unless we don't need them (e.g., \r\n and \n are skipped). // That allows us to create the strings from the bytes array without the skipped // chars. We stopp as soon as we see a blankline as that denotes that the main //attributes part is finished. String key = null; int last = 0; int current = 0; Map<String, String> headers = new HashMap<String, String>(); for (int i = 0; i < size; i++) { // skip \r and \n if it is follows by another \n // (we catch the blank line case in the next iteration) if (bytes[i] == '\r') { if ((i + 1 < size) && (bytes[i + 1] == '\n')) { continue; } } if (bytes[i] == '\n') { if ((i + 1 < size) && (bytes[i + 1] == ' ')) { i++; continue; } } // If we don't have a key yet and see the first : we parse it as the key // and skip the :<blank> that follows it. if ((key == null) && (bytes[i] == ':')) { key = new String(bytes, last, (current - last), "UTF-8"); if ((i + 1 < size) && (bytes[i + 1] == ' ')) { last = current + 1; continue; } else { throw new Exception( "Manifest error: Missing space separator - " + key); } } // if we are at the end of a line if (bytes[i] == '\n') { // and it is a blank line stop parsing (main attributes are done) if ((last == current) && (key == null)) { break; } // Otherwise, parse the value and add it to the map (we throw an // exception if we don't have a key or the key already exist. String value = new String(bytes, last, (current - last), "UTF-8"); if (key == null) { throw new Exception("Manifst error: Missing attribute name - " + value); } else if (headers.put(key, value) != null) { throw new Exception("Manifst error: Duplicate attribute name - " + key); } last = current; key = null; } else { // write back the byte if it needs to be included in the key or the value. bytes[current++] = bytes[i]; } } if ((filter == null) || filter.match(new MapToDictionary<String, String>(headers))) { bundles.add(new BundleDescriptor(loader, getParentURL(manifestURL).toExternalForm(), headers)); } } finally { if (input != null) { input.close(); } } } return bundles; } private URL getParentURL(URL url) throws Exception { String externalForm = url.toExternalForm(); return new URL(externalForm.substring(0, externalForm.length() - "META-INF/MANIFEST.MF".length())); } }