filestore 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/{memory_meta.rb → filestore/memory_meta.rb} +0 -2
- data/lib/{meta_manager.rb → filestore/meta_manager.rb} +0 -1
- data/lib/{multitenant_filestore.rb → filestore/multitenant_store.rb} +1 -1
- data/lib/{observer.rb → filestore/observer.rb} +0 -2
- data/lib/filestore/simple_store.rb +329 -0
- data/lib/filestore.rb +37 -330
- data/test/classes.rb +8 -0
- data/test/tc_filestore.rb +1 -1
- data/test/tc_meta.rb +0 -1
- data/test/tc_module.rb +1 -3
- data/test/tc_multitenant.rb +0 -1
- metadata +9 -31
- data/module.rb +0 -40
- /data/lib/{log.rb → filestore/log.rb} +0 -0
@@ -0,0 +1,329 @@
|
|
1
|
+
#
|
2
|
+
# filestore.rb
|
3
|
+
#
|
4
|
+
# author: Thomas Stätter
|
5
|
+
# date: 2012/11/07
|
6
|
+
#
|
7
|
+
module FileStore
|
8
|
+
#
|
9
|
+
# Base exception class used for errors occurring in this module
|
10
|
+
#
|
11
|
+
class FileStoreException < Exception
|
12
|
+
end
|
13
|
+
#
|
14
|
+
# Main library class implementing a simple file store used for storing and managing
|
15
|
+
# arbitrary files
|
16
|
+
#
|
17
|
+
class SimpleFileStore
|
18
|
+
include Logger
|
19
|
+
include OberservedSubject
|
20
|
+
|
21
|
+
# Name of the lock file
|
22
|
+
STORE_LOCK_FILE = ".locked"
|
23
|
+
# Name of the meta file describing the current file store
|
24
|
+
META_FILE = "filestore.yaml"
|
25
|
+
# The base name of the file store directory
|
26
|
+
STORE_ROOT = 'filestore'
|
27
|
+
# The base name of the directory for storing deleted files
|
28
|
+
DELETED_ROOT = 'deleted'
|
29
|
+
# The base name of the directory storing files extracted from file store by a
|
30
|
+
# rollback action
|
31
|
+
ROLLBACK_ROOT = 'rollback'
|
32
|
+
#
|
33
|
+
# Accessors for important properties
|
34
|
+
#
|
35
|
+
attr_reader :metaManager, :rootPath, :storePath, :deletedPath, :rollbackPath, :metaFile
|
36
|
+
#
|
37
|
+
# Initializes a new instance of SimpleFileStore
|
38
|
+
#
|
39
|
+
# Arguments:
|
40
|
+
# metaManager: The meta data manager instance to be used by this store
|
41
|
+
# rootPath: The path where the file store resides
|
42
|
+
#
|
43
|
+
def initialize(metaManager, rootPath = '.', logger)
|
44
|
+
raise FileStoreException, "Root path already locked" if SimpleFileStore.is_directory_locked?(rootPath)
|
45
|
+
raise FileStoreException, "FileStore root path #{rootPath} doesn't exist" if not File.directory?(rootPath)
|
46
|
+
raise FileStoreException, "FileStore root path #{rootPath} isn't writable" if not File.writable?(rootPath)
|
47
|
+
raise FileStoreException, "No meta data manager given" if metaManager.nil?
|
48
|
+
raise FileStoreException, "Meta data manager must be of type FileStore::MetaManager" if not metaManager.is_a?(MetaManager)
|
49
|
+
|
50
|
+
@metaManager = metaManager
|
51
|
+
@rootPath = rootPath
|
52
|
+
@storePath = File.join(@rootPath, STORE_ROOT)
|
53
|
+
@deletedPath = File.join(@rootPath, DELETED_ROOT)
|
54
|
+
@rollbackPath = File.join(@rootPath, ROLLBACK_ROOT)
|
55
|
+
@metaFile = File.join(@rootPath, META_FILE)
|
56
|
+
@locked = false
|
57
|
+
@logger = logger
|
58
|
+
|
59
|
+
self.initialize_obs
|
60
|
+
|
61
|
+
begin
|
62
|
+
# Try to recover existing store
|
63
|
+
SimpleFileStore.recover_store(self)
|
64
|
+
rescue FileStoreException => e
|
65
|
+
# Recovery failed, trying to create the store
|
66
|
+
SimpleFileStore.create_store(self)
|
67
|
+
end
|
68
|
+
|
69
|
+
lock
|
70
|
+
end
|
71
|
+
#
|
72
|
+
# Adds a file to the store
|
73
|
+
#
|
74
|
+
# Arguments:
|
75
|
+
# file: The file to be stored
|
76
|
+
# meta: Optional meta data to be stored along with the physical file
|
77
|
+
# shouldMove: Determines wether to original file should be deleted
|
78
|
+
#
|
79
|
+
# Returns:
|
80
|
+
# The newly created ID for the file
|
81
|
+
#
|
82
|
+
def add(file, meta = {}, shouldMove = true)
|
83
|
+
raise FileStoreException, "File #{file} not found" if not File.exists?(file)
|
84
|
+
raise FileStoreException, "File #{file} isn't readable" if not File.readable?(file)
|
85
|
+
raise FileStoreException, "File #{file} can't be removed" if not File.writable?(file)
|
86
|
+
|
87
|
+
meta = {} if meta.nil?
|
88
|
+
id = ""
|
89
|
+
|
90
|
+
begin
|
91
|
+
dir = SimpleFileStore.get_daily_directory(@storePath)
|
92
|
+
@logger.info "Adding file #{file} to directory #{dir}"
|
93
|
+
id = SimpleFileStore.get_id(self)
|
94
|
+
@logger.info "Using file id #{id}"
|
95
|
+
dstPath = File.join(dir, id)
|
96
|
+
@logger.info "Created destination path #{dstPath}"
|
97
|
+
|
98
|
+
shouldMove ? (@logger.info("Moving file"); FileUtils.mv(file, dstPath)) :
|
99
|
+
(@logger.info("Copying file"); FileUtils.copy_file(file, dstPath))
|
100
|
+
|
101
|
+
self.inform ObserverAction.new(:type => ObserverAction::TYPE_STORE_ADD,
|
102
|
+
:objects => [file, meta], :msg => "Added file to file store")
|
103
|
+
rescue Exception => e
|
104
|
+
raise FileStoreException, "Couldn't add file #{file} to store.", e.backtrace
|
105
|
+
end
|
106
|
+
|
107
|
+
meta[:path] = dstPath
|
108
|
+
@metaManager.add_or_update(id, meta)
|
109
|
+
|
110
|
+
return id
|
111
|
+
end
|
112
|
+
#
|
113
|
+
# Retrieves a file identified by it's ID
|
114
|
+
#
|
115
|
+
# Arguments:
|
116
|
+
# id: The files ID to retrieve
|
117
|
+
#
|
118
|
+
# Returns:
|
119
|
+
# A hash of file object (:path) and corresponding meta data (:data)
|
120
|
+
# representing the file in the store
|
121
|
+
#
|
122
|
+
def get(id)
|
123
|
+
raise FileStoreException, "No ID given" if id.nil? or id == ''
|
124
|
+
raise FileStoreException, "No file for ID #{id} found" if not @metaManager.has_id?(id)
|
125
|
+
|
126
|
+
md = @metaManager.get_data(id)
|
127
|
+
path = md[:path]
|
128
|
+
|
129
|
+
raise FileStoreException, "No valid meta data found for ID #{id}" if md.nil? or not File.exists?(path)
|
130
|
+
|
131
|
+
self.inform ObserverAction.new :type => ObserverAction::TYPE_STORE_GET,
|
132
|
+
:objects => [id], :msg => "Returning file from file store"
|
133
|
+
|
134
|
+
return { :path => File.new(path), :data => md }
|
135
|
+
end
|
136
|
+
#
|
137
|
+
# Moves a file from the current to the deleted store
|
138
|
+
#
|
139
|
+
# Arguments:
|
140
|
+
# id: The ID identifying the file to be moved
|
141
|
+
#
|
142
|
+
def remove(id)
|
143
|
+
raise FileStoreException, "No file ID given for removal" if id == '' or id.nil?
|
144
|
+
raise FileStoreException, "File ID for removal not found in store" if not @metaManager.has_id?(id)
|
145
|
+
|
146
|
+
file = @metaManager.get_data(id)[:path]
|
147
|
+
|
148
|
+
begin
|
149
|
+
@metaManager.remove(id)
|
150
|
+
|
151
|
+
dir = SimpleFileStore.get_daily_directory(@deletedPath)
|
152
|
+
dstPath = File.join(dir, id)
|
153
|
+
|
154
|
+
FileUtils.move(file, dstPath)
|
155
|
+
|
156
|
+
self.inform ObserverAction.new :type => ObserverAction::TYPE_STORE_REMOVE,
|
157
|
+
:objects => [id], :msg => "Deleted file from store"
|
158
|
+
rescue Exception => e
|
159
|
+
raise FileStoreException, "Couldn't move file #{file} to deleted store.\n#{e.message}"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
#
|
163
|
+
# Restores a file identified by it's id
|
164
|
+
#
|
165
|
+
# Arguments:
|
166
|
+
# id: The file ID
|
167
|
+
#
|
168
|
+
def restore(id)
|
169
|
+
raise FileStoreException, "No file ID given for restore" if id == '' or id.nil?
|
170
|
+
|
171
|
+
begin
|
172
|
+
md = @metaManager.restore id
|
173
|
+
@logger.debug "Restoring meta data #{md}"
|
174
|
+
file = md[:path]
|
175
|
+
|
176
|
+
dir = SimpleFileStore.get_daily_directory(@storePath)
|
177
|
+
dstPath = File.join(dir, id)
|
178
|
+
|
179
|
+
FileUtils.move(file, dstPath)
|
180
|
+
|
181
|
+
self.inform ObserverAction.new :type => ObserverAction::TYPE_STORE_RESTORE,
|
182
|
+
:objects => [id], :msg => "Restored file from store"
|
183
|
+
rescue Exception => e
|
184
|
+
raise FileStoreException, "Couldn't restore file #{file} from deleted store.\n#{e.message}"
|
185
|
+
#
|
186
|
+
# Delete restored entry from metaManager
|
187
|
+
@metaManager.delete(id)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
#
|
191
|
+
# Shuts down the file store
|
192
|
+
#
|
193
|
+
def shutdown
|
194
|
+
@metaManager.shutdown
|
195
|
+
|
196
|
+
release_lock
|
197
|
+
|
198
|
+
self.inform ObserverAction.new :type => ObserverAction::TYPE_STORE_SHUTDOWN,
|
199
|
+
:msg => "File store shutdown"
|
200
|
+
end
|
201
|
+
#
|
202
|
+
# Determines wether this store is locked
|
203
|
+
#
|
204
|
+
def locked?
|
205
|
+
return @locked
|
206
|
+
end
|
207
|
+
|
208
|
+
private
|
209
|
+
|
210
|
+
#
|
211
|
+
# Release the lock from the store
|
212
|
+
#
|
213
|
+
def release_lock
|
214
|
+
begin
|
215
|
+
File.delete File.join(@rootPath, STORE_LOCK_FILE)
|
216
|
+
@locked = false
|
217
|
+
rescue Exception => e
|
218
|
+
raise FileStoreException, "Couldn't release lock from #{@storePath}.\n#{e.message}"
|
219
|
+
end
|
220
|
+
end
|
221
|
+
#
|
222
|
+
# Locks the current instance of file store as well as the corresponding path on
|
223
|
+
# the file system using a hidden file
|
224
|
+
#
|
225
|
+
def lock
|
226
|
+
begin
|
227
|
+
FileUtils.touch File.join(@rootPath, STORE_LOCK_FILE)
|
228
|
+
@locked = true
|
229
|
+
rescue Exception => e
|
230
|
+
raise FileStoreException, "Couldn't lock the store in path #{@storePath}.\n#{e.message}"
|
231
|
+
end
|
232
|
+
end
|
233
|
+
#
|
234
|
+
# Determines wether the store path is already locked by another instance
|
235
|
+
# of SimpleFileStore
|
236
|
+
#
|
237
|
+
def self.is_directory_locked?(rootPath)
|
238
|
+
return File.exists?(File.join(rootPath, SimpleFileStore::STORE_LOCK_FILE))
|
239
|
+
end
|
240
|
+
#
|
241
|
+
# Creates a new file ID
|
242
|
+
#
|
243
|
+
# Returns:
|
244
|
+
# A string representing the file's ID
|
245
|
+
#
|
246
|
+
def self.get_id(store)
|
247
|
+
for i in 0..2 do
|
248
|
+
id = UUIDTools::UUID.random_create.to_s
|
249
|
+
|
250
|
+
return id if not store.metaManager.has_id?(id)
|
251
|
+
end
|
252
|
+
|
253
|
+
raise FileStoreException, "Couldn't find unique ID"
|
254
|
+
end
|
255
|
+
#
|
256
|
+
# Returns the currently used directory
|
257
|
+
#
|
258
|
+
def self.get_daily_directory(base)
|
259
|
+
date = Date.today
|
260
|
+
dir = File.join(base, date.year.to_s, date.month.to_s, date.day.to_s)
|
261
|
+
|
262
|
+
begin
|
263
|
+
FileUtils.mkdir_p(dir) if not File.directory?(dir)
|
264
|
+
rescue Exception => e
|
265
|
+
raise FileStoreException, "Can't create daily directory #{dir}.\n#{e.message}"
|
266
|
+
end
|
267
|
+
|
268
|
+
raise FileStoreException, "Daily directory #{dir} isn't writable" if not File.writable?(dir)
|
269
|
+
return dir
|
270
|
+
end
|
271
|
+
#
|
272
|
+
# Setup for a new file store directory
|
273
|
+
#
|
274
|
+
# Arguments:
|
275
|
+
# store: The file store instance to set up
|
276
|
+
#
|
277
|
+
def self.create_store(store)
|
278
|
+
# Try to create needed directories
|
279
|
+
begin
|
280
|
+
FileUtils.mkdir [store.storePath, store.deletedPath, store.rollbackPath]
|
281
|
+
rescue Errno::ENOENT => e
|
282
|
+
raise FileStoreException, "One ore more system directories couldn't be created.\n#{e.message}"
|
283
|
+
end
|
284
|
+
# Try to create hidden meta file
|
285
|
+
begin
|
286
|
+
meta = { :created_at => Date.today.strftime('%d.%m.%Y %H:%M:%S:%L'),
|
287
|
+
:storePath => store.storePath,
|
288
|
+
:deletedPath => store.deletedPath,
|
289
|
+
:rollbackPath => store.rollbackPath,
|
290
|
+
:created_by => Etc.getlogin
|
291
|
+
}
|
292
|
+
|
293
|
+
File.open(store.metaFile, "w+") do |fh|
|
294
|
+
YAML.dump(meta, fh)
|
295
|
+
end
|
296
|
+
|
297
|
+
#
|
298
|
+
# Creation was successful
|
299
|
+
#
|
300
|
+
rescue Exception => e
|
301
|
+
raise FileStoreException, "Meta file #{store.metaFile} couldn't be created.\n#{e.message}"
|
302
|
+
end
|
303
|
+
end
|
304
|
+
#
|
305
|
+
# Recover an existing file store
|
306
|
+
#
|
307
|
+
# Arguments:
|
308
|
+
# store: The file store instance to recover
|
309
|
+
#
|
310
|
+
def self.recover_store(store)
|
311
|
+
# trying to recover existing file store
|
312
|
+
begin
|
313
|
+
meta = YAML.load_file(store.metaFile)
|
314
|
+
|
315
|
+
raise FileStoreException, "Store directory not found" if not File.directory?(meta[:storePath])
|
316
|
+
raise FileStoreException, "Deleted directory not found" if not File.directory?(meta[:deletedPath])
|
317
|
+
raise FileStoreException, "Rollback directory not found" if not File.directory?(meta[:rollbackPath])
|
318
|
+
|
319
|
+
#
|
320
|
+
# Recovery was successful
|
321
|
+
#
|
322
|
+
rescue Exception => e
|
323
|
+
raise FileStoreException, "Unable to recover file store from path #{store.rootPath}.\n#{e.message}"
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
end
|
328
|
+
|
329
|
+
end
|
data/lib/filestore.rb
CHANGED
@@ -1,334 +1,41 @@
|
|
1
1
|
#
|
2
2
|
# filestore.rb
|
3
|
+
# @author Thomas Stätter
|
4
|
+
# @date 2012/11/26
|
5
|
+
# @description
|
3
6
|
#
|
4
|
-
|
5
|
-
#
|
7
|
+
LIBS = [
|
8
|
+
#
|
9
|
+
# Required 3rd party libs
|
10
|
+
#
|
11
|
+
'uuidtools',
|
12
|
+
'fileutils',
|
13
|
+
'yaml',
|
14
|
+
'etc',
|
15
|
+
'singleton'
|
16
|
+
]
|
17
|
+
FILESTORE_FILES = [
|
18
|
+
#
|
19
|
+
# FileStore specific libraries. Order matters!
|
20
|
+
#
|
21
|
+
'observer',
|
22
|
+
'meta_manager',
|
23
|
+
'log',
|
24
|
+
'memory_meta',
|
25
|
+
'simple_store',
|
26
|
+
'multitenant_store'
|
27
|
+
]
|
6
28
|
#
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
# arbitrary files
|
21
|
-
#
|
22
|
-
class SimpleFileStore
|
23
|
-
include Logger
|
24
|
-
include OberservedSubject
|
25
|
-
|
26
|
-
# Name of the lock file
|
27
|
-
STORE_LOCK_FILE = ".locked"
|
28
|
-
# Name of the meta file describing the current file store
|
29
|
-
META_FILE = "filestore.yaml"
|
30
|
-
# The base name of the file store directory
|
31
|
-
STORE_ROOT = 'filestore'
|
32
|
-
# The base name of the directory for storing deleted files
|
33
|
-
DELETED_ROOT = 'deleted'
|
34
|
-
# The base name of the directory storing files extracted from file store by a
|
35
|
-
# rollback action
|
36
|
-
ROLLBACK_ROOT = 'rollback'
|
37
|
-
#
|
38
|
-
# Accessors for important properties
|
39
|
-
#
|
40
|
-
attr_reader :metaManager, :rootPath, :storePath, :deletedPath, :rollbackPath, :metaFile
|
41
|
-
#
|
42
|
-
# Initializes a new instance of SimpleFileStore
|
43
|
-
#
|
44
|
-
# Arguments:
|
45
|
-
# metaManager: The meta data manager instance to be used by this store
|
46
|
-
# rootPath: The path where the file store resides
|
47
|
-
#
|
48
|
-
def initialize(metaManager, rootPath = '.', logger)
|
49
|
-
raise FileStoreException, "Root path already locked" if SimpleFileStore.is_directory_locked?(rootPath)
|
50
|
-
raise FileStoreException, "FileStore root path #{rootPath} doesn't exist" if not File.directory?(rootPath)
|
51
|
-
raise FileStoreException, "FileStore root path #{rootPath} isn't writable" if not File.writable?(rootPath)
|
52
|
-
raise FileStoreException, "No meta data manager given" if metaManager.nil?
|
53
|
-
raise FileStoreException, "Meta data manager must be of type FileStore::MetaManager" if not metaManager.is_a?(MetaManager)
|
54
|
-
|
55
|
-
@metaManager = metaManager
|
56
|
-
@rootPath = rootPath
|
57
|
-
@storePath = File.join(@rootPath, STORE_ROOT)
|
58
|
-
@deletedPath = File.join(@rootPath, DELETED_ROOT)
|
59
|
-
@rollbackPath = File.join(@rootPath, ROLLBACK_ROOT)
|
60
|
-
@metaFile = File.join(@rootPath, META_FILE)
|
61
|
-
@locked = false
|
62
|
-
@logger = logger
|
63
|
-
|
64
|
-
self.initialize_obs
|
65
|
-
|
66
|
-
begin
|
67
|
-
# Try to recover existing store
|
68
|
-
SimpleFileStore.recover_store(self)
|
69
|
-
rescue FileStoreException => e
|
70
|
-
# Recovery failed, trying to create the store
|
71
|
-
SimpleFileStore.create_store(self)
|
72
|
-
end
|
73
|
-
|
74
|
-
lock
|
75
|
-
end
|
76
|
-
#
|
77
|
-
# Adds a file to the store
|
78
|
-
#
|
79
|
-
# Arguments:
|
80
|
-
# file: The file to be stored
|
81
|
-
# meta: Optional meta data to be stored along with the physical file
|
82
|
-
# shouldMove: Determines wether to original file should be deleted
|
83
|
-
#
|
84
|
-
# Returns:
|
85
|
-
# The newly created ID for the file
|
86
|
-
#
|
87
|
-
def add(file, meta = {}, shouldMove = true)
|
88
|
-
raise FileStoreException, "File #{file} not found" if not File.exists?(file)
|
89
|
-
raise FileStoreException, "File #{file} isn't readable" if not File.readable?(file)
|
90
|
-
raise FileStoreException, "File #{file} can't be removed" if not File.writable?(file)
|
91
|
-
|
92
|
-
meta = {} if meta.nil?
|
93
|
-
id = ""
|
94
|
-
|
95
|
-
begin
|
96
|
-
dir = SimpleFileStore.get_daily_directory(@storePath)
|
97
|
-
@logger.info "Adding file #{file} to directory #{dir}"
|
98
|
-
id = SimpleFileStore.get_id(self)
|
99
|
-
@logger.info "Using file id #{id}"
|
100
|
-
dstPath = File.join(dir, id)
|
101
|
-
@logger.info "Created destination path #{dstPath}"
|
102
|
-
|
103
|
-
shouldMove ? (@logger.info("Moving file"); FileUtils.mv(file, dstPath)) :
|
104
|
-
(@logger.info("Copying file"); FileUtils.copy_file(file, dstPath))
|
105
|
-
|
106
|
-
self.inform ObserverAction.new(:type => ObserverAction::TYPE_STORE_ADD,
|
107
|
-
:objects => [file, meta], :msg => "Added file to file store")
|
108
|
-
rescue Exception => e
|
109
|
-
raise FileStoreException, "Couldn't add file #{file} to store.", e.backtrace
|
110
|
-
end
|
111
|
-
|
112
|
-
meta[:path] = dstPath
|
113
|
-
@metaManager.add_or_update(id, meta)
|
114
|
-
|
115
|
-
return id
|
116
|
-
end
|
117
|
-
#
|
118
|
-
# Retrieves a file identified by it's ID
|
119
|
-
#
|
120
|
-
# Arguments:
|
121
|
-
# id: The files ID to retrieve
|
122
|
-
#
|
123
|
-
# Returns:
|
124
|
-
# A hash of file object (:path) and corresponding meta data (:data)
|
125
|
-
# representing the file in the store
|
126
|
-
#
|
127
|
-
def get(id)
|
128
|
-
raise FileStoreException, "No ID given" if id.nil? or id == ''
|
129
|
-
raise FileStoreException, "No file for ID #{id} found" if not @metaManager.has_id?(id)
|
130
|
-
|
131
|
-
md = @metaManager.get_data(id)
|
132
|
-
path = md[:path]
|
133
|
-
|
134
|
-
raise FileStoreException, "No valid meta data found for ID #{id}" if md.nil? or not File.exists?(path)
|
135
|
-
|
136
|
-
self.inform ObserverAction.new :type => ObserverAction::TYPE_STORE_GET,
|
137
|
-
:objects => [id], :msg => "Returning file from file store"
|
138
|
-
|
139
|
-
return { :path => File.new(path), :data => md }
|
140
|
-
end
|
141
|
-
#
|
142
|
-
# Moves a file from the current to the deleted store
|
143
|
-
#
|
144
|
-
# Arguments:
|
145
|
-
# id: The ID identifying the file to be moved
|
146
|
-
#
|
147
|
-
def remove(id)
|
148
|
-
raise FileStoreException, "No file ID given for removal" if id == '' or id.nil?
|
149
|
-
raise FileStoreException, "File ID for removal not found in store" if not @metaManager.has_id?(id)
|
150
|
-
|
151
|
-
file = @metaManager.get_data(id)[:path]
|
152
|
-
|
153
|
-
begin
|
154
|
-
@metaManager.remove(id)
|
155
|
-
|
156
|
-
dir = SimpleFileStore.get_daily_directory(@deletedPath)
|
157
|
-
dstPath = File.join(dir, id)
|
158
|
-
|
159
|
-
FileUtils.move(file, dstPath)
|
160
|
-
|
161
|
-
self.inform ObserverAction.new :type => ObserverAction::TYPE_STORE_REMOVE,
|
162
|
-
:objects => [id], :msg => "Deleted file from store"
|
163
|
-
rescue Exception => e
|
164
|
-
raise FileStoreException, "Couldn't move file #{file} to deleted store.\n#{e.message}"
|
165
|
-
end
|
166
|
-
end
|
167
|
-
#
|
168
|
-
# Restores a file identified by it's id
|
169
|
-
#
|
170
|
-
# Arguments:
|
171
|
-
# id: The file ID
|
172
|
-
#
|
173
|
-
def restore(id)
|
174
|
-
raise FileStoreException, "No file ID given for restore" if id == '' or id.nil?
|
175
|
-
|
176
|
-
begin
|
177
|
-
md = @metaManager.restore id
|
178
|
-
@logger.debug "Restoring meta data #{md}"
|
179
|
-
file = md[:path]
|
180
|
-
|
181
|
-
dir = SimpleFileStore.get_daily_directory(@storePath)
|
182
|
-
dstPath = File.join(dir, id)
|
183
|
-
|
184
|
-
FileUtils.move(file, dstPath)
|
185
|
-
|
186
|
-
self.inform ObserverAction.new :type => ObserverAction::TYPE_STORE_RESTORE,
|
187
|
-
:objects => [id], :msg => "Restored file from store"
|
188
|
-
rescue Exception => e
|
189
|
-
raise FileStoreException, "Couldn't restore file #{file} from deleted store.\n#{e.message}"
|
190
|
-
#
|
191
|
-
# Delete restored entry from metaManager
|
192
|
-
@metaManager.delete(id)
|
193
|
-
end
|
194
|
-
end
|
195
|
-
#
|
196
|
-
# Shuts down the file store
|
197
|
-
#
|
198
|
-
def shutdown
|
199
|
-
@metaManager.shutdown
|
200
|
-
|
201
|
-
release_lock
|
202
|
-
|
203
|
-
self.inform ObserverAction.new :type => ObserverAction::TYPE_STORE_SHUTDOWN,
|
204
|
-
:msg => "File store shutdown"
|
205
|
-
end
|
206
|
-
#
|
207
|
-
# Determines wether this store is locked
|
208
|
-
#
|
209
|
-
def locked?
|
210
|
-
return @locked
|
211
|
-
end
|
212
|
-
|
213
|
-
private
|
214
|
-
|
215
|
-
#
|
216
|
-
# Release the lock from the store
|
217
|
-
#
|
218
|
-
def release_lock
|
219
|
-
begin
|
220
|
-
File.delete File.join(@rootPath, STORE_LOCK_FILE)
|
221
|
-
@locked = false
|
222
|
-
rescue Exception => e
|
223
|
-
raise FileStoreException, "Couldn't release lock from #{@storePath}.\n#{e.message}"
|
224
|
-
end
|
225
|
-
end
|
226
|
-
#
|
227
|
-
# Locks the current instance of file store as well as the corresponding path on
|
228
|
-
# the file system using a hidden file
|
229
|
-
#
|
230
|
-
def lock
|
231
|
-
begin
|
232
|
-
FileUtils.touch File.join(@rootPath, STORE_LOCK_FILE)
|
233
|
-
@locked = true
|
234
|
-
rescue Exception => e
|
235
|
-
raise FileStoreException, "Couldn't lock the store in path #{@storePath}.\n#{e.message}"
|
236
|
-
end
|
237
|
-
end
|
238
|
-
#
|
239
|
-
# Determines wether the store path is already locked by another instance
|
240
|
-
# of SimpleFileStore
|
241
|
-
#
|
242
|
-
def self.is_directory_locked?(rootPath)
|
243
|
-
return File.exists?(File.join(rootPath, SimpleFileStore::STORE_LOCK_FILE))
|
244
|
-
end
|
245
|
-
#
|
246
|
-
# Creates a new file ID
|
247
|
-
#
|
248
|
-
# Returns:
|
249
|
-
# A string representing the file's ID
|
250
|
-
#
|
251
|
-
def self.get_id(store)
|
252
|
-
for i in 0..2 do
|
253
|
-
id = UUIDTools::UUID.random_create.to_s
|
254
|
-
|
255
|
-
return id if not store.metaManager.has_id?(id)
|
256
|
-
end
|
257
|
-
|
258
|
-
raise FileStoreException, "Couldn't find unique ID"
|
259
|
-
end
|
260
|
-
#
|
261
|
-
# Returns the currently used directory
|
262
|
-
#
|
263
|
-
def self.get_daily_directory(base)
|
264
|
-
date = Date.today
|
265
|
-
dir = File.join(base, date.year.to_s, date.month.to_s, date.day.to_s)
|
266
|
-
|
267
|
-
begin
|
268
|
-
FileUtils.mkdir_p(dir) if not File.directory?(dir)
|
269
|
-
rescue Exception => e
|
270
|
-
raise FileStoreException, "Can't create daily directory #{dir}.\n#{e.message}"
|
271
|
-
end
|
272
|
-
|
273
|
-
raise FileStoreException, "Daily directory #{dir} isn't writable" if not File.writable?(dir)
|
274
|
-
return dir
|
275
|
-
end
|
276
|
-
#
|
277
|
-
# Setup for a new file store directory
|
278
|
-
#
|
279
|
-
# Arguments:
|
280
|
-
# store: The file store instance to set up
|
281
|
-
#
|
282
|
-
def self.create_store(store)
|
283
|
-
# Try to create needed directories
|
284
|
-
begin
|
285
|
-
FileUtils.mkdir [store.storePath, store.deletedPath, store.rollbackPath]
|
286
|
-
rescue Errno::ENOENT => e
|
287
|
-
raise FileStoreException, "One ore more system directories couldn't be created.\n#{e.message}"
|
288
|
-
end
|
289
|
-
# Try to create hidden meta file
|
290
|
-
begin
|
291
|
-
meta = { :created_at => Date.today.strftime('%d.%m.%Y %H:%M:%S:%L'),
|
292
|
-
:storePath => store.storePath,
|
293
|
-
:deletedPath => store.deletedPath,
|
294
|
-
:rollbackPath => store.rollbackPath,
|
295
|
-
:created_by => Etc.getlogin
|
296
|
-
}
|
297
|
-
|
298
|
-
File.open(store.metaFile, "w+") do |fh|
|
299
|
-
YAML.dump(meta, fh)
|
300
|
-
end
|
301
|
-
|
302
|
-
#
|
303
|
-
# Creation was successful
|
304
|
-
#
|
305
|
-
rescue Exception => e
|
306
|
-
raise FileStoreException, "Meta file #{store.metaFile} couldn't be created.\n#{e.message}"
|
307
|
-
end
|
308
|
-
end
|
309
|
-
#
|
310
|
-
# Recover an existing file store
|
311
|
-
#
|
312
|
-
# Arguments:
|
313
|
-
# store: The file store instance to recover
|
314
|
-
#
|
315
|
-
def self.recover_store(store)
|
316
|
-
# trying to recover existing file store
|
317
|
-
begin
|
318
|
-
meta = YAML.load_file(store.metaFile)
|
319
|
-
|
320
|
-
raise FileStoreException, "Store directory not found" if not File.directory?(meta[:storePath])
|
321
|
-
raise FileStoreException, "Deleted directory not found" if not File.directory?(meta[:deletedPath])
|
322
|
-
raise FileStoreException, "Rollback directory not found" if not File.directory?(meta[:rollbackPath])
|
323
|
-
|
324
|
-
#
|
325
|
-
# Recovery was successful
|
326
|
-
#
|
327
|
-
rescue Exception => e
|
328
|
-
raise FileStoreException, "Unable to recover file store from path #{store.rootPath}.\n#{e.message}"
|
329
|
-
end
|
330
|
-
end
|
331
|
-
|
332
|
-
end
|
333
|
-
|
334
|
-
end
|
29
|
+
# Loads required 3rd party libs as defined in FileStore::LIBS
|
30
|
+
#
|
31
|
+
LIBS.each do |l|
|
32
|
+
require l
|
33
|
+
end
|
34
|
+
#
|
35
|
+
# Loads required files as defined in FileStore::FILESTORE_FILES
|
36
|
+
#
|
37
|
+
FILESTORE_FILES.each do |l|
|
38
|
+
require File.join('filestore', l)
|
39
|
+
|
40
|
+
puts "Required file #{File.join('filestore', l)}"
|
41
|
+
end
|
data/test/classes.rb
CHANGED
data/test/tc_filestore.rb
CHANGED
data/test/tc_meta.rb
CHANGED
data/test/tc_module.rb
CHANGED
data/test/tc_multitenant.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: filestore
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.9
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -33,45 +33,23 @@ dependencies:
|
|
33
33
|
- - ! '>='
|
34
34
|
- !ruby/object:Gem::Version
|
35
35
|
version: '2.0'
|
36
|
-
- !ruby/object:Gem::Dependency
|
37
|
-
name: log4r
|
38
|
-
requirement: !ruby/object:Gem::Requirement
|
39
|
-
none: false
|
40
|
-
requirements:
|
41
|
-
- - ~>
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
version: 1.1.10
|
44
|
-
- - ! '>='
|
45
|
-
- !ruby/object:Gem::Version
|
46
|
-
version: '1.0'
|
47
|
-
type: :runtime
|
48
|
-
prerelease: false
|
49
|
-
version_requirements: !ruby/object:Gem::Requirement
|
50
|
-
none: false
|
51
|
-
requirements:
|
52
|
-
- - ~>
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: 1.1.10
|
55
|
-
- - ! '>='
|
56
|
-
- !ruby/object:Gem::Version
|
57
|
-
version: '1.0'
|
58
36
|
description: Organizes a file storage using the file system and some meta data
|
59
37
|
email: thomas.staetter@gmail.com
|
60
38
|
executables: []
|
61
39
|
extensions: []
|
62
40
|
extra_rdoc_files: []
|
63
41
|
files:
|
64
|
-
-
|
42
|
+
- lib/filestore/log.rb
|
43
|
+
- lib/filestore/memory_meta.rb
|
44
|
+
- lib/filestore/meta_manager.rb
|
45
|
+
- lib/filestore/multitenant_store.rb
|
46
|
+
- lib/filestore/observer.rb
|
47
|
+
- lib/filestore/simple_store.rb
|
65
48
|
- lib/filestore.rb
|
66
|
-
- lib/memory_meta.rb
|
67
|
-
- lib/log.rb
|
68
|
-
- lib/meta_manager.rb
|
69
|
-
- lib/multitenant_filestore.rb
|
70
|
-
- lib/observer.rb
|
71
49
|
- test/classes.rb
|
50
|
+
- test/tc_filestore.rb
|
72
51
|
- test/tc_meta.rb
|
73
52
|
- test/tc_module.rb
|
74
|
-
- test/tc_filestore.rb
|
75
53
|
- test/tc_multitenant.rb
|
76
54
|
- test/ts_all.rb
|
77
55
|
homepage: https://github.com/tstaetter/filestore-gem
|
@@ -101,8 +79,8 @@ specification_version: 3
|
|
101
79
|
summary: Simple file storage
|
102
80
|
test_files:
|
103
81
|
- test/classes.rb
|
82
|
+
- test/tc_filestore.rb
|
104
83
|
- test/tc_meta.rb
|
105
84
|
- test/tc_module.rb
|
106
|
-
- test/tc_filestore.rb
|
107
85
|
- test/tc_multitenant.rb
|
108
86
|
- test/ts_all.rb
|
data/module.rb
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# filestore.rb
|
3
|
-
# @author Thomas Stätter
|
4
|
-
# @date 2012/11/26
|
5
|
-
# @description
|
6
|
-
#
|
7
|
-
module FileStore
|
8
|
-
VERSION = '0.0.8'
|
9
|
-
LIB_FILES = [
|
10
|
-
#
|
11
|
-
# Required 3rd party libs
|
12
|
-
#
|
13
|
-
'uuidtools',
|
14
|
-
'fileutils',
|
15
|
-
'yaml',
|
16
|
-
'singleton',
|
17
|
-
'etc',
|
18
|
-
#
|
19
|
-
# FileStore specific libraries. Order matters!
|
20
|
-
#
|
21
|
-
'lib/meta_manager.rb',
|
22
|
-
'lib/log.rb',
|
23
|
-
'lib/observer.rb',
|
24
|
-
'lib/filestore.rb',
|
25
|
-
'lib/multitenant_filestore.rb',
|
26
|
-
'lib/memory_meta.rb'
|
27
|
-
]
|
28
|
-
#
|
29
|
-
# Loads required libs as defined in FileStore::LIB_FILES
|
30
|
-
#
|
31
|
-
def self.load_required
|
32
|
-
$LOAD_PATH << File.dirname(File.new(__FILE__))
|
33
|
-
|
34
|
-
LIB_FILES.each do |l|
|
35
|
-
require l
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
FileStore::load_required
|
File without changes
|