blob: 2246dfbe1efc3f5800ce5d470652a896d5b42d7e [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with this
# work for additional information regarding copyright ownership. The ASF
# licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
require 'uri'
require 'net/http'
require 'thread'
module Buildr
# Provides a collection of tasks and methods for using Jetty, specifically as a server
# for testing your application.
#
# Build files should always start Jetty by invoking the #use task, typically as
# a prerequisite. This task will start Jetty once during the build, and shut it down
# when the build completes.
#
# If you want to keep Jetty running across builds, and look at error messages, you can
# start Jetty in a separate console with:
# buildr jetty:start
# To stop this instance of Jetty, simply kill the process (Ctrl-C) or run:
# buildr jetty:stop
#
# If you start Jetty separately from the build, the #use task will connect to that
# existing server. Since you are using Jetty across several builds, you will want to
# cleanup any mess created by each build. You can use the #setup and #teardown tasks,
# which are called when Jetty is first used in the build, and when the build ends.
class Jetty
# Which version of Jetty we're using by default (change with options.jetty.version).
VERSION = '9.4.6.v20170531'
SLF4J_VERSION = '1.7.25'
# Libraries used by Jetty.
REQUIRES = [ "org.eclipse.jetty:jetty-server:jar:#{VERSION}",
"org.eclipse.jetty:jetty-webapp:jar:#{VERSION}",
"org.eclipse.jetty:jetty-http:jar:#{VERSION}",
"org.eclipse.jetty:jetty-util:jar:#{VERSION}",
"org.eclipse.jetty:jetty-io:jar:#{VERSION}",
"org.eclipse.jetty:jetty-servlet:jar:#{VERSION}",
"org.eclipse.jetty:jetty-security:jar:#{VERSION}",
"org.eclipse.jetty:jetty-xml:jar:#{VERSION}",
"org.slf4j:slf4j-api:jar:#{SLF4J_VERSION}",
"org.slf4j:slf4j-simple:jar:#{SLF4J_VERSION}",
"org.slf4j:jcl-over-slf4j:jar:#{SLF4J_VERSION}",
'javax.servlet:javax.servlet-api:jar:3.1.0' ]
Java.classpath << REQUIRES
Java.classpath << File.dirname(__FILE__)
# Default URL for Jetty (change with options.jetty.url).
URL = "http://localhost:8080"
class << self
# :call-seq:
# instance() => Jetty
#
# Returns an instance of Jetty.
def instance()
@instance ||= Jetty.new("jetty", URL)
end
end
def initialize(name, url) #:nodoc:
@url = url
namespace name do
@setup = task("setup")
@teardown = task("teardown")
@use = task("use") { fire }
end
end
# The URL for the Jetty server. Leave as is if you want to use the default server
# (http://localhost:8080).
attr_accessor :url
# :call-seq:
# start(pipe?)
#
# Starts Jetty. This method does not return, it keeps the thread running until
# Jetty is stopped. If you want to run Jetty parallel with other tasks in the build,
# invoke the #use task instead.
def start(sync = nil)
begin
puts "classpath #{Java.classpath.inspect}"
port = URI.parse(url).port
puts "Starting Jetty at http://localhost:#{port}" if verbose
Java.load
jetty = Java.org.apache.buildr.JettyWrapper.new(port)
sync << "Started" if sync
sleep # Forever
rescue Interrupt # Stopped from console
rescue Exception=>error
puts "#{error.class}: #{error.message}"
end
exit! # No at_exit
end
# :call-seq:
# stop()
#
# Stops Jetty. Stops a server running in a separate process.
def stop()
uri = URI.parse(url)
begin
Net::HTTP.start(uri.host, uri.port) do |http|
http.request_post "/buildr/stop", ""
end
rescue Errno::ECONNREFUSED
# Expected if Jetty server not running.
rescue EOFError
# We get EOFError because Jetty is brutally killed.
end
puts "Jetty server stopped"
end
# :call-seq:
# running?() => boolean
#
# Returns true if it finds a running Jetty server that supports the Buildr
# requests for deploying, stopping, etc.
def running?()
uri = URI.parse(url)
begin
Net::HTTP.start(uri.host, uri.port) do |http|
response = http.request_get("/buildr/")
response.is_a?(Net::HTTPSuccess) && response.body =~ /Alive/
end
rescue Errno::ECONNREFUSED, Errno::EBADF
false
end
end
# :call-seq:
# deploy(url, webapp) => path
#
# Deploy a WAR in the specified URL.
def deploy(url, webapp)
use.invoke
uri = URI.parse(url)
Net::HTTP.start(uri.host, uri.port) do |http|
response = http.request_post("/buildr/deploy", "webapp=#{webapp}&path=#{uri.path}")
if Net::HTTPOK === response && response.body =~ /Deployed/
path = response.body.split[1]
puts "Deployed #{webapp}, context path #{uri.path}" if trace?
path
else
fail "Deployment failed: #{response}"
end
end
end
# :call-seq:
# undeploy(url) => boolean
#
# Undeploys a WAR from the specified URL.
def undeploy(url)
use.invoke
uri = URI.parse(url)
Net::HTTP.start(uri.host, uri.port) do |http|
response = http.request_post("/buildr/undeploy", "path=#{uri.path}")
if Net::HTTPOK === response && response.body =~ /Undeployed/
true
else
fail "Deployment failed: #{response}"
end
end
end
# :call-seq:
# setup(*prereqs) => task
# setup(*prereqs) { |task| .. } => task
#
# This task executes when Jetty is first used in the build. You can use it to
# deploy artifacts into Jetty.
def setup(*prereqs, &block)
@setup.enhance prereqs, &block
end
# :call-seq:
# teardown(*prereqs) => task
# teardown(*prereqs) { |task| .. } => task
#
# This task executes when the build is done. You can use it to undeploy artifacts
# previously deployed into Jetty.
def teardown(*prereqs, &block)
@teardown.enhance prereqs, &block
end
# :call-seq:
# use(*prereqs) => task
# use(*prereqs) { |task| .. } => task
#
# If you intend to use Jetty, invoke this task. It will start a new instance of
# Jetty and close it when the build is done. However, if you already have a server
# running in the background (e.g. jetty:start), it will use that server and will
# not close it down.
def use(*prereqs, &block)
@use.enhance prereqs, &block
end
protected
# If you want to start Jetty inside the build, call this method instead of #start.
# It will spawn a separate process that will run Jetty, and will stop Jetty when
# the build ends. However, if you already started Jetty from the console (with
# take jetty:start), it will use the existing instance without shutting it down.
def fire()
unless running?
sync = Queue.new
Thread.new { start sync }
# Wait for Jetty to fire up before doing anything else.
sync.pop == "Started" or fail "Jetty not started"
puts "Jetty started" if verbose
at_exit { stop }
end
@setup.invoke
at_exit { @teardown.invoke }
end
end
namespace "jetty" do
desc "Start an instance of Jetty running in the background"
task("start") { Jetty.instance.start }
desc "Stop an instance of Jetty running in the background"
task("stop") { Jetty.instance.stop }
end
# :call-seq:
# jetty() => Jetty
#
# Returns a Jetty object. You can use this to discover the Jetty#use task,
# configure the Jetty#setup and Jetty#teardown tasks, deploy and undeploy to Jetty.
def jetty()
@jetty ||= Jetty.instance
end
end