myexperiment-hackers
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[myexperiment-hackers] [2909] branches/datasets: Fixed merge issues and


From: noreply
Subject: [myexperiment-hackers] [2909] branches/datasets: Fixed merge issues and zip download race condition
Date: Thu, 19 Jan 2012 09:07:09 -0500 (EST)

Revision
2909
Author
fbacall
Date
2012-01-19 09:07:09 -0500 (Thu, 19 Jan 2012)

Log Message

Fixed merge issues and zip download race condition

Modified Paths

Added Paths

Removed Paths

Diff

Modified: branches/datasets/app/controllers/data_sets_controller.rb (2908 => 2909)


--- branches/datasets/app/controllers/data_sets_controller.rb	2012-01-19 10:38:36 UTC (rev 2908)
+++ branches/datasets/app/controllers/data_sets_controller.rb	2012-01-19 14:07:09 UTC (rev 2909)
@@ -53,7 +53,7 @@
 
   def download
     if @data_set.estimate_size < Conf.max_upload_size
-      send_data File.read(@data_set.create_zip(current_user).path), :disposition => "attachment",
+      send_file @data_set.create_zip(current_user).path, :disposition => "attachment",
                 :filename => @data_set.archive_file_name
     else
       flash[:error] = "This data set is too big to download as a zip file. You may download each data item separately, however"

Modified: branches/datasets/app/controllers/packs_controller.rb (2908 => 2909)


--- branches/datasets/app/controllers/packs_controller.rb	2012-01-19 10:38:36 UTC (rev 2908)
+++ branches/datasets/app/controllers/packs_controller.rb	2012-01-19 14:07:09 UTC (rev 2909)
@@ -91,14 +91,13 @@
     image_hash["pack"] = "./public/images/" + method_to_icon_filename("pack")
     image_hash["link"] = "./public/images/" + method_to_icon_filename("remote")
     image_hash["denied"] = "./public/images/" + method_to_icon_filename("denied")
-    
-    @pack.create_zip(current_user, image_hash)
-    
+
     if allow_statistics_logging(@pack)
       @download = Download.create(:contribution => @pack.contribution, :user => (logged_in? ? current_user : nil), :user_agent => request.env['HTTP_USER_AGENT'], :accessed_from_site => accessed_from_website?())
     end
-    
-    send_file @pack.archive_file_path, :disposition => 'attachment'
+
+    send_file @pack.create_zip(current_user, image_hash).path, :disposition => 'attachment',
+              :filename => @pack.archive_file
   end
   
   # GET /packs/new

Modified: branches/datasets/app/helpers/application_helper.rb (2908 => 2909)


--- branches/datasets/app/helpers/application_helper.rb	2012-01-19 10:38:36 UTC (rev 2908)
+++ branches/datasets/app/helpers/application_helper.rb	2012-01-19 14:07:09 UTC (rev 2909)
@@ -106,7 +106,7 @@
     end
     
     title = truncate_to ? truncate(network.title, :length => truncate_to) : network.title
