[Title] Refactored the module about file-transfer
authordonghee yang <donghee.yang@samsung.com>
Wed, 10 Oct 2012 08:06:58 +0000 (17:06 +0900)
committerdonghee yang <donghee.yang@samsung.com>
Wed, 10 Oct 2012 08:06:58 +0000 (17:06 +0900)
src/build_server/BuildComm.rb [deleted file]
src/common/BuildComm.rb [new file with mode: 0644]
src/common/fileTransfer.rb

diff --git a/src/build_server/BuildComm.rb b/src/build_server/BuildComm.rb
deleted file mode 100644 (file)
index d8a0291..0000000
+++ /dev/null
@@ -1,675 +0,0 @@
-require "socket"
-
-=begin
-
- BuildComm.rb
-
-Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
-
-Contact:
-Taejun Ha <taejun.ha@samsung.com>
-Jiil Hyoun <jiil.hyoun@samsung.com>
-Donghyuk Yang <donghyuk.yang@samsung.com>
-DongHee Yang <donghee.yang@samsung.com>
-
-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.
-
-Contributors:
-- S-Core Co., Ltd
-=end
-
-$LOAD_PATH.unshift File.dirname(__FILE__)+"/src/common"
-require "log"
-require 'timeout'
-require "fileTransfer"
-require "net/ftp"
-require 'thread'
-
-ATTEMPTS = ["first", "second", "third"]
-
-class BuildCommServer
-       VERSION = "1.5.0"
-
-       private_class_method :new
-
-       def initialize(port, log, ftp_url, cache_dir)
-               @port = port
-               @log = log
-               @ftp_url = ftp_url
-               @cache_dir = cache_dir
-               @tcp_server = TCPServer.open( port )
-               @download_cache_mutex = Mutex.new
-       end
-
-    def self.create(port, log, ftp_url=nil, cache_dir=nil)
-               # checking port is  available
-               if port_open? port then
-                       raise "Port \"#{@port}\" is already in use."
-               end
-
-               if log.nil? then
-                       log = DummyLog.new
-               end
-
-               # create cache dir if not nil
-               if not cache_dir.nil? and not File.exist? cache_dir then 
-                       FileUtils.mkdir_p cache_dir 
-               end
-
-        return new(port, log, ftp_url, cache_dir)
-    end
-
-
-       # wait for connection and handle request
-       def wait_for_connection(quit_loop)
-               while( not quit_loop )
-                       req = @tcp_server.accept                
-       
-                       begin
-                               yield req if block_given?
-                       rescue
-                               @log.error $!
-                               @log.error "Caught a connection exception"
-                               req.close
-                       end
-               end
-       end
-
-
-       # terminate
-       def terminate
-               @tcp_server.close()
-       end
-
-       
-       # send_begin
-       def self.send_begin( req )
-               send( req, "=BEGIN,#{VERSION}")
-       end
-
-
-       def self.send_end( req )
-               send( req, "=END")
-       end
-
-
-       def self.send_chk( req )
-               send( req, "=CHK" )
-       end
-
-
-       def self.send( req, line )
-               begin
-                       if not req.nil? then
-                               req.puts line
-                       end
-               rescue
-                       raise "Connection is closed"
-               end
-       end
-
-
-       def send_file(req, src_file)
-               # 1. send "READY"
-               # 2. If "FTP,ip,username,passwd" is received,
-               #     Upload the src file using server's ftp_url.
-               #     if then ftp_url is nil, use the url on "FTP" message instead
-               #     After uploading, send "UPLOADED,ftp_filepath"
-               # 3. If "SUCC" is received, remove the file on FTP server
-
-        begin
-            if not File.exist? src_file then
-                @log.error "\"#{src_file}\" file does not exist"
-                req.puts "ERROR"
-                return false
-            end
-
-            req.puts "READY"
-                       @log.info "Ready to upload file"                        
-            while l = req.gets()
-                tok = l.split(",").map { |x| x.strip }
-                cmd = tok[0].strip
-                if cmd == "FTP" then
-                    if tok.count < 5 then
-                        @log.error "Server received wrong REQ : #{l.strip}"
-                        req.puts "ERROR"
-                        return false
-                    end
-
-                                       # get ftp connection info
-                                       if @ftp_url.nil? then
-                       ip = tok[1].strip
-                       port = tok[2].strip
-                       username = tok[3].strip
-                       passwd = tok[4].strip
-                       @log.info "Server received ftp server infomation from client : [#{ip}, #{port}]"
-                                       else
-                       url_contents = Utils.parse_ftpserver_url(@ftp_url)
-                                               ip = url_contents[0]
-                                               port = url_contents[1]
-                       username = url_contents[2]
-                       passwd = url_contents[3]
-                                       end
-
-                                       # upload to ftp server
-                                       ftp_filepath = nil                                      
-                                       @trans = FileTransfer.new(ip, port, username, passwd, @log)
-                                       for attempt in ATTEMPTS
-                           ftp_filepath = @trans.putfile( src_file )
-                                               if !ftp_filepath.nil? then break;
-                                               else @log.info "Server is the #{attempt} upload attempt fails" end
-                                       end                                             
-                                       if ftp_filepath.nil? then
-                                               req.puts "ERROR"                                                        
-                                               return false 
-                                       else 
-                                               @log.info "Server is the #{attempt} successful attempt to upload file: [#{File.basename(src_file)}]" 
-                                       end
-                    req.puts "UPLOADED,#{ftp_filepath}"
-                elsif cmd == "SUCC" then
-                    @log.info "Client downloaded file successfully"
-                    @trans.cleandir( ftp_filepath )
-                    @log.info "Cleaned temporary dir on FTP server: #{ftp_filepath}"
-                    break 
-                               elsif cmd == "ERROR" then                                       
-                    @log.error "Client failed to download file"
-                                       return false                                    
-                               end
-            end                
-        rescue => e
-            puts "[BuildCommServer] Exception"
-                       @log.error e.message
-                       @log.error e.backtrace.inspect
-                       return false                    
-               end
-
-        return true
-       end
-
-
-       # NOTE. dst_file can be directory
-       def receive_file(req, dst_file)
-               # 1. send "READY"
-               # 2. If "UPLOADED,ip,port,file_path,username,passwd" is received,
-               #    Download the file using my ftp_url.
-               #    If ftp_url is nil, use the url on "UPLOADED" messge instead
-               #    After downloading it, send "SUCC"
-
-               begin
-            req.puts "READY"
-                       while l = req.gets()
-                               tok = l.split(",").map { |x| x.strip }
-                               cmd = tok[0].strip
-                               if cmd == "CHECK_CACHE" then
-                                       file_name = tok[1]
-                                       file_size = tok[2].to_i
-                                       checksum = tok[3]
-       
-                                       # check download cache                          
-                               if File.exist? dst_file and File.directory? dst_file then 
-                                               target_file = File.join(dst_file,file_name)
-                               else 
-                                               target_file = dst_file 
-                                       end
-                                       if not @cache_dir.nil? and 
-                                               check_download_cache( target_file, file_size, checksum ) then
-
-                                               @log.info "Download cache hit! Copied from cache.: #{file_name}"
-                                               req.puts "CACHED"
-                                               break   
-                                       else
-                                               @log.info "Cached file not found!#{file_name}"
-                                               req.puts "NOT_CACHED"
-                                       end
-                               elsif cmd == "UPLOADED" then
-                                       @log.info "Client uploaded file to ftp server successful"
-                                       if tok.count < 6 then
-                                               @log.error "Server received wrong REQ : #{l.strip}"
-                                               req.puts "ERROR"
-                                               return false
-                                       end
-                                       filepath = tok[3].strip
-
-                                       # get ftp connection info
-                                       if @ftp_url.nil? then
-                                               ip = tok[1].strip
-                                               port = tok[2].strip
-                                               username = tok[4].strip
-                                               passwd = tok[5].strip
-                                               @log.info "Client sent ftp server infomations [#{ip}, #{port}]"
-                                       else
-                                               url_contents = Utils.parse_ftpserver_url(@ftp_url)
-                                               ip = url_contents[0]
-                                               port = url_contents[1]
-                                               username = url_contents[2]
-                                               passwd = url_contents[3]
-                                       end
-
-                                       # download from ftp server
-                                       dst_filepath = nil
-                                       @trans = FileTransfer.new(ip, port, username, passwd, @log)
-                                       for attempt in ATTEMPTS
-                           dst_filepath = @trans.getfile( filepath, dst_file )
-                                               if not dst_filepath.nil? then break
-                                               else 
-                                                       @log.warn "Server is the #{attempt} download attempt fails" 
-                                               end                                                     
-                                       end                                     
-                                       if dst_filepath.nil? then 
-                                                       req.puts "ERROR"                                                        
-                                                       return false                                                                                            
-                                       else @log.info " Server is the #{attempt} successful attempt to download" end
-                                       
-                                       # add to cache
-                                       if not @cache_dir.nil? then
-                                       if File.exist? dst_file and File.directory? dst_file then 
-                                                       target_file = File.join(dst_file,File.basename(dst_filepath))
-                                       else 
-                                                       target_file = dst_file 
-                                               end
-                                               add_download_cache(target_file)
-                                       end
-
-                    req.puts "SUCC"
-                    break
-                               elsif cmd == "ERROR" then
-                                       @log.error "Client failed to upload the file"
-                                       return false                            
-                               else
-                                       @log.warn "Unhandled message: #{l}"
-                               end
-                       end                             
-               rescue => e
-            puts "[BuildCommServer] Exception" 
-                       @log.error e.message
-                       @log.error e.backtrace.inspect
-                       return false                    
-               end
-
-        return true
-       end
-
-
-       def self.disconnect( req )
-               begin
-                       req.close
-               rescue
-               end
-       end
-
-       def self.port_open?( port )
-               Timeout::timeout(1) do
-                       begin
-                               TCPSocket.new("127.0.0.1",port).close
-                               true
-                       rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
-                               false
-                       end
-               end
-       rescue Timeout::Error
-               false
-       end     
-
-
-       private
-       def check_download_cache(dst_file, file_size, checksum )
-               file_name = File.basename(dst_file)
-               cache_file = "#{@cache_dir}/#{file_name}"
-
-               @download_cache_mutex.synchronize {
-                       found = false
-                       # check file exist
-                       if File.exist? cache_file and 
-                               File.size(cache_file) == file_size and
-                               Utils.checksum(cache_file) == checksum then
-
-                               # if hit , touch and copy
-                               FileUtils.touch cache_file
-                               FileUtils.copy_file(cache_file, dst_file)
-
-                               found = true
-                       end
-
-                       # refresh cache dir
-                       curr_time = Time.now
-                       Dir.entries(@cache_dir).each { |fname|
-                               if fname == "." or fname == ".." then next end
-                               file_path = "#{@cache_dir}/#{fname}"
-                               if File.mtime(file_path) + 3600 < curr_time then
-                                       FileUtils.rm_rf file_path
-                               end
-                       }
-
-                       return found
-               }
-       end
-
-
-       private
-       def add_download_cache(dst_file)
-               file_name = File.basename(dst_file)
-               cache_file = "#{@cache_dir}/#{file_name}"
-               @download_cache_mutex.synchronize {
-                       # copy & touch
-                       FileUtils.copy_file(dst_file, cache_file)
-                       FileUtils.touch cache_file
-               }
-       end
-end
-
-
-class BuildCommClient
-       VERSION = "1.5.0"
-
-       private_class_method :new
-
-       def initialize(socket, log)
-        @log = log
-               @socket = socket
-       end
-
-
-       # create
-       # if sec 0 or nil then not set timeout. it's timeout spec
-       def self.create(ip, port, log = nil, sec = 5)
-               # open socket
-               socket = nil
-               begin
-                       timeout(sec) do
-                               socket = TCPSocket.open( ip, port )
-                       end
-               rescue Timeout::Error
-                       return nil
-               rescue
-                       # unknown exception
-                       return nil
-               end
-
-               # refused
-               if socket.nil? then
-                       return nil
-               end
-
-               if log.nil? then
-                       log = DummyLog.new
-               end
-
-               return new(socket, log)
-       end
-
-
-       def send( msg )
-               if @socket.nil? then return false end
-
-               @socket.puts( msg )
-               return true
-       end     
-
-
-       def print_stream
-
-               begin
-                       l = @socket.gets()
-
-                       if l.nil? then 
-                               puts "Connection refused"
-                               return false 
-                       end
-
-                       # check protocol
-                       if not protocol_matched? l.strip then
-                               puts "Comm. Protocol version is mismatched! #{VERSION}"
-                               return false
-                       end
-
-               
-                       # get contents
-                       while line = @socket.gets()
-                               if line.strip == "=END" then break end
-                               if line.strip == "=CHK" then next end
-                               # print
-                               puts line.strip
-                       end
-               rescue
-                       puts "Connection closed"
-                       return false
-               end
-
-               return true
-       end
-
-
-       # handle 
-       def read_lines
-
-               begin
-                       # get first line
-                       l = nil
-                       timeout(5) do 
-                               l = @socket.gets()
-                       end
-
-                       if l.nil? then 
-                               return false 
-                       end
-
-                       # check protocol
-                       if not protocol_matched? l.strip then
-                               return false
-                       end
-
-                       # get contents
-                       while line = @socket.gets()
-                               if line.strip == "=END" then break end
-                               if line.strip == "=CHK" then next end
-
-                               # print
-                               yield line.strip if block_given?
-                       end
-               rescue Timeout::Error
-            puts "WARN: Connection timed out"
-                       return false
-               rescue => e
-            puts e.message
-                       return false
-               end
-
-               return true
-       end
-
-
-       # return result
-       def receive_data
-               result = []
-
-               begin
-                       l = @socket.gets()
-
-                       if l.nil? then 
-                               puts "Connection refused"
-                               return nil
-                       end
-
-                       # check protocol
-                       if not protocol_matched? l.strip then
-                               puts "Comm. Protocol version is mismatched! #{VERSION}"
-                               return nil
-                       end
-
-               
-                       # get contents
-                       while line = @socket.gets()
-                               if line.strip == "=END" then break end
-                               if line.strip == "=CHK" then next end
-                               # print
-                               result.push line.strip
-                       end
-               rescue
-                       puts "Connection closed"
-                       return nil
-               end
-
-               return result
-       end
-
-
-       def send_file(ip, port, username, passwd, src_file)
-        begin
-            l = @socket.gets()
-            if l.nil? then 
-                @log.error "[BuildCommClient] Connection refused"
-                               return false 
-                       end
-
-                       # check protocol
-                       if not protocol_matched? l.strip then
-                @log.error "[BuildCommClient] Comm. Protocol version is mismatched! #{VERSION}"
-                               return false
-                       end
-
-                       # 1. If "READY" is received, upload src file to FTP server
-                       #    After uploading it, send "UPLOADED,ip,file_path,username,passwd"
-                       # 2. If "SUCC" is received, remove the file on FTP server
-            while line = @socket.gets()
-                if line.strip == "READY" then
-                       @log.info "Server is ready to receive file"
-                                       file_name = File.basename(src_file)
-                                       file_size = File.size(src_file)
-                                       checksum = Utils.checksum(src_file)
-                    send "CHECK_CACHE,#{file_name},#{file_size},#{checksum}"
-                elsif line.strip == "CACHED" then
-                    @log.info "Server already has cached file"
-                elsif line.strip == "NOT_CACHED" then
-                       @log.info "Server doest not have cached file"
-
-                                       ftp_filepath = nil
-                                       @trans = FileTransfer.new(ip, port, username, passwd, @log)
-                                       for attempt in ATTEMPTS
-                       ftp_filepath = @trans.putfile( src_file )
-                                               if !ftp_filepath.nil? then break;
-                                               else @log.info "Client is the #{attempt} upload attempt fails" end
-                                       end                                             
-                                       if ftp_filepath.nil? then 
-                                               send "ERROR"                                                    
-                                               return false 
-                                       else @log.info "Client is the #{attempt} successful attempt to upload file" end
-                    send "UPLOADED,#{ip},#{port},#{ftp_filepath},#{username},#{passwd}"
-                elsif line.strip == "SUCC" then 
-                    @log.info "Server downloaded file sucessfully"
-                    @trans.cleandir( ftp_filepath )
-                    @log.info "Client cleaned temporary dir on ftp server: #{ftp_filepath}"
-                elsif line.strip == "ERROR" then
-                    @log.error "Server failed to download the file. Please check server log"
-                    return false
-                elsif line.strip == "=END" then
-                    break
-                end
-            end
-        rescue => e
-            puts "[BuildCommClient] Exception" 
-                       @log.error e.message
-                       @log.error e.backtrace.inspect
-                       return false                    
-               end
-
-               return true
-       end
-
-
-       # return file
-       def receive_file(ip, port, username, passwd, dst_file)
-               begin
-                       l = @socket.gets()
-
-                       if l.nil? then 
-                               @log.error "[BuildCommClient] Connection refused"
-                               return false
-                       end
-
-                       # check protocol
-                       if not protocol_matched? l.strip then
-                               @log.error "[BuildCommClient] Comm. Protocol version is mismatched! #{VERSION}"
-                               return false
-                       end
-
-                       # 1. If "READY" is received, send "FTP,ip,port,username,passwd"
-                       # 2. if "UPLOADED,ftp_file_path" is received,
-                       #    Download the file
-                       #    Send "SUCC"
-                       # 3. If "SUCC" is received, remove the file on FTP server
-            while line = @socket.gets()
-                cmd = line.split(",")[0].strip
-                #@log.info "[BuildCommClient] Received \"#{cmd}\" message from BuildCommServer"
-                if cmd == "READY" then
-                    send "FTP,#{ip},#{port},#{username},#{passwd}"
-                    @log.info "Client sent ftp server infomation to server : [#{ip}, #{port}]"
-                elsif cmd == "UPLOADED" then
-                    tok = line.split(",")
-                    if tok.length < 2 then
-                        @log.error "Client received wrong REQ : #{line.strip}"
-                        return false
-                    end
-                    ftp_filepath = tok[1].strip
-                                       @log.info "Server uploaded file sucessfully"                                    
-                                       dst_filepath = nil
-                                       @trans = FileTransfer.new(ip, port, username, passwd, @log)
-                                       for attempt in ATTEMPTS
-                           dst_filepath = @trans.getfile( ftp_filepath, dst_file )
-                                               if not dst_filepath.nil? then break
-                                               else 
-                                                       @log.warn "Client is the #{attempt} download attempt fails" 
-                                               end                                                     
-                                       end                                     
-                                       if dst_filepath.nil? then 
-                                               send "ERROR"                                                    
-                                               return false
-                                       else @log.info "Client is the #{attempt} successful attempt to download" end
-                    send "SUCC"
-                elsif cmd == "ERROR" then
-                    @log.error "Server failed to upload file. Check server log"
-                    return false
-                elsif cmd == "=END" then
-                    break
-                end
-            end
-               rescue => e
-            puts "[BuildCommServer] Exception" 
-                       @log.error e.message
-                       @log.error e.backtrace.inspect
-                       return false                    
-               end
-
-               return true
-       end
-
-
-       def terminate
-               @socket.close
-       end
-
-       
-       private
-
-
-       # check protocol
-       def protocol_matched?(l)
-
-               version = ( l.split(",")[1].nil? ? "1.0.0" : l.split(",")[1] ) 
-               if not l.start_with? "=BEGIN" or
-                       version.nil? or version != VERSION then
-                       return false
-               else
-                       return true
-               end     
-       end
-end
diff --git a/src/common/BuildComm.rb b/src/common/BuildComm.rb
new file mode 100644 (file)
index 0000000..950c90c
--- /dev/null
@@ -0,0 +1,568 @@
+require "socket"
+
+=begin
+
+ BuildComm.rb
+
+Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
+
+Contact:
+Taejun Ha <taejun.ha@samsung.com>
+Jiil Hyoun <jiil.hyoun@samsung.com>
+Donghyuk Yang <donghyuk.yang@samsung.com>
+DongHee Yang <donghee.yang@samsung.com>
+
+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.
+
+Contributors:
+- S-Core Co., Ltd
+=end
+
+$LOAD_PATH.unshift File.dirname(__FILE__)
+require "log"
+require 'timeout'
+require "fileTransfer"
+require "net/ftp"
+require 'thread'
+
+ATTEMPTS = ["first", "second", "third"]
+
+class BuildCommServer
+       VERSION = "1.5.0"
+
+       private_class_method :new
+
+       def initialize(port, log, ftp_url, cache_dir)
+               @port = port
+               @log = log
+               @ftp_url = ftp_url
+               @cache_dir = cache_dir
+               @tcp_server = TCPServer.open( port )
+               @download_cache_mutex = Mutex.new
+       end
+
+    def self.create(port, log, ftp_url=nil, cache_dir=nil)
+               # checking port is  available
+               if port_open? port then
+                       raise "Port \"#{@port}\" is already in use."
+               end
+
+               if log.nil? then
+                       log = DummyLog.new
+               end
+
+               # create cache dir if not nil
+               if not cache_dir.nil? and not File.exist? cache_dir then 
+                       FileUtils.mkdir_p cache_dir 
+               end
+
+        return new(port, log, ftp_url, cache_dir)
+    end
+
+
+       # wait for connection and handle request
+       def wait_for_connection(quit_loop)
+               while( not quit_loop )
+                       req = @tcp_server.accept                
+       
+                       begin
+                               yield req if block_given?
+                       rescue
+                               @log.error $!
+                               @log.error "Caught a connection exception"
+                               req.close
+                       end
+               end
+       end
+
+
+       # terminate
+       def terminate
+               @tcp_server.close()
+       end
+
+       
+       # send_begin
+       def self.send_begin( req )
+               send( req, "=BEGIN,#{VERSION}")
+       end
+
+
+       def self.send_end( req )
+               send( req, "=END")
+       end
+
+
+       def self.send_chk( req )
+               send( req, "=CHK" )
+       end
+
+
+       def self.send( req, line )
+               begin
+                       if not req.nil? then
+                               req.puts line
+                       end
+               rescue
+                       raise "Connection is closed"
+               end
+       end
+
+
+       def send_file(req, src_file)
+        begin
+            if not File.exist? src_file then
+                @log.error "\"#{src_file}\" file does not exist"
+                req.puts "ERROR"
+                return false
+            end
+
+            req.puts "READY"
+                       @log.info "Ready to upload file"
+
+                       # receive file from client 
+                       if not @ftp_url.nil? then
+                url_contents = Utils.parse_ftpserver_url(@ftp_url)
+                               ip = url_contents[0]
+                               port = url_contents[1]
+                username = url_contents[2]
+                passwd = url_contents[3]
+                               @trans = FileTransfer.new(@log,ip,port,username,passwd)
+                       else
+                               @trans = FileTransfer.new(@log)
+                       end
+
+                       if not @trans.send_file( src_file, req, false ) then
+                               return false
+                       end
+
+        rescue => e
+            puts "[BuildCommServer] Exception"
+                       @log.error e.message
+                       @log.error e.backtrace.inspect
+                       return false                    
+               end
+
+        return true
+       end
+
+
+       # NOTE. dst_file can be directory
+       def receive_file(req, dst_file)
+               # 1. send "READY"
+               # 2. If "UPLOADED,ip,port,file_path,username,passwd" is received,
+               #    Download the file using my ftp_url.
+               #    If ftp_url is nil, use the url on "UPLOADED" messge instead
+               #    After downloading it, send "SUCC"
+
+               begin
+            req.puts "READY"
+                       while l = req.gets()
+                               tok = l.split(",").map { |x| x.strip }
+                               cmd = tok[0].strip
+                               if cmd == "CHECK_CACHE" then
+                                       file_name = tok[1]
+                                       file_size = tok[2].to_i
+                                       checksum = tok[3]
+       
+                                       # check download cache                          
+                               if File.exist? dst_file and File.directory? dst_file then 
+                                               target_file = File.join(dst_file,file_name)
+                               else 
+                                               target_file = dst_file 
+                                       end
+                                       if not @cache_dir.nil? and 
+                                               check_download_cache( target_file, file_size, checksum ) then
+
+                                               @log.info "Download cache hit! Copied from cache.: #{file_name}"
+                                               req.puts "CACHED"
+                                       else
+                                               @log.info "Cached file not found!#{file_name}"
+                                               req.puts "NOT_CACHED"
+
+                                               # receive file from client 
+                                               if not @ftp_url.nil? then
+                                       url_contents = Utils.parse_ftpserver_url(@ftp_url)
+                                                       ip = url_contents[0]
+                                                       port = url_contents[1]
+                                       username = url_contents[2]
+                                       passwd = url_contents[3]
+                                                       @trans = FileTransfer.new(@log, ip, port, username, passwd)
+                                               else
+                                                       @trans = FileTransfer.new(@log)
+                                               end
+
+                                               if not @trans.receive_file( dst_file, req, false ) then
+                                                       return false
+                                               end
+
+                                               # add to cache
+                                               if not @cache_dir.nil? then
+                                               if File.exist? dst_file and File.directory? dst_file then 
+                                                               target_file = File.join(dst_file,File.basename(dst_filepath))
+                                               else 
+                                                               target_file = dst_file 
+                                                       end
+                                                       add_download_cache(target_file)
+                                               end
+                                       end
+                    break
+
+                               else
+                                       @log.warn "Unhandled message: #{l}"
+                               end
+                       end                             
+               rescue => e
+            puts "[BuildCommServer] Exception" 
+                       @log.error e.message
+                       @log.error e.backtrace.inspect
+                       return false                    
+               end
+
+        return true
+       end
+
+
+       def self.disconnect( req )
+               begin
+                       req.close
+               rescue
+               end
+       end
+
+       def self.port_open?( port )
+               Timeout::timeout(1) do
+                       begin
+                               TCPSocket.new("127.0.0.1",port).close
+                               true
+                       rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
+                               false
+                       end
+               end
+       rescue Timeout::Error
+               false
+       end     
+
+
+       private
+       def check_download_cache(dst_file, file_size, checksum )
+               file_name = File.basename(dst_file)
+               cache_file = "#{@cache_dir}/#{file_name}"
+
+               @download_cache_mutex.synchronize {
+                       found = false
+                       # check file exist
+                       if File.exist? cache_file and 
+                               File.size(cache_file) == file_size and
+                               Utils.checksum(cache_file) == checksum then
+
+                               # if hit , touch and copy
+                               FileUtils.touch cache_file
+                               FileUtils.copy_file(cache_file, dst_file)
+
+                               found = true
+                       end
+
+                       # refresh cache dir
+                       curr_time = Time.now
+                       Dir.entries(@cache_dir).each { |fname|
+                               if fname == "." or fname == ".." then next end
+                               file_path = "#{@cache_dir}/#{fname}"
+                               if File.mtime(file_path) + 3600 < curr_time then
+                                       FileUtils.rm_rf file_path
+                               end
+                       }
+
+                       return found
+               }
+       end
+
+
+       private
+       def add_download_cache(dst_file)
+               file_name = File.basename(dst_file)
+               cache_file = "#{@cache_dir}/#{file_name}"
+               @download_cache_mutex.synchronize {
+                       # copy & touch
+                       FileUtils.copy_file(dst_file, cache_file)
+                       FileUtils.touch cache_file
+               }
+       end
+end
+
+
+class BuildCommClient
+       VERSION = "1.5.0"
+
+       private_class_method :new
+
+       def initialize(socket, log)
+        @log = log
+               @socket = socket
+       end
+
+
+       # create
+       # if sec 0 or nil then not set timeout. it's timeout spec
+       def self.create(ip, port, log = nil, sec = 5)
+               # open socket
+               socket = nil
+               begin
+                       timeout(sec) do
+                               socket = TCPSocket.open( ip, port )
+                       end
+               rescue Timeout::Error
+                       return nil
+               rescue
+                       # unknown exception
+                       return nil
+               end
+
+               # refused
+               if socket.nil? then
+                       return nil
+               end
+
+               if log.nil? then
+                       log = DummyLog.new
+               end
+
+               return new(socket, log)
+       end
+
+
+       def send( msg )
+               if @socket.nil? then return false end
+
+               @socket.puts( msg )
+               return true
+       end     
+
+
+       def print_stream
+
+               begin
+                       l = @socket.gets()
+
+                       if l.nil? then 
+                               puts "Connection refused"
+                               return false 
+                       end
+
+                       # check protocol
+                       if not protocol_matched? l.strip then
+                               puts "Comm. Protocol version is mismatched! #{VERSION}"
+                               return false
+                       end
+
+               
+                       # get contents
+                       while line = @socket.gets()
+                               if line.strip == "=END" then break end
+                               if line.strip == "=CHK" then next end
+                               # print
+                               puts line.strip
+                       end
+               rescue
+                       puts "Connection closed"
+                       return false
+               end
+
+               return true
+       end
+
+
+       # handle 
+       def read_lines
+
+               begin
+                       # get first line
+                       l = nil
+                       timeout(5) do 
+                               l = @socket.gets()
+                       end
+
+                       if l.nil? then 
+                               return false 
+                       end
+
+                       # check protocol
+                       if not protocol_matched? l.strip then
+                               return false
+                       end
+
+                       # get contents
+                       while line = @socket.gets()
+                               if line.strip == "=END" then break end
+                               if line.strip == "=CHK" then next end
+
+                               # print
+                               yield line.strip if block_given?
+                       end
+               rescue Timeout::Error
+            puts "WARN: Connection timed out"
+                       return false
+               rescue => e
+            puts e.message
+                       return false
+               end
+
+               return true
+       end
+
+
+       # return result
+       def receive_data
+               result = []
+
+               begin
+                       l = @socket.gets()
+
+                       if l.nil? then 
+                               puts "Connection refused"
+                               return nil
+                       end
+
+                       # check protocol
+                       if not protocol_matched? l.strip then
+                               puts "Comm. Protocol version is mismatched! #{VERSION}"
+                               return nil
+                       end
+
+               
+                       # get contents
+                       while line = @socket.gets()
+                               if line.strip == "=END" then break end
+                               if line.strip == "=CHK" then next end
+                               # print
+                               result.push line.strip
+                       end
+               rescue
+                       puts "Connection closed"
+                       return nil
+               end
+
+               return result
+       end
+
+
+       def send_file(ip, port, username, passwd, src_file)
+        begin
+            l = @socket.gets()
+            if l.nil? then 
+                @log.error "[BuildCommClient] Connection refused"
+                               return false 
+                       end
+
+                       # check protocol
+                       if not protocol_matched? l.strip then
+                @log.error "[BuildCommClient] Comm. Protocol version is mismatched! #{VERSION}"
+                               return false
+                       end
+
+            while line = @socket.gets()
+                if line.strip == "READY" then
+                       @log.info "Server is ready to receive file"
+                                       file_name = File.basename(src_file)
+                                       file_size = File.size(src_file)
+                                       checksum = Utils.checksum(src_file)
+                    send "CHECK_CACHE,#{file_name},#{file_size},#{checksum}"
+
+                elsif line.strip == "CACHED" then
+                    @log.info "Server already has cached file"
+                                       break
+
+                elsif line.strip == "NOT_CACHED" then
+                       @log.info "Server doest not have cached file"
+                               
+                                       # send file to server
+                                       @trans = FileTransfer.new(@log, ip, port, username, passwd)
+                                       if not @trans.send_file( src_file, @socket, true ) then
+                                               return false
+                                       end
+                                       break
+
+                elsif line.strip == "=END" then
+                    break
+                end
+            end
+        rescue => e
+            puts "[BuildCommClient] Exception" 
+                       @log.error e.message
+                       @log.error e.backtrace.inspect
+                       return false                    
+               end
+
+               return true
+       end
+
+
+       # return file
+       def receive_file(ip, port, username, passwd, dst_file)
+               begin
+                       l = @socket.gets()
+
+                       if l.nil? then 
+                               @log.error "[BuildCommClient] Connection refused"
+                               return false
+                       end
+
+                       # check protocol
+                       if not protocol_matched? l.strip then
+                               @log.error "[BuildCommClient] Comm. Protocol version is mismatched! #{VERSION}"
+                               return false
+                       end
+
+            while line = @socket.gets()
+                cmd = line.split(",")[0].strip
+                if cmd == "READY" then
+                                       @trans = FileTransfer.new(@log, ip, port, username, passwd)
+                                       if not @trans.receive_file( dst_file, @socket, true ) then
+                                               return false
+                                       end
+                                       break 
+                elsif cmd == "=END" then
+                    break
+                end
+            end
+               rescue => e
+            puts "[BuildCommServer] Exception" 
+                       @log.error e.message
+                       @log.error e.backtrace.inspect
+                       return false                    
+               end
+
+               return true
+       end
+
+
+       def terminate
+               @socket.close
+       end
+
+       
+       private
+
+
+       # check protocol
+       def protocol_matched?(l)
+
+               version = ( l.split(",")[1].nil? ? "1.0.0" : l.split(",")[1] ) 
+               if not l.start_with? "=BEGIN" or
+                       version.nil? or version != VERSION then
+                       return false
+               else
+                       return true
+               end     
+       end
+end
index 09461ccae545aa2cd947cd974b5bacb0925d90e7..f504c4cbd91a1aa455e9d1bef914f47dee8d44c0 100644 (file)
@@ -3,7 +3,9 @@ require 'socket'
 require 'log'
 
 class FileTransfer
