PageRenderTime 35ms CodeModel.GetById 12ms app.highlight 17ms RepoModel.GetById 2ms app.codeStats 0ms

/modules/mod_development/z_development_server.erl

https://code.google.com/p/zotonic/
Erlang | 188 lines | 82 code | 35 blank | 71 comment | 3 complexity | 0536a4ca94b2211255cf56788169ccc0 MD5 | raw file
  1%% @author Marc Worrell <marc@worrell.nl>
  2%% @copyright 2009 Marc Worrell
  3%% Date: 2009-11-01
  4%%
  5%% @doc Development server.  Periodically loads modules whose beam file have been updated.
  6%% This server is started by the mod_development.
  7
  8%% Copyright 2009 Marc Worrell
  9%%
 10%% Licensed under the Apache License, Version 2.0 (the "License");
 11%% you may not use this file except in compliance with the License.
 12%% You may obtain a copy of the License at
 13%% 
 14%%     http://www.apache.org/licenses/LICENSE-2.0
 15%% 
 16%% Unless required by applicable law or agreed to in writing, software
 17%% distributed under the License is distributed on an "AS IS" BASIS,
 18%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 19%% See the License for the specific language governing permissions and
 20%% limitations under the License.
 21
 22-module(z_development_server).
 23-author("Marc Worrell <marc@worrell.nl>").
 24-behaviour(gen_server).
 25
 26%% gen_server exports
 27-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
 28-export([start_link/0, start_link/1]).
 29
 30%% interface functions
 31-export([
 32	reload/0,
 33	make/0
 34]).
 35
 36-include_lib("zotonic.hrl").
 37
 38% The state record for this server
 39-record(state, {}).
 40
 41% Interval for checking for new and/or changed files.
 42-define(DEV_POLL_INTERVAL, 10000).
 43
 44
 45%%====================================================================
 46%% API
 47%%====================================================================
 48%% @spec start_link() -> {ok,Pid} | ignore | {error,Error}
 49%% @doc Starts the server
 50start_link() ->
 51	start_link([]).
 52start_link(Args) when is_list(Args) ->
 53    gen_server:start_link({local, ?MODULE}, ?MODULE, Args, []).
 54
 55
 56%% @doc Check if beam files are changed, load the changed ones.
 57reload() ->
 58	gen_server:cast(?MODULE, reload).
 59
 60%% @doc Perform a make:all() and reload the changed beam files.
 61make() ->
 62	gen_server:cast(?MODULE, make).
 63
 64
 65%%====================================================================
 66%% gen_server callbacks
 67%%====================================================================
 68
 69%% @spec init(Args) -> {ok, State} |
 70%%                     {ok, State, Timeout} |
 71%%                     ignore               |
 72%%                     {stop, Reason}
 73%% @doc Initiates the server.
 74init(_Args) ->
 75    timer:send_interval(?DEV_POLL_INTERVAL, reload_beams),
 76    {ok, #state{}}.
 77
 78
 79%% @spec handle_call(Request, From, State) -> {reply, Reply, State} |
 80%%                                      {reply, Reply, State, Timeout} |
 81%%                                      {noreply, State} |
 82%%                                      {noreply, State, Timeout} |
 83%%                                      {stop, Reason, Reply, State} |
 84%%                                      {stop, Reason, State}
 85%% @doc Trap unknown calls
 86handle_call(Message, _From, State) ->
 87    {stop, {unknown_call, Message}, State}.
 88
 89
 90%% @spec handle_cast(Msg, State) -> {noreply, State} |
 91%%                                  {noreply, State, Timeout} |
 92%%                                  {stop, Reason, State}
 93handle_cast(reload, State) ->
 94	reload_all(),
 95    {noreply, State};
 96
 97handle_cast(make, State) ->
 98	make_all(),
 99	reload_all(),
100    {noreply, State};
101
102%% @doc Trap unknown casts
103handle_cast(Message, State) ->
104    {stop, {unknown_cast, Message}, State}.
105
106
107%% @spec handle_info(Info, State) -> {noreply, State} |
108%%                                       {noreply, State, Timeout} |
109%%                                       {stop, Reason, State}
110%% @doc Periodic check for changed beam files.
111handle_info(reload_beams, State) ->
112	reload_all(),
113    z_utils:flush_message(reload_beams), 
114    {noreply, State};
115
116%% @doc Handling all non call/cast messages
117handle_info(_Info, State) ->
118    {noreply, State}.
119
120%% @spec terminate(Reason, State) -> void()
121%% @doc This function is called by a gen_server when it is about to
122%% terminate. It should be the opposite of Module:init/1 and do any necessary
123%% cleaning up. When it returns, the gen_server terminates with Reason.
124%% The return value is ignored.
125terminate(_Reason, _State) ->
126    ok.
127
128%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
129%% @doc Convert process state when code is changed
130code_change(_OldVsn, State, _Extra) ->
131    {ok, State}.
132
133
134%%====================================================================
135%% support functions
136%%====================================================================
137
138%% @doc Check if any of the loaded modules has been changed. If so reload the module's beam file. Also rescan for templates and files.
139reload_all() ->
140	case reload_loaded_modules() of
141		[] -> 
142		    z_sites_manager:update_dispatchinfo(),
143		    [ z_module_indexer:reindex(C) || C <- z_sites_manager:get_site_contexts() ],
144		    ok;
145		_ -> 
146		    z:flush()
147	end.
148	
149
150%% @doc Remake beam files from source. Do not load the new files (yet).
151make_all() ->
152	make:all([]),
153	ok.
154
155%% @doc Reload a module, purge the old code.
156reload_module(M) ->
157	code:purge(M),
158	code:soft_purge(M),
159	{module, M} = code:load_file(M),
160	{ok, M}.
161
162%% @doc Reload all modules from the zotonic directory or subdirectories.  Return a list of modules reloaded. Empty list when nothing changed.
163%% @spec reload_loaded_modules() -> [Result]
164reload_loaded_modules() ->
165	Dir = z_utils:lib_dir(),
166	Modules = [{M,P} || {M, P} <- code:all_loaded(), is_list(P) andalso string:str(P, Dir) > 0],
167	[reload_module(M) || {M,Path} <- Modules, module_changed(M,Path)].
168	
169%% @doc Check if the version number of the module has been changed.  Skip template modules.
170%% @spec module_changed(atom(), filename()) -> bool()
171module_changed(Module, BeamFile) ->
172	case z_template:is_template_module(Module) of
173		true -> 
174			false;
175		false ->
176			Props = Module:module_info(attributes),
177			case proplists:get_value(vsn, Props) of
178				undefined ->
179					false;
180				Version ->
181					case beam_lib:version(BeamFile) of
182						{ok, {_Module, Version}} -> false;
183						{ok, {_Module, _OtherVersion}} -> true;
184						{error, _, _} -> false
185					end
186			end
187	end.
188