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

Direct Known Subclasses

War

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.



255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/warbler/jar.rb', line 255

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.



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

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



271
272
273
274
275
# File 'lib/warbler/jar.rb', line 271

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



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

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.



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

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.



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

def apply_traits(config)
  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.



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

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



302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
# File 'lib/warbler/jar.rb', line 302

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



292
293
294
295
296
297
298
299
300
# File 'lib/warbler/jar.rb', line 292

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



325
326
327
328
329
# File 'lib/warbler/jar.rb', line 325

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



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

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

#expand_erb(file, config) ⇒ Object



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

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

#find_application_files(config) ⇒ Object

Add all application directories and files to the archive.



240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/warbler/jar.rb', line 240

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



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

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.



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

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.



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

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



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
# File 'lib/warbler/jar.rb', line 213

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
end

#gather_all_rb_files(config) ⇒ Object



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

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



90
91
92
# File 'lib/warbler/jar.rb', line 90

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

#replace_compiled_ruby_files(config, compiled_ruby_files) ⇒ Object



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

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



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

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 Also known as: run_javac



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