blob: 4d3f7a6ebefbdefbc17e7345a60dd000f6859250 [file] [log] [blame]
/*
* 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.bundlerepository.impl;
import org.apache.felix.bundlerepository.*;
import org.apache.felix.service.command.Descriptor;
import org.apache.felix.service.command.Parameter;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.Version;
import java.io.*;
import java.lang.reflect.Array;
import java.net.URL;
import java.net.URLConnection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
public class ObrGogoCommand
{
private static final String REPO_ADD = "add";
private static final String REPO_REMOVE = "remove";
private static final String REPO_LIST = "list";
private static final String REPO_REFRESH = "refresh";
private static final char VERSION_SEPARATOR = '@';
private final BundleContext m_bc;
private final RepositoryAdmin m_repositoryAdmin;
public ObrGogoCommand(BundleContext bc, RepositoryAdmin repositoryAdmin)
{
m_bc = bc;
m_repositoryAdmin = repositoryAdmin;
}
private RepositoryAdmin getRepositoryAdmin()
{
return m_repositoryAdmin;
}
@Descriptor("manage repositories")
public void repos(
@Descriptor("( add | list | refresh | remove )") String action,
@Descriptor("space-delimited list of repository URLs") String[] args)
throws IOException
{
Object svcObj = getRepositoryAdmin();
if (svcObj == null)
{
return;
}
RepositoryAdmin ra = (RepositoryAdmin) svcObj;
if (args.length > 0)
{
for (int i = 0; i < args.length; i++)
{
try
{
if (action.equals(REPO_ADD))
{
ra.addRepository(args[i]);
}
else if (action.equals(REPO_REFRESH))
{
ra.removeRepository(args[i]);
ra.addRepository(args[i]);
}
else if (action.equals(REPO_REMOVE))
{
ra.removeRepository(args[i]);
}
else
{
System.out.println("Unknown repository operation: " + action);
}
}
catch (Exception ex)
{
ex.printStackTrace(System.err);
}
}
}
else
{
org.apache.felix.bundlerepository.Repository[] repos =
ra.listRepositories();
if ((repos != null) && (repos.length > 0))
{
for (int i = 0; i < repos.length; i++)
{
System.out.println(repos[i].getURI());
}
}
else
{
System.out.println("No repository URLs are set.");
}
}
}
@Descriptor("list repository resources")
public void list(
@Descriptor("display all versions")
@Parameter(names={ "-v", "--verbose" }, presentValue="true",
absentValue="false") boolean verbose,
@Descriptor("optional strings used for name matching") String[] args)
throws IOException, InvalidSyntaxException
{
Object svcObj = getRepositoryAdmin();
if (svcObj == null)
{
return;
}
RepositoryAdmin ra = (RepositoryAdmin) svcObj;
// Create a filter that will match presentation name or symbolic name.
StringBuffer sb = new StringBuffer();
if ((args == null) || (args.length == 0))
{
sb.append("(|(presentationname=*)(symbolicname=*))");
}
else
{
StringBuffer value = new StringBuffer();
for (int i = 0; i < args.length; i++)
{
if (i > 0)
{
value.append(" ");
}
value.append(args[i]);
}
sb.append("(|(presentationname=*");
sb.append(value);
sb.append("*)(symbolicname=*");
sb.append(value);
sb.append("*))");
}
// Use filter to get matching resources.
Resource[] resources = ra.discoverResources(sb.toString());
// Group the resources by symbolic name in descending version order,
// but keep them in overall sorted order by presentation name.
Map revisionMap = new TreeMap(new Comparator() {
public int compare(Object o1, Object o2)
{
Resource r1 = (Resource) o1;
Resource r2 = (Resource) o2;
// Assume if the symbolic name is equal, then the two are equal,
// since we are trying to aggregate by symbolic name.
int symCompare = r1.getSymbolicName().compareTo(r2.getSymbolicName());
if (symCompare == 0)
{
return 0;
}
// Otherwise, compare the presentation name to keep them sorted
// by presentation name. If the presentation names are equal, then
// use the symbolic name to differentiate.
int compare = (r1.getPresentationName() == null)
? -1
: (r2.getPresentationName() == null)
? 1
: r1.getPresentationName().compareToIgnoreCase(
r2.getPresentationName());
if (compare == 0)
{
return symCompare;
}
return compare;
}
});
for (int resIdx = 0; (resources != null) && (resIdx < resources.length); resIdx++)
{
Resource[] revisions = (Resource[]) revisionMap.get(resources[resIdx]);
revisionMap.put(resources[resIdx], addResourceByVersion(revisions, resources[resIdx]));
}
// Print any matching resources.
for (Iterator i = revisionMap.entrySet().iterator(); i.hasNext(); )
{
Map.Entry entry = (Map.Entry) i.next();
Resource[] revisions = (Resource[]) entry.getValue();
String name = revisions[0].getPresentationName();
name = (name == null) ? revisions[0].getSymbolicName() : name;
System.out.print(name);
if (verbose && revisions[0].getPresentationName() != null)
{
System.out.print(" [" + revisions[0].getSymbolicName() + "]");
}
System.out.print(" (");
int revIdx = 0;
do
{
if (revIdx > 0)
{
System.out.print(", ");
}
System.out.print(revisions[revIdx].getVersion());
revIdx++;
}
while (verbose && (revIdx < revisions.length));
if (!verbose && (revisions.length > 1))
{
System.out.print(", ...");
}
System.out.println(")");
}
if ((resources == null) || (resources.length == 0))
{
System.out.println("No matching bundles.");
}
}
@Descriptor("retrieve resource description from repository")
public void info(
@Descriptor("( <bundle-name> | <symbolic-name> | <bundle-id> )[@<version>] ...")
String[] args)
throws IOException, InvalidSyntaxException
{
Object svcObj = getRepositoryAdmin();
if (svcObj == null)
{
return;
}
RepositoryAdmin ra = (RepositoryAdmin) svcObj;
for (int argIdx = 0; (args != null) && (argIdx < args.length); argIdx++)
{
// Find the target's bundle resource.
String targetName = args[argIdx];
String targetVersion = null;
int idx = args[argIdx].indexOf(VERSION_SEPARATOR);
if (idx > 0)
{
targetName = args[argIdx].substring(0, idx);
targetVersion = args[argIdx].substring(idx + 1);
}
Resource[] resources = searchRepository(ra, targetName, targetVersion);
if ((resources == null) || (resources.length == 0))
{
System.err.println("Unknown bundle and/or version: " + args[argIdx]);
}
else
{
for (int resIdx = 0; resIdx < resources.length; resIdx++)
{
if (resIdx > 0)
{
System.out.println("");
}
printResource(System.out, resources[resIdx]);
}
}
}
}
@Descriptor("deploy resource from repository")
public void deploy(
@Descriptor("start deployed bundles")
@Parameter(names={ "-s", "--start" }, presentValue="true",
absentValue="false") boolean start,
@Descriptor("deploy required bundles only")
@Parameter(names={ "-ro", "--required-only" }, presentValue="true",
absentValue="false") boolean requiredOnly,
@Descriptor("( <bundle-name> | <symbolic-name> | <bundle-id> )[@<version>] ...")
String[] args)
throws IOException, InvalidSyntaxException
{
Object svcObj = getRepositoryAdmin();
if (svcObj == null)
{
return;
}
RepositoryAdmin ra = (RepositoryAdmin) svcObj;
Resolver resolver = ra.resolver();
for (int argIdx = 0; (args != null) && (argIdx < args.length); argIdx++)
{
// Find the target's bundle resource.
String targetName = args[argIdx];
String targetVersion = null;
int idx = args[argIdx].indexOf(VERSION_SEPARATOR);
if (idx > 0)
{
targetName = args[argIdx].substring(0, idx);
targetVersion = args[argIdx].substring(idx + 1);
}
Resource resource = selectNewestVersion(
searchRepository(ra, targetName, targetVersion));
if (resource != null)
{
resolver.add(resource);
}
else
{
System.err.println("Unknown bundle - " + args[argIdx]);
}
}
if ((resolver.getAddedResources() != null) &&
(resolver.getAddedResources().length > 0))
{
if (resolver.resolve())
{
System.out.println("Target resource(s):");
System.out.println(getUnderlineString(19));
Resource[] resources = resolver.getAddedResources();
for (int resIdx = 0; (resources != null) && (resIdx < resources.length); resIdx++)
{
System.out.println(" " + resources[resIdx].getPresentationName()
+ " (" + resources[resIdx].getVersion() + ")");
}
resources = resolver.getRequiredResources();
if ((resources != null) && (resources.length > 0))
{
System.out.println("\nRequired resource(s):");
System.out.println(getUnderlineString(21));
for (int resIdx = 0; resIdx < resources.length; resIdx++)
{
System.out.println(" " + resources[resIdx].getPresentationName()
+ " (" + resources[resIdx].getVersion() + ")");
}
}
if (!requiredOnly)
{
resources = resolver.getOptionalResources();
if ((resources != null) && (resources.length > 0))
{
System.out.println("\nOptional resource(s):");
System.out.println(getUnderlineString(21));
for (int resIdx = 0; resIdx < resources.length; resIdx++)
{
System.out.println(" " + resources[resIdx].getPresentationName()
+ " (" + resources[resIdx].getVersion() + ")");
}
}
}
try
{
System.out.print("\nDeploying...\n");
int options = 0;
if (start)
{
options |= Resolver.START;
}
if (requiredOnly)
{
options |= Resolver.NO_OPTIONAL_RESOURCES;
}
resolver.deploy(options);
System.out.println("done.");
}
catch (IllegalStateException ex)
{
System.err.println(ex);
}
}
else
{
Reason[] reqs = resolver.getUnsatisfiedRequirements();
if ((reqs != null) && (reqs.length > 0))
{
System.out.println("Unsatisfied requirement(s):");
System.out.println(getUnderlineString(27));
for (int reqIdx = 0; reqIdx < reqs.length; reqIdx++)
{
System.out.println(" " + reqs[reqIdx].getRequirement().getFilter());
System.out.println(" " + reqs[reqIdx].getResource().getPresentationName());
}
}
else
{
System.out.println("Could not resolve targets.");
}
}
}
}
@Descriptor("retrieve resource source code from repository")
public void source(
@Descriptor("extract source code")
@Parameter(names={ "-x", "--extract" }, presentValue="true",
absentValue="false") boolean extract,
@Descriptor("local target directory") File localDir,
@Descriptor("( <bundle-name> | <symbolic-name> | <bundle-id> )[@<version>] ...")
String[] args)
throws IOException, InvalidSyntaxException
{
Object svcObj = getRepositoryAdmin();
if (svcObj == null)
{
return;
}
RepositoryAdmin ra = (RepositoryAdmin) svcObj;
for (int argIdx = 0; argIdx < args.length; argIdx++)
{
// Find the target's bundle resource.
String targetName = args[argIdx];
String targetVersion = null;
int idx = args[argIdx].indexOf(VERSION_SEPARATOR);
if (idx > 0)
{
targetName = args[argIdx].substring(0, idx);
targetVersion = args[argIdx].substring(idx + 1);
}
Resource resource = selectNewestVersion(
searchRepository(ra, targetName, targetVersion));
if (resource == null)
{
System.err.println("Unknown bundle and/or version: " + args[argIdx]);
}
else
{
String srcURI = (String) resource.getProperties().get(Resource.SOURCE_URI);
if (srcURI != null)
{
downloadSource(
System.out, System.err, new URL(srcURI),
localDir, extract);
}
else
{
System.err.println("Missing source URL: " + args[argIdx]);
}
}
}
}
@Descriptor("retrieve resource JavaDoc from repository")
public void javadoc(
@Descriptor("extract documentation")
@Parameter(names={"-x", "--extract" }, presentValue="true",
absentValue="false") boolean extract,
@Descriptor("local target directory") File localDir,
@Descriptor("( <bundle-name> | <symbolic-name> | <bundle-id> )[@<version>] ...")
String[] args)
throws IOException, InvalidSyntaxException
{
Object svcObj = getRepositoryAdmin();
if (svcObj == null)
{
return;
}
RepositoryAdmin ra = (RepositoryAdmin) svcObj;
for (int argIdx = 0; argIdx < args.length; argIdx++)
{
// Find the target's bundle resource.
String targetName = args[argIdx];
String targetVersion = null;
int idx = args[argIdx].indexOf(VERSION_SEPARATOR);
if (idx > 0)
{
targetName = args[argIdx].substring(0, idx);
targetVersion = args[argIdx].substring(idx + 1);
}
Resource resource = selectNewestVersion(
searchRepository(ra, targetName, targetVersion));
if (resource == null)
{
System.err.println("Unknown bundle and/or version: " + args[argIdx]);
}
else
{
URL docURL = (URL) resource.getProperties().get("javadoc");
if (docURL != null)
{
downloadSource(
System.out, System.err, docURL, localDir, extract);
}
else
{
System.err.println("Missing javadoc URL: " + args[argIdx]);
}
}
}
}
private Resource[] searchRepository(
RepositoryAdmin ra, String targetId, String targetVersion)
throws InvalidSyntaxException
{
// Try to see if the targetId is a bundle ID.
try
{
Bundle bundle = m_bc.getBundle(Long.parseLong(targetId));
if (bundle != null)
{
targetId = bundle.getSymbolicName();
}
else
{
return null;
}
}
catch (NumberFormatException ex)
{
// It was not a number, so ignore.
}
// The targetId may be a bundle name or a bundle symbolic name,
// so create the appropriate LDAP query.
StringBuffer sb = new StringBuffer("(|(presentationname=");
sb.append(targetId);
sb.append(")(symbolicname=");
sb.append(targetId);
sb.append("))");
if (targetVersion != null)
{
sb.insert(0, "(&");
sb.append("(version=");
sb.append(targetVersion);
sb.append("))");
}
return ra.discoverResources(sb.toString());
}
private Resource selectNewestVersion(Resource[] resources)
{
int idx = -1;
Version v = null;
for (int i = 0; (resources != null) && (i < resources.length); i++)
{
if (i == 0)
{
idx = 0;
v = resources[i].getVersion();
}
else
{
Version vtmp = resources[i].getVersion();
if (vtmp.compareTo(v) > 0)
{
idx = i;
v = vtmp;
}
}
}
return (idx < 0) ? null : resources[idx];
}
private void printResource(PrintStream out, Resource resource)
{
String presentationName = resource.getPresentationName();
if (presentationName == null)
presentationName = resource.getSymbolicName();
System.out.println(getUnderlineString(presentationName.length()));
out.println(presentationName);
System.out.println(getUnderlineString(presentationName.length()));
Map map = resource.getProperties();
for (Iterator iter = map.entrySet().iterator(); iter.hasNext(); )
{
Map.Entry entry = (Map.Entry) iter.next();
if (entry.getValue().getClass().isArray())
{
out.println(entry.getKey() + ":");
for (int j = 0; j < Array.getLength(entry.getValue()); j++)
{
out.println(" " + Array.get(entry.getValue(), j));
}
}
else
{
out.println(entry.getKey() + ": " + entry.getValue());
}
}
Requirement[] reqs = resource.getRequirements();
if ((reqs != null) && (reqs.length > 0))
{
out.println("Requires:");
for (int i = 0; i < reqs.length; i++)
{
out.println(" " + reqs[i].getFilter());
}
}
Capability[] caps = resource.getCapabilities();
if ((caps != null) && (caps.length > 0))
{
out.println("Capabilities:");
for (int i = 0; i < caps.length; i++)
{
out.println(" " + caps[i].getPropertiesAsMap());
}
}
}
private static Resource[] addResourceByVersion(Resource[] revisions, Resource resource)
{
// We want to add the resource into the array of revisions
// in descending version sorted order (i.e., newest first)
Resource[] sorted = null;
if (revisions == null)
{
sorted = new Resource[] { resource };
}
else
{
Version version = resource.getVersion();
Version middleVersion = null;
int top = 0, bottom = revisions.length - 1, middle = 0;
while (top <= bottom)
{
middle = (bottom - top) / 2 + top;
middleVersion = revisions[middle].getVersion();
// Sort in reverse version order.
int cmp = middleVersion.compareTo(version);
if (cmp < 0)
{
bottom = middle - 1;
}
else
{
top = middle + 1;
}
}
// Ignore duplicates.
if ((top >= revisions.length) || (revisions[top] != resource))
{
sorted = new Resource[revisions.length + 1];
System.arraycopy(revisions, 0, sorted, 0, top);
System.arraycopy(revisions, top, sorted, top + 1, revisions.length - top);
sorted[top] = resource;
}
}
return sorted;
}
private final static StringBuffer m_sb = new StringBuffer();
public static String getUnderlineString(int len)
{
synchronized (m_sb)
{
m_sb.delete(0, m_sb.length());
for (int i = 0; i < len; i++)
{
m_sb.append('-');
}
return m_sb.toString();
}
}
public static void downloadSource(
PrintStream out, PrintStream err,
URL srcURL, File localDir, boolean extract)
{
// Get the file name from the URL.
String fileName = (srcURL.getFile().lastIndexOf('/') > 0)
? srcURL.getFile().substring(srcURL.getFile().lastIndexOf('/') + 1)
: srcURL.getFile();
try
{
out.println("Connecting...");
if (!localDir.exists())
{
err.println("Destination directory does not exist.");
}
File file = new File(localDir, fileName);
OutputStream os = new FileOutputStream(file);
URLConnection conn = srcURL.openConnection();
setProxyAuth(conn);
int total = conn.getContentLength();
InputStream is = conn.getInputStream();
if (total > 0)
{
out.println("Downloading " + fileName
+ " ( " + total + " bytes ).");
}
else
{
out.println("Downloading " + fileName + ".");
}
byte[] buffer = new byte[4096];
for (int len = is.read(buffer); len > 0; len = is.read(buffer))
{
os.write(buffer, 0, len);
}
os.close();
is.close();
if (extract)
{
is = new FileInputStream(file);
JarInputStream jis = new JarInputStream(is);
out.println("Extracting...");
unjar(jis, localDir);
jis.close();
file.delete();
}
}
catch (Exception ex)
{
err.println(ex);
}
}
public static void setProxyAuth(URLConnection conn) throws IOException
{
// Support for http proxy authentication
String auth = System.getProperty("http.proxyAuth");
if ((auth != null) && (auth.length() > 0))
{
if ("http".equals(conn.getURL().getProtocol())
|| "https".equals(conn.getURL().getProtocol()))
{
String base64 = Base64Encoder.base64Encode(auth);
conn.setRequestProperty("Proxy-Authorization", "Basic " + base64);
}
}
}
public static void unjar(JarInputStream jis, File dir)
throws IOException
{
// Reusable buffer.
byte[] buffer = new byte[4096];
// Loop through JAR entries.
for (JarEntry je = jis.getNextJarEntry();
je != null;
je = jis.getNextJarEntry())
{
if (je.getName().startsWith("/"))
{
throw new IOException("JAR resource cannot contain absolute paths.");
}
File target = new File(dir, je.getName());
// Check to see if the JAR entry is a directory.
if (je.isDirectory())
{
if (!target.exists())
{
if (!target.mkdirs())
{
throw new IOException("Unable to create target directory: "
+ target);
}
}
// Just continue since directories do not have content to copy.
continue;
}
int lastIndex = je.getName().lastIndexOf('/');
String name = (lastIndex >= 0) ?
je.getName().substring(lastIndex + 1) : je.getName();
String destination = (lastIndex >= 0) ?
je.getName().substring(0, lastIndex) : "";
// JAR files use '/', so convert it to platform separator.
destination = destination.replace('/', File.separatorChar);
copy(jis, dir, name, destination, buffer);
}
}
public static void copy(
InputStream is, File dir, String destName, String destDir, byte[] buffer)
throws IOException
{
if (destDir == null)
{
destDir = "";
}
// Make sure the target directory exists and
// that is actually a directory.
File targetDir = new File(dir, destDir);
if (!targetDir.exists())
{
if (!targetDir.mkdirs())
{
throw new IOException("Unable to create target directory: "
+ targetDir);
}
}
else if (!targetDir.isDirectory())
{
throw new IOException("Target is not a directory: "
+ targetDir);
}
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(new File(targetDir, destName)));
int count = 0;
while ((count = is.read(buffer)) > 0)
{
bos.write(buffer, 0, count);
}
bos.close();
}
}