Merge branch 'master' into release-9

This commit is contained in:
Jesse McConnell 2012-11-19 17:42:54 -06:00
commit 36a5b50b65
574 changed files with 18857 additions and 10020 deletions

View File

@ -1,6 +1,6 @@
jetty-9.0.0-SNAPSHOT
jetty-9.0.0.M2 - 05 November 2012
jetty-9.0.0.M2 - 06 November 2012
+ 371170 MongoSessionManager LastAccessTimeTest fails
+ 391877 org.eclipse.jetty.webapp.FragmentDescriptor incorrectly reporting
duplicate others for after ordering
@ -24,9 +24,86 @@ jetty-9.0.0.M2 - 05 November 2012
+ 393368 min websocket version
+ 393383 delay onClose call until closeOut is done
+ 393494 HashSessionManager can't delete unrestorable sessions on Windows
+ JETTY-1547 Jetty does not honor web.xml
web-app/jsp-config/jsp-property-group/default-content-type
+ JETTY-1549 jetty-maven-plugin fails to reload the LoginService properly
+ JETTY-1550 virtual WEB-INF not created if project has overlays
jetty-8.1.8.v20121106 - 06 November 2012
+ 371170 MongoSessionManager LastAccessTimeTest fails
+ 388675 Non utf8 encoded query strings not decoded to parameter map using
queryEncoding
+ 388706 Avoid unnecessary indirection through Charset.name
+ 389390 AnnotationConfiguration is ignored if the metadata-complete attribute
is present in an override descriptor regardless of the value
+ 389452 if web-fragment metadata-complete==true still scan its related jar if
there there is a ServletContainerInitializer, ensure webapp restarts work
+ 389686 Fix reference to org.eclipse.jetty.util.log.stderr.LONG system
property in javadoc for StdErrLog
+ 389956 Bad __context set in WebAppContext.start sequence with respect to ENC
setup
+ 389965 OPTIONS should allow spaces in comma separated list
+ 390108 Servlet 3.0 API for programmatic login doesn't appear to work
+ 390161 Apply DeferredAuthentication fix to jaspi
+ 390163 Implement ServletRegistration.Dynamic.setServletSecurity
+ 390503 http-method-omission element not being processed
+ 390560 The method AnnotationParser.getAnnotationHandlers(String) always
returns a empty collection.
+ 391080 Multipart temp files can be left on disk from Request.getPart and
getParts
+ 391082 No exception if multipart input stream incomplete
+ 391188 Files written with Request.getPart().write(filename) should not be
auto-deleted
+ 391483 fix bad javadoc example in shutdown handler
+ 391622 Be lenient on RFC6265 restriction on duplicate cookie names in same
response
+ 391623 Add option to --stop to wait for target jetty to stop
+ 391877 org.eclipse.jetty.webapp.FragmentDescriptor incorrectly reporting
duplicate others for after ordering
+ 392239 Allow no error-code or exception for error-pages
+ 392525 Add option to --stop-wait to specify timeout
+ 392641 JDBC Sessions not scavenged if expired during downtime
+ 392812 MongoSessionIDManager never purges old sessions
+ 393014 Mongodb purgevalid using query for purgeinvalid
+ 393015 Mongodb purge not rescheduled
+ 393075 Jetty WebSocket client cannot connect to Tomcat WebSocket Server
+ 393218 add xsd=application/xml mime mapping to defaults
+ 393363 Use Locale.ENGLISH for all toUpperCase and toLowerCase calls
+ 393368 min websocket version
+ 393383 delay onClose call until closeOut is done
+ 393494 HashSessionManager can't delete unrestorable sessions on Windows
+ JETTY-1547 Jetty does not honor web.xml
web-app/jsp-config/jsp-property-group/default-content-type
jetty-7.6.8.v20121106 - 06 November 2012
+ 371170 MongoSessionManager LastAccessTimeTest fails
+ 388675 Non utf8 encoded query strings not decoded to parameter map using
queryEncoding
+ 389686 Fix reference to org.eclipse.jetty.util.log.stderr.LONG system
property in javadoc for StdErrLog
+ 389956 Bad __context set in WebAppContext.start sequence with respect to ENC
setup
+ 389965 OPTIONS should allow spaces in comma separated list
+ 390161 Apply DeferredAuthentication fix to jaspi
+ 390560 The method AnnotationParser.getAnnotationHandlers(String) always
returns a empty collection.
+ 391483 fix bad javadoc example in shutdown handler
+ 391622 Be lenient on RFC6265 restriction on duplicate cookie names in same
response
+ 391623 Add option to --stop to wait for target jetty to stop
+ 392239 Allow no error-code or exception for error-pages
+ 392525 Add option to --stop-wait to specify timeout
+ 392641 JDBC Sessions not scavenged if expired during downtime
+ 392812 MongoSessionIDManager never purges old sessions
+ 393014 Mongodb purgevalid using query for purgeinvalid
+ 393015 Mongodb purge not rescheduled
+ 393075 Jetty WebSocket client cannot connect to Tomcat WebSocket Server
+ 393218 add xsd=application/xml mime mapping to defaults
+ 393363 Use Locale.ENGLISH for all toUpperCase and toLowerCase calls
+ 393368 min websocket version
+ 393383 delay onClose call until closeOut is done
+ 393494 HashSessionManager can't delete unrestorable sessions on Windows
jetty-9.0.0.M1 - 15 October 2012
+ 369349 directory with spaces --dry-run fix
+ 385049 fix issue with pipelined connections when switching protocols
@ -95,6 +172,10 @@ jetty-8.1.7.v20120910 - 10 September 2012
+ 388895 Update dependencies for jetty-jndi
+ fix busy logging statement re: sessions
jetty-7.6.7.v20120910 - 10 September 2012
+ 388895 Update dependencies for jetty-jndi
+ fix busy logging statement re: sessions
jetty-8.1.6.v20120903 - 03 September 2012
+ 347130 Empty getResourcePaths due to ZipFileClosedException
+ 367591 Support Env variables in XmlConfiguration.
@ -128,6 +209,39 @@ jetty-8.1.6.v20120903 - 03 September 2012
+ JETTY-1532 HTTP headers decoded with platform's default encoding
+ JETTY-1541 fixed different behaviour for single byte writes
jetty-7.6.6.v20120903 - 03 September 2012
+ 347130 Empty getResourcePaths due to ZipFileClosedException
+ 367591 Support Env variables in XmlConfiguration.
+ 377055 Prevent webapp classloader leaks
+ 379207 backported fixes from jetty-9 to make hierarchy work
+ 379423 Jetty URL Decoding fails for certain international characters
+ 383304 Reset PrintWriter on response recycle
+ 384847 better name
+ 385049 fix issue with pipelined connections when switching protocols
+ 385651 Message 'Address already in use' not specific enough
+ 386010 JspRuntimeContext rewraps System.err
+ 386591 add UnixCrypt note to about.html
+ 386714 used deferred auth for form login and error pages
+ 387896 populate session in SessionAuthentication as a valueBound in addition
to activation so it is populate when needed
+ 387943 Catch CNFE when no jstl jars are installed
+ 387953 jstl does not work with jetty-7 in osgi
+ 388072 GZipFilter incorrectly gzips when Accept-Encoding: gzip; q=0
+ 388073 null session id from cookie causes NPE fixed
+ 388102 Jetty HttpClient memory leaks when sending larger files
+ 388393 WebAppProvider doesn't work alongside OSGi deployer
+ 388502 handle earlyEOF with 500
+ 388652 Do not flush on handle return if request is suspended
+ JETTY-1501 Setting custom error response message changes page title
+ JETTY-1515 Include cookies on 304 responses from DefaultServlet.
+ JETTY-1527 handle requests with URIs like http://host (ie no / )
+ JETTY-1529 Ensure new session that has just been authenticated does not get
renewed
+ JETTY-1532 HTTP headers decoded with platform's default encoding
+ JETTY-1541 fixed different behaviour for single byte writes
+ 385925: make SslContextFactory.setProtocols and
SslContextFactory.setCipherSuites preserve the order of the given parameters
jetty-8.1.5.v20120716 - 16 June 2012
+ 376717 Balancer Servlet with round robin support, contribution, added
missing license
@ -159,6 +273,32 @@ jetty-8.1.5.v20120716 - 16 June 2012
+ JETTY-1525 Show handle status in response debug message
+ JETTY-1530 refine search control on ldap login module
jetty-7.6.5.v20120716 - 16 July 2012
+ 376717 Balancer Servlet with round robin support, contribution, added
missing license
+ 379250 Server is added to shutdown hook twice
+ 380866 maxIdleTime set to 0 after session migration
+ 381399 Unable to stop a jetty instance that has not finished starting
+ 381401 Print log warning when stop attempt made with incorrect STOP.KEY
+ 381402 Make ContextHandler take set of protected directories
+ 381521 set Vary:Accept-Encoding header for content that might be compressed
+ 381639 CrossOriginFilter does not support Access-Control-Expose-Headers.
+ 381712 Support all declared servlets that implement
org.apache.jasper.servlet.JspServlet
+ 381825 leave URI params in forwarded requestURI
+ 381876 Monitor should wait for child to finish before exiting.
+ 382343 Jetty XML support for Map is broken.
+ 383251 500 for SocketExceptions
+ 383881 WebSocketHandler sets request as handled
+ 384254 revert change to writable when not dispatched
+ 384847 CrossOriginFilter is not working.
+ 384896 JDBCSessionManager fails to load existing sessions on oracle when
contextPath is /
+ 384980 Jetty client unable to recover from Time outs when connection count
per address hits max.
+ JETTY-1525 Show handle status in response debug message
+ JETTY-1530 refine search control on ldap login module
jetty-8.1.4.v20120524 - 24 May 2012
+ 367608 ignore the aysncrequestreadtest as it is known to fail and is waiting
for a fix
@ -192,6 +332,38 @@ jetty-8.1.4.v20120524 - 24 May 2012
+ 380212 Clear buffer if parsing fails due to full buffer
+ 380222 JettyPolicyRuntimeTest failure
jetty-7.6.4.v20120524 - 24 May 2012
+ 367608 ignore the aysncrequestreadtest as it is known to fail and is waiting
for a fix
+ 371853 Support bundleentry: protocol for webapp embedded as directory in
osgi bundle
+ 373620 Add ch.qos.logback.access.jetty to the Import-Package for
jetty-osgi-boot-logback bundle
+ 376152 apply context resources recursively
+ 376801 Make JAAS login modules useable without jetty infrastructure
+ 377391 Manifest updates to jetty-osgi-boot-logback
+ 377492 NPE when deploying a Web Application Bundle with unresolved
Require-TldBundle
+ 377550 set charset when content type is set
+ 377587 ConnectHandler write will block on partial write
+ 377610 New session not timed out if an old session is invalidated in scope
of same request
+ 377709 Support for RequestParameterCallback missing
+ 378242 Re-extract war on restart if incomplete extraction
+ 378273 Remove default Bundle-Localization header
+ 378487 Null out contextPath on Request.recycle
+ 379015 Use factored jetty xml config files for defaults
+ 379046 avoid closing idle connections from selector thread
+ 379089 DefaultServlet ignores its resourceBase and uses context's
ResourceCollection when listing diretories
+ 379194 ProxyServlet enhancement to enable easy creation of alternative
HttpClient implementations
+ 379909 FormAuthenticator Rembers only the URL of first Request before
authentication
+ 380034 last modified times taken from JarEntry for JarFile resources
+ 380212 Clear buffer if parsing fails due to full buffer
+ 380222 JettyPolicyRuntimeTest failure
jetty-8.1.3.v20120416 - 16 April 2012
+ 349110 MultiPartFilter records the content-type in request params
+ 367172 Remove detection for slf4j NOPLogger
@ -234,6 +406,38 @@ jetty-8.1.3.v20120416 - 16 April 2012
request.getParameter
+ JETTY-1504 HttpServletResponseWrapper ignored when using asyncContext?
jetty-7.6.3.v20120416 - 16 April 2012
+ 367172 Remove detection for slf4j NOPLogger
+ 373269 Make ServletHandler.notFound() method impl do nothing - override to
send back 404.
+ 373421 address potential race condition related to the nonce queue removing
the same nonce twice
+ 373952 bind called too frequently on refresh
+ 374018 correctly handle requestperminuted underflow
+ 374252 SslConnection.onClose() does not forward to nested connection.
+ 374258 SPDY leaks SSLEngines. Made the test more reliable.
+ 374367 NPE in QueuedThreadPool.dump() with early java6 jvms
+ 374475 Response.sendRedirect does not encode UTF-8 characters properly
+ 374881 Set copyWebInf to false by default
+ 374891 enhancement to how ProxyServlet determines the proxy target
+ 375009 Filter initialization error will throw MultiException
+ 375083 Flow control should take in account window size changes from
concurrent SETTINGS
+ 375096 If starting a server instance fails in osgi it is cleaned up.
+ 375490 NPE with --help on command line
+ 375509 Stalled stream stalls other streams or session control frames. Now
using a "death pill" instead of a boolean in order to avoid race conditions
where DataInfos were read from the queue (but the boolean not updated yet),
and viceversa.
+ 375594 fixed SSL tests so they are not order dependent
+ 375709 Ensure resolveTempDirectory failure does not deadlock; improve error
message
+ 375970 HttpServletRequest.getRemoteAddr() returns null when HTTP is over
SPDY.
+ 376201 HalfClosed state not handled properly. Addendum to restore previous
behavior, where a closed stream was also half closed.
+ JETTY-1504 HttpServletResponseWrapper ignored when using asyncContext?
jetty-8.1.2.v20120308 - 08 March 2012
+ 370387 SafariWebsocketDraft0Test failure during build.
+ 371168 Update ClientCrossContextSessionTest
@ -256,6 +460,27 @@ jetty-8.1.2.v20120308 - 08 March 2012
+ JETTY-1489 WebAppProvider attempts to deploy .svn folder
+ JETTY-1494 .
jetty-7.6.2.v20120308 - 08 March 2012
+ 370387 SafariWebsocketDraft0Test failure during build.
+ 371168 Update ClientCrossContextSessionTest
+ 372093 handle quotes in Require-Bundle manifest string
+ 372457 Big response + slow clients + pipelined requests cause Jetty spinning
and eventually closing connections. Added a TODO for a method renaming that
will happen in the next major release (to avoid break implementers).
+ 372487 JDBCSessionManager does not work with Oracle
+ 372806 Command line should accept relative paths for xml config files
+ 373037 jetty.server.Response.setContentLength(int) should not close a Writer
when length=0
+ 373162 add improved implementation for getParameterMap(), needs a test
though and the existing setup doesn't seem like it would easily support the
needed test so need to do that still
+ 373306 Set default user agent extraction pattern for UserAgentFilter
+ 373567 cert validation issue with ocsp and crldp always being enabled when
validating turned on fixed
+ JETTY-1409 GzipFilter will double-compress application/x-gzip content
+ JETTY-1489 WebAppProvider attempts to deploy .svn folder
+ JETTY-1494 .
jetty-8.1.1.v20120215 - 15 February 2012
+ 369121 simplified test
+ 370120 jvm arguments added via start.ini and --exec are missing spaces
@ -270,12 +495,32 @@ jetty-8.1.1.v20120215 - 15 February 2012
+ JETTY-1484 Add option for HashSessionManager to delete session files if it
can't restore them
jetty-7.6.1.v20120215 - 15 February 2012
+ 369121 simplified test
+ 370120 jvm arguments added via start.ini and --exec are missing spaces
+ 370137 SslContextFactory does not respect order for
[included|excluded]Protocols() and [included|excluded]CipherSuites().
+ 370368 resolve stack overflow in mongo db session manager
+ 370386 Remove META-INF from jetty distro
+ 371040 nosqlsession needs to call correct super contructor for new sessions
+ 371041 valid was not being set to new mongo db sessions, and the call to
mongodb api was wrong in isIdInUse
+ 371162 NPE protection for nested security handlers
+ JETTY-1484 Add option for HashSessionManager to delete session files if it
can't restore them
jetty-8.1.0.v20120127 - 27 January 2012
+ 368773 allow authentication to be set by non securityHandler handlers
+ 368992 avoid update key while flushing during a write
+ 369216 turned off the shared resource cache
+ 369349 replace quotes with a space escape method
jetty-7.6.0.v20120127 - 27 January 2012
+ 368773 allow authentication to be set by non securityHandler handlers
+ 368992 avoid update key while flushing during a write
+ 369216 turned off the shared resource cache
+ 369349 replace quotes with a space escape method
jetty-8.1.0.RC5 - 20 January 2012
+ 359329 Prevent reinvocation of LoginModule.login with jaspi for already
authed user
@ -291,6 +536,22 @@ jetty-8.1.0.RC5 - 20 January 2012
+ JETTY-1475 made output state fields volatile to provide memory barrier for
non dispatched thread IO
jetty-7.6.0.RC5 - 20 January 2012
+ 359329 Prevent reinvocation of LoginModule.login with jaspi for already
authed user
+ 368632 Remove superfluous removal of org.apache.catalina.jsp_file
+ 368633 fixed configure.dtd resource mappings
+ 368635 moved lifecycle state reporting from toString to dump
+ 368773 process data constraints without realm
+ 368787 always set token view to new header buffers in httpparser
+ 368821 improved test harness
+ 368920 JettyAwareLogger always formats the arguments.
+ 368948 POM for jetty-jndi references unknown version for javax.activation.
+ 368992 avoid non-blocking flush when writing to avoid setting !_writable
without _writeblocked
+ JETTY-1475 made output state fields volatile to provide memory barrier for
non dispatched thread IO
jetty-8.1.0.RC4 - 13 January 2012
+ 365048 jetty Http client does not send proxy authentication when requesting
a Https-resource through a web-proxy.
@ -320,6 +581,21 @@ jetty-8.1.0.RC4 - 13 January 2012
BeanELResolver.properties
+ JETTY-1467 close half closed when idle
jetty-7.6.0.RC4 - 13 January 2012
+ 365048 jetty Http client does not send proxy authentication when requesting
a Https-resource through a web-proxy.
+ 366774 removed XSS vulnerbility
+ 367099 Upgrade jetty-websocket for RFC 6455 - Addendum.
+ 367716 simplified maxIdleTime logic
+ 368035 WebSocketClientFactory does not invoke super.doStop().
+ 368060 do not encode sendRedirect URLs
+ 368114 Protect against non-Strings in System properties for Log
+ 368189 WebSocketClientFactory should not manage external thread pool.
+ 368215 Remove debug from jaspi
+ 368240 Improve AggregateLifeCycle handling of shared lifecycles
+ 368291 Change warning to info for NoSuchFieldException on
BeanELResolver.properties
jetty-8.1.0.RC2 - 22 December 2011
+ 359329 jetty-jaspi must exports its packages. jetty-plus must import
javax.security
@ -344,6 +620,37 @@ jetty-8.1.0.RC2 - 22 December 2011
+ JETTY-1463 websocket D0 parser should return progress even if no fill done
+ JETTY-1465 NPE in ContextHandler.toString
jetty-7.6.0.RC3 - 05 January 2012
+ 367433 added tests to investigate
+ 367435 improved D00 test harness
+ 367485 HttpExchange canceled before response do not release connection.
+ 367502 WebSocket connections should be closed when application context is
stopped.
+ 367591 corrected configuration.xml version to 7.6
+ 367635 Added support for start.d directory
+ 367638 limit number of form parameters to avoid DOS
+ JETTY-1467 close half closed when idle
jetty-7.6.0.RC2 - 22 December 2011
+ 364638 HttpParser closes if data received while seeking EOF. Tests fixed to
cope
+ 364921 Made test less time sensitive for ssl
+ 364936 use Resource for opening URL streams
+ 365267 NullPointerException in bad Address
+ 365375 ResourceHandler should be a HandlerWrapper
+ 365750 Support WebSocket over SSL, aka wss://
+ 365932 Produce jetty-websocket aggregate jar for android use
+ 365947 Set headers for Auth failure and retry in http-spi
+ 366316 Superfluous printStackTrace on 404
+ 366342 Dont persist DosFilter trackers in http session
+ 366730 pass the time idle to onIdleExpire
+ 367048 test harness for guard on suspended requests
+ 367175 SSL 100% CPU spin in case of blocked write and RST.
+ 367219 WebSocketClient.open() fails when URI uses default ports.
+ JETTY-1460 suppress PrintWriter exceptions
+ JETTY-1463 websocket D0 parser should return progress even if no fill done
+ JETTY-1465 NPE in ContextHandler.toString
jetty-8.1.0.RC1 - 06 December 2011
+ 360245 The version of the javax.servlet packages to import is 2.6 instead of
3.0

View File

@ -0,0 +1,175 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.eclipse.jetty.aggregate</groupId>
<artifactId>jetty-all</artifactId>
<name>Jetty :: Aggregate :: All core Jetty</name>
<build>
<sourceDirectory>${project.build.directory}/sources</sourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack-dependencies</id>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<excludes>**/MANIFEST.MF,javax/**</excludes>
<excludeArtifactIds>javax</excludeArtifactIds>
<excludeGroupIds>javax,org.eclipse.jetty.orbit</excludeGroupIds>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
</configuration>
</execution>
<execution>
<id>unpack-source</id>
<phase>generate-sources</phase>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<classifier>sources</classifier>
<includes>**/*</includes>
<excludes>META-INF/**,**/Servlet3Continuation*,**/Jetty6Continuation*</excludes>
<includeGroupIds>org.eclipse.jetty</includeGroupIds>
<excludeArtifactIds>javax</excludeArtifactIds>
<excludeGroupIds>javax,org.eclipse.jetty.orbit</excludeGroupIds>
<outputDirectory>${project.build.directory}/sources</outputDirectory>
<overWriteReleases>true</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins
</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>package</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<archive>
<manifest>
</manifest>
<manifestEntries>
<mode>development</mode>
<url>http://eclipse.org/jetty</url>
<Built-By>${user.name}</Built-By>
<package>org.eclipse.jetty</package>
<Bundle-License>http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/tree/NOTICE.txt</Bundle-License>
<Bundle-Name>Jetty</Bundle-Name>
</manifestEntries>
</archive>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>javadoc-jar</id>
<phase>compile</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-client</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-deploy</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-server</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-client</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.orbit</groupId>
<artifactId>javax.servlet</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jmx</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-plus</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-annotations</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jaspi</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jndi</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-rewrite</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlets</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

102
dists/jetty-deb/pom.xml Normal file
View File

