Class: AemLookout::Watcher

Inherits:
Object
  • Object
show all
Defined in:
lib/aem_lookout/watcher.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(repo_path, config, log = nil) ⇒ Watcher

Returns a new instance of Watcher.



11
12
13
14
15
# File 'lib/aem_lookout/watcher.rb', line 11

def initialize(repo_path, config, log = nil)
  @repo_path = repo_path
  @config = config
  @log = log || Logger.new(STDOUT)
end

Instance Attribute Details

#configObject

Returns the value of attribute config.



9
10
11
# File 'lib/aem_lookout/watcher.rb', line 9

def config
  @config
end

#logObject

Returns the value of attribute log.



9
10
11
# File 'lib/aem_lookout/watcher.rb', line 9

def log
  @log
end

#repo_pathObject

Returns the value of attribute repo_path.



9
10
11
# File 'lib/aem_lookout/watcher.rb', line 9

def repo_path
  @repo_path
end

Class Method Details

.run(repo_path, config) ⇒ Object



5
6
7
# File 'lib/aem_lookout/watcher.rb', line 5

def self.run(repo_path, config)
  self.new(repo_path, config).run
end

Instance Method Details

#command_configsObject

Something like this [=> “java-core/src/main/java”, “pwd” => “java-core”, “command” => “mvn install -P author-localhost”]



181
182
183
184
185
186
# File 'lib/aem_lookout/watcher.rb', line 181

def command_configs
  config.fetch("commands", []).map do |command_config|
    validate_command_config!(command_config)
    command_config
  end
end

#create_threaded_fsevent(watch_path, options, &action_block) ⇒ Object

Watch a given path with speified options and call action_block when a non-ignored path is modified. This also ensures that only one action_block is running at a time, killing any other running blocks before starting a new one.



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/aem_lookout/watcher.rb', line 56

def create_threaded_fsevent(watch_path, options, &action_block)
  fsevent = FSEvent.new
  running_jobs = Set.new

  fsevent.watch watch_path, options do |paths|
    paths.delete_if {|path| ignored?(path) }
    log.warn "Detected change inside: #{paths.inspect}" unless paths.empty?

    if running_jobs.length > 0
      log.warn "A job is currently running for this watcher, killing..."
      running_jobs.each {|thread| thread.kill }
    else
      log.warn "Phew, no running jobs: #{running_jobs}"
    end

    job = Thread.new do
      action_block.call(paths)
      Thread.exit
    end

    track_job_on_list(job, running_jobs)
  end

  fsevent
end

#discover_jcr_path_from_file_in_vault_package(filesystem_path) ⇒ Object

Find the root of the package to determine the path used for the filter



215
216
217
218
219
220
221
222
223
# File 'lib/aem_lookout/watcher.rb', line 215

