filestore 0.0.8 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|