-       def initialize(ip, port, username, passwd, logger)
+       ATTEMPTS = ["first", "second", "third"]
+
+       def initialize(logger, ip=nil, port=nil, username=nil, passwd=nil )
                @ip = ip
                @port = port
                @username = username
@@ -16,20 +18,146 @@ class FileTransfer
        end
 
 
-    def putfile( bpath )
+       def send_file( src_file, conn, is_client=true )
+
+               if is_client then
+                       # check ftp info
+                       if @ip.nil? or @port.nil? or @username.nil? or @passwd.nil? then
+                               @log.error "No FTP information!" 
+                               conn.puts "UPLOAD_FAIL"                                                 
+                               return false 
+                       end
+                       conn.puts "DOWNLOAD_REQ,#{@ip},#{@port},#{@username},#{@passwd}"
+               end
+
+               ip = @ip; port = @port; username = @username; passwd = @passwd
+               while line = conn.gets()
+                       tok = line.split(",") { |x| x.strip }
+                       cmd = tok[0].strip
+                       case cmd
+                       when "UPLOAD_REQ"
+                               if @ip.nil? or @port.nil? or @username.nil? or @passwd.nil? then
+                                       ip = tok[1].strip
+                                       port = tok[2].strip
+                                       username = tok[3].strip
+                                       passwd = tok[4].strip
+                    @log.info "Using FTP information from remote... [#{ip}, #{port}]"
+                               end
+
+                               # upload to ftp
+                               ftp_filepath = nil
+                               for attempt in ATTEMPTS
+                               ftp_filepath = putfile( src_file, ip, port, username, passwd )
+                                       if !ftp_filepath.nil? then 
+                                               break
+                                       else 
+                                               @log.info "The #{attempt} uploading attempt failed!" 
+                                       end
+                               end
+
+                               if ftp_filepath.nil? then 
+                                       conn.puts "UPLOAD_FAIL"                                                 
+                                       return false 
+                               else 
+                                       @log.info "Upload is succeeded at #{attempt}" 
+                               conn.puts "UPLOAD_OK,#{ftp_filepath}"
+                               end
+
+                       # wait for download result
+                       when "DOWNLOAD_OK"
+                               @log.info "Received download success message from remote site"
+                               # clean
+                               cleandir( ftp_filepath, ip, port, username, passwd) 
+                       @log.info "Cleaned temporary dir on FTP server: #{ftp_filepath}"
+                               return true     
+                                       
+                       when "DOWNLOAD_FAIL"
+                               @log.info "Received download fail message from remote site"
+                               return false
+
+                       else
+                               @log.error "Unhandled message: #{line}"
+                               return false
+                       end
+               end     
+       end
+
+
+       def receive_file( dst_file, conn, is_client=false )
+
+               if is_client then
+                       # check ftp info
+                       if @ip.nil? or @port.nil? or @username.nil? or @passwd.nil? then
+                               @log.error "No FTP information!" 
+                               conn.puts "DOWNLOAD_FAIL"                                                       
+                               return false 
+                       end
+               conn.puts "UPLOAD_REQ,#{@ip},#{@port},#{@username},#{@passwd}"
+               end
+
+               ip = @ip; port = @port; username = @username; passwd = @passwd
+               while line = conn.gets()
+                       tok = line.split(",") { |x| x.strip }
+                       cmd = tok[0].strip
+                       case cmd
+                       when "DOWNLOAD_REQ"
+                               if @ip.nil? or @port.nil? or @username.nil? or @passwd.nil? then
+                                       ip = tok[1].strip
+                                       port = tok[2].strip
+                                       username = tok[3].strip
+                                       passwd = tok[4].strip
+                    @log.info "Using FTP information from remote... [#{ip}, #{port}]"
+                               end
+
+                       conn.puts "UPLOAD_REQ,#{ip},#{port},#{username},#{passwd}"
+                       when "UPLOAD_OK"
+                               @log.info "Received upload success message from remote site"
+                               filepath = tok[1].strip
+                               # download from ftp
+                               dst_filepath = nil
+                               for attempt in ATTEMPTS
+                                       dst_filepath = getfile( filepath, dst_file, ip, port, username, passwd )
+                                       if not dst_filepath.nil? then 
+                                               break
+                                       else 
+                                               @log.info "The #{attempt} downloading attempt failed!" 
+                                       end                                                     
+                               end                                     
+                               if dst_filepath.nil? then 
+                                       conn.puts "DOWNLOAD_FAIL"                                                       
+                                       return false                                                                                            
+                               else 
+                                       @log.info " Server is the #{attempt} successful attempt to download" 
+                                       conn.puts "DOWNLOAD_OK"
+                                       return true
+                               end
+
+                       when "UPLOAD_FAIL"
+                               @log.info "Received upload fail message from remote site"
+                               return false
+
+            else
+                               @log.error "Unhandled message: #{line}"
+                               return false
+                       end
+               end
+       end
+
+
+    def putfile( bpath, ip, port, username, passwd )
         filename = File.basename(bpath)
         uniqdir = Utils.create_uniq_name
         ftp_filepath = File.join(uniqdir, filename)
 
         begin
             ftp = Net::FTP.new