@ -0,0 +1,102 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.jetty.dist</groupId>
<artifactId>dist-parent</artifactId>
<version>9.0.0-SNAPSHOT</version>
</parent>
<artifactId>jetty-deb</artifactId>
<name>Jetty :: Unix Distributions :: Debian</name>
<packaging>deb</packaging>
<build>
<plugins>
<plugin>
<groupId>org.mortbay.jetty.toolchain</groupId>
<artifactId>unix-maven-plugin</artifactId>
<version>1.0-alpha-6.1</version>
<extensions>true</extensions>
<configuration>
<contact>Jetty Project</contact>
<contactEmail>jetty-dev@eclipse.org</contactEmail>
<name>Core Jetty ${project.version} Distribution</name>
<description>Jetty provides an Web server and javax.servlet
container, plus support for Web Sockets, OSGi, JMX, JNDI,
JASPI, AJP and many other integrations. These components are
open source and available for commercial use and
distribution.</description>
<deb>
<useFakeroot>false</useFakeroot>
<priority>optional</priority>
<section>java</section>
</deb>
<packages>
<package>
<id>jetty-server</id>
<assembly>
<extractArtifact>
<artifact>org.eclipse.jetty:jetty-distribution:zip</artifact>
<to>/usr/share/jetty9</to>
<pattern>/jetty-distribution-${project.version}(.*)</pattern>
<replacement>$1</replacement>
<excludes>
<exclude>jetty-distribution-*/javadoc</exclude>
<exclude>jetty-distribution-*/javadoc/**</exclude>
<exclude>jetty-distribution-*/logs/**</exclude>
<exclude>jetty-distribution-*/bin/**</exclude>
<exclude>jetty-distribution-*/webapps/**</exclude>
<exclude>jetty-distribution-*/*.html</exclude>
<exclude>jetty-distribution-*/*.txt</exclude>
</excludes>
</extractArtifact>
<extractArtifact>
<artifact>org.eclipse.jetty:jetty-distribution:zip</artifact>
<to>/usr/share/doc/jetty9</to>
<pattern>/jetty-distribution-${project.version}(.*)</pattern>
<replacement>$1</replacement>
<excludes>
<include>jetty-distribution-*/*.html</include>
<include>jetty-distribution-*/*.txt</include>
<exclude>jetty-distribution-*/**</exclude>
</excludes>
</extractArtifact>
<extractArtifact>
<artifact>org.eclipse.jetty:jetty-distribution:zip</artifact>
<to>/etc/jetty9</to>
<pattern>/jetty-distribution-${project.version}(.*)</pattern>
<replacement>$1</replacement>
<excludes>
<include>jetty-distribution-*/etc/**</include>
<exclude>jetty-distribution-*/**</exclude>
</excludes>
</extractArtifact>
</assembly>
</package>
<package>
<id>jetty-test-webapp</id>
<classifier>test-webapp</classifier>
<assembly>
<extractArtifact>
<artifact>org.eclipse.jetty:jetty-distribution:zip</artifact>
<to>/var/lib/jetty9/webapps</to>
<pattern>/jetty-distribution-${project.version}(.*)</pattern>
<replacement>$1</replacement>
<includes>
<include>jetty-distribution-*/webapps/**</include>
</includes>
</extractArtifact>
</assembly>
</package>
</packages>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-distribution</artifactId>
<version>${project.version}</version>
<type>zip</type>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,20 @@
#!/bin/bash
LOG_DIR=/var/lib/jetty9/logs
WEBAPP_DIR=/var/lib/jetty9/webapps
# copy the jetty start script into place
cp /usr/share/jetty9/bin/jetty.sh /etc/init.d/jetty
# make it generally executable
chmod 755 /etc/init.d/jetty
# ensure we have a logging directory
if [ ! -d "$LOG_DIR" ]; then
mkdir $LOG_DIR
fi
# ensure we have a webapps directory
if [ ! -d "$WEBAPP_DIR" ]; then
mkdir $WEBAPP_DIR
fi

View File

@ -0,0 +1,48 @@
#!/bin/bash
#rm -f /etc/init.d/jetty
case "$1" in
purge)
[...]
# find first and last SYSTEM_UID numbers
for LINE in `grep SYSTEM_UID /etc/adduser.conf | grep -v "^#"`; do
case $LINE in
FIRST_SYSTEM_UID*)
FIST_SYSTEM_UID=`echo $LINE | cut -f2 -d '='`
;;
LAST_SYSTEM_UID*)
LAST_SYSTEM_UID=`echo $LINE | cut -f2 -d '='`
;;
*)
;;
esac
done
# Remove system account if necessary
CREATEDUSER="jetty"
if [ -n "$FIST_SYSTEM_UID" ] && [ -n "$LAST_SYSTEM_UID" ]; then
if USERID=`getent passwd $CREATEDUSER | cut -f 3 -d ':'`; then
if [ -n "$USERID" ]; then
if [ "$FIST_SYSTEM_UID" -le "$USERID" ] && \
[ "$USERID" -le "$LAST_SYSTEM_UID" ]; then
echo -n "Removing $CREATEDUSER system user.."
deluser --quiet $CREATEDUSER || true
echo "..done"
fi
fi
fi
fi
# Remove system group if necessary
CREATEDGROUP="jetty"
FIRST_USER_GID=`grep ^USERS_GID /etc/adduser.conf | cut -f2 -d '='`
if [ -n "$FIST_USER_GID" ] then
if GROUPGID=`getent group $CREATEDGROUP | cut -f 3 -d ':'`; then
if [ -n "$GROUPGID" ]; then
if [ "$FIST_USER_GID" -gt "$GROUPGID" ]; then
echo -n "Removing $CREATEDGROUP group.."
delgroup --only-if-empty $CREATEDGROUP || true
echo "..done"
fi
fi
fi
fi

View File

@ -0,0 +1,60 @@
#!/bin/bash
case "$1" in
install|upgrade)
# If the package has default file it could be sourced, so that
# the local admin can overwrite the defaults
[ -f "/etc/default/jetty9" ] && . /etc/default/jetty9
# Sane defaults:
[ -z "$SERVER_HOME" ] && SERVER_HOME=/usr/share/jetty9
[ -z "$SERVER_USER" ] && SERVER_USER=jetty
[ -z "$SERVER_NAME" ] && SERVER_NAME="Jetty-9 Http and Servlet Engine"
[ -z "$SERVER_GROUP" ] && SERVER_GROUP=jetty
# Groups that the user will be added to, if undefined, then none.
ADDGROUP=""
# create user to avoid running server as root
# 1. create group if not existing
if ! getent group | grep -q "^$SERVER_GROUP:" ; then
echo -n "Adding group $SERVER_GROUP.."
addgroup --quiet --system $SERVER_GROUP 2>/dev/null ||true
echo "..done"
fi
# 2. create homedir if not existing
test -d $SERVER_HOME || mkdir $SERVER_HOME
# 3. create user if not existing
if ! getent passwd | grep -q "^$SERVER_USER:"; then
echo -n "Adding system user $SERVER_USER.."
adduser --quiet \
--system \
--ingroup $SERVER_GROUP \
--no-create-home \
--disabled-password \
$SERVER_USER 2>/dev/null || true
echo "..done"
fi
# 4. adjust passwd entry
usermod -c "$SERVER_NAME" \
-d $SERVER_HOME \
-g $SERVER_GROUP \
$SERVER_USER
# 5. adjust file and directory permissions
if ! dpkg-statoverride --list $SERVER_HOME >/dev/null
then
chown -R $SERVER_USER:$SERVER_GROUP $SERVER_HOME
chmod u=rwx,g=rxs,o= $SERVER_HOME
fi
# 6. Add the user to the ADDGROUP group
if test -n $ADDGROUP
then
if ! groups $SERVER_USER | cut -d: -f2 | \
grep -qw $ADDGROUP; then
adduser $SERVER_USER $ADDGROUP
fi
fi
;;

26
dists/pom.xml Normal file
View File

@ -0,0 +1,26 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>jetty-project</artifactId>
<groupId>org.eclipse.jetty</groupId>
<version>9.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.eclipse.jetty.dist</groupId>
<artifactId>dist-parent</artifactId>
<packaging>pom</packaging>
<name>Jetty :: Distribution :: Parent</name>
<profiles>
<profile>
<id>linux-packaging</id>
<!-- activation>
<os>
<name>Linux</name>
</os>
</activation-->
<modules>
<module>jetty-deb</module>
<!--module>jetty-rpm</module-->
</modules>
</profile>
</profiles>
</project>

View File

@ -1,9 +1,9 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<artifactId>examples-parent</artifactId>
<version>9.0.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.eclipse.jetty</groupId>

View File

@ -1,9 +1,9 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<artifactId>examples-parent</artifactId>
<version>9.0.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>example-jetty-embedded</artifactId>

View File

@ -106,7 +106,7 @@ public class LikeJettyXml
webapp_provider.setParentLoaderPriority(false);
webapp_provider.setExtractWars(true);
webapp_provider.setScanInterval(2);
webapp_provider.setDefaultsDescriptor(jetty_home + "/etc/webdefault.xml");
//webapp_provider.setDefaultsDescriptor(jetty_home + "/etc/webdefault.xml");
deployer.addAppProvider(webapp_provider);
HashLoginService login = new HashLoginService();

47
examples/pom.xml Normal file
View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
// ========================================================================
// Copyright (c) Webtide LLC
//
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.apache.org/licenses/LICENSE-2.0.txt
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
<version>9.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>examples-parent</artifactId>
<name>Jetty Examples :: Parent</name>
<packaging>pom</packaging>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<configuration>
<!-- No Point running Findbugs on example projects -->
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<modules>
<module>async-rest</module>
<module>embedded</module>
</modules>
</project>

View File

@ -1,4 +1,5 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
@ -8,7 +9,29 @@
<artifactId>jetty-ant</artifactId>
<packaging>jar</packaging>
<name>Jetty :: Ant Plugin</name>
<build>
<plugins>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-lib-deps</id>
<phase>generate-resources</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<includeGroupIds>org.eclipse.jetty</includeGroupIds>
<excludeGroupIds>org.eclipse.jetty.orbit,org.eclipse.jetty.spdy,org.eclipse.jetty.websocket,org.eclipse.jetty.drafts</excludeGroupIds>
<excludeArtifactIds>jetty-all,jetty-start,jetty-monitor,jetty-jsp</excludeArtifactIds>
<includeTypes>jar</includeTypes>
<outputDirectory>${project.build.directory}/test-lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
@ -21,6 +44,11 @@
<artifactId>ant</artifactId>
<version>1.6.5</version>
</dependency>
<dependency>
<groupId>ant</groupId>
<artifactId>ant-launcher</artifactId>
<version>1.6.5</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-security</artifactId>
@ -35,7 +63,7 @@
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>${project.version}</version>
</dependency>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-annotations</artifactId>

View File

@ -35,9 +35,6 @@ import org.eclipse.jetty.webapp.WebXmlConfiguration;
* properties into the configured web application. The list of classpath files,
* the application base directory and web.xml file could be specified in this
* way.
*
* @author Jakub Pawlowicz
* @author Athena Yao
*/
public class AntWebXmlConfiguration extends WebXmlConfiguration
{

View File

@ -37,8 +37,6 @@ import org.eclipse.jetty.util.Scanner;
/**
* Ant task for running a Jetty server.
*
* @author Jakub Pawlowicz
*/
public class JettyRunTask extends Task
{

View File

@ -27,7 +27,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.jetty.ant.types.Connectors.Connector;
import org.eclipse.jetty.ant.types.Connector;
import org.eclipse.jetty.ant.utils.ServerProxy;
import org.eclipse.jetty.ant.utils.TaskLog;
import org.eclipse.jetty.ant.utils.WebApplicationProxy;
@ -47,8 +47,6 @@ import org.xml.sax.SAXException;
/**
* A proxy class for interaction with Jetty server object. Used to have some
* level of abstraction over standard Jetty classes.
*
* @author Jakub Pawlowicz
*/
public class ServerProxyImpl implements ServerProxy
{
@ -128,7 +126,10 @@ public class ServerProxyImpl implements ServerProxy
jc.setPort(jettyConnector.getPort());
jc.setIdleTimeout(jettyConnector.getMaxIdleTime());
server.addConnector(jc);
}
// Configures login services
@ -217,6 +218,8 @@ public class ServerProxyImpl implements ServerProxy
try
{
server.start();
TaskLog.log("" + server.getConnectors()[0]);
startScanners();
server.join();

View File

@ -57,7 +57,6 @@ import org.eclipse.jetty.webapp.WebAppContext;
/**
* An abstraction layer over Jetty WebAppContext.
*
* @author Jakub Pawlowicz
*/
public class WebApplicationProxyImpl implements WebApplicationProxy
{

View File

@ -0,0 +1,57 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.ant.types;
public class Connector
{
private int port;
private int maxIdleTime;
public Connector()
{
}
public Connector(int port, int maxIdleTime)
{
this.port = port;
this.maxIdleTime = maxIdleTime;
}
public int getPort()
{
return port;
}
public void setPort(int port)
{
this.port = port;
}
public int getMaxIdleTime()
{
return maxIdleTime;
}
public void setMaxIdleTime(int maxIdleTime)
{
this.maxIdleTime = maxIdleTime;
}
}

View File

@ -25,7 +25,6 @@ import java.util.List;
/**
* Specifies a jetty configuration <connectors/> element for Ant build file.
*
* @author Jakub Pawlowicz
*/
public class Connectors
{
@ -81,33 +80,4 @@ public class Connectors
return defaultConnectors;
}
public class Connector
{
private int port;
private int maxIdleTime;
public Connector(int port, int maxIdleTime)
{
this.port = port;
this.maxIdleTime = maxIdleTime;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public int getMaxIdleTime() {
return maxIdleTime;
}
public void setMaxIdleTime(int maxIdleTime) {
this.maxIdleTime = maxIdleTime;
}
}
}

View File

@ -27,7 +27,6 @@ import org.eclipse.jetty.server.handler.ContextHandler;
/**
* Specifies <contextHandlers/> element in web app configuration.
*
* @author Jakub Pawlowicz
*/
public class ContextHandlers
{

View File

@ -31,7 +31,6 @@ import org.apache.tools.ant.DirectoryScanner;
* file. It is used to group application classes, libraries, and scannedTargets
* elements.
*
* @author Jakub Pawlowicz
*/
public class FileMatchingConfiguration
{

View File

@ -27,7 +27,6 @@ import org.eclipse.jetty.security.LoginService;
/**
* Specifies a jetty configuration <loginServices/> element for Ant build file.
*
* @author Jakub Pawlowicz
*/
public class LoginServices
{

View File

@ -28,7 +28,6 @@ import org.eclipse.jetty.ant.utils.TaskLog;
/**
* Ant <systemProperties/> tag definition.
*
* @author Jakub Pawlowicz
*/
public class SystemProperties
{

View File

@ -31,7 +31,6 @@ import org.apache.tools.ant.types.FileSet;
/**
* Ant's WebApp object definition.
*
* @author Jakub Pawlowicz
*/
public class WebApp
{

View File

@ -27,7 +27,6 @@ import org.apache.tools.ant.Task;
* Provides logging functionality for classes without access to the Ant project
* variable.
*
* @author Jakub Pawlowicz
*/
public class TaskLog
{

View File

@ -0,0 +1,297 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.ant;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.tools.ant.DefaultLogger;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.ProjectHelper;
import org.eclipse.jetty.toolchain.test.IO;
public class AntBuild
{
private Thread _process;
private String _ant;
private int _port;
private String _host;
public AntBuild(String ant)
{
_ant = ant;
}
private class AntBuildProcess implements Runnable
{
List<String[]> connList;
@Override
public void run()
{
File buildFile = new File(_ant);
Project antProject = new Project();
try
{
antProject.setUserProperty("ant.file",buildFile.getAbsolutePath());
DefaultLogger logger = new DefaultLogger();
ConsoleParser parser = new ConsoleParser();
connList = parser.newPattern(".*([0-9]+\\.[0-9]*\\.[0-9]*\\.[0-9]*):([0-9]*)",1);
PipedOutputStream pos = new PipedOutputStream();
PipedInputStream pis = new PipedInputStream(pos);
PipedOutputStream pose = new PipedOutputStream();
PipedInputStream pise = new PipedInputStream(pose);
startPump("STDOUT",parser,pis);
startPump("STDERR",parser,pise);
logger.setErrorPrintStream(new PrintStream(pos));
logger.setOutputPrintStream(new PrintStream(pose));
logger.setMessageOutputLevel(Project.MSG_VERBOSE);
antProject.addBuildListener(logger);
antProject.fireBuildStarted();
antProject.init();
ProjectHelper helper = ProjectHelper.getProjectHelper();
antProject.addReference("ant.projectHelper",helper);
helper.parse(antProject,buildFile);
antProject.executeTarget("jetty.run");
parser.waitForDone(10000,TimeUnit.MILLISECONDS);
}
catch (Exception e)
{
antProject.fireBuildFinished(e);
}
}
public void waitForStarted() throws Exception
{
while (connList == null || connList.isEmpty())
{
Thread.sleep(10);
}
}
public List<String[]> getConnectionList()
{
return connList;
}
}
public void start() throws Exception
{
System.out.println("Starting Ant Build ...");
AntBuildProcess abp = new AntBuildProcess();
_process = new Thread(abp);
_process.start();
abp.waitForStarted();
// once this has returned we should have the connection info we need
_host = abp.getConnectionList().get(0)[0];
_port = Integer.parseInt(abp.getConnectionList().get(0)[1]);
}
public int getJettyPort()
{
return _port;
}
public String getJettyHost()
{
return _host;
}
/**
* Stop the jetty server
*/
public void stop()
{
System.out.println("Stopping Ant Build ...");
_process.interrupt();
}
private static class ConsoleParser
{
private List<ConsolePattern> patterns = new ArrayList<ConsolePattern>();
private CountDownLatch latch;
private int count;
public List<String[]> newPattern(String exp, int cnt)
{
ConsolePattern pat = new ConsolePattern(exp,cnt);
patterns.add(pat);
count += cnt;
return pat.getMatches();
}
public void parse(String line)
{
for (ConsolePattern pat : patterns)
{
Matcher mat = pat.getMatcher(line);
if (mat.find())
{
int num = 0, count = mat.groupCount();
String[] match = new String[count];
while (num++ < count)
{
match[num - 1] = mat.group(num);
}
pat.getMatches().add(match);
if (pat.getCount() > 0)
{
getLatch().countDown();
}
}
}
}
public void waitForDone(long timeout, TimeUnit unit) throws InterruptedException
{
getLatch().await(timeout,unit);
}
private CountDownLatch getLatch()
{
synchronized (this)
{
if (latch == null)
{
latch = new CountDownLatch(count);
}
}
return latch;
}
}
private static class ConsolePattern
{
private Pattern pattern;
private List<String[]> matches;
private int count;
ConsolePattern(String exp, int cnt)
{
pattern = Pattern.compile(exp);
matches = new ArrayList<String[]>();
count = cnt;
}
public Matcher getMatcher(String line)
{
return pattern.matcher(line);
}
public List<String[]> getMatches()
{
return matches;
}
public int getCount()
{
return count;
}
}
private void startPump(String mode, ConsoleParser parser, InputStream inputStream)
{
ConsoleStreamer pump = new ConsoleStreamer(mode,inputStream);
pump.setParser(parser);
Thread thread = new Thread(pump,"ConsoleStreamer/" + mode);
thread.start();
}
/**
* Simple streamer for the console output from a Process
*/
private static class ConsoleStreamer implements Runnable
{
private String mode;
private BufferedReader reader;
private ConsoleParser parser;
public ConsoleStreamer(String mode, InputStream is)
{
this.mode = mode;
this.reader = new BufferedReader(new InputStreamReader(is));
}
public void setParser(ConsoleParser connector)
{
this.parser = connector;
}
public void run()
{
String line;
//System.out.printf("ConsoleStreamer/%s initiated%n",mode);
try
{
while ((line = reader.readLine()) != (null))
{
if (parser != null)
{
parser.parse(line);
}
System.out.println("[" + mode + "] " + line);
}
}
catch (IOException ignore)
{
/* ignore */
}
finally
{
IO.close(reader);
}
//System.out.printf("ConsoleStreamer/%s finished%n",mode);
}
}
}

View File

@ -16,34 +16,36 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.core.api;
package org.eclipse.jetty.ant;
import java.util.List;
import java.util.Map;
import java.net.HttpURLConnection;
import java.net.URI;
import org.eclipse.jetty.websocket.core.protocol.ExtensionConfig;
import junit.framework.Assert;
public interface UpgradeRequest
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.junit.Test;
public class JettyAntTaskTest
{
public void addExtensions(String... extConfigs);
public Map<String, String> getCookieMap();
public List<ExtensionConfig> getExtensions();
public String getHeader(String name);
public String getHost();
public String getHttpEndPointName();
public String getOrigin();
public List<String> getSubProtocols();
public boolean hasSubProtocol(String test);
public boolean isOrigin(String test);
public void setSubProtocols(String protocols);
@Test
public void testJettyTask() throws Exception
{
AntBuild build = new AntBuild(MavenTestingUtils.getTestResourceFile("test.xml").getAbsolutePath());
build.start();
URI uri = new URI("http://" + build.getJettyHost() + ":" + build.getJettyPort());
HttpURLConnection connection = (HttpURLConnection)uri.toURL().openConnection();
connection.connect();
Assert.assertEquals(404,connection.getResponseCode());
build.stop();
}
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<project name="Jetty-Ant integration test" basedir=".">
<path id="jetty.plugin.classpath">
<fileset dir="target/test-lib" includes="*.jar"/>
</path>
<taskdef classpathref="jetty.plugin.classpath" resource="tasks.properties" loaderref="jetty.loader" />
<typedef name="connector" classname="org.eclipse.jetty.ant.types.Connector"
classpathref="jetty.plugin.classpath" loaderref="jetty.loader" />
<target name="jetty.run">
<jetty>
<connectors>
<connector port="0"/>
</connectors>
</jetty>
</target>
</project>

View File

@ -20,7 +20,6 @@ package org.eclipse.jetty.client;
import java.io.IOException;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.URI;
@ -44,6 +43,7 @@ import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.CookieStore;
import org.eclipse.jetty.client.api.Destination;
import org.eclipse.jetty.client.api.ProxyConfiguration;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.http.HttpFields;
@ -127,6 +127,7 @@ public class HttpClient extends ContainerLifeCycle
private volatile long idleTimeout;
private volatile boolean tcpNoDelay = true;
private volatile boolean dispatchIO = true;
private volatile ProxyConfiguration proxyConfig;
public HttpClient()
{
@ -297,6 +298,9 @@ public class HttpClient extends ContainerLifeCycle
protected HttpDestination provideDestination(String scheme, String host, int port)
{
if (port <= 0)
port = "https".equalsIgnoreCase(scheme) ? 443 : 80;
String address = address(scheme, host, port);
HttpDestination destination = destinations.get(address);
if (destination == null)
@ -328,15 +332,11 @@ public class HttpClient extends ContainerLifeCycle
if (!Arrays.asList("http", "https").contains(scheme))
throw new IllegalArgumentException("Invalid protocol " + scheme);
int port = request.getPort();
if (port < 0)
port = "https".equals(scheme) ? 443 : 80;
for (Response.ResponseListener listener : listeners)
if (listener instanceof Schedulable)
((Schedulable)listener).schedule(scheduler);
HttpDestination destination = provideDestination(scheme, request.getHost(), port);
HttpDestination destination = provideDestination(scheme, request.getHost(), request.getPort());
destination.send(request, listeners);
}
@ -351,7 +351,7 @@ public class HttpClient extends ContainerLifeCycle
channel.bind(bindAddress);
configure(channel);
channel.configureBlocking(false);
channel.connect(new InetSocketAddress(destination.getHost(), destination.getPort()));
channel.connect(destination.getConnectAddress());
Future<Connection> result = new ConnectionCallback(destination, callback);
selectorManager.connect(channel, result);
@ -596,6 +596,16 @@ public class HttpClient extends ContainerLifeCycle
this.dispatchIO = dispatchIO;
}
public ProxyConfiguration getProxyConfiguration()
{
return proxyConfig;
}
public void setProxyConfiguration(ProxyConfiguration proxyConfig)
{
this.proxyConfig = proxyConfig;
}
@Override
public void dump(Appendable out, String indent) throws IOException
{

View File

@ -155,6 +155,11 @@ public class HttpConnection extends AbstractConnection implements Connection
path = "/";
request.path(path);
}
if (destination.isProxied() && HttpMethod.CONNECT != request.getMethod())
{
path = request.getURI();
request.path(path);
}
Fields fields = request.getParams();
if (!fields.isEmpty())
@ -359,13 +364,13 @@ public class HttpConnection extends AbstractConnection implements Connection
}
}
public boolean abort(HttpExchange exchange, String reason)
public boolean abort(HttpExchange exchange, Throwable cause)
{
// We want the return value to be that of the response
// because if the response has already successfully
// arrived then we failed to abort the exchange
sender.abort(exchange, reason);
return receiver.abort(exchange, reason);
sender.abort(exchange, cause);
return receiver.abort(exchange, cause);
}
public void proceed(boolean proceed)

View File

@ -77,9 +77,9 @@ public class HttpContentResponse implements ContentResponse
}
@Override
public boolean abort(String reason)
public boolean abort(Throwable cause)
{
return response.abort(reason);
return response.abort(cause);
}
@Override

View File

@ -98,10 +98,10 @@ public class HttpConversation implements Attributes
attributes.clear();
}
public boolean abort(String reason)
public boolean abort(Throwable cause)
{
HttpExchange exchange = exchanges.peekLast();
return exchange != null && exchange.abort(reason);
return exchange != null && exchange.abort(cause);
}
@Override

View File

@ -41,23 +41,27 @@ public class HttpCookieStore implements CookieStore
String host = destination.getHost();
int port = destination.getPort();
String key = host + ":" + port + path;
String key = host + ":" + port;
// First lookup: direct hit
Queue<HttpCookie> cookies = allCookies.get(key);
// Root path lookup
Queue<HttpCookie> cookies = allCookies.get(key + "/");
if (cookies != null)
accumulateCookies(destination, cookies, result);
// Second lookup: root path
if (!"/".equals(path))
// Path lookup
String[] split = path.split("/");
for (int i = 1; i < split.length; i++)
{
key = host + ":" + port + "/";
String segment = split[i];
key += "/" + segment;
cookies = allCookies.get(key);
if (cookies != null)
accumulateCookies(destination, cookies, result);
if (segment.length() > 0)
key += "/";
}
// Third lookup: parent domains
// Domain lookup
int domains = host.split("\\.").length - 1;
for (int i = 2; i <= domains; ++i)
{

View File

@ -19,6 +19,7 @@
package org.eclipse.jetty.client;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.AsynchronousCloseException;
import java.util.ArrayList;
import java.util.List;
@ -27,13 +28,19 @@ import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.Destination;
import org.eclipse.jetty.client.api.ProxyConfiguration;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.util.TimedResponseListener;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.FutureCallback;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
@ -48,25 +55,28 @@ public class HttpDestination implements Destination, AutoCloseable, Dumpable
private final AtomicInteger connectionCount = new AtomicInteger();
private final HttpClient client;
private final String scheme;
private final String host;
private final int port;
private final InetSocketAddress address;
private final Queue<RequestContext> requests;
private final BlockingQueue<Connection> idleConnections;
private final BlockingQueue<Connection> activeConnections;
private final RequestNotifier requestNotifier;
private final ResponseNotifier responseNotifier;
private final InetSocketAddress proxyAddress;
public HttpDestination(HttpClient client, String scheme, String host, int port)
{
this.client = client;
this.scheme = scheme;
this.host = host;
this.port = port;
this.address = new InetSocketAddress(host, port);
this.requests = new ArrayBlockingQueue<>(client.getMaxQueueSizePerAddress());
this.idleConnections = new ArrayBlockingQueue<>(client.getMaxConnectionsPerAddress());
this.activeConnections = new ArrayBlockingQueue<>(client.getMaxConnectionsPerAddress());
this.requestNotifier = new RequestNotifier(client);
this.responseNotifier = new ResponseNotifier(client);
ProxyConfiguration proxyConfig = client.getProxyConfiguration();
proxyAddress = proxyConfig != null && proxyConfig.matches(host, port) ?
new InetSocketAddress(proxyConfig.getHost(), proxyConfig.getPort()) : null;
}
protected BlockingQueue<Connection> getIdleConnections()
@ -88,23 +98,33 @@ public class HttpDestination implements Destination, AutoCloseable, Dumpable
@Override
public String getHost()
{
return host;
return address.getHostString();
}
@Override
public int getPort()
{
return port;
return address.getPort();
}
public InetSocketAddress getConnectAddress()
{
return isProxied() ? proxyAddress : address;
}
public boolean isProxied()
{
return proxyAddress != null;
}
public void send(Request request, List<Response.ResponseListener> listeners)
{
if (!scheme.equals(request.getScheme()))
throw new IllegalArgumentException("Invalid request scheme " + request.getScheme() + " for destination " + this);
if (!host.equals(request.getHost()))
if (!getHost().equals(request.getHost()))
throw new IllegalArgumentException("Invalid request host " + request.getHost() + " for destination " + this);
int port = request.getPort();
if (port >= 0 && this.port != port)
if (port >= 0 && getPort() != port)
throw new IllegalArgumentException("Invalid request port " + port + " for destination " + this);
RequestContext requestContext = new RequestContext(request, listeners);
@ -139,7 +159,7 @@ public class HttpDestination implements Destination, AutoCloseable, Dumpable
public Future<Connection> newConnection()
{
FutureCallback<Connection> result = new FutureCallback<>();
newConnection(result);
newConnection(new CONNECTCallback(result));
return result;
}
@ -170,30 +190,31 @@ public class HttpDestination implements Destination, AutoCloseable, Dumpable
if (connectionCount.compareAndSet(current, next))
{
LOG.debug("Creating connection {}/{} for {}", next, maxConnections, this);
newConnection(new Callback<Connection>()
CONNECTCallback connectCallback = new CONNECTCallback(new Callback<Connection>()
{
@Override
public void completed(Connection connection)
{
LOG.debug("Created connection {}/{} {} for {}", next, maxConnections, connection, HttpDestination.this);
process(connection, true);
}
@Override
public void failed(Connection connection, final Throwable x)
public void failed(final Connection connection, final Throwable x)
{
LOG.debug("Connection failed {} for {}", x, HttpDestination.this);
connectionCount.decrementAndGet();
client.getExecutor().execute(new Runnable()
{
@Override
public void run()
{
drain(x);
if (connection != null)
connection.close();
}
});
}
});
newConnection(new TCPCallback(next, maxConnections, connectCallback));
// Try again the idle connections
return idleConnections.poll();
}
@ -248,9 +269,10 @@ public class HttpDestination implements Destination, AutoCloseable, Dumpable
{
final Request request = requestContext.request;
final List<Response.ResponseListener> listeners = requestContext.listeners;
if (request.isAborted())
Throwable cause = request.getAbortCause();
if (cause != null)
{
abort(request, listeners, "Aborted");
abort(request, listeners, cause);
LOG.debug("Aborted {} before processing", request);
}
else
@ -300,10 +322,13 @@ public class HttpDestination implements Destination, AutoCloseable, Dumpable
public void remove(Connection connection)
{
LOG.debug("{} removed", connection);
connectionCount.decrementAndGet();
activeConnections.remove(connection);
idleConnections.remove(connection);
boolean removed = activeConnections.remove(connection);
removed |= idleConnections.remove(connection);
if (removed)
{
LOG.debug("{} removed", connection);
connectionCount.decrementAndGet();
}
// We need to execute queued requests even if this connection failed.
// We may create a connection that is not needed, but it will eventually
@ -334,7 +359,7 @@ public class HttpDestination implements Destination, AutoCloseable, Dumpable
LOG.debug("Closed {}", this);
}
public boolean abort(Request request, String reason)
public boolean abort(Request request, Throwable cause)
{
for (RequestContext requestContext : requests)
{
@ -343,7 +368,7 @@ public class HttpDestination implements Destination, AutoCloseable, Dumpable
if (requests.remove(requestContext))
{
// We were able to remove the pair, so it won't be processed
abort(request, requestContext.listeners, reason);
abort(request, requestContext.listeners, cause);
LOG.debug("Aborted {} while queued", request);
return true;
}
@ -352,13 +377,11 @@ public class HttpDestination implements Destination, AutoCloseable, Dumpable
return false;
}
private void abort(Request request, List<Response.ResponseListener> listeners, String reason)
private void abort(Request request, List<Response.ResponseListener> listeners, Throwable cause)
{
HttpResponse response = new HttpResponse(request, listeners);
HttpResponseException responseFailure = new HttpResponseException(reason, response);
responseNotifier.notifyFailure(listeners, response, responseFailure);
HttpRequestException requestFailure = new HttpRequestException(reason, request);
responseNotifier.notifyComplete(listeners, new Result(request, requestFailure, response, responseFailure));
responseNotifier.notifyFailure(listeners, response, cause);
responseNotifier.notifyComplete(listeners, new Result(request, cause, response, cause));
}
@Override
@ -382,7 +405,12 @@ public class HttpDestination implements Destination, AutoCloseable, Dumpable
@Override
public String toString()
{
return String.format("%s(%s://%s:%d)", HttpDestination.class.getSimpleName(), getScheme(), getHost(), getPort());
return String.format("%s(%s://%s:%d)%s",
HttpDestination.class.getSimpleName(),
getScheme(),
getHost(),
getPort(),
proxyAddress == null ? "" : " via " + proxyAddress.getHostString() + ":" + proxyAddress.getPort());
}
private static class RequestContext
@ -396,4 +424,90 @@ public class HttpDestination implements Destination, AutoCloseable, Dumpable
this.listeners = listeners;
}
}
private class TCPCallback implements Callback<Connection>
{
private final int current;
private final int max;
private final Callback<Connection> delegate;
private TCPCallback(int current, int max, Callback<Connection> delegate)
{
this.current = current;
this.max = max;
this.delegate = delegate;
}
@Override
public void completed(Connection connection)
{
LOG.debug("Created connection {}/{} {} for {}", current, max, connection, HttpDestination.this);
delegate.completed(connection);
}
@Override
public void failed(Connection connection, Throwable x)
{
LOG.debug("Connection failed {} for {}", x, HttpDestination.this);
connectionCount.decrementAndGet();
delegate.failed(connection, x);
}
}
private class CONNECTCallback implements Callback<Connection>
{
private final Callback<Connection> delegate;
private CONNECTCallback(Callback<Connection> delegate)
{
this.delegate = delegate;
}
@Override
public void completed(Connection connection)
{
boolean tunnel = isProxied() &&
"https".equalsIgnoreCase(getScheme()) &&
client.getSslContextFactory() != null;
if (tunnel)
tunnel(connection);
else
delegate.completed(connection);
}
@Override
public void failed(Connection connection, Throwable x)
{
delegate.failed(connection, x);
}
private void tunnel(final Connection connection)
{
String target = address.getHostString() + ":" + address.getPort();
Request connect = client.newRequest(proxyAddress.getHostString(), proxyAddress.getPort())
.scheme(HttpScheme.HTTP.asString())
.method(HttpMethod.CONNECT)
.path(target)
.header(HttpHeader.HOST.asString(), target);
connection.send(connect, new TimedResponseListener(client.getConnectTimeout(), TimeUnit.MILLISECONDS, connect, new Response.CompleteListener()
{
@Override
public void onComplete(Result result)
{
if (result.isFailed())
{
failed(connection, result.getFailure());
}
else
{
Response response = result.getResponse();
if (response.getStatus() == 200)
delegate.completed(connection);
else
failed(connection, new HttpResponseException("Received " + response + " for " + result.getRequest(), response));
}
}
}));
}
}
}

View File

@ -193,10 +193,10 @@ public class HttpExchange
return new AtomicMarkableReference<>(result, modified);
}
public boolean abort(String reason)
public boolean abort(Throwable cause)
{
LOG.debug("Aborting {} reason {}", this, reason);
boolean aborted = connection.abort(this, reason);
LOG.debug("Aborting {} reason {}", this, cause);
boolean aborted = connection.abort(this, cause);
LOG.debug("Aborted {}: {}", this, aborted);
return aborted;
}

View File

@ -387,9 +387,9 @@ public class HttpReceiver implements HttpParser.ResponseHandler<ByteBuffer>
fail(new TimeoutException());
}
public boolean abort(HttpExchange exchange, String reason)
public boolean abort(HttpExchange exchange, Throwable cause)
{
return fail(new HttpResponseException(reason == null ? "Response aborted" : reason, exchange.getResponse()));
return fail(cause);
}
private boolean updateState(State from, State to)

View File

@ -28,6 +28,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicLong;
@ -63,7 +64,7 @@ public class HttpRequest implements Request
private long idleTimeout;
private ContentProvider content;
private boolean followRedirects;
private volatile boolean aborted;
private volatile Throwable aborted;
public HttpRequest(HttpClient client, URI uri)
{
@ -403,17 +404,17 @@ public class HttpRequest implements Request
}
@Override
public boolean abort(String reason)
public boolean abort(Throwable cause)
{
aborted = true;
if (client.provideDestination(getScheme(), getHost(), getPort()).abort(this, reason))
aborted = Objects.requireNonNull(cause);
if (client.provideDestination(getScheme(), getHost(), getPort()).abort(this, cause))
return true;
HttpConversation conversation = client.getConversation(getConversationID(), false);
return conversation != null && conversation.abort(reason);
return conversation != null && conversation.abort(cause);
}
@Override
public boolean isAborted()
public Throwable getAbortCause()
{
return aborted;
}

View File

@ -98,9 +98,9 @@ public class HttpResponse implements Response
}
@Override
public boolean abort(String reason)
public boolean abort(Throwable cause)
{
return request.abort(reason);
return request.abort(cause);
}
@Override

View File

@ -82,9 +82,10 @@ public class HttpSender
}
Request request = exchange.getRequest();
if (request.isAborted())
Throwable cause = request.getAbortCause();
if (cause != null)
{
exchange.abort(null);
exchange.abort(cause);
}
else
{
@ -400,7 +401,7 @@ public class HttpSender
Result result = completion.getReference();
boolean notCommitted = current == State.IDLE || current == State.SEND;
if (result == null && notCommitted && !request.isAborted())
if (result == null && notCommitted && request.getAbortCause() == null)
{
result = exchange.responseComplete(failure).getReference();
exchange.terminateResponse();
@ -418,12 +419,12 @@ public class HttpSender
return true;
}
public boolean abort(HttpExchange exchange, String reason)
public boolean abort(HttpExchange exchange, Throwable cause)
{
State current = state.get();
boolean abortable = current == State.IDLE || current == State.SEND ||
current == State.COMMIT && contentIterator.hasNext();
return abortable && fail(new HttpRequestException(reason == null ? "Request aborted" : reason, exchange.getRequest()));
return abortable && fail(cause);
}
private void releaseBuffers(ByteBufferPool bufferPool, ByteBuffer header, ByteBuffer chunk)

View File

@ -126,8 +126,9 @@ public class RedirectProtocolHandler extends Response.Listener.Empty implements
@Override
public void onBegin(Request redirect)
{
if (request.isAborted())
redirect.abort(null);
Throwable cause = request.getAbortCause();
if (cause != null)
redirect.abort(cause);
}
});

View File

@ -16,36 +16,41 @@
// ========================================================================
//
package org.eclipse.jetty.server.handler;
package org.eclipse.jetty.client.api;
import org.eclipse.jetty.server.Handler;
import java.util.HashSet;
import java.util.Set;
/* ------------------------------------------------------------ */
/** ProxyHandler.
* <p>This class has been renamed to ConnectHandler, as it only implements
* the CONNECT method (and a ProxyServlet must be used for full proxy handling).
* @deprecated Use {@link ConnectHandler}
*/
public class ProxyHandler extends ConnectHandler
public class ProxyConfiguration
{
public ProxyHandler()
private final Set<String> excluded = new HashSet<>();
private final String host;
private final int port;
public ProxyConfiguration(String host, int port)
{
super();
this.host = host;
this.port = port;
}
public ProxyHandler(Handler handler, String[] white, String[] black)
public String getHost()
{
super(handler,white,black);
return host;
}
public ProxyHandler(Handler handler)
public int getPort()
{
super(handler);
return port;
}
public ProxyHandler(String[] white, String[] black)
public boolean matches(String host, int port)
{
super(white,black);
String hostPort = host + ":" + port;
return !getExcludedHosts().contains(hostPort);
}
public Set<String> getExcludedHosts()
{
return excluded;
}
}

View File

@ -308,15 +308,16 @@ public interface Request
/**
* Attempts to abort the send of this request.
*
* @param reason the abort reason
* @param cause the abort cause, must not be null
* @return whether the abort succeeded
*/
boolean abort(String reason);
boolean abort(Throwable cause);
/**
* @return whether {@link #abort(String)} was called
* @return the abort cause passed to {@link #abort(Throwable)},
* or null if this request has not been aborted
*/
boolean isAborted();
Throwable getAbortCause();
public interface RequestListener extends EventListener
{

View File

@ -71,10 +71,10 @@ public interface Response
/**
* Attempts to abort the receive of this response.
*
* @param reason the abort reason
* @param cause the abort cause, must not be null
* @return whether the abort succeeded
*/
boolean abort(String reason);
boolean abort(Throwable cause);
public interface ResponseListener extends EventListener
{

View File

@ -18,9 +18,6 @@
package org.eclipse.jetty.client.util;
import java.io.UnsupportedEncodingException;
import java.nio.charset.UnsupportedCharsetException;
import org.eclipse.jetty.client.api.Authentication;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
@ -60,15 +57,8 @@ public class BasicAuthentication implements Authentication
public Result authenticate(Request request, ContentResponse response, String wwwAuthenticate, Attributes context)
{
String encoding = StringUtil.__ISO_8859_1;
try
{
String value = "Basic " + B64Code.encode(user + ":" + password, encoding);
return new BasicResult(request.getURI(), value);
}
catch (UnsupportedEncodingException x)
{
throw new UnsupportedCharsetException(encoding);
}
String value = "Basic " + B64Code.encode(user + ":" + password, encoding);
return new BasicResult(request.getURI(), value);
}
private static class BasicResult implements Result

View File

@ -55,7 +55,7 @@ public class BlockingResponseListener extends BufferingResponseListener implemen
public boolean cancel(boolean mayInterruptIfRunning)
{
cancelled = true;
return request.abort("Cancelled");
return request.abort(new CancellationException());
}
@Override
@ -83,8 +83,9 @@ public class BlockingResponseListener extends BufferingResponseListener implemen
boolean expired = !latch.await(timeout, unit);
if (expired)
{
request.abort("Total timeout elapsed");
throw new TimeoutException();
TimeoutException reason = new TimeoutException();
request.abort(reason);
throw reason;
}
return getResult();
}

View File

@ -64,7 +64,7 @@ public abstract class BufferingResponseListener extends Response.Listener.Empty
HttpFields headers = response.getHeaders();
long length = headers.getLongField(HttpHeader.CONTENT_LENGTH.asString());
if (length > maxLength)
response.abort("Buffering capacity exceeded");
response.abort(new IllegalArgumentException("Buffering capacity exceeded"));
String contentType = headers.get(HttpHeader.CONTENT_TYPE);
if (contentType != null)

View File

@ -18,7 +18,6 @@
package org.eclipse.jetty.client.util;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.Iterator;
@ -49,46 +48,64 @@ public class InputStreamContentProvider implements ContentProvider
return -1;
}
protected ByteBuffer onRead(byte[] buffer, int offset, int length)
{
if (length <= 0)
return BufferUtil.EMPTY_BUFFER;
return ByteBuffer.wrap(buffer, offset, length);
}
@Override
public Iterator<ByteBuffer> iterator()
{
return new Iterator<ByteBuffer>()
{
private final byte[] buffer = new byte[bufferSize];
public boolean eof;
private final byte[] bytes = new byte[bufferSize];
private Exception failure;
private ByteBuffer buffer;
@Override
public boolean hasNext()
{
return !eof;
try
{
int read = stream.read(bytes);
if (read > 0)
{
buffer = onRead(bytes, 0, read);
return true;
}
else if (read < 0)
{
return false;
}
else
{
buffer = BufferUtil.EMPTY_BUFFER;
return true;
}
}
catch (Exception x)
{
if (failure == null)
{
failure = x;
return true;
}
return false;
}
}
@Override
public ByteBuffer next()
{
try
{
int read = stream.read(buffer);
if (read > 0)
{
return ByteBuffer.wrap(buffer, 0, read);
}
else if (read < 0)
{
if (eof)
throw new NoSuchElementException();
eof = true;
return BufferUtil.EMPTY_BUFFER;
}
else
{
return BufferUtil.EMPTY_BUFFER;
}
}
catch (IOException x)
{
throw (NoSuchElementException)new NoSuchElementException().initCause(x);
}
ByteBuffer result = buffer;
buffer = null;
if (failure != null)
throw (NoSuchElementException)new NoSuchElementException().initCause(failure);
if (result == null)
throw new NoSuchElementException();
return result;
}
@Override

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.client.util;
import java.nio.ByteBuffer;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.client.Schedulable;
@ -38,14 +39,14 @@ public class TimedResponseListener implements Response.Listener, Schedulable, Ru
private final long timeout;
private final TimeUnit unit;
private final Request request;
private final Response.Listener delegate;
private final Response.CompleteListener delegate;
public TimedResponseListener(long timeout, TimeUnit unit, Request request)
{
this(timeout, unit, request, new Empty());
}
public TimedResponseListener(long timeout, TimeUnit unit, Request request, Response.Listener delegate)
public TimedResponseListener(long timeout, TimeUnit unit, Request request, Response.CompleteListener delegate)
{
this.timeout = timeout;
this.unit = unit;
@ -56,31 +57,36 @@ public class TimedResponseListener implements Response.Listener, Schedulable, Ru
@Override
public void onBegin(Response response)
{
delegate.onBegin(response);
if (delegate instanceof Response.BeginListener)
((Response.BeginListener)delegate).onBegin(response);
}
@Override
public void onHeaders(Response response)
{
delegate.onHeaders(response);
if (delegate instanceof Response.HeadersListener)
((Response.HeadersListener)delegate).onHeaders(response);
}
@Override
public void onContent(Response response, ByteBuffer content)
{
delegate.onContent(response, content);
if (delegate instanceof Response.ContentListener)
((Response.ContentListener)delegate).onContent(response, content);
}
@Override
public void onSuccess(Response response)
{
delegate.onSuccess(response);
if (delegate instanceof Response.SuccessListener)
((Response.SuccessListener)delegate).onSuccess(response);
}
@Override
public void onFailure(Response response, Throwable failure)
{
delegate.onFailure(response, failure);
if (delegate instanceof Response.FailureListener)
((Response.FailureListener)delegate).onFailure(response, failure);
}
@Override
@ -111,7 +117,7 @@ public class TimedResponseListener implements Response.Listener, Schedulable, Ru
@Override
public void run()
{
request.abort("Total timeout elapsed");
request.abort(new TimeoutException("Total timeout elapsed"));
}
public boolean cancel()

View File

@ -408,7 +408,7 @@ public class HttpClientContinueTest extends AbstractHttpClientServerTest
@Override
public void onBegin(Response response)
{
response.abort(null);
response.abort(new Exception());
}
@Override

View File

@ -151,7 +151,7 @@ public class HttpClientTimeoutTest extends AbstractHttpClientServerTest
TimeUnit.MILLISECONDS.sleep(2 * timeout);
Assert.assertFalse(request.isAborted());
Assert.assertNull(request.getAbortCause());
}
@Slow
@ -208,7 +208,7 @@ public class HttpClientTimeoutTest extends AbstractHttpClientServerTest
TimeUnit.MILLISECONDS.sleep(2 * timeout);
Assert.assertFalse(request.isAborted());
Assert.assertNull(request.getAbortCause());
}
}

View File

@ -100,6 +100,21 @@ public class HttpCookieStoreTest
Assert.assertEquals("1", cookie.getValue());
}
@Test
public void testCookieStoredWithPathIsRetrievedWithChildPath() throws Exception
{
CookieStore cookies = new HttpCookieStore();
Destination destination = new HttpDestination(client, "http", "localhost", 80);
Assert.assertTrue(cookies.addCookie(destination, new HttpCookie("a", "1", null, "/path")));
List<HttpCookie> result = cookies.findCookies(destination, "/path/child");
Assert.assertNotNull(result);
Assert.assertEquals(1, result.size());
HttpCookie cookie = result.get(0);
Assert.assertEquals("a", cookie.getName());
Assert.assertEquals("1", cookie.getValue());
}
@Test
public void testCookieStoredWithParentDomainIsRetrievedWithChildDomain() throws Exception
{
@ -118,6 +133,19 @@ public class HttpCookieStoreTest
Assert.assertEquals(2, result.size());
}
@Test
public void testCookieStoredWithChildDomainIsNotRetrievedWithParentDomain() throws Exception
{
CookieStore cookies = new HttpCookieStore();
Destination childDestination = new HttpDestination(client, "http", "child.localhost.org", 80);
Assert.assertTrue(cookies.addCookie(childDestination, new HttpCookie("b", "2", null, "/")));
Destination parentDestination = new HttpDestination(client, "http", "localhost.org", 80);
List<HttpCookie> result = cookies.findCookies(parentDestination, "/path");
Assert.assertNotNull(result);
Assert.assertEquals(0, result.size());
}
@Test
public void testExpiredCookieIsNotRetrieved() throws Exception
{

View File

@ -33,7 +33,6 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.util.ByteBufferContentProvider;
import org.eclipse.jetty.server.HttpChannel;
@ -42,7 +41,6 @@ import org.eclipse.jetty.toolchain.test.annotation.Slow;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.log.StdErrLog;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Test;
@ -58,6 +56,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
{
start(new EmptyServerHandler());
final Throwable cause = new Exception();
final AtomicBoolean begin = new AtomicBoolean();
try
{
@ -68,7 +67,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
@Override
public void onQueued(Request request)
{
request.abort(null);
request.abort(cause);
}
@Override
@ -82,7 +81,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
}
catch (ExecutionException x)
{
Assert.assertThat(x.getCause(), Matchers.instanceOf(HttpResponseException.class));
Assert.assertSame(cause, x.getCause());
Assert.assertFalse(begin.get());
}
}
@ -93,6 +92,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
{
start(new EmptyServerHandler());
final Throwable cause = new Exception();
final CountDownLatch aborted = new CountDownLatch(1);
final CountDownLatch headers = new CountDownLatch(1);
try
@ -104,7 +104,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
@Override
public void onBegin(Request request)
{
if (request.abort(null))
if (request.abort(cause))
aborted.countDown();
}
@ -119,7 +119,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
}
catch (ExecutionException x)
{
Assert.assertThat(x.getCause(), Matchers.instanceOf(HttpResponseException.class));
Assert.assertSame(cause, x.getCause());
Assert.assertTrue(aborted.await(5, TimeUnit.SECONDS));
Assert.assertFalse(headers.await(1, TimeUnit.SECONDS));
}
@ -134,6 +134,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
// A) the request is failed before the response arrived, then we get an ExecutionException
// B) the request is failed after the response arrived, we get the 200 OK
final Throwable cause = new Exception();
final CountDownLatch aborted = new CountDownLatch(1);
try
{
@ -144,7 +145,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
@Override
public void onHeaders(Request request)
{
if (request.abort(null))
if (request.abort(cause))
aborted.countDown();
}
})
@ -154,7 +155,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
}
catch (ExecutionException x)
{
Assert.assertThat(x.getCause(), Matchers.instanceOf(HttpResponseException.class));
Assert.assertSame(cause, x.getCause());
Assert.assertTrue(aborted.await(5, TimeUnit.SECONDS));
}
}
@ -181,12 +182,8 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
}
});
// Test can behave in 3 ways:
// A) non-SSL, if the request is failed before the response arrived, then we get an ExecutionException
// B) non-SSL, if the request is failed after the response arrived, then we get the 500
// C) SSL, the server tries to write the 500, but the connection is already closed, the client
// reads -1 with a pending exchange and fails the response with an EOFException
StdErrLog.getLogger(HttpChannel.class).setHideStacks(true);
final Throwable cause = new Exception();
try
{
client.newRequest("localhost", connector.getLocalPort())
@ -196,7 +193,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
@Override
public void onHeaders(Request request)
{
request.abort(null);
request.abort(cause);
}
})
.content(new ByteBufferContentProvider(ByteBuffer.wrap(new byte[]{0}), ByteBuffer.wrap(new byte[]{1}))
@ -212,24 +209,15 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
}
catch (ExecutionException x)
{
Throwable cause = x.getCause();
if (cause instanceof EOFException)
Throwable abort = x.getCause();
if (abort instanceof EOFException)
{
// Server closed abruptly, behavior C
// Server closed abruptly
System.err.println("C");
}
else if (cause instanceof HttpRequestException)
else if (abort == cause)
{
// Request failed, behavior B
HttpRequestException xx = (HttpRequestException)cause;
Request request = xx.getRequest();
Assert.assertNotNull(request);
}
else if (cause instanceof HttpResponseException)
{
// Response failed, behavior A
HttpResponseException xx = (HttpResponseException)cause;
Response response = xx.getResponse();
Assert.assertNotNull(response);
// Expected
}
else
{
@ -270,7 +258,8 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
TimeUnit.MILLISECONDS.sleep(delay);
request.abort(null);
Throwable cause = new Exception();
request.abort(cause);
try
{
@ -278,7 +267,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
}
catch (ExecutionException x)
{
Assert.assertThat(x.getCause(), Matchers.instanceOf(HttpResponseException.class));
Assert.assertSame(cause, x.getCause());
}
}
@ -296,6 +285,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
}
});
final Throwable cause = new Exception();
client.getProtocolHandlers().clear();
client.getProtocolHandlers().add(new RedirectProtocolHandler(client)
{
@ -304,7 +294,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
{
// Abort the request after the 3xx response but before issuing the next request
if (!result.isFailed())
result.getRequest().abort(null);
result.getRequest().abort(cause);
super.onComplete(result);
}
});
@ -320,7 +310,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
}
catch (ExecutionException x)
{
Assert.assertThat(x.getCause(), Matchers.instanceOf(HttpResponseException.class));
Assert.assertSame(cause, x.getCause());
}
}
}

