Class: Warbler::Jar

Inherits:
Object
  • Object
show all
Includes:
PathmapHelper, PlatformHelper, RakeHelper
Defined in:
lib/warbler/jar.rb

Overview

Class that holds the files that will be stored in the jar file. The #files attribute contains a hash of pathnames inside the jar file to their contents. Contents can be one of:

  • nil representing a directory entry
  • Any object responding to read representing an in-memory blob
  • A String filename pointing to a file on disk

Constant Summary collapse

DEFAULT_MANIFEST =
%{Manifest-Version: 1.0\nCreated-By: Warbler #{Warbler::VERSION}\n\n}
DEFAULT_COMPILED_FILES_SLICE =
2500

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from PlatformHelper

#which

Methods included from RakeHelper

extended, included

Methods included from PathmapHelper

#apply_pathmaps

Constructor Details

#initializeJar

Returns a new instance of Jar.



29
30
31
# File 'lib/warbler/jar.rb', line 29

def initialize
  @files = {}
end

Instance Attribute Details

#app_filelistObject (readonly)

Returns the value of attribute app_filelist.



27
28
29
# File 'lib/warbler/jar.rb', line 27

def app_filelist
  @app_filelist
end

#filesObject (readonly)

Returns the value of attribute files.



26
27
28
# File 'lib/warbler/jar.rb', line 26

def files
  @files
end

Instance Method Details

#add_init_file(config) ⇒ Object

Add init.rb file to the war file.



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/warbler/jar.rb', line 259

def add_init_file(config)
  if config.init_contents
    contents = ''
    config.init_contents.each do |file|
      if file.respond_to?(:read)
        contents << file.read
      elsif File.extname(file) == '.erb'
        contents << expand_erb(file, config).read
      else
        contents << File.read(file)
      end
    end
    @files[config.init_filename] = StringIO.new(contents)
  end
end

#add_manifest(config = nil) ⇒ Object

Add a manifest file either from config or by making a default manifest.



184
185
186
187
188
189
190
191
192
# File 'lib/warbler/jar.rb', line 184

def add_manifest(config = nil)
  unless @files.keys.detect{ |k| k =~ /^META-INF\/MANIFEST\.MF$/i }
    if config && config.manifest_file
      @files['META-INF/MANIFEST.MF'] = config.manifest_file
    else
      @files['META-INF/MANIFEST.MF'] = StringIO.new(DEFAULT_MANIFEST)
    end
  end
end

#add_script_files(config) ⇒ Object



275
276
277
278
279
# File 'lib/warbler/jar.rb', line 275

def add_script_files(config)
  config.script_files.each do |file|
    @files["META-INF/#{File.basename(file)}"] = StringIO.new(File.read(file))
  end
end

#add_with_pathmaps(config, f, map_type) ⇒ Object



281
282
283
# File 'lib/warbler/jar.rb', line 281

def add_with_pathmaps(config, f, map_type)
  @files[apply_pathmaps(config, f, map_type)] = f
end

#apply(config) ⇒ Object

Apply the information in a Warbler::Config object in order to look for files to put into this war file.



149
150
151
152
153
154
155
156
157
158
# File 'lib/warbler/jar.rb', line 149

def apply(config)
  find_application_files(config)
  find_java_libs(config)
  find_java_classes(config)
  find_gems_files(config)
  add_manifest(config)
  add_init_file(config)
  add_script_files(config)
  apply_traits(config)
end

#apply_traits(config) ⇒ Object

Invoke a hook to allow the project traits to add or modify the archive contents.



178
179
180
181
# File 'lib/warbler/jar.rb', line 178

def apply_traits(config)
  puts "Applying traits #{config.traits}" unless silent?
  config.update_archive(self)
end

#compile(config) ⇒ Object



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/warbler/jar.rb', line 38

def compile(config)
  find_gems_files(config)
  # Compiling all Ruby files we can find -- do we need to allow an
  # option to configure what gets compiled?
  return if (config.compiled_ruby_files.nil? || config.compiled_ruby_files.empty?) && files.empty?

  if config.compile_gems
    ruby_files = gather_all_rb_files(config)
    run_jrubyc(config, ruby_files.values)
    replace_compiled_ruby_files_and_gems(config, ruby_files)
  else
    compiled_ruby_files = config.compiled_ruby_files - config.excludes.to_a
    run_jrubyc(config, compiled_ruby_files)
    replace_compiled_ruby_files(config, compiled_ruby_files)
  end
end

#contents(entry) ⇒ Object



33
34
35
36
# File 'lib/warbler/jar.rb', line 33

def contents(entry)
  file = files[entry]
  file.respond_to?(:read) ? file.read : File.read(file)
end

#create(config_or_path) ⇒ Object

Create the jar or war file. The single argument can either be a Warbler::Config or a filename of the file to create.



162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/warbler/jar.rb', line 162

def create(config_or_path)
  path = config_or_path
  if Warbler::Config === config_or_path
    path = "#{config_or_path.jar_name}.#{config_or_path.jar_extension}"
    path = File.join(config_or_path.autodeploy_dir, path) if config_or_path.autodeploy_dir
  end
  rm_f path
  ensure_directory_entries
  if Warbler::Config === config_or_path
    @files.delete("#{config_or_path.jar_name}/#{path}")
  end
  puts "Creating #{path}" unless silent?
  create_jar path, @files
end

#create_jar(jar_path, entries) ⇒ Object



306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/warbler/jar.rb', line 306

def create_jar(jar_path, entries)
  ZipSupport.create(jar_path) do |zipfile|
    entries.keys.sort.each do |entry|
      src = entries[entry]
      if src.respond_to?(:read)
        zipfile.get_output_stream(entry) { |f| f << src.read }
      elsif src.nil? || File.directory?(src)
        if File.symlink?(entry) && ! defined?(JRUBY_VERSION)
          warn "directory symlinks are not followed unless using JRuby; " +
               "#{entry.inspect} contents not in archive"
        end
        zipfile.mkdir(entry.dup) # in case it's frozen rubyzip 0.9.6.1 workaround
      elsif File.symlink?(src)
        zipfile.get_output_stream(entry) { |f| f << File.read(src) }
      elsif File.exist?(src)
        zipfile.add(entry, src)
      else
        warn "file not found; #{entry.inspect} not in archive"
      end
    end
  end
end

#ensure_directory_entriesObject



296
297
298
299
300
301
302
303
304
# File 'lib/warbler/jar.rb', line 296

def ensure_directory_entries
  files.select {|k,v| !v.nil? }.each do |k,v|
    dir = File.dirname(k)
    while dir != "." && !files.has_key?(dir)
      files[dir] = nil
      dir = File.dirname(dir)
    end
  end
end

#entry_in_jar(jar, entry) ⇒ Object



329
330
331
332
333
# File 'lib/warbler/jar.rb', line 329

def entry_in_jar(jar, entry)
  ZipSupport.open(jar) do |zf|
    zf.get_input_stream(entry) {|io| StringIO.new(io.read) }
  end
end

#erb_binding(config) ⇒ Object



291
292
293
294
# File 'lib/warbler/jar.rb', line 291

def erb_binding(config)
  webxml = config.webxml
  binding
end

#expand_erb(file, config) ⇒ Object



285
286
287
288
289
# File 'lib/warbler/jar.rb', line 285

def expand_erb(file, config)
  require 'erb'
  erb = ERB.new(File.read(file), trim_mode: '-')
  StringIO.new(erb.result(erb_binding(config)))
end

#find_application_files(config) ⇒ Object

Add all application directories and files to the archive.



244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/warbler/jar.rb', line 244

def find_application_files(config)
  config.dirs.select do |d|
    exists = File.directory?(d)
    warn "application directory `#{d}' does not exist or is not a directory; skipping" unless exists
    exists
  end.each do |d|
    @files[apply_pathmaps(config, d, :application)] = nil
  end
  @app_filelist = FileList[*(config.dirs.map{|d| %W{#{d}/**/*/**/* #{d}/*}}.flatten)]
  @app_filelist.include( *(config.includes.to_a) )
  @app_filelist.exclude( *(config.excludes.to_a) )
  @app_filelist.map {|f| add_with_pathmaps(config, f, :application) }
end

#find_gems_files(config) ⇒ Object

Add gems to WEB-INF/gems



205
206
207
208
209
# File 'lib/warbler/jar.rb', line 205

def find_gems_files(config)
  unless @compiled && config.compile_gems
    config.gems.specs(config.gem_dependencies).each {|spec| find_single_gem_files(config, spec) }
  end
end

#find_java_classes(config) ⇒ Object

Add java classes to WEB-INF/classes.



200
201
202
# File 'lib/warbler/jar.rb', line 200

def find_java_classes(config)
  config.java_classes.map { |f| add_with_pathmaps(config, f, :java_classes) }
end

#find_java_libs(config) ⇒ Object

Add java libraries to WEB-INF/lib.



195
196
197
# File 'lib/warbler/jar.rb', line 195

def find_java_libs(config)
  config.java_libs.map { |lib| add_with_pathmaps(config, lib, :java_libs) }
end

#find_single_gem_files(config, spec) ⇒ Object

Add a single gem to WEB-INF/gems



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/warbler/jar.rb', line 212

def find_single_gem_files(config, spec)
  full_gem_path = Pathname.new(spec.full_gem_path)

  # skip gems whose full_gem_path does not exist
  unless full_gem_path.exist?
    # its very likely that its a default gem e.g. json/jruby-openssl :
    if (Gem.default_dir rescue nil) && full_gem_path.to_s.start_with?(Gem.default_dir)
      # OK if the gem does not exists as its un-packed on the "shared" path
      # ... at least gem spec.spec_file should exists although not crucial
      if JRUBY_VERSION != JRubyJars::VERSION
        warn "skipping #{spec.name} default gem (assuming its part of jruby-jars #{JRubyJars::VERSION})" unless silent?
      end
    else
      warn "skipping #{spec.name} gem (#{full_gem_path.to_s} does not exist)"
    end
    return
  end

  @files[apply_pathmaps(config, "#{spec.full_name}.gemspec", :gemspecs)] = StringIO.new(spec.to_ruby)
  FileList["#{full_gem_path.to_s}/**/*"].each do |src|
    f = Pathname.new(src).relative_path_from(full_gem_path).to_s
    next if config.gem_excludes && config.gem_excludes.any? {|rx| f =~ rx }
    @files[apply_pathmaps(config, File.join(spec.full_name, f), :gems)] = src
  end
  if File.exist?(spec.gem_build_complete_path)
    base_dir = Pathname.new(spec.base_dir)
    gem_build_complete_path = Pathname.new(spec.gem_build_complete_path)
    @files[File.join(config.relative_gem_path, gem_build_complete_path.relative_path_from(base_dir))] = spec.gem_build_complete_path
  end
end

#gather_all_rb_files(config) ⇒ Object



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/warbler/jar.rb', line 123

def gather_all_rb_files(config)
  FileUtils.mkdir_p('tmp')
  # Gather all the files in the files list and copy them to the tmp directory
  files_to_compile = files.select { |_, f| !f.is_a?(StringIO) && f.end_with?('.rb') }
  files_to_compile.each do |jar_file, rb|
    FileUtils.mkdir_p(File.dirname(File.join('tmp', jar_file)))
    new_rb = File.join('tmp', jar_file)
    FileUtils.copy(rb, new_rb)
    files_to_compile[jar_file] = new_rb
  end
  # Gather all the application files which the user wrote (not dependencies)
  main_files_to_compile = config.compiled_ruby_files - config.excludes.to_a
  main_files_to_compile.each do |f|
    FileUtils.mkdir_p(File.dirname(File.join('tmp', f)))
    FileUtils.copy(f, File.join('tmp', f))
  end
  main_files_to_compile = main_files_to_compile.inject({}) {|h,f| h.merge!(f => f) }
  files.keys.each do |k|
    # Update files list to point to the temporary file
    files[k] = files_to_compile[k] || main_files_to_compile[k] || files[k]
  end
  main_files_to_compile.merge(files_to_compile)
end

#java_version(config) ⇒ Object



88
89
90
# File 'lib/warbler/jar.rb', line 88

def java_version(config)
  config.bytecode_version ? "-Djruby.bytecode.version=#{config.bytecode_version}" : ''
end

#replace_compiled_ruby_files(config, compiled_ruby_files) ⇒ Object



92
93
94
95
96
97
98
99
100
# File 'lib/warbler/jar.rb', line 92

def replace_compiled_ruby_files(config, compiled_ruby_files)
  # Exclude the rb files and recreate them. This
  # prevents the original contents being used.
  config.excludes += compiled_ruby_files

  compiled_ruby_files.each do |ruby_source|
    files[apply_pathmaps(config, ruby_source, :application)] = StringIO.new("load __FILE__.sub(/\.rb$/, '.class')")
  end
end

#replace_compiled_ruby_files_and_gems(config, compiled_ruby_files) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/warbler/jar.rb', line 102

def replace_compiled_ruby_files_and_gems(config, compiled_ruby_files)
  # Exclude the rb files and recreate them. This
  # prevents the original contents being used.
  config.excludes += compiled_ruby_files.keys

  compiled_ruby_files.each do |inside_jar, file_system_location|
    # The gems are already inside the gems folder inside the jar, however when using the :gems pathmap, they will
    # get put into the gems/gems folder, to prevent this we chop off the first gems folder directory
    inside_jar = inside_jar.dup
    if inside_jar.split(File::SEPARATOR).first == 'gems'
      inside_jar = inside_jar.split(File::SEPARATOR)[1..-1].join(File::SEPARATOR)
      pathmap = :gems
    else
      pathmap = :application
    end
    files[apply_pathmaps(config, inside_jar, pathmap)] = StringIO.new("load __FILE__.sub(/\.rb$/, '.class')")
    files[apply_pathmaps(config, inside_jar.sub(/\.rb$/, '.class'), pathmap)] = file_system_location.sub(/\.rb$/, '.class')
  end
end

#run_jrubyc(config, compiled_ruby_files) ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/warbler/jar.rb', line 57

def run_jrubyc(config, compiled_ruby_files)
  slice_size = (ENV['WARBLER_COMPILED_FILES_SLICE'] || 0).to_i
  slice_size = DEFAULT_COMPILED_FILES_SLICE if slice_size <= 0
  compiled_ruby_files.each_slice(slice_size) do |files|
    files = "\"#{files.join('" "')}\""
    classpath = config.java_libs.map { |lib| "\"#{lib.gsub('"', '\\"')}\"" }.join(File::PATH_SEPARATOR)
    # Need to use the version of JRuby in the application to compile it
    javac_cmd = %Q{java -classpath #{classpath} #{java_version(config)} org.jruby.Main -S jrubyc #{jrubyc_options(config)} #{files}}
    if which('java').nil? && which('env')
      sh_jrubyc %Q{env -i #{javac_cmd}}
    else
      sh_jrubyc javac_cmd
    end
  end
  @compiled = true
end