def discover_jcr_path_from_file_in_vault_package(filesystem_path)
  possible_jcr_root = Pathname(filesystem_path).parent
  while !possible_jcr_root.root?
    break if possible_jcr_root.basename.to_s == "jcr_root"
    possible_jcr_root = possible_jcr_root.parent
  end

  filesystem_path.gsub(/^#{possible_jcr_root.to_s}/, "")
end

#handle_sling_initial_content_change(paths, filesystem_path, jcr_path) ⇒ Object



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/aem_lookout/watcher.rb', line 134

def handle_sling_initial_content_change(paths, filesystem_path, jcr_path)
  paths.each do |path|
    if !File.exist?(path)
      log.info "#{path} no longer exists, syncing parent instead"
      path = File.dirname(path)
    end

    relative_jcr_path = path.gsub(/^.+#{filesystem_path}\//, "")

    AemLookout::Sync.new(
      hostnames: hostnames,
      filesystem: path,
      jcr: (Pathname(jcr_path) + relative_jcr_path).to_s,
      log: log,
      sling_initial_content: true
    ).run
  end
end

#hostnamesObject



202
203
204
# File 'lib/aem_lookout/watcher.rb', line 202

def hostnames
  config.fetch("instances")
end

#ignored?(file) ⇒ Boolean

Return true if file should not trigger a sync

Returns:

  • (Boolean)


207
208
209
210
211
212
# File 'lib/aem_lookout/watcher.rb', line 207

def ignored?(file)
  return true if File.extname(file) == ".tmp"
  return true if file.match(/___$/)
  return true if File.basename(file) == ".DS_Store"
  return false
end

#jcr_root_pathsObject



169
170
171
# File 'lib/aem_lookout/watcher.rb', line 169

def jcr_root_paths
  config.fetch("jcrRootPaths", []).map {|jcr_root_path| Pathname(repo_path) + jcr_root_path }.map(&:to_s)
end

#runObject



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/aem_lookout/watcher.rb', line 17

def run
  threads = jcr_root_paths.map do |jcr_root_paths|
    Thread.new { watch_vault_package(jcr_root_paths) }
  end

  threads += sling_initial_content_paths.map do |sling_initial_content_path|
    Thread.new { watch_sling_initial_content(sling_initial_content_path) }
  end

  threads += command_configs.map do |command_config|
    Thread.new { watch_command_config(command_config) }
  end

  wait_for(threads)
end

#sling_initial_content_pathsObject



173
174
175
176
177
178
# File 'lib/aem_lookout/watcher.rb', line 173

def sling_initial_content_paths
  config.fetch("slingInitialContentPaths", []).map do |sling_initial_content_path|
    validate_sling_initial_content_path!(sling_initial_content_path)
    sling_initial_content_path
  end
end

#sync_vault_package_paths(paths) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/aem_lookout/watcher.rb', line 94

def sync_vault_package_paths(paths)
  paths.each do |path|
    if !File.exist?(path)
      log.warn "#{path} no longer exists, syncing parent instead"
      path = File.dirname(path)
    end

    jcr_path = discover_jcr_path_from_file_in_vault_package(path)

    AemLookout::Sync.new(
      hostnames: hostnames,
      filesystem: path,
      jcr: jcr_path,
      log: log
    ).run
  end
end

#track_job_on_list(job, running_jobs) ⇒ Object

Adds to running job list and removes from list when thread completes.



83
84
85
86
87
88
89
90
91
92
# File 'lib/aem_lookout/watcher.rb', line 83

def track_job_on_list(job, running_jobs)
  Thread.new do
    running_jobs << job
    log.warn "Waiting for #{job} to finish"
    wait_for(job)
    log.warn "#{job} job finished"
    running_jobs.delete(job)
    Thread.exit
  end
end

#validate_command_config!(command_config) ⇒ Object

Ensures required keys are present. This is ugly.



189
190
191
192
193
194
# File 'lib/aem_lookout/watcher.rb', line 189

def validate_command_config!(command_config)
  required_keys = ["watch", "command"]
  unless command_config.has_key?(required_keys.first) and command_config.has_key?(required_keys.last)
    raise "commands entry is malformed (requires these keys: #{required_keys.join(", ")}): #{command_config.inspect}"
  end
end

#validate_sling_initial_content_path!(path) ⇒ Object



196
197
198
199
200
# File 'lib/aem_lookout/watcher.rb', line 196

def validate_sling_initial_content_path!(path)
  unless path.has_key?("filesystem") and path.has_key?("jcr")
    raise "slingInitialContentPaths entry is malformed (requires \"filesystem\" and \"jcr\" entry): #{path.inspect}"
  end
end

#wait_for(threads) ⇒ Object



33
34
35
# File 'lib/aem_lookout/watcher.rb', line 33

def wait_for(threads)
  sleep 1 until Array(threads).map {|thread| thread.join(1) }.all? {|result| result }
end

#watch_command_config(command_config) ⇒ Object



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/aem_lookout/watcher.rb', line 153

def watch_command_config(command_config)
  watch_path = command_config.fetch("watch")
  pwd = Pathname(repo_path) + command_config.fetch("pwd", "")
  command = command_config.fetch("command")

  options = {:latency => 1, :file_events => true}
  fsevent = create_threaded_fsevent watch_path, options do |paths|
    break if paths.empty?
    log.info "Running command"
    Terminal.new(log).execute_command("cd #{pwd} && #{command}")
  end

  log.info "Watching #{watch_path}, changes will run #{command.inspect}..."
  fsevent.run
end

#watch_sling_initial_content(path) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/aem_lookout/watcher.rb', line 112

def watch_sling_initial_content(path)
  filesystem_path = path.fetch("filesystem")
  jcr_path = path.fetch("jcr")

  if !File.exist?(filesystem_path)
    log.warn "Filesystem path for Sling-Initial-Content points to non-existing directory: #{filesystem_path}"
    return
  end

  options = {:latency => 0.1, :file_events => true}
  fsevent = create_threaded_fsevent filesystem_path.to_s, options do |paths|
    begin
      handle_sling_initial_content_change(paths, filesystem_path, jcr_path)
    rescue LookoutError => e
      log.error "An error occurred while handling sling initial content change: #{e.message}"
    end
  end

  log.info "Watching Sling-Initial-Content at #{filesystem_path} for changes..."
  fsevent.run
end

#watch_vault_package(jcr_root) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/aem_lookout/watcher.rb', line 37

def watch_vault_package(jcr_root)
  if !File.exist?(jcr_root)
    log.warn "jcr_root points to non-existing directory: #{jcr_root}"
    return
  end

  options = {:latency => 0.1, :file_events => true}
  fsevent = create_threaded_fsevent jcr_root.to_s, options do |paths|
    sync_vault_package_paths(paths)
  end

  log.info "Watching jcr_root at #{jcr_root} for changes..."
  fsevent.run
end