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.



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

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



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

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



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

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



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

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



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

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



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

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



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

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

#expand_erb(file, config) ⇒ Object



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

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.



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

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
238
239
240
241
242
# 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
  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



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