You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ace.apache.org by ja...@apache.org on 2017/07/19 13:13:26 UTC

svn commit: r1802389 - in /ace/trunk: org.apache.ace.obr/ org.apache.ace.obr/src/org/apache/ace/obr/servlet/ org.apache.ace.obr/src/org/apache/ace/obr/storage/ org.apache.ace.obr/src/org/apache/ace/obr/storage/file/ org.apache.ace.obr/test/org/apache/a...

Author: jawi
Date: Wed Jul 19 13:13:26 2017
New Revision: 1802389

URL: http://svn.apache.org/viewvc?rev=1802389&view=rev
Log:
ACE-626 allow OBR to report different base location

- allow the OBR servlet to be configured in what base URL it should use to
  report back to the client after handling a new uploaded resource. If no
  configuration is supplied, the "old" behaviour is used.


Added:
    ace/trunk/run-obr/conf/org.apache.ace.obr.servlet.cfg
Modified:
    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/BundleStore.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/packageinfo
    ace/trunk/org.apache.ace.obr/storage.bnd
    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

Modified: 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=1802389&r1=1802388&r2=1802389&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/servlet/Activator.java (original)
+++ ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/servlet/Activator.java Wed Jul 19 13:13:26 2017
@@ -30,18 +30,24 @@ import org.apache.ace.obr.storage.Bundle
 import org.apache.felix.dm.DependencyActivatorBase;
 import org.apache.felix.dm.DependencyManager;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.ManagedService;
 import org.osgi.service.log.LogService;
 
 public class Activator extends DependencyActivatorBase {
+	private static final String PID = "org.apache.ace.obr.servlet";
 
     @Override
     public void init(BundleContext context, DependencyManager manager) throws Exception {
         Properties servletProps = new Properties();
         servletProps.put(HTTP_WHITEBOARD_SERVLET_PATTERN, BundleServlet.SERVLET_ENDPOINT.concat("*"));
         servletProps.put(HTTP_WHITEBOARD_CONTEXT_SELECT, ACE_WHITEBOARD_CONTEXT_SELECT_FILTER);
+        servletProps.put(Constants.SERVICE_PID, PID);
+
+        String[] ifaces = { Servlet.class.getName(), ManagedService.class.getName() };
 
         manager.add(createComponent()
-            .setInterface(Servlet.class.getName(), servletProps)
+            .setInterface(ifaces, servletProps)
             .setImplementation(BundleServlet.class)
             .add(createServiceDependency()
                 .setService(BundleStore.class)

Modified: 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=1802389&r1=1802388&r2=1802389&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/servlet/BundleServlet.java (original)
+++ ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/servlet/BundleServlet.java Wed Jul 19 13:13:26 2017
@@ -24,11 +24,14 @@ import static javax.servlet.http.HttpSer
 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 org.osgi.service.log.LogService.LOG_DEBUG;
+import static org.osgi.service.log.LogService.LOG_INFO;
+import static org.osgi.service.log.LogService.LOG_WARNING;
 
-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;
@@ -37,12 +40,21 @@ import javax.servlet.http.HttpServletReq
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.ace.obr.storage.BundleStore;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
 import org.osgi.service.log.LogService;
 
 /**
  * Provides access to the OBR through a REST-ish API.
  */
-public class BundleServlet extends HttpServlet {
+public class BundleServlet extends HttpServlet implements ManagedService {
+    /**
+     * If defined, allows you to return a different base URL for uploaded resources. Otherwise, the base URL is derived
+     * from the originating request. Use this function in case your OBR is, for example, running behind a SSL offloading
+     * proxy.
+     */
+    private static final String KEY_OBR_BASE_URL = "obrBaseUrl";
+
     private static final long serialVersionUID = 1L;
 
     private static final int COPY_BUFFER_SIZE = 4096;
@@ -50,14 +62,34 @@ public class BundleServlet extends HttpS
     public static final String TEXT_MIMETYPE = "text/plain";
     public static final String SERVLET_ENDPOINT = "/obr/";
 
-    private volatile LogService m_log; /* will be injected by dependencymanager */
-    private volatile BundleStore m_store; /* will be injected by dependencymanager */
+    // Managed by Felix DM...
+    private volatile LogService m_log;
+    private volatile BundleStore m_store;
+    // Locally managed...
+    private volatile String m_obrBaseURL;
 
     @Override
     public String getServletInfo() {
         return "Apache ACE OBR Servlet";
     }
 
+    @Override
+    public void updated(Dictionary<String, ?> properties) throws ConfigurationException {
+        String obrBaseURL = null;
+        if (properties != null) {
+            obrBaseURL = (String) properties.get(KEY_OBR_BASE_URL);
+            // Coerse empty values back to null...
+            if (obrBaseURL != null && "".equals(obrBaseURL.trim())) {
+                obrBaseURL = null;
+            }
+            if (m_log != null) {
+                m_log.log(LOG_INFO, "Bundle servlet is using " + (obrBaseURL == null ? "(default)" : obrBaseURL)
+                    + " as default base for uploaded bundles...");
+            }
+        }
+        m_obrBaseURL = obrBaseURL;
+    }
+
     /**
      * Responds to POST requests sent to http://host:port/obr by writing the received data to the bundle store and
      * returning the persistent location. Will send out a response that contains one of the following status codes:
@@ -70,28 +102,31 @@ public class BundleServlet extends HttpS
      */
     @Override
     protected void doPost(HttpServletRequest request, HttpServletResponse response) {
-
         String fileName = request.getParameter("filename");
         boolean replace = Boolean.parseBoolean(request.getParameter("replace"));
+
         try {
             String storePath = m_store.put(request.getInputStream(), fileName, replace);
             if (storePath != null) {
+                m_log.log(LOG_INFO, "Uploaded '" + fileName + "' to OBR as: " + storePath + " (replace = " + replace + ")");
+
                 sendCreated(request, response, storePath);
             }
             else {
+                m_log.log(LOG_INFO, "Conflicting upload for '" + fileName + "': already exists and not replacing it...");
+
                 sendResponse(response, SC_CONFLICT);
             }
         }
         catch (IOException e) {
-            m_log.log(LogService.LOG_WARNING, "Exception handling request: " + request.getRequestURL(), e);
+            m_log.log(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>
+     * 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
@@ -108,27 +143,29 @@ public class BundleServlet extends HttpS
             String id = path.substring(1);
             try {
                 if (m_store.remove(id)) {
+                    m_log.log(LOG_INFO, "Deleted '" + id + "' from OBR...");
+
                     sendResponse(response, SC_OK);
                 }
                 else {
+                    m_log.log(LOG_INFO, "Unable to delete '" + id + "': it does not exist.");
+
                     sendResponse(response, SC_NOT_FOUND);
                 }
             }
             catch (IOException e) {
-                m_log.log(LogService.LOG_WARNING, "Exception handling request: " + request.getRequestURL(), e);
+                m_log.log(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>
+     * 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>
+     * <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>.
      */
@@ -142,10 +179,7 @@ public class BundleServlet extends HttpS
             // Remove leading slash...
             String id = path.substring(1);
 
-            ServletOutputStream output = null;
-            InputStream fileStream = null;
-            try {
-                fileStream = m_store.get(id);
+            try (InputStream fileStream = m_store.get(id); ServletOutputStream output = response.getOutputStream()) {
                 if (fileStream == null) {
                     sendResponse(response, HttpServletResponse.SC_NOT_FOUND);
                 }
@@ -153,7 +187,6 @@ public class BundleServlet extends HttpS
                     // 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);
@@ -161,31 +194,27 @@ public class BundleServlet extends HttpS
                 }
             }
             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()
+                // ACE-260: lower log-level of this exception; as it is probably because the
+                // remote hung up early...
+                m_log.log(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);
+                // ACE-260: all other exception are logged, as we might have a possible resource
+                // leak...
+                m_log.log(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>
+     * 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>
+     * <li><code>HttpServletResponse.SC_OK</code> - if all went fine <br>
      * The response is empty in all situations.
      */
     @Override
@@ -198,56 +227,41 @@ public class BundleServlet extends HttpS
             // 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);
+                if (m_store.exists(id)) {
+                    sendResponse(response, HttpServletResponse.SC_OK);
                 }
                 else {
-                    sendResponse(response, HttpServletResponse.SC_OK);
+                    sendResponse(response, HttpServletResponse.SC_NOT_FOUND);
                 }
             }
             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);
+                // ACE-260: all other exception are logged, as we might have a possible resource
+                // leak...
+                m_log.log(LOG_WARNING, "Exception in request: " + request.getRequestURL(), ex);
                 sendResponse(response, SC_INTERNAL_SERVER_ERROR);
             }
-            finally {
-                closeSafely(fileStream, request);
-                closeSafely(output, request);
-            }
-        }
-    }
-
-    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 created response with location header
     private void sendCreated(HttpServletRequest request, HttpServletResponse response, String relativePath) {
-        StringBuilder locationBuilder = new StringBuilder(request.getScheme()).append("://").append(request.getServerName());
-        boolean ignorePort = (request.getScheme().equals("http") && request.getServerPort() == 80) | (request.getScheme().equals("https") && request.getServerPort() == 443);
-        if(!ignorePort){
-            locationBuilder.append(":" + request.getServerPort());
+        StringBuffer location;
+
+        String obrBaseURL = m_obrBaseURL;
+        if (obrBaseURL != null) {
+            location = new StringBuffer(obrBaseURL);
         }
-        locationBuilder.append(SERVLET_ENDPOINT).append(relativePath);
-        response.setHeader("Location", locationBuilder.toString());
+        else {
+            location = request.getRequestURL();
+        }
+
+        if (location.charAt(location.length() - 1) != '/') {
+            location.append('/');
+        }
+        location.append(relativePath);
+
+        response.setHeader("Location", location.toString());
         response.setStatus(SC_CREATED);
     }
 
@@ -262,7 +276,7 @@ public class BundleServlet extends HttpS
             response.sendError(statusCode, description);
         }
         catch (Exception e) {
-            m_log.log(LogService.LOG_WARNING, "Unable to send response with status code '" + statusCode + "'", e);
+            m_log.log(LOG_WARNING, "Unable to send response with status code '" + statusCode + "'", e);
         }
     }
-}
\ No newline at end of file
+}

Modified: 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=1802389&r1=1802388&r2=1802389&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/BundleStore.java (original)
+++ ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/BundleStore.java Wed Jul 19 13:13:26 2017
@@ -28,34 +28,54 @@ import org.osgi.annotation.versioning.Pr
 public interface BundleStore {
 
     /**
+     * Returns whether or not a specified resource exists.
+     *
+     * @param filePath
+     *            the relative path of the resource
+     * @return <code>true</code> if the resource exists, <code>false</code> otherwise.
+     * @throws IOException
+     *             in case there was a problem testing the existance of the requested resource.
+     */
+    boolean exists(String filePath) throws IOException;
+
+    /**
      * Returns an <code>InputStream</code> to the data of the specified resource.
      *
-     * @param filePath Relative path of the the resource.
+     * @param filePath
+     *            Relative path of the 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.
+     * @throws java.io.IOException
+     *             If there was a problem returning the requested resource.
      */
-    public InputStream get(String filePath) throws IOException;
+    InputStream get(String filePath) throws IOException;
 
     /**
-     * Stores the specified resource in the store. If the resource already existed, it
-     * will only be accepted if you either try to store exactly the same resource (byte-by-byte)
-     * or tell it to forcefully replace the resource. The latter should only be done with
-     * extreme care.
+     * Stores the specified resource in the store. If the resource already existed, it will only be accepted if you
+     * either try to store exactly the same resource (byte-by-byte) or tell it to forcefully replace the resource. The
+     * latter should only be done with extreme care.
      *
-     * @param fileName name of the resource, ignored if the resource is an OSGi bundle
-     * @param data the actual data of the resource
-     * @param replace <code>true</code> to replace any existing resource with the same name
-     * @return the filePath if the resource was successfully stored, <code>null</code> if the resource already existed and was different
-     * @throws java.io.IOException If there was a problem reading or writing the data of the resource.
+     * @param fileName
+     *            name of the resource, ignored if the resource is an OSGi bundle
+     * @param data
+     *            the actual data of the resource
+     * @param replace
+     *            <code>true</code> to replace any existing resource with the same name
+     * @return the filePath if the resource was successfully stored, <code>null</code> if the resource already existed
+     *         and was different
+     * @throws java.io.IOException
+     *             If there was a problem reading or writing the data of the resource.
      */
-    public String put(InputStream data, String fileName, boolean replace) throws IOException;
+    String put(InputStream data, String fileName, boolean replace) throws IOException;
 
     /**
      * Removes the specified resource from the store.
      *
-     * @param filePath Relative path of the 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.
+     * @param filePath
+     *            Relative path of the 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 filePath) throws IOException;
-}
\ No newline at end of file
+    boolean remove(String filePath) throws IOException;
+}

Modified: 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=1802389&r1=1802388&r2=1802389&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/file/BundleFileStore.java (original)
+++ ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/file/BundleFileStore.java Wed Jul 19 13:13:26 2017
@@ -45,8 +45,8 @@ 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 index.xml should be retrievable from that path (which will internally be converted to an
- * absolute path).
+ * bundles and the index.xml should be retrievable from that path (which will internally be converted to an absolute
+ * path).
  */
 public class BundleFileStore implements BundleStore, ManagedService {
     private static final String REPOSITORY_XML = "index.xml";
@@ -62,7 +62,7 @@ public class BundleFileStore implements
 
     /**
      * Checks if the the directory was modified since we last checked. If so, the meta-data generator is called.
-     * 
+     *
      * @throws IOException
      *             If there is a problem synchronizing the meta-data.
      */
@@ -77,6 +77,13 @@ public class BundleFileStore implements
         }
     }
 
+    @Override
+    public boolean exists(String fileName) throws IOException {
+        File f = createFile(fileName);
+        return f.exists();
+    }
+
+    @Override
     public InputStream get(String fileName) throws IOException {
         if (REPOSITORY_XML.equals(fileName)) {
             synchronizeMetadata();
@@ -93,6 +100,7 @@ public class BundleFileStore implements
         return result;
     }
 
+    @Override
     public String put(InputStream data, String fileName, boolean replace) throws IOException {
         if (fileName == null) {
             fileName = "";
@@ -167,6 +175,7 @@ public class BundleFileStore implements
         }
     }
 
+    @Override
     public boolean remove(String fileName) throws IOException {
         File dir;
         synchronized (m_lock) {
@@ -189,6 +198,7 @@ public class BundleFileStore implements
         return false;
     }
 
+    @Override
     public void updated(Dictionary<String, ?> dict) throws ConfigurationException {
         if (dict != null) {
             String path = (String) dict.get(OBRFileStoreConstants.FILE_LOCATION_KEY);
@@ -230,7 +240,7 @@ public class BundleFileStore implements
     /**
      * Computes a magic checksum used to determine whether there where changes in the directory without actually looking
      * into the files or using observation.
-     * 
+     *
      * @param dir
      *            The directory
      * @return The checksum
@@ -268,7 +278,7 @@ public class BundleFileStore implements
 
     /**
      * Downloads a given input stream to a temporary file.
-     * 
+     *
      * @param source
      *            the input stream to download;
      * @throws IOException
@@ -295,7 +305,7 @@ public class BundleFileStore implements
 
     /**
      * Encapsulated the store layout strategy by creating the resource file based on the provided meta-data.
-     * 
+     *
      * @param metaData
      *            the meta-data for the resource
      * @return the resource file
@@ -328,7 +338,7 @@ public class BundleFileStore implements
     /**
      * Splits a name into parts, breaking at all dots as long as what's behind the dot resembles a Java package name
      * (ie. it starts with a lowercase character).
-     * 
+     *
      * @param name
      *            the name to split
      * @return an array of parts
@@ -363,7 +373,7 @@ public class BundleFileStore implements
 
     /**
      * Moves a given source file to a destination location, effectively resulting in a rename.
-     * 
+     *
      * @param source
      *            the source file to move;
      * @param dest
@@ -416,7 +426,7 @@ public class BundleFileStore implements
 
     /**
      * 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>.
      */
@@ -433,7 +443,7 @@ public class BundleFileStore implements
 
     /**
      * 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>.

Modified: 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=1802389&r1=1802388&r2=1802389&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/packageinfo (original)
+++ ace/trunk/org.apache.ace.obr/src/org/apache/ace/obr/storage/packageinfo Wed Jul 19 13:13:26 2017
@@ -1 +1 @@
-version 2.0.1
\ No newline at end of file
+version 2.1.0
\ No newline at end of file

Modified: ace/trunk/org.apache.ace.obr/storage.bnd
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.obr/storage.bnd?rev=1802389&r1=1802388&r2=1802389&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.obr/storage.bnd (original)
+++ ace/trunk/org.apache.ace.obr/storage.bnd Wed Jul 19 13:13:26 2017
@@ -4,6 +4,6 @@ Private-Package: org.apache.ace.obr.stor
 	org.apache.ace.obr.metadata.util
 Bundle-Activator: org.apache.ace.obr.storage.file.Activator
 Export-Package: org.apache.ace.obr.storage
-Bundle-Version: 2.0.2
+Bundle-Version: 2.1.0
 Bundle-Name: Apache ACE OBR Storage
 Bundle-Description: Registers a file based BundleStore for the OBR
\ No newline at end of file

Modified: ace/trunk/org.apache.ace.obr/test/org/apache/ace/obr/servlet/BundleServletTest.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.obr/test/org/apache/ace/obr/servlet/BundleServletTest.java?rev=1802389&r1=1802388&r2=1802389&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.obr/test/org/apache/ace/obr/servlet/BundleServletTest.java (original)
+++ ace/trunk/org.apache.ace.obr/test/org/apache/ace/obr/servlet/BundleServletTest.java Wed Jul 19 13:13:26 2017
@@ -214,6 +214,22 @@ public class BundleServletTest {
     }
 
     @Test()
+    public void testExistsInvalidResource() throws Exception {
+        m_requestFile = "UnknownFile";
+        m_bundleServlet.doHead(m_request, m_response);
+
+        assert m_status == HttpServletResponse.SC_NOT_FOUND : "We should have got response code " + HttpServletResponse.SC_NOT_FOUND + " and we got " + m_status;
+    }
+
+    @Test()
+    public void testExistsValidResource() throws Exception {
+        m_requestFile = "KnownFile";
+        m_bundleServlet.doHead(m_request, m_response);
+
+        assert m_status == HttpServletResponse.SC_OK : "We should have got response code " + HttpServletResponse.SC_NOT_FOUND + " and we got " + m_status;
+    }
+
+    @Test()
     public void testGetInValidResource() throws Exception {
         m_requestFile = "UnknownFile";
         m_bundleServlet.doGet(m_request, m_response);

Modified: ace/trunk/org.apache.ace.obr/test/org/apache/ace/obr/servlet/MockBundleStore.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.obr/test/org/apache/ace/obr/servlet/MockBundleStore.java?rev=1802389&r1=1802388&r2=1802389&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.obr/test/org/apache/ace/obr/servlet/MockBundleStore.java (original)
+++ ace/trunk/org.apache.ace.obr/test/org/apache/ace/obr/servlet/MockBundleStore.java Wed Jul 19 13:13:26 2017
@@ -33,6 +33,14 @@ public class MockBundleStore implements
         m_outFile = outFile;
     }
 
+    @Override
+    public boolean exists(String filePath) throws IOException {
+        if (filePath.equals("UnknownFile")) {
+            return false;
+        }
+        return true;
+    }
+
     public InputStream get(String fileName) throws IOException {
         if (fileName.equals("UnknownFile")) {
             return null;

Added: ace/trunk/run-obr/conf/org.apache.ace.obr.servlet.cfg
URL: http://svn.apache.org/viewvc/ace/trunk/run-obr/conf/org.apache.ace.obr.servlet.cfg?rev=1802389&view=auto
==============================================================================
--- ace/trunk/run-obr/conf/org.apache.ace.obr.servlet.cfg (added)
+++ ace/trunk/run-obr/conf/org.apache.ace.obr.servlet.cfg Wed Jul 19 13:13:26 2017
@@ -0,0 +1,3 @@
+# Licensed to the Apache Software Foundation (ASF) under the terms of ASLv2 (http://www.apache.org/licenses/LICENSE-2.0).
+
+obrBaseUrl = http://${org.apache.ace.obr}/obr/