View File

@ -60,7 +60,7 @@ public class HttpResponseAbortTest extends AbstractHttpClientServerTest
@Override
public void onBegin(Response response)
{
response.abort(null);
response.abort(new Exception());
}
})
.send(new Response.CompleteListener()
@ -88,7 +88,7 @@ public class HttpResponseAbortTest extends AbstractHttpClientServerTest
@Override
public void onHeaders(Response response)
{
response.abort(null);
response.abort(new Exception());
}
})
.send(new Response.CompleteListener()
@ -136,7 +136,7 @@ public class HttpResponseAbortTest extends AbstractHttpClientServerTest
@Override
public void onContent(Response response, ByteBuffer content)
{
response.abort(null);
response.abort(new Exception());
}
})
.send(new Response.CompleteListener()

View File

@ -18,6 +18,7 @@
package org.eclipse.jetty.client.api;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.file.Paths;
import java.util.concurrent.CountDownLatch;
@ -26,9 +27,10 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.util.BasicAuthentication;
import org.eclipse.jetty.client.util.BlockingResponseListener;
import org.eclipse.jetty.client.util.InputStreamContentProvider;
import org.eclipse.jetty.client.util.InputStreamResponseListener;
import org.eclipse.jetty.client.util.PathContentProvider;
import org.eclipse.jetty.http.HttpCookie;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpVersion;
@ -43,14 +45,16 @@ public class Usage
public void testGETBlocking_ShortAPI() throws Exception
{
HttpClient client = new HttpClient();
client.start();
Future<ContentResponse> responseFuture = client.GET("http://localhost:8080/foo");
// Block to get the response
Response response = responseFuture.get();
// Verify response status code
Assert.assertEquals(200, response.getStatus());
// Headers abstraction needed for:
// 1. case insensitivity
// 2. multi values
// 3. value conversion
// Reuse SPDY's ?
// Access headers
response.getHeaders().get("Content-Length");
}
@ -58,6 +62,8 @@ public class Usage
public void testGETBlocking() throws Exception
{
HttpClient client = new HttpClient();
client.start();
// Address must be provided, it's the only thing non defaultable
Request request = client.newRequest("localhost", 8080)
.scheme("https")
@ -67,11 +73,12 @@ public class Usage
.param("a", "b")
.header("X-Header", "Y-value")
.agent("Jetty HTTP Client")
// .decoder(null)
.content(null)
.idleTimeout(5000L);
Future<ContentResponse> responseFuture = request.send();
// Block to get the response
Response response = responseFuture.get();
Assert.assertEquals(200, response.getStatus());
}
@ -79,17 +86,26 @@ public class Usage
public void testGETAsync() throws Exception
{
HttpClient client = new HttpClient();
client.start();
final AtomicReference<Response> responseRef = new AtomicReference<>();
final CountDownLatch latch = new CountDownLatch(1);
client.newRequest("localhost", 8080).send(new Response.Listener.Empty()
client.newRequest("localhost", 8080)
// Send asynchronously
.send(new Response.CompleteListener()
{
@Override
public void onSuccess(Response response)
public void onComplete(Result result)
{
responseRef.set(response);
latch.countDown();
if (!result.isFailed())
{
responseRef.set(result.getResponse());
latch.countDown();
}
}
});
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
Response response = responseRef.get();
Assert.assertNotNull(response);
@ -97,9 +113,12 @@ public class Usage
}
@Test
public void testPOSTWithParams() throws Exception
public void testPOSTWithParams_ShortAPI() throws Exception
{
HttpClient client = new HttpClient();
client.start();
// One liner to POST
client.POST("http://localhost:8080").param("a", "\u20AC").send();
}
@ -107,7 +126,10 @@ public class Usage
public void testRequestListener() throws Exception
{
HttpClient client = new HttpClient();
client.start();
Response response = client.newRequest("localhost", 8080)
// Add a request listener
.listener(new Request.Listener.Empty()
{
@Override
@ -122,12 +144,19 @@ public class Usage
public void testRequestWithExplicitConnectionControl() throws Exception
{
HttpClient client = new HttpClient();
client.start();
// Create an explicit connection, and use try-with-resources to manage it
try (Connection connection = client.getDestination("http", "localhost", 8080).newConnection().get(5, TimeUnit.SECONDS))
{
Request request = client.newRequest("localhost", 8080);
// Asynchronous send but using BlockingResponseListener
BlockingResponseListener listener = new BlockingResponseListener(request);
connection.send(request, listener);
// Wait for the response on the listener
Response response = listener.get(5, TimeUnit.SECONDS);
Assert.assertNotNull(response);
Assert.assertEquals(200, response.getStatus());
}
@ -137,8 +166,11 @@ public class Usage
public void testFileUpload() throws Exception
{
HttpClient client = new HttpClient();
Response response = client.newRequest("localhost", 8080)
.content(new PathContentProvider(Paths.get(""))).send().get();
client.start();
// One liner to upload files
Response response = client.newRequest("localhost", 8080).file(Paths.get("file_to_upload.txt")).send().get();
Assert.assertEquals(200, response.getStatus());
}
@ -146,37 +178,68 @@ public class Usage
public void testCookie() throws Exception
{
HttpClient client = new HttpClient();
client.start();
// Set a cookie to be sent in requests that match the cookie's domain
client.getCookieStore().addCookie(client.getDestination("http", "host", 8080), new HttpCookie("name", "value"));
// Send a request for the cookie's domain
Response response = client.newRequest("host", 8080).send().get();
Assert.assertEquals(200, response.getStatus());
}
// @Test
// public void testAuthentication() throws Exception
// {
// HTTPClient client = new HTTPClient();
// client.newRequest("localhost", 8080).authentication(new Authentication.Kerberos()).build().send().get().status(); // 200
// }
@Test
public void testBasicAuthentication() throws Exception
{
HttpClient client = new HttpClient();
client.start();
String uri = "http://localhost:8080/secure";
// Setup Basic authentication credentials for TestRealm
client.getAuthenticationStore().addAuthentication(new BasicAuthentication(uri, "TestRealm", "username", "password"));
// One liner to send the request
ContentResponse response = client.newRequest(uri).send().get(5, TimeUnit.SECONDS);
Assert.assertEquals(200, response.getStatus());
}
@Test
public void testFollowRedirects() throws Exception
{
HttpClient client = new HttpClient();
client.start();
// Do not follow redirects by default
client.setFollowRedirects(false);
client.newRequest("localhost", 8080).followRedirects(true).send().get().getStatus(); // 200
ContentResponse response = client.newRequest("localhost", 8080)
// Follow redirects for this request only
.followRedirects(true)
.send()
.get(5, TimeUnit.SECONDS);
Assert.assertEquals(200, response.getStatus());
}
@Test
public void testResponseInputStream() throws Exception
{
HttpClient client = new HttpClient();
client.start();
InputStreamResponseListener listener = new InputStreamResponseListener();
// Send asynchronously with the InputStreamResponseListener
client.newRequest("localhost", 8080).send(listener);
// Call to get() blocks until the headers arrived
// Call to the listener's get() blocks until the headers arrived
Response response = listener.get(5, TimeUnit.SECONDS);
// Now check the response information that arrived to decide whether to read the content
if (response.getStatus() == 200)
{
// Solution 1: use input stream
byte[] buffer = new byte[256];
try (InputStream input = listener.getInputStream())
{
@ -185,37 +248,30 @@ public class Usage
int read = input.read(buffer);
if (read < 0)
break;
// No need for output stream; for example, parse bytes
// Do something with the bytes just read
}
}
}
else
{
response.abort(null);
response.abort(new Exception());
}
}
// @Test
// public void testResponseOutputStream() throws Exception
// {
// HttpClient client = new HttpClient();
//
// try (FileOutputStream output = new FileOutputStream(""))
// {
// OutputStreamResponseListener listener = new OutputStreamResponseListener(output);
// client.newRequest("localhost", 8080).send(listener);
// // Call to get() blocks until the headers arrived
// Response response = listener.get(5, TimeUnit.SECONDS);
// if (response.status() == 200)
// {
// // Solution 2: write to output stream
// {
// listener.writeTo(output);
// }
// }
// else
// {
// response.abort();
// }
// }
@Test
public void testRequestInputStream() throws Exception
{
HttpClient client = new HttpClient();
client.start();
InputStream input = new ByteArrayInputStream("content".getBytes("UTF-8"));
ContentResponse response = client.newRequest("localhost", 8080)
// Provide the content as InputStream
.content(new InputStreamContentProvider(input))
.send()
.get(5, TimeUnit.SECONDS);
Assert.assertEquals(200, response.getStatus());
}
}

View File

@ -133,12 +133,24 @@
</goals>
<configuration>
<includeGroupIds>org.eclipse.jetty</includeGroupIds>
<excludeGroupIds>org.eclipse.jetty.orbit,org.eclipse.jetty.spdy</excludeGroupIds>
<excludeGroupIds>org.eclipse.jetty.orbit,org.eclipse.jetty.spdy,org.eclipse.jetty.websocket,org.eclipse.jetty.drafts</excludeGroupIds>
<excludeArtifactIds>jetty-all,jetty-start,jetty-monitor,jetty-jsp</excludeArtifactIds>
<includeTypes>jar</includeTypes>
<outputDirectory>${assembly-directory}/lib</outputDirectory>
</configuration>
</execution>
<execution>
<id>copy-lib-websocket-deps</id>
<phase>generate-resources</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<includeGroupIds>org.eclipse.jetty.websocket,org.eclipse.jetty.drafts</includeGroupIds>
<includeTypes>jar</includeTypes>
<outputDirectory>${assembly-directory}/lib/websocket</outputDirectory>
</configuration>
</execution>
<execution>
<id>copy-orbit-servlet-api-deps</id>
<phase>generate-resources</phase>
@ -346,6 +358,11 @@
<artifactId>jetty-servlets</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-client</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-server</artifactId>

View File

@ -138,18 +138,27 @@ etc/jetty-http.xml
#===========================================================
# SPDY Connector
# SPDY should not be used with HTTPS as the SPDY connector
# also provides HTTPS.
# The NPN jar must be separately downloaded from
# http://repo1.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.0.v20120525/
# Must also enable --exec or use --exec-print (see above) or
# add -Xbootclasspath/p:lib/npn-boot-1.1.0.v20120525.jar to
# the java commandline you use to start jetty.
# Make sure that you either reconfigure ports or comment
# jetty-http.xml/jetty-https.xml to avoid port conflicts.
#
# SPDY should not be used with HTTPS. Use HTTPS or SPDY, but
# not both, as SPDY also provides HTTPS support.
#
# SPDY requires the NPN jar iwhich must be separately downloaded:
#
# http://repo1.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.1.v20121030/npn-boot-1.1.1.v20121030.jar
#
# Which should be saved in lib/npn-boot-1.1.1.v20121030.jar
#
# To include the NPN jar on the boot path, you must either:
#
# a) enable --exec above and uncomment the -Xbootclass line
# below
#
# b) Add -Xbootclasspath/p:lib/npn-boot-1.1.1.v20121030.jar
# to the command line when running jetty.
#
#-----------------------------------------------------------
# OPTIONS=spdy
# -Xbootclasspath/p:lib/npn-boot-1.1.0.v20120525.jar
# -Xbootclasspath/p:lib/npn-boot-1.1.1.v20121030.jar
# etc/jetty-spdy.xml
#===========================================================

View File

@ -36,6 +36,7 @@ public interface HttpContent
String getLastModified();
ByteBuffer getIndirectBuffer();
ByteBuffer getDirectBuffer();
String getETag();
Resource getResource();
long getContentLength();
InputStream getInputStream() throws IOException;
@ -50,19 +51,33 @@ public interface HttpContent
final Resource _resource;
final String _mimeType;
final int _maxBuffer;
final String _etag;
/* ------------------------------------------------------------ */
public ResourceAsHttpContent(final Resource resource, final String mimeType)
{
_resource=resource;
_mimeType=mimeType;
_maxBuffer=-1;
this(resource,mimeType,-1,false);
}
/* ------------------------------------------------------------ */
public ResourceAsHttpContent(final Resource resource, final String mimeType, int maxBuffer)
{
this(resource,mimeType,maxBuffer,false);
}
/* ------------------------------------------------------------ */
public ResourceAsHttpContent(final Resource resource, final String mimeType, boolean etag)
{
this(resource,mimeType,-1,etag);
}
/* ------------------------------------------------------------ */
public ResourceAsHttpContent(final Resource resource, final String mimeType, int maxBuffer, boolean etag)
{
_resource=resource;
_mimeType=mimeType;
_maxBuffer=maxBuffer;
_etag=etag?resource.getWeakETag():null;
}
/* ------------------------------------------------------------ */
@ -85,6 +100,13 @@ public interface HttpContent
{
return null;
}
/* ------------------------------------------------------------ */
@Override
public String getETag()
{
return _etag;
}
/* ------------------------------------------------------------ */
@Override

View File

@ -562,7 +562,7 @@ public class HttpParser
switch (_header)
{
case CONTENT_LENGTH:
if (_endOfContent != EndOfContent.CHUNKED_CONTENT && _responseStatus!=304 && _responseStatus!=204 && (_responseStatus<100 || _responseStatus>=200))
if (_endOfContent != EndOfContent.CHUNKED_CONTENT)
{
try
{
@ -640,7 +640,6 @@ public class HttpParser
_header=null;
_value=null;
// now handle the ch
if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
{
@ -657,8 +656,15 @@ public class HttpParser
return true;
}
// so work out the _content demarcation
if (_endOfContent == EndOfContent.UNKNOWN_CONTENT)
// is it a response that cannot have a body?
if (_responseHandler !=null && // response
(_responseStatus == 304 || // not-modified response
_responseStatus == 204 || // no-content response
_responseStatus < 200)) // 1xx response
_endOfContent=EndOfContent.NO_CONTENT; // ignore any other headers set
// else if we don't know framing
else if (_endOfContent == EndOfContent.UNKNOWN_CONTENT)
{
if (_responseStatus == 0 // request
|| _responseStatus == 304 // not-modified response

View File

@ -470,6 +470,25 @@ public class HttpParserTest
assertTrue(_messageCompleted);
}
@Test
public void testResponse101WithTransferEncoding() throws Exception
{
ByteBuffer buffer= BufferUtil.toBuffer(
"HTTP/1.1 101 switching protocols\015\012"
+ "Transfer-Encoding: chunked\015\012"
+ "\015\012");
Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.ResponseHandler)handler);
parser.parseNext(buffer);
assertEquals("HTTP/1.1", _methodOrVersion);
assertEquals("101", _uriOrStatus);
assertEquals("switching protocols", _versionOrReason);
assertEquals(null,_content);
assertTrue(_headerCompleted);
assertTrue(_messageCompleted);
}
@Test
public void testSeekEOF() throws Exception
{

View File

@ -21,38 +21,19 @@ package org.eclipse.jetty.io;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.Scheduler;
public abstract class AbstractEndPoint implements EndPoint
public abstract class AbstractEndPoint extends IdleTimeout implements EndPoint
{
private static final Logger LOG = Log.getLogger(AbstractEndPoint.class);
private final long _created=System.currentTimeMillis();
private final InetSocketAddress _local;
private final InetSocketAddress _remote;
private final Scheduler _scheduler;
private final AtomicReference<Scheduler.Task> _timeout = new AtomicReference<>();
private final Runnable _idleTask = new Runnable()
{
@Override
public void run()
{
long idleLeft=checkIdleTimeout();
if (idleLeft>=0)
scheduleIdleTimeout(idleLeft > 0 ? idleLeft : getIdleTimeout());
}
};
private volatile long _idleTimeout;
private volatile long _idleTimestamp=System.currentTimeMillis();
private volatile Connection _connection;
private final FillInterest _fillInterest = new FillInterest()
@ -74,9 +55,9 @@ public abstract class AbstractEndPoint implements EndPoint
protected AbstractEndPoint(Scheduler scheduler,InetSocketAddress local,InetSocketAddress remote)
{
super(scheduler);
_local=local;
_remote=remote;
_scheduler=scheduler;
}
@Override
@ -85,19 +66,6 @@ public abstract class AbstractEndPoint implements EndPoint
return _created;
}
@Override
public long getIdleTimeout()
{
return _idleTimeout;
}
@Override
public void setIdleTimeout(long idleTimeout)
{
_idleTimeout = idleTimeout;
}
@Override
public InetSocketAddress getLocalAddress()
{
@ -109,17 +77,7 @@ public abstract class AbstractEndPoint implements EndPoint
{
return _remote;
}
public long getIdleTimestamp()
{
return _idleTimestamp;
}
protected void notIdle()
{
_idleTimestamp=System.currentTimeMillis();
}
@Override
public Connection getConnection()
{
@ -145,6 +103,12 @@ public abstract class AbstractEndPoint implements EndPoint
_writeFlusher.onClose();
_fillInterest.onClose();
}
@Override
public void close()
{
super.close();
}
@Override
public <C> void fillInterested(C context, Callback<C> callback) throws IllegalStateException
@ -173,56 +137,23 @@ public abstract class AbstractEndPoint implements EndPoint
return _writeFlusher;
}
protected void scheduleIdleTimeout(long delay)
@Override
protected void onIdleExpired(TimeoutException timeout)
{
Scheduler.Task newTimeout = null;
if (isOpen() && delay > 0 && _scheduler!=null)
newTimeout = _scheduler.schedule(_idleTask, delay, TimeUnit.MILLISECONDS);
Scheduler.Task oldTimeout = _timeout.getAndSet(newTimeout);
if (oldTimeout != null)
oldTimeout.cancel();
}
protected long checkIdleTimeout()
{
if (isOpen())
if (isOutputShutdown() || _fillInterest.isInterested() || _writeFlusher.isInProgress())
{
long idleTimestamp = getIdleTimestamp();
long idleTimeout = getIdleTimeout();
long idleElapsed = System.currentTimeMillis() - idleTimestamp;
long idleLeft = idleTimeout - idleElapsed;
LOG.debug("{} idle timeout check, elapsed: {} ms, remaining: {} ms", this, idleElapsed, idleLeft);
if (isOutputShutdown() || _fillInterest.isInterested() || _writeFlusher.isInProgress())
{
if (idleTimestamp != 0 && idleTimeout > 0)
{
if (idleLeft <= 0)
{
LOG.debug("{} idle timeout expired", this);
boolean output_shutdown=isOutputShutdown();
TimeoutException timeout = new TimeoutException("Idle timeout expired: " + idleElapsed + "/" + idleTimeout + " ms");
_fillInterest.onFail(timeout);
_writeFlusher.onFail(timeout);
if (output_shutdown)
close();
notIdle();
}
}
}
return idleLeft>=0?idleLeft:0;
boolean output_shutdown=isOutputShutdown();
_fillInterest.onFail(timeout);
_writeFlusher.onFail(timeout);
if (output_shutdown)
close();
}
return -1;
}
@Override
public String toString()
{
return String.format("%s@%x{%s<r-l>%s,o=%b,is=%b,os=%b,fi=%s,wf=%s,to=%d}{%s}",
return String.format("%s@%x{%s<r-l>%s,o=%b,is=%b,os=%b,fi=%s,wf=%s,it=%d}{%s}",
getClass().getSimpleName(),
hashCode(),
getRemoteAddress(),
@ -232,7 +163,7 @@ public abstract class AbstractEndPoint implements EndPoint
isOutputShutdown(),
_fillInterest,
_writeFlusher,
_idleTimeout,
getIdleTimeout(),
getConnection());
}
}

View File

@ -0,0 +1,153 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.io;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.Scheduler;
/* ------------------------------------------------------------ */
/** An Abstract implementation of an Idle Timeout.
*
* This implementation is optimised that timeout operations are not cancelled on
* every operation. Rather timeout are allowed to expire and a check is then made
* to see when the last operation took place. If the idle timeout has not expired,
* the timeout is rescheduled for the earliest possible time a timeout could occur.
*
*/
public abstract class IdleTimeout
{
private static final Logger LOG = Log.getLogger(IdleTimeout.class);
private final Scheduler _scheduler;
private final AtomicReference<Scheduler.Task> _timeout = new AtomicReference<>();
private volatile long _idleTimeout;
private volatile long _idleTimestamp=System.currentTimeMillis();
private final Runnable _idleTask = new Runnable()
{
@Override
public void run()
{
long idleLeft=checkIdleTimeout();
if (idleLeft>=0)
scheduleIdleTimeout(idleLeft > 0 ? idleLeft : getIdleTimeout());
}
};
/* ------------------------------------------------------------ */
/**
* @param scheduler A scheduler used to schedule checks for the idle timeout.
*/
public IdleTimeout(Scheduler scheduler)
{
_scheduler=scheduler;
}
public long getIdleTimestamp()
{
return _idleTimestamp;
}
public long getIdleTimeout()
{
return _idleTimeout;
}
public void setIdleTimeout(long idleTimeout)
{
_idleTimeout = idleTimeout;
}
/* ------------------------------------------------------------ */
/** This method should be called when non-idle activity has taken place.
*/
public void notIdle()
{
_idleTimestamp=System.currentTimeMillis();
}
protected void scheduleIdleTimeout(long delay)
{
Scheduler.Task newTimeout = null;
if (isOpen() && delay > 0 && _scheduler!=null)
newTimeout = _scheduler.schedule(_idleTask, delay, TimeUnit.MILLISECONDS);
Scheduler.Task oldTimeout = _timeout.getAndSet(newTimeout);
if (oldTimeout != null)
oldTimeout.cancel();
}
protected void close()
{
Scheduler.Task oldTimeout = _timeout.getAndSet(null);
if (oldTimeout != null)
oldTimeout.cancel();
}
protected long checkIdleTimeout()
{
if (isOpen())
{
long idleTimestamp = getIdleTimestamp();
long idleTimeout = getIdleTimeout();
long idleElapsed = System.currentTimeMillis() - idleTimestamp;
long idleLeft = idleTimeout - idleElapsed;
LOG.debug("{} idle timeout check, elapsed: {} ms, remaining: {} ms", this, idleElapsed, idleLeft);
if (idleTimestamp != 0 && idleTimeout > 0)
{
if (idleLeft <= 0)
{
LOG.debug("{} idle timeout expired", this);
try
{
onIdleExpired(new TimeoutException("Idle timeout expired: " + idleElapsed + "/" + idleTimeout + " ms"));
}
finally
{
notIdle();
}
}
}
return idleLeft>=0?idleLeft:0;
}
return -1;
}
/* ------------------------------------------------------------ */
/** This abstract method is called when the idle timeout has expired.
* @param timeout a TimeoutException
*/
abstract protected void onIdleExpired(TimeoutException timeout);
/* ------------------------------------------------------------ */
/** This abstract method should be called to check if idle timeouts
* should still be checked.
* @return True if the entity monitored should still be checked for idle timeouts
*/
abstract protected boolean isOpen();
}

View File

@ -19,6 +19,7 @@
package org.eclipse.jetty.security.jaspi;
import java.security.Principal;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -137,7 +138,7 @@ public class JaspiAuthenticatorFactory extends DefaultAuthenticatorFactory
{
if (_serviceSubject!=null)
return _serviceSubject;
List subjects = server.getBeans(Subject.class);
List<Subject> subjects = (List<Subject>)server.getBeans(Subject.class);
if (subjects.size()>0)
return (Subject)subjects.get(0);
return null;

View File

@ -31,7 +31,7 @@ import javax.security.auth.message.MessagePolicy;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpHeaders;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -75,7 +75,7 @@ public class BasicAuthModule extends BaseAuthModule
{
HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage();
HttpServletResponse response = (HttpServletResponse) messageInfo.getResponseMessage();
String credentials = request.getHeader(HttpHeaders.AUTHORIZATION);
String credentials = request.getHeader(HttpHeader.AUTHORIZATION.asString());
try
{
@ -87,7 +87,7 @@ public class BasicAuthModule extends BaseAuthModule
}
if (!isMandatory(messageInfo)) { return AuthStatus.SUCCESS; }
response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "basic realm=\"" + realmName + '"');
response.setHeader(HttpHeader.WWW_AUTHENTICATE.asString(), "basic realm=\"" + realmName + '"');
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
return AuthStatus.SEND_CONTINUE;
}

View File

@ -32,7 +32,7 @@ import javax.security.auth.message.MessagePolicy;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpHeaders;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.util.security.Credential;
import org.eclipse.jetty.util.B64Code;
@ -87,7 +87,7 @@ public class DigestAuthModule extends BaseAuthModule
{
HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage();
HttpServletResponse response = (HttpServletResponse) messageInfo.getResponseMessage();
String credentials = request.getHeader(HttpHeaders.AUTHORIZATION);
String credentials = request.getHeader(HttpHeader.AUTHORIZATION.asString());
try
{
@ -155,7 +155,7 @@ public class DigestAuthModule extends BaseAuthModule
if (!isMandatory(messageInfo)) { return AuthStatus.SUCCESS; }
String domain = request.getContextPath();
if (domain == null) domain = "/";
response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "Digest realm=\"" + realmName
response.setHeader(HttpHeader.WWW_AUTHENTICATE.asString(), "Digest realm=\"" + realmName
+ "\", domain=\""
+ domain
+ "\", nonce=\""

View File

@ -87,6 +87,11 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jaas</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-plus</artifactId>

View File

@ -54,11 +54,17 @@ import org.eclipse.jetty.xml.XmlConfiguration;
/**
* AbstractJettyMojo
*
*
* Common base class for most jetty mojos.
*
*
*/
public abstract class AbstractJettyMojo extends AbstractMojo
{
/**
*
*/
public String PORT_SYSPROPERTY = "jetty.port";
/**
* Whether or not to include dependencies on the plugin's classpath with &lt;scope&gt;provided&lt;/scope&gt;
@ -79,7 +85,6 @@ public abstract class AbstractJettyMojo extends AbstractMojo
protected String[] excludedGoals;
/**
* List of connectors to use. If none are configured
* then the default is a single SelectChannelConnector at port 8080. You can
@ -114,7 +119,6 @@ public abstract class AbstractJettyMojo extends AbstractMojo
protected LoginService[] loginServices;
/**
* A RequestLog implementation to use for the webapp at runtime.
* Consider using instead the &lt;jettyXml&gt; element to specify external jetty xml config file.
@ -126,7 +130,6 @@ public abstract class AbstractJettyMojo extends AbstractMojo
protected RequestLog requestLog;
/**
* An instance of org.eclipse.jetty.webapp.WebAppContext that represents the webapp.
* Use any of its setters to configure the webapp. This is the preferred and most
@ -138,32 +141,6 @@ public abstract class AbstractJettyMojo extends AbstractMojo
protected JettyWebAppContext webApp;
/**
* The context path for the webapp. Defaults to the
* name of the webapp's artifact.
*
* @deprecated Use &lt;webApp&gt;&lt;contextPath&gt; instead.
* @parameter expression="/${project.artifactId}"
* @required
* @readonly
*/
protected String contextPath;
/**
* The temporary directory to use for the webapp.
* Defaults to target/tmp.
*
* @deprecated Use %lt;webApp&gt;&lt;tempDirectory&gt; instead.
* @parameter expression="${project.build.directory}/tmp"
* @required
* @readonly
*/
protected File tmpDirectory;
/**
* The interval in seconds to scan the webapp for changes
* and restart the context if necessary. Ignored if reload
@ -184,6 +161,7 @@ public abstract class AbstractJettyMojo extends AbstractMojo
* @parameter expression="${jetty.reload}" default-value="automatic"
*/
protected String reload;
/**
* File containing system properties to be set before execution
@ -196,6 +174,7 @@ public abstract class AbstractJettyMojo extends AbstractMojo
*/
protected File systemPropertiesFile;
/**
* System properties to set before execution.
* Note that these properties will NOT override System properties
@ -207,7 +186,6 @@ public abstract class AbstractJettyMojo extends AbstractMojo
protected SystemProperties systemProperties;
/**
* Comma separated list of a jetty xml configuration files whose contents
* will be applied before any plugin configuration. Optional.
@ -226,6 +204,7 @@ public abstract class AbstractJettyMojo extends AbstractMojo
*/
protected int stopPort;
/**
* Key to provide when stopping jetty on executing java -DSTOP.KEY=&lt;stopKey&gt;
* -DSTOP.PORT=&lt;stopPort&gt; -jar start.jar --stop
@ -234,6 +213,7 @@ public abstract class AbstractJettyMojo extends AbstractMojo
*/
protected String stopKey;
/**
* <p>
* Determines whether or not the server blocks when started. The default
@ -250,6 +230,7 @@ public abstract class AbstractJettyMojo extends AbstractMojo
*/
protected boolean daemon;
/**
* Skip this mojo execution.
*
@ -290,8 +271,7 @@ public abstract class AbstractJettyMojo extends AbstractMojo
* @parameter expression="${mojoExecution}"
* @readonly
*/
private org.apache.maven.plugin.MojoExecution execution;
protected org.apache.maven.plugin.MojoExecution execution;
/**
@ -300,8 +280,7 @@ public abstract class AbstractJettyMojo extends AbstractMojo
* @parameter expression="${plugin.artifacts}"
* @readonly
*/
private List pluginArtifacts;
protected List pluginArtifacts;
/**
@ -309,16 +288,19 @@ public abstract class AbstractJettyMojo extends AbstractMojo
*/
protected JettyServer server;
/**
* A scanner to check for changes to the webapp
*/
protected Scanner scanner;
/**
* List of files and directories to scan
*/
protected ArrayList<File> scanList;
/**
* List of Listeners for the scanner
*/
@ -329,14 +311,17 @@ public abstract class AbstractJettyMojo extends AbstractMojo
* A scanner to check ENTER hits on the console
*/
protected Thread consoleScanner;
public String PORT_SYSPROPERTY = "jetty.port";
public abstract void restartWebApp(boolean reconfigureScanner) throws Exception;
public abstract void checkPomConfiguration() throws MojoExecutionException;
public abstract void checkPomConfiguration() throws MojoExecutionException;
public abstract void configureScanner () throws MojoExecutionException;
@ -344,9 +329,12 @@ public abstract class AbstractJettyMojo extends AbstractMojo
/**
* @see org.apache.maven.plugin.Mojo#execute()
*/
public void execute() throws MojoExecutionException, MojoFailureException
{
getLog().info("Configuring Jetty for project: " + getProject().getName());
getLog().info("Configuring Jetty for project: " + this.project.getName());
if (skip)
{
getLog().info("Skipping Jetty start: jetty.skip==true");
@ -367,6 +355,11 @@ public abstract class AbstractJettyMojo extends AbstractMojo
}
/**
* @throws MojoExecutionException
*/
public void configurePluginClasspath() throws MojoExecutionException
{
//if we are configured to include the provided dependencies on the plugin's classpath
@ -407,6 +400,12 @@ public abstract class AbstractJettyMojo extends AbstractMojo
}
/**
* @param artifact
* @return
*/
public boolean isPluginArtifact(Artifact artifact)
{
if (pluginArtifacts == null || pluginArtifacts.isEmpty())
@ -424,6 +423,12 @@ public abstract class AbstractJettyMojo extends AbstractMojo
return isPluginArtifact;
}
/**
* @throws Exception
*/
public void finishConfigurationBeforeStart() throws Exception
{
HandlerCollection contexts = (HandlerCollection)server.getChildHandlerByClass(ContextHandlerCollection.class);
@ -439,6 +444,9 @@ public abstract class AbstractJettyMojo extends AbstractMojo
/**
* @throws Exception
*/
public void applyJettyXml() throws Exception
{
if (getJettyXmlFiles() == null)
@ -454,6 +462,10 @@ public abstract class AbstractJettyMojo extends AbstractMojo
/**
* @throws MojoExecutionException
*/
public void startJetty () throws MojoExecutionException
{
try
@ -462,13 +474,10 @@ public abstract class AbstractJettyMojo extends AbstractMojo
printSystemProperties();
this.server = new JettyServer();
setServer(this.server);
//apply any config from a jetty.xml file first which is able to
//be overwritten by config in the pom.xml
applyJettyXml ();
applyJettyXml ();
// if the user hasn't configured their project's pom to use a
// different set of connectors,
@ -488,10 +497,9 @@ public abstract class AbstractJettyMojo extends AbstractMojo
}
}
//set up a RequestLog if one is provided
if (this.requestLog != null)
getServer().setRequestLog(this.requestLog);
this.server.setRequestLog(this.requestLog);
//set up the webapp and any context provided
this.server.configureHandlers();
@ -502,7 +510,7 @@ public abstract class AbstractJettyMojo extends AbstractMojo
for (int i = 0; (this.loginServices != null) && i < this.loginServices.length; i++)
{
getLog().debug(this.loginServices[i].getClass().getName() + ": "+ this.loginServices[i].toString());
getServer().addBean(this.loginServices[i]);
this.server.addBean(this.loginServices[i]);
}
//do any other configuration required by the
@ -543,12 +551,12 @@ public abstract class AbstractJettyMojo extends AbstractMojo
{
getLog().info("Jetty server exiting.");
}
}
}
}
/**
* Subclasses should invoke this to setup basic info
* on the webapp
@ -571,22 +579,23 @@ public abstract class AbstractJettyMojo extends AbstractMojo
getLog().info("Applying context xml file "+contextXml);
xmlConfiguration.configure(webApp);
}
//If no contextPath was specified, go with our default
//If no contextPath was specified, go with default of project artifactid
String cp = webApp.getContextPath();
if (cp == null || "".equals(cp))
{
webApp.setContextPath((contextPath.startsWith("/") ? contextPath : "/"+ contextPath));
}
cp = "/"+project.getArtifactId();
webApp.setContextPath(cp);
}
//If no tmp directory was specified, and we have one, use it
if (webApp.getTempDirectory() == null && tmpDirectory != null)
if (webApp.getTempDirectory() == null)
{
if (!tmpDirectory.exists())
tmpDirectory.mkdirs();
webApp.setTempDirectory(tmpDirectory);
File target = new File(project.getBuild().getDirectory());
File tmp = new File(target,"tmp");
if (!tmp.exists())
tmp.mkdirs();
webApp.setTempDirectory(tmp);
}
getLog().info("Context path = " + webApp.getContextPath());
@ -595,6 +604,9 @@ public abstract class AbstractJettyMojo extends AbstractMojo
getLog().info("Web overrides = "+(webApp.getOverrideDescriptor()==null?" none":webApp.getOverrideDescriptor()));
}
/**
* Run a scanner thread on the given list of files and directories, calling
* stop/start on the given list of LifeCycle objects if any of the watched
@ -604,7 +616,7 @@ public abstract class AbstractJettyMojo extends AbstractMojo
private void startScanner() throws Exception
{
// check if scanning is enabled
if (getScanIntervalSeconds() <= 0) return;
if (scanIntervalSeconds <= 0) return;
// check if reload is manual. It disables file scanning
if ( "manual".equalsIgnoreCase( reload ) )
@ -617,17 +629,19 @@ public abstract class AbstractJettyMojo extends AbstractMojo
scanner = new Scanner();
scanner.setReportExistingFilesOnStartup(false);
scanner.setScanInterval(getScanIntervalSeconds());
scanner.setScanDirs(getScanList());
scanner.setScanInterval(scanIntervalSeconds);
scanner.setScanDirs(scanList);
scanner.setRecursive(true);
List listeners = getScannerListeners();
Iterator itor = (listeners==null?null:listeners.iterator());
Iterator itor = (this.scannerListeners==null?null:this.scannerListeners.iterator());
while (itor!=null && itor.hasNext())
scanner.addListener((Scanner.Listener)itor.next());
getLog().info("Starting scanner at interval of " + getScanIntervalSeconds()+ " seconds.");
getLog().info("Starting scanner at interval of " + scanIntervalSeconds + " seconds.");
scanner.start();
}
/**
* Run a thread that monitors the console input to detect ENTER hits.
*/
@ -641,6 +655,12 @@ public abstract class AbstractJettyMojo extends AbstractMojo
}
}
/**
*
*/
private void printSystemProperties ()
{
// print out which system properties were set up
@ -658,6 +678,9 @@ public abstract class AbstractJettyMojo extends AbstractMojo
}
}
/**
* Try and find a jetty-web.xml file, using some
* historical naming conventions if necessary.
@ -684,61 +707,12 @@ public abstract class AbstractJettyMojo extends AbstractMojo
}
public Scanner getScanner ()
{
return scanner;
}
public MavenProject getProject()
{
return this.project;
}
public void setProject(MavenProject project) {
this.project = project;
}
public File getTmpDirectory()
{
return this.tmpDirectory;
}
public void setTmpDirectory(File tmpDirectory) {
this.tmpDirectory = tmpDirectory;
}
/**
* @return Returns the contextPath.
* @param file
* @throws Exception
*/
public String getContextPath()
{
return this.contextPath;
}
public void setContextPath(String contextPath) {
this.contextPath = contextPath;
}
/**
* @return Returns the scanIntervalSeconds.
*/
public int getScanIntervalSeconds()
{
return this.scanIntervalSeconds;
}
public void setScanIntervalSeconds(int scanIntervalSeconds) {
this.scanIntervalSeconds = scanIntervalSeconds;
}
/**
* @return returns the path to the systemPropertiesFile
*/
public File getSystemPropertiesFile()
{
return this.systemPropertiesFile;
}
public void setSystemPropertiesFile(File file) throws Exception
{
this.systemPropertiesFile = file;
@ -760,10 +734,15 @@ public abstract class AbstractJettyMojo extends AbstractMojo
this.systemProperties.setSystemProperty(prop);
}
}
}
}
/**
* @param systemProperties
*/
public void setSystemProperties(SystemProperties systemProperties)
{
if (this.systemProperties == null)
@ -778,12 +757,16 @@ public abstract class AbstractJettyMojo extends AbstractMojo
}
}
}
public SystemProperties getSystemProperties ()
{
return this.systemProperties;
}
/**
* @return
*/
public List<File> getJettyXmlFiles()
{
if ( this.jettyXml == null )
@ -810,170 +793,12 @@ public abstract class AbstractJettyMojo extends AbstractMojo
return jettyXmlFiles;
}
public JettyServer getServer ()
{
return this.server;
}
public void setServer (JettyServer server)
{
this.server = server;
}
public void setScanList (ArrayList<File> list)
{
this.scanList = new ArrayList<File>(list);
}
public ArrayList<File> getScanList ()
{
return this.scanList;
}
public void setScannerListeners (ArrayList<Scanner.BulkListener> listeners)
{
this.scannerListeners = new ArrayList<Scanner.BulkListener>(listeners);
}
public ArrayList getScannerListeners()
{
return this.scannerListeners;
}
public JettyWebAppContext getWebAppConfig()
{
return webApp;
}
public void setWebAppConfig(JettyWebAppContext webAppConfig)
{
this.webApp = webAppConfig;
}
public RequestLog getRequestLog()
{
return requestLog;
}
public void setRequestLog(RequestLog requestLog)
{
this.requestLog = requestLog;
}
public LoginService[] getLoginServices()
{
return loginServices;
}
public void setLoginServices(LoginService[] loginServices)
{
this.loginServices = loginServices;
}
public ContextHandler[] getContextHandlers()
{
return contextHandlers;
}
public void setContextHandlers(ContextHandler[] contextHandlers)
{
this.contextHandlers = contextHandlers;
}
public Connector[] getConnectors()
{
return connectors;
}
public void setConnectors(Connector[] connectors)
{
this.connectors = connectors;
}
public String getReload()
{
return reload;
}
public void setReload(String reload)
{
this.reload = reload;
}
public String getJettyConfig()
{
return jettyXml;
}
public void setJettyConfig(String jettyConfig)
{
this.jettyXml = jettyConfig;
}
public String getWebAppXml()
{
return contextXml;
}
public void setWebAppXml(String webAppXml)
{
this.contextXml = webAppXml;
}
public boolean isSkip()
{
return skip;
}
public void setSkip(boolean skip)
{
this.skip = skip;
}
public boolean isDaemon()
{
return daemon;
}
public void setDaemon(boolean daemon)
{
this.daemon = daemon;
}
public String getStopKey()
{
return stopKey;
}
public void setStopKey(String stopKey)
{
this.stopKey = stopKey;
}
public int getStopPort()
{
return stopPort;
}
public void setStopPort(int stopPort)
{
this.stopPort = stopPort;
}
public List getPluginArtifacts()
{
return pluginArtifacts;
}
public void setPluginArtifacts(List pluginArtifacts)
{
this.pluginArtifacts = pluginArtifacts;
}
/**
* @param goal
* @return
*/
public boolean isExcluded (String goal)
{
if (excludedGoals == null || goal == null)

View File

@ -20,11 +20,26 @@ package org.eclipse.jetty.maven.plugin;
import java.io.IOException;
/**
* ConsoleScanner
*
* Read input from stdin
*/
public class ConsoleScanner extends Thread
{
private final AbstractJettyMojo mojo;
/**
* @param mojo
*/
public ConsoleScanner(AbstractJettyMojo mojo)
{
this.mojo = mojo;
@ -32,6 +47,12 @@ public class ConsoleScanner extends Thread
setDaemon(true);
}
/**
* @see java.lang.Thread#run()
*/
public void run()
{
try
@ -48,6 +69,12 @@ public class ConsoleScanner extends Thread
}
}
/**
*
*/
private void getSomeSleep()
{
try
@ -60,6 +87,12 @@ public class ConsoleScanner extends Thread
}
}
/**
* @throws IOException
*/
private void checkSystemInput() throws IOException
{
while (System.in.available() > 0) {
@ -75,6 +108,8 @@ public class ConsoleScanner extends Thread
}
/**
* Skip buffered bytes of system console.
*/
@ -101,6 +136,12 @@ public class ConsoleScanner extends Thread
}
}
/**
*
*/
private void restartWebApp()
{
try

View File

@ -18,33 +18,24 @@
package org.eclipse.jetty.maven.plugin;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.AbstractMojo;
@ -52,10 +43,7 @@ import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.component.repository.ComponentDependency;
import org.eclipse.jetty.server.RequestLog;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.resource.Resource;
/**
@ -104,7 +92,6 @@ public class JettyRunForkedMojo extends AbstractMojo
* @readonly
*/
private MavenProject project;
/**
@ -125,6 +112,7 @@ public class JettyRunForkedMojo extends AbstractMojo
*/
private String webXml;
/**
* The target directory
*
@ -153,8 +141,7 @@ public class JettyRunForkedMojo extends AbstractMojo
* @required
*
*/
private File classesDirectory;
private File classesDirectory;
/**
@ -164,6 +151,7 @@ public class JettyRunForkedMojo extends AbstractMojo
* @required
*/
private File testClassesDirectory;
/**
* Root directory for all html/jsp etc files
@ -171,17 +159,8 @@ public class JettyRunForkedMojo extends AbstractMojo
* @parameter expression="${basedir}/src/main/webapp"
*
*/
private File webAppSourceDirectory;
/**
* Directories that contain static resources
* for the webapp. Optional.
*
* @parameter
*/
private File[] resourceBases;
private File webAppSourceDirectory;
/**
* If true, the webAppSourceDirectory will be first on the list of
@ -201,12 +180,9 @@ public class JettyRunForkedMojo extends AbstractMojo
private String jettyXml;
/**
* The context path for the webapp. Defaults to the
* name of the webapp's artifact.
* The context path for the webapp. Defaults to / for jetty-9
*
* @parameter expression="/${project.artifactId}"
* @required
* @readonly
* @parameter expression="/"
*/
private String contextPath;
@ -224,6 +200,7 @@ public class JettyRunForkedMojo extends AbstractMojo
*/
private boolean skip;
/**
* Port to listen to stop jetty on executing -DSTOP.PORT=&lt;stopPort&gt;
* -DSTOP.KEY=&lt;stopKey&gt; -jar start.jar --stop
@ -231,6 +208,7 @@ public class JettyRunForkedMojo extends AbstractMojo
* @required
*/
protected int stopPort;
/**
* Key to provide when stopping jetty on executing java -DSTOP.KEY=&lt;stopKey&gt;
@ -246,7 +224,6 @@ public class JettyRunForkedMojo extends AbstractMojo
* @parameter
*/
private String jvmArgs;
/**
@ -263,19 +240,33 @@ public class JettyRunForkedMojo extends AbstractMojo
private PluginDescriptor plugin;
/**
* @parameter expression="true" default-value="true"
*/
private boolean waitForChild;
/**
* The forked jetty instance
*/
private Process forkedProcess;
private Random random;
/**
* Random number generator
*/
private Random random;
/**
* ShutdownThread
*
*
*/
public class ShutdownThread extends Thread
{
public ShutdownThread()
@ -292,6 +283,51 @@ public class JettyRunForkedMojo extends AbstractMojo
}
}
/**
* ConsoleStreamer
*
* Simple streamer for the console output from a Process
*/
private static class ConsoleStreamer implements Runnable
{
private String mode;
private BufferedReader reader;
public ConsoleStreamer(String mode, InputStream is)
{
this.mode = mode;
this.reader = new BufferedReader(new InputStreamReader(is));
}
public void run()
{
String line;
try
{
while ((line = reader.readLine()) != (null))
{
System.out.println("[" + mode + "] " + line);
}
}
catch (IOException ignore)
{
/* ignore */
}
finally
{
IO.close(reader);
}
}
}
/**
* @see org.apache.maven.plugin.Mojo#execute()
*/
@ -310,6 +346,12 @@ public class JettyRunForkedMojo extends AbstractMojo
}
/**
* @return
* @throws MojoExecutionException
*/
public List<String> getProvidedJars() throws MojoExecutionException
{
//if we are configured to include the provided dependencies on the plugin's classpath
@ -336,7 +378,13 @@ public class JettyRunForkedMojo extends AbstractMojo
return null;
}
/* ------------------------------------------------------------ */
/**
* @return
* @throws MojoExecutionException
*/
public File prepareConfiguration() throws MojoExecutionException
{
try
@ -371,29 +419,10 @@ public class JettyRunForkedMojo extends AbstractMojo
//sort out base dir of webapp
if (webAppSourceDirectory != null)
props.put("base.dir", webAppSourceDirectory.getAbsolutePath());
//sort out the resource base directories of the webapp
StringBuilder builder = new StringBuilder();
if (baseAppFirst)
{
add((webAppSourceDirectory==null?null:webAppSourceDirectory.getAbsolutePath()), builder);
if (resourceBases != null)
{
for (File resDir:resourceBases)
add(resDir.getAbsolutePath(), builder);
}
}
else
{
if (resourceBases != null)
{
for (File resDir:resourceBases)
add(resDir.getAbsolutePath(), builder);
}
add((webAppSourceDirectory==null?null:webAppSourceDirectory.getAbsolutePath()), builder);
}
props.put("res.dirs", builder.toString());
StringBuilder builder = new StringBuilder();
props.put("base.first", Boolean.toString(baseAppFirst));
//web-inf classes
List<File> classDirs = getClassesDirs();
@ -428,18 +457,34 @@ public class JettyRunForkedMojo extends AbstractMojo
}
props.put("lib.jars", strbuff.toString());
//any overlays
List<File> overlays = getOverlays();
strbuff.setLength(0);
for (int i=0; i<overlays.size(); i++)
//any war files
List<Artifact> warArtifacts = getWarArtifacts();
for (int i=0; i<warArtifacts.size(); i++)
{
File f = overlays.get(i);
strbuff.append(f.getAbsolutePath());
if (i < overlays.size()-1)
strbuff.append(",");
strbuff.setLength(0);
Artifact a = warArtifacts.get(i);
strbuff.append(a.getGroupId()+",");
strbuff.append(a.getArtifactId()+",");
strbuff.append(a.getFile().getAbsolutePath());
props.put("maven.war.artifact."+i, strbuff.toString());
}
props.put("overlay.files", strbuff.toString());
//any overlay configuration
WarPluginInfo warPlugin = new WarPluginInfo(project);
//add in the war plugins default includes and excludes
props.put("maven.war.includes", toCSV(warPlugin.getDependentMavenWarIncludes()));
props.put("maven.war.excludes", toCSV(warPlugin.getDependentMavenWarExcludes()));
List<OverlayConfig> configs = warPlugin.getMavenWarOverlayConfigs();
int i=0;
for (OverlayConfig c:configs)
{
props.put("maven.war.overlay."+(i++), c.toString());
}
props.store(new BufferedOutputStream(new FileOutputStream(propsFile)), "properties for forked webapp");
return propsFile;
}
@ -449,15 +494,12 @@ public class JettyRunForkedMojo extends AbstractMojo
}
}
private void add (String string, StringBuilder builder)
{
if (string == null)
return;
if (builder.length() > 0)
builder.append(",");
builder.append(string);
}
/**
* @return
*/
private List<File> getClassesDirs ()
{
List<File> classesDirs = new ArrayList<File>();
@ -474,24 +516,34 @@ public class JettyRunForkedMojo extends AbstractMojo
}
private List<File> getOverlays()
/**
* @return
* @throws MalformedURLException
* @throws IOException
*/
private List<Artifact> getWarArtifacts()
throws MalformedURLException, IOException
{
List<File> overlays = new ArrayList<File>();
List<Artifact> warArtifacts = new ArrayList<Artifact>();
for ( Iterator<Artifact> iter = project.getArtifacts().iterator(); iter.hasNext(); )
{
Artifact artifact = (Artifact) iter.next();
if (artifact.getType().equals("war"))
overlays.add(artifact.getFile());
warArtifacts.add(artifact);
}
return overlays;
return warArtifacts;
}
/**
* @return
*/
private List<File> getDependencyFiles ()
{
List<File> dependencyFiles = new ArrayList<File>();
@ -512,6 +564,13 @@ public class JettyRunForkedMojo extends AbstractMojo
return dependencyFiles;
}
/**
* @param artifact
* @return
*/
public boolean isPluginArtifact(Artifact artifact)
{
if (pluginArtifacts == null || pluginArtifacts.isEmpty())
@ -531,7 +590,12 @@ public class JettyRunForkedMojo extends AbstractMojo
private Set<Artifact> getExtraJars()
/**
* @return
* @throws Exception
*/
private Set<Artifact> getExtraJars()
throws Exception
{
Set<Artifact> extraJars = new HashSet<Artifact>();
@ -557,7 +621,11 @@ public class JettyRunForkedMojo extends AbstractMojo
}
/* ------------------------------------------------------------ */
/**
* @throws MojoExecutionException
*/
public void startJettyRunner() throws MojoExecutionException
{
try
@ -678,7 +746,12 @@ public class JettyRunForkedMojo extends AbstractMojo
}
/**
* @return
* @throws Exception
*/
public String getClassPath() throws Exception
{
StringBuilder classPath = new StringBuilder();
@ -720,6 +793,12 @@ public class JettyRunForkedMojo extends AbstractMojo
return classPath.toString();
}
/**
* @return
*/
private String getJavaBin()
{
String javaexes[] = new String[]
@ -738,6 +817,13 @@ public class JettyRunForkedMojo extends AbstractMojo
return "java";
}
/**
* @param path
* @return
*/
public static String fileSeparators(String path)
{
StringBuilder ret = new StringBuilder();
@ -755,6 +841,13 @@ public class JettyRunForkedMojo extends AbstractMojo
return ret.toString();
}
/**
* @param path
* @return
*/
public static String pathSeparators(String path)
{
StringBuilder ret = new StringBuilder();
@ -772,13 +865,24 @@ public class JettyRunForkedMojo extends AbstractMojo
return ret.toString();
}
/**
* @return
*/
private String createToken ()
{
return Long.toString(random.nextLong()^System.currentTimeMillis(), 36).toUpperCase(Locale.ENGLISH);
}
/**
* @param mode
* @param inputStream
*/
private void startPump(String mode, InputStream inputStream)
{
ConsoleStreamer pump = new ConsoleStreamer(mode,inputStream);
@ -787,42 +891,25 @@ public class JettyRunForkedMojo extends AbstractMojo
thread.start();
}
/**
* Simple streamer for the console output from a Process
* @param strings
* @return
*/
private static class ConsoleStreamer implements Runnable
private String toCSV (List<String> strings)
{
private String mode;
private BufferedReader reader;
public ConsoleStreamer(String mode, InputStream is)
if (strings == null)
return "";
StringBuffer strbuff = new StringBuffer();
Iterator<String> itor = strings.iterator();
while (itor.hasNext())
{
this.mode = mode;
this.reader = new BufferedReader(new InputStreamReader(is));
}
public void run()
{
String line;
try
{
while ((line = reader.readLine()) != (null))
{
System.out.println("[" + mode + "] " + line);
}
}
catch (IOException ignore)
{
/* ignore */
}
finally
{
IO.close(reader);
}
strbuff.append(itor.next());
if (itor.hasNext())
strbuff.append(",");
}
return strbuff.toString();
}
}

