diff --git a/Engine/swift/storlet_middleware/storlet_handler.py b/Engine/swift/storlet_middleware/storlet_handler.py index b14ed783..d538bc71 100755 --- a/Engine/swift/storlet_middleware/storlet_handler.py +++ b/Engine/swift/storlet_middleware/storlet_handler.py @@ -142,7 +142,8 @@ class StorletHandlerMiddleware(object): if not is_success(orig_resp.status_int): return orig_resp - if self._is_slo_get_request(req, orig_resp, account, \ + if self._is_range_request(req) == True or \ + self._is_slo_get_request(req, orig_resp, account, \ container, obj) or \ self.proxy_only_storlet_execution == True: # For SLOs, and proxy only mode @@ -207,7 +208,8 @@ class StorletHandlerMiddleware(object): gateway.augmentStorletRequest(req) original_resp = req.get_response(self.app) - if self._is_slo_get_request(req, original_resp, account, \ + if self._is_range_request(req) == True or \ + self._is_slo_get_request(req, original_resp, account, \ container, obj) or \ self.proxy_only_storlet_execution == True: # SLO / proxy only case: @@ -280,6 +282,16 @@ class StorletHandlerMiddleware(object): return req.get_response(self.app) + ''' + Determines whether the request is a byte-range request + args: + req: the request + ''' + def _is_range_request(self, req): + if 'Range' in req.headers: + return True + return False + ''' Determines from a GET request and its associated response if the object is a SLO diff --git a/StorletSamples/HalfStorlet/build.xml b/StorletSamples/HalfStorlet/build.xml new file mode 100644 index 00000000..a4a202b0 --- /dev/null +++ b/StorletSamples/HalfStorlet/build.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/StorletSamples/HalfStorlet/src/com/ibm/storlet/half/HalfStorlet.java b/StorletSamples/HalfStorlet/src/com/ibm/storlet/half/HalfStorlet.java new file mode 100644 index 00000000..9d39fee5 --- /dev/null +++ b/StorletSamples/HalfStorlet/src/com/ibm/storlet/half/HalfStorlet.java @@ -0,0 +1,84 @@ +/*---------------------------------------------------------------------------- + * Copyright IBM Corp. 2015, 2015 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. + * --------------------------------------------------------------------------- + */ + +/*============================================================================ + 07-Jul-2015 cdoron Initial implementation. + ===========================================================================*/ + +package com.ibm.storlet.half; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.io.InputStream; +import java.io.OutputStream; + +import com.ibm.storlet.common.IStorlet; +import com.ibm.storlet.common.StorletException; +import com.ibm.storlet.common.StorletInputStream; +import com.ibm.storlet.common.StorletLogger; +import com.ibm.storlet.common.StorletObjectOutputStream; +import com.ibm.storlet.common.StorletOutputStream; + +public class HalfStorlet implements IStorlet +{ + @Override + public void invoke( ArrayList inputStreams, + ArrayList outputStreams, + Map parameters, + StorletLogger log ) + throws StorletException { + log.emitLog("HalfStorlet Invoked"); + + StorletInputStream sis = inputStreams.get(0); + + StorletObjectOutputStream storletObjectOutputStream; + storletObjectOutputStream = (StorletObjectOutputStream)outputStreams.get(0); + storletObjectOutputStream.setMetadata(sis.getMetadata()); + + /* + * Copy every other byte from input stream to output stream + */ + log.emitLog("Copying every other byte"); + StorletInputStream psis = (StorletInputStream)inputStreams.get(0); + InputStream is; + is = psis.getStream(); + + OutputStream os = storletObjectOutputStream.getStream(); + try { + log.emitLog(new Date().toString() + "About to read from input"); + int a; + boolean bool = true; + while ( (a = is.read()) != -1 ) { + if (bool) + os.write(a); + bool = !bool; + } + } catch (Exception e) { + log.emitLog("Copying every other byte from input stream to output stream failed: " + e.getMessage()); + throw new StorletException("Copying every other byte from input stream to output stream failed: " + + e.getMessage()); + } finally { + try { + is.close(); + os.close(); + } catch (IOException e) { } + } + log.emitLog("HalfStorlet Invocation done"); + } +} diff --git a/SystemTests/half_storlet_test.py b/SystemTests/half_storlet_test.py new file mode 100644 index 00000000..04634ccc --- /dev/null +++ b/SystemTests/half_storlet_test.py @@ -0,0 +1,144 @@ +'''------------------------------------------------------------------------- +Copyright IBM Corp. 2015, 2015 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. +-------------------------------------------------------------------------''' + +import os +import json +import random +import string +from sys_test_params import * +from swiftclient import client as c + +from storlets_test_utils import put_storlet_containers, put_storlet_object, \ + progress, progress_ln, progress_msg + +'''------------------------------------------------------------------------''' +# Test Constants +HALF_PATH_TO_BUNDLE ='../StorletSamples/HalfStorlet/bin/' +HALF_STORLET_NAME='halfstorlet-1.0.jar' +HALF_SOURCE_FILE = 'source.txt' + +'''------------------------------------------------------------------------''' +def put_storlet_input_object(url, token): + resp = dict() + metadata = {'X-Object-Meta-Testkey':'tester'} + f = open('%s/%s' %(HALF_PATH_TO_BUNDLE, HALF_SOURCE_FILE),'r') + c.put_object(url, token, 'myobjects', HALF_SOURCE_FILE, f, + content_type = "application/octet-stream", + headers = metadata, + response_dict = resp) + f.close() + status = resp.get('status') + assert (status == 200 or status == 201) + +'''------------------------------------------------------------------------''' +def deploy_storlet(url,token): + #No need to create containers every time + #put_storlet_containers(url, token) + put_storlet_object( url, token, + HALF_STORLET_NAME, + HALF_PATH_TO_BUNDLE, + '', + 'com.ibm.storlet.half.HalfStorlet') + put_storlet_input_object( url, token ) + +'''------------------------------------------------------------------------''' + +def invoke_storlet(url, token, op, params = None, global_params = None, headers = None): + if params != None: + querystring='' + for key in params: + querystring += '%s=%s,' % (key, params[key]) + querystring = querystring[:-1] + else: + querystring = None + + metadata = {'X-Run-Storlet': HALF_STORLET_NAME} + if headers: + metadata.update(headers) + + if op == 'GET': + # Get original object + original_headers, original_content = c.get_object(url, token, + 'myobjects', + HALF_SOURCE_FILE, + response_dict=dict()) + #print original_headers + file_length = int(original_headers['content-length']) + processed_headers, returned_content = c.get_object(url, token, + 'myobjects', + HALF_SOURCE_FILE, + query_string = querystring, + response_dict=dict(), + headers=metadata, + resp_chunk_size = file_length) + processed_content = '' + for chunk in returned_content: + if chunk: + processed_content+=chunk + + assert(original_headers['X-Object-Meta-Testkey'.lower()] == processed_headers['X-Object-Meta-Testkey'.lower()]) + return processed_content + + + if op == 'PUT': + # PUT a random file + response = dict() + uploaded_content = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(1024)) + random_md = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(32)) + #content_length = 1024 + content_length = None + headers = {'X-Run-Storlet': HALF_STORLET_NAME, + 'X-Object-Meta-Testkey' : random_md } + c.put_object(url, token, 'myobjects', 'half_random_source', uploaded_content, + content_length, None, None, "application/octet-stream", + headers, None, None, querystring, response) + resp_headers, saved_content = c.get_object(url, token, + 'myobjects', + 'half_random_source', + response_dict=dict()) + + if params != None and params.get('double',None) == 'true': + assert(uploaded_content==saved_content[:1024]) + assert(uploaded_content==saved_content[1024:]) + else: + assert(uploaded_content == saved_content) + + if params != None and params.get('execute',None) != None: + assert(resp_headers['X-Object-Meta-Execution result'.lower()] == '42') + + assert(resp_headers['X-Object-Meta-Testkey'.lower()] == random_md) + +'''------------------------------------------------------------------------''' +def main(): + os_options = {'tenant_name': ACCOUNT} + url, token = c.get_auth( 'http://' + AUTH_IP + ":" + + AUTH_PORT + '/v2.0', + ACCOUNT + ':' + USER_NAME, + PASSWORD, + os_options = os_options, + auth_version = '2.0' ) + + print 'Deploying Half storlet and dependencies' + + deploy_storlet(url, token) + + print "Invoking Half storlet on GET" + assert (invoke_storlet(url, token,'GET') == 'acegikmn') + print "Invoking Half storlet on GET with byte ranges" + assert (invoke_storlet(url, token,'GET', headers = {'range': 'bytes=5-10'}) == 'fhj') + +'''------------------------------------------------------------------------''' +if __name__ == "__main__": + main() diff --git a/SystemTests/sys_test.py b/SystemTests/sys_test.py index 3b7f0540..ee22b188 100644 --- a/SystemTests/sys_test.py +++ b/SystemTests/sys_test.py @@ -164,6 +164,7 @@ def main(): os.system('python execdep_test.py') os.system('python identity_storlet_test.py') + os.system('python half_storlet_test.py') os.system('python metadata_storlet_test.py') os.system('python SLO_test.py') diff --git a/build.xml b/build.xml index 027800eb..9d6cffa8 100644 --- a/build.xml +++ b/build.xml @@ -71,6 +71,7 @@ +