FELIX-3111 : Separate OBR Plugin
FELIX-3107 : Separate Shell Plugin
FELIX-3099 : Separate Deployment Admin plugin
FELIX-3100 : Separate SCR plugin
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1169777 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole-plugins/obr/src/main/java/org/apache/felix/webconsole/plugins/obr/internal/AbstractBundleRepositoryRenderHelper.java b/webconsole-plugins/obr/src/main/java/org/apache/felix/webconsole/plugins/obr/internal/AbstractBundleRepositoryRenderHelper.java
new file mode 100644
index 0000000..9e0b5f2
--- /dev/null
+++ b/webconsole-plugins/obr/src/main/java/org/apache/felix/webconsole/plugins/obr/internal/AbstractBundleRepositoryRenderHelper.java
@@ -0,0 +1,74 @@
+/*
+ * 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.webconsole.plugins.obr.internal;
+
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import org.apache.felix.webconsole.AbstractWebConsolePlugin;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.util.tracker.ServiceTracker;
+
+
+abstract class AbstractBundleRepositoryRenderHelper
+{
+
+ protected final AbstractWebConsolePlugin logger;
+
+ private final ServiceTracker repositoryAdmin;
+
+
+ protected AbstractBundleRepositoryRenderHelper( final AbstractWebConsolePlugin logger,
+ final BundleContext bundleContext, final String serviceName )
+ {
+ this.logger = logger;
+ this.repositoryAdmin = new ServiceTracker( bundleContext, serviceName, null );
+ this.repositoryAdmin.open();
+ }
+
+
+ void dispose()
+ {
+ repositoryAdmin.close();
+ }
+
+
+ boolean hasRepositoryAdmin()
+ {
+ return getRepositoryAdmin() != null;
+ }
+
+
+ protected final Object getRepositoryAdmin()
+ {
+ return repositoryAdmin.getService();
+ }
+
+
+ abstract void doDeploy( String[] bundles, boolean start, boolean optional );
+
+
+ abstract void doAction( String action, String urlParam ) throws IOException, ServletException;
+
+
+ abstract String getData( final String filter, final boolean details, final Bundle[] bundles );
+
+}
\ No newline at end of file
diff --git a/webconsole-plugins/obr/src/main/java/org/apache/felix/webconsole/plugins/obr/internal/Activator.java b/webconsole-plugins/obr/src/main/java/org/apache/felix/webconsole/plugins/obr/internal/Activator.java
new file mode 100644
index 0000000..0a53380
--- /dev/null
+++ b/webconsole-plugins/obr/src/main/java/org/apache/felix/webconsole/plugins/obr/internal/Activator.java
@@ -0,0 +1,103 @@
+/*
+ * 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.webconsole.plugins.obr.internal;
+
+import org.apache.felix.webconsole.SimpleWebConsolePlugin;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+/**
+ * Activator is the main starting class.
+ */
+public class Activator implements BundleActivator, ServiceTrackerCustomizer, Constants
+{
+
+ private ServiceTracker tracker;
+ private BundleContext context;
+
+ private SimpleWebConsolePlugin plugin;
+
+ /**
+ * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
+ */
+ public final void start(BundleContext context) throws Exception
+ {
+ this.context = context;
+ Filter filter = context.createFilter("(|" //$NON-NLS-1$
+ + '(' + OBJECTCLASS + "=org.osgi.service.obr.RepositoryAdmin)" //$NON-NLS-1$
+ + '(' + OBJECTCLASS + "=org.apache.felix.bundlerepository.RepositoryAdmin)" //$NON-NLS-1$
+ + ')');
+ this.tracker = new ServiceTracker(context, filter, this);
+ this.tracker.open();
+ }
+
+ /**
+ * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
+ */
+ public final void stop(BundleContext context) throws Exception
+ {
+ if (tracker != null)
+ {
+ tracker.close();
+ tracker = null;
+ }
+ }
+
+ // - begin tracker
+ /**
+ * @see org.osgi.util.tracker.ServiceTrackerCustomizer#modifiedService(org.osgi.framework.ServiceReference,
+ * java.lang.Object)
+ */
+ public final void modifiedService(ServiceReference reference, Object service)
+ {/* unused */
+ }
+
+ /**
+ * @see org.osgi.util.tracker.ServiceTrackerCustomizer#addingService(org.osgi.framework.ServiceReference)
+ */
+ public final Object addingService(ServiceReference reference)
+ {
+ SimpleWebConsolePlugin plugin = this.plugin;
+ if (plugin == null)
+ {
+ this.plugin = plugin = new WebConsolePlugin().register(context);
+ }
+
+ return context.getService(reference);
+ }
+
+ /**
+ * @see org.osgi.util.tracker.ServiceTrackerCustomizer#removedService(org.osgi.framework.ServiceReference,
+ * java.lang.Object)
+ */
+ public final void removedService(ServiceReference reference, Object service)
+ {
+ SimpleWebConsolePlugin plugin = this.plugin;
+
+ if (tracker.getTrackingCount() == 0 && plugin != null)
+ {
+ plugin.unregister();
+ this.plugin = null;
+ }
+
+ }
+}
diff --git a/webconsole-plugins/obr/src/main/java/org/apache/felix/webconsole/plugins/obr/internal/FelixBundleRepositoryRenderHelper.java b/webconsole-plugins/obr/src/main/java/org/apache/felix/webconsole/plugins/obr/internal/FelixBundleRepositoryRenderHelper.java
new file mode 100644
index 0000000..9858577
--- /dev/null
+++ b/webconsole-plugins/obr/src/main/java/org/apache/felix/webconsole/plugins/obr/internal/FelixBundleRepositoryRenderHelper.java
@@ -0,0 +1,276 @@
+/*
+ * 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.webconsole.plugins.obr.internal;
+
+
+import java.io.IOException;
+import javax.servlet.ServletException;
+import org.apache.felix.bundlerepository.Capability;
+import org.apache.felix.bundlerepository.Property;
+import org.apache.felix.bundlerepository.Reason;
+import org.apache.felix.bundlerepository.Repository;
+import org.apache.felix.bundlerepository.RepositoryAdmin;
+import org.apache.felix.bundlerepository.Requirement;
+import org.apache.felix.bundlerepository.Resolver;
+import org.apache.felix.bundlerepository.Resource;
+import org.apache.felix.webconsole.AbstractWebConsolePlugin;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+
+
+/**
+ * This class provides a plugin for rendering the available OSGi Bundle Repositories
+ * and the resources they provide.
+ */
+class FelixBundleRepositoryRenderHelper extends AbstractBundleRepositoryRenderHelper
+{
+
+ FelixBundleRepositoryRenderHelper( AbstractWebConsolePlugin logger, BundleContext bundleContext )
+ {
+ super( logger, bundleContext, RepositoryAdmin.class.getName() );
+ }
+
+
+ String getData( final String filter, final boolean details, Bundle[] bundles )
+ {
+ RepositoryAdmin admin = ( RepositoryAdmin ) getRepositoryAdmin();
+ if ( admin != null )
+ {
+ JSONObject json = new JSONObject();
+ try
+ {
+ json.put( "status", true ); //$NON-NLS-1$
+ json.put( "details", details ); //$NON-NLS-1$
+
+ final Repository repositories[] = admin.listRepositories();
+ for ( int i = 0; repositories != null && i < repositories.length; i++ )
+ {
+ json.append( "repositories", new JSONObject() //$NON-NLS-1$
+ .put( "lastModified", repositories[i].getLastModified() ) //$NON-NLS-1$
+ .put( "name", repositories[i].getName() ) //$NON-NLS-1$
+ .put( "url", repositories[i].getURI() ) ); //$NON-NLS-1$
+ }
+
+ Resource[] resources = admin.discoverResources( filter );
+ for ( int i = 0; resources != null && i < resources.length; i++ )
+ {
+ json.append( "resources", toJSON( resources[i], bundles, details ) ); //$NON-NLS-1$
+ }
+
+ }
+ catch ( JSONException e )
+ {
+ logger.log( "Failed to serialize repository to JSON object.", e );
+ }
+ catch ( Exception e )
+ {
+ logger.log( "Failed to parse filter '" + filter + "'", e );
+ try
+ {
+ String reason = "filter=" + filter;
+ if ( e.getMessage() != null )
+ {
+ reason = e.getMessage() + "(" + reason + ")";
+ }
+ json.put( "error", reason ); //$NON-NLS-1$
+ }
+ catch ( JSONException je )
+ {
+ // ignore
+ }
+ }
+ return json.toString();
+ }
+
+ // fall back to no data
+ return "{}"; //$NON-NLS-1$
+ }
+
+
+ final void doAction( String action, String urlParam ) throws IOException, ServletException
+ {
+ RepositoryAdmin admin = ( RepositoryAdmin ) getRepositoryAdmin();
+ Repository[] repos = admin.listRepositories();
+ Repository repo = getRepository( repos, urlParam );
+
+ String uri = repo != null ? repo.getURI() : urlParam;
+
+ if ( "delete".equals( action ) ) //$NON-NLS-1$
+ {
+ if ( !admin.removeRepository( uri ) )
+ {
+ throw new ServletException( "Failed to remove repository with URL " + uri );
+ }
+ }
+ else if ( "add".equals( action ) || "refresh".equals( action ) ) //$NON-NLS-1$ //$NON-NLS-2$
+ {
+ try
+ {
+ admin.addRepository( uri );
+ }
+ catch ( IOException e )
+ {
+ throw e;
+ }
+ catch ( Exception e )
+ {
+ throw new ServletException( "Failed to " + action + " repository " + uri + ": " + e.toString() );
+ }
+
+ }
+ }
+
+
+ final void doDeploy( String[] bundles, boolean start, boolean optional )
+ {
+ try
+ {
+ // check whether we have to do something
+ if ( bundles == null || bundles.length == 0 )
+ {
+ logger.log( "No resources to deploy" );
+ return;
+ }
+
+ RepositoryAdmin repoAdmin = ( RepositoryAdmin ) getRepositoryAdmin();
+ Resolver resolver = repoAdmin.resolver();
+
+ // prepare the deployment
+ for ( int i = 0; i < bundles.length; i++ )
+ {
+ String bundle = bundles[i];
+ if ( bundle == null || bundle.equals( "-" ) ) //$NON-NLS-1$
+ {
+ continue;
+ }
+
+ String filter = "(id=" + bundle + ")";
+ Resource[] resources = repoAdmin.discoverResources( filter );
+ if ( resources != null && resources.length > 0 )
+ {
+ resolver.add( resources[0] );
+ }
+ }
+
+ FelixDeployer.deploy( resolver, logger, start, optional );
+ }
+ catch ( InvalidSyntaxException e )
+ {
+ throw new IllegalStateException( e );
+ }
+ }
+
+
+ private final Repository getRepository( Repository[] repos, String repositoryUrl )
+ {
+ if ( repositoryUrl == null || repositoryUrl.length() == 0 )
+ {
+ return null;
+ }
+
+ for ( int i = 0; i < repos.length; i++ )
+ {
+ if ( repositoryUrl.equals( repos[i].getURI() ) )
+ {
+ return repos[i];
+ }
+ }
+
+ return null;
+ }
+
+
+ private final JSONObject toJSON( Resource resource, Bundle[] bundles, boolean details ) throws JSONException
+ {
+ final String symbolicName = resource.getSymbolicName();
+ final String version = resource.getVersion().toString();
+ boolean installed = false;
+ for ( int i = 0; symbolicName != null && !installed && bundles != null && i < bundles.length; i++ )
+ {
+ final String ver = ( String ) bundles[i].getHeaders( "" ).get( Constants.BUNDLE_VERSION ); //$NON-NLS-1$
+ installed = symbolicName.equals( bundles[i].getSymbolicName() ) && version.equals( ver );
+ }
+ JSONObject json = new JSONObject( resource.getProperties() ) //
+ .put( "id", resource.getId() ) // //$NON-NLS-1$
+ .put( "presentationname", resource.getPresentationName() ) // //$NON-NLS-1$
+ .put( "symbolicname", symbolicName ) // //$NON-NLS-1$
+ .put( "url", resource.getURI() ) // //$NON-NLS-1$
+ .put( "version", version ) // //$NON-NLS-1$
+ .put( "categories", resource.getCategories() ) // //$NON-NLS-1$
+ .put( "installed", installed ); //$NON-NLS-1$
+
+ if ( details )
+ {
+ Capability[] caps = resource.getCapabilities();
+ for ( int i = 0; caps != null && i < caps.length; i++ )
+ {
+ json.append( "capabilities", new JSONObject() //$NON-NLS-1$
+ .put( "name", caps[i].getName() ) //$NON-NLS-1$
+ .put( "properties", toJSON( caps[i].getProperties() ) ) ); //$NON-NLS-1$
+ }
+ Requirement[] reqs = resource.getRequirements();
+ for ( int i = 0; reqs != null && i < reqs.length; i++ )
+ {
+ json.append( "requirements", new JSONObject() //$NON-NLS-1$
+ .put( "name", reqs[i].getName() ) //$NON-NLS-1$
+ .put( "filter", reqs[i].getFilter() ) //$NON-NLS-1$
+ .put( "optional", reqs[i].isOptional() ) ); //$NON-NLS-1$
+ }
+
+ final RepositoryAdmin admin = ( RepositoryAdmin ) getRepositoryAdmin();
+ Resolver resolver = admin.resolver();
+ resolver.add( resource );
+ resolver.resolve( Resolver.NO_OPTIONAL_RESOURCES );
+ Resource[] required = resolver.getRequiredResources();
+ for ( int i = 0; required != null && i < required.length; i++ )
+ {
+ json.append( "required", toJSON( required[i], bundles, false ) ); //$NON-NLS-1$
+ }
+ Resource[] optional = resolver.getOptionalResources();
+ for ( int i = 0; optional != null && i < optional.length; i++ )
+ {
+ json.append( "optional", toJSON( optional[i], bundles, false ) ); //$NON-NLS-1$
+ }
+ Reason[] unsatisfied = resolver.getUnsatisfiedRequirements();
+ for ( int i = 0; unsatisfied != null && i < unsatisfied.length; i++ )
+ {
+ json.append( "unsatisfied", new JSONObject() //$NON-NLS-1$
+ .put( "name", unsatisfied[i].getRequirement().getName() ) //$NON-NLS-1$
+ .put( "filter", unsatisfied[i].getRequirement().getFilter() ) //$NON-NLS-1$
+ .put( "optional", unsatisfied[i].getRequirement().isOptional() ) ); //$NON-NLS-1$
+ }
+ }
+ return json;
+ }
+
+
+ private JSONObject toJSON( final Property[] props ) throws JSONException
+ {
+ JSONObject json = new JSONObject();
+ for ( int i = 0; props != null && i < props.length; i++ )
+ {
+ json.put( props[i].getName(), props[i].getValue() );
+ }
+ return json;
+ }
+}
diff --git a/webconsole-plugins/obr/src/main/java/org/apache/felix/webconsole/plugins/obr/internal/FelixDeployer.java b/webconsole-plugins/obr/src/main/java/org/apache/felix/webconsole/plugins/obr/internal/FelixDeployer.java
new file mode 100644
index 0000000..bfed872
--- /dev/null
+++ b/webconsole-plugins/obr/src/main/java/org/apache/felix/webconsole/plugins/obr/internal/FelixDeployer.java
@@ -0,0 +1,114 @@
+/*
+ * 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.webconsole.plugins.obr.internal;
+
+
+import org.apache.felix.bundlerepository.Reason;
+import org.apache.felix.bundlerepository.Resolver;
+import org.apache.felix.bundlerepository.Resource;
+import org.apache.felix.webconsole.AbstractWebConsolePlugin;
+import org.osgi.service.log.LogService;
+
+
+class FelixDeployer implements Runnable
+{
+
+ private final Resolver obrResolver;
+
+ private final AbstractWebConsolePlugin logger;
+
+ private final boolean startBundles;
+
+ private final boolean optionalDependencies;
+
+ static void deploy(Resolver obrResolver, AbstractWebConsolePlugin logger, boolean startBundles,
+ boolean optionalDependencies)
+ {
+ final FelixDeployer d = new FelixDeployer(obrResolver, logger, startBundles, optionalDependencies);
+ final Thread t = new Thread(d, "OBR Bundle Deployer (Apache Felix API)");
+ t.start();
+ }
+
+ private FelixDeployer(Resolver obrResolver, AbstractWebConsolePlugin logger, boolean startBundles,
+ boolean optionalDependencies)
+ {
+ this.obrResolver = obrResolver;
+ this.logger = logger;
+ this.startBundles = startBundles;
+ this.optionalDependencies = optionalDependencies;
+ }
+
+ public void run()
+ {
+ int flags = 0;
+ flags += (startBundles ? Resolver.START : 0);
+ flags += (optionalDependencies ? 0 : Resolver.NO_OPTIONAL_RESOURCES);
+ try
+ {
+ if ( obrResolver.resolve( flags ) )
+ {
+
+ logResource( "Installing Requested Resources", obrResolver.getAddedResources() );
+ logResource( "Installing Required Resources", obrResolver.getRequiredResources() );
+ logResource( "Installing Optional Resources", obrResolver.getOptionalResources() );
+
+ obrResolver.deploy( flags );
+ }
+ else
+ {
+ logRequirements( "Cannot Install requested bundles due to unsatisfied requirements",
+ obrResolver.getUnsatisfiedRequirements() );
+ }
+ }
+ catch ( Exception ie )
+ {
+ logger.log( LogService.LOG_ERROR, "Cannot install bundles", ie );
+ }
+ }
+
+
+ private void logResource( String message, Resource[] res )
+ {
+ if ( res != null && res.length > 0 )
+ {
+ logger.log( LogService.LOG_INFO, message );
+ for ( int i = 0; i < res.length; i++ )
+ {
+ logger.log( LogService.LOG_INFO, " " + i + ": " + res[i].getSymbolicName() + ", "
+ + res[i].getVersion() );
+ }
+ }
+ }
+
+
+ private void logRequirements( String message, Reason[] reasons )
+ {
+ logger.log( LogService.LOG_ERROR, message );
+ for ( int i = 0; reasons != null && i < reasons.length; i++ )
+ {
+ String moreInfo = reasons[i].getRequirement().getComment();
+ if ( moreInfo == null )
+ {
+ moreInfo = reasons[i].getRequirement().getFilter().toString();
+ }
+ logger.log( LogService.LOG_ERROR, " " + i + ": " + reasons[i].getRequirement().getName() + " (" + moreInfo + ")" );
+ }
+ }
+
+}
diff --git a/webconsole-plugins/obr/src/main/java/org/apache/felix/webconsole/plugins/obr/internal/OsgiBundleRepositoryRenderHelper.java b/webconsole-plugins/obr/src/main/java/org/apache/felix/webconsole/plugins/obr/internal/OsgiBundleRepositoryRenderHelper.java
new file mode 100644
index 0000000..fb22b5b
--- /dev/null
+++ b/webconsole-plugins/obr/src/main/java/org/apache/felix/webconsole/plugins/obr/internal/OsgiBundleRepositoryRenderHelper.java
@@ -0,0 +1,256 @@
+/*
+ * 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.webconsole.plugins.obr.internal;
+
+
+import java.io.IOException;
+import java.net.URL;
+import javax.servlet.ServletException;
+import org.apache.felix.webconsole.AbstractWebConsolePlugin;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.obr.Capability;
+import org.osgi.service.obr.Repository;
+import org.osgi.service.obr.RepositoryAdmin;
+import org.osgi.service.obr.Requirement;
+import org.osgi.service.obr.Resolver;
+import org.osgi.service.obr.Resource;
+
+
+/**
+ * This class provides a plugin for rendering the available OSGi Bundle Repositories
+ * and the resources they provide.
+ */
+class OsgiBundleRepositoryRenderHelper extends AbstractBundleRepositoryRenderHelper
+{
+
+ OsgiBundleRepositoryRenderHelper( final AbstractWebConsolePlugin logger, final BundleContext bundleContext )
+ {
+ super( logger, bundleContext, RepositoryAdmin.class.getName() );
+ }
+
+
+ String getData( final String filter, final boolean details, Bundle[] bundles )
+ {
+ RepositoryAdmin admin = ( RepositoryAdmin ) getRepositoryAdmin();
+ if ( admin != null )
+ {
+ JSONObject json = new JSONObject();
+ try
+ {
+ json.put( "status", true ); //$NON-NLS-1$
+ json.put( "details", details ); //$NON-NLS-1$
+
+ final Repository repositories[] = admin.listRepositories();
+ for ( int i = 0; repositories != null && i < repositories.length; i++ )
+ {
+ json.append( "repositories", new JSONObject() //$NON-NLS-1$
+ .put( "lastModified", repositories[i].getLastModified() ) //$NON-NLS-1$
+ .put( "name", repositories[i].getName() ) //$NON-NLS-1$
+ .put( "url", repositories[i].getURL() ) ); //$NON-NLS-1$
+ }
+
+ Resource[] resources = admin.discoverResources( filter );
+ for ( int i = 0; resources != null && i < resources.length; i++ )
+ {
+ json.append( "resources", toJSON( resources[i], bundles, details ) ); //$NON-NLS-1$
+ }
+ }
+ catch ( JSONException e )
+ {
+ logger.log( "Failed to serialize repository to JSON object.", e );
+ }
+ catch ( Exception e )
+ {
+ logger.log( "Failed to parse filter '" + filter + "'", e );
+ try
+ {
+ String reason = "filter=" + filter;
+ if ( e.getMessage() != null )
+ {
+ reason = e.getMessage() + "(" + reason + ")";
+ }
+ json.put( "error", reason ); //$NON-NLS-1$
+ }
+ catch ( JSONException je )
+ {
+ // ignore
+ }
+ }
+
+ return json.toString();
+ }
+
+ // fall back to no data
+ return "{}"; //$NON-NLS-1$
+ }
+
+ void doAction( String action, String urlParam ) throws IOException, ServletException
+ {
+ RepositoryAdmin admin = ( RepositoryAdmin ) getRepositoryAdmin();
+ Repository[] repos = admin.listRepositories();
+ Repository repo = getRepository( repos, urlParam );
+
+ URL uri = repo != null ? repo.getURL() : new URL( urlParam );
+
+ if ( "delete".equals( action ) ) //$NON-NLS-1$
+ {
+ if ( !admin.removeRepository( uri ) )
+ {
+ throw new ServletException( "Failed to remove repository with URL " + uri );
+ }
+ }
+ else if ( "add".equals( action ) || "refresh".equals( action ) ) //$NON-NLS-1$ //$NON-NLS-2$
+ {
+ try
+ {
+ admin.addRepository( uri );
+ }
+ catch ( IOException e )
+ {
+ throw e;
+ }
+ catch ( Exception e )
+ {
+ throw new ServletException( "Failed to " + action + " repository " + uri + ": " + e.toString() );
+ }
+
+ }
+ }
+
+
+ final void doDeploy( String[] bundles, boolean start, boolean optional )
+ {
+ // check whether we have to do something
+ if ( bundles == null || bundles.length == 0 )
+ {
+ logger.log( "No resources to deploy" );
+ return;
+ }
+
+ RepositoryAdmin repoAdmin = ( RepositoryAdmin ) getRepositoryAdmin();
+ Resolver resolver = repoAdmin.resolver();
+
+ // prepare the deployment
+ for ( int i = 0; i < bundles.length; i++ )
+ {
+ String bundle = bundles[i];
+ if ( bundle == null || bundle.equals( "-" ) )
+ {
+ continue;
+ }
+
+ String filter = "(id=" + bundle + ")";
+ Resource[] resources = repoAdmin.discoverResources( filter );
+ if ( resources != null && resources.length > 0 )
+ {
+ resolver.add( resources[0] );
+ }
+ }
+
+ OsgiDeployer.deploy( resolver, logger, start );
+ }
+
+
+ private final Repository getRepository( Repository[] repos, String repositoryUrl )
+ {
+ if ( repositoryUrl == null || repositoryUrl.length() == 0 )
+ {
+ return null;
+ }
+
+ for ( int i = 0; i < repos.length; i++ )
+ {
+ if ( repositoryUrl.equals( repos[i].getURL().toString() ) )
+ {
+ return repos[i];
+ }
+ }
+
+ return null;
+ }
+
+
+ private final JSONObject toJSON( Resource resource, Bundle[] bundles, boolean details ) throws JSONException
+ {
+ final String symbolicName = resource.getSymbolicName();
+ final String version = resource.getVersion().toString();
+ boolean installed = false;
+ for ( int i = 0; symbolicName != null && !installed && bundles != null && i < bundles.length; i++ )
+ {
+ final String ver = ( String ) bundles[i].getHeaders( "" ).get( Constants.BUNDLE_VERSION ); //$NON-NLS-1$
+ installed = symbolicName.equals( bundles[i].getSymbolicName() ) && version.equals( ver );
+ }
+ JSONObject json = new JSONObject( resource.getProperties() ) //
+ .put( "id", resource.getId() ) // //$NON-NLS-1$
+ .put( "presentationname", resource.getPresentationName() ) // //$NON-NLS-1$
+ .put( "symbolicname", symbolicName ) // //$NON-NLS-1$
+ .put( "url", resource.getURL() ) // //$NON-NLS-1$
+ .put( "version", version ) // //$NON-NLS-1$
+ .put( "categories", resource.getCategories() ) // //$NON-NLS-1$
+ .put( "installed", installed ); //$NON-NLS-1$
+
+ if ( details )
+ {
+ Capability[] caps = resource.getCapabilities();
+ for ( int i = 0; caps != null && i < caps.length; i++ )
+ {
+ json.append( "capabilities", new JSONObject() //$NON-NLS-1$
+ .put( "name", caps[i].getName() ) //$NON-NLS-1$
+ .put( "properties", new JSONObject( caps[i].getProperties() ) ) ); //$NON-NLS-1$
+ }
+ Requirement[] reqs = resource.getRequirements();
+ for ( int i = 0; reqs != null && i < reqs.length; i++ )
+ {
+ json.append( "requirements", new JSONObject() //$NON-NLS-1$
+ .put( "name", reqs[i].getName() ) //$NON-NLS-1$
+ .put( "filter", reqs[i].getFilter() ) //$NON-NLS-1$
+ .put( "optional", reqs[i].isOptional() ) ); //$NON-NLS-1$
+ }
+
+ final RepositoryAdmin admin = ( RepositoryAdmin ) getRepositoryAdmin();
+ Resolver resolver = admin.resolver();
+ resolver.add( resource );
+ resolver.resolve(); // (Resolver.NO_OPTIONAL_RESOURCES);
+ Resource[] required = resolver.getRequiredResources();
+ for ( int i = 0; required != null && i < required.length; i++ )
+ {
+ json.append( "required", toJSON( required[i], bundles, false ) ); //$NON-NLS-1$
+ }
+ Resource[] optional = resolver.getOptionalResources();
+ for ( int i = 0; optional != null && i < optional.length; i++ )
+ {
+ json.append( "optional", toJSON( optional[i], bundles, false ) ); //$NON-NLS-1$
+ }
+ Requirement/*Reason*/[] unsatisfied = resolver.getUnsatisfiedRequirements();
+ for ( int i = 0; unsatisfied != null && i < unsatisfied.length; i++ )
+ {
+ json.append( "unsatisfied", new JSONObject() //$NON-NLS-1$
+ .put( "name", unsatisfied[i].getName() ) //$NON-NLS-1$
+ .put( "filter", unsatisfied[i].getFilter() ) //$NON-NLS-1$
+ .put( "optional", unsatisfied[i].isOptional() ) ); //$NON-NLS-1$
+ }
+ }
+ return json;
+ }
+
+}
diff --git a/webconsole-plugins/obr/src/main/java/org/apache/felix/webconsole/plugins/obr/internal/OsgiDeployer.java b/webconsole-plugins/obr/src/main/java/org/apache/felix/webconsole/plugins/obr/internal/OsgiDeployer.java
new file mode 100644
index 0000000..e20da6f
--- /dev/null
+++ b/webconsole-plugins/obr/src/main/java/org/apache/felix/webconsole/plugins/obr/internal/OsgiDeployer.java
@@ -0,0 +1,110 @@
+/*
+ * 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.webconsole.plugins.obr.internal;
+
+
+import org.apache.felix.webconsole.AbstractWebConsolePlugin;
+import org.osgi.service.log.LogService;
+import org.osgi.service.obr.Requirement;
+import org.osgi.service.obr.Resolver;
+import org.osgi.service.obr.Resource;
+
+
+class OsgiDeployer implements Runnable
+{
+
+ private final Resolver obrResolver;
+
+ private final AbstractWebConsolePlugin logger;
+
+ private final boolean startBundles;
+
+
+ OsgiDeployer( Resolver obrResolver, AbstractWebConsolePlugin logger, boolean startBundles )
+ {
+ this.obrResolver = obrResolver;
+ this.logger = logger;
+ this.startBundles = startBundles;
+ }
+
+ static void deploy( Resolver obrResolver, AbstractWebConsolePlugin logger, boolean startBundles )
+ {
+ final OsgiDeployer d = new OsgiDeployer( obrResolver, logger, startBundles );
+ final Thread t = new Thread( d, "OBR Bundle Deployer (OSGi API)" );
+ t.start();
+ }
+
+ /**
+ * @see java.lang.Runnable#run()
+ */
+ public void run()
+ {
+ try
+ {
+ if ( obrResolver.resolve() )
+ {
+
+ logResource( logger, "Installing Requested Resources", obrResolver.getAddedResources() );
+ logResource( logger, "Installing Required Resources", obrResolver.getRequiredResources() );
+ logResource( logger, "Installing Optional Resources", obrResolver.getOptionalResources() );
+
+ obrResolver.deploy( startBundles );
+ }
+ else
+ {
+ logRequirements( logger, "Cannot Install requested bundles due to unsatisfied requirements",
+ obrResolver.getUnsatisfiedRequirements() );
+ }
+ }
+ catch ( Exception ie )
+ {
+ logger.log( LogService.LOG_ERROR, "Cannot install bundles", ie );
+ }
+ }
+
+
+ public static void logResource( AbstractWebConsolePlugin logger, String message, Resource[] res )
+ {
+ if ( res != null && res.length > 0 )
+ {
+ logger.log( LogService.LOG_INFO, message );
+ for ( int i = 0; i < res.length; i++ )
+ {
+ logger.log( LogService.LOG_INFO, " " + i + ": " + res[i].getSymbolicName() + ", "
+ + res[i].getVersion() );
+ }
+ }
+ }
+
+
+ public static void logRequirements( AbstractWebConsolePlugin logger, String message, Requirement[] reasons )
+ {
+ logger.log( LogService.LOG_ERROR, message );
+ for ( int i = 0; reasons != null && i < reasons.length; i++ )
+ {
+ String moreInfo = reasons[i].getComment();
+ if ( moreInfo == null )
+ {
+ moreInfo = reasons[i].getFilter().toString();
+ }
+ logger.log( LogService.LOG_ERROR, " " + i + ": " + reasons[i].getName() + " (" + moreInfo + ")" );
+ }
+ }
+
+}
diff --git a/webconsole-plugins/obr/src/main/java/org/apache/felix/webconsole/plugins/obr/internal/WebConsolePlugin.java b/webconsole-plugins/obr/src/main/java/org/apache/felix/webconsole/plugins/obr/internal/WebConsolePlugin.java
new file mode 100644
index 0000000..c80a88c
--- /dev/null
+++ b/webconsole-plugins/obr/src/main/java/org/apache/felix/webconsole/plugins/obr/internal/WebConsolePlugin.java
@@ -0,0 +1,323 @@
+/*
+ * 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.webconsole.plugins.obr.internal;
+
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.felix.webconsole.DefaultVariableResolver;
+import org.apache.felix.webconsole.SimpleWebConsolePlugin;
+import org.apache.felix.webconsole.WebConsoleUtil;
+
+
+/**
+ * This class provides a plugin for rendering the available OSGi Bundle Repositories
+ * and the resources they provide.
+ */
+class WebConsolePlugin extends SimpleWebConsolePlugin
+{
+ private static final String LABEL = "obr"; //$NON-NLS-1$
+ private static final String TITLE = "%obr.pluginTitle"; //$NON-NLS-1$
+ private static final String CSS[] = { "/" + LABEL + "/res/plugin.css" }; //$NON-NLS-1$ //$NON-NLS-2$
+
+ // templates
+ private final String TEMPLATE;
+
+ private AbstractBundleRepositoryRenderHelper helper;
+
+
+ /**
+ *
+ */
+ public WebConsolePlugin()
+ {
+ super( LABEL, TITLE, CSS );
+
+ // load templates
+ TEMPLATE = readTemplateFile("/res/plugin.html"); //$NON-NLS-1$
+ }
+
+
+ /**
+ * @see org.apache.felix.webconsole.SimpleWebConsolePlugin#deactivate()
+ */
+ public void deactivate()
+ {
+ if ( helper != null )
+ {
+ helper.dispose();
+ helper = null;
+ }
+
+ super.deactivate();
+ }
+
+
+ /**
+ * @see org.apache.felix.webconsole.AbstractWebConsolePlugin#renderContent(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+ */
+ protected void renderContent( HttpServletRequest request, HttpServletResponse response ) throws IOException
+ {
+ // prepare variables
+ DefaultVariableResolver vars = ( ( DefaultVariableResolver ) WebConsoleUtil.getVariableResolver( request ) );
+ vars.put( "__data__", getData( request ) ); //$NON-NLS-1$
+
+ response.getWriter().print( TEMPLATE );
+ }
+
+
+ /**
+ * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+ */
+ protected void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException,
+ IOException
+ {
+ if ( !hasRepositoryAdmin() )
+ {
+ response.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "RepositoryAdmin service is missing" );
+ return;
+ }
+
+ final String action = request.getParameter( "action" ); //$NON-NLS-1$
+ final String deploy = request.getParameter( "deploy" ); //$NON-NLS-1$
+ final String deploystart = request.getParameter( "deploystart" ); //$NON-NLS-1$
+ final String optional = request.getParameter( "optional" ); //$NON-NLS-1$
+
+ if ( action != null )
+ {
+ doAction( action, request.getParameter( "url" ) ); //$NON-NLS-1$
+ response.getWriter().print( getData( request ) );
+ return;
+ }
+
+ if ( deploy != null || deploystart != null )
+ {
+ doDeploy( request.getParameterValues( "bundle" ), deploystart != null, optional != null ); //$NON-NLS-1$
+ doGet( request, response );
+ return;
+ }
+
+ super.doPost( request, response );
+ }
+
+
+ AbstractBundleRepositoryRenderHelper getHelper()
+ {
+ if ( helper == null )
+ {
+ try
+ {
+ helper = new FelixBundleRepositoryRenderHelper( this, getBundleContext() );
+ }
+ catch ( Throwable felixt )
+ {
+ // ClassNotFoundException, ClassDefNotFoundError
+
+ try
+ {
+ helper = new OsgiBundleRepositoryRenderHelper( this, getBundleContext() );
+ }
+ catch ( Throwable osgit )
+ {
+ // ClassNotFoundException, ClassDefNotFoundError
+ }
+ }
+ }
+
+ return helper;
+ }
+
+
+ private boolean hasRepositoryAdmin()
+ {
+ AbstractBundleRepositoryRenderHelper helper = getHelper();
+ return helper != null && helper.hasRepositoryAdmin();
+ }
+
+
+ private String getData( final HttpServletRequest request )
+ {
+ AbstractBundleRepositoryRenderHelper helper = getHelper();
+ if ( helper == null || !helper.hasRepositoryAdmin() )
+ {
+ return "{}"; //$NON-NLS-1$
+ }
+
+ RequestInfo info = new RequestInfo( request );
+
+ final String filter;
+ String list = info.getList();
+ if ( list != null )
+ {
+ if ( "-".equals( list ) ) //$NON-NLS-1$
+ {
+ StringBuffer sb = new StringBuffer( "(!(|" ); //$NON-NLS-1$
+ for ( int c = 0; c < 26; c++ )
+ {
+ sb.append( "(presentationname=" ).append( ( char ) ( 'a' + c ) ) //$NON-NLS-1$
+ .append( "*)(presentationname=" ).append( ( char ) ( 'A' + c ) ) //$NON-NLS-1$
+ .append( "*)" ); //$NON-NLS-1$
+ }
+ sb.append( "))" ); //$NON-NLS-1$
+ filter = sb.toString();
+ }
+ else
+ {
+ filter = "(|(presentationname=" + list.toLowerCase() //$NON-NLS-1$
+ + "*)(presentationname=" + list.toUpperCase() + "*))"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ else
+ {
+ String query = info.getQuery();
+ if ( query != null )
+ {
+ if ( query.indexOf( '=' ) > 0 )
+ {
+ if ( query.startsWith( "(" ) ) //$NON-NLS-1$
+ {
+ filter = query;
+ }
+ else
+ {
+ filter = "(" + query + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ else
+ {
+ filter = "(|(presentationame=*" + query + "*)(symbolicname=*" + query + "*))"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ }
+ else
+ {
+ StringBuffer sb = new StringBuffer( "(&" ); //$NON-NLS-1$
+ for ( Enumeration e = request.getParameterNames(); e.hasMoreElements(); )
+ {
+ String k = ( String ) e.nextElement();
+ String v = request.getParameter( k );
+ if ( v != null && v.length() > 0
+ && !"details".equals( k ) //$NON-NLS-1$
+ && !"deploy".equals( k ) //$NON-NLS-1$
+ && !"deploystart".equals( k ) //$NON-NLS-1$
+ && !"bundle".equals( k ) //$NON-NLS-1$
+ && !"optional".equals( k ) ) //$NON-NLS-1$
+ {
+ sb.append( '(' ).append( k ).append( '=' ).append( v ).append( ')' );
+ }
+ }
+ sb.append( ')' );
+ filter = (sb.length() > 3) ? sb.toString() : null;
+ }
+ }
+
+ return helper.getData( filter, info.showDetails(), getBundleContext().getBundles() );
+ }
+
+
+ private void doAction( String action, String urlParam ) throws IOException, ServletException
+ {
+ AbstractBundleRepositoryRenderHelper helper = getHelper();
+ if ( helper != null )
+ {
+ helper.doAction( action, urlParam );
+ }
+ }
+
+
+ private void doDeploy( String[] bundles, boolean start, boolean optional )
+ {
+ AbstractBundleRepositoryRenderHelper helper = getHelper();
+ if ( helper != null )
+ {
+ helper.doDeploy( bundles, start, optional );
+ }
+ }
+
+ private static class RequestInfo {
+
+ private final HttpServletRequest request;
+
+ private boolean details;
+ private String query;
+ private String list;
+
+
+ RequestInfo( final HttpServletRequest request )
+ {
+ this.request = request;
+ }
+
+
+ boolean showDetails()
+ {
+ getQuery();
+ return details;
+ }
+
+
+ String getQuery()
+ {
+ if ( query == null )
+ {
+ String query = WebConsoleUtil.urlDecode( request.getParameter( "query" ) ); //$NON-NLS-1$
+ boolean details = false;
+ if ( query == null && request.getPathInfo().length() > 5 )
+ {
+ // cut off "/obr/" prefix (might want to use getTitle ??)
+ String path = request.getPathInfo().substring( 5 );
+ int slash = path.indexOf( '/' );
+ if ( slash < 0 )
+ {
+ // symbolic name only, version ??
+ query = "(symbolicname=" + path + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ else
+ {
+ query = "(&(symbolicname=" + path.substring( 0, slash ) //$NON-NLS-1$
+ + ")(version=" + path.substring( slash + 1 ) + "))"; //$NON-NLS-1$ //$NON-NLS-2$
+ details = true;
+ }
+ }
+
+ this.query = query;
+ this.details = details;
+ }
+ return query;
+ }
+
+
+ String getList()
+ {
+ if ( list == null )
+ {
+ list = WebConsoleUtil.urlDecode( request.getParameter( "list" ) ); //$NON-NLS-1$
+ if ( list == null && !request.getParameterNames().hasMoreElements() && getQuery() == null )
+ {
+ list = "a"; //$NON-NLS-1$
+ }
+ }
+ return list;
+ }
+ }
+}
diff --git a/webconsole-plugins/obr/src/main/native2ascii/OSGI-INF/l10n/bundle_bg.properties b/webconsole-plugins/obr/src/main/native2ascii/OSGI-INF/l10n/bundle_bg.properties
new file mode 100644
index 0000000..89b6aae
--- /dev/null
+++ b/webconsole-plugins/obr/src/main/native2ascii/OSGI-INF/l10n/bundle_bg.properties
@@ -0,0 +1,49 @@
+#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.
+
+#
+# Web Console strings for reference all strings here are commented.
+# This file may be used to produce a translation of the strings
+#
+# Note that properties files are ISO-8859-1 encoded. To provide translations
+# for languages requiring different character encodings, you may use the
+# native2ascii Maven Plugin from http://mojo.codehaus.org/native2ascii-maven-plugin/
+# to translate the natively encoded files to ISO-8859-1 during bundle build
+#
+# Translations requiring non-ISO-8859-1 encoding are placed in the
+# src/main/native2ascii/OSGI-INF/l10n folder and are converted using said
+# plugin while building the bundle
+# native2ascii -encoding utf-8 bundle_bg.raw_properties bundle_bg.properties
+
+# OBR Plugin
+obr.pluginTitle=OSGi Хранилище
+obr.status.ok=Apache Bundle Repository услъгата е достъпна.
+obr.status.no=Нуждаете се от Apache Bundle Repository услугата за да се възползвате от тази функционалност!
+obr.version.select=Изберете версия...
+obr.repo.title=Бъндъл хранилище
+obr.action.add=Добавяне
+obr.action.search=Търсене
+obr.action.search.description=Въведе част от презентацията на бъндъла (име, описание, версия ...), \
+ или валиден OSGi филтър
+obr.action.deploy=Инсталиране на избраните
+obr.action.deploystart=Инсталиране и стартиране на избраните
+obr.repo.name=Име
+obr.repo.url=Адрес
+obr.repo.lastModified=Последна промяна
+obr.repo.actions=Действия
+obr.res.title=Достъпни ресурси
+obr.res.name=Име на ресурса
+obr.res.installedVer=Инсталирана версия
+obr.error=Грешка при филтриране на ресурсите
diff --git a/webconsole-plugins/obr/src/main/native2ascii/OSGI-INF/l10n/bundle_de.properties b/webconsole-plugins/obr/src/main/native2ascii/OSGI-INF/l10n/bundle_de.properties
new file mode 100644
index 0000000..e99dfb5
--- /dev/null
+++ b/webconsole-plugins/obr/src/main/native2ascii/OSGI-INF/l10n/bundle_de.properties
@@ -0,0 +1,50 @@
+#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.
+
+#
+# Web Console strings for reference all strings here are commented.
+# This file may be used to produce a translation of the strings
+#
+# Note that properties files are ISO-8859-1 encoded. To provide translations
+# for languages requiring different character encodings, you may use the
+# native2ascii Maven Plugin from http://mojo.codehaus.org/native2ascii-maven-plugin/
+# to translate the natively encoded files to ISO-8859-1 during bundle build
+#
+# Translations requiring non-ISO-8859-1 encoding are placed in the
+# src/main/native2ascii/OSGI-INF/l10n folder and are converted using said
+# plugin while building the bundle
+#
+
+# OBR Plugin
+obr.pluginTitle=OSGi Repository
+obr.status.ok=Der Apache Bundle Repository Service ist aktiv und bereit.
+obr.status.no=Der Apache Bundle Repository Service ist nicht aktiv!
+obr.version.select=Wähle Version...
+obr.repo.title=Bundle Repositories
+obr.action.add=Hinzufügen
+obr.action.search=Suchen
+obr.action.search.description=Geben Sie einen Teil des Präsentations- oder \
+ symoblischen Namens der gesuchten Bundles oder einen gültigen OSGi \
+ Filter Ausdruck
+obr.action.deploy=Ausgewählte Installieren
+obr.action.deploystart=Ausgewählte Installieren und Starten
+obr.repo.name=Name
+obr.repo.url=URL
+obr.repo.lastModified=Letzte Änderung
+obr.repo.actions=Aktionen
+obr.res.title=Verfügbare Resourcen
+obr.res.name=Resource Name
+obr.res.installedVer=Installierte Version
+obr.error=Fehler bei der Auswahl der Resourcen
diff --git a/webconsole-plugins/obr/src/main/native2ascii/OSGI-INF/l10n/bundle_ru.properties b/webconsole-plugins/obr/src/main/native2ascii/OSGI-INF/l10n/bundle_ru.properties
new file mode 100644
index 0000000..f3cebef
--- /dev/null
+++ b/webconsole-plugins/obr/src/main/native2ascii/OSGI-INF/l10n/bundle_ru.properties
@@ -0,0 +1,49 @@
+#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.
+
+#
+# Web Console strings for reference all strings here are commented.
+# This file may be used to produce a translation of the strings
+#
+# Note that properties files are ISO-8859-1 encoded. To provide translations
+# for languages requiring different character encodings, you may use the
+# native2ascii Maven Plugin from http://mojo.codehaus.org/native2ascii-maven-plugin/
+# to translate the natively encoded files to ISO-8859-1 during bundle build
+#
+# Translations requiring non-ISO-8859-1 encoding are placed in the
+# src/main/native2ascii/OSGI-INF/l10n folder and are converted using said
+# plugin while building the bundle
+#
+
+# OBR Plugin
+obr.pluginTitle=Хранилище OSGi
+obr.status.ok=Сервис Apache Bundle Repository запущен и работает.
+obr.status.no=Сервис Apache Bundle Repository не найден!
+obr.version.select=Выберите версию...
+obr.repo.title=Хранилище модулей
+obr.action.add=Добавить
+obr.action.search=Поиск
+obr.action.search.description=Введите слово, содержащееся в описании модуля (или в имени, версии и т. д.) \
+ или правильный OSGi фильтр
+obr.action.deploy=Установить выбранное
+obr.action.deploystart=Установить и запустить выбранное
+obr.repo.name=Имя
+obr.repo.url=Адрес
+obr.repo.lastModified=Последнее изменение
+obr.repo.actions=Действия
+obr.res.title=Доступные ресурсы
+obr.res.name=Имя ресурса
+obr.res.installedVer=Установленная версия
+obr.error=Ошибка фильтрации ресурсов
diff --git a/webconsole-plugins/obr/src/main/resources/OSGI-INF/l10n/bundle.properties b/webconsole-plugins/obr/src/main/resources/OSGI-INF/l10n/bundle.properties
new file mode 100644
index 0000000..78812a5
--- /dev/null
+++ b/webconsole-plugins/obr/src/main/resources/OSGI-INF/l10n/bundle.properties
@@ -0,0 +1,49 @@
+#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.
+
+#
+# Web Console strings for reference all strings here are commented.
+# This file may be used to produce a translation of the strings
+#
+# Note that properties files are ISO-8859-1 encoded. To provide translations
+# for languages requiring different character encodings, you may use the
+# native2ascii Maven Plugin from http://mojo.codehaus.org/native2ascii-maven-plugin/
+# to translate the natively encoded files to ISO-8859-1 during bundle build
+#
+# Translations requiring non-ISO-8859-1 encoding are placed in the
+# src/main/native2ascii/OSGI-INF/l10n folder and are converted using said
+# plugin while building the bundle
+#
+
+# OBR Plugin
+obr.pluginTitle=OSGi Repository
+obr.status.ok=The Apache Bundle Repository service is up and running.
+obr.status.no=The Apache Bundle Repository service is not available!
+obr.version.select=Select Version...
+obr.repo.title=Bundle Repositories
+obr.action.add=Add
+obr.action.search=Search
+obr.action.search.description=Enter word contained in bundle presentation \
+ and/or symbolic name or a valid OSGi Filter Expression
+obr.action.deploy=Deploy Selected
+obr.action.deploystart=Deploy and Start Selected
+obr.repo.name=Name
+obr.repo.url=URL
+obr.repo.lastModified=Last Modified
+obr.repo.actions=Actions
+obr.res.title=Available Resources
+obr.res.name=Resource Name
+obr.res.installedVer=Installed Version
+obr.error=Error Filtering Resources
diff --git a/webconsole-plugins/obr/src/main/resources/res/plugin.css b/webconsole-plugins/obr/src/main/resources/res/plugin.css
new file mode 100644
index 0000000..a6ef84c
--- /dev/null
+++ b/webconsole-plugins/obr/src/main/resources/res/plugin.css
@@ -0,0 +1,6 @@
+label {
+ font-family: Verdana, Arial, sans-serif;
+ font-size: 1em;
+ font-style: normal;
+ font-weight: normal;
+}
\ No newline at end of file
diff --git a/webconsole-plugins/obr/src/main/resources/res/plugin.html b/webconsole-plugins/obr/src/main/resources/res/plugin.html
new file mode 100644
index 0000000..4037a24
--- /dev/null
+++ b/webconsole-plugins/obr/src/main/resources/res/plugin.html
@@ -0,0 +1,102 @@
+<script type="text/javascript" src="${pluginRoot}/res/plugin.js"></script>
+<script type="text/javascript">
+var i18n = {
+ status_ok : '${obr.status.ok}',
+ status_no : '${obr.status.no}',
+ selectVersion : '${obr.version.select}',
+ error : '${obr.error}'
+}
+var obrData = ${__data__};
+</script>
+
+<p class="statline">${obr.status.ok}</p>
+
+<div id="ifStatusOK" class="ui-helper-hidden">
+
+<div class="ui-widget-header ui-corner-top buttonGroup">
+ <span style="float: left; margin-left: 1em">${obr.repo.title}</span>
+ <input style="width: 50%" type="text" id="addRepoUri" />
+ <button id="addRepoBtn">${obr.action.add}</button>
+</div>
+
+<table id="repoTable" class="nicetable">
+ <thead>
+ <tr>
+ <th class="col_Name">${obr.repo.name}</th>
+ <th class="col_URL">${obr.repo.url}</th>
+ <th class="col_lastMod">${obr.repo.lastModified}</th>
+ <th class="col_Actions">${obr.repo.actions}</th>
+ </tr>
+ </thead>
+ <tbody> <!-- template: will be replaced dynamically by JS -->
+ <tr>
+ <td>name</td>
+ <td>url</td>
+ <td>date</td>
+ <td>
+ <ul class="icons ui-widget">
+ <li class="dynhover" title="${refresh}"><span class="ui-icon ui-icon-refresh"> </span></li>
+ <li class="dynhover" title="${delete}"><span class="ui-icon ui-icon-trash"> </span></li>
+ </ul>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<br/>
+
+<div class="ui-widget-header ui-corner-top buttonGroup">
+ <span style="float: left; margin-left: 1em">${obr.res.title}</span>
+ <span>
+ <a href="${pluginRoot}?list=a">A</a>
+ <a href="${pluginRoot}?list=b">B</a>
+ <a href="${pluginRoot}?list=c">C</a>
+ <a href="${pluginRoot}?list=d">D</a>
+ <a href="${pluginRoot}?list=e">E</a>
+ <a href="${pluginRoot}?list=f">F</a>
+ <a href="${pluginRoot}?list=g">G</a>
+ <a href="${pluginRoot}?list=h">H</a>
+ <a href="${pluginRoot}?list=i">I</a>
+ <a href="${pluginRoot}?list=j">J</a>
+ <a href="${pluginRoot}?list=k">K</a>
+ <a href="${pluginRoot}?list=l">L</a>
+ <a href="${pluginRoot}?list=m">M</a>
+ <a href="${pluginRoot}?list=n">N</a>
+ <a href="${pluginRoot}?list=o">O</a>
+ <a href="${pluginRoot}?list=p">P</a>
+ <a href="${pluginRoot}?list=q">Q</a>
+ <a href="${pluginRoot}?list=r">R</a>
+ <a href="${pluginRoot}?list=s">S</a>
+ <a href="${pluginRoot}?list=t">T</a>
+ <a href="${pluginRoot}?list=u">U</a>
+ <a href="${pluginRoot}?list=v">V</a>
+ <a href="${pluginRoot}?list=w">W</a>
+ <a href="${pluginRoot}?list=x">X</a>
+ <a href="${pluginRoot}?list=y">Y</a>
+ <a href="${pluginRoot}?list=z">Z</a>
+ <a href="${pluginRoot}?list=-">?</a>
+ </span>
+ <input type="text" id="searchField" title="${obr.action.search.description}"/>
+ <button id="searchBtn">${obr.action.search}</button>
+</div>
+
+<table id="resTable" class="nicetable">
+ <thead>
+ <tr>
+ <th class="col_ResName">${obr.res.name}</th>
+ <th class="col_VersionInst">${obr.res.installedVer}</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr><td colspan="2">dummy</td></tr>
+ </tbody>
+</table>
+
+<table id="detailsTable" class="nicetable ui-helper-hidden">
+ <tbody id="detailsTableBody">
+ </tbody>
+</table>
+
+<br/>
+
+</div> <!-- ifStatusOK -->
\ No newline at end of file
diff --git a/webconsole-plugins/obr/src/main/resources/res/plugin.js b/webconsole-plugins/obr/src/main/resources/res/plugin.js
new file mode 100644
index 0000000..159ccfa
--- /dev/null
+++ b/webconsole-plugins/obr/src/main/resources/res/plugin.js
@@ -0,0 +1,510 @@
+/*
+ * 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.
+ */
+
+var repoTable = false;
+var repoTableTemplate = false;
+var addRepoUri = false;
+var resTable = false;
+var searchField = false;
+var ifStatusOK = false;
+
+//This prototype is provided by the Mozilla foundation and
+//is distributed under the MIT license.
+//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license
+if (!Array.prototype.map)
+{
+ Array.prototype.map = function(fun /*, thisp*/)
+ {
+ var len = this.length;
+ if (typeof fun != "function")
+ throw new TypeError();
+
+ var res = new Array(len);
+ var thisp = arguments[1];
+ for (var i = 0; i < len; i++)
+ {
+ if (i in this)
+ res[i] = fun.call(thisp, this[i], i, this);
+ }
+
+ return res;
+ };
+}
+
+var uid = 0;
+function guid() {
+ uid = uid + 1;
+ return (0x10000 + uid).toString(16).substring(1) + (((1+Math.random())*0x10000)|0).toString(16).substring(1);
+}
+
+/* displays a date in the user's local timezone */
+function localTm(time) {
+ return (time ? new Date(time) : new Date()).toLocaleString();
+}
+
+function doRepoAction(action, url) {
+ if ( !url ) {
+ Xalert('Invalid URI: ' + url, 'Error');
+ } else {
+ $.post(pluginRoot, {
+ 'action' : action,
+ 'url' : url
+ }, renderData, 'json');
+ }
+}
+
+function showDetails( symbolicname, version ) {
+ window.location.href = pluginRoot + '/' + symbolicname + '/' + version;
+}
+
+function showVersions( symbolicname ) {
+ var _id = symbolicname.replace(/\./g, '_');
+ $("#block" + _id).append("<div id='pluginInlineVersions" + _id + "' style='margin-left: 4em'><ul/></div>");
+ $("#img" + _id).each(function() {
+ $(this).
+ removeClass('ui-icon-triangle-1-e').//right
+ addClass('ui-icon-triangle-1-s');//down
+ });
+ $("#entry" + _id).each(function() {
+ $(this).
+ unbind('click').
+ click(function() {hideVersions(symbolicname)}).
+ attr("title", "Hide Versions");
+ });
+ var versions = [];
+ for (var i in obrData.resources ) {
+ if (obrData.resources[i].symbolicname == symbolicname) {
+ versions.push(obrData.resources[i].version);
+ }
+ }
+ versions.sort();
+ for (var i in versions) {
+ var txt = "<li><a href='javascript: showDetails(\"" + symbolicname + "\",\"" + versions[i] + "\")'>" + versions[i] + "</a></li>";
+ $("#pluginInlineVersions" + _id + " > ul").append(txt);
+ }
+}
+
+function hideVersions( symbolicname ) {
+ var _id = symbolicname.replace(/\./g, '_');
+ $("#img" + _id).each(function() {
+ $(this).
+ removeClass('ui-icon-triangle-1-s').//down
+ addClass('ui-icon-triangle-1-e');//right
+ });
+ $("#pluginInlineVersions" + _id).each(function() {
+ $(this).
+ remove();
+ });
+ $("#entry" + _id).each(function() {
+ $(this).
+ unbind('click').
+ click(function() {showVersions(symbolicname)}).
+ attr("title", "Show Versions");
+ });
+}
+
+function renderResource(res) {
+ // proceed with resource
+ var _id = res.symbolicname.replace(/\./g, '_');
+ var _tr = resTable.find('#row' + _id);
+
+ if (_tr.length == 0) { // not created yet, create it
+ var blockElement = createElement('span', '', {
+ id: 'block' + _id
+ });
+ var titleElement = createElement('span', '', {
+ id: 'entry' + _id,
+ title: "Show Versions"
+ });
+ var inputElement = createElement('span', 'ui-icon ui-icon-triangle-1-e', {
+ id: 'img' + _id,
+ style: {display: "inline-block"}
+ });
+ blockElement.appendChild(titleElement);
+ titleElement.appendChild(inputElement);
+ titleElement.appendChild(text(" "));
+ if (res.presentationname) {
+ titleElement.appendChild(text(res.presentationname));
+ titleElement.appendChild(text(" ("));
+ titleElement.appendChild(text(res.symbolicname));
+ titleElement.appendChild(text(")"));
+ } else {
+ titleElement.appendChild(text(res.symbolicname));
+ }
+ $(titleElement).click(function() {showVersions(res.symbolicname)});
+
+ _tr = tr( null, { 'id' : 'row' + _id } , [
+ td( null, null, [ blockElement ] ),
+ td( null, null, [ text(res.installed ? res.version : '') ] )
+ ]);
+ resTable.append( _tr );
+ }
+}
+
+function getCapabilitiesByName(res, name) {
+ var caps = [];
+ for (var v in res.capabilities) {
+ if (res.capabilities[v].name == name) {
+ caps.push(res.capabilities[v]);
+ }
+ }
+ return caps;
+}
+
+function getRequirementsByName(res, name) {
+ var caps = [];
+ for (var v in res.requirements) {
+ if (res.requirements[v].name == name) {
+ caps.push(res.requirements[v]);
+ }
+ }
+ return caps;
+}
+
+function createDetailedTable(enclosing, name, headers, rows, callback) {
+ if (rows && rows.length > 0) {
+ var uuid = guid();
+ var title = createElement('span', null, null, [
+ createElement('span', 'ui-icon ui-icon-triangle-1-e', { id: "img"+uuid, style: {display: "inline-block"} }),
+ text(" "),
+ text(name)
+ ]);
+ enclosing.append(tr(null, null, [
+ td(null, null, [ title ]),
+ td(null, null, [ createElement('table', 'nicetable ui-widget ui-helper-hidden', { id: "alt1"+uuid }, [
+ createElement('thead', null, null, [
+ tr(null, null, headers.map(function(x) {
+ return th('ui-widget-header', null, [text(x)]);
+ }))
+ ]),
+ createElement('tbody', null, null,
+ rows.map(function(x) {
+ var values = callback(x);
+ var tds = values.map(function(x) {
+ return td(null, null, [x]);
+ });
+ return tr(null, null, tds);
+ })
+ )
+ ]),
+ createElement('span', null, { id: "alt2"+uuid }, [
+ text(rows.length)
+ ])
+ ])
+ ]));
+ $(title).
+ unbind('click').
+ click(function(event) {
+ event.preventDefault();
+ $("#img"+uuid).toggleClass('ui-icon-triangle-1-s').//down
+ toggleClass('ui-icon-triangle-1-e');//right
+ $("#alt1"+uuid).toggle();
+ $("#alt2"+uuid).toggle();
+ });
+ }
+}
+
+function trim(stringToTrim) {
+ return stringToTrim.replace(/^\s+|\s+$/g,"");
+}
+
+function parseSimpleFilter(filter) {
+ filter = filter.substring(1, filter.length-1);
+ var start = 0;
+ var pos = 0;
+ var c = filter.charAt(pos);
+ while (c != '~' && c != '<' && c != '>' && c != '=' && c != '(' && c != ')') {
+ if (c == '<' && filterChars[pos+1] == '*') {
+ break;
+ }
+ if (c == '*' && filterChars[pos+1] == '>') {
+ break;
+ }
+ pos++;
+ c = filter.charAt(pos);
+ }
+ if (pos == start) {
+ throw ("Missing attr: " + filter.substring(pos));
+ }
+
+ var attr = trim(filter.substring(start, pos));
+ var oper = filter.substring(pos, pos+2);
+ var value;
+ if (oper == '*>' || oper == '~=' || oper == '>=' || oper == '<=' || oper == '<*') {
+ value = trim(filter.substring(pos+2));
+ if (value == '') {
+ throw ("Missing value: " + filter.substring(pos));
+ }
+
+ return { operator: oper, operands: [ attr, value ]};
+ } else {
+ if (c != '=') {
+ throw ("Invalid operator: " + filter.substring(pos));
+ }
+ oper = '=';
+ value = filter.substring(pos+1);
+ if (value == '*' ) {
+ return { operator: '=*', operands: [ attr ]};
+ }
+ return { operator: '=', operands: [ attr, value ]};
+ }
+}
+
+function parseFilter(filter) {
+ if (filter.charAt(0) != "(" || filter.charAt(filter.length-1) != ")") {
+ throw "Wrong parenthesis: " + filter;
+ }
+ if (filter.charAt(1) == "!") {
+ return { operator: filter.charAt(1), operands: [ parseFilter(filter.substring(2, filter.length-1)) ] };
+ }
+ if (filter.charAt(1) == "|" || filter.charAt(1) == "&") {
+ var inner = filter.substring(2, filter.length-1);
+ var flts = inner.match(/\([^\(\)]*(\([^\(\)]*(\([^\(\)]*(\([^\(\)]*\))*[^\(\)]*\))*[^\(\)]*\))*[^\(\)]*\)/g);
+ return { operator: filter.charAt(1), operands: flts.map(function(x) { return parseFilter(x); }) };
+ }
+ return parseSimpleFilter(filter);
+}
+
+function simplify(filter) {
+ if (filter.operator == '&' || filter.operator == '|') {
+ filter.operands = filter.operands.map(function(x) { return simplify(x); });
+ } else if (filter.operator == '!') {
+ if (filter.operands[0].operator == '<=') {
+ filter.operator = '>';
+ filter.operands = filter.operands[0].operands;
+ } else if (filter.operands[0].operator == '>=') {
+ filter.operator = '<';
+ filter.operands = filter.operands[0].operands;
+ }
+ }
+ return filter;
+}
+
+function addRow(tbody, key, value) {
+ if (value) {
+ tbody.append( tr(null, null, [
+ td(null, null, [ text(key) ]),
+ td(null, null, [ text(value) ])
+ ]));
+ }
+}
+
+function renderDetailedResource(res) {
+ var tbody = $('#detailsTableBody');
+
+ tbody.append( tr(null, null, [
+ th('ui-widget-header', null, [
+ text("Resource")
+ ]),
+ th('ui-widget-header', null, [
+ createElement('form', 'button-group', { method: "post"}, [
+ createElement('input', null, { type: "hidden", name: "bundle", value: res.id}),
+ createElement('input', 'ui-state-default ui-corner-all', { type: "submit", name: "deploy", value: "Deploy" }, [ text("dummy")]),
+ createElement('input', 'ui-state-default ui-corner-all', { type: "submit", name: "deploystart", value: "Deploy and Start" }, [ text("dummy")]),
+ text(" "),
+ createElement('input', 'ui-state-default ui-corner-all', { id: "optional", type: "checkbox", name: "optional" }),
+ text(" "),
+ createElement('label', 'ui-widget', { 'for': "optional" }, [ text("deploy optional dependencies") ])
+ ])
+ ])
+ ]));
+
+ addRow(tbody, "Name", res.presentationname);
+ addRow(tbody, "Description", res.description);
+ addRow(tbody, "Symbolic name", res.symbolicname);
+ addRow(tbody, "Version", res.version);
+ addRow(tbody, "URI", res.uri);
+ addRow(tbody, "Documentation", res.documentation);
+ addRow(tbody, "Javadoc", res.javadoc);
+ addRow(tbody, "Source", res.source);
+ addRow(tbody, "License", res.license);
+ addRow(tbody, "Copyright", res.copyright);
+ addRow(tbody, "Size", res.size);
+
+ // Exported packages
+ createDetailedTable(tbody, "Exported packages", ["Package", "Version"],
+ getCapabilitiesByName(res, "package").sort(function(a,b) {
+ var pa = a.properties['package'], pb = b.properties['package']; return pa == pb ? 0 : pa < pb ? -1 : +1;
+ }),
+ function(p) {
+ return [ text(p.properties['package']), text(p.properties['version']) ];
+ });
+ // Exported services
+ createDetailedTable(tbody, "Exported services", ["Service"], getCapabilitiesByName(res, "service"), function(p) {
+ return [ text(p.properties['service']) ];
+ });
+ // Imported packages
+ createDetailedTable(tbody, "Imported packages", ["Package", "Version", "Optional"], getRequirementsByName(res, "package"), function(p) {
+ var f = parseFilter(p.filter);
+ simplify(f);
+ var n, vmin = "[0.0.0", vmax = "infinity)";
+ if (f.operator == '&') {
+ for (var i in f.operands) {
+ var fi = f.operands[i];
+ if (fi.operands[0] == 'package' && fi.operator == '=') {
+ n = fi.operands[1];
+ }
+ if (fi.operands[0] == 'version') {
+ if (fi.operator == '>=') {
+ vmin = '[' + fi.operands[1];
+ }
+ if (fi.operator == '>') {
+ vmin = '(' + fi.operands[1];
+ }
+ if (fi.operator == '<=') {
+ vmax = fi.operands[1] + "]";
+ }
+ if (fi.operator == '<') {
+ vmax = fi.operands[1] + ")";
+ }
+ }
+ }
+ }
+ return [ text(n ? n : p.filter), text(vmin + ", " + vmax), text(p.optional) ];
+ });
+ // Imported bundles
+ createDetailedTable(tbody, "Imported bundles", ["Bundle", "Version", "Optional"], getRequirementsByName(res, "bundle"), function(p) {
+ return [ text(p.filter), text(""), text(p.optional) ];
+ });
+ // Imported services
+ createDetailedTable(tbody, "Imported services", ["Service", "Optional"], getRequirementsByName(res, "service"), function(p) {
+ return [ text(p.filter), text(p.optional) ];
+ });
+ // Required dependencies
+ createDetailedTable(tbody, "Dependencies", ["Name", "Version"], res.required, function(p) {
+ var a = createElement('a', null, { href: (pluginRoot + '/' + p.symbolicname + '/' + p.version) });
+ a.appendChild(text(p.presentationname ? p.presentationname : p.symbolicname));
+ return [ a, text(p.version) ];
+ });
+ // Optional dependencies
+ createDetailedTable(tbody, "Optional Dependencies", ["Name", "Version"], res.optional, function(p) {
+ var a = createElement('a', null, { href: (pluginRoot + '/' + p.symbolicname + '/' + p.version) });
+ a.appendChild(text(p.presentationname ? p.presentationname : p.symbolicname));
+ return [ a, text(p.version) ];
+ });
+ // Unsatisfied requirements
+ createDetailedTable(tbody, "Unsatisfied Requirements", ["Requirement", "Optional"], res.unsatisfied, function(p) {
+ return [ text(p.filter), text(p.optional) ];
+ });
+
+// $('#detailsTableBody').append( tr(null, null, [ th('ui-widget-header', { colspan: 2 }, [ text("Resource") ]) ]) );
+// $('#detailsTableBody').append( tbody );
+}
+
+function renderRepository(repo) {
+ var _tr = repoTableTemplate.clone();
+ _tr.find('td:eq(0)').text( repo.name );
+ _tr.find('td:eq(1)').text( repo.url );
+ _tr.find('td:eq(2)').text( localTm(repo.lastModified) );
+ _tr.find('li:eq(0)').click(function() {
+ doRepoAction('refresh', repo.url);
+ });
+ _tr.find('li:eq(1)').click(function() {
+ doRepoAction('delete', repo.url);
+ });
+ repoTable.append(_tr);
+}
+
+function renderData() {
+ repoTable.empty();
+ resTable.empty();
+ if ( obrData.status ) {
+ $('.statline').html(i18n.status_ok);
+ ifStatusOK.removeClass('ui-helper-hidden');
+ for (var i in obrData.repositories ) {
+ renderRepository( obrData.repositories[i] );
+ }
+ if (obrData.details) {
+ $('#resTable').addClass('ui-helper-hidden');
+ $('#detailsTable').removeClass('ui-helper-hidden');
+ for (var i in obrData.resources ) {
+ renderDetailedResource( obrData.resources[i] );
+ }
+ } else if (obrData.resources) {
+ for (var i in obrData.resources ) {
+ renderResource( obrData.resources[i] );
+ }
+ } else if (obrData.error) {
+ _tr = tr( "ui-state-error", null , [
+ td( "ui-state-error-text", { 'colspan': '2' },
+ [ text(i18n.error + ": " + obrData.error) ] )
+ ]);
+ resTable.append( _tr );
+ }
+ } else {
+ $('.statline').html(i18n.status_no);
+ ifStatusOK.addClass('ui-helper-hidden');
+ }
+}
+
+
+$.extend({
+ getUrlVars: function(){
+ var vars = [], hash;
+ var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
+ for(var i = 0; i < hashes.length; i++)
+ {
+ var j = hashes[i].indexOf('=');
+ if (j > 0) {
+ var k = hashes[i].slice(0, j);
+ var v = hashes[i].slice(j + 1);
+ vars.push(k);
+ vars[k] = v;
+ } else {
+ vars.push(hashes[i]);
+ vars[hashes[i]] = true;
+ }
+ }
+ return vars;
+ },
+ getUrlVar: function(name){
+ return $.getUrlVars()[name];
+ }
+});
+
+$(document).ready( function() {
+ repoTable = $('#repoTable tbody');
+ repoTableTemplate = repoTable.find('tr').clone();
+ addRepoUri = $('#addRepoUri');
+ resTable = $('#resTable tbody').empty();
+ searchField = $('#searchField');
+ ifStatusOK = $('#ifStatusOK');
+
+ var query = $.getUrlVar('query');
+ if (query) {
+ searchField.val(decodeURIComponent(query));
+ }
+
+ $('#addRepoBtn').click(function(event) {
+ event.preventDefault();
+ doRepoAction('add', addRepoUri.val());
+ });
+ $('#searchBtn').click(function(event) {
+ event.preventDefault();
+ window.location.href = pluginRoot + '?query=' + encodeURIComponent(searchField.val());
+ });
+ searchField.keypress(function(event) {
+ if (event.keyCode == 13) {
+ event.preventDefault();
+ $('#searchBtn').click();
+ }
+ });
+
+ renderData();
+ initStaticWidgets();
+});
+