-            if @port.nil? or @port == "" then
-                ftp.connect(@ip)
+            if port.nil? or port == "" then
+                ftp.connect(ip)
             else
-                ftp.connect(@ip, @port)                                 
+                ftp.connect(ip, port)                                 
             end
-                       @log.info "[FTP log] Connected FTP server (#{@ip}:#{@port})"
-            ftp.login(@username, @passwd)
+                       @log.info "[FTP log] Connected FTP server (#{ip}:#{port})"
+            ftp.login(username, passwd)
             ftp.binary = true
                        ftp.passive = true                      
             ftp.mkdir(uniqdir)
@@ -53,7 +181,7 @@ class FileTransfer
                return ftp_filepath
     end
 
-    def getfile( bpath, target )
+    def getfile( bpath, target, ip, port, username, passwd )
         dirname = File.dirname(bpath)
         filename = File.basename(bpath)
 
@@ -66,13 +194,13 @@ class FileTransfer
 
         begin
             ftp = Net::FTP.new
-            if @port.nil? or @port == "" then
-                ftp.connect(@ip)
+            if port.nil? or port == "" then
+                ftp.connect(ip)
             else                
-                ftp.connect(@ip, @port)
+                ftp.connect(ip, port)
             end                
-                       @log.info "[FTP log] Connected FTP server (#{@ip}:#{@port})"
-            ftp.login(@username, @passwd)
+                       @log.info "[FTP log] Connected FTP server (#{ip}:#{port})"
+            ftp.login(username, passwd)
             ftp.binary = true
                        ftp.passive = true
             ftp.chdir(dirname)
@@ -94,18 +222,18 @@ class FileTransfer
         return bpath
     end
 
-    def cleandir(path)
+    def cleandir(path, ip, port, username, passwd)
         dirname = File.dirname(path)
 
         begin
             ftp = Net::FTP.new
-            if @port.nil? or @port == "" then
-                ftp.connect(@ip)
+            if port.nil? or port == "" then
+                ftp.connect(ip)
             else
-                ftp.connect(@ip, @port)                    
+                ftp.connect(ip, port)                    
             end                    
-                       @log.info "[FTP log] Connected FTP server (#{@ip}:#{@port})"
-            ftp.login(@username, @passwd)
+                       @log.info "[FTP log] Connected FTP server (#{ip}:#{port})"
+            ftp.login(username, passwd)
             old_dir = ftp.pwd
             ftp.chdir(dirname)
             list = ftp.ls