View File

@ -20,10 +20,13 @@ package org.eclipse.jetty.maven.plugin;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.MojoExecutionException;
@ -33,6 +36,7 @@ import org.eclipse.jetty.util.Scanner;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.WebAppContext;
/**
* <p>
* This goal is used in-situ on a Maven project without first requiring that the project
@ -66,6 +70,7 @@ public class JettyRunMojo extends AbstractJettyMojo
public static final String DEFAULT_WEBAPP_SRC = "src"+File.separator+"main"+File.separator+"webapp";
/**
* If true, the &lt;testOutputDirectory&gt;
* and the dependencies of &lt;scope&gt;test&lt;scope&gt;
@ -73,7 +78,7 @@ public class JettyRunMojo extends AbstractJettyMojo
*
* @parameter alias="useTestClasspath" default-value="false"
*/
private boolean useTestScope;
protected boolean useTestScope;
/**
@ -83,7 +88,7 @@ public class JettyRunMojo extends AbstractJettyMojo
* @parameter expression="${maven.war.webxml}"
* @readonly
*/
private String webXml;
protected String webXml;
/**
@ -93,8 +98,7 @@ public class JettyRunMojo extends AbstractJettyMojo
* @required
*
*/
private File classesDirectory;
protected File classesDirectory;
/**
@ -103,7 +107,8 @@ public class JettyRunMojo extends AbstractJettyMojo
* @parameter expression="${project.build.testOutputDirectory}"
* @required
*/
private File testClassesDirectory;
protected File testClassesDirectory;
/**
* Root directory for all html/jsp etc files
@ -111,14 +116,14 @@ public class JettyRunMojo extends AbstractJettyMojo
* @parameter expression="${maven.war.src}"
*
*/
private File webAppSourceDirectory;
protected File webAppSourceDirectory;
/**
* List of files or directories to additionally periodically scan for changes. Optional.
* @parameter
*/
private File[] scanTargets;
protected File[] scanTargets;
/**
@ -127,15 +132,40 @@ public class JettyRunMojo extends AbstractJettyMojo
* or in conjunction with &lt;scanTargets&gt;.Optional.
* @parameter
*/
private ScanTargetPattern[] scanTargetPatterns;
protected ScanTargetPattern[] scanTargetPatterns;
/**
* Extra scan targets as a list
*/
private List<File> extraScanTargets;
protected List<File> extraScanTargets;
/**
* maven-war-plugin reference
*/
protected WarPluginInfo warPluginInfo;
/**
* List of deps that are wars
*/
protected List<Artifact> warArtifacts;
/**
* @see org.eclipse.jetty.maven.plugin.AbstractJettyMojo#execute()
*/
@Override
public void execute() throws MojoExecutionException, MojoFailureException
{
warPluginInfo = new WarPluginInfo(project);
super.execute();
}
@ -150,14 +180,14 @@ public class JettyRunMojo extends AbstractJettyMojo
// check the location of the static content/jsps etc
try
{
if ((getWebAppSourceDirectory() == null) || !getWebAppSourceDirectory().exists())
if ((webAppSourceDirectory == null) || !webAppSourceDirectory.exists())
{
File defaultWebAppSrcDir = new File (project.getBasedir(), DEFAULT_WEBAPP_SRC);
getLog().info("webAppSourceDirectory"+(getWebAppSourceDirectory()==null?" not set.":" does not exist.")+" Defaulting to "+defaultWebAppSrcDir.getAbsolutePath());
getLog().info("webAppSourceDirectory"+(webAppSourceDirectory == null ? " not set." : " does not exist.")+" Defaulting to "+defaultWebAppSrcDir.getAbsolutePath());
webAppSourceDirectory = defaultWebAppSrcDir;
}
else
getLog().info( "Webapp source directory = " + getWebAppSourceDirectory().getCanonicalPath());
getLog().info( "Webapp source directory = " + webAppSourceDirectory.getCanonicalPath());
}
catch (IOException e)
{
@ -179,12 +209,12 @@ public class JettyRunMojo extends AbstractJettyMojo
try
{
//allow a webapp with no classes in it (just jsps/html)
if (getClassesDirectory() != null)
if (classesDirectory != null)
{
if (!getClassesDirectory().exists())
getLog().info( "Classes directory "+ getClassesDirectory().getCanonicalPath()+ " does not exist");
if (!classesDirectory.exists())
getLog().info( "Classes directory "+ classesDirectory.getCanonicalPath()+ " does not exist");
else
getLog().info("Classes = " + getClassesDirectory().getCanonicalPath());
getLog().info("Classes = " + classesDirectory.getCanonicalPath());
}
else
getLog().info("Classes directory not set");
@ -194,18 +224,16 @@ public class JettyRunMojo extends AbstractJettyMojo
throw new MojoExecutionException("Location of classesDirectory does not exist");
}
setExtraScanTargets(new ArrayList<File>());
extraScanTargets = new ArrayList<File>();
if (scanTargets != null)
{
for (int i=0; i< scanTargets.length; i++)
{
getLog().info("Added extra scan target:"+ scanTargets[i]);
getExtraScanTargets().add(scanTargets[i]);
extraScanTargets.add(scanTargets[i]);
}
}
if (scanTargetPatterns!=null)
{
for (int i=0;i<scanTargetPatterns.length; i++)
@ -236,11 +264,11 @@ public class JettyRunMojo extends AbstractJettyMojo
itor = files.iterator();
while (itor.hasNext())
getLog().info("Adding extra scan target from pattern: "+itor.next());
List<File> currentTargets = getExtraScanTargets();
List<File> currentTargets = extraScanTargets;
if(currentTargets!=null && !currentTargets.isEmpty())
currentTargets.addAll(files);
else
setExtraScanTargets(files);
extraScanTargets = files;
}
catch (IOException e)
{
@ -253,7 +281,9 @@ public class JettyRunMojo extends AbstractJettyMojo
/**
* @see org.eclipse.jetty.maven.plugin.AbstractJettyMojo#configureWebApplication()
*/
public void configureWebApplication() throws Exception
{
super.configureWebApplication();
@ -270,13 +300,61 @@ public class JettyRunMojo extends AbstractJettyMojo
if (webApp.getBaseResource() == null)
webApp.setBaseResource(webAppSourceDirectoryResource);
if (getClassesDirectory() != null)
webApp.setClasses (getClassesDirectory());
if (classesDirectory != null)
webApp.setClasses (classesDirectory);
if (useTestScope && (testClassesDirectory != null))
webApp.setTestClasses (testClassesDirectory);
webApp.setWebInfLib (getDependencyFiles());
//get copy of a list of war artifacts
Set<Artifact> matchedWarArtifacts = new HashSet<Artifact>();
//make sure each of the war artifacts is added to the scanner
for (Artifact a:getWarArtifacts())
extraScanTargets.add(a.getFile());
//process any overlays and the war type artifacts
List<Overlay> overlays = new ArrayList<Overlay>();
for (OverlayConfig config:warPluginInfo.getMavenWarOverlayConfigs())
{
//overlays can be individually skipped
if (config.isSkip())
continue;
//an empty overlay refers to the current project - important for ordering
if (config.isCurrentProject())
{
Overlay overlay = new Overlay(config, null);
overlays.add(overlay);
continue;
}
//if a war matches an overlay config
Artifact a = getArtifactForOverlay(config, getWarArtifacts());
if (a != null)
{
matchedWarArtifacts.add(a);
SelectiveJarResource r = new SelectiveJarResource(new URL("jar:"+Resource.toURL(a.getFile()).toString()+"!/"));
r.setIncludes(config.getIncludes());
r.setExcludes(config.getExcludes());
Overlay overlay = new Overlay(config, r);
overlays.add(overlay);
}
}
//iterate over the left over war artifacts and unpack them (without include/exclude processing) as necessary
for (Artifact a: getWarArtifacts())
{
if (!matchedWarArtifacts.contains(a))
{
Overlay overlay = new Overlay(null, Resource.newResource(new URL("jar:"+Resource.toURL(a.getFile()).toString()+"!/")));
overlays.add(overlay);
}
}
webApp.setOverlays(overlays);
//if we have not already set web.xml location, need to set one up
if (webApp.getDescriptor() == null)
{
@ -311,14 +389,20 @@ public class JettyRunMojo extends AbstractJettyMojo
}
}
getLog().info( "web.xml file = "+webApp.getDescriptor());
getLog().info("Webapp directory = " + getWebAppSourceDirectory().getCanonicalPath());
getLog().info("Webapp directory = " + webAppSourceDirectory.getCanonicalPath());
}
/**
* @see org.eclipse.jetty.maven.plugin.AbstractJettyMojo#configureScanner()
*/
public void configureScanner ()
throws MojoExecutionException
{
// start the scanner thread (if necessary) on the main webapp
final ArrayList<File> scanList = new ArrayList<File>();
scanList = new ArrayList<File>();
if (webApp.getDescriptor() != null)
{
try
@ -375,25 +459,25 @@ public class JettyRunMojo extends AbstractJettyMojo
}
File jettyWebXmlFile = findJettyWebXmlFile(new File(getWebAppSourceDirectory(),"WEB-INF"));
File jettyWebXmlFile = findJettyWebXmlFile(new File(webAppSourceDirectory,"WEB-INF"));
if (jettyWebXmlFile != null)
scanList.add(jettyWebXmlFile);
scanList.addAll(getExtraScanTargets());
scanList.add(getProject().getFile());
scanList.addAll(extraScanTargets);
scanList.add(project.getFile());
if (webApp.getTestClasses() != null)
scanList.add(webApp.getTestClasses());
if (webApp.getClasses() != null)
scanList.add(webApp.getClasses());
scanList.addAll(webApp.getWebInfLib());
setScanList(scanList);
ArrayList<Scanner.BulkListener> listeners = new ArrayList<Scanner.BulkListener>();
listeners.add(new Scanner.BulkListener()
scannerListeners = new ArrayList<Scanner.BulkListener>();
scannerListeners.add(new Scanner.BulkListener()
{
public void filesChanged (List changes)
{
try
{
boolean reconfigure = changes.contains(getProject().getFile().getCanonicalPath());
boolean reconfigure = changes.contains(project.getFile().getCanonicalPath());
restartWebApp(reconfigure);
}
catch (Exception e)
@ -402,9 +486,14 @@ public class JettyRunMojo extends AbstractJettyMojo
}
}
});
setScannerListeners(listeners);
}
/**
* @see org.eclipse.jetty.maven.plugin.AbstractJettyMojo#restartWebApp(boolean)
*/
public void restartWebApp(boolean reconfigureScanner) throws Exception
{
getLog().info("restarting "+webApp);
@ -424,14 +513,14 @@ public class JettyRunMojo extends AbstractJettyMojo
scanList.add(new File(webApp.getDescriptor()));
if (webApp.getJettyEnvXml() != null)
scanList.add(new File(webApp.getJettyEnvXml()));
scanList.addAll(getExtraScanTargets());
scanList.add(getProject().getFile());
scanList.addAll(extraScanTargets);
scanList.add(project.getFile());
if (webApp.getTestClasses() != null)
scanList.add(webApp.getTestClasses());
if (webApp.getClasses() != null)
scanList.add(webApp.getClasses());
scanList.addAll(webApp.getWebInfLib());
getScanner().setScanDirs(scanList);
scanner.setScanDirs(scanList);
}
getLog().debug("Restarting webapp ...");
@ -439,10 +528,15 @@ public class JettyRunMojo extends AbstractJettyMojo
getLog().info("Restart completed at "+new Date().toString());
}
/**
* @return
*/
private List<File> getDependencyFiles ()
{
List<File> dependencyFiles = new ArrayList<File>();
List<Resource> overlays = new ArrayList<Resource>();
for ( Iterator<Artifact> iter = projectArtifacts.iterator(); iter.hasNext(); )
{
Artifact artifact = (Artifact) iter.next();
@ -450,17 +544,6 @@ public class JettyRunMojo extends AbstractJettyMojo
// Include runtime and compile time libraries, and possibly test libs too
if(artifact.getType().equals("war"))
{
try
{
Resource r=Resource.newResource("jar:"+Resource.toURL(artifact.getFile()).toString()+"!/");
overlays.add(r);
getLog().info("Adding overlay for war project artifact "+artifact.getId());
getExtraScanTargets().add(artifact.getFile());
}
catch(Exception e)
{
throw new RuntimeException(e);
}
continue;
}
@ -473,136 +556,79 @@ public class JettyRunMojo extends AbstractJettyMojo
dependencyFiles.add(artifact.getFile());
getLog().debug( "Adding artifact " + artifact.getFile().getName() + " with scope "+artifact.getScope()+" for WEB-INF/lib " );
}
webApp.setOverlays(overlays);
return dependencyFiles;
}
private List<File> setUpClassPath(File webInfClasses, File testClasses, List<File> webInfJars)
/**
* @return
*/
private List<Artifact> getWarArtifacts ()
{
List<File> classPathFiles = new ArrayList<File>();
if (webInfClasses != null)
classPathFiles.add(webInfClasses);
if (testClasses != null)
classPathFiles.add(testClasses);
classPathFiles.addAll(webInfJars);
if (getLog().isDebugEnabled())
if (warArtifacts != null)
return warArtifacts;
warArtifacts = new ArrayList<Artifact>();
for ( Iterator<Artifact> iter = projectArtifacts.iterator(); iter.hasNext(); )
{
for (int i = 0; i < classPathFiles.size(); i++)
Artifact artifact = (Artifact) iter.next();
if (artifact.getType().equals("war"))
{
getLog().debug("classpath element: "+ ((File) classPathFiles.get(i)).getName());
try
{
warArtifacts.add(artifact);
getLog().info("Dependent war artifact "+artifact.getId());
}
catch(Exception e)
{
throw new RuntimeException(e);
}
}
}
return classPathFiles;
return warArtifacts;
}
private List<File> getClassesDirs ()
{
List<File> classesDirs = new ArrayList<File>();
//if using the test classes, make sure they are first
//on the list
if (useTestScope && (testClassesDirectory != null))
classesDirs.add(testClassesDirectory);
if (getClassesDirectory() != null)
classesDirs.add(getClassesDirectory());
return classesDirs;
}
public void execute() throws MojoExecutionException, MojoFailureException
{
super.execute();
}
public String getWebXml()
{
return this.webXml;
}
public void setWebXml(String webXml) {
this.webXml = webXml;
}
public File getClassesDirectory()
{
return this.classesDirectory;
}
public void setClassesDirectory(File classesDirectory) {
this.classesDirectory = classesDirectory;
}
public File getWebAppSourceDirectory()
{
return this.webAppSourceDirectory;
}
public void setWebAppSourceDirectory(File webAppSourceDirectory)
{
this.webAppSourceDirectory = webAppSourceDirectory;
}
public List<File> getExtraScanTargets()
{
return this.extraScanTargets;
}
public void setExtraScanTargets(List<File> list)
{
this.extraScanTargets = list;
}
public boolean isUseTestClasspath()
{
return useTestScope;
}
public void setUseTestClasspath(boolean useTestClasspath)
{
this.useTestScope = useTestClasspath;
}
public File getTestClassesDirectory()
{
return testClassesDirectory;
}
public void setTestClassesDirectory(File testClassesDirectory)
{
this.testClassesDirectory = testClassesDirectory;
}
public File[] getScanTargets()
{
return scanTargets;
}
public void setScanTargets(File[] scanTargets)
{
this.scanTargets = scanTargets;
}
public ScanTargetPattern[] getScanTargetPatterns()
{
return scanTargetPatterns;
}
public void setScanTargetPatterns(ScanTargetPattern[] scanTargetPatterns)
{
this.scanTargetPatterns = scanTargetPatterns;
}
/**
* @param o
* @param warArtifacts
* @return
*/
protected Artifact getArtifactForOverlay (OverlayConfig o, List<Artifact> warArtifacts)
{
if (o == null || warArtifacts == null || warArtifacts.isEmpty())
return null;
for (Artifact a:warArtifacts)
{
if (overlayMatchesArtifact (o, a))
{
return a;
}
}
return null;
}
/**
* @param o
* @param a
* @return
*/
protected boolean overlayMatchesArtifact(OverlayConfig o, Artifact a)
{
if (((o.getGroupId() == null && a.getGroupId() == null) || (o.getGroupId() != null && o.getGroupId().equals(a.getGroupId())))
&& ((o.getArtifactId() == null && a.getArtifactId() == null) || (o.getArtifactId() != null && o.getArtifactId().equals(a.getArtifactId())))
&& ((o.getClassifier() == null) || (o.getClassifier().equals(a.getClassifier()))))
return true;
return false;
}
}

