@@ -16,37 +16,37 @@ namespace Microsoft.Azure.WebJobs.Script
1616 public class ScriptHost : JobHost
1717 {
1818 private const string HostAssemblyName = "ScriptHost" ;
19+ private FileSystemWatcher _fileWatcher ;
1920
20- protected ScriptHost ( JobHostConfiguration config , ScriptHostConfiguration scriptConfig )
21- : base ( config )
21+ protected ScriptHost ( ScriptHostConfiguration scriptConfig )
22+ : base ( scriptConfig . HostConfig )
2223 {
23- HostConfig = config ;
2424 ScriptConfig = scriptConfig ;
2525 }
2626
27- public JobHostConfiguration HostConfig { get ; private set ; }
28-
2927 public ScriptHostConfiguration ScriptConfig { get ; private set ; }
3028
29+ public bool Restart { get ; private set ; }
30+
3131 protected virtual void Initialize ( )
3232 {
3333 List < FunctionDescriptorProvider > descriptionProviders = new List < FunctionDescriptorProvider > ( )
3434 {
35- new ScriptFunctionDescriptorProvider ( HostConfig , ScriptConfig . RootPath ) ,
36- new NodeFunctionDescriptorProvider ( HostConfig , ScriptConfig . RootPath )
35+ new ScriptFunctionDescriptorProvider ( ScriptConfig ) ,
36+ new NodeFunctionDescriptorProvider ( this , ScriptConfig )
3737 } ;
3838
39- if ( HostConfig . IsDevelopment )
39+ if ( ScriptConfig . HostConfig . IsDevelopment )
4040 {
41- HostConfig . UseDevelopmentSettings ( ) ;
41+ ScriptConfig . HostConfig . UseDevelopmentSettings ( ) ;
4242 }
4343
4444 // read host.json and apply to JobHostConfiguration
4545 string hostConfigFilePath = Path . Combine ( ScriptConfig . RootPath , "host.json" ) ;
4646 Console . WriteLine ( string . Format ( "Reading host configuration file '{0}'" , hostConfigFilePath ) ) ;
4747 string json = File . ReadAllText ( hostConfigFilePath ) ;
4848 JObject hostConfig = JObject . Parse ( json ) ;
49- ApplyConfiguration ( hostConfig , HostConfig ) ;
49+ ApplyConfiguration ( hostConfig , ScriptConfig ) ;
5050
5151 // read all script functions and apply to JobHostConfiguration
5252 Collection < FunctionDescriptor > functions = ReadFunctions ( ScriptConfig , descriptionProviders ) ;
@@ -57,8 +57,33 @@ protected virtual void Initialize()
5757 List < Type > types = new List < Type > ( ) ;
5858 types . Add ( type ) ;
5959
60- HostConfig . TypeLocator = new TypeLocator ( types ) ;
61- HostConfig . NameResolver = new NameResolver ( ) ;
60+ ScriptConfig . HostConfig . TypeLocator = new TypeLocator ( types ) ;
61+ ScriptConfig . HostConfig . NameResolver = new NameResolver ( ) ;
62+
63+ if ( ScriptConfig . WatchFiles )
64+ {
65+ _fileWatcher = new FileSystemWatcher ( ScriptConfig . RootPath , "*.json" )
66+ {
67+ IncludeSubdirectories = true ,
68+ EnableRaisingEvents = true
69+ } ;
70+ _fileWatcher . Changed += OnConfigurationFileChanged ;
71+ }
72+ }
73+
74+ private void StopAndRestart ( )
75+ {
76+ if ( Restart )
77+ {
78+ // we've already received a restart call
79+ return ;
80+ }
81+
82+ Console . WriteLine ( "The host configuration file has changed. Restarting." ) ;
83+
84+ // Flag for restart and stop the host.
85+ Restart = true ;
86+ Stop ( ) ;
6287 }
6388
6489 public static ScriptHost Create ( ScriptHostConfiguration scriptConfig = null )
@@ -76,8 +101,7 @@ public static ScriptHost Create(ScriptHostConfiguration scriptConfig = null)
76101 scriptConfig . RootPath = Path . Combine ( Environment . CurrentDirectory , scriptConfig . RootPath ) ;
77102 }
78103
79- JobHostConfiguration config = new JobHostConfiguration ( ) ;
80- ScriptHost scriptHost = new ScriptHost ( config , scriptConfig ) ;
104+ ScriptHost scriptHost = new ScriptHost ( scriptConfig ) ;
81105 scriptHost . Initialize ( ) ;
82106
83107 return scriptHost ;
@@ -180,14 +204,22 @@ internal static Collection<FunctionDescriptor> ReadFunctions(List<FunctionFolder
180204 return functionDescriptors ;
181205 }
182206
183- internal static void ApplyConfiguration ( JObject config , JobHostConfiguration jobHostConfig )
207+ internal static void ApplyConfiguration ( JObject config , ScriptHostConfiguration scriptConfig )
184208 {
209+ JobHostConfiguration hostConfig = scriptConfig . HostConfig ;
210+
185211 JToken hostId = ( JToken ) config [ "id" ] ;
186212 if ( hostId == null )
187213 {
188214 throw new InvalidOperationException ( "An 'id' must be specified in the host configuration." ) ;
189215 }
190- jobHostConfig . HostId = ( string ) hostId ;
216+ hostConfig . HostId = ( string ) hostId ;
217+
218+ JToken watchFiles = ( JToken ) config [ "watchFiles" ] ;
219+ if ( watchFiles != null && watchFiles . Type == JTokenType . Boolean )
220+ {
221+ scriptConfig . WatchFiles = ( bool ) watchFiles ;
222+ }
191223
192224 // Apply Queues configuration
193225 JObject configSection = ( JObject ) config [ "queues" ] ;
@@ -196,19 +228,19 @@ internal static void ApplyConfiguration(JObject config, JobHostConfiguration job
196228 {
197229 if ( configSection . TryGetValue ( "maxPollingInterval" , out value ) )
198230 {
199- jobHostConfig . Queues . MaxPollingInterval = TimeSpan . FromMilliseconds ( ( int ) value ) ;
231+ hostConfig . Queues . MaxPollingInterval = TimeSpan . FromMilliseconds ( ( int ) value ) ;
200232 }
201233 if ( configSection . TryGetValue ( "batchSize" , out value ) )
202234 {
203- jobHostConfig . Queues . BatchSize = ( int ) value ;
235+ hostConfig . Queues . BatchSize = ( int ) value ;
204236 }
205237 if ( configSection . TryGetValue ( "maxDequeueCount" , out value ) )
206238 {
207- jobHostConfig . Queues . MaxDequeueCount = ( int ) value ;
239+ hostConfig . Queues . MaxDequeueCount = ( int ) value ;
208240 }
209241 if ( configSection . TryGetValue ( "newBatchThreshold" , out value ) )
210242 {
211- jobHostConfig . Queues . NewBatchThreshold = ( int ) value ;
243+ hostConfig . Queues . NewBatchThreshold = ( int ) value ;
212244 }
213245 }
214246
@@ -219,23 +251,23 @@ internal static void ApplyConfiguration(JObject config, JobHostConfiguration job
219251 {
220252 if ( configSection . TryGetValue ( "lockPeriod" , out value ) )
221253 {
222- jobHostConfig . Singleton . LockPeriod = TimeSpan . Parse ( ( string ) value ) ;
254+ hostConfig . Singleton . LockPeriod = TimeSpan . Parse ( ( string ) value ) ;
223255 }
224256 if ( configSection . TryGetValue ( "listenerLockPeriod" , out value ) )
225257 {
226- jobHostConfig . Singleton . ListenerLockPeriod = TimeSpan . Parse ( ( string ) value ) ;
258+ hostConfig . Singleton . ListenerLockPeriod = TimeSpan . Parse ( ( string ) value ) ;
227259 }
228260 if ( configSection . TryGetValue ( "listenerLockRecoveryPollingInterval" , out value ) )
229261 {
230- jobHostConfig . Singleton . ListenerLockRecoveryPollingInterval = TimeSpan . Parse ( ( string ) value ) ;
262+ hostConfig . Singleton . ListenerLockRecoveryPollingInterval = TimeSpan . Parse ( ( string ) value ) ;
231263 }
232264 if ( configSection . TryGetValue ( "lockAcquisitionTimeout" , out value ) )
233265 {
234- jobHostConfig . Singleton . LockAcquisitionTimeout = TimeSpan . Parse ( ( string ) value ) ;
266+ hostConfig . Singleton . LockAcquisitionTimeout = TimeSpan . Parse ( ( string ) value ) ;
235267 }
236268 if ( configSection . TryGetValue ( "lockAcquisitionPollingInterval" , out value ) )
237269 {
238- jobHostConfig . Singleton . LockAcquisitionPollingInterval = TimeSpan . Parse ( ( string ) value ) ;
270+ hostConfig . Singleton . LockAcquisitionPollingInterval = TimeSpan . Parse ( ( string ) value ) ;
239271 }
240272 }
241273
@@ -250,7 +282,7 @@ internal static void ApplyConfiguration(JObject config, JobHostConfiguration job
250282 sbConfig . MessageOptions . MaxConcurrentCalls = ( int ) value ;
251283 }
252284 }
253- jobHostConfig . UseServiceBus ( sbConfig ) ;
285+ hostConfig . UseServiceBus ( sbConfig ) ;
254286
255287 // Apply Tracing configuration
256288 configSection = ( JObject ) config [ "tracing" ] ;
@@ -259,7 +291,7 @@ internal static void ApplyConfiguration(JObject config, JobHostConfiguration job
259291 TraceLevel consoleLevel ;
260292 if ( Enum . TryParse < TraceLevel > ( ( string ) value , true , out consoleLevel ) )
261293 {
262- jobHostConfig . Tracing . ConsoleLevel = consoleLevel ;
294+ hostConfig . Tracing . ConsoleLevel = consoleLevel ;
263295 }
264296 }
265297
@@ -270,9 +302,33 @@ internal static void ApplyConfiguration(JObject config, JobHostConfiguration job
270302 {
271303 webHooksConfig = new WebHooksConfiguration ( ( int ) value ) ;
272304 }
273- jobHostConfig . UseWebHooks ( webHooksConfig ) ;
305+ hostConfig . UseWebHooks ( webHooksConfig ) ;
306+
307+ hostConfig . UseTimers ( ) ;
308+ }
309+
310+ private void OnConfigurationFileChanged ( object sender , FileSystemEventArgs e )
311+ {
312+ string fileName = Path . GetFileName ( e . Name ) ;
313+
314+ if ( ! Restart &&
315+ ( ( string . Compare ( fileName , "host.json" ) == 0 ) || string . Compare ( fileName , "function.json" ) == 0 ) )
316+ {
317+ StopAndRestart ( ) ;
318+ }
319+ }
320+
321+ protected override void Dispose ( bool disposing )
322+ {
323+ if ( disposing )
324+ {
325+ if ( _fileWatcher != null )
326+ {
327+ _fileWatcher . Dispose ( ) ;
328+ }
329+ }
274330
275- jobHostConfig . UseTimers ( ) ;
331+ base . Dispose ( disposing ) ;
276332 }
277333 }
278334}
0 commit comments