-    return link_to(h(title), group_url(network))
+    return link_to(h(title), network_url(network))
   end
   
   def avatar(user_id, size=200, url=""
@@ -367,7 +367,7 @@
     when "Blob", "Pack", "Blog", "DataSet"
       resource = eval(contributabletype).find_by_id(contributableid)
       name = h(resource.label)
-      return link ? link_to(name, poly_url(resource)) : name
+      return link ? link_to(name, polymorphic_url(resource)) : name
     when "Workflow"
       if w = Workflow.find(:first, :conditions => ["id = ?", contributableid])
         name = h(w.title)
@@ -1586,17 +1586,4 @@
     @layout = layout || {"layout" => Conf.page_template, "stylesheets" => [Conf.stylesheet]}
   end
 
-
-  #todo: fixme: hack! Work around for blob/group routes
-  def poly_url(object, options = {})
-    if object.kind_of?(Blob)
-      file_url(object, options = {})
-    elsif object.kind_of?(Network)
-      group_url(object, options = {})
-    else
-      polymorphic_url(object, options = {})
-    end
-  end
-
-
 end

Modified: branches/datasets/app/models/data_item.rb (2908 => 2909)


--- branches/datasets/app/models/data_item.rb	2012-01-19 10:38:36 UTC (rev 2908)
+++ branches/datasets/app/models/data_item.rb	2012-01-19 14:07:09 UTC (rev 2909)
@@ -8,6 +8,7 @@
   # Things that can be attached as data to a workflow port:
   # NOTE: If adding a new data type, be sure to update app/helpers/data_sets_helper.rb with a
   #       description of the new type. Also check the estimate_size method of models/data_set.rb still makes sense.
+  #       Also look at the create_zip in the same file.
 
   # - These types of data are independent from the DataItem and should remain in the DB regardless of what happens to
   #   the DataItem connected to it:

Modified: branches/datasets/app/models/data_set.rb (2908 => 2909)


--- branches/datasets/app/models/data_set.rb	2012-01-19 10:38:36 UTC (rev 2908)
+++ branches/datasets/app/models/data_set.rb	2012-01-19 14:07:09 UTC (rev 2909)
@@ -8,11 +8,12 @@
   SUPPORTED_TYPES = ["Taverna 1", "Taverna 2", "RapidMiner"].freeze  # Only supporting these for now, due to how input/output
                                                                      #  ports are fetched
 
-  TEMPFILE_LIFE = 2 * (60 * 60 * 24).freeze  # Files older than this will be deleted when the create_zip method is called.
+  TEMPFILE_LIFE = (2 * 24 * 60 * 60).freeze  # Files older than this will be deleted when the create_zip method is called.
                                      # Set to 2 days to ensure that a file isn't deleted whilst someone is downloading it
                                      # Could maybe go in settings.yml?
 
-  include ZipInMemory
+  include EasyZip
+
   include ActionController::UrlWriter #To generate URLs for the metadata file of the zip archive
   default_url_options[:host] = URI.parse(Conf.base_uri).host
 
@@ -56,12 +57,12 @@
     # Todo: Fixme: This needs to happen when server is first set up, too.
     #              If 1 million people create zip files on a certain day, and then no one else does ever again,
     #                                                                  1 million files will stay on the system!
-    FileUtils.rm(Dir.glob("#{DataSet.archive_temp_folder}/*").select{|f| (Time.now - File.stat(f).mtime) > TEMPFILE_LIFE}, :force => true)
+    FileUtils.rm(Dir.glob("#{DataSet.archive_temp_folder}/data_set_*.zip").select{|f| (Time.now - File.stat(f).mtime) > TEMPFILE_LIFE}, :force => true)
 
     # Create the zip file
-    new_zip(File.new(DataSet.archive_temp_path, "w+")) do |zipfile|
+    new_zip(File.new(self.archive_temp_path, "w+")) do |zipfile|
 
-      #Add each data item to the zip. Inputs/outputs are seperated into folders. Each input/output datum is named as
+      #Add each data item to the zip. Inputs/outputs are separated into folders. Each input/output datum is named as
       # the port it relates to, followed by a dash, followed by either the name of the file if it is a blob,
       # or "text.txt" if it is just text data.
 
@@ -103,8 +104,8 @@
     "tmp/data_sets"
   end
 
-  def self.archive_temp_path
-    "#{archive_temp_folder}/#{Time.now.strftime("%Y%m%d_%H%M%S")}_#{rand(1000000)}.zip"
+  def archive_temp_path
+    "#{DataSet.archive_temp_folder}/data_set_#{id}_#{Time.now.strftime("%Y%m%d_%H%M%S")}_#{rand(1000000)}.zip"
   end
 
   def metadata(stats)

Modified: branches/datasets/app/models/pack.rb (2908 => 2909)


--- branches/datasets/app/models/pack.rb	2012-01-19 10:38:36 UTC (rev 2908)
+++ branches/datasets/app/models/pack.rb	2012-01-19 14:07:09 UTC (rev 2909)
@@ -13,6 +13,12 @@
 
 class Pack < ActiveRecord::Base
 
+  TEMPFILE_LIFE = (2 * 24 * 60 * 60).freeze  # Files older than this will be deleted when the create_zip method is called.
+                                     # Set to 2 days to ensure that a file isn't deleted whilst someone is downloading it
+                                     # Could maybe go in settings.yml?
+
+  include EasyZip
+
   acts_as_site_entity :owner_text => 'Creator'
 
   acts_as_contributable
@@ -71,6 +77,10 @@
     # single declaration point of where the zip archives for downloadable packs would live
     return "tmp/packs"
   end
+
+  def archive_temp_path
+    "#{Pack.archive_folder}/pack_#{id}_#{Time.now.strftime("%Y%m%d_%H%M%S")}_#{rand(1000000)}.zip"
+  end
   
   def archive_file(no_timestamp=false)
     # the name of the zip file, where contents of current pack will be placed
@@ -112,7 +122,7 @@
     if [Workflow, Workflow::Version, Blob].include?(item.class)
       data = ""
     elsif item.kind_of?(DataSet)
-      data = ""
+      data = ""
     else
       raise "Don't know how to get the contents of #{item.class.name.pluralize}"
     end
@@ -129,63 +139,47 @@
     # check if the temp folder for storing packs exists
     FileUtils.mkdir(Pack.archive_folder) if not File.exists?(Pack.archive_folder)
 
-    # check to see if the file exists already, and if it does, delete it
-    # (regular _expression_ needed to take care of timestamps)
-    FileUtils.rm Dir.glob(archive_file_path(true).gsub(/[\[\]]/, "?")), :force => true
+    # Delete old temp zip files
+    # Todo: Fixme: This needs to happen when server is first set up, too.
+    #              If 1 million people create zip files on a certain day, and then no one else does ever again,
+    #                                                                  1 million files will stay on the system!
+    FileUtils.rm(Dir.glob("#{Pack.archive_folder}/pack_*.zip").select{|f| (Time.now - File.stat(f).mtime) > TEMPFILE_LIFE}, :force => true)
 
-    # create the zip file
-    zipfile = Zip::ZipFile.open(archive_file_path, Zip::ZipFile::CREATE)
+    # Create the zip file
+    new_zip(File.new(self.archive_temp_path, "w+")) do |zipfile|
 
-    # will keep a list of all filenames that are put into the archive (to delete temp files later)
-    zip_filenames = []
+      # Add contributables to the zip file
+      self.contributable_entries.each do |entry|
+        item = entry.contributable
+        item_type = item.class.name
 
-    # Add contributables to the zip file
-    self.contributable_entries.each do |entry|
-      item = entry.contributable
-      item_type = item.class.name
+        if entry.contributable_version
+          item = item.find_version(entry.contributable_version)
+        end
 
-      if entry.contributable_version
-        item = item.find_version(entry.contributable_version)
+        if item_type != "Pack" && Authorization.is_authorized?('download', nil, item, user)
+          stats[:downloaded][item_type] ||= []
+          stats[:downloaded][item_type] << entry
+          filename = item_filename(item)
+          zipfile.add_data("#{item_type.model_alias.underscore.pluralize}/#{filename}",item_data(item, user))
+        elsif Authorization.is_authorized?('view', nil, item, user)
+          stats[:view_only][item_type] ||= []
+          stats[:view_only][item_type] << entry
+        else
+          stats[:hidden] << entry
+        end
       end
 
-      if item_type != "Pack" && Authorization.is_authorized?('download', nil, item, user)
-        stats[:downloaded][item_type] ||= []
-        stats[:downloaded][item_type] << entry
-        filename = item_filename(item)
-        zip_filenames << filename
-        zipfile.get_output_stream("#{item_type.model_alias.underscore.pluralize}/#{filename}") { |stream| stream.write(item_data(item, user))}
-      elsif Authorization.is_authorized?('view', nil, item, user)
-        stats[:view_only][item_type] ||= []
-        stats[:view_only][item_type] << entry
-      else
-        stats[:hidden] << entry
+      # Add metadata files
+      zipfile.add_data("_Pack Info.txt", text_metadata(stats))
+      zipfile.add_data("index.html", html_metadata(stats))
+
+      # Add CSS and images
+      zipfile.add_data("index.css", File.read("./public/stylesheets/pack-snapshot.css"))
+      list_images_hash.each_key do |k|
+        zipfile.add_data("_images/#{k}.png", File.read(list_images_hash[k]))
       end
     end
-
-    zip_filenames << "_Pack Info.txt"
-    zipfile.get_output_stream("_Pack Info.txt") { |stream| stream.write(text_metadata(stats)) }
-    zip_filenames << "index.html"
-    zipfile.get_output_stream("index.html") { |stream| stream.write(html_metadata(stats)) }
-    zipfile.add("index.css", "./public/stylesheets/pack-snapshot.css")
-    zipfile.mkdir("_images") # LIST BULLET IMAGES
-    zipfile.add("_images/workflow.png", list_images_hash["workflow"])
-    zipfile.add("_images/file.png", list_images_hash["file"])
-    zipfile.add("_images/pack.png", list_images_hash["pack"])
-    zipfile.add("_images/link.png", list_images_hash["link"])
-    zipfile.add("_images/denied.png", list_images_hash["denied"])
-
-    zipfile.close() # finalize the archive file
-
-    # set read permissions on the zip file
-    File.chmod(0644, archive_file_path)
-
-    # remove any temporary files that were created while creating the zip file
-    # (these are created in the same place, where the zip file is stored)
-    zip_filenames.each do |temp_file|
-      FileUtils.rm Dir.glob(Pack.archive_folder + "/" + "#{temp_file}*"), :force => true # 'force' option makes sure that exceptions are never raised
-    end
-
-    archive_file_path
   end
 
   def text_metadata(stats)

Modified: branches/datasets/app/views/data_items/data_types/blob/_data.html.erb (2908 => 2909)


--- branches/datasets/app/views/data_items/data_types/blob/_data.html.erb	2012-01-19 10:38:36 UTC (rev 2908)
+++ branches/datasets/app/views/data_items/data_types/blob/_data.html.erb	2012-01-19 14:07:09 UTC (rev 2909)
@@ -3,15 +3,15 @@
   <% can_view = can_download || Authorization.is_authorized?("view", nil, data, current_user) %>
   <% if can_view -%>
     <div style="float:left">
-      <b>Title:</b> <%= link_to "#{h truncate(data.label, :length => 70)}", file_path(data), :class => "file_link" %><br/>
+      <b>Title:</b> <%= link_to "#{h truncate(data.label, :length => 70)}", blob_path(data), :class => "file_link" %><br/>
       <b>Type: </b> <%= h data.content_type.title %><br/>
       <b>Size: </b> <%= number_to_human_size(data.content_blob.data.size) %><br/>
     </div>
     <div class="actions">
-      <%= icon('show', file_path(data), nil, nil, 'View') %><br/>
+      <%= icon('show', blob_path(data), nil, nil, 'View') %><br/>
 
       <% if can_download %>
-        <%= icon('download', download_file_path(data), nil, nil, 'Download') %><br/>
+        <%= icon('download', download_blob_path(data), nil, nil, 'Download') %><br/>
       <% end %>
 
     </div>

Modified: branches/datasets/app/views/packs/_add_item.rhtml (2908 => 2909)


--- branches/datasets/app/views/packs/_add_item.rhtml	2012-01-19 10:38:36 UTC (rev 2908)
+++ branches/datasets/app/views/packs/_add_item.rhtml	2012-01-19 14:07:09 UTC (rev 2909)
@@ -48,7 +48,7 @@
 						<td>
 							<select id="uri2" name="uri" style="width: 320px;">
 					  		<% contributions.reject {|c| c == @pack || c.contributable.nil?}.each do |c| -%>
-                  <option value="<%= poly_url(c.contributable) -%>">
+                  <option value="<%= polymorphic_url(c.contributable) -%>">
                     <%= "#{visible_name c.contributable_type}: #{c.contributable.label}" -%>
                   </option>
 					  		<% end -%>

Copied: branches/datasets/lib/easy_zip.rb (from rev 2868, branches/datasets/lib/zip_in_memory.rb) (0 => 2909)


--- branches/datasets/lib/easy_zip.rb	                        (rev 0)
+++ branches/datasets/lib/easy_zip.rb	2012-01-19 14:07:09 UTC (rev 2909)
@@ -0,0 +1,49 @@
+# myExperiment: lib/easy_zip.rb
+#
+# Copyright (c) 2011 University of Manchester and the University of Southampton.
+# See license.txt for details.
+
+
+# An easier way of making zip files without necessarily using the file system.
+# This avoids issues to do with deleting files after sending to the client, without deleting them before they are
+#  fully sent.
+# Adapted from from:
+# http://blog.devinterface.com/2010/02/create-zip-files-on-the-fly/
+# and
+# http://stackoverflow.com/questions/4797315/rails-on-the-fly-streaming-of-output-in-zip-format
+
+require 'zip/zip'
+
+module EasyZip
+
+  # Takes an IO object, eg. a stream.
+  # Writes the zip file to the object and returns it back.
+  def new_zip(io)
+    Zip::IOOutputStream.open(io) do |z|
+      yield z
+    end
+    io
+  end
+
+end
+
+module Zip
+  # Extension to ZipOutputStream to make it take a generic IO object rather than a file path.
+  class IOOutputStream < ZipOutputStream
+    def initialize io
+      super '-'
+      @outputStream = io
+    end
+
+    def stream
+      @outputStream
+    end
+
+    # A quick way to add data to the zip file. Takes the title of the file to be written to the archive
+    # (can include directories, which will be created where necessary), and the data for the file.
+    def add_data(title, data)
+      self.put_next_entry title
+      self << data
+    end
+  end
+end

Deleted: branches/datasets/lib/zip_in_memory.rb (2908 => 2909)


--- branches/datasets/lib/zip_in_memory.rb	2012-01-19 10:38:36 UTC (rev 2908)
+++ branches/datasets/lib/zip_in_memory.rb	2012-01-19 14:07:09 UTC (rev 2909)
@@ -1,48 +0,0 @@
-# myExperiment: lib/zip_in_memory.rb
-#
-# Copyright (c) 2011 University of Manchester and the University of Southampton.
-# See license.txt for details.
-
-
-# An easier way of making zip files without necessarily using the file system.
-# This avoids issues to do with deleting files after sending to the client, without deleting them before they are
-#  fully sent.
-# Adapted from from:
-# http://blog.devinterface.com/2010/02/create-zip-files-on-the-fly/
-# and
-# http://stackoverflow.com/questions/4797315/rails-on-the-fly-streaming-of-output-in-zip-format
-
-require 'zip/zip'
-
-module ZipInMemory
-
-  # Takes an IO object, eg. a stream.
-  # Writes the zip file to the object and returns it back.
-  def new_zip(io)
-    Zip::IOOutputStream.open(io) do |z|
-      yield z
-    end
-  end
-
-end
-
-module Zip
-  # Extension to ZipOutputStream to make it take a generic IO object rather than a file path.
-  class IOOutputStream < ZipOutputStream
-    def initialize io
-      super '-'
-      @outputStream = io
-    end
-
-    def stream
-      @outputStream
-    end
-
-    # A quick way to add data to the zip file. Takes the title of the file to be written to the archive
-    # (can include directories, which will be created where necessary), and the data for the file.
-    def add_data(title, data)
-      self.put_next_entry title
-      self << data
-    end
-  end
-end

Modified: branches/datasets/test/functional/packs_controller_test.rb (2908 => 2909)


--- branches/datasets/test/functional/packs_controller_test.rb	2012-01-19 10:38:36 UTC (rev 2908)
+++ branches/datasets/test/functional/packs_controller_test.rb	2012-01-19 14:07:09 UTC (rev 2909)
@@ -82,11 +82,15 @@
   end
 
   test "can download a pack" do
-    get :download, :id => packs(:pack_1).id
+    pack = packs(:pack_1)
+    existing_files = Dir.glob("#{RAILS_ROOT}/#{Pack.archive_folder}/pack_#{pack.id}*")
 
+    get :download, :id => pack.id
     assert_response :success
-    assert File.exists?(assigns(:pack).archive_file_path)
 
-    FileUtils.rm assigns(:pack).archive_file_path
+    new_files = (Dir.glob("#{RAILS_ROOT}/#{Pack.archive_folder}/pack_#{pack.id}*") - existing_files)
+    assert new_files.size == 1
+
+    FileUtils.rm(new_files)
   end
 end

reply via email to

[Prev in Thread] Current Thread [Next in Thread]