View File

@ -67,7 +67,17 @@ public class JettyRunWarExplodedMojo extends AbstractJettyMojo
/**
* @see org.eclipse.jetty.maven.plugin.AbstractJettyMojo#execute()
*/
public void execute () throws MojoExecutionException, MojoFailureException
{
super.execute();
}
/**
*
* @see org.mortbay.jetty.plugin.AbstractJettyMojo#checkPomConfiguration()
@ -77,13 +87,16 @@ public class JettyRunWarExplodedMojo extends AbstractJettyMojo
return;
}
/**
* @see org.mortbay.jetty.plugin.AbstractJettyMojo#configureScanner()
*/
public void configureScanner() throws MojoExecutionException
{
final ArrayList<File> scanList = new ArrayList<File>();
scanList.add(getProject().getFile());
scanList = new ArrayList<File>();
scanList.add(project.getFile());
File webInfDir = new File(war,"WEB-INF");
scanList.add(new File(webInfDir, "web.xml"));
File jettyWebXmlFile = findJettyWebXmlFile(webInfDir);
@ -94,16 +107,15 @@ public class JettyRunWarExplodedMojo extends AbstractJettyMojo
scanList.add(jettyEnvXmlFile);
scanList.add(new File(webInfDir, "classes"));
scanList.add(new File(webInfDir, "lib"));
setScanList(scanList);
ArrayList<Scanner.BulkListener> listeners = new ArrayList<Scanner.BulkListener>();
listeners.add(new Scanner.BulkListener()
scannerListeners = new ArrayList<Scanner.BulkListener>();
scannerListeners.add(new Scanner.BulkListener()
{
public void filesChanged(List changes)
{
try
{
boolean reconfigure = changes.contains(getProject().getFile().getCanonicalPath());
boolean reconfigure = changes.contains(project.getFile().getCanonicalPath());
restartWebApp(reconfigure);
}
catch (Exception e)
@ -112,12 +124,14 @@ public class JettyRunWarExplodedMojo extends AbstractJettyMojo
}
}
});
setScannerListeners(listeners);
}
/**
* @see org.eclipse.jetty.maven.plugin.AbstractJettyMojo#restartWebApp(boolean)
*/
public void restartWebApp(boolean reconfigureScanner) throws Exception
{
getLog().info("Restarting webapp");
@ -132,9 +146,8 @@ public class JettyRunWarExplodedMojo extends AbstractJettyMojo
if (reconfigureScanner)
{
getLog().info("Reconfiguring scanner after change to pom.xml ...");
ArrayList<File> scanList = getScanList();
scanList.clear();
scanList.add(getProject().getFile());
scanList.add(project.getFile());
File webInfDir = new File(war,"WEB-INF");
scanList.add(new File(webInfDir, "web.xml"));
File jettyWebXmlFile = findJettyWebXmlFile(webInfDir);
@ -145,8 +158,7 @@ public class JettyRunWarExplodedMojo extends AbstractJettyMojo
scanList.add(jettyEnvXmlFile);
scanList.add(new File(webInfDir, "classes"));
scanList.add(new File(webInfDir, "lib"));
setScanList(scanList);
getScanner().setScanDirs(scanList);
scanner.setScanDirs(scanList);
}
getLog().debug("Restarting webapp ...");
@ -155,19 +167,14 @@ public class JettyRunWarExplodedMojo extends AbstractJettyMojo
}
/**
* @see org.eclipse.jetty.maven.plugin.AbstractJettyMojo#configureWebApplication()
*/
public void configureWebApplication () throws Exception
{
super.configureWebApplication();
webApp.setWar(war.getCanonicalPath());
}
public void execute () throws MojoExecutionException, MojoFailureException
{
super.execute();
}
}

View File

