Current section
Files
Jump to
Current section
Files
src/filezcache.erl
%% @author Marc Worrell
%% @copyright 2013-2014 Marc Worrell
%% Copyright 2013-2014 Marc Worrell
%%
%% 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.
-module(filezcache).
-export([
insert/2,
insert/3,
insert_file/2,
insert_file/3,
insert_tmpfile/2,
insert_tmpfile/3,
insert_wait/1,
insert_stream/1,
insert_stream/3,
insert_stream/4,
append_stream/2,
finish_stream/1,
locate_monitor/1,
access/1,
lookup/1,
lookup_file/1,
lookup/2,
lookup_file/2,
delete/1,
where/1,
stats/0,
data_dir/0,
journal_dir/0,
checksum/1
]).
-define(BLOCK_SIZE, 65536).
%%% API
insert(Key, Bin) ->
insert(Key, Bin, []).
insert(Key, Bin, Opts) when is_binary(Bin) ->
insert_1(Key, {data, Bin}, Opts).
insert_file(Key, FilePath) ->
insert_file(Key, FilePath, []).
insert_file(Key, FilePath, Opts) ->
insert_1(Key, {file, FilePath}, Opts).
insert_tmpfile(Key, FilePath) ->
insert_tmpfile(Key, FilePath, []).
insert_tmpfile(Key, FilePath, Opts) ->
insert_1(Key, {tmpfile, FilePath}, Opts).
insert_wait(Key) ->
insert_1(Key, none, []).
insert_stream(Key) ->
insert_stream(Key, undefined, []).
insert_stream(Key, FinalSize, Opts) ->
insert_1(Key, {stream_start, self(), FinalSize}, Opts).
insert_stream(Key, FinalSize, StreamFun, Opts) when is_function(StreamFun, 1) ->
insert_1(Key, {stream_fun, self(), FinalSize, StreamFun}, Opts).
append_stream(Pid, Bin) ->
filezcache_entry:append_stream(Pid, Bin).
finish_stream(Pid) ->
filezcache_entry:finish_stream(Pid).
-spec locate_monitor(term()) -> {ok, {file, integer(), string()}} | {ok, {pid, pid()}} | {error, term()}.
locate_monitor(Key) ->
case filezcache_entry_manager:lookup(Key, self()) of
{ok, _Found} = OK ->
OK;
{error, enoent} ->
case where(Key) of
undefined ->
{error, enoent};
Pid ->
filezcache_entry_manager:log_access(Key, self()),
{ok, {pid, Pid}}
end
end.
-spec access(term()) -> ok.
access(Key) ->
filezcache_entry_manager:log_access(Key).
-spec lookup(term()) -> {ok, {file, integer(), string()}} | {ok, {device, pid()}} | {error, term()}.
lookup(Key) ->
lookup(Key, []).
lookup(Pid, Opts) when is_pid(Pid) ->
filezcache_entry:fetch(Pid, Opts);
lookup(Key, Opts) ->
filezcache_event:lookup(Key),
case filezcache_entry_manager:lookup(Key) of
{ok, _Found} = OK ->
OK;
{error, enoent} ->
case filezcache_store:lookup(Key) of
{ok, Pid} ->
filezcache_entry:fetch(Pid, Opts);
{error, _} = Error ->
Error
end;
{error, _} = Error ->
Error
end.
-spec lookup_file(term()) -> {ok, {file, integer(), string()}} | {error, term()}.
lookup_file(Key) ->
lookup_file(Key, []).
lookup_file(Pid, Opts) when is_pid(Pid) ->
try
filezcache_entry:fetch_file(Pid, Opts)
catch
exit:{noproc, _} ->
{error, enoent}
end;
lookup_file(Key, Opts) ->
filezcache_event:lookup(Key),
case filezcache_entry_manager:lookup(Key) of
{ok, Found} ->
Found;
{error, enoent} ->
case filezcache_store:lookup(Key) of
{ok, Pid} ->
try
filezcache_entry:fetch_file(Pid, Opts)
catch
exit:{noproc, _} ->
{error, enoent}
end;
{error, _} = Error ->
Error
end;
{error, _} = Error ->
Error
end.
-spec delete(term()) -> ok | {error, lockedlog_a}.
delete(Key) ->
{ok,_} = filezcache_entry_manager:delete(Key),
case filezcache_store:lookup(Key) of
{ok, Pid} ->
filezcache_entry:delete(Pid);
{error, enoent} ->
ok;
{error, _Reason} = Error ->
Error
end.
-spec where(term()) -> pid() | undefined.
where(Key) ->
case filezcache_store:lookup(Key) of
{ok, Pid} -> Pid;
{error, enoent} -> undefined
end.
-spec stats() -> list().
stats() ->
filezcache_entry_manager:stats().
%%% Support functions
insert_1(Key, DataSource, Opts) ->
insert_or_error(filezcache_store:lookup(Key), Key, DataSource, Opts).
insert_or_error({ok, Pid}, _Key, _DataSource, _Opts) ->
{error, {already_started, Pid}};
insert_or_error({error, enoent}, Key, DataSource, Opts) ->
case filezcache_entry_manager:insert(Key, Opts) of
{ok, Pid} ->
case DataSource of
none -> ok;
_Data -> ok = filezcache_entry:store(Pid, DataSource)
end,
{ok, Pid};
{error, _} = Error ->
Error
end.
%% @doc Return the directory for the storage of the cached files
-spec data_dir() -> file:filename().
data_dir() ->
case application:get_env(filezcache, data_dir) of
undefined -> filename:join([priv_dir(), "data"]);
{ok, Dir} -> Dir
end.
%% @doc Return the directory for the storage of the log/journal files
-spec journal_dir() -> file:filename().
journal_dir() ->
case application:get_env(filezcache, journal_dir) of
undefined -> filename:join([priv_dir(), "journal"]);
{ok, Dir} -> Dir
end.
priv_dir() ->
case code:priv_dir(?MODULE) of
{error, bad_name} -> "priv";
PrivDir -> PrivDir
end.
-spec checksum(file:filename()) -> binary().
checksum(Filename) ->
Ctx = crypto:hash_init(sha),
{ok, FD} = file:open(Filename, [read,binary]),
Ctx1 = checksum1(Ctx, FD),
file:close(FD),
crypto:hash_final(Ctx1).
checksum1(Ctx, FD) ->
case file:read(FD, ?BLOCK_SIZE) of
eof ->
Ctx;
{ok, Data} ->
checksum1(crypto:hash_update(Ctx, Data), FD)
end.