You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ace.apache.org by ma...@apache.org on 2013/04/02 15:26:44 UTC
svn commit: r1463529 [1/3] - in /ace/trunk: org.apache.ace.obr.metadata/
org.apache.ace.obr.servlet/ org.apache.ace.obr.storage/ org.apache.ace.obr/
org.apache.ace.obr/.settings/ org.apache.ace.obr/src/
org.apache.ace.obr/src/org/ org.apache.ace.obr/sr...
Author: marrs
Date: Tue Apr 2 13:26:43 2013
New Revision: 1463529
URL: http://svn.apache.org/r1463529
Log:
ACE-333 Initial merge of OBR projects.
Added:
ace/trunk/org.apache.ace.obr/
ace/trunk/org.apache.ace.obr/.classpath
ace/trunk/org.apache.ace.obr/.project
ace/trunk/org.apache.ace.obr/.settings/
ace/trunk/org.apache.ace.obr/.settings/org.eclipse.jdt.core.prefs
ace/trunk/org.apache.ace.obr/bnd.bnd
ace/trunk/org.apache.ace.obr/build.xml
ace/trunk/org.apache.ace.obr/metadata.bnd
ace/trunk/org.apache.ace.obr/servlet.bnd
ace/trunk/org.apache.ace.obr/src/
ace/trunk/org.apache.ace.obr/src/org/
ace/trunk/org.apache.ace.obr/src/org/apache/
ace/trunk/org.apache.ace.obr/src/org/apache/ace/
ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/
ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/metadata/
ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/metadata/MetadataGenerator.java
ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/metadata/bindex/
ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/metadata/bindex/Activator.java
ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/metadata/bindex/BIndexMetadataGenerator.java
ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/metadata/packageinfo
ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/servlet/
ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/servlet/Activator.java
ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/servlet/BundleServlet.java
ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/
ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/BundleStore.java
ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/file/
ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/file/Activator.java
ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/file/BundleFileStore.java
ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/file/constants/
ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/file/constants/OBRFileStoreConstants.java
ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/packageinfo
ace/trunk/org.apache.ace.obr/src/org/osgi/
ace/trunk/org.apache.ace.obr/src/org/osgi/impl/
ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/
ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/bindex/
ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/bindex/Index.java
ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/bindex/packageinfo
ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/obr/
ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/obr/resource/
ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/obr/resource/BundleInfo.java
ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/obr/resource/CapabilityImpl.java
ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/obr/resource/FilterImpl.java
ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/obr/resource/Manifest.java
ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/obr/resource/ManifestEntry.java
ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/obr/resource/Parameter.java
ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/obr/resource/RepositoryImpl.java
ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/obr/resource/RequirementImpl.java
ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/obr/resource/ResourceImpl.java
ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/obr/resource/StringSet.java
ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/obr/resource/Tag.java
ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/obr/resource/VersionRange.java
ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/obr/resource/packageinfo
ace/trunk/org.apache.ace.obr/src/org/osgi/service/
ace/trunk/org.apache.ace.obr/src/org/osgi/service/obr/
ace/trunk/org.apache.ace.obr/src/org/osgi/service/obr/Capability.java
ace/trunk/org.apache.ace.obr/src/org/osgi/service/obr/CapabilityProvider.java
ace/trunk/org.apache.ace.obr/src/org/osgi/service/obr/Repository.java
ace/trunk/org.apache.ace.obr/src/org/osgi/service/obr/RepositoryAdmin.java
ace/trunk/org.apache.ace.obr/src/org/osgi/service/obr/RepositoryPermission.java
ace/trunk/org.apache.ace.obr/src/org/osgi/service/obr/Requirement.java
ace/trunk/org.apache.ace.obr/src/org/osgi/service/obr/Resolver.java
ace/trunk/org.apache.ace.obr/src/org/osgi/service/obr/Resource.java
ace/trunk/org.apache.ace.obr/src/org/osgi/service/obr/packageinfo
ace/trunk/org.apache.ace.obr/storage.bnd
ace/trunk/org.apache.ace.obr/test/
ace/trunk/org.apache.ace.obr/test/org/
ace/trunk/org.apache.ace.obr/test/org/apache/
ace/trunk/org.apache.ace.obr/test/org/apache/ace/
ace/trunk/org.apache.ace.obr/test/org/apache/ace/obr/
ace/trunk/org.apache.ace.obr/test/org/apache/ace/obr/metadata/
ace/trunk/org.apache.ace.obr/test/org/apache/ace/obr/metadata/bindeximpl/
ace/trunk/org.apache.ace.obr/test/org/apache/ace/obr/metadata/bindeximpl/BindexMetadataTest.java
ace/trunk/org.apache.ace.obr/test/org/apache/ace/obr/metadata/bindeximpl/VersionRangeTest.java
ace/trunk/org.apache.ace.obr/test/org/apache/ace/obr/servlet/
ace/trunk/org.apache.ace.obr/test/org/apache/ace/obr/servlet/BundleServletTest.java
ace/trunk/org.apache.ace.obr/test/org/apache/ace/obr/servlet/MockBundleStore.java
ace/trunk/org.apache.ace.obr/test/org/apache/ace/obr/storage/
ace/trunk/org.apache.ace.obr/test/org/apache/ace/obr/storage/file/
ace/trunk/org.apache.ace.obr/test/org/apache/ace/obr/storage/file/BundleFileStoreTest.java
ace/trunk/org.apache.ace.obr/test/org/apache/ace/obr/storage/file/MockMetadataGenerator.java
Removed:
ace/trunk/org.apache.ace.obr.metadata/
ace/trunk/org.apache.ace.obr.servlet/
ace/trunk/org.apache.ace.obr.storage/
Added: ace/trunk/org.apache.ace.obr/.classpath
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.obr/.classpath?rev=1463529&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.obr/.classpath (added)
+++ ace/trunk/org.apache.ace.obr/.classpath Tue Apr 2 13:26:43 2013
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" output="bin_test" path="test"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="aQute.bnd.classpath.container"/>
+ <classpathentry kind="con" path="org.testng.TESTNG_CONTAINER"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
Added: ace/trunk/org.apache.ace.obr/.project
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.obr/.project?rev=1463529&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.obr/.project (added)
+++ ace/trunk/org.apache.ace.obr/.project Tue Apr 2 13:26:43 2013
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.apache.ace.obr</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>bndtools.core.bndbuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>bndtools.core.bndnature</nature>
+ </natures>
+</projectDescription>
Added: ace/trunk/org.apache.ace.obr/.settings/org.eclipse.jdt.core.prefs
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.obr/.settings/org.eclipse.jdt.core.prefs?rev=1463529&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.obr/.settings/org.eclipse.jdt.core.prefs (added)
+++ ace/trunk/org.apache.ace.obr/.settings/org.eclipse.jdt.core.prefs Tue Apr 2 13:26:43 2013
@@ -0,0 +1,11 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
Added: ace/trunk/org.apache.ace.obr/bnd.bnd
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.obr/bnd.bnd?rev=1463529&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.obr/bnd.bnd (added)
+++ ace/trunk/org.apache.ace.obr/bnd.bnd Tue Apr 2 13:26:43 2013
@@ -0,0 +1,12 @@
+-sub: *.bnd
+-buildpath: osgi.core,\
+ osgi.cmpn,\
+ org.apache.felix.dependencymanager,\
+ javax.servlet,\
+ org.apache.ace.authentication.api;version=latest,\
+ org.apache.ace.test;version=latest,\
+ org.apache.ace.deployment.provider.api;version=latest,\
+ org.apache.ace.deployment.provider.base;version=latest,\
+ kxml2;version=2.3.0
+
+
Added: ace/trunk/org.apache.ace.obr/build.xml
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.obr/build.xml?rev=1463529&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.obr/build.xml (added)
+++ ace/trunk/org.apache.ace.obr/build.xml Tue Apr 2 13:26:43 2013
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="project" default="build">
+
+ <!-- -->
+
+ <import file="../cnf/build.xml" />
+</project>
Added: ace/trunk/org.apache.ace.obr/metadata.bnd
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.obr/metadata.bnd?rev=1463529&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.obr/metadata.bnd (added)
+++ ace/trunk/org.apache.ace.obr/metadata.bnd Tue Apr 2 13:26:43 2013
@@ -0,0 +1,15 @@
+Private-Package: org.apache.ace.obr.metadata.bindex,\
+ org.kxml2.io,\
+ org.kxml2.kdom,\
+ org.kxml2.wap,\
+ org.kxml2.wap.syncml,\
+ org.kxml2.wap.wml,\
+ org.kxml2.wap.wv,\
+ org.xmlpull.v1,\
+ org.osgi.impl.bundle.bindex,\
+ org.osgi.impl.bundle.obr.resource
+Bundle-Activator: org.apache.ace.obr.metadata.bindex.Activator
+Export-Package: org.apache.ace.obr.metadata,\
+ org.osgi.impl.bundle.obr.resource,\
+ org.osgi.service.obr
+Bundle-Version: 1.0.0
Added: ace/trunk/org.apache.ace.obr/servlet.bnd
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.obr/servlet.bnd?rev=1463529&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.obr/servlet.bnd (added)
+++ ace/trunk/org.apache.ace.obr/servlet.bnd Tue Apr 2 13:26:43 2013
@@ -0,0 +1,3 @@
+Private-Package: org.apache.ace.obr.servlet
+Bundle-Activator: org.apache.ace.obr.servlet.Activator
+Bundle-Version: 1.0.0
Added: ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/metadata/MetadataGenerator.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/metadata/MetadataGenerator.java?rev=1463529&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/metadata/MetadataGenerator.java (added)
+++ ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/metadata/MetadataGenerator.java Tue Apr 2 13:26:43 2013
@@ -0,0 +1,37 @@
+/*
+ * 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.ace.obr.metadata;
+
+import java.io.File;
+import java.io.IOException;
+
+public interface MetadataGenerator
+{
+
+ /**
+ * Generates the repository.xml based upon the new set of Bundles in the given directory. The xml is created
+ * as result of this method in the given directory in a file called repository.xml.
+ * This methods creates the file in an atomic fashion (this includes retrying to overwrite an existing file until success).
+ *
+ * @param directory the location where to store the newly created repository.xml
+ *
+ * @throws java.io.IOException If I/O problems occur when generating the new meta data index file.
+ */
+ public void generateMetadata(File directory) throws IOException;
+}
\ No newline at end of file
Added: ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/metadata/bindex/Activator.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/metadata/bindex/Activator.java?rev=1463529&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/metadata/bindex/Activator.java (added)
+++ ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/metadata/bindex/Activator.java Tue Apr 2 13:26:43 2013
@@ -0,0 +1,43 @@
+/*
+ * 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.ace.obr.metadata.bindex;
+
+import org.apache.ace.obr.metadata.MetadataGenerator;
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+public class Activator extends DependencyActivatorBase {
+
+ @Override
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+ manager.add(createComponent()
+ .setInterface(MetadataGenerator.class.getName(), null)
+ .setImplementation(BIndexMetadataGenerator.class)
+ .add(createServiceDependency()
+ .setService(LogService.class)
+ .setRequired(false)));
+ }
+
+ @Override
+ public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+ // Nothing to be done
+ }
+}
\ No newline at end of file
Added: ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/metadata/bindex/BIndexMetadataGenerator.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/metadata/bindex/BIndexMetadataGenerator.java?rev=1463529&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/metadata/bindex/BIndexMetadataGenerator.java (added)
+++ ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/metadata/bindex/BIndexMetadataGenerator.java Tue Apr 2 13:26:43 2013
@@ -0,0 +1,163 @@
+/*
+ * 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.ace.obr.metadata.bindex;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+
+import org.apache.ace.obr.metadata.MetadataGenerator;
+import org.osgi.impl.bundle.bindex.Index;
+import org.osgi.service.log.LogService;
+
+public class BIndexMetadataGenerator implements MetadataGenerator {
+
+ private static final String INDEX_FILENAME = "repository";
+ private static final String INDEX_EXTENSION = ".xml";
+
+ private volatile LogService m_log; /* will be injected by dependencymanager */
+
+ public void generateMetadata(File directory) throws IOException {
+ if (directory.isDirectory()) {
+ File tempIndex;
+ File index = new File(directory, INDEX_FILENAME + INDEX_EXTENSION);
+ try {
+ tempIndex = File.createTempFile("repo", INDEX_EXTENSION, directory);
+ Index.main(new String[] { "-q", "-a", "-r", tempIndex.getAbsolutePath(), directory.getAbsolutePath() });
+ renameFile(tempIndex, index);
+ }
+ catch (IOException e) {
+ if (m_log != null) {
+ m_log.log(LogService.LOG_ERROR, "Unable to create temporary file for new repository index.", e);
+ }
+ throw e;
+ }
+ catch (InterruptedException e) {
+ if (m_log != null) {
+ m_log.log(LogService.LOG_ERROR, "Waiting for next attempt to move temporary repository index failed.", e);
+ }
+ // Make sure the thread's administration remains correct...
+ Thread.currentThread().interrupt();
+ }
+ catch (Exception e) {
+ if (m_log != null) {
+ m_log.log(LogService.LOG_ERROR, "Failed to generate new repository index.", e);
+ }
+ throw new IOException("Failed to generate new repository index. + (" + e.getMessage() + ")");
+ }
+ }
+ }
+
+ /**
+ * Renames a given source file to a new destination file, using Commons-IO.
+ * <p>This avoids the problem mentioned in ACE-155.</p>
+ *
+ * @param source the file to rename;
+ * @param dest the file to rename to.
+ */
+ private void renameFile(File source, File dest) throws IOException, InterruptedException {
+ boolean renameOK = false;
+ int attempts = 0;
+ while (!renameOK && (attempts++ < 10)) {
+ try {
+ renameOK = moveFile(source, dest);
+ }
+ catch (IOException e) {
+ // In all other cases, we assume the source file is still locked and cannot be removed;
+ Thread.sleep(1000);
+ }
+ }
+
+ if (!renameOK) {
+ if (m_log != null) {
+ m_log.log(LogService.LOG_ERROR, "Unable to move new repository index to it's final location.");
+ }
+ throw new IOException("Could not move temporary index file (" + source.getAbsolutePath() + ") to it's final location (" + dest.getAbsolutePath() + ")");
+ }
+ }
+
+ /**
+ * Moves a given source file to a destination location, effectively resulting in a rename.
+ *
+ * @param source the source file to move;
+ * @param dest the destination file to move the file to.
+ * @return <code>true</code> if the move succeeded.
+ * @throws IOException in case of I/O problems.
+ */
+ private boolean moveFile(File source, File dest) throws IOException {
+ final int bufferSize = 1024 * 1024; // 1MB
+
+ FileInputStream fis = null;
+ FileOutputStream fos = null;
+ FileChannel input = null;
+ FileChannel output = null;
+
+ try {
+ fis = new FileInputStream(source);
+ input = fis.getChannel();
+
+ fos = new FileOutputStream(dest);
+ output = fos.getChannel();
+
+ long size = input.size();
+ long pos = 0;
+ while (pos < size) {
+ pos += output.transferFrom(input, pos, Math.min(size - pos, bufferSize));
+ }
+ }
+ finally {
+ closeQuietly(fos);
+ closeQuietly(fis);
+ closeQuietly(output);
+ closeQuietly(input);
+ }
+
+ if (source.length() != dest.length()) {
+ throw new IOException("Failed to move file! Not all contents from '" + source + "' copied to '" + dest + "'!");
+ }
+
+ dest.setLastModified(source.lastModified());
+
+ if (!source.delete()) {
+ dest.delete();
+ throw new IOException("Failed to move file! Source file (" + source + ") locked?");
+ }
+
+ return true;
+ }
+
+ /**
+ * Safely closes a given resource, ignoring any I/O exceptions that might occur by this.
+ *
+ * @param resource the resource to close, can be <code>null</code>.
+ */
+ private void closeQuietly(Closeable resource) {
+ try {
+ if (resource != null) {
+ resource.close();
+ }
+ }
+ catch (IOException e) {
+ // Ignored...
+ }
+ }
+}
\ No newline at end of file
Added: ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/metadata/packageinfo
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/metadata/packageinfo?rev=1463529&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/metadata/packageinfo (added)
+++ ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/metadata/packageinfo Tue Apr 2 13:26:43 2013
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file
Added: ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/servlet/Activator.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/servlet/Activator.java?rev=1463529&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/servlet/Activator.java (added)
+++ ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/servlet/Activator.java Tue Apr 2 13:26:43 2013
@@ -0,0 +1,52 @@
+/*
+ * 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.ace.obr.servlet;
+
+import javax.servlet.Servlet;
+
+import org.apache.ace.obr.storage.BundleStore;
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+public class Activator extends DependencyActivatorBase {
+ public static final String PID = "org.apache.ace.obr.servlet";
+
+ @Override
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+ manager.add(createComponent()
+ .setInterface(Servlet.class.getName(), null)
+ .setImplementation(BundleServlet.class)
+ .add(createConfigurationDependency()
+ .setPropagate(true)
+ .setPid(PID))
+ .add(createServiceDependency()
+ .setService(BundleStore.class)
+ .setRequired(true))
+ .add(createServiceDependency()
+ .setService(LogService.class)
+ .setRequired(false)));
+ }
+
+ @Override
+ public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+ // do nothing
+ }
+}
\ No newline at end of file
Added: ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/servlet/BundleServlet.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/servlet/BundleServlet.java?rev=1463529&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/servlet/BundleServlet.java (added)
+++ ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/servlet/BundleServlet.java Tue Apr 2 13:26:43 2013
@@ -0,0 +1,332 @@
+/*
+ * 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.ace.obr.servlet;
+
+import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
+import static javax.servlet.http.HttpServletResponse.SC_CONFLICT;
+import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
+import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
+import static javax.servlet.http.HttpServletResponse.SC_OK;
+import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
+
+import java.io.Closeable;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Dictionary;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.ace.authentication.api.AuthenticationService;
+import org.apache.ace.obr.storage.BundleStore;
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.log.LogService;
+import org.osgi.service.useradmin.User;
+
+/**
+ * Provides access to the OBR through a REST-ish API.
+ */
+public class BundleServlet extends HttpServlet implements ManagedService {
+ private static final long serialVersionUID = 1L;
+
+ /** A boolean denoting whether or not authentication is enabled. */
+ private static final String KEY_USE_AUTHENTICATION = "authentication.enabled";
+
+ private static final int COPY_BUFFER_SIZE = 4096;
+
+ public static final String TEXT_MIMETYPE = "text/plain";
+
+ private volatile DependencyManager m_dm; // injected by Dependency Manager
+ private volatile LogService m_log; /* will be injected by dependencymanager */
+ private volatile BundleStore m_store; /* will be injected by dependencymanager */
+ private volatile AuthenticationService m_authService;
+
+ private volatile boolean m_useAuth = false;
+
+ @Override
+ public String getServletInfo() {
+ return "Apache ACE OBR Servlet";
+ }
+
+ public void updated(Dictionary settings) throws ConfigurationException {
+ if (settings != null) {
+ String useAuthString = (String) settings.get(KEY_USE_AUTHENTICATION);
+ if (useAuthString == null
+ || !("true".equalsIgnoreCase(useAuthString) || "false".equalsIgnoreCase(useAuthString))) {
+ throw new ConfigurationException(KEY_USE_AUTHENTICATION, "Missing or invalid value!");
+ }
+ boolean useAuth = Boolean.parseBoolean(useAuthString);
+
+ m_useAuth = useAuth;
+ }
+ else {
+ m_useAuth = false;
+ }
+ }
+
+ /**
+ * Called by Dependency Manager upon initialization of this component.
+ *
+ * @param comp the component to initialize, cannot be <code>null</code>.
+ */
+ protected void init(Component comp) {
+ comp.add(m_dm.createServiceDependency()
+ .setService(AuthenticationService.class)
+ .setRequired(m_useAuth)
+ .setInstanceBound(true)
+ );
+ }
+
+ /**
+ * Responds to POST requests sent to http://host:port/obr/resource by writing the received data to the bundle store.
+ * Will send out a response that contains one of the following status codes:
+ * <ul>
+ * <li><code>HttpServletResponse.SC_BAD_REQUEST</code> - if no resource was specified</li>
+ * <li><code>HttpServletResponse.SC_CONFLICT</code> - if the resource already exists</li>
+ * <li><code>HttpServletResponse.SC_INTERNAL_SERVER_ERROR</code> - if there was a problem storing the resource</li>
+ * <li><code>HttpServletResponse.SC_OK</code> - if all went fine</li>
+ * </ul>
+ */
+ @Override
+ protected void doPost(HttpServletRequest request, HttpServletResponse response) {
+ String path = request.getPathInfo();
+ if ((path == null) || (path.length() <= 1)) {
+ sendResponse(response, SC_BAD_REQUEST);
+ }
+ else {
+ String id = path.substring(1);
+ try {
+ if (m_store.put(id, request.getInputStream())) {
+ sendResponse(response, SC_OK);
+ }
+ else {
+ sendResponse(response, SC_CONFLICT);
+ }
+ }
+ catch (IOException e) {
+ m_log.log(LogService.LOG_WARNING, "Exception handling request: " + request.getRequestURL(), e);
+ sendResponse(response, SC_INTERNAL_SERVER_ERROR);
+ }
+ }
+ }
+
+ /**
+ * Responds to DELETE requests sent to http://host:port/obr/resource by deleting the file specified.
+ * Will send out a response that contains one of the following status codes:
+ * <br>
+ * <li><code>HttpServletResponse.SC_BAD_REQUEST</code> - if no resource was specified
+ * <li><code>HttpServletResponse.SC_NOT_FOUND</code> - if the specified resource did not exist
+ * <li><code>HttpServletResponse.SC_INTERNAL_SERVER_ERROR</code> - if there was a problem deleting the resource
+ * <li><code>HttpServletResponse.SC_OK</code> - if all went fine
+ */
+ @Override
+ protected void doDelete(HttpServletRequest request, HttpServletResponse response) {
+ String path = request.getPathInfo();
+ if ((path == null) || (path.length() <= 1)) {
+ sendResponse(response, SC_BAD_REQUEST);
+ }
+ else {
+ // Remove leading slash...
+ String id = path.substring(1);
+ try {
+ if (m_store.remove(id)) {
+ sendResponse(response, SC_OK);
+ }
+ else {
+ sendResponse(response, SC_NOT_FOUND);
+ }
+ }
+ catch (IOException e) {
+ m_log.log(LogService.LOG_WARNING, "Exception handling request: " + request.getRequestURL(), e);
+ sendResponse(response, SC_INTERNAL_SERVER_ERROR);
+ }
+ }
+ }
+
+ /**
+ * Responds to GET requests sent to http://host:port/obr/resource with a stream to the specified filename.
+ * Will send out a response that contains one of the following status codes:
+ * <br>
+ * <li><code>HttpServletResponse.SC_BAD_REQUEST</code> - if no resource is specified
+ * <li><code>HttpServletResponse.SC_INTERNAL_SERVER_ERROR</code> - if there was a problem storing the resource
+ * <li><code>HttpServletResponse.SC_OK</code> - if all went fine
+ * <br>
+ * The response will only contain the data of the requested resource if the status code of the response is
+ * <code>HttpServletResponse.SC_OK</code>.
+ */
+ @Override
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) {
+ String path = request.getPathInfo();
+ if ((path == null) || (path.length() <= 1)) {
+ sendResponse(response, SC_BAD_REQUEST);
+ }
+ else {
+ // Remove leasing slash...
+ String id = path.substring(1);
+
+ ServletOutputStream output = null;
+ InputStream fileStream = null;
+ try {
+ fileStream = m_store.get(id);
+ if (fileStream == null) {
+ sendResponse(response, HttpServletResponse.SC_NOT_FOUND);
+ }
+ else {
+ // send the bundle as stream to the caller
+ response.setContentType(TEXT_MIMETYPE);
+
+ output = response.getOutputStream();
+ byte[] buffer = new byte[COPY_BUFFER_SIZE];
+ for (int bytes = fileStream.read(buffer); bytes != -1; bytes = fileStream.read(buffer)) {
+ output.write(buffer, 0, bytes);
+ }
+ }
+ }
+ catch (EOFException ex) {
+ // ACE-260: lower log-level of this exception; as it is probably because the remote hung up early...
+ m_log.log(LogService.LOG_DEBUG, "EOF Exception in request: " + request.getRequestURL()
+ + "; probably the remote hung up early.");
+ }
+ catch (IOException ex) {
+ // ACE-260: all other exception are logged, as we might have a possible resource leak...
+ m_log.log(LogService.LOG_WARNING, "Exception in request: " + request.getRequestURL(), ex);
+ sendResponse(response, SC_INTERNAL_SERVER_ERROR);
+ }
+ finally {
+ closeSafely(fileStream, request);
+ closeSafely(output, request);
+ }
+ }
+ }
+
+ /**
+ * Responds to HEAD requests sent to http://host:port/obr/resource with a stream to the specified filename.
+ * Will send out a response that contains one of the following status codes:
+ * <br>
+ * <li><code>HttpServletResponse.SC_BAD_REQUEST</code> - if no resource is specified
+ * <li><code>HttpServletResponse.SC_INTERNAL_SERVER_ERROR</code> - if there was a problem storing the resource
+ * <li><code>HttpServletResponse.SC_NOT_FOUND</code> - if the requested resource does not exist
+ * <li><code>HttpServletResponse.SC_OK</code> - if all went fine
+ * <br>
+ * The response is empty in all situations.
+ */
+ @Override
+ protected void doHead(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ String path = request.getPathInfo();
+ if ((path == null) || (path.length() <= 1)) {
+ sendResponse(response, SC_BAD_REQUEST);
+ }
+ else {
+ // Remove leasing slash...
+ String id = path.substring(1);
+
+ ServletOutputStream output = null;
+ InputStream fileStream = null;
+ try {
+ // TODO extend the store with an exists method???
+ fileStream = m_store.get(id);
+ if (fileStream == null) {
+ sendResponse(response, HttpServletResponse.SC_NOT_FOUND);
+ }
+ else {
+ sendResponse(response, HttpServletResponse.SC_OK);
+ }
+ }
+ catch (IOException ex) {
+ // ACE-260: all other exception are logged, as we might have a possible resource leak...
+ m_log.log(LogService.LOG_WARNING, "Exception in request: " + request.getRequestURL(), ex);
+ sendResponse(response, SC_INTERNAL_SERVER_ERROR);
+ }
+ finally {
+ closeSafely(fileStream, request);
+ closeSafely(output, request);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ if (!authenticate(req)) {
+ // Authentication failed; don't proceed with the original request...
+ resp.sendError(SC_UNAUTHORIZED);
+ } else {
+ // Authentication successful, proceed with original request...
+ super.service(req, resp);
+ }
+ }
+
+ /**
+ * Authenticates, if needed the user with the information from the given request.
+ *
+ * @param request the request to obtain the credentials from, cannot be <code>null</code>.
+ * @return <code>true</code> if the authentication was successful, <code>false</code> otherwise.
+ */
+ private boolean authenticate(HttpServletRequest request) {
+ if (m_useAuth) {
+ User user = m_authService.authenticate(request);
+ if (user == null) {
+ m_log.log(LogService.LOG_INFO, "Authentication failure!");
+ }
+ return (user != null);
+ }
+ return true;
+ }
+
+ private void closeSafely(Closeable resource, HttpServletRequest request) {
+ if (resource != null) {
+ try {
+ resource.close();
+ }
+ catch (EOFException ex) {
+ // ACE-260: lower log-level of this exception; as it is probably because the remote hung up early...
+ m_log.log(LogService.LOG_DEBUG, "EOF Exception trying to close stream: " + request.getRequestURL()
+ + "; probably the remote hung up early.");
+ }
+ catch (Exception ex) {
+ // ACE-260: all other exception are logged, as we might have a possible resource leak...
+ m_log.log(LogService.LOG_WARNING, "Exception trying to close stream: " + request.getRequestURL(), ex);
+ }
+ }
+ }
+
+ // send a response with the specified status code
+ private void sendResponse(HttpServletResponse response, int statusCode) {
+ sendResponse(response, statusCode, "");
+ }
+
+ // send a response with the specified status code and description
+ private void sendResponse(HttpServletResponse response, int statusCode, String description) {
+ try {
+ response.sendError(statusCode, description);
+ }
+ catch (Exception e) {
+ m_log.log(LogService.LOG_WARNING, "Unable to send response with status code '" + statusCode + "'", e);
+ }
+ }
+}
\ No newline at end of file
Added: ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/BundleStore.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/BundleStore.java?rev=1463529&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/BundleStore.java (added)
+++ ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/BundleStore.java Tue Apr 2 13:26:43 2013
@@ -0,0 +1,56 @@
+package org.apache.ace.obr.storage;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.osgi.service.cm.ManagedService;
+
+/*
+ * 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.
+ */
+
+public interface BundleStore extends ManagedService {
+
+ /**
+ * Returns an <code>InputStream</code> to the data of the specified resource.
+ *
+ * @param fileName Identifier of the requested resource.
+ * @return <code>InputStream</code> to the requested resource or <code>null</code> if no such resource is available.
+ * @throws java.io.IOException If there was a problem returning the requested resource.
+ */
+ public InputStream get(String fileName) throws IOException;
+
+ /**
+ * Stores the specified resource in the store.
+ *
+ * @param fileName Identifier of the resource.
+ * @param data The actual data of the resource.
+ * @return <code>true</code> if the resource was successfully stored, <code>false</code> if the resource already existed
+ * @throws java.io.IOException If there was a problem reading or writing the data of the resource.
+ */
+ public boolean put(String fileName, InputStream data) throws IOException;
+
+ /**
+ * Removes the specified resource from the store.
+ *
+ * @param filename Identifier of the resource.
+ * @return <code>true</code> if the resource was successfully removed, <code>false</code> if the resource was not present to begin with
+ * @throws java.io.IOException If there was a problem removing the data of the resource from the store.
+ */
+ public boolean remove(String filename) throws IOException;
+}
\ No newline at end of file
Added: ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/file/Activator.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/file/Activator.java?rev=1463529&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/file/Activator.java (added)
+++ ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/file/Activator.java Tue Apr 2 13:26:43 2013
@@ -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.
+ */
+package org.apache.ace.obr.storage.file;
+
+import org.apache.ace.obr.metadata.MetadataGenerator;
+import org.apache.ace.obr.storage.BundleStore;
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.log.LogService;
+
+public class Activator extends DependencyActivatorBase {
+ public static final String PID = "org.apache.ace.obr.storage.file";
+
+ @Override
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+ manager.add(createComponent()
+ .setInterface(BundleStore.class.getName(), null)
+ .setImplementation(BundleFileStore.class)
+ .add(createConfigurationDependency()
+ .setPid(PID))
+ .add(createServiceDependency()
+ .setService(MetadataGenerator.class)
+ .setRequired(true))
+ .add(createServiceDependency()
+ .setService(LogService.class)
+ .setRequired(false)));
+ }
+
+ @Override
+ public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+ // Nothing to do here
+ }
+}
\ No newline at end of file
Added: ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/file/BundleFileStore.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/file/BundleFileStore.java?rev=1463529&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/file/BundleFileStore.java (added)
+++ ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/file/BundleFileStore.java Tue Apr 2 13:26:43 2013
@@ -0,0 +1,313 @@
+/*
+ * 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.ace.obr.storage.file;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.channels.FileChannel;
+import java.util.Dictionary;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.ace.obr.metadata.MetadataGenerator;
+import org.apache.ace.obr.storage.BundleStore;
+import org.apache.ace.obr.storage.file.constants.OBRFileStoreConstants;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.log.LogService;
+
+/**
+ * This BundleStore retrieves the files from the file system. Via the Configurator the relative path is set, and all bundles and
+ * the repository.xml should be retrievable from that path (which will internally be converted to an absolute path).
+ */
+public class BundleFileStore implements BundleStore, ManagedService {
+
+ private static int BUFFER_SIZE = 8 * 1024;
+ private static final String REPOSITORY_XML = "repository.xml";
+
+ private final Map<String, Long> m_foundFiles = new ConcurrentHashMap<String, Long>();
+ private final Object m_dirLock = new Object();
+
+ private volatile MetadataGenerator m_metadata; /* will be injected by dependencymanager */
+ private volatile LogService m_log; /* will be injected by dependencymanager */
+
+ /** protected by m_dirLock. */
+ private File m_dir;
+
+ public void generateMetadata() throws IOException {
+ File dir = getWorkingDir();
+ File[] files = dir.listFiles();
+
+ m_metadata.generateMetadata(dir);
+
+ for (File current : files) {
+ m_foundFiles.put(current.getAbsolutePath(), current.lastModified() ^ current.length());
+ }
+ }
+
+ public InputStream get(String fileName) throws IOException {
+ if (REPOSITORY_XML.equals(fileName) && directoryChanged(getWorkingDir())) {
+ generateMetadata(); // might be called too often
+ }
+ FileInputStream result = null;
+ try {
+ result = new FileInputStream(createFile(fileName));
+ } catch (FileNotFoundException e) {
+ // Resource does not exist; notify caller by returning null...
+ }
+ return result;
+ }
+
+ public boolean put(String fileName, InputStream data) throws IOException {
+ final File file = createFile(fileName);
+
+ boolean success = false;
+ if (!file.exists()) {
+ File folder = file.getAbsoluteFile().getParentFile();
+ if (!folder.isDirectory() && !folder.mkdirs()) {
+ throw new IOException("Could not create folder " + folder);
+ }
+ try {
+ // the reason for first writing to a temporary file is that we want to minimize
+ // the window where someone could be looking at a "partial" file that is still being
+ // uploaded
+ downloadToFile(data, file);
+ success = true;
+ }
+ catch (IOException e) {
+ // if anything goes wrong while reading from the input stream or
+ // moving the file, delete the temporary file
+ }
+ }
+ return success;
+ }
+
+ public boolean remove(String fileName) throws IOException {
+ File file = createFile(fileName);
+
+ if (file.exists()) {
+ if (file.delete()) {
+ return true;
+ }
+ else {
+ throw new IOException("Unable to delete file (" + file.getAbsolutePath() + ")");
+ }
+ }
+
+ return false;
+ }
+
+ @SuppressWarnings("unchecked")
+ public void updated(Dictionary dict) throws ConfigurationException {
+ if (dict != null) {
+ String path = (String) dict.get(OBRFileStoreConstants.FILE_LOCATION_KEY);
+ if (path == null) {
+ throw new ConfigurationException(OBRFileStoreConstants.FILE_LOCATION_KEY, "Missing property");
+ }
+
+ File newDir = new File(path);
+ File curDir = getWorkingDir();
+
+ if (!newDir.equals(curDir)) {
+ if (!newDir.exists()) {
+ newDir.mkdirs();
+ }
+ else if (!newDir.isDirectory()) {
+ throw new ConfigurationException(OBRFileStoreConstants.FILE_LOCATION_KEY, "Is not a directory: " + newDir);
+ }
+
+ synchronized (m_dirLock) {
+ m_dir = newDir;
+ }
+
+ m_foundFiles.clear();
+ }
+ }
+ else {
+ // clean up after getting a null as dictionary, as the service is going to be pulled afterwards
+ m_foundFiles.clear();
+ }
+ }
+
+ /**
+ * Called by dependencymanager upon start of this component.
+ */
+ protected void start() {
+ try {
+ generateMetadata();
+ }
+ catch (IOException e) {
+ m_log.log(LogService.LOG_ERROR, "Could not generate initial meta data for bundle repository");
+ }
+ }
+
+ @SuppressWarnings("boxing")
+ private boolean directoryChanged(File dir) {
+ File[] files = dir.listFiles();
+
+ // if number of files changed, create new metadata
+ if (files.length != m_foundFiles.size()) {
+ return true;
+ }
+
+ // iterate over the current files
+ for (File current : files) {
+ Long modifiedDateAndLengthXOR = m_foundFiles.get(current.getAbsolutePath());
+ // if one of the current files is not in the old set of files, create new metadata
+ if (modifiedDateAndLengthXOR == null) {
+ return true;
+ }
+ // else if of one of the files the size or the date has been changed, create new metadata
+ if ((current.lastModified() ^ current.length()) != modifiedDateAndLengthXOR) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Downloads a given input stream to a temporary file and if done, moves it to its final location.
+ *
+ * @param source the input stream to download;
+ * @param dest the destination to write the downloaded file to.
+ * @throws IOException in case of I/O problems.
+ */
+ private void downloadToFile(InputStream source, File dest) throws IOException {
+ File tempFile = File.createTempFile("obr", ".tmp");
+
+ FileOutputStream fos = null;
+
+ try {
+ fos = new FileOutputStream(tempFile);
+
+ int read;
+ byte[] buffer = new byte[BUFFER_SIZE];
+ while ((read = source.read(buffer)) >= 0) {
+ fos.write(buffer, 0, read);
+ }
+ fos.flush();
+ fos.close();
+
+ if (!tempFile.renameTo(dest)) {
+ if (!moveFile(tempFile, dest)) {
+ throw new IOException("Failed to move file store to its destination!");
+ }
+ }
+ }
+ finally {
+ closeQuietly(fos);
+
+ tempFile.delete();
+ }
+ }
+
+ /**
+ * @return the working directory of this file store.
+ */
+ private File getWorkingDir() {
+ final File dir;
+ synchronized (m_dirLock) {
+ dir = m_dir;
+ }
+ return dir;
+ }
+
+ /**
+ * Moves a given source file to a destination location, effectively resulting in a rename.
+ *
+ * @param source the source file to move;
+ * @param dest the destination file to move the file to.
+ * @return <code>true</code> if the move succeeded.
+ * @throws IOException in case of I/O problems.
+ */
+ private boolean moveFile(File source, File dest) throws IOException {
+ final int bufferSize = 1024 * 1024; // 1MB
+
+ FileInputStream fis = null;
+ FileOutputStream fos = null;
+ FileChannel input = null;
+ FileChannel output = null;
+
+ try {
+ fis = new FileInputStream(source);
+ input = fis.getChannel();
+
+ fos = new FileOutputStream(dest);
+ output = fos.getChannel();
+
+ long size = input.size();
+ long pos = 0;
+ while (pos < size) {
+ pos += output.transferFrom(input, pos, Math.min(size - pos, bufferSize));
+ }
+ }
+ finally {
+ closeQuietly(fos);
+ closeQuietly(fis);
+ closeQuietly(output);
+ closeQuietly(input);
+ }
+
+ if (source.length() != dest.length()) {
+ throw new IOException("Failed to move file! Not all contents from '" + source + "' copied to '" + dest + "'!");
+ }
+
+ dest.setLastModified(source.lastModified());
+
+ if (!source.delete()) {
+ dest.delete();
+ throw new IOException("Failed to move file! Source file (" + source + ") locked?");
+ }
+
+ return true;
+ }
+
+ /**
+ * Safely closes a given resource, ignoring any I/O exceptions that might occur by this.
+ *
+ * @param resource the resource to close, can be <code>null</code>.
+ */
+ private void closeQuietly(Closeable resource) {
+ try {
+ if (resource != null) {
+ resource.close();
+ }
+ }
+ catch (IOException e) {
+ // Ignored...
+ }
+ }
+
+ /**
+ * Creates a {@link File} object with the given file name in the current working directory.
+ *
+ * @param fileName the name of the file.
+ * @return a {@link File} object, never <code>null</code>.
+ * @see #getWorkingDir()
+ */
+ private File createFile(String fileName) {
+ return new File(getWorkingDir(), fileName);
+ }
+}
\ No newline at end of file
Added: ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/file/constants/OBRFileStoreConstants.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/file/constants/OBRFileStoreConstants.java?rev=1463529&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/file/constants/OBRFileStoreConstants.java (added)
+++ ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/file/constants/OBRFileStoreConstants.java Tue Apr 2 13:26:43 2013
@@ -0,0 +1,24 @@
+/*
+ * 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.ace.obr.storage.file.constants;
+
+public interface OBRFileStoreConstants
+{
+ public static final String FILE_LOCATION_KEY = "fileLocation";
+}
\ No newline at end of file
Added: ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/packageinfo
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/packageinfo?rev=1463529&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/packageinfo (added)
+++ ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/packageinfo Tue Apr 2 13:26:43 2013
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file
Added: ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/bindex/Index.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/bindex/Index.java?rev=1463529&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/bindex/Index.java (added)
+++ ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/bindex/Index.java Tue Apr 2 13:26:43 2013
@@ -0,0 +1,308 @@
+/*
+ * $Id: Index.java 44 2007-07-13 20:49:41Z hargrave@us.ibm.com $
+ *
+ * Copyright (c) OSGi Alliance (2002, 2006, 2007). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.osgi.impl.bundle.bindex;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.zip.CRC32;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+import org.osgi.impl.bundle.obr.resource.BundleInfo;
+import org.osgi.impl.bundle.obr.resource.RepositoryImpl;
+import org.osgi.impl.bundle.obr.resource.ResourceImpl;
+import org.osgi.impl.bundle.obr.resource.Tag;
+
+/**
+ * Iterate over a set of given bundles and convert them to resources. When -a is
+ * specified, other resources than bundles will be included too. After
+ * this, convert an local urls (file systems, JAR file) to relative URLs and
+ * create a ZIP file with the complete content. This ZIP file can be used in an
+ * OSGi Framework to map to an http service or it can be expanded on the web
+ * server's file system.
+ *
+ * @version $Revision: 44 $
+ */
+public class Index
+{
+ static String repositoryFileName = "repository.xml";
+ static URL licenseURL = null;
+ static boolean quiet = false;
+ static String name = "Untitled";
+ static boolean all = false;
+ static String urlTemplate = null;
+ static File rootFile = new File("")
+ .getAbsoluteFile();
+ static RepositoryImpl repository;
+ static String root;
+
+ /**
+ * Main entry. See -help for options.
+ *
+ * @param args
+ * @throws Exception
+ */
+ public static void main(String args[]) throws Exception {
+ System.err.println("Bundle Indexer | v2.2");
+ System.err.println("(c) 2007 OSGi, All Rights Reserved");
+
+ Set resources = new HashSet();
+ root = rootFile.toURL().toString();
+ repository = new RepositoryImpl(rootFile.toURL());
+
+ for (int i = 0; i < args.length; i++) {
+ try {
+ if (args[i].startsWith("-n")) {
+ name = args[++i];
+ }
+ else
+ if (args[i].startsWith("-r")) {
+ repositoryFileName = args[++i];
+ repository = new RepositoryImpl(new File(
+ repositoryFileName).getAbsoluteFile().toURL());
+ }
+ else
+ if (args[i].startsWith("-q")) {
+ quiet = true;
+ }
+ else
+ if (args[i].startsWith("-t")) {
+ urlTemplate = args[++i];
+ }
+ else
+ if (args[i].startsWith("-l")) {
+ licenseURL = new URL(new File("").toURL(),
+ args[++i]);
+ }
+ else
+ if (args[i].startsWith("-help")) {
+ System.err
+ .println("bindex [-t \"%s\" symbolic name \"%v\" version \"%f\" filename \"%p\" dirpath ] [ -r repository.(xml|zip) ] [-help] [-l file:license.html ] [-quiet] [-all] <jar file>*");
+ }
+ else
+ if (args[i].startsWith("-a")) {
+ all = true;
+ }
+ else {
+ recurse(resources, new File(args[i]));
+ }
+ }
+ catch (Exception e) {
+ System.err.println("Error in " + args[i] + " : " +
+ e.getMessage());
+ e.printStackTrace();
+ }
+ }
+
+ List sorted = new ArrayList(resources);
+ Collections.sort(sorted, new Comparator() {
+ public int compare(Object r1, Object r2) {
+ String s1 = getName((ResourceImpl) r1);
+ String s2 = getName((ResourceImpl) r2);
+ return s1.compareTo(s2);
+ }
+ });
+
+ Tag tag = doIndex(sorted);
+ if (repositoryFileName != null) {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ PrintWriter pw = new PrintWriter(new OutputStreamWriter(out,
+ "UTF-8"));
+
+ pw.println("<?xml version='1.0' encoding='utf-8'?>");
+ pw
+ .println("<?xml-stylesheet type='text/xsl' href='http://www2.osgi.org/www/obr2html.xsl'?>");
+
+ tag.print(0, pw);
+ pw.close();
+ byte buffer[] = out.toByteArray();
+ String name = "repository.xml";
+ FileOutputStream fout = new FileOutputStream(repositoryFileName);
+
+ if (repositoryFileName.endsWith(".zip")) {
+ ZipOutputStream zip = new ZipOutputStream(fout);
+ CRC32 checksum = new CRC32();
+ checksum.update(buffer);
+ ZipEntry ze = new ZipEntry(name);
+ ze.setSize(buffer.length);
+ ze.setCrc(checksum.getValue());
+ zip.putNextEntry(ze);
+ zip.write(buffer, 0, buffer.length);
+ zip.closeEntry();
+ zip.close();
+ }
+ else {
+ fout.write(buffer);
+ }
+ fout.close();
+ }
+
+ if (!quiet) {
+ PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
+ pw.println("<?xml version='1.0' encoding='utf-8'?>");
+ pw
+ .println("<?xml-stylesheet type='text/xsl' href='http://www2.osgi.org/www/obr2html.xsl'?>");
+ tag.print(0, pw);
+ pw.close();
+ }
+ }
+
+ static String getName(ResourceImpl impl) {
+ String s = impl.getSymbolicName();
+ if (s != null) {
+ return s;
+ }
+ else {
+ return "no-symbolic-name";
+ }
+ }
+
+ static void recurse(Set resources, File path) throws Exception {
+ if (path.isDirectory()) {
+ String list[] = path.list();
+ for (int i = 0; i < list.length; i++) {
+ recurse(resources, new File(path, list[i]));
+ }
+ }
+ else {
+ if (path.getName().equals("repository.xml") || path.getName().equals(new File(repositoryFileName).getName())) {
+ // do not index our repository.xml, nor the file we are working on now.
+ return;
+ }
+ if (path.getName().endsWith(".jar")) {
+ BundleInfo info = new BundleInfo(repository, path);
+ ResourceImpl resource = info.build();
+ if (urlTemplate != null) {
+ doTemplate(path, resource);
+ }
+ else {
+ resource.setURL(path.toURL());
+ }
+
+ resources.add(resource);
+ }
+ else {
+ // this is some other resource, we might want to include it.
+ if (all) {
+ resources.add(new ResourceImpl(repository, path.toURL()));
+ }
+ }
+ }
+ }
+
+ static void doTemplate(File path, ResourceImpl resource)
+ throws MalformedURLException {
+ String dir = path.getAbsoluteFile().getParentFile().getAbsoluteFile()
+ .toURL().toString();
+ if (dir.endsWith("/")) {
+ dir = dir.substring(0, dir.length() - 1);
+ }
+
+ if (dir.startsWith(root)) {
+ dir = dir.substring(root.length());
+ }
+
+ String url = urlTemplate.replaceAll("%v", "" + resource.getVersion());
+ url = url.replaceAll("%s", resource.getSymbolicName());
+ url = url.replaceAll("%f", path.getName());
+ url = url.replaceAll("%p", dir);
+ resource.setURL(new URL(url));
+ }
+
+ /**
+ * Create the repository index
+ *
+ * @param resources Set of resources
+ * @param collected The output zip file
+ * @throws java.io.IOException
+ */
+ static Tag doIndex(Collection resources) throws IOException {
+ Tag repository = new Tag("repository");
+ repository.addAttribute("lastmodified", new Date());
+ repository.addAttribute("name", name);
+
+ for (Iterator i = resources.iterator(); i.hasNext();) {
+ ResourceImpl resource = (ResourceImpl) i.next();
+ repository.addContent(resource.toXML());
+ }
+ return repository;
+ }
+
+ /**
+ * Add the resource to the ZIP file, calculating the CRC etc.
+ *
+ * @param zip The output ZIP file
+ * @param name The name of the resource
+ * @param actual The contents stream
+ * @throws java.io.IOException
+ */
+ static void addToZip(ZipOutputStream zip, String name, InputStream actual)
+ throws IOException {
+ byte buffer[];
+ buffer = readAll(actual, 0);
+ actual.close();
+ CRC32 checksum = new CRC32();
+ checksum.update(buffer);
+ ZipEntry ze = new ZipEntry(name);
+ ze.setSize(buffer.length);
+ ze.setCrc(checksum.getValue());
+ zip.putNextEntry(ze);
+ zip.write(buffer, 0, buffer.length);
+ zip.closeEntry();
+ }
+
+ /**
+ * Read a complete stream till EOF. This method will parse the input stream
+ * until a -1 is discovered.
+ *
+ * The method is recursive. It keeps on calling a higher level routine until
+ * EOF. Only then is the result buffer calculated.
+ */
+ static byte[] readAll(InputStream in, int offset) throws IOException {
+ byte temp[] = new byte[4096];
+ byte result[];
+ int size = in.read(temp, 0, temp.length);
+ if (size <= 0) {
+ return new byte[offset];
+ }
+ //
+ // We have a positive result, copy it
+ // to the right offset.
+ //
+ result = readAll(in, offset + size);
+ System.arraycopy(temp, 0, result, offset, size);
+ return result;
+ }
+
+}
\ No newline at end of file
Added: ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/bindex/packageinfo
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/bindex/packageinfo?rev=1463529&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/bindex/packageinfo (added)
+++ ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/bindex/packageinfo Tue Apr 2 13:26:43 2013
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file
Added: ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/obr/resource/BundleInfo.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/obr/resource/BundleInfo.java?rev=1463529&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/obr/resource/BundleInfo.java (added)
+++ ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/obr/resource/BundleInfo.java Tue Apr 2 13:26:43 2013
@@ -0,0 +1,506 @@
+/*
+ * $Id: BundleInfo.java 44 2007-07-13 20:49:41Z hargrave@us.ibm.com $
+ *
+ * Copyright (c) OSGi Alliance (2002, 2006, 2007). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.osgi.impl.bundle.obr.resource;
+
+import java.io.*;
+import java.net.URL;
+import java.util.*;
+import java.util.zip.*;
+
+import org.osgi.service.obr.Resource;
+
+/**
+ * Convert a bundle to a generic resource description and store its local
+ * dependencies (like for example a license file in the JAR) in a zip file.
+ *
+ * @version $Revision: 44 $
+ */
+public class BundleInfo {
+ Manifest manifest;
+ File bundleJar;
+ ZipFile jar;
+ String license;
+ Properties localization;
+ RepositoryImpl repository;
+
+ /**
+ * Parse a zipFile from the file system. We only need the manifest and the
+ * localization. So a zip file is used to minimze memory consumption.
+ *
+ * @param bundleJar Path name
+ * @throws Exception Any errors that occur
+ */
+ public BundleInfo(RepositoryImpl repository, File bundleJar) throws Exception {
+ this.bundleJar = bundleJar;
+ this.repository = repository;
+
+ if (!this.bundleJar.exists())
+ throw new FileNotFoundException(bundleJar.toString());
+
+ jar = new ZipFile(bundleJar);
+ ZipEntry entry = jar.getEntry("META-INF/MANIFEST.MF");
+ if (entry == null)
+ throw new FileNotFoundException("No Manifest in "
+ + bundleJar.toString());
+ manifest = new Manifest(jar.getInputStream(entry));
+ }
+
+ public BundleInfo(Manifest manifest) throws Exception {
+ this.manifest = manifest;
+ }
+
+ /**
+ * Convert the bundle to a Resource. All URIs are going to be abslute, but
+ * could be local.
+ *
+ * @return the resource
+ * @throws Exception
+ */
+ public ResourceImpl build() throws Exception {
+ ResourceImpl resource;
+ // Setup the manifest
+ // and create a resource
+ resource = new ResourceImpl(repository, manifest.getSymbolicName(), manifest
+ .getVersion());
+
+ try {
+
+ // Calculate the location URL of the JAR
+ URL location = new URL("jar:" + bundleJar.toURL().toString() + "!/");
+ resource.setURL(bundleJar.toURL());
+ resource.setFile(bundleJar);
+
+ doReferences(resource, location);
+ doSize(resource);
+ doCategories(resource);
+ doImportExportServices(resource);
+ doDeclarativeServices(resource);
+ doFragment(resource);
+ doRequires(resource);
+ doBundle(resource);
+ doExports(resource);
+ doImports(resource);
+ doExecutionEnvironment(resource);
+
+ return resource;
+ }
+ finally {
+ try {
+ jar.close();
+ }
+ catch (Exception e) {
+ // ignore
+ }
+ }
+ }
+
+ /**
+ * Check the size and add it.
+ *
+ * @param resource
+ */
+ void doSize(ResourceImpl resource) {
+ long size = bundleJar.length();
+ if (size > 0)
+ resource.setSize(size);
+ }
+
+ /**
+ * Find the categories, break them up and add them.
+ *
+ * @param resource
+ */
+ void doCategories(ResourceImpl resource) {
+ for (int i = 0; i < manifest.getCategories().length; i++) {
+ String category = manifest.getCategories()[i];
+ resource.addCategory(category);
+ }
+ }
+
+ void doReferences(ResourceImpl resource, URL location) {
+ // Presentation name
+ String name = translated("Bundle-Name");
+ if (name != null)
+ resource.setPresentationName(name);
+
+ // Handle license. -l allows a global license
+ // set when no license is included.
+
+ String license = translated("Bundle-License");
+ if (license != null)
+ resource.setLicense(toURL(location, license));
+ else if (this.license != null)
+ resource.setLicense(toURL(location, this.license));
+
+ String description = translated("Bundle-Description");
+ if (description != null)
+ resource.setDescription(description);
+
+ String copyright = translated("Bundle-Copyright");
+ if (copyright != null)
+ resource.setCopyright(copyright);
+
+ String documentation = translated("Bundle-DocURL");
+ if (documentation != null)
+ resource.setDocumentation(toURL(location, documentation));
+
+ String source = manifest.getValue("Bundle-Source");
+ if (source != null)
+ resource.setSource(toURL(location, source));
+ }
+
+ URL toURL(URL location, String source) {
+ try {
+ return new URL(location, source);
+ }
+ catch (Exception e) {
+ System.err.println("Error in converting url: " + location + " : "
+ + source);
+ return null;
+ }
+ }
+
+ void doDeclarativeServices(ResourceImpl resource) throws Exception {
+ String serviceComponent = manifest.getValue("service-component");
+ if (serviceComponent == null)
+ return;
+
+ StringTokenizer st = new StringTokenizer(serviceComponent, " ,\t");
+ String parts[] = new String[st.countTokens()];
+ for (int i = 0; i < parts.length; i++)
+ parts[i] = st.nextToken();
+
+ for (int i = 0; i < parts.length; i++) {
+ ZipEntry entry = jar.getEntry(parts[i]);
+ if (entry == null) {
+ System.err.println("Bad Service-Component header: "
+ + serviceComponent + ", no such file " + parts[i]);
+ }
+ InputStream in = jar.getInputStream(entry);
+ // TODO parse declarative services files.
+ in.close();
+ }
+ }
+
+ void doImportExportServices(ResourceImpl resource) throws IOException {
+ String importServices = manifest.getValue("import-service");
+ if (importServices != null) {
+ List entries = manifest.getEntries(importServices);
+ for (Iterator i = entries.iterator(); i.hasNext();) {
+ ManifestEntry entry = (ManifestEntry) i.next();
+ RequirementImpl ri = new RequirementImpl("service");
+ ri.setFilter(createServiceFilter(entry));
+ ri.setComment("Import Service " + entry.getName());
+
+ // TODO the following is arbitrary
+ ri.setOptional(false);
+ ri.setMultiple(true);
+ resource.addRequirement(ri);
+ }
+ }
+
+ String exportServices = manifest.getValue("export-service");
+ if (exportServices != null) {
+ List entries = manifest.getEntries(exportServices);
+ for (Iterator i = entries.iterator(); i.hasNext();) {
+ ManifestEntry entry = (ManifestEntry) i.next();
+ CapabilityImpl cap = createServiceCapability(entry);
+ resource.addCapability(cap);
+ }
+ }
+ }
+
+ String translated(String key) {
+ return translate(manifest.getValue(key));
+ }
+
+ void doFragment(ResourceImpl resource) {
+ // Check if we are a fragment
+ ManifestEntry entry = manifest.getHost();
+ if (entry == null) {
+ return;
+ }
+ else {
+ // We are a fragment, create a requirement
+ // to our host.
+ RequirementImpl r = new RequirementImpl("bundle");
+ StringBuffer sb = new StringBuffer();
+ sb.append("(&(symbolicname=");
+ sb.append(entry.getName());
+ sb.append(")(version>=");
+ sb.append(entry.getVersion());
+ sb.append("))");
+ r.setFilter(sb.toString());
+ r.setComment("Required Host " + entry.getName() );
+ r.setExtend(true);
+ r.setOptional(false);
+ r.setMultiple(false);
+ resource.addRequirement(r);
+
+ // And insert a capability that we are available
+ // as a fragment. ### Do we need that with extend?
+ CapabilityImpl capability = new CapabilityImpl("fragment");
+ capability.addProperty("host", entry.getName());
+ capability.addProperty("version", entry.getVersion());
+ resource.addCapability(capability);
+ }
+ }
+
+ void doRequires(ResourceImpl resource) {
+ List entries = manifest.getRequire();
+ if (entries == null)
+ return;
+
+ for (Iterator i = entries.iterator(); i.hasNext();) {
+ ManifestEntry entry = (ManifestEntry) i.next();
+ RequirementImpl r = new RequirementImpl("bundle");
+
+ StringBuffer sb = new StringBuffer();
+ sb.append("(&(symbolicname=");
+ sb.append(entry.getName());
+ sb.append(")(version>=");
+ sb.append(entry.getVersion());
+ sb.append("))");
+ r.setFilter(sb.toString());
+ r.setComment("Require Bundle " + entry.getName() + "; "
+ + entry.getVersion());
+ if (entry.directives == null
+ || "true".equalsIgnoreCase((String) entry.directives
+ .get("resolution")))
+ r.setOptional(false);
+ else
+ r.setOptional(true);
+ resource.addRequirement(r);
+ }
+ }
+
+ void doExecutionEnvironment(ResourceImpl resource) {
+ String[] parts = manifest.getRequiredExecutionEnvironments();
+ if (parts == null)
+ return;
+
+ StringBuffer sb = new StringBuffer();
+ sb.append("(|");
+ for (int i = 0; i < parts.length; i++) {
+ String part = parts[i];
+ sb.append("(ee=");
+ sb.append(part);
+ sb.append(")");
+ }
+ sb.append(")");
+
+ RequirementImpl req = new RequirementImpl("ee");
+ req.setFilter(sb.toString());
+ req.setComment("Execution Environment " + sb.toString());
+ resource.addRequirement(req);
+ }
+
+ void doImports(ResourceImpl resource) {
+ List requirements = new ArrayList();
+ List packages = manifest.getImports();
+ if (packages == null)
+ return;
+
+ for (Iterator i = packages.iterator(); i.hasNext();) {
+ ManifestEntry pack = (ManifestEntry) i.next();
+ RequirementImpl requirement = new RequirementImpl("package");
+
+ createImportFilter(requirement, "package", pack);
+ requirement.setComment("Import package " + pack);
+ requirements.add(requirement);
+ }
+ for (Iterator i = requirements.iterator(); i.hasNext();)
+ resource.addRequirement((RequirementImpl) i.next());
+ }
+
+ String createServiceFilter(ManifestEntry pack) {
+ StringBuffer filter = new StringBuffer();
+ filter.append("(service=");
+ filter.append(pack.getName());
+ filter.append(")");
+ return filter.toString();
+ }
+
+ void createImportFilter(RequirementImpl req, String name, ManifestEntry pack) {
+ StringBuffer filter = new StringBuffer();
+ filter.append("(&(");
+ filter.append(name);
+ filter.append("=");
+ filter.append(pack.getName());
+ filter.append(")");
+ VersionRange version = pack.getVersion();
+ if (version != null) {
+ if ( version.isRange() ) {
+ filter.append("(version");
+ filter.append(">");
+ if (version.includeLow())
+ filter.append("=");
+ filter.append(version.low);
+ filter.append(")");
+
+ filter.append("(version");
+ filter.append("<");
+ if (version.includeHigh())
+ filter.append("=");
+ filter.append(version.high);
+ filter.append(")");
+ }
+ else {
+ filter.append("(version>=");
+ filter.append(pack.getVersion());
+ filter.append(")");
+ }
+ }
+ Map attributes = pack.getAttributes();
+ Set attrs = doImportPackageAttributes(req, filter, attributes);
+ if (attrs.size() > 0) {
+ String del = "";
+ filter.append("(mandatory:<*");
+ for (Iterator i = attrs.iterator(); i.hasNext();) {
+ filter.append(del);
+ filter.append(i.next());
+ del = ", ";
+ }
+ filter.append(")");
+ }
+ filter.append(")");
+ req.setFilter(filter.toString());
+ }
+
+ Set doImportPackageAttributes(RequirementImpl req, StringBuffer filter,
+ Map attributes) {
+ HashSet set = new HashSet();
+
+ if (attributes != null)
+ for (Iterator i = attributes.keySet().iterator(); i.hasNext();) {
+ String attribute = (String) i.next();
+ String value = (String) attributes.get(attribute);
+ if (attribute.equalsIgnoreCase("specification-version")
+ || attribute.equalsIgnoreCase("version"))
+ continue;
+ else if (attribute.equalsIgnoreCase("resolution:")) {
+ req.setOptional(value.equalsIgnoreCase("optional"));
+ }
+ if (attribute.endsWith(":")) {
+ // Ignore
+ }
+ else {
+ filter.append("(");
+ filter.append(attribute);
+ filter.append("=");
+ filter.append(attributes.get(attribute));
+ filter.append(")");
+ set.add(attribute);
+ }
+ }
+ return set;
+ }
+
+ void doBundle(ResourceImpl resource) {
+ CapabilityImpl capability = new CapabilityImpl("bundle");
+ capability.addProperty("symbolicname", manifest.getSymbolicName());
+ if (manifest.getValue("Bundle-Name") != null)
+ capability.addProperty(
+ Resource.PRESENTATION_NAME,
+ translated("Bundle-Name"));
+ capability.addProperty("version", manifest.getVersion());
+ capability
+ .addProperty("manifestversion", manifest.getManifestVersion());
+
+ /**
+ * Is this needed TODO
+ */
+ ManifestEntry host = manifest.getHost();
+ if (host != null) {
+ capability.addProperty("host", host.getName());
+ if (host.getVersion() != null)
+ capability.addProperty("version", host.getVersion());
+ }
+ resource.addCapability(capability);
+ }
+
+ void doExports(ResourceImpl resource) {
+ List capabilities = new ArrayList();
+ List packages = manifest.getExports();
+ if (packages != null) {
+ for (Iterator i = packages.iterator(); i.hasNext();) {
+ ManifestEntry pack = (ManifestEntry) i.next();
+ CapabilityImpl capability = createCapability("package", pack);
+ capabilities.add(capability);
+ }
+ }
+ for (Iterator i = capabilities.iterator(); i.hasNext();)
+ resource.addCapability((CapabilityImpl) i.next());
+ }
+
+ CapabilityImpl createServiceCapability(ManifestEntry pack) {
+ CapabilityImpl capability = new CapabilityImpl("service");
+ capability.addProperty("service", pack.getName());
+ return capability;
+ }
+
+ CapabilityImpl createCapability(String name, ManifestEntry pack) {
+ CapabilityImpl capability = new CapabilityImpl(name);
+ capability.addProperty(name, pack.getName());
+ capability.addProperty("version", pack.getVersion());
+ Map attributes = pack.getAttributes();
+ if (attributes != null)
+ for (Iterator at = attributes.keySet().iterator(); at.hasNext();) {
+ String key = (String) at.next();
+ if (key.equalsIgnoreCase("specification-version")
+ || key.equalsIgnoreCase("version"))
+ continue;
+ else {
+ Object value = attributes.get(key);
+ capability.addProperty(key, value);
+ }
+ }
+ return capability;
+ }
+
+ String translate(String s) {
+ if (s == null)
+ return null;
+
+ if (!s.startsWith("%")) {
+ return s;
+ }
+
+ if (localization == null)
+ try {
+ localization = new Properties();
+ String path = manifest
+ .getValue("Bundle-Localization", "bundle");
+ path += ".properties";
+ InputStream in = jar.getInputStream(new ZipEntry(path));
+ if (in != null) {
+ localization.load(in);
+ in.close();
+ }
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ }
+ s = s.substring(1);
+ return localization.getProperty(s, s);
+ }
+
+ File getZipFile() {
+ return bundleJar;
+ }
+}
\ No newline at end of file
Added: ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/obr/resource/CapabilityImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/obr/resource/CapabilityImpl.java?rev=1463529&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/obr/resource/CapabilityImpl.java (added)
+++ ace/trunk/org.apache.ace.obr/src/org/osgi/impl/bundle/obr/resource/CapabilityImpl.java Tue Apr 2 13:26:43 2013
@@ -0,0 +1,109 @@
+/*
+ * $Id: CapabilityImpl.java 44 2007-07-13 20:49:41Z hargrave@us.ibm.com $
+ *
+ * Copyright (c) OSGi Alliance (2002, 2006, 2007). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.osgi.impl.bundle.obr.resource;
+
+import java.util.*;
+
+import org.osgi.service.obr.Capability;
+import org.xmlpull.v1.XmlPullParser;
+
+
+
+public class CapabilityImpl implements Capability {
+ String name;
+ Map properties = new TreeMap();
+
+ public CapabilityImpl(String name) {
+ this.name = name;
+ }
+
+ public CapabilityImpl(XmlPullParser parser) throws Exception {
+ parser.require(XmlPullParser.START_TAG, null, "capability");
+ name = parser.getAttributeValue(null,"name");
+ while ( parser.nextTag() == XmlPullParser.START_TAG ) {
+ if ( parser.getName().equals("p")) {
+ String name = parser.getAttributeValue(null,"n");
+ String value = parser.getAttributeValue(null,"v");
+ String type = parser.getAttributeValue(null,"t");
+ Object v = value;
+
+ if ( "nummeric".equals(type))
+ v = new Long(value);
+ else if ( "version".equals(type))
+ v = new VersionRange(value);
+ addProperty(name,v);
+ }
+ parser.next();
+ parser.require(XmlPullParser.END_TAG, null, "p" );
+ }
+ parser.require(XmlPullParser.END_TAG, null, "capability" );
+ }
+
+
+ public void addProperty(String key, Object value) {
+ List values = (List) properties.get(key);
+ if (values == null) {
+ values = new ArrayList();
+ properties.put(key, values);
+ }
+ values.add(value);
+ }
+
+ public Tag toXML() {
+ return toXML(this);
+ }
+
+ public static Tag toXML(Capability capability) {
+ Tag tag = new Tag("capability");
+ tag.addAttribute("name", capability.getName());
+ Map properties = capability.getProperties();
+ for ( Iterator k= properties.keySet().iterator(); k.hasNext(); ) {
+ String key = (String) k.next();
+ List values = (List) properties.get(key);
+ for ( Iterator v = values.iterator(); v.hasNext(); ) {
+ Object value = v.next();
+ Tag p = new Tag("p");
+ tag.addContent(p);
+ p.addAttribute("n", key);
+ if ( value != null )
+ p.addAttribute("v", value.toString());
+ else
+ System.out.println("Missing value " + key);
+ String type = null;
+ if (value instanceof Number )
+ type = "number";
+ else if (value.getClass() == VersionRange.class)
+ type = "version";
+ if (type != null)
+ p.addAttribute("t", type);
+ }
+ }
+ return tag;
+ }
+
+
+ public String getName() {
+ return name;
+ }
+
+
+ public Map getProperties() {
+ return properties;
+ }
+
+}