@ -64,7 +64,6 @@ public class JettyRunWarMojo extends AbstractJettyMojo
*/
private File war;
/**
* @see org.apache.maven.plugin.Mojo#execute()
@ -76,6 +75,7 @@ public class JettyRunWarMojo extends AbstractJettyMojo
public void configureWebApplication () throws Exception
{
super.configureWebApplication();
@ -85,6 +85,7 @@ public class JettyRunWarMojo extends AbstractJettyMojo
/**
* @see org.mortbay.jetty.plugin.AbstractJettyMojo#checkPomConfiguration()
*/
@ -95,24 +96,24 @@ public class JettyRunWarMojo extends AbstractJettyMojo
/* (non-Javadoc)
/**
* @see org.eclipse.jetty.server.plugin.AbstractJettyMojo#configureScanner()
*/
public void configureScanner() throws MojoExecutionException
{
final ArrayList scanList = new ArrayList();
scanList.add(getProject().getFile());
scanList = new ArrayList();
scanList.add(project.getFile());
scanList.add(war);
setScanList(scanList);
ArrayList listeners = new ArrayList();
listeners.add(new Scanner.BulkListener()
scannerListeners = new ArrayList();
scannerListeners.add(new Scanner.BulkListener()
{
public void filesChanged(List changes)
{
try
{
boolean reconfigure = changes.contains(getProject().getFile().getCanonicalPath());
boolean reconfigure = changes.contains(project.getFile().getCanonicalPath());
restartWebApp(reconfigure);
}
catch (Exception e)
@ -121,10 +122,14 @@ public class JettyRunWarMojo extends AbstractJettyMojo
}
}
});
setScannerListeners(listeners);
}
/**
* @see org.eclipse.jetty.maven.plugin.AbstractJettyMojo#restartWebApp(boolean)
*/
public void restartWebApp(boolean reconfigureScanner) throws Exception
{
getLog().info("Restarting webapp ...");
@ -139,12 +144,10 @@ public class JettyRunWarMojo extends AbstractJettyMojo
if (reconfigureScanner)
{
getLog().info("Reconfiguring scanner after change to pom.xml ...");
ArrayList scanList = getScanList();
scanList.clear();
scanList.add(getProject().getFile());
scanList.add(project.getFile());
scanList.add(war);
setScanList(scanList);
getScanner().setScanDirs(scanList);
scanner.setScanDirs(scanList);
}
getLog().debug("Restarting webapp ...");

View File

@ -28,6 +28,7 @@ import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.apache.maven.artifact.Artifact;
import org.eclipse.jetty.plus.webapp.EnvConfiguration;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.log.Log;
@ -59,40 +60,40 @@ public class JettyWebAppContext extends WebAppContext
private static final String WEB_INF_CLASSES_PREFIX = "/WEB-INF/classes";
private static final String WEB_INF_LIB_PREFIX = "/WEB-INF/lib";
private File classes = null;
private File testClasses = null;
private final List<File> webInfClasses = new ArrayList<File>();
private final List<File> webInfJars = new ArrayList<File>();
private final Map<String, File> webInfJarMap = new HashMap<String, File>();
private final EnvConfiguration envConfig;
private List<File> classpathFiles; //webInfClasses+testClasses+webInfJars
private String jettyEnvXml;
private List<Resource> overlays;
private File _classes = null;
private File _testClasses = null;
private final List<File> _webInfClasses = new ArrayList<File>();
private final List<File> _webInfJars = new ArrayList<File>();
private final Map<String, File> _webInfJarMap = new HashMap<String, File>();
private final EnvConfiguration _envConfig;
private List<File> _classpathFiles; //webInfClasses+testClasses+webInfJars
private String _jettyEnvXml;
private List<Overlay> _overlays;
/**
* @deprecated The value of this parameter will be ignored by the plugin. Overlays will always be unpacked.
*/
private boolean unpackOverlays;
/**
* Set the "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern" with a pattern for matching jars on
* container classpath to scan. This is analogous to the WebAppContext.setAttribute() call.
*/
private String containerIncludeJarPattern = null;
private String _containerIncludeJarPattern = null;
/**
* Set the "org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern" with a pattern for matching jars on
* webapp's classpath to scan. This is analogous to the WebAppContext.setAttribute() call.
*/
private String webInfIncludeJarPattern = null;
private String _webInfIncludeJarPattern = null;
/**
* @deprecated The value of this parameter will be ignored by the plugin. This option will be always disabled.
* If there is no maven-war-plugin config for ordering of the current project in the
* sequence of overlays, use this to control whether the current project is added
* first or last in list of overlaid resources
*/
private boolean copyWebInf;
private boolean _baseAppFirst = true;
private boolean baseAppFirst = true;
public JettyWebAppContext ()
throws Exception
@ -103,7 +104,7 @@ public class JettyWebAppContext extends WebAppContext
new WebXmlConfiguration(),
new MetaInfConfiguration(),
new FragmentConfiguration(),
envConfig = new EnvConfiguration(),
_envConfig = new EnvConfiguration(),
new org.eclipse.jetty.plus.webapp.PlusConfiguration(),
new MavenAnnotationConfiguration(),
new JettyWebXmlConfiguration()
@ -113,114 +114,94 @@ public class JettyWebAppContext extends WebAppContext
}
public void setContainerIncludeJarPattern(String pattern)
{
containerIncludeJarPattern = pattern;
_containerIncludeJarPattern = pattern;
}
public String getContainerIncludeJarPattern()
{
return containerIncludeJarPattern;
return _containerIncludeJarPattern;
}
public String getWebInfIncludeJarPattern()
{
return webInfIncludeJarPattern;
return _webInfIncludeJarPattern;
}
public void setWebInfIncludeJarPattern(String pattern)
{
webInfIncludeJarPattern = pattern;
_webInfIncludeJarPattern = pattern;
}
public boolean getUnpackOverlays()
{
return unpackOverlays;
}
public void setUnpackOverlays(boolean unpackOverlays)
{
this.unpackOverlays = unpackOverlays;
}
public List<File> getClassPathFiles()
{
return this.classpathFiles;
return this._classpathFiles;
}
public void setOverlays (List<Resource> overlays)
{
this.overlays = overlays;
}
public List<Resource> getOverlays ()
{
return this.overlays;
}
public void setJettyEnvXml (String jettyEnvXml)
{
this.jettyEnvXml = jettyEnvXml;
this._jettyEnvXml = jettyEnvXml;
}
public String getJettyEnvXml()
{
return this.jettyEnvXml;
return this._jettyEnvXml;
}
public void setClasses(File dir)
{
classes = dir;
_classes = dir;
}
public File getClasses()
{
return classes;
return _classes;
}
public void setWebInfLib (List<File> jars)
{
webInfJars.addAll(jars);
_webInfJars.addAll(jars);
}
public void setTestClasses (File dir)
{
testClasses = dir;
_testClasses = dir;
}
public File getTestClasses ()
{
return testClasses;
return _testClasses;
}
/* ------------------------------------------------------------ */
@Override
public void setCopyWebInf(boolean value)
/**
* Ordered list of wars to overlay on top of the current project. The list
* may contain an overlay that represents the current project.
* @param overlays
*/
public void setOverlays (List<Overlay> overlays)
{
copyWebInf = value;
_overlays = overlays;
}
/* ------------------------------------------------------------ */
@Override
public boolean isCopyWebInf()
public List<Overlay> getOverlays()
{
return copyWebInf;
return _overlays;
}
/* ------------------------------------------------------------ */
public void setBaseAppFirst(boolean value)
{
baseAppFirst = value;
_baseAppFirst = value;
}
/* ------------------------------------------------------------ */
public boolean getBaseAppFirst()
{
return baseAppFirst;
return _baseAppFirst;
}
/* ------------------------------------------------------------ */
@ -244,7 +225,7 @@ public class JettyWebAppContext extends WebAppContext
public List<File> getWebInfLib()
{
return webInfJars;
return _webInfJars;
}
public void doStart () throws Exception
@ -254,7 +235,7 @@ public class JettyWebAppContext extends WebAppContext
//Allow user to set up pattern for names of jars from the container classpath
//that will be scanned - note that by default NO jars are scanned
String tmp = containerIncludeJarPattern;
String tmp = _containerIncludeJarPattern;
if (tmp==null || "".equals(tmp))
tmp = (String)getAttribute(WebInfConfiguration.CONTAINER_JAR_PATTERN);
@ -264,32 +245,32 @@ public class JettyWebAppContext extends WebAppContext
//Allow user to set up pattern of jar names from WEB-INF that will be scanned.
//Note that by default ALL jars considered to be in WEB-INF will be scanned - setting
//a pattern restricts scanning
if (webInfIncludeJarPattern != null)
setAttribute(WebInfConfiguration.WEBINF_JAR_PATTERN, webInfIncludeJarPattern);
if (_webInfIncludeJarPattern != null)
setAttribute(WebInfConfiguration.WEBINF_JAR_PATTERN, _webInfIncludeJarPattern);
//Set up the classes dirs that comprises the equivalent of WEB-INF/classes
if (testClasses != null)
webInfClasses.add(testClasses);
if (classes != null)
webInfClasses.add(classes);
if (_testClasses != null)
_webInfClasses.add(_testClasses);
if (_classes != null)
_webInfClasses.add(_classes);
// Set up the classpath
classpathFiles = new ArrayList<File>();
classpathFiles.addAll(webInfClasses);
classpathFiles.addAll(webInfJars);
_classpathFiles = new ArrayList<File>();
_classpathFiles.addAll(_webInfClasses);
_classpathFiles.addAll(_webInfJars);
// Initialize map containing all jars in /WEB-INF/lib
webInfJarMap.clear();
for (File file : webInfJars)
_webInfJarMap.clear();
for (File file : _webInfJars)
{
// Return all jar files from class path
String fileName = file.getName();
if (fileName.endsWith(".jar"))
webInfJarMap.put(fileName, file);
_webInfJarMap.put(fileName, file);
}
if (this.jettyEnvXml != null)
envConfig.setJettyEnvXml(Resource.toURL(new File(this.jettyEnvXml)));
if (this._jettyEnvXml != null)
_envConfig.setJettyEnvXml(Resource.toURL(new File(this._jettyEnvXml)));
// CHECK setShutdown(false);
super.doStart();
@ -297,18 +278,18 @@ public class JettyWebAppContext extends WebAppContext
public void doStop () throws Exception
{
if (classpathFiles != null)
classpathFiles.clear();
classpathFiles = null;
if (_classpathFiles != null)
_classpathFiles.clear();
_classpathFiles = null;
classes = null;
testClasses = null;
_classes = null;
_testClasses = null;
if (webInfJarMap != null)
webInfJarMap.clear();
if (_webInfJarMap != null)
_webInfJarMap.clear();
webInfClasses.clear();
webInfJars.clear();
_webInfClasses.clear();
_webInfJars.clear();
@ -326,7 +307,7 @@ public class JettyWebAppContext extends WebAppContext
resource = super.getResource(uriInContext);
// If no regular resource exists check for access to /WEB-INF/lib or /WEB-INF/classes
if ((resource == null || !resource.exists()) && uriInContext != null && classes != null)
if ((resource == null || !resource.exists()) && uriInContext != null && _classes != null)
{
String uri = URIUtil.canonicalPath(uriInContext);
if (uri == null)
@ -341,19 +322,19 @@ public class JettyWebAppContext extends WebAppContext
{
//exact match for a WEB-INF/classes, so preferentially return the resource matching the web-inf classes
//rather than the test classes
if (classes != null)
return Resource.newResource(classes);
else if (testClasses != null)
return Resource.newResource(testClasses);
if (_classes != null)
return Resource.newResource(_classes);
else if (_testClasses != null)
return Resource.newResource(_testClasses);
}
else
{
//try matching
Resource res = null;
int i=0;
while (res == null && (i < webInfClasses.size()))
while (res == null && (i < _webInfClasses.size()))
{
String newPath = uri.replace(WEB_INF_CLASSES_PREFIX, webInfClasses.get(i).getPath());
String newPath = uri.replace(WEB_INF_CLASSES_PREFIX, _webInfClasses.get(i).getPath());
res = Resource.newResource(newPath);
if (!res.exists())
{
@ -373,7 +354,7 @@ public class JettyWebAppContext extends WebAppContext
jarName = jarName.substring(1);
if (jarName.length()==0)
return null;
File jarFile = webInfJarMap.get(jarName);
File jarFile = _webInfJarMap.get(jarName);
if (jarFile != null)
return Resource.newResource(jarFile.getPath());
@ -406,7 +387,7 @@ public class JettyWebAppContext extends WebAppContext
//add in the dependency jars as a virtual WEB-INF/lib entry
if (path.startsWith(WEB_INF_LIB_PREFIX))
{
for (String fileName : webInfJarMap.keySet())
for (String fileName : _webInfJarMap.keySet())
{
// Return all jar files from class path
allPaths.add(WEB_INF_LIB_PREFIX + "/" + fileName);
@ -416,9 +397,9 @@ public class JettyWebAppContext extends WebAppContext
{
int i=0;
while (i < webInfClasses.size())
while (i < _webInfClasses.size())
{
String newPath = path.replace(WEB_INF_CLASSES_PREFIX, webInfClasses.get(i).getPath());
String newPath = path.replace(WEB_INF_CLASSES_PREFIX, _webInfClasses.get(i).getPath());
allPaths.addAll(super.getResourcePaths(newPath));
i++;
}

View File

@ -20,14 +20,14 @@ package org.eclipse.jetty.maven.plugin;
import java.io.File;
import java.io.IOException;
import java.net.URLClassLoader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import org.apache.maven.artifact.Artifact;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
@ -36,29 +36,43 @@ import org.eclipse.jetty.webapp.WebAppClassLoader;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.webapp.WebInfConfiguration;
/**
* MavenWebInfConfiguration
*
* WebInfConfiguration to take account of overlaid wars expressed as project dependencies and
* potentiall configured via the maven-war-plugin.
*
*/
public class MavenWebInfConfiguration extends WebInfConfiguration
{
private static final Logger LOG = Log.getLogger(MavenWebInfConfiguration.class);
private static final Logger LOG = Log.getLogger(WebInfConfiguration.class);
protected static int COUNTER = 0;
protected Resource _originalResourceBase;
protected Resource[] _unpackedOverlays;
protected List<Resource> _unpackedOverlayResources;
/**
* @see org.eclipse.jetty.webapp.WebInfConfiguration#configure(org.eclipse.jetty.webapp.WebAppContext)
*/
public void configure(WebAppContext context) throws Exception
{
JettyWebAppContext jwac = (JettyWebAppContext)context;
//put the classes dir and all dependencies into the classpath
if (jwac.getClassPathFiles() != null)
{
if (LOG.isDebugEnabled()) LOG.debug("Setting up classpath ...");
//put the classes dir and all dependencies into the classpath
Iterator itor = jwac.getClassPathFiles().iterator();
while (itor.hasNext())
((WebAppClassLoader)context.getClassLoader()).addClassPath(((File)itor.next()).getCanonicalPath());
//if (LOG.isDebugEnabled())
//LOG.debug("Classpath = "+LazyList.array2List(((URLClassLoader)context.getClassLoader()).getURLs()));
}
super.configure(context);
// knock out environmental maven and plexus classes from webAppContext
@ -76,31 +90,45 @@ public class MavenWebInfConfiguration extends WebInfConfiguration
context.setServerClasses( newServerClasses );
}
/**
* @see org.eclipse.jetty.webapp.WebInfConfiguration#preConfigure(org.eclipse.jetty.webapp.WebAppContext)
*/
public void preConfigure(WebAppContext context) throws Exception
{
super.preConfigure(context);
}
/**
* @see org.eclipse.jetty.webapp.AbstractConfiguration#postConfigure(org.eclipse.jetty.webapp.WebAppContext)
*/
public void postConfigure(WebAppContext context) throws Exception
{
super.postConfigure(context);
}
/**
* @see org.eclipse.jetty.webapp.WebInfConfiguration#deconfigure(org.eclipse.jetty.webapp.WebAppContext)
*/
public void deconfigure(WebAppContext context) throws Exception
{
JettyWebAppContext jwac = (JettyWebAppContext)context;
{
//remove the unpacked wars
if (_unpackedOverlays != null && _unpackedOverlays.length>0)
if (_unpackedOverlayResources != null && !_unpackedOverlayResources.isEmpty())
{
try
{
for (int i=0; i<_unpackedOverlays.length; i++)
for (Resource r:_unpackedOverlayResources)
{
IO.delete(_unpackedOverlays[i].getFile());
IO.delete(r.getFile());
}
}
catch (IOException e)
@ -111,12 +139,11 @@ public class MavenWebInfConfiguration extends WebInfConfiguration
super.deconfigure(context);
//restore whatever the base resource was before we might have included overlaid wars
context.setBaseResource(_originalResourceBase);
}
/**
* @see org.eclipse.jetty.webapp.WebInfConfiguration#unpack(org.eclipse.jetty.webapp.WebAppContext)
*/
@ -126,62 +153,49 @@ public class MavenWebInfConfiguration extends WebInfConfiguration
//Unpack and find base resource as normal
super.unpack(context);
//Add in any overlays as a resource collection for the base
//Get the base resource for the "virtual" webapp
_originalResourceBase = context.getBaseResource();
JettyWebAppContext jwac = (JettyWebAppContext)context;
//Add in any overlaid wars as base resources
//determine sequencing of overlays
_unpackedOverlayResources = new ArrayList<Resource>();
if (jwac.getOverlays() != null && !jwac.getOverlays().isEmpty())
{
Resource[] origResources = null;
int origSize = 0;
List<Resource> resourceBaseCollection = new ArrayList<Resource>();
if (jwac.getBaseResource() != null)
for (Overlay o:jwac.getOverlays())
{
if (jwac.getBaseResource() instanceof ResourceCollection)
//can refer to the current project in list of overlays for ordering purposes
if (o.getConfig() != null && o.getConfig().isCurrentProject())
{
origResources = ((ResourceCollection)jwac.getBaseResource()).getResources();
origSize = origResources.length;
resourceBaseCollection.add(_originalResourceBase);
continue;
}
else
{
origResources = new Resource[1];
origResources[0] = jwac.getBaseResource();
origSize = 1;
}
}
int overlaySize = jwac.getOverlays().size();
Resource[] newResources = new Resource[origSize + overlaySize];
int offset = 0;
if (origSize > 0)
Resource unpacked = unpackOverlay(jwac,o);
_unpackedOverlayResources.add(unpacked); //remember the unpacked overlays for later so we can delete the tmp files
resourceBaseCollection.add(unpacked); //add in the selectively unpacked overlay in the correct order to the webapps resource base
}
if (!resourceBaseCollection.contains(_originalResourceBase))
{
if (jwac.getBaseAppFirst())
{
System.arraycopy(origResources,0,newResources,0,origSize);
offset = origSize;
LOG.info("Adding virtual project first in resource base list");
resourceBaseCollection.add(0, _originalResourceBase);
}
else
{
System.arraycopy(origResources,0,newResources,overlaySize,origSize);
LOG.info("Adding virtual project last in resource base list");
resourceBaseCollection.add(_originalResourceBase);
}
}
// Overlays are always unpacked
_unpackedOverlays = new Resource[overlaySize];
List<Resource> overlays = jwac.getOverlays();
for (int idx=0; idx<overlaySize; idx++)
{
LOG.info("Unpacking overlay: " + overlays.get(idx));
_unpackedOverlays[idx] = unpackOverlay(context, overlays.get(idx));
newResources[idx+offset] = _unpackedOverlays[idx];
LOG.info("Adding overlay: " + _unpackedOverlays[idx]);
}
jwac.setBaseResource(new ResourceCollection(newResources));
jwac.setBaseResource(new ResourceCollection(resourceBaseCollection.toArray(new Resource[resourceBaseCollection.size()])));
}
}
@ -225,25 +239,68 @@ public class MavenWebInfConfiguration extends WebInfConfiguration
protected Resource unpackOverlay (WebAppContext context, Resource overlay)
protected Resource unpackOverlay (WebAppContext context, Overlay overlay)
throws IOException
{
LOG.info("Unpacking overlay: " + overlay);
//resolve if not already resolved
resolveTempDirectory(context);
if (overlay.getResource() == null)
return null; //nothing to unpack
//Get the name of the overlayed war and unpack it to a dir of the
//same name in the temporary directory
String name = overlay.getName();
String name = overlay.getResource().getName();
if (name.endsWith("!/"))
name = name.substring(0,name.length()-2);
int i = name.lastIndexOf('/');
if (i>0)
name = name.substring(i+1,name.length());
name = name.replace('.', '_');
File dir = new File(context.getTempDirectory(), name);
overlay.copyTo(dir);
name = name+(++COUNTER); //add some digits to ensure uniqueness
File dir = new File(context.getTempDirectory(), name);
//if specified targetPath, unpack to that subdir instead
File unpackDir = dir;
if (overlay.getConfig() != null && overlay.getConfig().getTargetPath() != null)
unpackDir = new File (dir, overlay.getConfig().getTargetPath());
overlay.getResource().copyTo(unpackDir);
//use top level of unpacked content
Resource unpackedOverlay = Resource.newResource(dir.getCanonicalPath());
LOG.info("Unpacked overlay: "+overlay+" to "+unpackedOverlay);
return unpackedOverlay;
}
protected Artifact getArtifactForOverlay (OverlayConfig o, List<Artifact> warArtifacts)
{
if (o == null || warArtifacts == null || warArtifacts.isEmpty())
return null;
for (Artifact a:warArtifacts)
{
if (overlayMatchesArtifact (o, a))
{
return a;
}
}
return null;
}
protected boolean overlayMatchesArtifact(OverlayConfig o, Artifact a)
{
if ((o.getGroupId() == null && a.getGroupId() == null) || (o.getGroupId() != null && o.getGroupId().equals(a.getGroupId())))
{
if ((o.getArtifactId() == null && a.getArtifactId() == null) || (o.getArtifactId() != null && o.getArtifactId().equals(a.getArtifactId())))
{
if ((o.getClassifier() == null) || (o.getClassifier().equals(a.getClassifier())))
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,74 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.maven.plugin;
import org.eclipse.jetty.util.resource.Resource;
/**
* Overlay
*
*
*/
public class Overlay
{
private OverlayConfig _config;
private Resource _resource;
public Overlay (OverlayConfig config, Resource resource)
{
_config = config;
_resource = resource;
}
public Overlay (OverlayConfig config)
{
_config = config;
}
public void setResource (Resource r)
{
_resource = r;
}
public Resource getResource ()
{
return _resource;
}
public OverlayConfig getConfig ()
{
return _config;
}
public String toString()
{
StringBuffer strbuff = new StringBuffer();
if (_resource != null)
strbuff.append(_resource);
if (_config != null)
{
strbuff.append(" [");
strbuff.append(_config);
strbuff.append("]");
}
return strbuff.toString();
}
}

View File

@ -0,0 +1,304 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.maven.plugin;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import edu.emory.mathcs.backport.java.util.Arrays;
/**
* OverlayConfig
*
*
*/
public class OverlayConfig
{
private String targetPath;
private String groupId;
private String artifactId;
private String classifier;
private List<String> includes;
private List<String> excludes;
private boolean skip;
private boolean filtered;
public OverlayConfig() {}
public OverlayConfig(String fmt, List<String> defaultIncludes, List<String> defaultExcludes)
{
if (fmt == null)
return;
String[] atoms = fmt.split(",");
for (int i=0;i<atoms.length;i++)
{
String s = atoms[i].trim();
switch (i)
{
case 0:
{
if (!"".equals(s))
groupId = s;
break;
}
case 1:
{
if (!"".equals(s))
artifactId = s;
break;
}
case 2:
{
if (!"".equals(s))
classifier = s;
break;
}
case 3:
{
if (!"".equals(s))
targetPath = s;
break;
}
case 4:
{
if ("".equals(s))
skip = false;
else
skip = Boolean.valueOf(s);
break;
}
case 5:
{
if ("".equals(s))
filtered = false;
else
filtered = Boolean.valueOf(s);
break;
}
case 6:
{
if ("".equals(s))
break;
String[] incs = s.split(";");
if (incs.length > 0)
includes = Arrays.asList(incs);
break;
}
case 7:
{
if ("".equals(s))
break;
String[] exs = s.split(";");
if (exs.length > 0)
excludes = Arrays.asList(exs);
break;
}
}
}
}
public OverlayConfig(Xpp3Dom root, List<String> defaultIncludes, List<String> defaultExcludes)
{
Xpp3Dom node = root.getChild("groupId");
setGroupId(node==null?null:node.getValue());
node = root.getChild("artifactId");
setArtifactId(node==null?null:node.getValue());
node = root.getChild("classifier");
setClassifier(node==null?null:node.getValue());
node = root.getChild("targetPath");
setTargetPath(node==null?null:node.getValue());
node = root.getChild("skip");
setSkip(node==null?false:Boolean.valueOf(node.getValue()));
node = root.getChild("filtered");
setFiltered(node==null?false:Boolean.valueOf(node.getValue()));
node = root.getChild("includes");
List<String> includes = null;
if (node != null && node.getChildCount() > 0)
{
Xpp3Dom[] list = node.getChildren("include");
for (int j=0; list != null && j < list.length;j++)
{
if (includes == null)
includes = new ArrayList<String>();
includes.add(list[j].getValue());
}
}
if (includes == null && defaultIncludes != null)
{
includes = new ArrayList<String>();
includes.addAll(defaultIncludes);
}
setIncludes(includes);
node = root.getChild("excludes");
List<String> excludes = null;
if (node != null && node.getChildCount() > 0)
{
Xpp3Dom[] list = node.getChildren("exclude");
for (int j=0; list != null && j < list.length;j++)
{
if (excludes == null)
excludes = new ArrayList<String>();
excludes.add(list[j].getValue());
}
}
if (excludes == null && defaultExcludes != null)
{
excludes = new ArrayList<String>();
excludes.addAll(defaultExcludes);
}
setExcludes(excludes);
}
public String getTargetPath()
{
return targetPath;
}
public void setTargetPath(String targetPath)
{
this.targetPath = targetPath;
}
public String getGroupId()
{
return groupId;
}
public void setGroupId(String groupId)
{
this.groupId = groupId;
}
public String getArtifactId()
{
return artifactId;
}
public void setArtifactId(String artifactId)
{
this.artifactId = artifactId;
}
public String getClassifier()
{
return classifier;
}
public void setClassifier(String classifier)
{
this.classifier = classifier;
}
public List<String> getIncludes()
{
return includes;
}
public void setIncludes(List<String> includes)
{
this.includes = includes;
}
public List<String> getExcludes()
{
return excludes;
}
public void setExcludes(List<String> excludes)
{
this.excludes = excludes;
}
public boolean isSkip()
{
return skip;
}
public void setSkip(boolean skip)
{
this.skip = skip;
}
public boolean isFiltered()
{
return filtered;
}
public void setFiltered(boolean filtered)
{
this.filtered = filtered;
}
public boolean isCurrentProject()
{
if (this.groupId == null && this.artifactId == null)
return true;
return false;
}
public String toString()
{
StringBuffer strbuff = new StringBuffer();
strbuff.append((groupId != null ? groupId : "")+",");
strbuff.append((artifactId != null ? artifactId : "")+",");
strbuff.append((classifier != null ? classifier : "")+",");
strbuff.append((targetPath != null ? targetPath : "")+",");
strbuff.append(""+skip+",");
strbuff.append(""+filtered+",");
if (includes != null)
{
Iterator<String> itor = includes.iterator();
while (itor.hasNext())
{
strbuff.append(itor.next());
if (itor.hasNext())
strbuff.append(";");
}
}
strbuff.append(", ");
if (excludes != null)
{
Iterator<String> itor = excludes.iterator();
while (itor.hasNext())
{
strbuff.append(itor.next());
if (itor.hasNext())
strbuff.append(";");
}
}
return strbuff.toString();
}
}

View File

@ -0,0 +1,236 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.maven.plugin;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;
import org.codehaus.plexus.util.SelectorUtils;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.JarResource;
/**
* SelectiveJarResource
*
* Selectively copies resources from a jar file based on includes/excludes.
*
*/
public class SelectiveJarResource extends JarResource
{
private static final Logger LOG = Log.getLogger(SelectiveJarResource.class);
public static final List<String> DEFAULT_INCLUDES = Arrays.asList(new String[]{"**"});// No includes supplied, so set it to 'matches all'
public static final List<String> DEFAULT_EXCLUDES = Collections.emptyList(); //No includes, set to no exclusions
List<String> _includes = null;
List<String> _excludes = null;
boolean _caseSensitive = false;
/**
* @param url
*/
public SelectiveJarResource(URL url)
{
super(url);
}
/**
* @param url
* @param useCaches
*/
public SelectiveJarResource(URL url, boolean useCaches)
{
super(url, useCaches);
}
public void setCaseSensitive (boolean caseSensitive)
{
_caseSensitive = caseSensitive;
}
public void setIncludes (List<String> patterns)
{
_includes = patterns;
}
public void setExcludes (List<String> patterns)
{
_excludes = patterns;
}
protected boolean isIncluded (String name)
{
for (String include:_includes)
{
if (SelectorUtils.matchPath(include, name, _caseSensitive))
{
return true;
}
}
return false;
}
protected boolean isExcluded (String name)
{
for (String exclude:_excludes)
{
if (SelectorUtils.matchPath (exclude, name, _caseSensitive))
{
return true;
}
}
return false;
}
/**
* @see org.eclipse.jetty.util.resource.JarResource#copyTo(java.io.File)
*/
@Override
public void copyTo(File directory) throws IOException
{
if (_includes == null)
_includes = DEFAULT_INCLUDES;
if (_excludes == null)
_excludes = DEFAULT_EXCLUDES;
//Copy contents of the jar file to the given directory,
//using the includes and excludes patterns to control which
//parts of the jar file are copied
if (!exists())
return;
String urlString = this.getURL().toExternalForm().trim();
int endOfJarUrl = urlString.indexOf("!/");
int startOfJarUrl = (endOfJarUrl >= 0?4:0);
if (endOfJarUrl < 0)
throw new IOException("Not a valid jar url: "+urlString);
URL jarFileURL = new URL(urlString.substring(startOfJarUrl, endOfJarUrl));
InputStream is = jarFileURL.openConnection().getInputStream();
JarInputStream jin = new JarInputStream(is);
JarEntry entry;
while((entry=jin.getNextJarEntry())!=null)
{
String entryName = entry.getName();
LOG.debug("Looking at "+entryName);
String dotCheck = entryName.replace('\\', '/');
dotCheck = URIUtil.canonicalPath(dotCheck);
if (dotCheck == null)
{
LOG.info("Invalid entry: "+entryName);
continue;
}
File file=new File(directory,entryName);
if (entry.isDirectory())
{
if (isIncluded(entryName))
{
if (!isExcluded(entryName))
{
// Make directory
if (!file.exists())
file.mkdirs();
}
else
LOG.debug("{} dir is excluded", entryName);
}
else
LOG.debug("{} dir is NOT included", entryName);
}
else
{
//entry is a file, is it included?
if (isIncluded(entryName))
{
if (!isExcluded(entryName))
{
// make directory (some jars don't list dirs)
File dir = new File(file.getParent());
if (!dir.exists())
dir.mkdirs();
// Make file
FileOutputStream fout = null;
try
{
fout = new FileOutputStream(file);
IO.copy(jin,fout);
}
finally
{
IO.close(fout);
}
// touch the file.
if (entry.getTime()>=0)
file.setLastModified(entry.getTime());
}
else
LOG.debug("{} file is excluded", entryName);
}
else
LOG.debug("{} file is NOT included", entryName);
}
}
Manifest manifest = jin.getManifest();
if (manifest != null)
{
if (isIncluded("META-INF") && !isExcluded("META-INF"))
{
File metaInf = new File (directory, "META-INF");
metaInf.mkdir();
File f = new File(metaInf, "MANIFEST.MF");
FileOutputStream fout = new FileOutputStream(f);
manifest.write(fout);
fout.close();
}
}
IO.close(jin);
}
}

View File

@ -20,24 +20,33 @@ package org.eclipse.jetty.maven.plugin;
import java.io.File;
import java.io.FileInputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Set;
import java.util.TreeMap;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceCollection;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.xml.XmlConfiguration;
/**
* Starter
*
* Class which is exec'ed to create a new jetty process. Used by the JettyRunForked mojo.
*
*/
public class Starter
{
public static final String PORT_SYSPROPERTY = "jetty.port";
@ -56,7 +65,57 @@ public class Starter
private String token;
/**
* Artifact
*
* A mock maven Artifact class as the maven jars are not put onto the classpath for the
* execution of this class.
*
*/
public class Artifact
{
public String gid;
public String aid;
public String path;
public Resource resource;
public Artifact (String csv)
{
if (csv != null && !"".equals(csv))
{
String[] atoms = csv.split(",");
if (atoms.length >= 3)
{
gid = atoms[0].trim();
aid = atoms[1].trim();
path = atoms[2].trim();
}
}
}
public Artifact (String gid, String aid, String path)
{
this.gid = gid;
this.aid = aid;
this.path = path;
}
public boolean equals(Object o)
{
if (!(o instanceof Artifact))
return false;
Artifact ao = (Artifact)o;
return (((gid == null && ao.gid == null) || (gid != null && gid.equals(ao.gid)))
&& ((aid == null && ao.aid == null) || (aid != null && aid.equals(ao.aid))));
}
}
/**
* @throws Exception
*/
public void configureJetty () throws Exception
{
LOG.debug("Starting Jetty Server ...");
@ -115,6 +174,9 @@ public class Starter
}
/**
* @throws Exception
*/
public void configureWebApp ()
throws Exception
{
@ -127,39 +189,105 @@ public class Starter
if (str != null)
webApp.setContextPath(str);
// - web.xml
str = (String)props.get("web.xml");
if (str != null)
webApp.setDescriptor(str);
webApp.setDescriptor(str);
// - the tmp directory
str = (String)props.getProperty("tmp.dir");
if (str != null)
webApp.setTempDirectory(new File(str.trim()));
// - the base directory
str = (String)props.getProperty("base.dir");
if (str != null && !"".equals(str.trim()))
webApp.setWar(str);
// - the multiple comma separated resource dirs
str = (String)props.getProperty("res.dirs");
if (str != null && !"".equals(str.trim()))
{
ResourceCollection resources = new ResourceCollection(str);
webApp.setBaseResource(resources);
webApp.setWar(str);
webApp.setBaseResource(Resource.newResource(str));
}
// - overlays
str = (String)props.getProperty("overlay.files");
// - put virtual webapp base resource first on resource path or not
str = (String)props.getProperty("base.first");
if (str != null && !"".equals(str.trim()))
webApp.setBaseAppFirst(Boolean.getBoolean(str));
//For overlays
str = (String)props.getProperty("maven.war.includes");
List<String> defaultWarIncludes = fromCSV(str);
str = (String)props.getProperty("maven.war.excludes");
List<String> defaultWarExcludes = fromCSV(str);
//List of war artifacts
List<Artifact> wars = new ArrayList<Artifact>();
//List of OverlayConfigs
TreeMap<String, OverlayConfig> orderedConfigs = new TreeMap<String, OverlayConfig>();
Enumeration<String> pnames = (Enumeration<String>)props.propertyNames();
while (pnames.hasMoreElements())
{
List<Resource> overlays = new ArrayList<Resource>();
String[] names = str.split(",");
for (int j=0; names != null && j < names.length; j++)
overlays.add(Resource.newResource("jar:"+Resource.toURL(new File(names[j].trim())).toString()+"!/"));
webApp.setOverlays(overlays);
String n = pnames.nextElement();
if (n.startsWith("maven.war.artifact"))
{
Artifact a = new Artifact((String)props.get(n));
a.resource = Resource.newResource("jar:"+Resource.toURL(new File(a.path)).toString()+"!/");
wars.add(a);
}
else if (n.startsWith("maven.war.overlay"))
{
OverlayConfig c = new OverlayConfig ((String)props.get(n), defaultWarIncludes, defaultWarExcludes);
orderedConfigs.put(n,c);
}
}
Set<Artifact> matchedWars = new HashSet<Artifact>();
//process any overlays and the war type artifacts
List<Overlay> overlays = new ArrayList<Overlay>();
for (OverlayConfig config:orderedConfigs.values())
{
//overlays can be individually skipped
if (config.isSkip())
continue;
//an empty overlay refers to the current project - important for ordering
if (config.isCurrentProject())
{
Overlay overlay = new Overlay(config, null);
overlays.add(overlay);
continue;
}
//if a war matches an overlay config
Artifact a = getArtifactForOverlayConfig(config, wars);
if (a != null)
{
matchedWars.add(a);
SelectiveJarResource r = new SelectiveJarResource(new URL("jar:"+Resource.toURL(new File(a.path)).toString()+"!/"));
r.setIncludes(config.getIncludes());
r.setExcludes(config.getExcludes());
Overlay overlay = new Overlay(config, r);
overlays.add(overlay);
}
}
//iterate over the left over war artifacts and unpack them (without include/exclude processing) as necessary
for (Artifact a: wars)
{
if (!matchedWars.contains(a))
{
Overlay overlay = new Overlay(null, a.resource);
overlays.add(overlay);
}
}
webApp.setOverlays(overlays);
// - the equivalent of web-inf classes
str = (String)props.getProperty("classes.dir");
@ -188,6 +316,10 @@ public class Starter
}
/**
* @param args
* @throws Exception
*/
public void getConfiguration (String[] args)
throws Exception
{
@ -235,6 +367,9 @@ public class Starter
}
/**
* @throws Exception
*/
public void run() throws Exception
{
if (monitor != null)
@ -245,12 +380,18 @@ public class Starter
}
/**
* @throws Exception
*/
public void join () throws Exception
{
server.join();
}
/**
* @param e
*/
public void communicateStartupResult (Exception e)
{
if (token != null)
@ -263,6 +404,9 @@ public class Starter
}
/**
* @throws Exception
*/
public void applyJettyXml() throws Exception
{
if (jettyXmls == null)
@ -279,6 +423,10 @@ public class Starter
/**
* @param handler
* @param handlers
*/
protected void prependHandler (Handler handler, HandlerCollection handlers)
{
if (handler == null || handlers == null)
@ -292,6 +440,55 @@ public class Starter
}
/**
* @param c
* @param wars
* @return
*/
protected Artifact getArtifactForOverlayConfig (OverlayConfig c, List<Artifact> wars)
{
if (wars == null || wars.isEmpty() || c == null)
return null;
Artifact war = null;
Iterator<Artifact> itor = wars.iterator();
while(itor.hasNext() && war == null)
{
Artifact a = itor.next();
if (((c.getGroupId() == null && a.gid == null) || (c.getGroupId() != null && c.getGroupId().equals(a.gid)))
&& ((c.getArtifactId() == null && a.aid == null) || (c.getArtifactId() != null && c.getArtifactId().equals(a.aid)))
&& ((c.getClassifier() == null) || (c.getClassifier().equals(a.aid))))
{
war = a;
}
}
return war;
}
/**
* @param csv
* @return
*/
private List<String> fromCSV (String csv)
{
if (csv == null || "".equals(csv.trim()))
return null;
String[] atoms = csv.split(",");
List<String> list = new ArrayList<String>();
for (String a:atoms)
{
list.add(a.trim());
}
return list;
}
/**
* @param args
*/
public static final void main(String[] args)
{
if (args == null)

View File

@ -0,0 +1,203 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.maven.plugin;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.apache.maven.model.Plugin;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.util.xml.Xpp3Dom;
/**
* WarPluginInfo
*
* Information about the maven-war-plugin contained in the pom
*/
public class WarPluginInfo
{
private MavenProject _project;
private Plugin _plugin;
private List<String> _dependentMavenWarIncludes;
private List<String> _dependentMavenWarExcludes;
private List<OverlayConfig> _overlayConfigs;
/**
* @param project
*/
public WarPluginInfo (MavenProject project)
{
_project = project;
}
/**
* Find the maven-war-plugin, if one is configured
* @return
*/
public Plugin getPlugin()
{
if (_plugin == null)
{
List plugins = _project.getBuildPlugins();
if (plugins == null)
return null;
Iterator itor = plugins.iterator();
while (itor.hasNext() && _plugin==null)
{
Plugin plugin = (Plugin)itor.next();
if ("maven-war-plugin".equals(plugin.getArtifactId()))
_plugin = plugin;
}
}
return _plugin;
}
/**
* Get value of dependentWarIncludes for maven-war-plugin
* @return
*/
public List<String> getDependentMavenWarIncludes()
{
if (_dependentMavenWarIncludes == null)
{
getPlugin();
if (_plugin == null)
return null;
Xpp3Dom node = (Xpp3Dom)_plugin.getConfiguration();
if (node == null)
return null;
node = node.getChild("dependentWarIncludes");
if (node == null)
return null;
String val = node.getValue();
_dependentMavenWarIncludes = Arrays.asList(val.split(","));
}
return _dependentMavenWarIncludes;
}
/**
* Get value of dependentWarExcludes for maven-war-plugin
* @return
*/
public List<String> getDependentMavenWarExcludes()
{
if (_dependentMavenWarExcludes == null)
{
getPlugin();
if (_plugin == null)
return null;
Xpp3Dom node = (Xpp3Dom)_plugin.getConfiguration();
if (node == null)
return null;
node = node.getChild("dependentWarExcludes");
if (node == null)
return null;
String val = node.getValue();
_dependentMavenWarExcludes = Arrays.asList(val.split(","));
}
return _dependentMavenWarExcludes;
}
/**
* Get config for any overlays that have been declared for the maven-war-plugin.
*
* @return
*/
public List<OverlayConfig> getMavenWarOverlayConfigs ()
{
if (_overlayConfigs == null)
{
getPlugin();
if (_plugin == null)
return Collections.emptyList();
getDependentMavenWarIncludes();
getDependentMavenWarExcludes();
Xpp3Dom node = (Xpp3Dom)_plugin.getConfiguration();
if (node == null)
return Collections.emptyList();
node = node.getChild("overlays");
if (node == null)
return Collections.emptyList();
Xpp3Dom[] nodes = node.getChildren("overlay");
if (nodes == null)
return Collections.emptyList();
_overlayConfigs = new ArrayList<OverlayConfig>();
for (int i=0;i<nodes.length;i++)
{
OverlayConfig overlayConfig = new OverlayConfig(nodes[i], _dependentMavenWarIncludes, _dependentMavenWarExcludes);
_overlayConfigs.add(overlayConfig);
}
}
return _overlayConfigs;
}
/**
* @return the xml as a string
*/
public String getMavenWarOverlayConfigAsString ()
{
getPlugin();
if (_plugin == null)
return "";
Xpp3Dom node = (Xpp3Dom)_plugin.getConfiguration();
if (node == null)
return "";
return node.toString();
}
}

View File

@ -22,11 +22,6 @@
<npn-version>1.1.0.v20120525</npn-version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit-dep</artifactId>
<version>4.10</version>
</dependency>
<!-- Pax Exam Dependencies -->
<!-- OPS4J Swissbox Dependencies -->
<dependency>
@ -233,6 +228,18 @@
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-api</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-common</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-client</artifactId>
@ -241,7 +248,7 @@
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-core</artifactId>
<artifactId>websocket-servlet</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
@ -251,6 +258,11 @@
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.drafts</groupId>
<artifactId>javax.websocket-api</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.spdy</groupId>
@ -319,6 +331,11 @@
<classifier>webbundle</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>

View File

@ -1,13 +1,11 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
<!DOCTYPE Configure PUBLIC "-" "http://www.eclipse.org/jetty/configure.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<!-- =========================================================== -->
<!-- Configure Authentication Login Service -->
<!-- Realms may be configured for the entire server here, or -->
<!-- they can be configured for a specific web app in a context -->
<!-- configuration (see $(jetty.home)/contexts/test.xml for an -->
<!-- configuration (see $(jetty.home)/webapps/test.xml for an -->
<!-- example). -->
<!-- =========================================================== -->
<Call name="addBean">
@ -19,5 +17,4 @@
</New>
</Arg>
</Call>
</Configure>
</Configure>

View File

@ -103,7 +103,10 @@ public class AbstractTestOSGi
{
diagnoseNonActiveOrNonResolvedBundle(b);
}
Assert.assertTrue(b.getState() == Bundle.ACTIVE || b.getState() == Bundle.RESOLVED);
Assert.assertTrue("Bundle: " + b + " (state should be " +
"ACTIVE[" + Bundle.ACTIVE + "] or RESOLVED[" + Bundle.RESOLVED + "]" +
", but was [" + b.getState() + "])",
(b.getState() == Bundle.ACTIVE) || (b.getState() == Bundle.RESOLVED));
}
}

View File

@ -86,10 +86,12 @@ public class TestJettyOSGiBootCore extends AbstractTestOSGi {
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-security" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-servlets" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty" ).artifactId( "jetty-client" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty.websocket" ).artifactId( "websocket-core" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty.drafts" ).artifactId( "javax.websocket-api" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty.websocket" ).artifactId( "websocket-api" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty.websocket" ).artifactId( "websocket-common" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty.websocket" ).artifactId( "websocket-servlet" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty.websocket" ).artifactId( "websocket-server" ).versionAsInProject().noStart());
//optional:
//res.add(mavenBundle().groupId( "org.eclipse.jetty.websocket" ).artifactId( "websocket-client" ).versionAsInProject().noStart());
res.add(mavenBundle().groupId( "org.eclipse.jetty.websocket" ).artifactId( "websocket-client" ).versionAsInProject().noStart());
return res;
}

View File

@ -1,88 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>jetty-project</artifactId>
<groupId>org.eclipse.jetty</groupId>
<version>9.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-proxy</artifactId>
<name>Jetty :: Proxy</name>
<description>Jetty Proxy</description>
<properties>
<bundle-symbolic-name>${project.groupId}.servlets</bundle-symbolic-name>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>manifest</goal>
</goals>
<configuration>
<instructions>
<Import-Package>javax.servlet.*;version="2.6.0",*</Import-Package>
</instructions>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<!--
Required for OSGI
-->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<configuration>
<onlyAnalyze>org.eclipse.jetty.servlets.*</onlyAnalyze>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-continuation</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-client</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.orbit</groupId>
<artifactId>javax.servlet</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -1,62 +0,0 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
<!-- =============================================================== -->
<!-- Configure the Jetty Server -->
<!-- -->
<!-- Documentation of this file format can be found at: -->
<!-- http://docs.codehaus.org/display/JETTY/jetty.xml -->
<!-- -->
<!-- =============================================================== -->
<Configure id="Proxy" class="org.eclipse.jetty.server.Server">
<!-- =========================================================== -->
<!-- Server Thread Pool -->
<!-- =========================================================== -->
<Set name="ThreadPool">
<!-- Default queued blocking threadpool
-->
<New class="org.eclipse.jetty.util.thread.QueuedThreadPool">
<Set name="minThreads">10</Set>
<Set name="maxThreads">50</Set>
</New>
</Set>
<!-- =========================================================== -->
<!-- Set connectors -->
<!-- =========================================================== -->
<Call name="addConnector">
<Arg>
<New class="org.eclipse.jetty.server.ServerConnector">
<Set name="host"><Property name="jetty.host" /></Set>
<Set name="port"><Property name="jetty.port" default="8888"/></Set>
<Set name="idleTimeout">300000</Set>
<Set name="Acceptors">2</Set>
<Set name="statsOn">false</Set>
</New>
</Arg>
</Call>
<!-- =========================================================== -->
<Set name="handler">
<New id="Servlets" class="org.eclipse.jetty.servlet.ServletHandler">
<Call name="addServletWithMapping">
<Arg>org.eclipse.jetty.servlets.ProxyServlet</Arg>
<Arg>/</Arg>
</Call>
</New>
</Set>
<!-- =========================================================== -->
<!-- extra options -->
<!-- =========================================================== -->
<Set name="stopAtShutdown">true</Set>
<Set name="sendServerVersion">true</Set>
<Set name="sendDateHeader">true</Set>
<Set name="stopTimeout">1000</Set>
</Configure>

View File

@ -1,422 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.proxy;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.UnavailableException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.server.Request;
/**
* 6
*/
public class BalancerServlet extends ProxyServlet
{
private static final class BalancerMember
{
private String _name;
private String _proxyTo;
private HttpURI _backendURI;
public BalancerMember(String name, String proxyTo)
{
super();
_name = name;
_proxyTo = proxyTo;
_backendURI = new HttpURI(_proxyTo);
}
public String getProxyTo()
{
return _proxyTo;
}
public HttpURI getBackendURI()
{
return _backendURI;
}
@Override
public String toString()
{
return "BalancerMember [_name=" + _name + ", _proxyTo=" + _proxyTo + "]";
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((_name == null)?0:_name.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
BalancerMember other = (BalancerMember)obj;
if (_name == null)
{
if (other._name != null)
return false;
}
else if (!_name.equals(other._name))
return false;
return true;
}
}
private static final class RoundRobinIterator implements Iterator<BalancerMember>
{
private BalancerMember[] _balancerMembers;
private AtomicInteger _index;
public RoundRobinIterator(Collection<BalancerMember> balancerMembers)
{
_balancerMembers = (BalancerMember[])balancerMembers.toArray(new BalancerMember[balancerMembers.size()]);
_index = new AtomicInteger(-1);
}
public boolean hasNext()
{
return true;
}
public BalancerMember next()
{
BalancerMember balancerMember = null;
while (balancerMember == null)
{
int currentIndex = _index.get();
int nextIndex = (currentIndex + 1) % _balancerMembers.length;
if (_index.compareAndSet(currentIndex,nextIndex))
{
balancerMember = _balancerMembers[nextIndex];
}
}
return balancerMember;
}
public void remove()
{
throw new UnsupportedOperationException();
}
}
private static final String BALANCER_MEMBER_PREFIX = "BalancerMember.";
private static final List<String> FORBIDDEN_CONFIG_PARAMETERS;
static
{
List<String> params = new LinkedList<String>();
params.add("HostHeader");
params.add("whiteList");
params.add("blackList");
FORBIDDEN_CONFIG_PARAMETERS = Collections.unmodifiableList(params);
}
private static final List<String> REVERSE_PROXY_HEADERS;
static
{
List<String> params = new LinkedList<String>();
params.add("Location");
params.add("Content-Location");
params.add("URI");
REVERSE_PROXY_HEADERS = Collections.unmodifiableList(params);
}
private static final String JSESSIONID = "jsessionid";
private static final String JSESSIONID_URL_PREFIX = JSESSIONID + "=";
private boolean _stickySessions;
private Set<BalancerMember> _balancerMembers = new HashSet<BalancerMember>();
private boolean _proxyPassReverse;
private RoundRobinIterator _roundRobinIterator;
@Override
public void init(ServletConfig config) throws ServletException
{
validateConfig(config);
super.init(config);
initStickySessions(config);
initBalancers(config);
initProxyPassReverse(config);
postInit();
}
private void validateConfig(ServletConfig config) throws ServletException
{
@SuppressWarnings("unchecked")
List<String> initParameterNames = Collections.list(config.getInitParameterNames());
for (String initParameterName : initParameterNames)
{
if (FORBIDDEN_CONFIG_PARAMETERS.contains(initParameterName))
{
throw new UnavailableException(initParameterName + " not supported in " + getClass().getName());
}
}
}
private void initStickySessions(ServletConfig config) throws ServletException
{
_stickySessions = "true".equalsIgnoreCase(config.getInitParameter("StickySessions"));
}
private void initBalancers(ServletConfig config) throws ServletException
{
Set<String> balancerNames = getBalancerNames(config);
for (String balancerName : balancerNames)
{
String memberProxyToParam = BALANCER_MEMBER_PREFIX + balancerName + ".ProxyTo";
String proxyTo = config.getInitParameter(memberProxyToParam);
if (proxyTo == null || proxyTo.trim().length() == 0)
{
throw new UnavailableException(memberProxyToParam + " parameter is empty.");
}
_balancerMembers.add(new BalancerMember(balancerName,proxyTo));
}
}
private void initProxyPassReverse(ServletConfig config)
{
_proxyPassReverse = "true".equalsIgnoreCase(config.getInitParameter("ProxyPassReverse"));
}
private void postInit()
{
_roundRobinIterator = new RoundRobinIterator(_balancerMembers);
}
private Set<String> getBalancerNames(ServletConfig config) throws ServletException
{
Set<String> names = new HashSet<String>();
@SuppressWarnings("unchecked")
List<String> initParameterNames = Collections.list(config.getInitParameterNames());
for (String initParameterName : initParameterNames)
{
if (!initParameterName.startsWith(BALANCER_MEMBER_PREFIX))
{
continue;
}
int endOfNameIndex = initParameterName.lastIndexOf(".");
if (endOfNameIndex <= BALANCER_MEMBER_PREFIX.length())
{
throw new UnavailableException(initParameterName + " parameter does not provide a balancer member name");
}
names.add(initParameterName.substring(BALANCER_MEMBER_PREFIX.length(),endOfNameIndex));
}
return names;
}
@Override
protected HttpURI proxyHttpURI(HttpServletRequest request, String uri) throws MalformedURLException
{
BalancerMember balancerMember = selectBalancerMember(request);
try
{
URI dstUri = new URI(balancerMember.getProxyTo() + "/" + uri).normalize();
return new HttpURI(dstUri.toString());
}
catch (URISyntaxException e)
{
throw new MalformedURLException(e.getMessage());
}
}
private BalancerMember selectBalancerMember(HttpServletRequest request)
{
BalancerMember balancerMember = null;
if (_stickySessions)
{
String name = getBalancerMemberNameFromSessionId(request);
if (name != null)
{
balancerMember = findBalancerMemberByName(name);
if (balancerMember != null)
{
return balancerMember;
}
}
}
return _roundRobinIterator.next();
}
private BalancerMember findBalancerMemberByName(String name)
{
BalancerMember example = new BalancerMember(name,"");
for (BalancerMember balancerMember : _balancerMembers)
{
if (balancerMember.equals(example))
{
return balancerMember;
}
}
return null;
}
private String getBalancerMemberNameFromSessionId(HttpServletRequest request)
{
String name = getBalancerMemberNameFromSessionCookie(request);
if (name == null)
{
name = getBalancerMemberNameFromURL(request);
}
return name;
}
private String getBalancerMemberNameFromSessionCookie(HttpServletRequest request)
{
Cookie[] cookies = request.getCookies();
String name = null;
for (Cookie cookie : cookies)
{
if (JSESSIONID.equalsIgnoreCase(cookie.getName()))
{
name = extractBalancerMemberNameFromSessionId(cookie.getValue());
break;
}
}
return name;
}
private String getBalancerMemberNameFromURL(HttpServletRequest request)
{
String name = null;
String requestURI = request.getRequestURI();
int idx = requestURI.lastIndexOf(";");
if (idx != -1)
{
String requestURISuffix = requestURI.substring(idx);
if (requestURISuffix.startsWith(JSESSIONID_URL_PREFIX))
{
name = extractBalancerMemberNameFromSessionId(requestURISuffix.substring(JSESSIONID_URL_PREFIX.length()));
}
}
return name;
}
private String extractBalancerMemberNameFromSessionId(String sessionId)
{
String name = null;
int idx = sessionId.lastIndexOf(".");
if (idx != -1)
{
String sessionIdSuffix = sessionId.substring(idx + 1);
name = (sessionIdSuffix.length() > 0)?sessionIdSuffix:null;
}
return name;
}
@Override
protected String filterResponseHeaderValue(String headerName, String headerValue, HttpServletRequest request)
{
if (_proxyPassReverse && REVERSE_PROXY_HEADERS.contains(headerName))
{
HttpURI locationURI = new HttpURI(headerValue);
if (isAbsoluteLocation(locationURI) && isBackendLocation(locationURI))
{
Request jettyRequest = (Request)request;
URI reverseUri;
try
{
reverseUri = new URI(jettyRequest.getRootURL().append(locationURI.getCompletePath()).toString()).normalize();
return reverseUri.toURL().toString();
}
catch (Exception e)
{
_log.warn("Not filtering header response",e);
return headerValue;
}
}
}
return headerValue;
}
private boolean isBackendLocation(HttpURI locationURI)
{
for (BalancerMember balancerMember : _balancerMembers)
{
HttpURI backendURI = balancerMember.getBackendURI();
if (backendURI.getHost().equals(locationURI.getHost()) && backendURI.getScheme().equals(locationURI.getScheme())
&& backendURI.getPort() == locationURI.getPort())
{
return true;
}
}
return false;
}
private boolean isAbsoluteLocation(HttpURI locationURI)
{
return locationURI.getHost() != null;
}
@Override
public String getHostHeader()
{
throw new UnsupportedOperationException("HostHeader not supported in " + getClass().getName());
}
@Override
public void setHostHeader(String hostHeader)
{
throw new UnsupportedOperationException("HostHeader not supported in " + getClass().getName());
}
@Override
public boolean validateDestination(String host, String path)
{
return true;
}
}

View File

@ -1,902 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.proxy;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.continuation.Continuation;
import org.eclipse.jetty.continuation.ContinuationSupport;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.PathMap;
import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.util.HostMap;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
/**
* Asynchronous Proxy Servlet.
*
* Forward requests to another server either as a standard web proxy (as defined by RFC2616) or as a transparent proxy.
* <p>
* This servlet needs the jetty-util and jetty-client classes to be available to the web application.
* <p>
* To facilitate JMX monitoring, the "HttpClient" and "ThreadPool" are set as context attributes prefixed with the servlet name.
* <p>
* The following init parameters may be used to configure the servlet:
* <ul>
* <li>name - Name of Proxy servlet (default: "ProxyServlet"
* <li>maxThreads - maximum threads
* <li>maxConnections - maximum connections per destination
* <li>timeout - the period in ms the client will wait for a response from the proxied server
* <li>idleTimeout - the period in ms a connection to proxied server can be idle for before it is closed
* <li>requestHeaderSize - the size of the request header buffer (d. 6,144)
* <li>requestBufferSize - the size of the request buffer (d. 12,288)
* <li>responseHeaderSize - the size of the response header buffer (d. 6,144)
* <li>responseBufferSize - the size of the response buffer (d. 32,768)
* <li>HostHeader - Force the host header to a particular value
* <li>whiteList - comma-separated list of allowed proxy destinations
* <li>blackList - comma-separated list of forbidden proxy destinations
* </ul>
*
* @see org.eclipse.jetty.server.handler.ConnectHandler
*/
public class ProxyServlet implements Servlet
{
protected Logger _log;
protected HttpClient _client;
protected String _hostHeader;
protected HashSet<String> _DontProxyHeaders = new HashSet<String>();
{
_DontProxyHeaders.add("proxy-connection");
_DontProxyHeaders.add("connection");
_DontProxyHeaders.add("keep-alive");
_DontProxyHeaders.add("transfer-encoding");
_DontProxyHeaders.add("te");
_DontProxyHeaders.add("trailer");
_DontProxyHeaders.add("proxy-authorization");
_DontProxyHeaders.add("proxy-authenticate");
_DontProxyHeaders.add("upgrade");
}
protected ServletConfig _config;
protected ServletContext _context;
protected HostMap<PathMap> _white = new HostMap<PathMap>();
protected HostMap<PathMap> _black = new HostMap<PathMap>();
/* ------------------------------------------------------------ */
/*
* (non-Javadoc)
*
* @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
*/
public void init(ServletConfig config) throws ServletException
{
_config = config;
_context = config.getServletContext();
_hostHeader = config.getInitParameter("HostHeader");
try
{
_log = createLogger(config);
_client = createHttpClient(config);
if (_context != null)
{
_context.setAttribute(config.getServletName() + ".ThreadPool",_client.getThreadPool());
_context.setAttribute(config.getServletName() + ".HttpClient",_client);
}
String white = config.getInitParameter("whiteList");
if (white != null)
{
parseList(white,_white);
}
String black = config.getInitParameter("blackList");
if (black != null)
{
parseList(black,_black);
}
}
catch (Exception e)
{
throw new ServletException(e);
}
}
public void destroy()
{
try
{
_client.stop();
}
catch (Exception x)
{
_log.debug(x);
}
}
/**
* Create and return a logger based on the ServletConfig for use in the
* proxy servlet
*
* @param config
* @return Logger
*/
protected Logger createLogger(ServletConfig config)
{
return Log.getLogger("org.eclipse.jetty.servlets." + config.getServletName());
}
/**
* Create and return an HttpClientInstance
*
* @return HttpClient
*/
protected HttpClient createHttpClientInstance()
{
return new HttpClient();
}
/**
* Create and return an HttpClient based on ServletConfig
*
* By default this implementation will create an instance of the
* HttpClient for use by this proxy servlet.
*
* @param config
* @return HttpClient
* @throws Exception
*/
protected HttpClient createHttpClient(ServletConfig config) throws Exception
{
HttpClient client = createHttpClientInstance();
client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
String t = config.getInitParameter("maxThreads");
if (t != null)
{
client.setThreadPool(new QueuedThreadPool(Integer.parseInt(t)));
}
else
{
client.setThreadPool(new QueuedThreadPool());
}
((QueuedThreadPool)client.getThreadPool()).setName(config.getServletName());
t = config.getInitParameter("maxConnections");
if (t != null)
{
client.setMaxConnectionsPerAddress(Integer.parseInt(t));
}
t = config.getInitParameter("timeout");
if ( t != null )
{
client.setTimeout(Long.parseLong(t));
}
t = config.getInitParameter("idleTimeout");
if ( t != null )
{
client.setIdleTimeout(Long.parseLong(t));
}
t = config.getInitParameter("requestHeaderSize");
if ( t != null )
{
client.setRequestHeaderSize(Integer.parseInt(t));
}
t = config.getInitParameter("requestBufferSize");
if ( t != null )
{
client.setRequestBufferSize(Integer.parseInt(t));
}
t = config.getInitParameter("responseHeaderSize");
if ( t != null )
{
client.setResponseHeaderSize(Integer.parseInt(t));
}
t = config.getInitParameter("responseBufferSize");
if ( t != null )
{
client.setResponseBufferSize(Integer.parseInt(t));
}
client.start();
return client;
}
/* ------------------------------------------------------------ */
/**
* Helper function to process a parameter value containing a list of new entries and initialize the specified host map.
*
* @param list
* comma-separated list of new entries
* @param hostMap
* target host map
*/
private void parseList(String list, HostMap<PathMap> hostMap)
{
if (list != null && list.length() > 0)
{
int idx;
String entry;
StringTokenizer entries = new StringTokenizer(list,",");
while (entries.hasMoreTokens())
{
entry = entries.nextToken();
idx = entry.indexOf('/');
String host = idx > 0?entry.substring(0,idx):entry;
String path = idx > 0?entry.substring(idx):"/*";
host = host.trim();
PathMap pathMap = hostMap.get(host);
if (pathMap == null)
{
pathMap = new PathMap(true);
hostMap.put(host,pathMap);
}
if (path != null)
{
pathMap.put(path,path);
}
}
}
}
/* ------------------------------------------------------------ */
/**
* Check the request hostname and path against white- and blacklist.
*
* @param host
* hostname to check
* @param path
* path to check
* @return true if request is allowed to be proxied
*/
public boolean validateDestination(String host, String path)
{
if (_white.size() > 0)
{
boolean match = false;
Object whiteObj = _white.getLazyMatches(host);
if (whiteObj != null)
{
List whiteList = (whiteObj instanceof List)?(List)whiteObj:Collections.singletonList(whiteObj);
for (Object entry : whiteList)
{
PathMap pathMap = ((Map.Entry<String, PathMap>)entry).getValue();
if (match = (pathMap != null && (pathMap.size() == 0 || pathMap.match(path) != null)))
break;
}
}
if (!match)
return false;
}
if (_black.size() > 0)
{
Object blackObj = _black.getLazyMatches(host);
if (blackObj != null)
{
List blackList = (blackObj instanceof List)?(List)blackObj:Collections.singletonList(blackObj);
for (Object entry : blackList)
{
PathMap pathMap = ((Map.Entry<String, PathMap>)entry).getValue();
if (pathMap != null && (pathMap.size() == 0 || pathMap.match(path) != null))
return false;
}
}
}
return true;
}
/* ------------------------------------------------------------ */
/*
* (non-Javadoc)
*
* @see javax.servlet.Servlet#getServletConfig()
*/
public ServletConfig getServletConfig()
{
return _config;
}
/* ------------------------------------------------------------ */
/**
* Get the hostHeader.
*
* @return the hostHeader
*/
public String getHostHeader()
{
return _hostHeader;
}
/* ------------------------------------------------------------ */
/**
* Set the hostHeader.
*
* @param hostHeader
* the hostHeader to set
*/
public void setHostHeader(String hostHeader)
{
_hostHeader = hostHeader;
}
/* ------------------------------------------------------------ */
/*
* (non-Javadoc)
*
* @see javax.servlet.Servlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
*/
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
{
final int debug = _log.isDebugEnabled()?req.hashCode():0;
final HttpServletRequest request = (HttpServletRequest)req;
final HttpServletResponse response = (HttpServletResponse)res;
if ("CONNECT".equalsIgnoreCase(request.getMethod()))
{
handleConnect(request,response);
}
else
{
final InputStream in = request.getInputStream();
final OutputStream out = response.getOutputStream();
final Continuation continuation = ContinuationSupport.getContinuation(request);
if (!continuation.isInitial())
response.sendError(HttpServletResponse.SC_GATEWAY_TIMEOUT); // Need better test that isInitial
else
{
String uri = request.getRequestURI();
if (request.getQueryString() != null)
uri += "?" + request.getQueryString();
HttpURI url = proxyHttpURI(request,uri);
if (debug != 0)
_log.debug(debug + " proxy " + uri + "-->" + url);
if (url == null)
{
response.sendError(HttpServletResponse.SC_FORBIDDEN);
return;
}
HttpExchange exchange = new HttpExchange()
{
@Override
protected void onRequestCommitted() throws IOException
{
}
@Override
protected void onRequestComplete() throws IOException
{
}
@Override
protected void onResponseComplete() throws IOException
{
if (debug != 0)
_log.debug(debug + " complete");
continuation.complete();
}
@Override
protected void onResponseContent(Buffer content) throws IOException
{
if (debug != 0)
_log.debug(debug + " content" + content.length());
content.writeTo(out);
}
@Override
protected void onResponseHeaderComplete() throws IOException
{
}
@Override
protected void onResponseStatus(Buffer version, int status, Buffer reason) throws IOException
{
if (debug != 0)
_log.debug(debug + " " + version + " " + status + " " + reason);
if (reason != null && reason.length() > 0)
response.setStatus(status,reason.toString());
else
response.setStatus(status);
}
@Override
protected void onResponseHeader(Buffer name, Buffer value) throws IOException
{
String nameString = name.toString();
String s = nameString.toLowerCase(Locale.ENGLISH);
if (!_DontProxyHeaders.contains(s) || (HttpHeader.CONNECTION.is(name) && HttpHeaderValue.CLOSE.is(value)))
{
if (debug != 0)
_log.debug(debug + " " + name + ": " + value);
String filteredHeaderValue = filterResponseHeaderValue(nameString,value.toString(),request);
if (filteredHeaderValue != null && filteredHeaderValue.trim().length() > 0)
{
if (debug != 0)
_log.debug(debug + " " + name + ": (filtered): " + filteredHeaderValue);
response.addHeader(nameString,filteredHeaderValue);
}
}
else if (debug != 0)
_log.debug(debug + " " + name + "! " + value);
}
@Override
protected void onConnectionFailed(Throwable ex)
{
handleOnConnectionFailed(ex,request,response);
// it is possible this might trigger before the
// continuation.suspend()
if (!continuation.isInitial())
{
continuation.complete();
}
}
@Override
protected void onException(Throwable ex)
{
if (ex instanceof EofException)
{
_log.ignore(ex);
return;
}
handleOnException(ex,request,response);
// it is possible this might trigger before the
// continuation.suspend()
if (!continuation.isInitial())
{
continuation.complete();
}
}
@Override
protected void onExpire()
{
handleOnExpire(request,response);
continuation.complete();
}
};
exchange.setScheme((HttpScheme.HTTPS.is(request.getScheme())?HttpScheme.HTTPS:HttpScheme.HTTP).asString());
exchange.setMethod(request.getMethod());
exchange.setURL(url.toString());
exchange.setVersion(HttpVersion.CACHE.get(request.getProtocol()));
if (debug != 0)
_log.debug(debug + " " + request.getMethod() + " " + url + " " + request.getProtocol());
// check connection header
String connectionHdr = request.getHeader("Connection");
if (connectionHdr != null)
{
connectionHdr = connectionHdr.toLowerCase(Locale.ENGLISH);
if (connectionHdr.indexOf("keep-alive") < 0 && connectionHdr.indexOf("close") < 0)
connectionHdr = null;
}
// force host
if (_hostHeader != null)
exchange.setRequestHeader("Host",_hostHeader);
// copy headers
boolean xForwardedFor = false;
boolean hasContent = false;
long contentLength = -1;
Enumeration<?> enm = request.getHeaderNames();
while (enm.hasMoreElements())
{
// TODO could be better than this!
String hdr = (String)enm.nextElement();
String lhdr = hdr.toLowerCase(Locale.ENGLISH);
if (_DontProxyHeaders.contains(lhdr))
continue;
if (connectionHdr != null && connectionHdr.indexOf(lhdr) >= 0)
continue;
if (_hostHeader != null && "host".equals(lhdr))
continue;
if ("content-type".equals(lhdr))
hasContent = true;
else if ("content-length".equals(lhdr))
{
contentLength = request.getContentLength();
exchange.setRequestHeader(HttpHeader.CONTENT_LENGTH.asString(),Long.toString(contentLength));
if (contentLength > 0)
hasContent = true;
}
else if ("x-forwarded-for".equals(lhdr))
xForwardedFor = true;
Enumeration<?> vals = request.getHeaders(hdr);
while (vals.hasMoreElements())
{
String val = (String)vals.nextElement();
if (val != null)
{
if (debug != 0)
_log.debug(debug + " " + hdr + ": " + val);
exchange.setRequestHeader(hdr,val);
}
}
}
// Proxy headers
exchange.setRequestHeader("Via","1.1 (jetty)");
if (!xForwardedFor)
{
exchange.addRequestHeader("X-Forwarded-For",request.getRemoteAddr());
exchange.addRequestHeader("X-Forwarded-Proto",request.getScheme());
exchange.addRequestHeader("X-Forwarded-Host",request.getHeader("Host"));
exchange.addRequestHeader("X-Forwarded-Server",request.getLocalName());
}
if (hasContent)
{
exchange.setRequestContentSource(in);
}
customizeExchange(exchange, request);
/*
* we need to set the timeout on the continuation to take into
* account the timeout of the HttpClient and the HttpExchange
*/
long ctimeout = (_client.getTimeout() > exchange.getTimeout()) ? _client.getTimeout() : exchange.getTimeout();
// continuation fudge factor of 1000, underlying components
// should fail/expire first from exchange
if ( ctimeout == 0 )
{
continuation.setTimeout(0); // ideally never times out
}
else
{
continuation.setTimeout(ctimeout + 1000);
}
customizeContinuation(continuation);
continuation.suspend(response);
_client.send(exchange);
}
}
}
/* ------------------------------------------------------------ */
public void handleConnect(HttpServletRequest request, HttpServletResponse response) throws IOException
{
String uri = request.getRequestURI();
String port = "";
String host = "";
int c = uri.indexOf(':');
if (c >= 0)
{
port = uri.substring(c + 1);
host = uri.substring(0,c);
if (host.indexOf('/') > 0)
host = host.substring(host.indexOf('/') + 1);
}
// TODO - make this async!
InetSocketAddress inetAddress = new InetSocketAddress(host,Integer.parseInt(port));
// if (isForbidden(HttpMessage.__SSL_SCHEME,addrPort.getHost(),addrPort.getPort(),false))
// {
// sendForbid(request,response,uri);
// }
// else
{
InputStream in = request.getInputStream();
OutputStream out = response.getOutputStream();
Socket socket = new Socket(inetAddress.getAddress(),inetAddress.getPort());
response.setStatus(200);
response.setHeader("Connection","close");
response.flushBuffer();
// TODO prevent real close!
IO.copyThread(socket.getInputStream(),out);
IO.copy(in,socket.getOutputStream());
}
}
/* ------------------------------------------------------------ */
protected HttpURI proxyHttpURI(HttpServletRequest request, String uri) throws MalformedURLException
{
return proxyHttpURI(request.getScheme(), request.getServerName(), request.getServerPort(), uri);
}
protected HttpURI proxyHttpURI(String scheme, String serverName, int serverPort, String uri) throws MalformedURLException
{
if (!validateDestination(serverName,uri))
return null;
return new HttpURI(scheme + "://" + serverName + ":" + serverPort + uri);
}
/*
* (non-Javadoc)
*
* @see javax.servlet.Servlet#getServletInfo()
*/
public String getServletInfo()
{
return "Proxy Servlet";
}
/**
* Extension point for subclasses to customize an exchange. Useful for setting timeouts etc. The default implementation does nothing.
*
* @param exchange
* @param request
*/
protected void customizeExchange(HttpExchange exchange, HttpServletRequest request)
{
}
/**
* Extension point for subclasses to customize the Continuation after it's initial creation in the service method. Useful for setting timeouts etc. The
* default implementation does nothing.
*
* @param continuation
*/
protected void customizeContinuation(Continuation continuation)
{
}
/**
* Extension point for custom handling of an HttpExchange's onConnectionFailed method. The default implementation delegates to
* {@link #handleOnException(Throwable, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
*
* @param ex
* @param request
* @param response
*/
protected void handleOnConnectionFailed(Throwable ex, HttpServletRequest request, HttpServletResponse response)
{
handleOnException(ex,request,response);
}
/**
* Extension point for custom handling of an HttpExchange's onException method. The default implementation sets the response status to
* HttpServletResponse.SC_INTERNAL_SERVER_ERROR (503)
*
* @param ex
* @param request
* @param response
*/
protected void handleOnException(Throwable ex, HttpServletRequest request, HttpServletResponse response)
{
if (ex instanceof IOException)
{
_log.warn(ex.toString());
_log.debug(ex);
}
else
_log.warn(ex);
if (!response.isCommitted())
{
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
/**
* Extension point for custom handling of an HttpExchange's onExpire method. The default implementation sets the response status to
* HttpServletResponse.SC_GATEWAY_TIMEOUT (504)
*
* @param request
* @param response
*/
protected void handleOnExpire(HttpServletRequest request, HttpServletResponse response)
{
if (!response.isCommitted())
{
response.setStatus(HttpServletResponse.SC_GATEWAY_TIMEOUT);
}
}
/**
* Extension point for remote server response header filtering. The default implementation returns the header value as is. If null is returned, this header
* won't be forwarded back to the client.
*
* @param headerName
* @param headerValue
* @param request
* @return filteredHeaderValue
*/
protected String filterResponseHeaderValue(String headerName, String headerValue, HttpServletRequest request)
{
return headerValue;
}
/**
* Transparent Proxy.
*
* This convenience extension to ProxyServlet configures the servlet as a transparent proxy. The servlet is configured with init parameters:
* <ul>
* <li>ProxyTo - a URI like http://host:80/context to which the request is proxied.
* <li>Prefix - a URI prefix that is striped from the start of the forwarded URI.
* </ul>
* For example, if a request was received at /foo/bar and the ProxyTo was http://host:80/context and the Prefix was /foo, then the request would be proxied
* to http://host:80/context/bar
*
*/
public static class Transparent extends ProxyServlet
{
String _prefix;
String _proxyTo;
public Transparent()
{
}
public Transparent(String prefix, String host, int port)
{
this(prefix,"http",host,port,null);
}
public Transparent(String prefix, String schema, String host, int port, String path)
{
try
{
if (prefix != null)
{
_prefix = new URI(prefix).normalize().toString();
}
_proxyTo = new URI(schema,null,host,port,path,null,null).normalize().toString();
}
catch (URISyntaxException ex)
{
_log.debug("Invalid URI syntax",ex);
}
}
@Override
public void init(ServletConfig config) throws ServletException
{
super.init(config);
String prefix = config.getInitParameter("Prefix");
_prefix = prefix == null?_prefix:prefix;
// Adjust prefix value to account for context path
String contextPath = _context.getContextPath();
_prefix = _prefix == null?contextPath:(contextPath + _prefix);
String proxyTo = config.getInitParameter("ProxyTo");
_proxyTo = proxyTo == null?_proxyTo:proxyTo;
if (_proxyTo == null)
throw new UnavailableException("ProxyTo parameter is requred.");
if (!_prefix.startsWith("/"))
throw new UnavailableException("Prefix parameter must start with a '/'.");
_log.info(config.getServletName() + " @ " + _prefix + " to " + _proxyTo);
}
@Override
protected HttpURI proxyHttpURI(final String scheme, final String serverName, int serverPort, final String uri) throws MalformedURLException
{
try
{
if (!uri.startsWith(_prefix))
return null;
URI dstUri = new URI(_proxyTo + uri.substring(_prefix.length())).normalize();
if (!validateDestination(dstUri.getHost(),dstUri.getPath()))
return null;
return new HttpURI(dstUri.toString());
}
catch (URISyntaxException ex)
{
throw new MalformedURLException(ex.getMessage());
}
}
}
}

View File

@ -1,164 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.servlets;
import java.io.IOException;
import javax.servlet.http.HttpServlet;
import org.eclipse.jetty.client.ContentExchange;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.http.HttpCookie;
import org.eclipse.jetty.http.HttpMethods;
import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.proxy.BalancerServlet;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.SelectChannelConnector;
import org.eclipse.jetty.server.session.HashSessionIdManager;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.After;
import org.junit.Before;
public abstract class AbstractBalancerServletTest
{
private boolean _stickySessions;
private Server _node1;
private Server _node2;
private Server _balancerServer;
private HttpClient _httpClient;
@Before
public void setUp() throws Exception
{
_httpClient = new HttpClient();
_httpClient.registerListener("org.eclipse.jetty.client.RedirectListener");
_httpClient.start();
}
@After
public void tearDown() throws Exception
{
stopServer(_node1);
stopServer(_node2);
stopServer(_balancerServer);
_httpClient.stop();
}
private void stopServer(Server server)
{
try
{
server.stop();
}
catch (Exception e)
{
// Do nothing
}
}
protected void setStickySessions(boolean stickySessions)
{
_stickySessions = stickySessions;
}
protected void startBalancer(Class<? extends HttpServlet> httpServletClass) throws Exception
{
_node1 = createServer(new ServletHolder(httpServletClass.newInstance()),"/pipo","/molo/*");
setSessionIdManager(_node1,"node1");
_node1.start();
_node2 = createServer(new ServletHolder(httpServletClass.newInstance()),"/pipo","/molo/*");
setSessionIdManager(_node2,"node2");
_node2.start();
BalancerServlet balancerServlet = new BalancerServlet();
ServletHolder balancerServletHolder = new ServletHolder(balancerServlet);
balancerServletHolder.setInitParameter("StickySessions",String.valueOf(_stickySessions));
balancerServletHolder.setInitParameter("ProxyPassReverse","true");
balancerServletHolder.setInitParameter("BalancerMember." + "node1" + ".ProxyTo","http://localhost:" + getServerPort(_node1));
balancerServletHolder.setInitParameter("BalancerMember." + "node2" + ".ProxyTo","http://localhost:" + getServerPort(_node2));
_balancerServer = createServer(balancerServletHolder,"/pipo","/molo/*");
_balancerServer.start();
}
private Server createServer(ServletHolder servletHolder, String appContext, String servletUrlPattern)
{
Server server = new Server();
SelectChannelConnector httpConnector = new SelectChannelConnector(server);
server.addConnector(httpConnector);
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath(appContext);
server.setHandler(context);
context.addServlet(servletHolder,servletUrlPattern);
return server;
}
private void setSessionIdManager(Server node, String nodeName)
{
HashSessionIdManager sessionIdManager = new HashSessionIdManager();
sessionIdManager.setWorkerName(nodeName);
node.setSessionIdManager(sessionIdManager);
}
private int getServerPort(Server server)
{
return ((Connector.NetConnector)server.getConnectors()[0]).getLocalPort();
}
protected byte[] sendRequestToBalancer(String requestUri) throws IOException, InterruptedException
{
ContentExchange exchange = new ContentExchange()
{
@Override
protected void onResponseHeader(Buffer name, Buffer value) throws IOException
{
// Cookie persistence
if (name.toString().equals("Set-Cookie"))
{
String cookieVal = value.toString();
if (cookieVal.startsWith("JSESSIONID="))
{
String jsessionid = cookieVal.split(";")[0].substring("JSESSIONID=".length());
_httpClient.getDestination(getAddress(),false).addCookie(new HttpCookie("JSESSIONID",jsessionid));
}
}
}
};
exchange.setURL("http://localhost:" + getServerPort(_balancerServer) + "/pipo/molo/" + requestUri);
exchange.setMethod(HttpMethods.GET);
_httpClient.send(exchange);
exchange.waitForDone();
return exchange.getResponseContentBytes();
}
}

View File

@ -1,218 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.server.handler;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.BufferedReader;
import java.io.EOFException;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jetty.proxy.ConnectHandler;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.SelectChannelConnector;
import org.eclipse.jetty.server.Server;
import org.junit.AfterClass;
/**
* @version $Revision$ $Date$
*/
public abstract class AbstractConnectHandlerTest
{
protected static Server server;
protected static Connector.NetConnector serverConnector;
protected static Server proxy;
protected static Connector proxyConnector;
protected static void startServer(Connector.NetConnector connector, Handler handler) throws Exception
{
server = new Server();
serverConnector = connector;
server.addConnector(serverConnector);
server.setHandler(handler);
server.start();
}
protected static void startProxy() throws Exception
{
proxy = new Server();
proxyConnector = new SelectChannelConnector();
proxy.addConnector(proxyConnector);
proxy.setHandler(new ConnectHandler());
proxy.start();
}
@AfterClass
public static void stop() throws Exception
{
stopProxy();
stopServer();
}
protected static void stopServer() throws Exception
{
server.stop();
server.join();
}
protected static void stopProxy() throws Exception
{
proxy.stop();
proxy.join();
}
protected Response readResponse(BufferedReader reader) throws IOException
{
// Simplified parser for HTTP responses
String line = reader.readLine();
if (line == null)
throw new EOFException();
Matcher responseLine = Pattern.compile("HTTP/1\\.1\\s+(\\d+)").matcher(line);
assertTrue(responseLine.lookingAt());
String code = responseLine.group(1);
Map<String, String> headers = new LinkedHashMap<String, String>();
while ((line = reader.readLine()) != null)
{
if (line.trim().length() == 0)
break;
Matcher header = Pattern.compile("([^:]+):\\s*(.*)").matcher(line);
assertTrue(header.lookingAt());
String headerName = header.group(1);
String headerValue = header.group(2);
headers.put(headerName.toLowerCase(Locale.ENGLISH), headerValue.toLowerCase(Locale.ENGLISH));
}
StringBuilder body;
if (headers.containsKey("content-length"))
{
int readLen = 0;
int length = Integer.parseInt(headers.get("content-length"));
body=new StringBuilder(length);
try
{
for (int i = 0; i < length; ++i)
{
char c = (char)reader.read();
body.append(c);
readLen++;
}
}
catch (SocketTimeoutException e)
{
System.err.printf("Read %,d bytes (out of an expected %,d bytes)%n",readLen,length);
throw e;
}
}
else if ("chunked".equals(headers.get("transfer-encoding")))
{
body = new StringBuilder(64*1024);
while ((line = reader.readLine()) != null)
{
if ("0".equals(line))
{
line = reader.readLine();
assertEquals("", line);
break;
}
try
{
Thread.sleep(5);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
int length = Integer.parseInt(line, 16);
for (int i = 0; i < length; ++i)
{
char c = (char)reader.read();
body.append(c);
}
line = reader.readLine();
assertEquals("", line);
}
}
else throw new IllegalStateException();
return new Response(code, headers, body.toString().trim());
}
protected Socket newSocket() throws IOException
{
Socket socket = new Socket("localhost", ((Connector.NetConnector)proxyConnector).getLocalPort());
socket.setSoTimeout(10000);
return socket;
}
protected class Response
{
private final String code;
private final Map<String, String> headers;
private final String body;
private Response(String code, Map<String, String> headers, String body)
{
this.code = code;
this.headers = headers;
this.body = body;
}
public String getCode()
{
return code;
}
public Map<String, String> getHeaders()
{
return headers;
}
public String getBody()
{
return body;
}
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append(code).append("\r\n");
for (Map.Entry<String, String> entry : headers.entrySet())
builder.append(entry.getKey()).append(": ").append(entry.getValue()).append("\r\n");
builder.append("\r\n");
builder.append(body);
return builder.toString();
}
}
}

View File

@ -1,53 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.proxy;
import java.util.EnumSet;
import javax.servlet.DispatcherType;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
public class AsyncProxyServer
{
public static void main(String[] args)
throws Exception
{
Server server = new Server();
Connector connector=new SelectChannelConnector();
connector.setPort(8888);
server.setConnectors(new Connector[]{connector});
ServletHandler handler=new ServletHandler();
server.setHandler(handler);
//FilterHolder gzip = handler.addFilterWithMapping("org.eclipse.jetty.servlet.GzipFilter","/*",EnumSet.of(DispatcherType.REQUEST,DispatcherType.ASYNC));
//gzip.setAsyncSupported(true);
//gzip.setInitParameter("minGzipSize","256");
ServletHolder proxy = handler.addServletWithMapping("org.eclipse.jetty.servlets.ProxyServlet","/");
proxy.setAsyncSupported(true);
server.start();
server.join();
}
}

View File

@ -1,135 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.servlets;
import static org.junit.Assert.*;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Test;
/**
*
*/
public class BalancerServletTest extends AbstractBalancerServletTest
{
@Test
public void testRoundRobinBalancer() throws Exception
{
setStickySessions(false);
startBalancer(CounterServlet.class);
for (int i = 0; i < 10; i++)
{
byte[] responseBytes = sendRequestToBalancer("/");
String returnedCounter = readFirstLine(responseBytes);
// RR : response should increment every other request
String expectedCounter = String.valueOf(i / 2);
assertEquals(expectedCounter,returnedCounter);
}
}
@Test
public void testStickySessionsBalancer() throws Exception
{
setStickySessions(true);
startBalancer(CounterServlet.class);
for (int i = 0; i < 10; i++)
{
byte[] responseBytes = sendRequestToBalancer("/");
String returnedCounter = readFirstLine(responseBytes);
// RR : response should increment on each request
String expectedCounter = String.valueOf(i);
assertEquals(expectedCounter,returnedCounter);
}
}
@Test
public void testProxyPassReverse() throws Exception
{
setStickySessions(false);
startBalancer(RelocationServlet.class);
byte[] responseBytes = sendRequestToBalancer("index.html");
String msg = readFirstLine(responseBytes);
assertEquals("success",msg);
}
private String readFirstLine(byte[] responseBytes) throws IOException
{
BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(responseBytes)));
return reader.readLine();
}
@SuppressWarnings("serial")
public static final class CounterServlet extends HttpServlet
{
private int counter;
@Override
public void init() throws ServletException
{
counter = 0;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
// Force session creation
req.getSession();
resp.setContentType("text/plain");
resp.getWriter().println(counter++);
}
}
@SuppressWarnings("serial")
public static final class RelocationServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
if (req.getRequestURI().endsWith("/index.html"))
{
resp.sendRedirect("http://localhost:" + req.getLocalPort() + req.getContextPath() + req.getServletPath() + "/other.html?secret=pipo%20molo");
return;
}
resp.setContentType("text/plain");
if ("pipo molo".equals(req.getParameter("secret")))
{
resp.getWriter().println("success");
}
else
{
resp.getWriter().println("failure");
}
}
}
}

View File

@ -1,77 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.proxy;
import static org.mockito.Mockito.when;
import static org.mockito.Matchers.*;
import static org.junit.Assert.*;
import static org.hamcrest.Matchers.*;
import java.io.IOException;
import org.eclipse.jetty.io.EndPoint;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
/* ------------------------------------------------------------ */
/**
*/
@RunWith(MockitoJUnitRunner.class)
public class ConnectHandlerUnitTest
{
@Mock
private EndPoint endPoint;
// TODO update for jetty-9
@Test
@Ignore
public void testPartialWritesWithNonFullBuffer() throws IOException
{
/*
ConnectHandler connectHandler = new ConnectHandler();
final byte[] bytes = "foo bar".getBytes();
Buffer buffer = new ByteArrayBuffer(bytes.length * 2);
buffer.put(bytes);
when(endPoint.flush(buffer)).thenAnswer(new Answer<Object>()
{
public Object answer(InvocationOnMock invocation)
{
Object[] args = invocation.getArguments();
Buffer buffer = (Buffer)args[0];
int skip = bytes.length/2;
buffer.skip(skip);
return skip;
}
});
when(endPoint.blockWritable(anyInt())).thenReturn(true);
// method to test
connectHandler.write(endPoint,buffer,null);
assertThat(buffer.length(),is(0));
*/
}
}

View File

@ -1,194 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.servlets;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import junit.framework.Assert;
import org.eclipse.jetty.client.ContentExchange;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.proxy.ProxyServlet;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.junit.After;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.hamcrest.Matchers.*;
public class ProxyServletTest
{
private Server _server;
private Connector _connector;
private HttpClient _client;
public void init(HttpServlet servlet) throws Exception
{
_server = new Server();
_connector = new SelectChannelConnector();
_server.addConnector(_connector);
HandlerCollection handlers = new HandlerCollection();
_server.setHandler(handlers);
ServletContextHandler proxyCtx = new ServletContextHandler(handlers, "/proxy", ServletContextHandler.NO_SESSIONS);
ServletHolder proxyServletHolder = new ServletHolder(new ProxyServlet()
{
@Override
protected HttpURI proxyHttpURI(String scheme, String serverName, int serverPort, String uri) throws MalformedURLException
{
// Proxies any call to "/proxy" to "/"
return new HttpURI(scheme + "://" + serverName + ":" + serverPort + uri.substring("/proxy".length()));
}
});
proxyServletHolder.setInitParameter("timeout", String.valueOf(5 * 60 * 1000L));
proxyCtx.addServlet(proxyServletHolder, "/*");
ServletContextHandler appCtx = new ServletContextHandler(handlers, "/", ServletContextHandler.SESSIONS);
ServletHolder appServletHolder = new ServletHolder(servlet);
appCtx.addServlet(appServletHolder, "/*");
handlers.addHandler(proxyCtx);
handlers.addHandler(appCtx);
_server.start();
_client = new HttpClient();
_client.start();
}
@After
public void destroy() throws Exception
{
if (_client != null)
_client.stop();
if (_server != null)
{
_server.stop();
_server.join();
}
}
@Test
public void testXForwardedHostHeader() throws Exception
{
init(new HttpServlet()
{
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
PrintWriter writer = resp.getWriter();
writer.write(req.getHeader("X-Forwarded-Host"));
writer.flush();
}
});
String url = "http://localhost:" + _connector.getLocalPort() + "/proxy/test";
ContentExchange exchange = new ContentExchange();
exchange.setURL(url);
_client.send(exchange);
exchange.waitForDone();
assertThat("Response expected to contain content of X-Forwarded-Host Header from the request",exchange.getResponseContent(),equalTo("localhost:"
+ _connector.getLocalPort()));
}
@Test
public void testBigDownloadWithSlowReader() throws Exception
{
// Create a 6 MiB file
final File file = File.createTempFile("test_", null, MavenTestingUtils.getTargetTestingDir());
file.deleteOnExit();
FileOutputStream fos = new FileOutputStream(file);
byte[] buffer = new byte[1024];
Arrays.fill(buffer, (byte)'X');
for (int i = 0; i < 6 * 1024; ++i)
fos.write(buffer);
fos.close();
init(new HttpServlet()
{
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
FileInputStream fis = new FileInputStream(file);
ServletOutputStream output = response.getOutputStream();
byte[] buffer = new byte[1024];
int read;
while ((read = fis.read(buffer)) >= 0)
output.write(buffer, 0, read);
fis.close();
}
});
String url = "http://localhost:" + _connector.getLocalPort() + "/proxy/test";
ContentExchange exchange = new ContentExchange(true)
{
@Override
protected void onResponseContent(Buffer content) throws IOException
{
try
{
// Slow down the reader
TimeUnit.MILLISECONDS.sleep(10);
super.onResponseContent(content);
}
catch (InterruptedException x)
{
throw (IOException)new IOException().initCause(x);
}
}
};
exchange.setURL(url);
long start = System.nanoTime();
_client.send(exchange);
Assert.assertEquals(HttpExchange.STATUS_COMPLETED, exchange.waitForDone());
long elapsed = System.nanoTime() - start;
Assert.assertEquals(HttpStatus.OK_200, exchange.getResponseStatus());
Assert.assertEquals(file.length(), exchange.getResponseContentBytes().length);
long millis = TimeUnit.NANOSECONDS.toMillis(elapsed);
long rate = file.length() / 1024 * 1000 / millis;
System.out.printf("download rate = %d KiB/s%n", rate);
}
}

View File

@ -50,7 +50,7 @@ public abstract class AbstractNetworkConnector extends AbstractConnector impleme
}
@Override
@ManagedAttribute("Host this connector binds to")
@ManagedAttribute("The network interface this connector binds to as an IP address or a hostname. If null or 0.0.0.0, then bind to all interfaces.")
public String getHost()
{
return _host;
@ -62,7 +62,7 @@ public abstract class AbstractNetworkConnector extends AbstractConnector impleme
}
@Override
@ManagedAttribute("Port this connector listens on")
@ManagedAttribute("Port this connector listens on. If set the 0 a random port is assigned which may be obtained with getLocalPort()")
public int getPort()
{
return _port;

View File

@ -23,10 +23,27 @@ import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
/**
* <p>Factory for {@link Connection}s.</p>
* <p>A Factory to create {@link Connection} instances for {@link Connector}s.</p>
* <p>A Connection factory is responsible for instantiating and configuring a {@link Connection} instance
* to handle an {@link EndPoint} accepted by a {@link Connector}.</p>
* <p>
* A ConnectionFactory has a protocol name that represents the protocol of the Connections
* created. Example of protocol names include:<dl>
* <dt>http</dt><dd>Creates a HTTP connection that can handle multiple versions of HTTP from 0.9 to 1.1</dd>
* <dt>spdy/2</dt><dd>Creates a HTTP connection that handles a specific version of the SPDY protocol</dd>
* <dt>SSL-XYZ</dt><dd>Create an SSL connection chained to a connection obtained from a connection factory
* with a protocol "XYZ".</dd>
* <dt>SSL-http</dt><dd>Create an SSL connection chained to a HTTP connection (aka https)</dd>
* <dt>SSL-npn</dt><dd>Create an SSL connection chained to a NPN connection, that uses a negotiation with
* the client to determine the next protocol.</dd>
* </dl>
*/
public interface ConnectionFactory
{
/* ------------------------------------------------------------ */
/**
* @return A string representing the protocol name.
*/
public String getProtocol();
/**

View File

@ -75,7 +75,7 @@ public interface Connector extends LifeCycle, Graceful
public List<String> getProtocols();
/**
* @return the dle timeout for connections in milliseconds
* @return the max idle timeout for connections in milliseconds
*/
@ManagedAttribute("maximum time a connection can be idle before being closed (in ms)")
public long getIdleTimeout();

View File

@ -27,6 +27,25 @@ import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.server.HttpConfiguration.Customizer;
/* ------------------------------------------------------------ */
/** Customize Requests for Proxy Forwarding.
* <p>
* This customizer looks at at HTTP request for headers that indicate
* it has been forwarded by one or more proxies. Specifically handled are:
* <ul>
* <li>X-Forwarded-Host</li>
* <li>X-Forwarded-Server</li>
* <li>X-Forwarded-For</li>
* <li>X-Forwarded-Proto</li>
* </ul>
* <p>If these headers are present, then the {@link Request} object is updated
* so that the proxy is not seen as the other end point of the connection on which
* the request came</p>
* <p>Headers can also be defined so that forwarded SSL Session IDs and Cipher
* suites may be customised</p>
* @see http://en.wikipedia.org/wiki/X-Forwarded-For
*/
public class ForwardedRequestCustomizer implements Customizer
{
private String _hostHeader;

View File

@ -420,39 +420,42 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
switch (header)
{
case EXPECT:
HttpHeaderValue expect = HttpHeaderValue.CACHE.get(value);
switch (expect == null ? HttpHeaderValue.UNKNOWN : expect)
if (_version.getVersion()>=HttpVersion.HTTP_1_1.getVersion())
{
case CONTINUE:
_expect100Continue = true;
break;
HttpHeaderValue expect = HttpHeaderValue.CACHE.get(value);
switch (expect == null ? HttpHeaderValue.UNKNOWN : expect)
{
case CONTINUE:
_expect100Continue = true;
break;
case PROCESSING:
_expect102Processing = true;
break;
case PROCESSING:
_expect102Processing = true;
break;
default:
String[] values = value.split(",");
for (int i = 0; values != null && i < values.length; i++)
{
expect = HttpHeaderValue.CACHE.get(values[i].trim());
if (expect == null)
_expect = true;
else
default:
String[] values = value.split(",");
for (int i = 0; values != null && i < values.length; i++)
{
switch (expect)
expect = HttpHeaderValue.CACHE.get(values[i].trim());
if (expect == null)
_expect = true;
else
{
case CONTINUE:
_expect100Continue = true;
break;
case PROCESSING:
_expect102Processing = true;
break;
default:
_expect = true;
switch (expect)
{
case CONTINUE:
_expect100Continue = true;
break;
case PROCESSING:
_expect102Processing = true;
break;
default:
_expect = true;
}
}
}
}
}
}
break;
@ -581,6 +584,7 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
// TODO is it worthwhile sending if we are at EoF?
// "application" info failed to commit, commit with a failsafe 500 info
_transport.send(HttpGenerator.RESPONSE_500_INFO,null,true);
complete=true;
throw e;
}
catch (Exception e)
@ -588,8 +592,15 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
LOG.warn(e);
// "application" info failed to commit, commit with a failsafe 500 info
_transport.send(HttpGenerator.RESPONSE_500_INFO,null,true);
complete=true;
throw e;
}
finally
{
// TODO this indicates the relationship with HttpOutput is not exactly correct
if (complete)
_response.getHttpOutput().closed();
}
}
return committed;
}

View File

@ -25,6 +25,18 @@ import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
/* ------------------------------------------------------------ */
/** HTTP Configuration.
* <p>This class is a holder of HTTP configuration for use by the
* {@link HttpChannel} class. Typically a HTTPConfiguration instance
* is instantiated and passed to a {@link ConnectionFactory} that can
* create HTTP channels (eg HTTP, AJP or SPDY).</p>
* <p>The configuration held by this class is not for the wire protocol,
* but for the interpretation and handling of HTTP requests that could
* be transported by a variety of protocols.
* </p>
*/
@ManagedObject("HTTP Configuration")
public class HttpConfiguration
{
@ -49,6 +61,10 @@ public class HttpConfiguration
{
}
/* ------------------------------------------------------------ */
/** Create a configuration from another.
* @param config The configuration to copy.
*/
public HttpConfiguration(HttpConfiguration config)
{
_customizers.addAll(config._customizers);
@ -59,11 +75,20 @@ public class HttpConfiguration
_secureScheme=config._secureScheme;
}
/* ------------------------------------------------------------ */
/**
* <p>Add a {@link Customizer} that is invoked for every
* request received.</p>
* <p>Customiser are often used to interpret optional headers (eg {@link ForwardedRequestCustomizer}) or
* optional protocol semantics (eg {@link SecureRequestCustomizer}).
* @param customizer A request customizer
*/
public void addCustomizer(Customizer customizer)
{
_customizers.add(customizer);
}
/* ------------------------------------------------------------ */
public List<Customizer> getCustomizers()
{
return _customizers;
@ -107,32 +132,72 @@ public class HttpConfiguration
return _secureScheme;
}
/* ------------------------------------------------------------ */
/**
* <p>Set the {@link Customizer}s that are invoked for every
* request received.</p>
* <p>Customisers are often used to interpret optional headers (eg {@link ForwardedRequestCustomizer}) or
* optional protocol semantics (eg {@link SecureRequestCustomizer}).
* @param customizers
*/
public void setCustomizers(List<Customizer> customizers)
{
_customizers.clear();
_customizers.addAll(customizers);
}
/* ------------------------------------------------------------ */
/**
* Set the size of the buffer into which response content is aggregated
* before being sent to the client. A larger buffer can improve performance by allowing
* a content producer to run without blocking, however larger buffers consume more memory and
* may induce some latency before a client starts processing the content.
* @param responseBufferSize buffer size in bytes.
*/
public void setOutputBufferSize(int responseBufferSize)
{
_outputBufferSize = responseBufferSize;
}
/* ------------------------------------------------------------ */
/** Set the maximum size of a request header.
* <p>Larger headers will allow for more and/or larger cookies plus larger form content encoded
* in a URL. However, larger headers consume more memory and can make a server more vulnerable to denial of service
* attacks.</p>
* @param requestHeaderSize Max header size in bytes
*/
public void setRequestHeaderSize(int requestHeaderSize)
{
_requestHeaderSize = requestHeaderSize;
}
/* ------------------------------------------------------------ */
/** Set the maximum size of a response header.
*
* <p>Larger headers will allow for more and/or larger cookies and longer HTTP headers (eg for redirection).
* However, larger headers will also consume more memory.</p>
* @param responseHeaderSize Response header size in bytes.
*/
public void setResponseHeaderSize(int responseHeaderSize)
{
_responseHeaderSize = responseHeaderSize;
}
/* ------------------------------------------------------------ */
/** Set the TCP/IP port used for CONFIDENTIAL and INTEGRAL
* redirections.
* @param confidentialPort
*/
public void setSecurePort(int confidentialPort)
{
_securePort = confidentialPort;
}
/* ------------------------------------------------------------ */
/** Set the URI scheme used for CONFIDENTIAL and INTEGRAL
* redirections.
* @param confidentialScheme A string like"https"
*/
public void setSecureScheme(String confidentialScheme)
{
_secureScheme = confidentialScheme;

View File

@ -25,12 +25,13 @@ import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeoutException;
import org.eclipse.jetty.http.HttpGenerator;
import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpParser;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.Connection;
@ -61,7 +62,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
private volatile ByteBuffer _chunk = null;
private BlockingCallback _readBlocker = new BlockingCallback();
private BlockingCallback _writeBlocker = new BlockingCallback();
// TODO get rid of this
private final Runnable _channelRunner = new Runnable()
{
@ -77,7 +78,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
{
setCurrentConnection(null);
}
}
};
@ -410,7 +411,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
}
}
}
@Override
public <C> void send(ResponseInfo info, ByteBuffer content, boolean lastContent, C context, Callback<C> callback)
{
@ -514,6 +515,10 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
}
}
public ByteBuffer getRequestBuffer()
{
return _requestBuffer;
}
private class Input extends ByteBufferHttpInput
{
@ -543,7 +548,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
// Do we have content ready to parse?
if (BufferUtil.isEmpty(_requestBuffer))
{
{
// If no more input
if (getEndPoint().isInputShutdown())
{
@ -642,24 +647,32 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
switch (version)
{
case HTTP_0_9:
{
persistent = false;
break;
}
case HTTP_1_0:
{
persistent = getRequest().getHttpFields().contains(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString());
if (!persistent)
persistent = HttpMethod.CONNECT.is(getRequest().getMethod());
if (persistent)
getResponse().getHttpFields().add(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE);
break;
}
case HTTP_1_1:
{
persistent = !getRequest().getHttpFields().contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString());
if (!persistent)
persistent = HttpMethod.CONNECT.is(getRequest().getMethod());
if (!persistent)
getResponse().getHttpFields().add(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE);
break;
}
default:
{
throw new IllegalStateException();
}
}
if (!persistent)

View File

@ -82,6 +82,19 @@ public class HttpOutput extends ServletOutputStream
{
_closed = false;
}
/** Called by the HttpChannel if the output was closed
* externally (eg by a 500 exception handling).
*/
void closed()
{
_closed = true;
if (_aggregate != null)
{
_channel.getConnector().getByteBufferPool().release(_aggregate);
_aggregate = null;
}
}
@Override
public void close()
@ -237,6 +250,10 @@ public class HttpOutput extends ServletOutputStream
response.getHttpFields().putDateField(HttpHeader.LAST_MODIFIED, lml);
}
String etag=httpContent.getETag();
if (etag!=null)
response.getHttpFields().put(HttpHeader.ETAG,etag);
content = httpContent.getDirectBuffer();
if (content == null)
content = httpContent.getIndirectBuffer();
@ -255,7 +272,8 @@ public class HttpOutput extends ServletOutputStream
// Process content.
if (content instanceof ByteBuffer)
{
_channel.write((ByteBuffer)content, true); // TODO: we have written all content ?
_channel.write((ByteBuffer)content, true);
_closed=true;
}
else if (content instanceof ReadableByteChannel)
{
@ -297,7 +315,6 @@ public class HttpOutput extends ServletOutputStream
buffer.limit(len);
_channel.write(buffer,false);
}
_channel.write(BufferUtil.EMPTY_BUFFER,true);
}
finally
{

Some files were not shown because too many files have changed in this diff Show More