indexing description: "Handles persistent advertisement and configuration cache" license: "MIT license (see ../license.txt)" author: "Beat Strasser " date: "$Date$" revision: "$Revision$" class P2P_CACHE_MANAGER inherit KL_OPERATING_SYSTEM export {NONE} all end KL_SHARED_FILE_SYSTEM export {NONE} all end P2P_CREATORS_SHARED create make feature {NONE} -- Initialization make (a_path: STRING; a_logger: L4E_LOGGER) is -- Create manager instance for advertisement cache with given `a_path' require Path_valid: a_path /= Void do logger := a_logger -- detect directory separator for current file system if is_windows then dir := windows_file_system.directory_separator.out else dir := unix_file_system.directory_separator.out end logger.debugging ("cache_manager: Detected file system directory separator: " + dir) -- make sure given `a_path' is correct path in current file system if a_path.has (windows_file_system.directory_separator) then repository_path := file_system.pathname_from_file_system (a_path, windows_file_system) else repository_path := file_system.pathname_from_file_system (a_path, unix_file_system) end repository_path := file_system.absolute_pathname (repository_path) -- check path file_system.create_directory (repository_path) has_failed := not file_system.directory_exists (repository_path) -- initialize cache structure if not has_failed then check_cache_structure else repository_path := Void end end feature -- Status report has_failed: BOOLEAN -- Has last read or write operation failed? feature -- Access repository_path: STRING -- Path where advertisement cache resists has_configuration_advertisement: BOOLEAN is -- Is platform configuration advertisement in cache? require Repository_valid: repository_path /= Void do Result := file_exists (fn_configuration) end configuration_advertisement: P2P_CONFIGURATION is -- Cached platform configuration advertisement require Repository_valid: repository_path /= Void local content: STRING do content := read_from_file (fn_configuration) if content /= Void and not content.is_empty then create Result.parse_from_string (content) if not Result.is_valid then remove_configuration_advertisement Result := Void end end ensure Result_valid: Result /= Void implies Result.is_valid end has_peergroup_advertisement (an_id: P2P_PEERGROUP_ID): BOOLEAN is -- Is peer group advertisement in cache? require Repository_valid: repository_path /= Void Id_valid: an_id /= Void and an_id.is_valid do Result := file_exists (fn_peergroup (an_id)) end peergroup_advertisement (an_id: P2P_PEERGROUP_ID): P2P_PEERGROUP_ADVERTISEMENT is -- Cached peer group advertisement with given `an_id' require Repository_valid: repository_path /= Void Id_valid: an_id /= Void and an_id.is_valid local content: STRING do content := read_from_file (fn_peergroup (an_id)) if content /= Void and not content.is_empty then create Result.parse_from_string (content) if not Result.is_valid then remove_peergroup_advertisement (an_id) Result := Void end end ensure Result_valid: Result /= Void implies Result.is_valid end has_peer_advertisement (a_group_id: P2P_PEERGROUP_ID; an_id: P2P_PEER_ID): BOOLEAN is -- Is peer advertisement in cache? require Repository_valid: repository_path /= Void Group_id_valid: a_group_id /= Void and a_group_id.is_valid Id_valid: an_id /= Void and an_id.is_valid do Result := file_exists (fn_peer (a_group_id, an_id)) end peer_advertisement (a_group_id: P2P_PEERGROUP_ID; an_id: P2P_PEER_ID): P2P_PEER_ADVERTISEMENT is -- Cached peer advertisement with given `an_id' in given `group' require Repository_valid: repository_path /= Void Group_id_valid: a_group_id /= Void and a_group_id.is_valid Id_valid: an_id /= Void and an_id.is_valid local content: STRING do content := read_from_file (fn_peer (a_group_id, an_id)) if content /= Void and not content.is_empty then create Result.parse_from_string (content) if not Result.is_valid then remove_peer_advertisement (a_group_id, an_id) Result := Void end end ensure Result_valid: Result /= Void implies Result.is_valid end has_module_implementation_advertisement (an_id: P2P_MODULE_SPECIFICATION_ID): BOOLEAN is -- Is module implementation advertisement in cache? require Repository_valid: repository_path /= Void Id_valid: an_id /= Void and an_id.is_valid do Result := file_exists (fn_mia (an_id)) end module_implementation_advertisement (an_id: P2P_MODULE_SPECIFICATION_ID): P2P_MODULE_IMPLEMENTATION_ADVERTISEMENT is -- Cached module implementation advertisement with given `an_id' require Repository_valid: repository_path /= Void Id_valid: an_id /= Void and an_id.is_valid local content: STRING do content := read_from_file (fn_mia (an_id)) if content /= Void and not content.is_empty then create Result.parse_from_string (content) if not Result.is_valid then remove_module_implementation_advertisement (an_id) Result := Void end end ensure Result_valid: Result /= Void implies Result.is_valid end has_general_advertisement (a_group_id: P2P_PEERGROUP_ID; an_id: STRING): BOOLEAN is -- Is general advertisement with given `an_id' in cache? require Repository_valid: repository_path /= Void Group_id_valid: a_group_id /= Void and a_group_id.is_valid Id_valid: an_id /= Void do Result := file_exists (fn_general (a_group_id, an_id)) end general_advertisement (a_group_id: P2P_PEERGROUP_ID; an_id: STRING): P2P_ADVERTISEMENT is -- Cached general advertisement with given `an_id' require Repository_valid: repository_path /= Void Group_id_valid: a_group_id /= Void and a_group_id.is_valid Id_valid: an_id /= Void local content: STRING unknown: P2P_UNKNOWN_ADVERTISEMENT do content := read_from_file (fn_general (a_group_id, an_id)) if content /= Void and not content.is_empty then create unknown.parse_from_string (content) Result := xml_document_creator.advertisement_from_element (unknown.document.document.root_element) unknown ?= Result if Result = Void or unknown /= Void then -- Advertisement is invalid or an unknown advertisement remove_general_advertisement (a_group_id, an_id) Result := Void end end ensure Result_valid: Result /= Void implies Result.is_valid end peer_advertisement_ids (a_group_id: P2P_PEERGROUP_ID): DS_LIST [P2P_PEER_ID] is -- Ids of available peer advertisements for given `a_group_id' require Group_valid: a_group_id /= Void and a_group_id.is_valid local file_names: like file_list pid: P2P_PEER_ID do from file_names := file_list (dir_peers (a_group_id)) file_names.start create {DS_ARRAYED_LIST [P2P_PEER_ID]} Result.make (file_names.count) until file_names.after loop file_names.item_for_iteration.remove_tail (ext.count) create pid.make_from_urn (urn_prefix + file_names.item_for_iteration) if pid.is_valid then Result.put_last (pid) end file_names.forth end ensure Result_set: Result /= Void end peergroup_advertisement_ids: DS_LIST [P2P_PEERGROUP_ID] is -- Ids of available peer group advertisements local file_names: like file_list pid: P2P_PEERGROUP_ID do from file_names := file_list ("") file_names.start create {DS_ARRAYED_LIST [P2P_PEERGROUP_ID]} Result.make (file_names.count) until file_names.after loop if not file_names.item_for_iteration.substring (file_names.item_for_iteration.count - ext.count, file_names.item_for_iteration.count).is_equal (ext) then pid := id_creator.create_peergroup_id (urn_prefix + file_names.item_for_iteration) if pid /= Void and pid.is_valid then Result.put_last (pid) end end file_names.forth end ensure Result_set: Result /= Void end general_advertisement_ids (a_group_id: P2P_PEERGROUP_ID): DS_LIST [STRING] is -- Ids of available general advertisements for given `a_group_id' require Group_valid: a_group_id /= Void and a_group_id.is_valid local file_names: like file_list do from file_names := file_list (dir_generals (a_group_id)) file_names.start create {DS_ARRAYED_LIST [STRING]} Result.make (file_names.count) until file_names.after loop file_names.item_for_iteration.remove_tail (ext.count) Result.put_last (file_names.item_for_iteration) file_names.forth end ensure Result_set: Result /= Void end feature -- Store store_configuration_advertisement (adv: P2P_CONFIGURATION) is -- Store given configuration advertisement in cache require Repository_valid: repository_path /= Void Advertisement_valid: adv /= Void and adv.is_valid do write_to_file (adv.out, fn_configuration) end store_peergroup_advertisement (adv: P2P_PEERGROUP_ADVERTISEMENT) is -- Store given peer group advertisement in cache require Repository_valid: repository_path /= Void Advertisement_valid: adv /= Void and adv.is_valid do check_cache_structure_for_group (adv.group_id) write_to_file (adv.out, fn_peergroup (adv.group_id)) end store_peer_advertisement (adv: P2P_PEER_ADVERTISEMENT) is -- Store given peer advertisement in cache require Repository_valid: repository_path /= Void Advertisement_valid: adv /= Void and adv.is_valid do check_cache_structure_for_group (adv.group_id) write_to_file (adv.out, fn_peer (adv.group_id, adv.peer_id)) end store_module_implementation_advertisement (adv: P2P_MODULE_IMPLEMENTATION_ADVERTISEMENT) is -- Store given module implementation advertisement in cache require Repository_valid: repository_path /= Void Advertisement_valid: adv /= Void and adv.is_valid do check_cache_structure write_to_file (adv.out, fn_mia (adv.specification_id)) end store_general_advertisement (a_group_id: P2P_PEERGROUP_ID; adv: P2P_ADVERTISEMENT) is -- Store given general advertisement in cache under its unique id require Repository_valid: repository_path /= Void Advertisement_valid: adv /= Void and adv.is_valid do check_cache_structure_for_group (a_group_id) write_to_file (adv.out, fn_general (a_group_id, adv.unique_id)) end feature -- Removal flush_entire_repository is -- Remove entire cache content require Repository_valid: repository_path /= Void do remove_directory_content (repository_path) end flush_peer_advertisements (a_group_id: P2P_PEERGROUP_ID) is -- Remove peers advertisements of given group require Repository_valid: repository_path /= Void Group_id_valid: a_group_id /= Void and a_group_id.is_valid do remove_directory_content (dir_peers (a_group_id)) end flush_general_advertisements (a_group_id: P2P_PEERGROUP_ID) is -- Remove general advertisements of given group require Repository_valid: repository_path /= Void Group_id_valid: a_group_id /= Void and a_group_id.is_valid do remove_directory_content (dir_generals (a_group_id)) end remove_configuration_advertisement is -- Remove configuration advertisement require Repository_valid: repository_path /= Void do remove_file (fn_configuration) end remove_peergroup_advertisement (an_id: P2P_PEERGROUP_ID) is -- Remove peer group advertisement require Repository_valid: repository_path /= Void Id_valid: an_id /= Void and an_id.is_valid do remove_file (fn_peergroup (an_id)) end remove_peer_advertisement (a_group_id: P2P_PEERGROUP_ID; an_id: P2P_PEER_ID) is -- Remove peer advertisement require Repository_valid: repository_path /= Void Group_id_valid: a_group_id /= Void and a_group_id.is_valid Id_valid: an_id /= Void and an_id.is_valid do remove_file (fn_peer (a_group_id, an_id)) end remove_module_implementation_advertisement (an_id: P2P_MODULE_SPECIFICATION_ID) is -- Remove module implementation advertisement require Repository_valid: repository_path /= Void Id_valid: an_id /= Void and an_id.is_valid do remove_file (fn_mia (an_id)) end remove_general_advertisement (a_group_id: P2P_PEERGROUP_ID; an_id: STRING) is -- Remove general advertisement require Repository_valid: repository_path /= Void Group_id_valid: a_group_id /= Void and a_group_id.is_valid Id_valid: an_id /= Void do remove_file (fn_general (a_group_id, an_id)) end feature {NONE} -- Implementation dir: STRING Ext: STRING is ".xml" Urn_prefix: STRING is "urn:" Platform_config: STRING is "PlatformConfig" Peergroup: STRING is "PeerGroup" Peers: STRING is "Peers" Modules: STRING is "Modules" Generals: STRING is "Advs" logger: L4E_LOGGER read_from_file (a_path: STRING): STRING is -- Read string from file identified by `a_path' (relative to `repository_path') require Repository_valid: repository_path /= Void Path_valid: a_path /= Void local file: KI_TEXT_INPUT_FILE do if is_windows then create {KL_WINDOWS_INPUT_FILE} file.make (file_system.pathname (repository_path, a_path)) else create {KL_UNIX_INPUT_FILE} file.make (file_system.pathname (repository_path, a_path)) end file.open_read has_failed := not file.is_open_read if not has_failed then from create Result.make_empty until file.end_of_input loop file.read_string (4096) Result.append (file.last_string) end file.close else logger.debugging ("cache_manager: Error reading from file: " + a_path) end end write_to_file (content, a_path: STRING) is -- Write `content' to file identified by given `a_path' (relative to `repository_path') require Repository_valid: repository_path /= Void Content_valid: content /= Void and not content.is_empty Path_valid: a_path /= Void local file: KI_TEXT_OUTPUT_FILE do if is_windows then create {KL_WINDOWS_OUTPUT_FILE} file.make (file_system.pathname (repository_path, a_path)) else create {KL_UNIX_OUTPUT_FILE} file.make (file_system.pathname (repository_path, a_path)) end file.open_write has_failed := not file.is_open_write if not has_failed then file.put_string (content) file.close else logger.debugging ("cache_manager: Error writing to file: " + a_path) end end remove_file (a_path: STRING) is -- Remove file with given `a_path' require Repository_valid: repository_path /= Void Path_valid: a_path /= Void do file_system.delete_file (file_system.pathname (repository_path, a_path)) has_failed := False end file_exists (a_path: STRING): BOOLEAN is -- Does file with given `a_path' exist? require Repository_valid: repository_path /= Void Path_valid: a_path /= Void do Result := file_system.file_exists (file_system.pathname (repository_path, a_path)) has_failed := False end file_list (a_dir: STRING): DS_LIST [STRING] is -- List of file names in given directory `a_dir' local failed: BOOLEAN fsdir: KL_DIRECTORY list: ARRAY [STRING] do if not failed then create {DS_LINKED_LIST [STRING]} Result.make_default create fsdir.make (file_system.pathname (repository_path, a_dir)) list := fsdir.filenames if list /= Void then create {DS_ARRAYED_LIST [STRING]} Result.make_from_array (list) else failed := True create {DS_ARRAYED_LIST [STRING]} Result.make_default end end has_failed := failed rescue failed := True logger.debugging ("cache_manager: Error retrieving file list, directory: " + a_dir) retry end check_cache_structure is -- Make sure general cache structure is ok require Repository_valid: repository_path /= Void do -- modules/ file_system.create_directory (file_system.pathname (repository_path, modules)) end check_cache_structure_for_group (a_group_id: P2P_PEERGROUP_ID) is -- Make sure general cache structure and for `a_group_id' is ok require Repository_valid: repository_path /= Void Group_id_valid: a_group_id /= Void and a_group_id.is_valid do check_cache_structure -- [peergroupid]/ file_system.create_directory (file_system.pathname (repository_path, simplify (a_group_id.out))) -- [peergroupid]/Peers file_system.create_directory (file_system.pathname (repository_path, simplify (a_group_id.out) + dir + peers)) -- [peergroupid]/Advs file_system.create_directory (file_system.pathname (repository_path, simplify (a_group_id.out) + dir + generals)) end remove_directory_content (a_dir: STRING) is -- Remove entire cache content require Repository_valid: repository_path /= Void Directory_valid: a_dir /= Void local dir_path: STRING do dir_path := file_system.pathname (repository_path, a_dir) file_system.recursive_delete_directory (dir_path) file_system.create_directory (dir_path) has_failed := not file_system.directory_exists (dir_path) end simplify (in: STRING): STRING is -- Simplify a string require Input_valid: in /= Void do Result := in.twin if Result.substring (1, 4).is_equal (urn_prefix) then Result.remove_head (4) end ensure Result_set: Result /= Void end fn_configuration: STRING is -- Relative file path of configuration advertisement do Result := platform_config + ext end fn_peergroup (an_id: P2P_PEERGROUP_ID): STRING is -- Relative file path of peer group advertisement require Id_valid: an_id /= Void and an_id.is_valid do Result := simplify (an_id.out) + dir + peergroup + ext end fn_peer (a_group_id: P2P_PEERGROUP_ID; an_id: P2P_PEER_ID): STRING is -- Relative file path of peer advertisement require Group_id_valid: a_group_id /= Void and a_group_id.is_valid Id_valid: an_id /= Void and an_id.is_valid do Result := dir_peers (a_group_id) + dir + simplify (an_id.out) + ext end dir_peers (a_group_id: P2P_PEERGROUP_ID): STRING is -- Relative file path of peer advertisements for given `a_group_id' require Group_id_valid: a_group_id /= Void and a_group_id.is_valid do Result := simplify (a_group_id.out) + dir + peers end fn_mia (an_id: P2P_MODULE_SPECIFICATION_ID): STRING is -- Relative file path of module implementation advertisement require Id_valid: an_id /= Void and an_id.is_valid do Result := modules + dir + simplify (an_id.out) + ext end fn_general (a_group_id: P2P_PEERGROUP_ID; an_id: STRING): STRING is -- Relative file path of general advertisement require Group_id_valid: a_group_id /= Void and a_group_id.is_valid Id_valid: an_id /= Void do Result := dir_generals (a_group_id) + dir + an_id + ext end dir_generals (a_group_id: P2P_PEERGROUP_ID): STRING is -- Relative file path of general advertisements for given `a_group_id' require Group_id_valid: a_group_id /= Void and a_group_id.is_valid do Result := simplify (a_group_id.out) + dir + generals end end