Merged branch 'jetty-7' into 'jetty-8'.
diff --git a/.gitignore b/.gitignore
index 12ab1b6..815d296 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,3 +38,4 @@
#maven
*.versionsBackup
+*.releaseBackup
diff --git a/VERSION.txt b/VERSION.txt
index 21b0980..8433c05 100644
--- a/VERSION.txt
+++ b/VERSION.txt
@@ -1,9 +1,20 @@
-jetty-7.6.19-SNAPSHOT
+jetty-8.1.19-SNAPSHOT
-jetty-7.6.18.20150929 - 29 September 2015
+jetty-8.1.18.v20150929 - 29 September 2015
+ + 467434 NPE in SslContextFactory
+ 475851 AbstractGenerator.setResponse can produce an invalid Response header
+ + 477817 Fixed memory leak in QueuedThreadPool
+ 477948 Connections leaked when response contains Connection: close header.
+jetty-8.1.17.v20150415 - 15 April 2015
+ + 409788 Large POST body causes java.lang.IllegalStateException: SENDING =>
+ HEADERS.
+ + 433802 check EOF in send1xx
+ + 442839 highly fragmented websocket messages can result in corrupt binary
+ messages
+ + 445953 fixed loop closing stream write
+ + 456741 Eof check in flush
+
jetty-7.6.17.v20150415 - 15 April 2015
+ 409788 Large POST body causes java.lang.IllegalStateException: SENDING =>
HEADERS.
@@ -12,6 +23,17 @@
messages
+ 445953 fixed loop closing stream write
+jetty-8.1.16.v20140903 - 03 September 2014
+ + 409788 Large POST body causes java.lang.IllegalStateException: SENDING =>
+ HEADERS.
+ + 433689 Evict idle HttpDestinations from client
+ + 433802 check EOF in send1xx
+ + 438996 Scavenger-Timer in HashSessionManager can die because of
+ IllegalStateException from getMaxInactiveInterval
+ + 442048 fixed sendRedirect %2F encoding
+ + 442839 highly fragmented websocket messages can result in corrupt binary
+ messages
+
jetty-7.6.16.v20140903 - 03 September 2014
+ 409788 Large POST body causes java.lang.IllegalStateException: SENDING =>
HEADERS.
@@ -19,6 +41,24 @@
+ 442839 highly fragmented websocket messages can result in corrupt binary
messages
+jetty-8.1.15.v20140411 - 11 April 2014
+ + 397167 Remote Access documentation is wrong
+ + 419799 complete after exceptions thrown from async error pages
+ + 420776 complete error pages after startAsync
+ + 421197 fix method comment and ensure close synchronized
+ + 422137 Added maxQueued to QueuedThreadPool MBean
+ + 424180 improve bad message errors
+ + 425038 WebSocketClient leaks file handles when exceptions are thrown from
+ open()
+ + 425551 Memory Leak in SelectConnector$ConnectTimeout.expired.
+ + 426658 backport Bug 425930 to jetty-8
+ + 427761 allow endpoints to be interrupted
+ + 428708 JDBCSessionIdManager when clearing expired sessions failed, jetty
+ should still be able to startup
+ + 428710 JDBCSession(Id)Manager use 'read committed isolation level'
+ + 430968 Use wrapped response with async dispatch
+ + 432452 ConnectHandler does not timeout sockets in FIN_WAIT2.
+
jetty-7.6.15.v20140411 - 11 April 2014
+ 422137 Added maxQueued to QueuedThreadPool MBean
+ 425038 WebSocketClient leaks file handles when exceptions are thrown from
@@ -26,6 +66,13 @@
+ 425551 Memory Leak in SelectConnector$ConnectTimeout.expired.
+ 432452 ConnectHandler does not timeout sockets in FIN_WAIT2.
+jetty-8.1.14.v20131031 - 31 October 2013
+ + 417772 fixed low resources idle timeout
+ + 418636 Name anonymous filter and holders with classname-hashcode
+ + 419432 Allow to override the SslContextFactory on a per-destination basis.
+ + 420048 DefaultServlet alias checks configured resourceBase
+ + 420530 AbstractLoginModule never fails a login
+
jetty-7.6.14.v20131031 - 31 October 2013
+ 417772 fixed low resources idle timeout
+ 418636 Name anonymous filter and holders with classname-hashcode
@@ -33,6 +80,29 @@
+ 420048 DefaultServlet alias checks configured resourceBase
+ 420530 AbstractLoginModule never fails a login
+jetty-8.1.13.v20130916 - 16 September 2013
+ + 412629 PropertyFileLoginModule doesn't cache user configuration file even
+ for refreshInterval=0
+ + 413484 setAttribute in nosql session management better handles _dirty status
+ + 413684 deprecated unsafe alias checkers
+ + 414235 RequestLogHandler configured on a context fails to handle forwarded
+ requests
+ + 414393 StringIndexOutofBoundsException with > 8k multipart content without
+ CR or LF
+ + 414431 Avoid debug NPE race
+ + 414507 Ensure AnnotationParser ignores parent dir hierarchy when checking
+ for hidden dirnames
+ + 414652 WebSocket's sendMessage() may hang on congested connections.
+ + 415192 <jsp-file> maps to JspPropertyGroupServlet instead of JspServlet
+ + 415401 Add XmlConfiguration.initializeDefaults that allows to set default
+ values for any XmlConfiguration that may be overridden in the config file
+ + 416266 HttpServletResponse.encodeURL() encodes on first request when only
+ SessionTrackingMode.COOKIE is used
+ + 416585 WebInfConfiguration examines webapp classloader first instead of its
+ parent when looking for container jars
+ + 416787 StringIndexOutOfBounds with a pathMap of ""
+ + 416990 JMX names statically unique
+
jetty-7.6.13.v20130916 - 16 September 2013
+ 412629 PropertyFileLoginModule doesn't cache user configuration file even
for refreshInterval=0
@@ -53,6 +123,44 @@
parent when looking for container jars
+ 416990 JMX names statically unique
+jetty-8.1.12.v20130726 - 26 July 2013
+ + 396706 CGI support parameters
+ + 397193 MongoSessionManager refresh updates last access time
+ + 407342 ReloadedSessionMissingClassTest uses class compiled with jdk7
+ + 408529 Etags set in 304 response
+ + 408600 set correct jetty.url in all pom files
+ + 408642 setContentType from addHeader
+ + 408662 In pax-web servlet services requests even if init() has not finished
+ running
+ + 408806 getParameter returns null on Multipart request if called before
+ request.getPart()/getParts()
+ + 408909 GzipFilter setting of headers when reset and/or not compressed
+ + 409028 Jetty HttpClient does not work with proxy CONNECT method.
+ + 409133 Empty <welcome-file> causes StackOverflowError
+ + 409436 NPE on context restart using dynamic servlet registration
+ + 409449 Ensure servlets, filters and listeners added via dynamic
+ registration, annotations or descriptors are cleaned on context restarts
+ + 409556 FileInputStream not closed in DirectNIOBuffer
+ + 410405 Avoid NPE for requestDispatcher(../)
+ + 410630 MongoSessionManager conflicting session update op
+ + 410750 NoSQLSessions: implement session context data persistence across
+ server restarts
+ + 410893 async support defaults to false for spec created servlets and filters
+ + 411135 HttpClient may send proxied https requests to the proxy instead of
+ the target server.
+ + 411216 RequestLogHandler handles async completion
+ + 411458 MultiPartFilter getParameterMap doesn't preserve multivalued
+ parameters 411459 MultiPartFilter.Wrapper getParameter should use charset
+ encoding of part
+ + 411755 MultiPartInputStreamParser fails on base64 encoded content
+ + 411909 GzipFilter flushbuffer() results in erroneous finish() call
+ + 412712 HttpClient does not send the terminal chunk after partial writes.
+ + 412750 HttpClient close expired connections fix
+ + 413371 Default JSON.Converters for List and Set.
+ + 413372 JSON Enum uses name rather than toString()
+ + 413684 Trailing slash shows JSP source
+ + 413812 Make RateTracker serializable
+
jetty-7.6.12.v20130726 - 26 July 2013
+ 396706 CGI support parameters
+ 397193 MongoSessionManager refresh updates last access time
@@ -84,6 +192,70 @@
+ 413684 Trailing slash shows JSP source
+ 413812 Make RateTracker serializable
+jetty-8.1.11.v20130520 - 20 May 2013
+ + 402844 STOP.PORT & STOP.KEY behaviour has changed
+ + 403281 jetty.sh waits for started or failure before returning
+ + 403513 jetty:run goal cannot be executed twice during the maven build
+ + 403570 Asynchronous Request Logging
+ + 404010 fix cast exception in mongodb session manager
+ + 404128 Add Vary headers rather than set them
+ + 404283 org.eclipse.jetty.util.Scanner.scanFile() dies with an NPE if
+ listFiles() returns null
+ + 404325 data constraint redirection does send default port
+ + 404517 Close connection if request received after half close
+ + 404789 Support IPv6 addresses in DoSFilter white list.
+ + 404958 Fixed Resource.newSystemResource striped / handling
+ + 405281 allow filemappedbuffers to not be used
+ + 405537 NPE in rendering JSP using SPDY and wrapped ServletRequest
+ + 406437 Digest Auth supports out of order nc
+ + 406618 Jetty startup in OSGi Equinox fails when using option
+ jetty.home.bundle=org.eclipse.jetty.osgi.boot
+ + 406923 CR line termination
+ + 407136 @PreDestroy called after Servlet.destroy()
+ + 407173 java.lang.IllegalStateException: null when using JDBCSessionManager
+ + 407931 Add toggle for failing on servlet availability
+ + 407976 JDBCSessionIdManager potentially leaves server in bad state after
+ startup
+ + 408077 HashSessionManager leaves file handles open after being stopped
+ + 408446 Multipart parsing issue with boundry and charset in ContentType
+ header
+
+jetty-8.1.10.v20130312 - 12 March 2013
+ + 376273 Early EOF because of SSL Protocol Error on
+ https://api-3t.paypal.com/nvp.
+ + 381521 allow compress methods to be configured
+ + 392129 fixed handling of timeouts after startAsync
+ + 394064 ensure that JarFile instances are closed on JarFileResource.release()
+ + 398649 ServletContextListener.contextDestroyed() is not called on
+ ContextHandler unregistration
+ + 399703 made encoding error handling consistent
+ + 399799 do not hold lock while calling invalidation listeners
+ + 399967 Shutdown hook calls destroy
+ + 400040 NullPointerException in HttpGenerator.prepareBuffers
+ + 400142 ConcurrentModificationException in JDBC SessionManger
+ + 400144 When loading a session fails the JDBCSessionManger produces duplicate
+ session IDs
+ + 400312 ServletContextListener.contextInitialized() is not called when added
+ in ServletContainerInitializer.onStartup
+ + 400457 Thread context classloader hierarchy not searched when finding
+ webapp's java:comp/env
+ + 400859 limit max size of writes from cached content
+ + 401211 Remove requirement for jetty-websocket.jar in WEB-INF/lib
+ + 401317 Make Safari 5.x websocket support minVersion level error more clear
+ + 401382 Prevent parseAvailable from parsing next chunk when previous has not
+ been consumed. Handle no content-type in chunked request.
+ + 401474 Performance problem in org.eclipse.jetty.annotation.AnnotationParser
+ + 401485 zip file closed exception
+ + 401531 StringIndexOutOfBoundsException for "/*" <url-pattern> of
+ <jsp-property-group> fix for multiple mappings to *.jsp
+ + 401908 Enhance DosFilter to allow dynamic configuration of attributes.
+ + 402048 org.eclipse.jetty.server.ShutdownMonitor doesn't stop after the jetty
+ server is stopped
+ + 402485 reseed secure random
+ + 402735 jetty.sh to support status which is == check
+ + 402833 Test harness for global error page and hide exception message from
+ reason string
+
jetty-7.6.11.v20130520 - 20 May 2013
+ 402844 STOP.PORT & STOP.KEY behaviour has changed
+ 403281 jetty.sh waits for started or failure before returning
@@ -141,6 +313,57 @@
+ 402833 Test harness for global error page and hide exception message from
reason string
+jetty-8.1.9.v20130131 - 31 January 2013
+ + 362226 HttpConnection "wait" call causes thread resource exhaustion
+ + 367638 throw exception for excess form keys
+ + 381521 Only set Vary header when content could be compressed
+ + 382237 support non java JSON classes
+ + 391248 fixing localhost checking in statistics servlet
+ + 391249 fix for invalid XML node dispatchedTimeMean in statistics servlet
+ + 391345 fix missing br tag in statistics servlet
+ + 391623 Add option to --stop to wait for target jetty to stop
+ + 392417 Prevent Cookie parsing interpreting unicode chars
+ + 392492 expect headers only examined for requests>=HTTP/1.1
+ + 393075 1xx 204 and 304 ignore all headers suggesting content
+ + 393158 java.lang.IllegalStateException when sending an empty InputStream
+ + 393220 remove dead code from ServletHandler and log ServletExceptions in
+ warn instead of debug
+ + 393947 additional tests
+ + 393968 fix typo in javadoc
+ + 394294 A web-bundle started before jetty-osgi should be deployed as a webapp
+ when jetty-osgi starts
+ + 394514 Preserve URI parameters in sendRedirect
+ + 394541 remove continuation jar from distro, add as dep to test-jetty-webapp
+ + 394719 remove regex from classpath matching
+ + 394811 Make JAASLoginService log login failures to DEBUG instead of WARN.
+ Same for some other exceptions.
+ + 394829 Session can not be restored after SessionManager.setIdleSavePeriod
+ has saved the session
+ + 394839 Allow multipart mime with no boundary
+ + 394870 Make enablement of remote access to test webapp configurable in
+ override-web.xml
+ + 395215 Multipart mime with just LF and no CRLF
+ + 395380 add ValidUrlRule to jetty-rewrite
+ + 395394 allow logging from boot classloader
+ + 396253 FilterRegistration wrong order
+ + 396459 Log specific message for empty request body for multipart mime
+ requests
+ + 396500 HttpClient Exchange takes forever to complete when less content sent
+ than Content-Length
+ + 396574 add JETTY_HOME as a location for pid to be found
+ + 396886 MultiPartFilter strips bad escaping on filename="..."
+ + 397110 Accept %uXXXX encodings in URIs
+ + 397111 Tolerate empty or excessive whitespace preceeding MultiParts
+ + 397112 Requests with byte-range throws NPE if requested file has no mimetype
+ (eg no file extension)
+ + 397130 maxFormContentSize set in jetty.xml is ignored
+ + 397190 improve ValidUrlRule to iterate on codepoints
+ + 397321 Wrong condition in default start.config for annotations
+ + 397535 Support pluggable alias checking to support symbolic links
+ + 398337 UTF-16 percent encoding in UTF-16 form content
+ + 399132 check parent dir of session store against file to be removed
+ + JETTY-1533 handle URL with no path
+
jetty-7.6.9.v20130131 - 31 January 2013
+ 362226 HttpConnection "wait" call causes thread resource exhaustion
+ 367638 throw exception for excess form keys
@@ -186,6 +409,52 @@
+ 399132 check parent dir of session store against file to be removed
+ JETTY-1533 handle URL with no path
+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
@@ -215,11 +484,11 @@
+ 393383 delay onClose call until closeOut is done
+ 393494 HashSessionManager can't delete unrestorable sessions on Windows
-jetty-7.6.7.v20120910 - 10 September 2012
+jetty-8.1.7.v20120910 - 10 September 2012
+ 388895 Update dependencies for jetty-jndi
+ fix busy logging statement re: sessions
-jetty-7.6.6.v20120903 - 03 September 2012
+jetty-8.1.6.v20120903 - 03 September 2012
+ 347130 Empty getResourcePaths due to ZipFileClosedException
+ 367591 Support Env variables in XmlConfiguration.
+ 377055 Prevent webapp classloader leaks
@@ -252,7 +521,7 @@
+ 385925 make SslContextFactory.setProtocols and
SslContextFactory.setCipherSuites preserve the order of the given parameters
-jetty-7.6.5.v20120716 - 16 July 2012
+jetty-8.1.5.v20120716 - 16 June 2012
+ 376717 Balancer Servlet with round robin support, contribution, added
missing license
+ 379250 Server is added to shutdown hook twice
@@ -270,15 +539,20 @@
+ 383251 500 for SocketExceptions
+ 383881 WebSocketHandler sets request as handled
+ 384254 revert change to writable when not dispatched
+ + 384280 Implement preliminary ServletRegistrations
+ 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.
+ + 385138 add getter for session path and max cookie age that seemed to
+ disappear in a merge long ago
+ + JETTY-1523 It is imposible to map servlet to "/" using
+ WebApplicationInitializer
+ JETTY-1525 Show handle status in response debug message
+ JETTY-1530 refine search control on ldap login module
-jetty-7.6.4.v20120524 - 24 May 2012
+jetty-8.1.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
@@ -287,9 +561,10 @@
jetty-osgi-boot-logback bundle
+ 376152 apply context resources recursively
+ 376801 Make JAAS login modules useable without jetty infrastructure
+ + 377323 Request#getParts() throws ServletException when it should be throwing
+ IllegalStateException
+ 377391 Manifest updates to jetty-osgi-boot-logback
- + 377492 NPE when deploying a Web Application Bundle with unresolved
- Require-TldBundle
+ + 377492 NPE if jsp taglibs bundle not deployed
+ 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
@@ -310,14 +585,18 @@
+ 380212 Clear buffer if parsing fails due to full buffer
+ 380222 JettyPolicyRuntimeTest failure
-jetty-7.6.3.v20120416 - 16 April 2012
+jetty-8.1.3.v20120416 - 16 April 2012
+ + 349110 MultiPartFilter records the content-type in request params
+ 367172 Remove detection for slf4j NOPLogger
+ + 372678 Embedded Examples need updates for new LoginService requirement
+ 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
+ + 374152 jetty-all-server MANIFEST contains wrong import:
+ javax.servlet.annotation;version="[2.6,3)"
+ 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
@@ -336,13 +615,19 @@
+ 375594 fixed SSL tests so they are not order dependent
+ 375709 Ensure resolveTempDirectory failure does not deadlock; improve error
message
+ + 375906 Part.getHeader method not case insensitive
+ 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.
+ + 376324 <max-file-size> is not respected in <multipart-config>
+ + JETTY-1495 Ensure dynamic servlet addition does not cause servlets to be
+ inited.
+ + JETTY-1500 form parameters from multipart request not available via
+ request.getParameter
+ JETTY-1504 HttpServletResponseWrapper ignored when using asyncContext?
-jetty-7.6.2.v20120308 - 08 March 2012
+jetty-8.1.2.v20120308 - 08 March 2012
+ 370387 SafariWebsocketDraft0Test failure during build.
+ 371168 Update ClientCrossContextSessionTest
+ 372093 handle quotes in Require-Bundle manifest string
@@ -359,11 +644,12 @@
+ 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
+ + 373603 NullPointer in WebServletAnnotation
+ JETTY-1409 GzipFilter will double-compress application/x-gzip content
+ JETTY-1489 WebAppProvider attempts to deploy .svn folder
+ JETTY-1494 .
-jetty-7.6.1.v20120215 - 15 February 2012
+jetty-8.1.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
@@ -377,12 +663,98 @@
+ JETTY-1484 Add option for HashSessionManager to delete session files if it
can't restore them
-jetty-7.6.0.v20120127 - 27 January 2012
+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-8.1.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 NPE in HttpGenerator.prepareBuffers() test case.
+ + 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.
+ + 366774 removed XSS vulnerbility
+ + 367099 Upgrade jetty-websocket for RFC 6455 - Addendum.
+ + 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.
+ + 367548 jetty-osgi-boot must not import the nested package twice
+ + 367591 corrected configuration.xml version to 7.6
+ + 367635 Added support for start.d directory
+ + 367716 simplified maxIdleTime logic
+ + 368035 WebSocketClientFactory does not invoke super.doStop().
+ + 368060 do not encode sendRedirect URLs
+ + 368112 NPE on <jsp-config><taglib> element parsing web.xml
+ + 368113 Support servlet mapping to ""
+ + 368114 Protect against non-Strings in System properties for Log
+ + 368189 WebSocketClientFactory should not manage external thread pool. 368240
+ - Improve AggregateLifeCycle handling of shared lifecycles
+ + 368215 Remove debug from jaspi
+ + 368240 Better handling of locally created ThreadPool. Forgot to null out
+ field.
+ + 368291 Change warning to info for NoSuchFieldException on
+ BeanELResolver.properties
+ + 367638 limit number of form parameters to avoid DOS
+ + JETTY-1467 close half closed when idle
+
+jetty-8.1.0.RC2 - 22 December 2011
+ + 359329 jetty-jaspi must exports its packages. jetty-plus must import
+ javax.security
+ + 364638 HttpParser closes if data received while seeking EOF. Tests fixed to
+ cope
+ + 364921 Made test less time sensitive
+ + 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.
+ + 367383 jsp-config element must be returned for
+ ServletContext.getJspConfigDescriptor
+ + 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
+ + 365370 ServletHandler can fall through to nested handler
+
+jetty-8.1.0.RC0 - 30 November 2011
+ + 352565 cookie httponly flag ignored
+ + 353285 ServletSecurity annotation ignored
+ + 357163 jetty 8 ought to proxy jetty8 javadocs
+ + 357209 JSP tag listeners not called
+ + 360051 SocketConnectionTest.testServerClosedConnection is excluded.
+ + 361135 Allow session cookies to NEVER be marked as secure, even on HTTPS
+ requests.
+ + 362249 update shell scripts to jetty8
+ + 363878 Add ecj compiler to jetty-8 for jsp
+ + 364283 can't parse the servlet multipart-config for the web.xml
+ + 364430 Support web.xml enabled state for servlets
+
jetty-7.6.0.RC5 - 20 January 2012
+ 359329 Prevent reinvocation of LoginModule.login with jaspi for already
authed user
@@ -489,6 +861,22 @@
+ 364657 Support HTTP only cookies from standard API
+ JETTY-1442 add _hostHeader setter for ProxyRule
+jetty-8.0.4.v20111024 - 24 October 2011
+ + 358263 JDBCSessionIdManager add setDatasource(DataSource) method
+ + 358649 Replace existing StdErrLog system properties for DEBUG/IGNORED with
+ LEVEL instead.
+ + 360836 Accept parameters with bad UTF-8. Use replacement character
+ + 360912 CrossOriginFilter does not send Access-Control-Allow-Origin on
+ responses. 355103 Make allowCredentials default to true in
+ CrossOriginFilter.
+ + 360938 Connections closed after a while.
+ + 361135 secure cookies for sessions
+ + 361319 Log initialization does not catch correct exceptions on all jvms
+ + 361325 359292 Allow KeyStore to be set
+ + 361456 release timer task on connection failed
+ + 361655 ExecutorThreadPool.isLowOnThreads() returns wrong value.
+ + JETTY-1444 start threadpool before selector manager
+
jetty-7.5.4.v20111024 - 24 October 2011
+ 358263 JDBCSessionIdManager add setDatasource(DataSource) method
+ 358649 Replace existing StdErrLog system properties for DEBUG/IGNORED with
@@ -504,12 +892,12 @@
+ 361655 ExecutorThreadPool.isLowOnThreads() returns wrong value.
+ JETTY-1444 start threadpool before selector manager
-jetty-7.5.3.v20111011 - 11 October 2011
+jetty-8.0.3.v20111011 - 11 October 2011
+ 348978 migrate jetty-http-spi
+ 358649 StdErrLog system properties for package/class logging LEVEL.
-jetty-7.5.2.v20111006 - 06 October 2011
- + 336443 check nonce count is increasing
+jetty-8.0.2.v20111006 - 06 October 2011
+ + 336443 add missing comma in DigestAuthenticator string
+ 342161 ScannerTest fails intermittently on Mac OS X
+ 346419 testing HttpClient FDs
+ 353267 Request._parameters initialization bug
@@ -517,8 +905,10 @@
+ 353627 Basic Auth checks that Basic method has been send
+ 356144 Allow SelectorManager thread priority to be set
+ 356274 Start SSL socket factory in call to open()
+ + 357163 jetty 8 ought to proxy jetty8 javadocs
+ 357178 websockets draft 14 support
+ 357188 Send content buffer directly
+ + 357209 JSP tag listeners not called
+ 357216 Logging via Log4J does not expand braces in format strings
+ 357240 more half close refinements
+ 357338 remove debug
@@ -558,6 +948,73 @@
+ JETTY-1434 Add a jsp that exercises jstl.
+ JETTY-1439 space in directory installation path causes classloader problem
+jetty-7.5.3.v20111011 - 11 October 2011
+ + 348978 migrate jetty-http-spi
+ + 358649 StdErrLog system properties for package/class logging LEVEL.
+
+jetty-7.5.2.v20111006 - 06 October 2011
+ + 336443 check nonce count is increasing
+ + 342161 ScannerTest fails intermittently on Mac OS X
+ + 346419 testing HttpClient FDs
+ + 353267 Request._parameters initialization bug
+ + 353509 jetty-client unit tests are running too long
+ + 353627 Basic Auth checks that Basic method has been send
+ + 356144 Allow SelectorManager thread priority to be set
+ + 356274 Start SSL socket factory in call to open()
+ + 357178 websockets draft 14 support
+ + 357188 Send content buffer directly
+ + 357209 JSP tag listeners not called
+ + 357216 Logging via Log4J does not expand braces in format strings
+ + 357240 more half close refinements
+ + 357338 remove debug
+ + 357672 resolve issue with serializing pojos with mongodb session manager,
+ thanks to john simone for the discovery and fix
+ + 357959 Include javadoc in distribution
+ + 358027 NullPointerException in ResourceHandler with jetty-stylesheet.css
+ + 358035 idle time only active if > 0
+ + 358147 Add catch for UnknownHostException to fix leaky file descriptor in
+ client
+ + 358164 Dispatch from servlet to handler
+ + 358263 add method for osgi users to register a driver as Class.forName does
+ not work for them
+ + 358649 StdErrLog system properties for package/class logging LEVEL.
+ + 358674 Still allows sslv3 for now
+ + 358687 Updated jsp does not scan for system tlds Fixed pattern.
+ + 358784 JSP broken on Java 1.5
+ + 358925 bit more javadoc on usage
+ + 358959 File descriptor leak with UnresolvedAddressException
+ + 359309 adjust previous test for servletPath to include pathInfo
+ + 359673 updated websocket version handling
+ + 359675 Principal != String, fix for issue in property file login manager
+ + 360051 SocketConnectionTest.testServerClosedConnection is excluded.
+ + 360066 jsps referenced in web.xml <jsp-file> elements do not compile
+ + JETTY-1130 Access Sessions from HashSessionIdManager
+ + JETTY-1277 Fixed sendRedirect encoding of relative locations
+ + JETTY-1322 idle sweeper checks for closed endp
+ + JETTY-1377 extra logging for busy selector
+ + JETTY-1378 new sys property for the latest jsp-impl to force the use of the
+ JDTCompiler when running in OSGi.
+ + JETTY-1414 applied to PropertyUserStore
+ + JETTY-1415 Start/Stop Server and Client only once in test, code format
+ + JETTY-1420 Set Host header for new request in RedirectListener
+ + JETTY-1421 Implement RedirectListener.onException,onConnectionFailed
+ + JETTY-1423 force connection to be closed returned
+ + JETTY-1430 local JNDI contexts don't carry environment
+ + JETTY-1434 Add a jsp that exercises jstl.
+ + JETTY-1439 space in directory installation path causes classloader problem
+
+jetty-8.0.1.v20110908 - 08 September 2011
+ + 350634 Added Resource.newResource(File)
+ + 356190 fix monodb tests for changed test api
+ + 356428 removed timed waits from test
+ + 356693 reduce visibility to webapp of websocket implementations
+ + 356695 jetty server jars are provided for websockets
+ + 356726 Instead of the sessionDestroyed called sessionCreated after
+ invalidate session
+ + 356751 Add null protection to ServletContextHandler.doStop
+ + 356823 correctly decode close codes. Send not utf-8 close code.
+ + 357058 Acceptor thread blocking
+
jetty-7.5.1.v20110908 - 08 September 2011
+ 350634 Added Resource.newResource(File)
+ 356190 fix monodb tests for changed test api
@@ -570,6 +1027,12 @@
+ 356823 correctly decode close codes. Send not utf-8 close code.
+ 357058 Acceptor thread blocking
+jetty-8.0.0.v20110901 - 01 September 2011
+ + 352565 cookie httponly flag ignored
+ + 353073 better warnings
+ + 353285 ServletSecurity annotation ignored
+ + 356421 Upgraded websocket to draft 13 support
+
jetty-7.5.0.v20110901 - 01 September 2011
+ 356421 Upgraded websocket to draft 13 support
+ 353073 better warnings
@@ -604,6 +1067,19 @@
+ JETTY-1414 HashLoginService doesn't refresh realm if specified config
filename is not an absolute platform specific value
+jetty-8.0.0.RC0 - 16 August 2011
+ + Merge from jetty-7.4.3
+ + Enable annotations by default
+ + 352565 cookie httponly flag ignored
+ + 353285 ServletSecurity annotation ignored
+
+jetty-8.0.0.M3 - 27 May 2011
+ + 324505 Implement API login
+ + 335500 request.getParts() throws a NullPointerException
+ + 343472 isUserInRole does not prevent subsequent login call.
+ + 346180 jsp-2.2 support
+ + Updated to jetty-7.4.2.v20110526
+
jetty-7.5.0.RC0 - 15 August 2011
+ 298502 Handle 200 Connect responses with no content-length
+ 347484 / - > ${/} in some paths in grant codebases
@@ -786,6 +1262,26 @@
+ Ensure generated fragment names are unique
+ Added extra session removal test
+jetty-8.0.0.M2 - 16 November 2010
+ + 320073 Reconsile configuration mechanism
+ + 321068 JSF2 fails to initialize
+ + 324493 Registration init parameter handling null check, setInitParameters
+ additive
+ + 324505 Request.login method must throw ServletException if it cant login
+ + 324872 allow disabling listener restriction from using *Registration
+ interfaces
+ + 327416 Change meaning of @HandlesTypes in line with latest interpretation by
+ JSR315
+ + 327489 Change meaning of @MultipartConfig to match servlet spec 3.0
+ maintenance release 3.0a
+ + 328008 Handle update to Servlet Spec 3 Section 8.2.3.h.ii
+ + 330188 Reject web-fragment.xml with same <name> as another already loaded
+ one
+ + 330208 Support new wording on servlet-mapping and filter-mapping merging
+ from servlet3.0a
+ + 330292 request.getParts() returns only one part when the name is the same
+ + Update to jetty-7.2.1.v20101111
+
jetty-7.3.1.v20110307 - 07 March 2011
+ 316382 Support a more strict SSL option with certificates
+ 333481 Handle UCS-4 codepoints in decode and encode
@@ -811,9 +1307,7 @@
+ 338068 Leaking ConstraintMappings on redeploy
+ 338092 ProxyServlet leaks memory
+ 338607 Removed managed attributes when context is stopped
- + 338880 Fixed failing buffer range checks
- + 338920 Handle non existent real path directories
- + 338961 AJP packet size
+ + 338819 Externally control Deployment Manager application lifecycle
+ JETTY-1304 Allow quoted boundaries in Multipart filter
+ JETTY-1317 More elegent handling of bad URIs in requests
+ JETTY-1331 Allow alternate XML configuration processors (eg spring)
@@ -999,7 +1493,6 @@
+ JETTY-1245 Do not use direct buffers with NIO SSL
+ JETTY-1249 Apply max idle time to all connectors
+ JETTY-1250 Parallel start of HandlerCollection
- + JETTY-1252 Handle more multipart transfer encodings
+ JETTY-1256 annotation and jta jars from Orbit
+ JETTY-1259 NullPointerException in JDBCSessionIdManager when invalidating
session
@@ -1027,6 +1520,15 @@
+ JETTY-1249 Apply max idle time to all connectors
+ JETTY-1251 Replace then close selector for JVM bugs
+jetty-8.0.0.M1 - 12 July 2010
+ + 306350 Ensure jars excluded by ordering are not scanned for annotations
+ + JETTY-1224 Change jetty-8 merge rules for fragment descriptors and
+ annotations
+ + Ensure <absolute-ordering> in web.xml overrides relative <ordering> in
+ fragments
+ + Ensure empty <absolute-ordering> implies exclusion of all fragments
+ + Ensure servlet-api jar class inheritance hierarchy is scanned
+
jetty-7.1.5.v20100705
+ Update ecj to 3.6 Helios release drop
+ 288194 Add blacklist/whitelist to ProxyServlet and ProxyHandler
@@ -1190,6 +1692,9 @@
+ Fix jetty-plus.xml reference to addLifeCycle
+ JETTY-1200 SSL NIO Endpoint wraps non NIO buffers
+ JETTY-1202 Use platform default algorithm for SecureRandom
+ + Merged 7.0.2.v20100331
+ + Add NPE protection to ContainerInitializerConfiguration
+ + Temporarily remove jetty-osgi module to clarify jsp version compatibility
+ JETTY-1212 handle long content lengths
+ JETTY-1214 avoid ISE when scavenging invalid session
+ JETTY-903 Stop both caches
@@ -1338,6 +1843,11 @@
+ 305997 Coalesce buffers in ChannelEndPoint.flush()
+ 306028 Enable TCP_NODELAY by default in client connectors
+jetty-8.0.0.M0 - 28 February 2010
+ + Updated servlet 3.0 spec 20100224
+ + Merged 7.0.1.v20091116
+ + Updated to cometd 1.0.1
+
jetty-7.0.1.v20091125 - 25 November 2009
+ 274251 DefaultServlet supports exact match mode.
+ 288401 HttpExchange.cancel() Method Unimplemented
diff --git a/example-async-rest/async-rest-jar/pom.xml b/example-async-rest/async-rest-jar/pom.xml
new file mode 100644
index 0000000..87c5de9
--- /dev/null
+++ b/example-async-rest/async-rest-jar/pom.xml
@@ -0,0 +1,24 @@
+<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>example-async-rest</artifactId>
+ <version>8.1.19-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.eclipse.jetty.example-async-rest</groupId>
+ <artifactId>example-async-rest-jar</artifactId>
+ <packaging>jar</packaging>
+ <name>Example Async Rest :: Jar</name>
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-client</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.orbit</groupId>
+ <artifactId>javax.servlet</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/example-async-rest/async-rest-jar/src/main/java/org/eclipse/jetty/example/asyncrest/AbstractRestServlet.java b/example-async-rest/async-rest-jar/src/main/java/org/eclipse/jetty/example/asyncrest/AbstractRestServlet.java
new file mode 100644
index 0000000..f23518e
--- /dev/null
+++ b/example-async-rest/async-rest-jar/src/main/java/org/eclipse/jetty/example/asyncrest/AbstractRestServlet.java
@@ -0,0 +1,120 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.example.asyncrest;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.net.URLEncoder;
+import java.util.Map;
+import java.util.Queue;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Abstract Servlet implementation class AsyncRESTServlet.
+ * Enquires ebay REST service for auctions by key word.
+ * May be configured with init parameters: <dl>
+ * <dt>appid</dt><dd>The eBay application ID to use</dd>
+ * </dl>
+ * Each request examines the following request parameters:<dl>
+ * <dt>items</dt><dd>The keyword to search for</dd>
+ * </dl>
+ */
+public class AbstractRestServlet extends HttpServlet
+{
+ protected final static String __DEFAULT_APPID = "Webtide81-adf4-4f0a-ad58-d91e41bbe85";
+ protected final static String STYLE =
+ "<style type='text/css'>"+
+ " img.thumb:hover {height:50px}"+
+ " img.thumb {vertical-align:text-top}"+
+ " span.red {color: #ff0000}"+
+ " span.green {color: #00ff00}"+
+ " iframe {border: 0px}"+
+ "</style>";
+
+ protected final static String ITEMS_PARAM = "items";
+ protected final static String APPID_PARAM = "appid";
+
+ protected String _appid;
+
+ public void init(ServletConfig servletConfig) throws ServletException
+ {
+ if (servletConfig.getInitParameter(APPID_PARAM) == null)
+ _appid = __DEFAULT_APPID;
+ else
+ _appid = servletConfig.getInitParameter(APPID_PARAM);
+ }
+
+ protected String restURL(String item)
+ {
+ try
+ {
+ return ("http://open.api.ebay.com/shopping?MaxEntries=3&appid=" + _appid +
+ "&version=573&siteid=0&callname=FindItems&responseencoding=JSON&QueryKeywords=" +
+ URLEncoder.encode(item,"UTF-8"));
+ }
+ catch(Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected String generateThumbs(Queue<Map<String,String>> results)
+ {
+ StringBuilder thumbs = new StringBuilder();
+ for (Map<String, String> m : results)
+ {
+ if (!m.containsKey("GalleryURL"))
+ continue;
+
+ thumbs.append("<a href=\""+m.get("ViewItemURLForNaturalSearch")+"\">");
+ thumbs.append("<img class='thumb' border='1px' height='25px'"+
+ " src='"+m.get("GalleryURL")+"'"+
+ " title='"+m.get("Title")+"'"+
+ "/>");
+ thumbs.append("</a> ");
+ }
+ return thumbs.toString();
+ }
+
+ protected String ms(long nano)
+ {
+ BigDecimal dec = new BigDecimal(nano);
+ return dec.divide(new BigDecimal(1000000L)).setScale(1,RoundingMode.UP).toString();
+ }
+
+ protected int width(long nano)
+ {
+ int w=(int)((nano+999999L)/5000000L);
+ if (w==0)
+ w=2;
+ return w;
+ }
+
+ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ doGet(request, response);
+ }
+
+}
diff --git a/example-async-rest/async-rest-jar/src/main/java/org/eclipse/jetty/example/asyncrest/AsyncRestServlet.java b/example-async-rest/async-rest-jar/src/main/java/org/eclipse/jetty/example/asyncrest/AsyncRestServlet.java
new file mode 100644
index 0000000..4438559
--- /dev/null
+++ b/example-async-rest/async-rest-jar/src/main/java/org/eclipse/jetty/example/asyncrest/AsyncRestServlet.java
@@ -0,0 +1,211 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.example.asyncrest;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.client.ContentExchange;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.util.ajax.JSON;
+
+/**
+ * Servlet implementation class AsyncRESTServlet.
+ * Enquires ebay REST service for auctions by key word.
+ * May be configured with init parameters: <dl>
+ * <dt>appid</dt><dd>The eBay application ID to use</dd>
+ * </dl>
+ * Each request examines the following request parameters:<dl>
+ * <dt>items</dt><dd>The keyword to search for</dd>
+ * </dl>
+ */
+public class AsyncRestServlet extends AbstractRestServlet
+{
+ final static String RESULTS_ATTR = "org.eclipse.jetty.demo.client";
+ final static String DURATION_ATTR = "org.eclipse.jetty.demo.duration";
+ final static String START_ATTR = "org.eclispe.jetty.demo.start";
+
+ HttpClient _client;
+
+ public void init(ServletConfig servletConfig) throws ServletException
+ {
+ super.init(servletConfig);
+
+ _client = new HttpClient();
+ _client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
+
+ try
+ {
+ _client.start();
+ }
+ catch (Exception e)
+ {
+ throw new ServletException(e);
+ }
+ }
+
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ Long start=System.nanoTime();
+
+ // Do we have results yet?
+ Queue<Map<String, String>> results = (Queue<Map<String, String>>) request.getAttribute(RESULTS_ATTR);
+
+ // If no results, this must be the first dispatch, so send the REST request(s)
+ if (results==null)
+ {
+ // define results data structures
+ final Queue<Map<String, String>> resultsQueue = new ConcurrentLinkedQueue<Map<String,String>>();
+ request.setAttribute(RESULTS_ATTR, results=resultsQueue);
+
+ // suspend the request
+ // This is done before scheduling async handling to avoid race of
+ // dispatch before startAsync!
+ final AsyncContext async = request.startAsync();
+ async.setTimeout(30000);
+
+ // extract keywords to search for
+ String[] keywords=request.getParameter(ITEMS_PARAM).split(",");
+ final AtomicInteger outstanding=new AtomicInteger(keywords.length);
+
+ // Send request each keyword
+ for (final String item:keywords)
+ {
+ _client.send(
+ new AsyncRestRequest(item)
+ {
+ void onAuctionFound(Map<String,String> auction)
+ {
+ resultsQueue.add(auction);
+ }
+ void onComplete()
+ {
+ if (outstanding.decrementAndGet()<=0)
+ async.dispatch();
+ }
+ });
+ }
+
+ // save timing info and return
+ request.setAttribute(START_ATTR, start);
+ request.setAttribute(DURATION_ATTR, new Long(System.nanoTime() - start));
+
+ return;
+ }
+
+ // We have results!
+
+ // Generate the response
+ String thumbs = generateThumbs(results);
+
+ response.setContentType("text/html");
+ PrintWriter out = response.getWriter();
+ out.println("<html><head>");
+ out.println(STYLE);
+ out.println("</head><body><small>");
+
+ long initial = (Long) request.getAttribute(DURATION_ATTR);
+ long start0 = (Long) request.getAttribute(START_ATTR);
+
+ long now = System.nanoTime();
+ long total=now-start0;
+ long generate=now-start;
+ long thread=initial+generate;
+
+ out.print("<b>Asynchronous: "+request.getParameter(ITEMS_PARAM)+"</b><br/>");
+ out.print("Total Time: "+ms(total)+"ms<br/>");
+
+ out.print("Thread held (<span class='red'>red</span>): "+ms(thread)+"ms (" + ms(initial) + " initial + " + ms(generate) + " generate )<br/>");
+ out.print("Async wait (<span class='green'>green</span>): "+ms(total-thread)+"ms<br/>");
+
+ out.println("<img border='0px' src='asyncrest/red.png' height='20px' width='"+width(initial)+"px'>"+
+ "<img border='0px' src='asyncrest/green.png' height='20px' width='"+width(total-thread)+"px'>"+
+ "<img border='0px' src='asyncrest/red.png' height='20px' width='"+width(generate)+"px'>");
+
+ out.println("<hr />");
+ out.println(thumbs);
+ out.println("</small>");
+ out.println("</body></html>");
+ out.close();
+ }
+
+ private abstract class AsyncRestRequest extends ContentExchange
+ {
+ AsyncRestRequest(final String item)
+ {
+ // send the exchange
+ setMethod("GET");
+ setURL(restURL(item));
+ }
+
+ abstract void onAuctionFound(Map<String,String> details);
+ abstract void onComplete();
+
+ protected void onResponseComplete() throws IOException
+ {
+ // extract auctions from the results
+ Map<String,?> query = (Map<String,?>) JSON.parse(this.getResponseContent());
+ Object[] auctions = (Object[]) query.get("Item");
+ if (auctions != null)
+ {
+ for (Object o : auctions)
+ onAuctionFound((Map<String,String>)o);
+ }
+
+ onComplete();
+ }
+
+ /* ------------------------------------------------------------ */
+ protected void onConnectionFailed(Throwable ex)
+ {
+ getServletContext().log("onConnectionFailed: ",ex);
+ onComplete();
+ }
+
+ /* ------------------------------------------------------------ */
+ protected void onException(Throwable ex)
+ {
+ getServletContext().log("onConnectionFailed: ",ex);
+ onComplete();
+ }
+
+ /* ------------------------------------------------------------ */
+ protected void onExpire()
+ {
+ getServletContext().log("onConnectionFailed: expired");
+ onComplete();
+ }
+ }
+
+ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ doGet(request, response);
+ }
+
+}
diff --git a/example-async-rest/async-rest-jar/src/main/java/org/eclipse/jetty/example/asyncrest/SerialRestServlet.java b/example-async-rest/async-rest-jar/src/main/java/org/eclipse/jetty/example/asyncrest/SerialRestServlet.java
new file mode 100644
index 0000000..b6af591
--- /dev/null
+++ b/example-async-rest/async-rest-jar/src/main/java/org/eclipse/jetty/example/asyncrest/SerialRestServlet.java
@@ -0,0 +1,106 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.example.asyncrest;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Queue;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.util.ajax.JSON;
+
+/**
+ * Servlet implementation class SerialRestServlet
+ */
+public class SerialRestServlet extends AbstractRestServlet
+{
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ long start = System.nanoTime();
+
+
+ String[] keywords=request.getParameter(ITEMS_PARAM).split(",");
+ Queue<Map<String,String>> results = new LinkedList<Map<String,String>>();
+
+ // make all requests serially
+ for (String itemName : keywords)
+ {
+ URL url = new URL(restURL(itemName));
+
+ HttpURLConnection connection = (HttpURLConnection)url.openConnection();
+ connection.setRequestMethod("GET");
+
+ Map query = (Map)JSON.parse(new BufferedReader(new InputStreamReader(connection.getInputStream())));
+ Object[] auctions = (Object[]) query.get("Item");
+ if (auctions != null)
+ {
+ for (Object o : auctions)
+ results.add((Map) o);
+ }
+ }
+
+
+ // Generate the response
+ String thumbs=generateThumbs(results);
+
+ response.setContentType("text/html");
+ PrintWriter out = response.getWriter();
+ out.println("<html><head>");
+ out.println(STYLE);
+ out.println("</head><body><small>");
+
+ long now = System.nanoTime();
+ long total=now-start;
+
+ out.print("<b>Blocking: "+request.getParameter(ITEMS_PARAM)+"</b><br/>");
+ out.print("Total Time: "+ms(total)+"ms<br/>");
+ out.print("Thread held (<span class='red'>red</span>): "+ms(total)+"ms<br/>");
+
+ out.println("<img border='0px' src='asyncrest/red.png' height='20px' width='"+width(total)+"px'>");
+
+ out.println("<hr />");
+ out.println(thumbs);
+ out.println("</small>");
+ out.println("</body></html>");
+ out.close();
+
+
+
+ }
+
+ /**
+ * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
+ * response)
+ */
+ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ doGet(request, response);
+ }
+
+}
diff --git a/example-async-rest/async-rest-jar/src/main/resources/META-INF/resources/asyncrest.html b/example-async-rest/async-rest-jar/src/main/resources/META-INF/resources/asyncrest.html
new file mode 100644
index 0000000..f92f7f6
--- /dev/null
+++ b/example-async-rest/async-rest-jar/src/main/resources/META-INF/resources/asyncrest.html
@@ -0,0 +1,38 @@
+<html>
+ <head>
+ <style type='text/css'>
+ iframe {border: 0px}
+ table, tr, td {border: 0px}
+ </style>
+</head>
+<body>
+<h1>Blocking vs Asynchronous REST</h1>
+<p>
+This demo calls the EBay WS API both synchronously and asynchronously,
+to obtain items matching each of the keywords passed on the query
+string. The time the request thread is head is displayed for both.
+</p>
+
+<table width='100%'>
+
+<tr>
+<td>
+ <iframe id="f1" width='100%' height='175px' src="testSerial?items=kayak"></iframe>
+</td>
+<td>
+ <iframe id="f3" width='100%' height='175px' src="testSerial?items=mouse,beer,gnome"></iframe>
+</td>
+</tr>
+
+<tr>
+<td>
+ <iframe id="f2" width='100%' height='175px' src="testAsync?items=kayak"/></iframe>
+</td>
+<td>
+ <iframe id="f4" width='100%' height='175px' src="testAsync?items=mouse,beer,gnome"/></iframe>
+</td>
+</tr>
+
+</table>
+</body>
+</html>
diff --git a/example-async-rest/async-rest-jar/src/main/resources/META-INF/resources/asyncrest/green.png b/example-async-rest/async-rest-jar/src/main/resources/META-INF/resources/asyncrest/green.png
new file mode 100644
index 0000000..d0fb842
--- /dev/null
+++ b/example-async-rest/async-rest-jar/src/main/resources/META-INF/resources/asyncrest/green.png
Binary files differ
diff --git a/example-async-rest/async-rest-jar/src/main/resources/META-INF/resources/asyncrest/red.png b/example-async-rest/async-rest-jar/src/main/resources/META-INF/resources/asyncrest/red.png
new file mode 100644
index 0000000..f2a79a0
--- /dev/null
+++ b/example-async-rest/async-rest-jar/src/main/resources/META-INF/resources/asyncrest/red.png
Binary files differ
diff --git a/example-async-rest/async-rest-jar/src/main/resources/META-INF/web-fragment.xml b/example-async-rest/async-rest-jar/src/main/resources/META-INF/web-fragment.xml
new file mode 100644
index 0000000..9876d99
--- /dev/null
+++ b/example-async-rest/async-rest-jar/src/main/resources/META-INF/web-fragment.xml
@@ -0,0 +1,22 @@
+<web-fragment>
+ <servlet>
+ <display-name>SerialRestServlet</display-name>
+ <servlet-name>SerialRestServlet</servlet-name>
+ <servlet-class>org.eclipse.jetty.example.asyncrest.SerialRestServlet</servlet-class>
+ </servlet>
+ <servlet-mapping>
+ <servlet-name>SerialRestServlet</servlet-name>
+ <url-pattern>/testSerial</url-pattern>
+ </servlet-mapping>
+
+ <servlet>
+ <display-name>AsyncRestServlet</display-name>
+ <servlet-name>AsyncRestServlet</servlet-name>
+ <servlet-class>org.eclipse.jetty.example.asyncrest.AsyncRestServlet</servlet-class>
+ <async-supported>true</async-supported>
+ </servlet>
+ <servlet-mapping>
+ <servlet-name>AsyncRestServlet</servlet-name>
+ <url-pattern>/testAsync</url-pattern>
+ </servlet-mapping>
+</web-fragment>
\ No newline at end of file
diff --git a/example-async-rest/async-rest-webapp/pom.xml b/example-async-rest/async-rest-webapp/pom.xml
new file mode 100644
index 0000000..3cf6a5c
--- /dev/null
+++ b/example-async-rest/async-rest-webapp/pom.xml
@@ -0,0 +1,34 @@
+<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>example-async-rest</artifactId>
+ <version>8.1.19-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.eclipse.jetty.example-async-rest</groupId>
+ <artifactId>example-async-rest-webapp</artifactId>
+ <name>Example Async Rest :: Webapp</name>
+ <url>http://www.eclipse.org/jetty</url>
+ <packaging>war</packaging>
+ <build>
+ <finalName>async-rest</finalName>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.jetty.example-async-rest</groupId>
+ <artifactId>example-async-rest-jar</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-webapp</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.orbit</groupId>
+ <artifactId>javax.servlet</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/example-async-rest/async-rest-webapp/src/main/webapp/META-INF/MANIFEST.MF b/example-async-rest/async-rest-webapp/src/main/webapp/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..5e94951
--- /dev/null
+++ b/example-async-rest/async-rest-webapp/src/main/webapp/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Class-Path:
+
diff --git a/example-async-rest/async-rest-webapp/src/main/webapp/WEB-INF/web.xml b/example-async-rest/async-rest-webapp/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..8916bdb
--- /dev/null
+++ b/example-async-rest/async-rest-webapp/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+ version="3.0">
+
+ <display-name>Async REST Webservice Example</display-name>
+
+</web-app>
diff --git a/example-async-rest/async-rest-webapp/src/main/webapp/index.html b/example-async-rest/async-rest-webapp/src/main/webapp/index.html
new file mode 100644
index 0000000..f92f7f6
--- /dev/null
+++ b/example-async-rest/async-rest-webapp/src/main/webapp/index.html
@@ -0,0 +1,38 @@
+<html>
+ <head>
+ <style type='text/css'>
+ iframe {border: 0px}
+ table, tr, td {border: 0px}
+ </style>
+</head>
+<body>
+<h1>Blocking vs Asynchronous REST</h1>
+<p>
+This demo calls the EBay WS API both synchronously and asynchronously,
+to obtain items matching each of the keywords passed on the query
+string. The time the request thread is head is displayed for both.
+</p>
+
+<table width='100%'>
+
+<tr>
+<td>
+ <iframe id="f1" width='100%' height='175px' src="testSerial?items=kayak"></iframe>
+</td>
+<td>
+ <iframe id="f3" width='100%' height='175px' src="testSerial?items=mouse,beer,gnome"></iframe>
+</td>
+</tr>
+
+<tr>
+<td>
+ <iframe id="f2" width='100%' height='175px' src="testAsync?items=kayak"/></iframe>
+</td>
+<td>
+ <iframe id="f4" width='100%' height='175px' src="testAsync?items=mouse,beer,gnome"/></iframe>
+</td>
+</tr>
+
+</table>
+</body>
+</html>
diff --git a/example-async-rest/async-rest-webapp/src/test/java/org/eclipse/jetty/example/asyncrest/DemoServer.java b/example-async-rest/async-rest-webapp/src/test/java/org/eclipse/jetty/example/asyncrest/DemoServer.java
new file mode 100644
index 0000000..bb5ded0
--- /dev/null
+++ b/example-async-rest/async-rest-webapp/src/test/java/org/eclipse/jetty/example/asyncrest/DemoServer.java
@@ -0,0 +1,48 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.example.asyncrest;
+
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.nio.SelectChannelConnector;
+import org.eclipse.jetty.webapp.WebAppContext;
+
+public class DemoServer
+{
+ public static void main(String[] args)
+ throws Exception
+ {
+ String jetty_home = System.getProperty("jetty.home",".");
+
+ Server server = new Server();
+
+ Connector connector=new SelectChannelConnector();
+ connector.setPort(Integer.getInteger("jetty.port",8080).intValue());
+ server.setConnectors(new Connector[]{connector});
+
+ WebAppContext webapp = new WebAppContext();
+ webapp.setContextPath("/");
+ webapp.setWar(jetty_home+"/target/example-async-rest-webapp-8.0.0.M0-SNAPSHOT");
+
+ server.setHandler(webapp);
+
+ server.start();
+ server.join();
+ }
+}
diff --git a/example-async-rest/pom.xml b/example-async-rest/pom.xml
new file mode 100644
index 0000000..df9baaf
--- /dev/null
+++ b/example-async-rest/pom.xml
@@ -0,0 +1,17 @@
+<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>8.1.19-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>example-async-rest</artifactId>
+ <packaging>pom</packaging>
+ <name>Example Async Rest</name>
+ <url>http://www.eclipse.org/jetty</url>
+ <modules>
+ <module>async-rest-jar</module>
+ <module>async-rest-webapp</module>
+ </modules>
+</project>
diff --git a/example-jetty-embedded/pom.xml b/example-jetty-embedded/pom.xml
index c594d0a..7630c72 100644
--- a/example-jetty-embedded/pom.xml
+++ b/example-jetty-embedded/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>example-jetty-embedded</artifactId>
diff --git a/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java b/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java
index 76a8bc2..2e72770 100644
--- a/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java
+++ b/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java
@@ -149,7 +149,7 @@
webapp_provider.setDefaultsDescriptor(jetty_home + "/etc/webdefault.xml");
webapp_provider.setContextXmlDir(jetty_home + "/contexts");
deployer.addAppProvider(webapp_provider);
-
+
HashLoginService login = new HashLoginService();
login.setName("Test Realm");
login.setConfig(jetty_home + "/etc/realm.properties");
diff --git a/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/ManyContexts.java b/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/ManyContexts.java
index 1104f9a..bdc8057 100644
--- a/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/ManyContexts.java
+++ b/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/ManyContexts.java
@@ -74,5 +74,4 @@
System.err.println(server.dump());
server.join();
}
-
}
diff --git a/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java b/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java
index 192d7f2..5414ddf 100644
--- a/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java
+++ b/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java
@@ -34,7 +34,11 @@
server.setConnectors(new Connector[]
{ connector });
- String war = args.length > 0?args[0]: "../test-jetty-webapp/target/test-jetty-webapp-" + Server.getVersion();
+
+ //If you're running this from inside Eclipse, then Server.getVersion will not provide
+ //the correct number as there is no manifest. Use the command line instead to provide the path to the
+ //test webapp
+ String war = args.length > 0?args[0]: "../test-jetty-webapp/target/test-jetty-webapp-"+Server.getVersion();
String path = args.length > 1?args[1]:"/";
System.err.println(war + " " + path);
@@ -42,6 +46,15 @@
WebAppContext webapp = new WebAppContext();
webapp.setContextPath(path);
webapp.setWar(war);
+
+ //If the webapp contains security constraints, you will need to configure a LoginService
+ if (war.contains("test-jetty-webapp"))
+ {
+ org.eclipse.jetty.security.HashLoginService loginService = new org.eclipse.jetty.security.HashLoginService();
+ loginService.setName("Test Realm");
+ loginService.setConfig("src/test/resources/realm.properties");
+ webapp.getSecurityHandler().setLoginService(loginService);
+ }
server.setHandler(webapp);
diff --git a/jetty-aggregate/jetty-all-server/pom.xml b/jetty-aggregate/jetty-all-server/pom.xml
index 7bf09c0..9ebcc1e 100644
--- a/jetty-aggregate/jetty-all-server/pom.xml
+++ b/jetty-aggregate/jetty-all-server/pom.xml
@@ -3,7 +3,7 @@
<parent>
<groupId>org.eclipse.jetty.aggregate</groupId>
<artifactId>jetty-aggregate-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-all-server</artifactId>
@@ -79,10 +79,11 @@
<instructions>
<Import-Package>
!org.eclipse.jetty*,
- com.sun.org.apache.commons.logging;version="[2.1,3)";split="glassfish";resolution:=optional,
javax.annotation;version="1.0.0";resolution:=optional,
- javax.servlet;version="2.5.0",
- javax.servlet.http;version="2.5.0",
+ javax.servlet;version="2.6.0",
+ javax.servlet.annotation;version="2.6.0",
+ javax.servlet.descriptor;version="2.6.0",
+ javax.servlet.http;version="2.6.0",
javax.mail;version="1.4.0";resolution:=optional,
javax.mail.event;version="1.4.0";resolution:=optional,
javax.mail.internet;version="1.4.0";resolution:=optional,
diff --git a/jetty-aggregate/jetty-all/pom.xml b/jetty-aggregate/jetty-all/pom.xml
index 6e77e04..d326cff 100644
--- a/jetty-aggregate/jetty-all/pom.xml
+++ b/jetty-aggregate/jetty-all/pom.xml
@@ -3,7 +3,7 @@
<parent>
<groupId>org.eclipse.jetty.aggregate</groupId>
<artifactId>jetty-aggregate-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-all</artifactId>
diff --git a/jetty-aggregate/jetty-client/pom.xml b/jetty-aggregate/jetty-client/pom.xml
index b6614b8..1f184bf 100644
--- a/jetty-aggregate/jetty-client/pom.xml
+++ b/jetty-aggregate/jetty-client/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.aggregate</groupId>
<artifactId>jetty-aggregate-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-client</artifactId>
diff --git a/jetty-aggregate/jetty-plus/pom.xml b/jetty-aggregate/jetty-plus/pom.xml
index 5f9e9dc..8958c9a 100644
--- a/jetty-aggregate/jetty-plus/pom.xml
+++ b/jetty-aggregate/jetty-plus/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.aggregate</groupId>
<artifactId>jetty-aggregate-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-plus</artifactId>
diff --git a/jetty-aggregate/jetty-server/pom.xml b/jetty-aggregate/jetty-server/pom.xml
index f4f230a..710ebb1 100644
--- a/jetty-aggregate/jetty-server/pom.xml
+++ b/jetty-aggregate/jetty-server/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.aggregate</groupId>
<artifactId>jetty-aggregate-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-server</artifactId>
diff --git a/jetty-aggregate/jetty-servlet/pom.xml b/jetty-aggregate/jetty-servlet/pom.xml
index a275eec..bdcab66 100644
--- a/jetty-aggregate/jetty-servlet/pom.xml
+++ b/jetty-aggregate/jetty-servlet/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.aggregate</groupId>
<artifactId>jetty-aggregate-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-servlet</artifactId>
diff --git a/jetty-aggregate/jetty-webapp/pom.xml b/jetty-aggregate/jetty-webapp/pom.xml
index ccefbe8..0765356 100644
--- a/jetty-aggregate/jetty-webapp/pom.xml
+++ b/jetty-aggregate/jetty-webapp/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.aggregate</groupId>
<artifactId>jetty-aggregate-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-webapp</artifactId>
diff --git a/jetty-aggregate/jetty-websocket/pom.xml b/jetty-aggregate/jetty-websocket/pom.xml
index b51b437..b56eae1 100644
--- a/jetty-aggregate/jetty-websocket/pom.xml
+++ b/jetty-aggregate/jetty-websocket/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.aggregate</groupId>
<artifactId>jetty-aggregate-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-websocket</artifactId>
diff --git a/jetty-aggregate/pom.xml b/jetty-aggregate/pom.xml
index 3f1e3e0..afed546 100644
--- a/jetty-aggregate/pom.xml
+++ b/jetty-aggregate/pom.xml
@@ -4,7 +4,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jetty.aggregate</groupId>
<artifactId>jetty-aggregate-project</artifactId>
diff --git a/jetty-ajp/pom.xml b/jetty-ajp/pom.xml
index 2b866e0..1347258 100644
--- a/jetty-ajp/pom.xml
+++ b/jetty-ajp/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-ajp</artifactId>
@@ -22,6 +22,11 @@
<goals>
<goal>manifest</goal>
</goals>
+ <configuration>
+ <instructions>
+ <Import-Package>javax.servlet.*;version="2.6.0",*</Import-Package>
+ </instructions>
+ </configuration>
</execution>
</executions>
</plugin>
diff --git a/jetty-annotations/pom.xml b/jetty-annotations/pom.xml
index dd7fb3b..9b17371 100644
--- a/jetty-annotations/pom.xml
+++ b/jetty-annotations/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-annotations</artifactId>
@@ -15,6 +15,23 @@
<build>
<plugins>
<plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptorRefs>
+ <descriptorRef>config</descriptorRef>
+ </descriptorRefs>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
@@ -26,7 +43,7 @@
</goals>
<configuration>
<instructions>
- <Import-Package>javax.servlet.*;version="[2.5,3.0)",*</Import-Package>
+ <Import-Package>javax.servlet.*;version="2.6.0",*</Import-Package>
</instructions>
</configuration>
</execution>
diff --git a/jetty-annotations/src/main/config/etc/jetty-annotations.xml b/jetty-annotations/src/main/config/etc/jetty-annotations.xml
new file mode 100644
index 0000000..7aa719d
--- /dev/null
+++ b/jetty-annotations/src/main/config/etc/jetty-annotations.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+
+ <!-- ================================================================= -->
+ <!-- Enable annotations - configure deployment steps for every web app -->
+ <!-- ================================================================= -->
+ <Call name="setAttribute">
+ <Arg>org.eclipse.jetty.webapp.configuration</Arg>
+ <Arg>
+ <Array type="java.lang.String">
+ <Item>org.eclipse.jetty.webapp.WebInfConfiguration</Item>
+ <Item>org.eclipse.jetty.webapp.WebXmlConfiguration</Item>
+ <Item>org.eclipse.jetty.webapp.MetaInfConfiguration</Item>
+ <Item>org.eclipse.jetty.webapp.FragmentConfiguration</Item>
+ <Item>org.eclipse.jetty.annotations.AnnotationConfiguration</Item>
+ <Item>org.eclipse.jetty.webapp.JettyWebXmlConfiguration</Item>
+ </Array>
+ </Arg>
+ </Call>
+
+</Configure>
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AbstractDiscoverableAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AbstractDiscoverableAnnotationHandler.java
index abd99bb..afdc266 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AbstractDiscoverableAnnotationHandler.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AbstractDiscoverableAnnotationHandler.java
@@ -22,7 +22,7 @@
import java.util.List;
import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler;
-import org.eclipse.jetty.annotations.AnnotationParser.Value;
+import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.DiscoveredAnnotation;
import org.eclipse.jetty.webapp.WebAppContext;
@@ -34,13 +34,32 @@
public abstract class AbstractDiscoverableAnnotationHandler implements DiscoverableAnnotationHandler
{
protected WebAppContext _context;
- protected List<DiscoveredAnnotation> _annotations = new ArrayList<DiscoveredAnnotation>();
+ protected List<DiscoveredAnnotation> _annotations;
+ protected Resource _resource;
public AbstractDiscoverableAnnotationHandler(WebAppContext context)
{
+ this(context, null);
+ }
+
+ public AbstractDiscoverableAnnotationHandler(WebAppContext context, List<DiscoveredAnnotation> list)
+ {
_context = context;
+ if (list == null)
+ _annotations = new ArrayList<DiscoveredAnnotation>();
+ else
+ _annotations = list;
}
+ public Resource getResource()
+ {
+ return _resource;
+ }
+
+ public void setResource(Resource resource)
+ {
+ _resource = resource;
+ }
public List<DiscoveredAnnotation> getAnnotationList ()
{
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java
index 2b2dfae..d24cb02 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java
@@ -18,8 +18,26 @@
package org.eclipse.jetty.annotations;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ServiceLoader;
+
+import javax.servlet.ServletContainerInitializer;
+import javax.servlet.annotation.HandlesTypes;
+
+import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler;
+import org.eclipse.jetty.plus.annotation.ContainerInitializer;
+import org.eclipse.jetty.util.MultiMap;
+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.webapp.AbstractConfiguration;
+import org.eclipse.jetty.webapp.FragmentDescriptor;
+import org.eclipse.jetty.webapp.MetaDataComplete;
import org.eclipse.jetty.webapp.WebAppContext;
+import org.eclipse.jetty.webapp.WebDescriptor;
/**
* Configuration for Annotations
@@ -28,15 +46,478 @@
*/
public class AnnotationConfiguration extends AbstractConfiguration
{
+ private static final Logger LOG = Log.getLogger(AnnotationConfiguration.class);
+ public static final String CLASS_INHERITANCE_MAP = "org.eclipse.jetty.classInheritanceMap";
+ public static final String CONTAINER_INITIALIZERS = "org.eclipse.jetty.containerInitializers";
+ public static final String CONTAINER_INITIALIZER_LISTENER = "org.eclipse.jetty.containerInitializerListener";
+
+
+ protected List<DiscoverableAnnotationHandler> _discoverableAnnotationHandlers = new ArrayList<DiscoverableAnnotationHandler>();
+ protected ClassInheritanceHandler _classInheritanceHandler;
+ protected List<ContainerInitializerAnnotationHandler> _containerInitializerAnnotationHandlers = new ArrayList<ContainerInitializerAnnotationHandler>();
+
+
+ @Override
+ public void preConfigure(final WebAppContext context) throws Exception
+ {
+ }
+
+ @Override
+ public void deconfigure(WebAppContext context) throws Exception
+ {
+ context.removeAttribute(CLASS_INHERITANCE_MAP);
+ context.removeAttribute(CONTAINER_INITIALIZERS);
+ ServletContainerInitializerListener listener = (ServletContainerInitializerListener)context.getAttribute(CONTAINER_INITIALIZER_LISTENER);
+ if (listener != null)
+ {
+ context.removeBean(listener);
+ context.removeAttribute(CONTAINER_INITIALIZER_LISTENER);
+ }
+ }
+
+ /**
+ * @see org.eclipse.jetty.webapp.AbstractConfiguration#configure(org.eclipse.jetty.webapp.WebAppContext)
+ */
@Override
public void configure(WebAppContext context) throws Exception
- {
+ {
+ boolean metadataComplete = context.getMetaData().isMetaDataComplete();
context.addDecorator(new AnnotationDecorator(context));
+
+
+ //Even if metadata is complete, we still need to scan for ServletContainerInitializers - if there are any
+ AnnotationParser parser = null;
+ if (!metadataComplete)
+ {
+ //If metadata isn't complete, if this is a servlet 3 webapp or isConfigDiscovered is true, we need to search for annotations
+ if (context.getServletContext().getEffectiveMajorVersion() >= 3 || context.isConfigurationDiscovered())
+ {
+ _discoverableAnnotationHandlers.add(new WebServletAnnotationHandler(context));
+ _discoverableAnnotationHandlers.add(new WebFilterAnnotationHandler(context));
+ _discoverableAnnotationHandlers.add(new WebListenerAnnotationHandler(context));
+ }
+ }
+ else
+ if (LOG.isDebugEnabled()) LOG.debug("Metadata-complete==true, not processing discoverable servlet annotations for context "+context);
+
+
+
+ //Regardless of metadata, if there are any ServletContainerInitializers with @HandlesTypes, then we need to scan all the
+ //classes so we can call their onStartup() methods correctly
+ createServletContainerInitializerAnnotationHandlers(context, getNonExcludedInitializers(context));
+
+ if (!_discoverableAnnotationHandlers.isEmpty() || _classInheritanceHandler != null || !_containerInitializerAnnotationHandlers.isEmpty())
+ {
+ parser = createAnnotationParser();
+ if (LOG.isDebugEnabled()) LOG.debug("Scanning all classses for annotations: webxmlVersion="+context.getServletContext().getEffectiveMajorVersion()+" configurationDiscovered="+context.isConfigurationDiscovered());
+ parseContainerPath(context, parser);
+ //email from Rajiv Mordani jsrs 315 7 April 2010
+ // If there is a <others/> then the ordering should be
+ // WEB-INF/classes the order of the declared elements + others.
+ // In case there is no others then it is
+ // WEB-INF/classes + order of the elements.
+ parseWebInfClasses(context, parser);
+ parseWebInfLib (context, parser);
+
+ for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers)
+ context.getMetaData().addDiscoveredAnnotations(((AbstractDiscoverableAnnotationHandler)h).getAnnotationList());
+ }
+ }
+
+
+
+ /**
+ * @see org.eclipse.jetty.webapp.AbstractConfiguration#postConfigure(org.eclipse.jetty.webapp.WebAppContext)
+ */
+ @Override
+ public void postConfigure(WebAppContext context) throws Exception
+ {
+ MultiMap map = (MultiMap)context.getAttribute(CLASS_INHERITANCE_MAP);
+ if (map != null)
+ map.clear();
+
+ context.removeAttribute(CLASS_INHERITANCE_MAP);
+
+ List<ContainerInitializer> initializers = (List<ContainerInitializer>)context.getAttribute(CONTAINER_INITIALIZERS);
+ if (initializers != null)
+ initializers.clear();
+ if (_discoverableAnnotationHandlers != null)
+ _discoverableAnnotationHandlers.clear();
+
+ _classInheritanceHandler = null;
+ if (_containerInitializerAnnotationHandlers != null)
+ _containerInitializerAnnotationHandlers.clear();
+
+ super.postConfigure(context);
}
+ /**
+ * @return a new AnnotationParser. This method can be overridden to use a different impleemntation of
+ * the AnnotationParser. Note that this is considered internal API.
+ */
+ protected AnnotationParser createAnnotationParser()
+ {
+ return new AnnotationParser();
+ }
+
+ /**
+ * @see org.eclipse.jetty.webapp.AbstractConfiguration#cloneConfigure(org.eclipse.jetty.webapp.WebAppContext, org.eclipse.jetty.webapp.WebAppContext)
+ */
@Override
public void cloneConfigure(WebAppContext template, WebAppContext context) throws Exception
{
context.addDecorator(new AnnotationDecorator(context));
}
+
+
+
+ /**
+ * @param context
+ * @param scis
+ * @throws Exception
+ */
+ public void createServletContainerInitializerAnnotationHandlers (WebAppContext context, List<ServletContainerInitializer> scis)
+ throws Exception
+ {
+
+ if (scis == null || scis.isEmpty())
+ return; // nothing to do
+
+
+ List<ContainerInitializer> initializers = new ArrayList<ContainerInitializer>();
+ context.setAttribute(CONTAINER_INITIALIZERS, initializers);
+
+ for (ServletContainerInitializer service : scis)
+ {
+ HandlesTypes annotation = service.getClass().getAnnotation(HandlesTypes.class);
+ ContainerInitializer initializer = new ContainerInitializer();
+ initializer.setTarget(service);
+ initializers.add(initializer);
+ if (annotation != null)
+ {
+ //There is a HandlesTypes annotation on the on the ServletContainerInitializer
+ Class[] classes = annotation.value();
+ if (classes != null)
+ {
+ initializer.setInterestedTypes(classes);
+
+ //If we haven't already done so, we need to register a handler that will
+ //process the whole class hierarchy to satisfy the ServletContainerInitializer
+ if (context.getAttribute(CLASS_INHERITANCE_MAP) == null)
+ {
+ MultiMap map = new MultiMap();
+ context.setAttribute(CLASS_INHERITANCE_MAP, map);
+ _classInheritanceHandler = new ClassInheritanceHandler(map);
+ }
+
+ for (Class c: classes)
+ {
+ //The value of one of the HandlesTypes classes is actually an Annotation itself so
+ //register a handler for it
+ if (c.isAnnotation())
+ {
+ if (LOG.isDebugEnabled()) LOG.debug("Registering annotation handler for "+c.getName());
+
+ _containerInitializerAnnotationHandlers.add(new ContainerInitializerAnnotationHandler(initializer, c));
+ }
+ }
+ }
+ else
+ if (LOG.isDebugEnabled()) LOG.debug("No classes in HandlesTypes on initializer "+service.getClass());
+ }
+ else
+ if (LOG.isDebugEnabled()) LOG.debug("No annotation on initializer "+service.getClass());
+ }
+
+
+ //add a bean which will call the servletcontainerinitializers when appropriate
+ ServletContainerInitializerListener listener = (ServletContainerInitializerListener)context.getAttribute(CONTAINER_INITIALIZER_LISTENER);
+ if (listener != null)
+ throw new IllegalStateException("ServletContainerInitializerListener already exists");
+ listener = new ServletContainerInitializerListener();
+ listener.setWebAppContext(context);
+ context.setAttribute(CONTAINER_INITIALIZER_LISTENER, listener);
+ context.addBean(listener, true);
+ }
+
+
+
+ /**
+ * Check to see if the ServletContainerIntializer loaded via the ServiceLoader came
+ * from a jar that is excluded by the fragment ordering. See ServletSpec 3.0 p.85.
+ * @param orderedJars
+ * @param service
+ * @return
+ */
+ public boolean isFromExcludedJar (WebAppContext context, ServletContainerInitializer service)
+ throws Exception
+ {
+ List<Resource> orderedJars = context.getMetaData().getOrderedWebInfJars();
+
+ //If no ordering, nothing is excluded
+ if (context.getMetaData().getOrdering() == null)
+ return false;
+
+ //there is an ordering, but there are no jars resulting from the ordering, everything excluded
+ if (orderedJars.isEmpty())
+ return true;
+
+ String loadingJarName = Thread.currentThread().getContextClassLoader().getResource(service.getClass().getName().replace('.','/')+".class").toString();
+
+ int i = loadingJarName.indexOf(".jar");
+ if (i < 0)
+ return false; //not from a jar therefore not from WEB-INF so not excludable
+
+ loadingJarName = loadingJarName.substring(0,i+4);
+ loadingJarName = (loadingJarName.startsWith("jar:")?loadingJarName.substring(4):loadingJarName);
+ URI loadingJarURI = Resource.newResource(loadingJarName).getURI();
+ boolean found = false;
+ Iterator<Resource> itor = orderedJars.iterator();
+ while (!found && itor.hasNext())
+ {
+ Resource r = itor.next();
+ found = r.getURI().equals(loadingJarURI);
+ }
+
+ return !found;
+ }
+
+
+
+ /**
+ * @param context
+ * @return
+ * @throws Exception
+ */
+ public List<ServletContainerInitializer> getNonExcludedInitializers (WebAppContext context)
+ throws Exception
+ {
+ List<ServletContainerInitializer> nonExcludedInitializers = new ArrayList<ServletContainerInitializer>();
+
+ //We use the ServiceLoader mechanism to find the ServletContainerInitializer classes to inspect
+ ServiceLoader<ServletContainerInitializer> loadedInitializers = ServiceLoader.load(ServletContainerInitializer.class, context.getClassLoader());
+
+ if (loadedInitializers != null)
+ {
+ for (ServletContainerInitializer service : loadedInitializers)
+ {
+ if (!isFromExcludedJar(context, service))
+ nonExcludedInitializers.add(service);
+ }
+ }
+ return nonExcludedInitializers;
+ }
+
+
+
+
+ /**
+ * Scan jars on container path.
+ *
+ * @param context
+ * @param parser
+ * @throws Exception
+ */
+ public void parseContainerPath (final WebAppContext context, final AnnotationParser parser)
+ throws Exception
+ {
+ //if no pattern for the container path is defined, then by default scan NOTHING
+ LOG.debug("Scanning container jars");
+
+ //always parse for discoverable annotations as well as class hierarchy and servletcontainerinitializer related annotations
+ parser.clearHandlers();
+ for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers)
+ {
+ if (h instanceof AbstractDiscoverableAnnotationHandler)
+ ((AbstractDiscoverableAnnotationHandler)h).setResource(null); //
+ }
+ parser.registerHandlers(_discoverableAnnotationHandlers);
+ parser.registerHandler(_classInheritanceHandler);
+ parser.registerHandlers(_containerInitializerAnnotationHandlers);
+
+ //Convert from Resource to URI
+ ArrayList<URI> containerUris = new ArrayList<URI>();
+ for (Resource r : context.getMetaData().getOrderedContainerJars())
+ {
+ URI uri = r.getURI();
+ containerUris.add(uri);
+ }
+
+ parser.parse (containerUris.toArray(new URI[containerUris.size()]),
+ new ClassNameResolver ()
+ {
+ public boolean isExcluded (String name)
+ {
+ if (context.isSystemClass(name)) return false;
+ if (context.isServerClass(name)) return true;
+ return false;
+ }
+
+ public boolean shouldOverride (String name)
+ {
+ //looking at system classpath
+ if (context.isParentLoaderPriority())
+ return true;
+ return false;
+ }
+ });
+
+
+ }
+
+
+ /**
+ * Scan jars in WEB-INF/lib
+ *
+ * @param context
+ * @param parser
+ * @throws Exception
+ */
+ public void parseWebInfLib (final WebAppContext context, final AnnotationParser parser)
+ throws Exception
+ {
+ List<FragmentDescriptor> frags = context.getMetaData().getFragments();
+
+ //email from Rajiv Mordani jsrs 315 7 April 2010
+ //jars that do not have a web-fragment.xml are still considered fragments
+ //they have to participate in the ordering
+ ArrayList<URI> webInfUris = new ArrayList<URI>();
+
+ List<Resource> jars = context.getMetaData().getOrderedWebInfJars();
+
+ //No ordering just use the jars in any order
+ if (jars == null || jars.isEmpty())
+ jars = context.getMetaData().getWebInfJars();
+
+ for (Resource r : jars)
+ {
+ //for each jar, we decide which set of annotations we need to parse for
+ parser.clearHandlers();
+ URI uri = r.getURI();
+ FragmentDescriptor f = getFragmentFromJar(r, frags);
+
+ //if its from a fragment jar that is metadata complete, we should skip scanning for @webservlet etc
+ // but yet we still need to do the scanning for the classes on behalf of the servletcontainerinitializers
+ //if a jar has no web-fragment.xml we scan it (because it is not excluded by the ordering)
+ //or if it has a fragment we scan it if it is not metadata complete
+ if (f == null || !isMetaDataComplete(f) || _classInheritanceHandler != null || !_containerInitializerAnnotationHandlers.isEmpty())
+ {
+ //register the classinheritance handler if there is one
+ parser.registerHandler(_classInheritanceHandler);
+
+ //register the handlers for the @HandlesTypes values that are themselves annotations if there are any
+ parser.registerHandlers(_containerInitializerAnnotationHandlers);
+
+ //only register the discoverable annotation handlers if this fragment is not metadata complete, or has no fragment descriptor
+ if (f == null || !isMetaDataComplete(f))
+ {
+ for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers)
+ {
+ if (h instanceof AbstractDiscoverableAnnotationHandler)
+ ((AbstractDiscoverableAnnotationHandler)h).setResource(r);
+ }
+ parser.registerHandlers(_discoverableAnnotationHandlers);
+ }
+
+ parser.parse(uri,
+ new ClassNameResolver()
+ {
+ public boolean isExcluded (String name)
+ {
+ if (context.isSystemClass(name)) return true;
+ if (context.isServerClass(name)) return false;
+ return false;
+ }
+
+ public boolean shouldOverride (String name)
+ {
+ //looking at webapp classpath, found already-parsed class of same name - did it come from system or duplicate in webapp?
+ if (context.isParentLoaderPriority())
+ return false;
+ return true;
+ }
+ });
+ }
+ }
+ }
+
+ /**
+ * Scan classes in WEB-INF/classes
+ *
+ * @param context
+ * @param parser
+ * @throws Exception
+ */
+ public void parseWebInfClasses (final WebAppContext context, final AnnotationParser parser)
+ throws Exception
+ {
+ LOG.debug("Scanning classes in WEB-INF/classes");
+ if (context.getWebInf() != null)
+ {
+ Resource classesDir = context.getWebInf().addPath("classes/");
+ if (classesDir.exists())
+ {
+ parser.clearHandlers();
+ for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers)
+ {
+ if (h instanceof AbstractDiscoverableAnnotationHandler)
+ ((AbstractDiscoverableAnnotationHandler)h).setResource(null); //
+ }
+ parser.registerHandlers(_discoverableAnnotationHandlers);
+ parser.registerHandler(_classInheritanceHandler);
+ parser.registerHandlers(_containerInitializerAnnotationHandlers);
+
+ parser.parse(classesDir,
+ new ClassNameResolver()
+ {
+ public boolean isExcluded (String name)
+ {
+ if (context.isSystemClass(name)) return true;
+ if (context.isServerClass(name)) return false;
+ return false;
+ }
+
+ public boolean shouldOverride (String name)
+ {
+ //looking at webapp classpath, found already-parsed class of same name - did it come from system or duplicate in webapp?
+ if (context.isParentLoaderPriority())
+ return false;
+ return true;
+ }
+ });
+ }
+ }
+ }
+
+
+
+ /**
+ * Get the web-fragment.xml from a jar
+ *
+ * @param jar
+ * @param frags
+ * @return
+ * @throws Exception
+ */
+ public FragmentDescriptor getFragmentFromJar (Resource jar, List<FragmentDescriptor> frags)
+ throws Exception
+ {
+ //check if the jar has a web-fragment.xml
+ FragmentDescriptor d = null;
+ for (FragmentDescriptor frag: frags)
+ {
+ Resource fragResource = frag.getResource(); //eg jar:file:///a/b/c/foo.jar!/META-INF/web-fragment.xml
+ if (Resource.isContainedIn(fragResource,jar))
+ {
+ d = frag;
+ break;
+ }
+ }
+ return d;
+ }
+
+ public boolean isMetaDataComplete (WebDescriptor d)
+ {
+ return (d!=null && d.getMetaDataComplete() == MetaDataComplete.True);
+ }
}
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationDecorator.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationDecorator.java
index 766af72..7802224 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationDecorator.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationDecorator.java
@@ -50,6 +50,8 @@
_introspector.registerHandler(new PostConstructAnnotationHandler(context));
_introspector.registerHandler(new PreDestroyAnnotationHandler(context));
_introspector.registerHandler(new DeclareRolesAnnotationHandler(context));
+ _introspector.registerHandler(new MultiPartConfigAnnotationHandler(context));
+ _introspector.registerHandler(new ServletSecurityAnnotationHandler(context));
}
/* ------------------------------------------------------------ */
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java
index 0a2e628..3e252c2 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java
@@ -25,12 +25,9 @@
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
-import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
@@ -56,10 +53,7 @@
private static final Logger LOG = Log.getLogger(AnnotationParser.class);
protected Set<String> _parsedClassNames = new HashSet<String>();
- protected Map<String, List<DiscoverableAnnotationHandler>> _annotationHandlers = new HashMap<String, List<DiscoverableAnnotationHandler>>();
- protected List<ClassHandler> _classHandlers = new ArrayList<ClassHandler>();
- protected List<MethodHandler> _methodHandlers = new ArrayList<MethodHandler>();
- protected List<FieldHandler> _fieldHandlers = new ArrayList<FieldHandler>();
+ protected List<Handler> _handlers = new ArrayList<Handler>();
public static String normalize (String name)
{
@@ -170,37 +164,122 @@
- public interface DiscoverableAnnotationHandler
+ /**
+ * Handler
+ *
+ * Signature for all handlers that respond to parsing class files.
+ */
+ public interface Handler
{
+
+ }
+
+
+
+ /**
+ * DiscoverableAnnotationHandler
+ *
+ * Processes an annotation when it is discovered on a class.
+ */
+ public interface DiscoverableAnnotationHandler extends Handler
+ {
+ /**
+ * Process an annotation that was discovered on a class
+ * @param className
+ * @param version
+ * @param access
+ * @param signature
+ * @param superName
+ * @param interfaces
+ * @param annotation
+ * @param values
+ */
public void handleClass (String className, int version, int access,
String signature, String superName, String[] interfaces,
String annotation, List<Value>values);
+ /**
+ * Process an annotation that was discovered on a method
+ * @param className
+ * @param methodName
+ * @param access
+ * @param desc
+ * @param signature
+ * @param exceptions
+ * @param annotation
+ * @param values
+ */
public void handleMethod (String className, String methodName, int access,
String desc, String signature,String[] exceptions,
String annotation, List<Value>values);
+
+ /**
+ * Process an annotation that was discovered on a field
+ * @param className
+ * @param fieldName
+ * @param access
+ * @param fieldType
+ * @param signature
+ * @param value
+ * @param annotation
+ * @param values
+ */
public void handleField (String className, String fieldName, int access,
String fieldType, String signature, Object value,
String annotation, List<Value>values);
+
+
+ /**
+ * Get the name of the annotation processed by this handler. Can be null
+ *
+ * @return
+ */
+ public String getAnnotationName();
}
- public interface ClassHandler
+
+ /**
+ * ClassHandler
+ *
+ * Responds to finding a Class
+ */
+ public interface ClassHandler extends Handler
{
public void handle (String className, int version, int access, String signature, String superName, String[] interfaces);
}
- public interface MethodHandler
+
+
+ /**
+ * MethodHandler
+ *
+ * Responds to finding a Method
+ */
+ public interface MethodHandler extends Handler
{
public void handle (String className, String methodName, int access, String desc, String signature,String[] exceptions);
}
- public interface FieldHandler
+
+ /**
+ * FieldHandler
+ *
+ * Responds to finding a Field
+ */
+ public interface FieldHandler extends Handler
{
public void handle (String className, String fieldName, int access, String fieldType, String signature, Object value);
}
+
+
+ /**
+ * MyAnnotationVisitor
+ *
+ * ASM Visitor for Annotations
+ */
public class MyAnnotationVisitor implements AnnotationVisitor
{
List<Value> _annotationValues;
@@ -309,10 +388,13 @@
for (String s : interfaces)
normalizedInterfaces[i++] = normalize(s);
}
-
- for (ClassHandler h : AnnotationParser.this._classHandlers)
+
+ for (Handler h : AnnotationParser.this._handlers)
{
- h.handle(_className, _version, _access, _signature, normalize(_superName), normalizedInterfaces);
+ if (h instanceof ClassHandler)
+ {
+ ((ClassHandler)h).handle(_className, _version, _access, _signature, normalize(_superName), normalizedInterfaces);
+ }
}
}
@@ -325,12 +407,13 @@
super.visitEnd();
//call all AnnotationHandlers with classname, annotation name + values
- List<DiscoverableAnnotationHandler> handlers = AnnotationParser.this._annotationHandlers.get(_annotationName);
- if (handlers != null)
+ for (Handler h : AnnotationParser.this._handlers)
{
- for (DiscoverableAnnotationHandler h:handlers)
+ if (h instanceof DiscoverableAnnotationHandler)
{
- h.handleClass(_className, _version, _access, _signature, _superName, _interfaces, _annotationName, _annotationValues);
+ DiscoverableAnnotationHandler dah = (DiscoverableAnnotationHandler)h;
+ if (_annotationName.equalsIgnoreCase(dah.getAnnotationName()))
+ dah.handleClass(_className, _version, _access, _signature, _superName, _interfaces, _annotationName, _annotationValues);
}
}
}
@@ -356,12 +439,13 @@
{
super.visitEnd();
//call all AnnotationHandlers with classname, method, annotation name + values
- List<DiscoverableAnnotationHandler> handlers = AnnotationParser.this._annotationHandlers.get(_annotationName);
- if (handlers != null)
+ for (Handler h : AnnotationParser.this._handlers)
{
- for (DiscoverableAnnotationHandler h:handlers)
+ if (h instanceof DiscoverableAnnotationHandler)
{
- h.handleMethod(_className, name, access, methodDesc, signature, exceptions, _annotationName, _annotationValues);
+ DiscoverableAnnotationHandler dah = (DiscoverableAnnotationHandler)h;
+ if (_annotationName.equalsIgnoreCase(dah.getAnnotationName()))
+ dah.handleMethod(_className, name, access, methodDesc, signature, exceptions, _annotationName, _annotationValues);
}
}
}
@@ -388,12 +472,13 @@
public void visitEnd()
{
super.visitEnd();
- List<DiscoverableAnnotationHandler> handlers = AnnotationParser.this._annotationHandlers.get(_annotationName);
- if (handlers != null)
+ for (Handler h : AnnotationParser.this._handlers)
{
- for (DiscoverableAnnotationHandler h:handlers)
+ if (h instanceof DiscoverableAnnotationHandler)
{
- h.handleField(_className, fieldName, access, fieldType, signature, value, _annotationName, _annotationValues);
+ DiscoverableAnnotationHandler dah = (DiscoverableAnnotationHandler)h;
+ if (_annotationName.equalsIgnoreCase(dah.getAnnotationName()))
+ dah.handleField(_className, fieldName, access, fieldType, signature, value, _annotationName, _annotationValues);
}
}
}
@@ -409,46 +494,130 @@
* Register a handler that will be called back when the named annotation is
* encountered on a class.
*
+ * @deprecated see registerHandler(Handler)
* @param annotationName
* @param handler
*/
public void registerAnnotationHandler (String annotationName, DiscoverableAnnotationHandler handler)
{
- List<DiscoverableAnnotationHandler> handlers = _annotationHandlers.get(annotationName);
- if (handlers == null)
- {
- handlers = new ArrayList<DiscoverableAnnotationHandler>();
- _annotationHandlers.put(annotationName, handlers);
- }
- handlers.add(handler);
+ _handlers.add(handler);
}
+
+ /**
+ * @deprecated
+ * @param annotationName
+ * @return
+ */
public List<DiscoverableAnnotationHandler> getAnnotationHandlers(String annotationName)
{
- List<DiscoverableAnnotationHandler> handlers = _annotationHandlers.get(annotationName);
- if (handlers == null)
- return Collections.emptyList();
- return new ArrayList<DiscoverableAnnotationHandler>(handlers);
+ List<DiscoverableAnnotationHandler> handlers = new ArrayList<DiscoverableAnnotationHandler>();
+ for (Handler h:_handlers)
+ {
+ if (h instanceof DiscoverableAnnotationHandler)
+ {
+ DiscoverableAnnotationHandler dah = (DiscoverableAnnotationHandler)h;
+ if (annotationName.equals(dah.getAnnotationName()))
+ handlers.add(dah);
+ }
+ }
+
+ return handlers;
}
+ /**
+ * @deprecated
+ * @return
+ */
public List<DiscoverableAnnotationHandler> getAnnotationHandlers()
{
- List<DiscoverableAnnotationHandler> allHandlers = new ArrayList<DiscoverableAnnotationHandler>();
- for (List<DiscoverableAnnotationHandler> list:_annotationHandlers.values())
- allHandlers.addAll(list);
- return allHandlers;
+ List<DiscoverableAnnotationHandler> allAnnotationHandlers = new ArrayList<DiscoverableAnnotationHandler>();
+ for (Handler h:_handlers)
+ {
+ if (h instanceof DiscoverableAnnotationHandler)
+ allAnnotationHandlers.add((DiscoverableAnnotationHandler)h);
+ }
+ return allAnnotationHandlers;
}
+ /**
+ * @deprecated see registerHandler(Handler)
+ * @param handler
+ */
public void registerClassHandler (ClassHandler handler)
{
- _classHandlers.add(handler);
+ _handlers.add(handler);
}
+
+
+
+ /**
+ * Add a particular handler
+ *
+ * @param h
+ */
+ public void registerHandler(Handler h)
+ {
+ if (h == null)
+ return;
+
+ _handlers.add(h);
+ }
+
+
+ /**
+ * Add a list of handlers
+ *
+ * @param handlers
+ */
+ public void registerHandlers(List<? extends Handler> handlers)
+ {
+ if (handlers == null)
+ return;
+ _handlers.addAll(handlers);
+ }
+
+
+ /**
+ * Remove a particular handler
+ *
+ * @param h
+ * @return
+ */
+ public boolean deregisterHandler(Handler h)
+ {
+ return _handlers.remove(h);
+ }
+
+
+ /**
+ * Remove all registered handlers
+ */
+ public void clearHandlers()
+ {
+ _handlers.clear();
+ }
+
+ /**
+ * True if the class has already been processed, false otherwise
+ * @param className
+ * @return
+ */
public boolean isParsed (String className)
{
return _parsedClassNames.contains(className);
}
+
+
+ /**
+ * Parse a given class
+ *
+ * @param className
+ * @param resolver
+ * @throws Exception
+ */
public void parse (String className, ClassNameResolver resolver)
throws Exception
{
@@ -470,6 +639,16 @@
}
}
+
+
+ /**
+ * Parse the given class, optionally walking its inheritance hierarchy
+ *
+ * @param clazz
+ * @param resolver
+ * @param visitSuperClasses
+ * @throws Exception
+ */
public void parse (Class clazz, ClassNameResolver resolver, boolean visitSuperClasses)
throws Exception
{
@@ -496,6 +675,15 @@
}
}
+
+
+ /**
+ * Parse the given classes
+ *
+ * @param classNames
+ * @param resolver
+ * @throws Exception
+ */
public void parse (String[] classNames, ClassNameResolver resolver)
throws Exception
{
@@ -505,6 +693,14 @@
parse(Arrays.asList(classNames), resolver);
}
+
+ /**
+ * Parse the given classes
+ *
+ * @param classNames
+ * @param resolver
+ * @throws Exception
+ */
public void parse (List<String> classNames, ClassNameResolver resolver)
throws Exception
{
@@ -523,6 +719,14 @@
}
}
+
+ /**
+ * Parse all classes in a directory
+ *
+ * @param dir
+ * @param resolver
+ * @throws Exception
+ */
public void parse (Resource dir, ClassNameResolver resolver)
throws Exception
{
@@ -563,8 +767,9 @@
/**
- * Find annotations on classes in the supplied classloader.
+ * Parse classes in the supplied classloader.
* Only class files in jar files will be scanned.
+ *
* @param loader
* @param visitParents
* @param nullInclusive
@@ -617,7 +822,8 @@
/**
- * Find annotations in classes in the supplied url of jar files.
+ * Parse classes in the supplied url of jar files.
+ *
* @param uris
* @param resolver
* @throws Exception
@@ -663,10 +869,8 @@
scanner.scan(null, uris, true);
}
-
/**
- * Parse a single jar file for classes.
- *
+ * Parse a particular resource
* @param uri
* @param resolver
* @throws Exception
@@ -680,14 +884,15 @@
parse(uris, resolver);
}
-
+
+
/**
- * Scan a class for annotations.
+ * Use ASM on a class
*
* @param is
* @throws IOException
*/
- private void scanClass (InputStream is)
+ protected void scanClass (InputStream is)
throws IOException
{
ClassReader reader = new ClassReader(is);
@@ -756,3 +961,4 @@
return true;
}
}
+
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ClassInheritanceHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ClassInheritanceHandler.java
index c1e8de5..b8ea523 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ClassInheritanceHandler.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ClassInheritanceHandler.java
@@ -35,10 +35,16 @@
private static final Logger LOG = Log.getLogger(ClassInheritanceHandler.class);
- MultiMap _inheritanceMap = new MultiMap();
+ MultiMap _inheritanceMap;
public ClassInheritanceHandler()
{
+ _inheritanceMap = new MultiMap();
+ }
+
+ public ClassInheritanceHandler(MultiMap map)
+ {
+ _inheritanceMap = map;
}
public void handle(String className, int version, int access, String signature, String superName, String[] interfaces)
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ContainerInitializerAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ContainerInitializerAnnotationHandler.java
new file mode 100644
index 0000000..6c60c0e
--- /dev/null
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ContainerInitializerAnnotationHandler.java
@@ -0,0 +1,83 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.annotations;
+
+import java.util.List;
+
+import javax.servlet.annotation.HandlesTypes;
+
+import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler;
+import org.eclipse.jetty.annotations.AnnotationParser.Value;
+import org.eclipse.jetty.plus.annotation.ContainerInitializer;
+import org.eclipse.jetty.util.Loader;
+import org.eclipse.jetty.util.log.Log;
+
+/**
+ * ContainerInitializerAnnotationHandler
+ *
+ * Discovers classes that contain the specified annotation, either at class or
+ * method level. The specified annotation is derived from an @HandlesTypes on
+ * a ServletContainerInitializer class.
+ */
+public class ContainerInitializerAnnotationHandler implements DiscoverableAnnotationHandler
+{
+ ContainerInitializer _initializer;
+ Class _annotation;
+
+ public ContainerInitializerAnnotationHandler (ContainerInitializer initializer, Class annotation)
+ {
+ _initializer = initializer;
+ _annotation = annotation;
+ }
+
+ /**
+ * Handle finding a class that is annotated with the annotation we were constructed with.
+ * @see org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler#handleClass(java.lang.String, int, int, java.lang.String, java.lang.String, java.lang.String[], java.lang.String, java.util.List)
+ */
+ public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotationName,
+ List<Value> values)
+ {
+ _initializer.addAnnotatedTypeName(className);
+ }
+
+ public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation,
+ List<Value> values)
+ {
+ _initializer.addAnnotatedTypeName(className);
+ }
+
+ public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation,
+ List<Value> values)
+ {
+ _initializer.addAnnotatedTypeName(className);
+ }
+
+ @Override
+ public String getAnnotationName()
+ {
+ return _annotation.getName();
+ }
+
+ public ContainerInitializer getContainerInitializer()
+ {
+ return _initializer;
+ }
+
+}
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/MultiPartConfigAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/MultiPartConfigAnnotationHandler.java
new file mode 100644
index 0000000..f2b20eb
--- /dev/null
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/MultiPartConfigAnnotationHandler.java
@@ -0,0 +1,95 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.annotations;
+
+import javax.servlet.MultipartConfigElement;
+import javax.servlet.Servlet;
+import javax.servlet.annotation.MultipartConfig;
+
+import org.eclipse.jetty.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.webapp.Descriptor;
+import org.eclipse.jetty.webapp.MetaData;
+import org.eclipse.jetty.webapp.WebAppContext;
+
+/**
+ * MultiPartConfigAnnotationHandler
+ *
+ *
+ */
+public class MultiPartConfigAnnotationHandler extends AbstractIntrospectableAnnotationHandler
+{
+ protected WebAppContext _context;
+
+ public MultiPartConfigAnnotationHandler(WebAppContext context)
+ {
+ //TODO verify that MultipartConfig is not inheritable
+ super(false);
+ _context = context;
+ }
+ /**
+ * @see org.eclipse.jetty.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler#doHandle(java.lang.Class)
+ */
+ public void doHandle(Class clazz)
+ {
+ if (!Servlet.class.isAssignableFrom(clazz))
+ return;
+
+ MultipartConfig multi = (MultipartConfig) clazz.getAnnotation(MultipartConfig.class);
+ if (multi == null)
+ return;
+
+ MetaData metaData = _context.getMetaData();
+
+ //TODO: The MultipartConfigElement needs to be set on the ServletHolder's Registration.
+ //How to identify the correct Servlet? If the Servlet has no WebServlet annotation on it, does it mean that this MultipartConfig
+ //annotation applies to all declared instances in web.xml/programmatically?
+ //Assuming TRUE for now.
+
+ ServletHolder holder = getServletHolderForClass(clazz);
+ if (holder != null)
+ {
+ Descriptor d = metaData.getOriginDescriptor(holder.getName()+".servlet.multipart-config");
+ //if a descriptor has already set the value for multipart config, do not
+ //let the annotation override it
+ if (d == null)
+ {
+ metaData.setOrigin(holder.getName()+".servlet.multipart-config");
+ holder.getRegistration().setMultipartConfig(new MultipartConfigElement(multi));
+ }
+ }
+ }
+
+ private ServletHolder getServletHolderForClass (Class clazz)
+ {
+ ServletHolder holder = null;
+ ServletHolder[] holders = _context.getServletHandler().getServlets();
+ if (holders != null)
+ {
+ for (ServletHolder h : holders)
+ {
+ if (h.getClassName() != null && h.getClassName().equals(clazz.getName()))
+ {
+ holder = h;
+ }
+ }
+ }
+ return holder;
+ }
+}
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/RunAsAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/RunAsAnnotationHandler.java
index 10f8dcb..ad22ff4 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/RunAsAnnotationHandler.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/RunAsAnnotationHandler.java
@@ -107,7 +107,7 @@
{
for (ServletHolder h : holders)
{
- if (h.getClassName().equals(clazz.getName()))
+ if (h.getClassName() != null && h.getClassName().equals(clazz.getName()))
{
holder = h;
}
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletContainerInitializerListener.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletContainerInitializerListener.java
new file mode 100644
index 0000000..082fead
--- /dev/null
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletContainerInitializerListener.java
@@ -0,0 +1,144 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.annotations;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jetty.annotations.AnnotationConfiguration;
+import org.eclipse.jetty.plus.annotation.ContainerInitializer;
+import org.eclipse.jetty.util.MultiMap;
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.webapp.WebAppContext;
+
+/**
+ * ServletContainerInitializerListener
+ *
+ *
+ */
+public class ServletContainerInitializerListener extends AbstractLifeCycle
+{
+ private static final Logger LOG = Log.getLogger(ServletContainerInitializerListener.class);
+ protected WebAppContext _context = null;
+
+
+ public void setWebAppContext (WebAppContext context)
+ {
+ _context = context;
+ }
+
+
+ /**
+ * Call the doStart method of the ServletContainerInitializers
+ * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
+ */
+ public void doStart()
+ {
+ List<ContainerInitializer> initializers = (List<ContainerInitializer>)_context.getAttribute(AnnotationConfiguration.CONTAINER_INITIALIZERS);
+ MultiMap classMap = (MultiMap)_context.getAttribute(AnnotationConfiguration.CLASS_INHERITANCE_MAP);
+
+ if (initializers != null)
+ {
+ for (ContainerInitializer i : initializers)
+ {
+ //We have already found the classes that directly have an annotation that was in the HandlesTypes
+ //annotation of the ServletContainerInitializer. For each of those classes, walk the inheritance
+ //hierarchy to find classes that extend or implement them.
+ if (i.getAnnotatedTypeNames() != null)
+ {
+ Set<String> annotatedClassNames = new HashSet<String>(i.getAnnotatedTypeNames());
+ for (String name : annotatedClassNames)
+ {
+ //add the class with the annotation
+ i.addApplicableTypeName(name);
+ //add the classes that inherit the annotation
+ if (classMap != null)
+ {
+ List<String> implementsOrExtends = (List<String>)classMap.getValues(name);
+ if (implementsOrExtends != null && !implementsOrExtends.isEmpty())
+ addInheritedTypes(classMap, i, implementsOrExtends);
+ }
+ }
+ }
+
+
+ //Now we need to look at the HandlesTypes classes that were not annotations. We need to
+ //find all classes that extend or implement them.
+ if (i.getInterestedTypes() != null)
+ {
+ for (Class c : i.getInterestedTypes())
+ {
+ if (!c.isAnnotation())
+ {
+ //add the classes that implement or extend the class.
+ //TODO but not including the class itself?
+ if (classMap != null)
+ {
+ List<String> implementsOrExtends = (List<String>)classMap.getValues(c.getName());
+ if (implementsOrExtends != null && !implementsOrExtends.isEmpty())
+ addInheritedTypes(classMap, i, implementsOrExtends);
+ }
+ }
+ }
+ }
+
+ //instantiate ServletContainerInitializers, call doStart
+ try
+ {
+ i.callStartup(_context);
+ }
+ catch (Exception e)
+ {
+ LOG.warn(e);
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+
+
+ void addInheritedTypes (MultiMap classMap, ContainerInitializer initializer, List<String> applicableTypes)
+ {
+ for (String s : applicableTypes)
+ {
+ //add the name of the class that extends or implements
+ initializer.addApplicableTypeName(s);
+
+ //walk the hierarchy and find all types that extend or implement it
+ List<String> implementsOrExtends = (List<String>)classMap.getValues(s);
+ if (implementsOrExtends != null && !implementsOrExtends.isEmpty())
+ addInheritedTypes (classMap, initializer, implementsOrExtends);
+ }
+ }
+
+
+
+ /**
+ * Nothing to do for ServletContainerInitializers on stop
+ * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop()
+ */
+ public void doStop()
+ {
+
+ }
+
+}
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletSecurityAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletSecurityAnnotationHandler.java
new file mode 100644
index 0000000..d6432f8
--- /dev/null
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletSecurityAnnotationHandler.java
@@ -0,0 +1,196 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.annotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.ServletSecurityElement;
+import javax.servlet.annotation.HttpConstraint;
+import javax.servlet.annotation.HttpMethodConstraint;
+import javax.servlet.annotation.ServletSecurity;
+import javax.servlet.annotation.ServletSecurity.EmptyRoleSemantic;
+import javax.servlet.annotation.ServletSecurity.TransportGuarantee;
+
+import org.eclipse.jetty.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler;
+import org.eclipse.jetty.security.ConstraintAware;
+import org.eclipse.jetty.security.ConstraintMapping;
+import org.eclipse.jetty.security.ConstraintSecurityHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.servlet.ServletMapping;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.security.Constraint;
+import org.eclipse.jetty.webapp.Origin;
+import org.eclipse.jetty.webapp.WebAppContext;
+
+/**
+ * ServletSecurityAnnotationHandler
+ *
+ * Inspect a class to see if it has an @ServletSecurity annotation on it,
+ * setting up the <security-constraint>s.
+ *
+ * A servlet can be defined in:
+ * <ul>
+ * <li>web.xml
+ * <li>web-fragment.xml
+ * <li>@WebServlet annotation discovered
+ * <li>ServletContext.createServlet
+ * </ul>
+ *
+ * The ServletSecurity annotation for a servlet should only be processed
+ * iff metadata-complete == false.
+ */
+public class ServletSecurityAnnotationHandler extends AbstractIntrospectableAnnotationHandler
+{
+ private static final Logger LOG = Log.getLogger(ServletSecurityAnnotationHandler.class);
+
+ private WebAppContext _context;
+
+ public ServletSecurityAnnotationHandler(WebAppContext wac)
+ {
+ super(false);
+ _context = wac;
+ }
+
+ /**
+ * @see org.eclipse.jetty.annotations.AnnotationIntrospector.IntrospectableAnnotationHandler#handle(java.lang.Class)
+ */
+ public void doHandle(Class clazz)
+ {
+ if (!(_context.getSecurityHandler() instanceof ConstraintAware))
+ {
+ LOG.warn("SecurityHandler not ConstraintAware, skipping security annotation processing");
+ return;
+ }
+
+ ServletSecurity servletSecurity = (ServletSecurity)clazz.getAnnotation(ServletSecurity.class);
+ if (servletSecurity == null)
+ return;
+
+ //If there are already constraints defined (ie from web.xml) that match any
+ //of the url patterns defined for this servlet, then skip the security annotation.
+
+ List<ServletMapping> servletMappings = getServletMappings(clazz.getCanonicalName());
+ List<ConstraintMapping> constraintMappings = ((ConstraintAware)_context.getSecurityHandler()).getConstraintMappings();
+
+ if (constraintsExist(servletMappings, constraintMappings))
+ {
+ LOG.warn("Constraints already defined for "+clazz.getName()+", skipping ServletSecurity annotation");
+ return;
+ }
+
+ //Make a fresh list
+ constraintMappings = new ArrayList<ConstraintMapping>();
+
+ ServletSecurityElement securityElement = new ServletSecurityElement(servletSecurity);
+ for (ServletMapping sm : servletMappings)
+ {
+ for (String url : sm.getPathSpecs())
+ {
+ _context.getMetaData().setOrigin("constraint.url."+url, Origin.Annotation);
+ constraintMappings.addAll(ConstraintSecurityHandler.createConstraintsWithMappingsForPath(clazz.getName(), url, securityElement));
+ }
+ }
+
+ //set up the security constraints produced by the annotation
+ ConstraintAware securityHandler = (ConstraintAware)_context.getSecurityHandler();
+
+ for (ConstraintMapping m:constraintMappings)
+ securityHandler.addConstraintMapping(m);
+ }
+
+
+
+ /**
+ * Make a jetty Constraint object, which represents the <auth-constraint> and
+ * <user-data-constraint> elements, based on the security annotation.
+ * @param servlet
+ * @param rolesAllowed
+ * @param permitOrDeny
+ * @param transport
+ * @return
+ */
+ protected Constraint makeConstraint (Class servlet, String[] rolesAllowed, EmptyRoleSemantic permitOrDeny, TransportGuarantee transport)
+ {
+ return ConstraintSecurityHandler.createConstraint(servlet.getName(), rolesAllowed, permitOrDeny, transport);
+ }
+
+
+
+ /**
+ * Get the ServletMappings for the servlet's class.
+ * @param className
+ * @return
+ */
+ protected List<ServletMapping> getServletMappings(String className)
+ {
+ List<ServletMapping> results = new ArrayList<ServletMapping>();
+ ServletMapping[] mappings = _context.getServletHandler().getServletMappings();
+ for (ServletMapping mapping : mappings)
+ {
+ //Check the name of the servlet that this mapping applies to, and then find the ServletHolder for it to find it's class
+ ServletHolder holder = _context.getServletHandler().getServlet(mapping.getServletName());
+ if (holder.getClassName() != null && holder.getClassName().equals(className))
+ results.add(mapping);
+ }
+ return results;
+ }
+
+
+
+ /**
+ * Check if there are already <security-constraint> elements defined that match the url-patterns for
+ * the servlet.
+ * @param servletMappings
+ * @return
+ */
+ protected boolean constraintsExist (List<ServletMapping> servletMappings, List<ConstraintMapping> constraintMappings)
+ {
+ boolean exists = false;
+
+ //Check to see if the path spec on each constraint mapping matches a pathSpec in the servlet mappings.
+ //If it does, then we should ignore the security annotations.
+ for (ServletMapping mapping : servletMappings)
+ {
+ //Get its url mappings
+ String[] pathSpecs = mapping.getPathSpecs();
+ if (pathSpecs == null)
+ continue;
+
+ //Check through the constraints to see if there are any whose pathSpecs (url mappings)
+ //match the servlet. If so, then we already have constraints defined for this servlet,
+ //and we will not be processing the annotation (ie web.xml or programmatic override).
+ for (int i=0; constraintMappings != null && i < constraintMappings.size() && !exists; i++)
+ {
+ for (int j=0; j < pathSpecs.length; j++)
+ {
+ //TODO decide if we need to check the origin
+ if (pathSpecs[j].equals(constraintMappings.get(i).getPathSpec()))
+ {
+ exists = true;
+ break;
+ }
+ }
+ }
+ }
+ return exists;
+ }
+
+}
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/Util.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/Util.java
index 260dc5c..0ef5476 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/Util.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/Util.java
@@ -61,7 +61,8 @@
javax.servlet.ServletRequestListener.class.isAssignableFrom(c) ||
javax.servlet.ServletRequestAttributeListener.class.isAssignableFrom(c) ||
javax.servlet.http.HttpSessionListener.class.isAssignableFrom(c) ||
- javax.servlet.http.HttpSessionAttributeListener.class.isAssignableFrom(c))
+ javax.servlet.http.HttpSessionAttributeListener.class.isAssignableFrom(c) ||
+ javax.servlet.AsyncListener.class.isAssignableFrom(c))
isServlet=true;
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebFilterAnnotation.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebFilterAnnotation.java
new file mode 100644
index 0000000..4e3567d
--- /dev/null
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebFilterAnnotation.java
@@ -0,0 +1,223 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.annotations;
+
+import java.util.ArrayList;
+import java.util.EnumSet;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.Filter;
+import javax.servlet.annotation.WebFilter;
+import javax.servlet.annotation.WebInitParam;
+
+import org.eclipse.jetty.servlet.FilterHolder;
+import org.eclipse.jetty.servlet.FilterMapping;
+import org.eclipse.jetty.servlet.Holder;
+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.webapp.DiscoveredAnnotation;
+import org.eclipse.jetty.webapp.MetaData;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.eclipse.jetty.webapp.Origin;
+
+/**
+ * WebFilterAnnotation
+ *
+ *
+ */
+public class WebFilterAnnotation extends DiscoveredAnnotation
+{
+ private static final Logger LOG = Log.getLogger(WebFilterAnnotation.class);
+
+ /**
+ * @param context
+ * @param className
+ */
+ public WebFilterAnnotation(WebAppContext context, String className)
+ {
+ super(context, className);
+ }
+
+ public WebFilterAnnotation(WebAppContext context, String className, Resource resource)
+ {
+ super(context, className, resource);
+ }
+
+ /**
+ * @see org.eclipse.jetty.annotations.ClassAnnotation#apply()
+ */
+ public void apply()
+ {
+ // TODO verify against rules for annotation v descriptor
+
+ Class clazz = getTargetClass();
+ if (clazz == null)
+ {
+ LOG.warn(_className+" cannot be loaded");
+ return;
+ }
+
+
+ //Servlet Spec 8.1.2
+ if (!Filter.class.isAssignableFrom(clazz))
+ {
+ LOG.warn(clazz.getName()+" is not assignable from javax.servlet.Filter");
+ return;
+ }
+ MetaData metaData = _context.getMetaData();
+
+ WebFilter filterAnnotation = (WebFilter)clazz.getAnnotation(WebFilter.class);
+
+ if (filterAnnotation.value().length > 0 && filterAnnotation.urlPatterns().length > 0)
+ {
+ LOG.warn(clazz.getName()+" defines both @WebFilter.value and @WebFilter.urlPatterns");
+ return;
+ }
+
+ String name = (filterAnnotation.filterName().equals("")?clazz.getName():filterAnnotation.filterName());
+ String[] urlPatterns = filterAnnotation.value();
+ if (urlPatterns.length == 0)
+ urlPatterns = filterAnnotation.urlPatterns();
+
+ FilterHolder holder = _context.getServletHandler().getFilter(name);
+ if (holder == null)
+ {
+ //Filter with this name does not already exist, so add it
+ holder = _context.getServletHandler().newFilterHolder(Holder.Source.ANNOTATION);
+ holder.setName(name);
+
+ holder.setHeldClass(clazz);
+ metaData.setOrigin(name+".filter.filter-class");
+
+ holder.setDisplayName(filterAnnotation.displayName());
+ metaData.setOrigin(name+".filter.display-name");
+
+ for (WebInitParam ip: filterAnnotation.initParams())
+ {
+ holder.setInitParameter(ip.name(), ip.value());
+ metaData.setOrigin(name+".filter.init-param."+ip.name());
+ }
+
+ FilterMapping mapping = new FilterMapping();
+ mapping.setFilterName(holder.getName());
+
+ if (urlPatterns.length > 0)
+ {
+ ArrayList paths = new ArrayList();
+ for (String s:urlPatterns)
+ {
+ paths.add(Util.normalizePattern(s));
+ }
+ mapping.setPathSpecs((String[])paths.toArray(new String[paths.size()]));
+ }
+
+ if (filterAnnotation.servletNames().length > 0)
+ {
+ ArrayList<String> names = new ArrayList<String>();
+ for (String s : filterAnnotation.servletNames())
+ {
+ names.add(s);
+ }
+ mapping.setServletNames((String[])names.toArray(new String[names.size()]));
+ }
+
+ EnumSet<DispatcherType> dispatcherSet = EnumSet.noneOf(DispatcherType.class);
+ for (DispatcherType d : filterAnnotation.dispatcherTypes())
+ {
+ dispatcherSet.add(d);
+ }
+ mapping.setDispatcherTypes(dispatcherSet);
+ metaData.setOrigin(name+".filter.mappings");
+
+ holder.setAsyncSupported(filterAnnotation.asyncSupported());
+ metaData.setOrigin(name+".filter.async-supported");
+
+ _context.getServletHandler().addFilter(holder);
+ _context.getServletHandler().addFilterMapping(mapping);
+ }
+ else
+ {
+ //A Filter definition for the same name already exists from web.xml
+ //ServletSpec 3.0 p81 if the Filter is already defined and has mappings,
+ //they override the annotation. If it already has DispatcherType set, that
+ //also overrides the annotation. Init-params are additive, but web.xml overrides
+ //init-params of the same name.
+ for (WebInitParam ip: filterAnnotation.initParams())
+ {
+ //if (holder.getInitParameter(ip.name()) == null)
+ if (metaData.getOrigin(name+".filter.init-param."+ip.name())==Origin.NotSet)
+ {
+ holder.setInitParameter(ip.name(), ip.value());
+ metaData.setOrigin(name+".filter.init-param."+ip.name());
+ }
+ }
+
+ FilterMapping[] mappings = _context.getServletHandler().getFilterMappings();
+ boolean mappingExists = false;
+ if (mappings != null)
+ {
+ for (FilterMapping m:mappings)
+ {
+ if (m.getFilterName().equals(name))
+ {
+ mappingExists = true;
+ break;
+ }
+ }
+ }
+ //if a descriptor didn't specify at least one mapping, use the mappings from the annotation and the DispatcherTypes
+ //from the annotation
+ if (!mappingExists)
+ {
+ FilterMapping mapping = new FilterMapping();
+ mapping.setFilterName(holder.getName());
+
+ if (urlPatterns.length > 0)
+ {
+ ArrayList paths = new ArrayList();
+ for (String s:urlPatterns)
+ {
+ paths.add(Util.normalizePattern(s));
+ }
+ mapping.setPathSpecs((String[])paths.toArray(new String[paths.size()]));
+ }
+ if (filterAnnotation.servletNames().length > 0)
+ {
+ ArrayList<String> names = new ArrayList<String>();
+ for (String s : filterAnnotation.servletNames())
+ {
+ names.add(s);
+ }
+ mapping.setServletNames((String[])names.toArray(new String[names.size()]));
+ }
+
+ EnumSet<DispatcherType> dispatcherSet = EnumSet.noneOf(DispatcherType.class);
+ for (DispatcherType d : filterAnnotation.dispatcherTypes())
+ {
+ dispatcherSet.add(d);
+ }
+ mapping.setDispatcherTypes(dispatcherSet);
+ _context.getServletHandler().addFilterMapping(mapping);
+ metaData.setOrigin(name+".filter.mappings");
+ }
+ }
+ }
+
+}
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebFilterAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebFilterAnnotationHandler.java
new file mode 100644
index 0000000..b37b85d
--- /dev/null
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebFilterAnnotationHandler.java
@@ -0,0 +1,74 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.annotations;
+
+import java.util.List;
+
+import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler;
+import org.eclipse.jetty.annotations.AnnotationParser.Value;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.webapp.DiscoveredAnnotation;
+import org.eclipse.jetty.webapp.WebAppContext;
+
+/**
+ * WebFilterAnnotationHandler
+ *
+ *
+ */
+public class WebFilterAnnotationHandler extends AbstractDiscoverableAnnotationHandler
+{
+ private static final Logger LOG = Log.getLogger(WebFilterAnnotationHandler.class);
+
+ public WebFilterAnnotationHandler (WebAppContext context)
+ {
+ super(context);
+ }
+
+ public WebFilterAnnotationHandler (WebAppContext context, List<DiscoveredAnnotation> list)
+ {
+ super(context, list);
+ }
+
+ public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
+ List<Value> values)
+ {
+ WebFilterAnnotation wfAnnotation = new WebFilterAnnotation(_context, className, _resource);
+ addAnnotation(wfAnnotation);
+ }
+
+ public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation,
+ List<Value> values)
+ {
+ LOG.warn ("@WebFilter not applicable for fields: "+className+"."+fieldName);
+ }
+
+ public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation,
+ List<Value> values)
+ {
+ LOG.warn ("@WebFilter not applicable for methods: "+className+"."+methodName+" "+signature);
+ }
+
+ @Override
+ public String getAnnotationName()
+ {
+ return "javax.servlet.annotation.WebFilter";
+ }
+
+}
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotation.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotation.java
new file mode 100644
index 0000000..0ad8242
--- /dev/null
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotation.java
@@ -0,0 +1,96 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.annotations;
+
+import javax.servlet.ServletContextAttributeListener;
+import javax.servlet.ServletContextListener;
+import javax.servlet.ServletRequestAttributeListener;
+import javax.servlet.ServletRequestListener;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionListener;
+
+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.webapp.DiscoveredAnnotation;
+import org.eclipse.jetty.webapp.MetaData;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.eclipse.jetty.webapp.Origin;
+
+/**
+ * WebListenerAnnotation
+ *
+ *
+ */
+public class WebListenerAnnotation extends DiscoveredAnnotation
+{
+ private static final Logger LOG = Log.getLogger(WebListenerAnnotation.class);
+
+ /**
+ * @param context
+ * @param className
+ */
+ public WebListenerAnnotation(WebAppContext context, String className)
+ {
+ super(context, className);
+ }
+
+ public WebListenerAnnotation(WebAppContext context, String className, Resource resource)
+ {
+ super(context, className, resource);
+ }
+
+ /**
+ * @see org.eclipse.jetty.annotations.ClassAnnotation#apply()
+ */
+ public void apply()
+ {
+ // TODO check algorithm against ordering rules for descriptors v annotations
+
+ Class clazz = getTargetClass();
+
+ if (clazz == null)
+ {
+ LOG.warn(_className+" cannot be loaded");
+ return;
+ }
+
+ try
+ {
+ if (ServletContextListener.class.isAssignableFrom(clazz) ||
+ ServletContextAttributeListener.class.isAssignableFrom(clazz) ||
+ ServletRequestListener.class.isAssignableFrom(clazz) ||
+ ServletRequestAttributeListener.class.isAssignableFrom(clazz) ||
+ HttpSessionListener.class.isAssignableFrom(clazz) ||
+ HttpSessionAttributeListener.class.isAssignableFrom(clazz))
+ {
+ java.util.EventListener listener = (java.util.EventListener)clazz.newInstance();
+ MetaData metaData = _context.getMetaData();
+ if (metaData.getOrigin(clazz.getName()+".listener") == Origin.NotSet)
+ _context.addEventListener(listener);
+ }
+ else
+ LOG.warn(clazz.getName()+" does not implement one of the servlet listener interfaces");
+ }
+ catch (Exception e)
+ {
+ LOG.warn(e);
+ }
+ }
+}
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotationHandler.java
new file mode 100644
index 0000000..74753a8
--- /dev/null
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotationHandler.java
@@ -0,0 +1,71 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.annotations;
+
+import java.util.List;
+
+import org.eclipse.jetty.annotations.AnnotationParser.Value;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.webapp.DiscoveredAnnotation;
+import org.eclipse.jetty.webapp.WebAppContext;
+
+public class WebListenerAnnotationHandler extends AbstractDiscoverableAnnotationHandler
+{
+ private static final Logger LOG = Log.getLogger(WebListenerAnnotationHandler.class);
+
+ public WebListenerAnnotationHandler (WebAppContext context)
+ {
+ super(context);
+ }
+
+ public WebListenerAnnotationHandler (WebAppContext context, List<DiscoveredAnnotation> list)
+ {
+ super(context, list);
+ }
+
+ /**
+ * @see org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler#handleClass(java.lang.String, int, int, java.lang.String, java.lang.String, java.lang.String[], java.lang.String, java.util.List)
+ */
+ public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
+ List<Value> values)
+ {
+ WebListenerAnnotation wlAnnotation = new WebListenerAnnotation(_context, className, _resource);
+ addAnnotation(wlAnnotation);
+ }
+
+ public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation,
+ List<Value> values)
+ {
+ LOG.warn ("@WebListener is not applicable to fields: "+className+"."+fieldName);
+ }
+
+ public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation,
+ List<Value> values)
+ {
+ LOG.warn ("@WebListener is not applicable to methods: "+className+"."+methodName+" "+signature);
+ }
+
+ @Override
+ public String getAnnotationName()
+ {
+ return "javax.servlet.annotation.WebListener";
+ }
+
+}
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotation.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotation.java
new file mode 100644
index 0000000..82f7290
--- /dev/null
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotation.java
@@ -0,0 +1,227 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.annotations;
+
+import java.util.ArrayList;
+
+import javax.servlet.annotation.WebInitParam;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+
+import org.eclipse.jetty.servlet.Holder;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.servlet.ServletMapping;
+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;
+import org.eclipse.jetty.webapp.DiscoveredAnnotation;
+import org.eclipse.jetty.webapp.MetaData;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.eclipse.jetty.webapp.Origin;
+
+/**
+ * WebServletAnnotation
+ *
+ *
+ */
+public class WebServletAnnotation extends DiscoveredAnnotation
+{
+ private static final Logger LOG = Log.getLogger(WebServletAnnotation.class);
+
+ public WebServletAnnotation (WebAppContext context, String className)
+ {
+ super(context, className);
+ }
+
+
+ public WebServletAnnotation (WebAppContext context, String className, Resource resource)
+ {
+ super(context, className, resource);
+ }
+
+ /**
+ * @see org.eclipse.jetty.annotations.ClassAnnotation#apply()
+ */
+ public void apply()
+ {
+ //TODO check this algorithm with new rules for applying descriptors and annotations in order
+ Class clazz = getTargetClass();
+
+ if (clazz == null)
+ {
+ LOG.warn(_className+" cannot be loaded");
+ return;
+ }
+
+ //Servlet Spec 8.1.1
+ if (!HttpServlet.class.isAssignableFrom(clazz))
+ {
+ LOG.warn(clazz.getName()+" is not assignable from javax.servlet.http.HttpServlet");
+ return;
+ }
+
+ WebServlet annotation = (WebServlet)clazz.getAnnotation(WebServlet.class);
+
+ if (annotation.urlPatterns().length > 0 && annotation.value().length > 0)
+ {
+ LOG.warn(clazz.getName()+ " defines both @WebServlet.value and @WebServlet.urlPatterns");
+ return;
+ }
+
+ String[] urlPatterns = annotation.value();
+ if (urlPatterns.length == 0)
+ urlPatterns = annotation.urlPatterns();
+
+ if (urlPatterns.length == 0)
+ {
+ LOG.warn(clazz.getName()+ " defines neither @WebServlet.value nor @WebServlet.urlPatterns");
+ return;
+ }
+
+ //canonicalize the patterns
+ ArrayList<String> urlPatternList = new ArrayList<String>();
+ for (String p : urlPatterns)
+ urlPatternList.add(Util.normalizePattern(p));
+
+ String servletName = (annotation.name().equals("")?clazz.getName():annotation.name());
+
+ MetaData metaData = _context.getMetaData();
+
+ //Find out if a <servlet> already exists with this name
+ ServletHolder[] holders = _context.getServletHandler().getServlets();
+ boolean isNew = true;
+ ServletHolder holder = null;
+ if (holders != null)
+ {
+ for (ServletHolder h : holders)
+ {
+ if (h.getName() != null && servletName.equals(h.getName()))
+ {
+ holder = h;
+ isNew = false;
+ break;
+ }
+ }
+ }
+
+ if (isNew)
+ {
+ //No servlet of this name has already been defined, either by a descriptor
+ //or another annotation (which would be impossible).
+ holder = _context.getServletHandler().newServletHolder(Holder.Source.ANNOTATION);
+ holder.setHeldClass(clazz);
+ metaData.setOrigin(servletName+".servlet.servlet-class");
+
+ holder.setName(servletName);
+ holder.setDisplayName(annotation.displayName());
+ metaData.setOrigin(servletName+".servlet.display-name");
+
+ holder.setInitOrder(annotation.loadOnStartup());
+ metaData.setOrigin(servletName+".servlet.load-on-startup");
+
+ holder.setAsyncSupported(annotation.asyncSupported());
+ metaData.setOrigin(servletName+".servlet.async-supported");
+
+ for (WebInitParam ip:annotation.initParams())
+ {
+ holder.setInitParameter(ip.name(), ip.value());
+ metaData.setOrigin(servletName+".servlet.init-param."+ip.name());
+ }
+
+ _context.getServletHandler().addServlet(holder);
+ ServletMapping mapping = new ServletMapping();
+ mapping.setServletName(holder.getName());
+ mapping.setPathSpecs( LazyList.toStringArray(urlPatternList));
+ _context.getServletHandler().addServletMapping(mapping);
+ metaData.setOrigin(servletName+".servlet.mappings");
+ }
+ else
+ {
+ //set the class according to the servlet that is annotated, if it wasn't already
+ //NOTE: this may be considered as "completing" an incomplete servlet registration, and it is
+ //not clear from servlet 3.0 spec whether this is intended, or if only a ServletContext.addServlet() call
+ //can complete it, see http://java.net/jira/browse/SERVLET_SPEC-42
+ if (holder.getClassName() == null)
+ holder.setClassName(clazz.getName());
+ if (holder.getHeldClass() == null)
+ holder.setHeldClass(clazz);
+
+ //check if the existing servlet has each init-param from the annotation
+ //if not, add it
+ for (WebInitParam ip:annotation.initParams())
+ {
+ if (metaData.getOrigin(servletName+".servlet.init-param"+ip.name())==Origin.NotSet)
+ {
+ holder.setInitParameter(ip.name(), ip.value());
+ metaData.setOrigin(servletName+".servlet.init-param."+ip.name());
+ }
+ }
+
+ //check the url-patterns
+ //ServletSpec 3.0 p81 If a servlet already has url mappings from a
+ //webxml or fragment descriptor the annotation is ignored. However, we want to be able to
+ //replace mappings that were given in webdefault.xml
+ boolean mappingsExist = false;
+ boolean anyNonDefaults = false;
+ ServletMapping[] allMappings = _context.getServletHandler().getServletMappings();
+ if (allMappings != null)
+ {
+ for (ServletMapping m:allMappings)
+ {
+ if (m.getServletName() != null && servletName.equals(m.getServletName()))
+ {
+ mappingsExist = true;
+ if (!m.isDefault())
+ {
+ anyNonDefaults = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (anyNonDefaults)
+ return; //if any mappings already set by a descriptor that is not webdefault.xml, we're done
+
+ boolean clash = false;
+ if (mappingsExist)
+ {
+ for (String p:urlPatternList)
+ {
+ ServletMapping m = _context.getServletHandler().getServletMapping(p);
+ if (m != null && !m.isDefault())
+ {
+ //trying to override a servlet-mapping that was added not by webdefault.xml
+ clash = true;
+ break;
+ }
+ }
+ }
+
+ if (!mappingsExist || !clash)
+ {
+ ServletMapping m = new ServletMapping();
+ m.setServletName(servletName);
+ m.setPathSpecs(LazyList.toStringArray(urlPatternList));
+ _context.getServletHandler().addServletMapping(m);
+ }
+ }
+ }
+}
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotationHandler.java
new file mode 100644
index 0000000..f89dd4e
--- /dev/null
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotationHandler.java
@@ -0,0 +1,85 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.annotations;
+
+import java.util.List;
+
+import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler;
+import org.eclipse.jetty.annotations.AnnotationParser.Value;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.webapp.DiscoveredAnnotation;
+import org.eclipse.jetty.webapp.WebAppContext;
+
+/**
+ * WebServletAnnotationHandler
+ *
+ * Process a WebServlet annotation on a class.
+ *
+ */
+public class WebServletAnnotationHandler extends AbstractDiscoverableAnnotationHandler
+{
+ private static final Logger LOG = Log.getLogger(WebServletAnnotationHandler.class);
+
+ public WebServletAnnotationHandler (WebAppContext context)
+ {
+ super(context);
+ }
+
+ public WebServletAnnotationHandler (WebAppContext context, List<DiscoveredAnnotation> list)
+ {
+ super(context, list);
+ }
+
+
+ /**
+ * Handle discovering a WebServlet annotation.
+ *
+ *
+ * @see org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler#handleClass(java.lang.String, int, int, java.lang.String, java.lang.String, java.lang.String[], java.lang.String, java.util.List)
+ */
+ public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotationName,
+ List<Value> values)
+ {
+ if (!"javax.servlet.annotation.WebServlet".equals(annotationName))
+ return;
+
+ WebServletAnnotation annotation = new WebServletAnnotation (_context, className, _resource);
+ addAnnotation(annotation);
+ }
+
+ public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation,
+ List<Value> values)
+ {
+ LOG.warn ("@WebServlet annotation not supported for fields");
+ }
+
+ public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation,
+ List<Value> values)
+ {
+ LOG.warn ("@WebServlet annotation not supported for methods");
+ }
+
+
+ @Override
+ public String getAnnotationName()
+ {
+ return "javax.servlet.annotation.WebServlet";
+ }
+}
diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/FilterC.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/FilterC.java
index 3886e0f..d123274 100644
--- a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/FilterC.java
+++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/FilterC.java
@@ -24,16 +24,20 @@
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.annotation.security.RunAs;
+import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
+import javax.servlet.annotation.WebFilter;
+import javax.servlet.annotation.WebInitParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
+@WebFilter(filterName="CFilter", dispatcherTypes={DispatcherType.REQUEST}, urlPatterns = {"/*"}, initParams={@WebInitParam(name="a", value="99")}, asyncSupported=false)
@RunAs("admin")
public class FilterC implements Filter
{
diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/ListenerC.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/ListenerC.java
index 10fc5f9..c010e57 100644
--- a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/ListenerC.java
+++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/ListenerC.java
@@ -20,7 +20,9 @@
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
+import javax.servlet.annotation.WebListener;
+@WebListener
public class ListenerC implements ServletContextListener
{
diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/ServletC.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/ServletC.java
index 9b5dcd4..96ed7c2 100644
--- a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/ServletC.java
+++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/ServletC.java
@@ -26,12 +26,21 @@
import javax.annotation.security.DeclareRoles;
import javax.annotation.security.RunAs;
import javax.servlet.ServletException;
+import javax.servlet.annotation.HttpConstraint;
+import javax.servlet.annotation.HttpMethodConstraint;
+import javax.servlet.annotation.MultipartConfig;
+import javax.servlet.annotation.ServletSecurity;
+import javax.servlet.annotation.WebInitParam;
+import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@DeclareRoles({"alice"})
+@WebServlet(urlPatterns = { "/foo/*", "/bah/*" }, name="CServlet", initParams={@WebInitParam(name="x", value="y")}, loadOnStartup=2, asyncSupported=false)
+@MultipartConfig(fileSizeThreshold=1000, maxFileSize=2000, maxRequestSize=3000)
@RunAs("admin")
+@ServletSecurity(value=@HttpConstraint(rolesAllowed={"fred", "bill", "dorothy"}), httpMethodConstraints={@HttpMethodConstraint(value="GET", rolesAllowed={"bob", "carol", "ted"})})
public class ServletC extends HttpServlet
{
@Resource (mappedName="foo", type=Double.class)
diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationConfiguration.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationConfiguration.java
new file mode 100644
index 0000000..eca8092
--- /dev/null
+++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationConfiguration.java
@@ -0,0 +1,58 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.annotations;
+
+import java.io.File;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.webapp.FragmentDescriptor;
+import org.eclipse.jetty.webapp.WebAppContext;
+
+/**
+ * TestAnnotationConfiguration
+ *
+ *
+ */
+public class TestAnnotationConfiguration extends TestCase
+{
+ public void testGetFragmentFromJar ()
+ throws Exception
+ {
+ String dir = System.getProperty("basedir", ".");
+ File file = new File(dir);
+ file=new File(file.getCanonicalPath());
+ URL url=file.toURL();
+
+ Resource jar1 = Resource.newResource(url+"file.jar");
+
+ AnnotationConfiguration config = new AnnotationConfiguration();
+ WebAppContext wac = new WebAppContext();
+
+ List<FragmentDescriptor> frags = new ArrayList<FragmentDescriptor>();
+ frags.add(new FragmentDescriptor(Resource.newResource("jar:"+url+"file.jar!/fooa.props")));
+ frags.add(new FragmentDescriptor(Resource.newResource("jar:"+url+"file2.jar!/foob.props")));
+
+ assertNotNull(config.getFragmentFromJar(jar1, frags));
+ }
+}
diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationInheritance.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationInheritance.java
index b2d7de1..c74155f 100644
--- a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationInheritance.java
+++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationInheritance.java
@@ -18,20 +18,23 @@
package org.eclipse.jetty.annotations;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
import java.util.ArrayList;
import java.util.List;
-
+import java.util.Map;
import javax.naming.Context;
import javax.naming.InitialContext;
import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler;
import org.eclipse.jetty.annotations.AnnotationParser.Value;
+import org.eclipse.jetty.util.MultiMap;
import org.junit.After;
import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
/**
*
*/
@@ -63,6 +66,12 @@
{
annotatedMethods.add(className+"."+methodName);
}
+
+ @Override
+ public String getAnnotationName()
+ {
+ return "org.eclipse.jetty.annotations.Sample";
+ }
}
@After
@@ -82,7 +91,7 @@
SampleHandler handler = new SampleHandler();
AnnotationParser parser = new AnnotationParser();
- parser.registerAnnotationHandler("org.eclipse.jetty.annotations.Sample", handler);
+ parser.registerHandler(handler);
parser.parse(classNames, new ClassNameResolver ()
{
public boolean isExcluded(String name)
@@ -191,4 +200,40 @@
});
assertEquals (1, handler.annotatedClassNames.size());
}
+
+ @Test
+ public void testTypeInheritanceHandling() throws Exception
+ {
+ AnnotationParser parser = new AnnotationParser();
+ ClassInheritanceHandler handler = new ClassInheritanceHandler();
+ parser.registerClassHandler(handler);
+
+ class Foo implements InterfaceD
+ {
+ }
+
+ classNames.clear();
+ classNames.add(ClassA.class.getName());
+ classNames.add(ClassB.class.getName());
+ classNames.add(InterfaceD.class.getName());
+ classNames.add(Foo.class.getName());
+
+ parser.parse(classNames, null);
+
+ MultiMap map = handler.getMap();
+ assertNotNull(map);
+ assertFalse(map.isEmpty());
+ assertEquals(2, map.size());
+ Map stringArrayMap = map.toStringArrayMap();
+ assertTrue (stringArrayMap.keySet().contains("org.eclipse.jetty.annotations.ClassA"));
+ assertTrue (stringArrayMap.keySet().contains("org.eclipse.jetty.annotations.InterfaceD"));
+ String[] classes = (String[])stringArrayMap.get("org.eclipse.jetty.annotations.ClassA");
+ assertEquals(1, classes.length);
+ assertEquals ("org.eclipse.jetty.annotations.ClassB", classes[0]);
+
+ classes = (String[])stringArrayMap.get("org.eclipse.jetty.annotations.InterfaceD");
+ assertEquals(2, classes.length);
+ assertEquals ("org.eclipse.jetty.annotations.ClassB", classes[0]);
+ assertEquals(Foo.class.getName(), classes[1]);
+ }
}
diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java
index 3359857..f39c599 100644
--- a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java
+++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java
@@ -53,10 +53,13 @@
public static class TrackingAnnotationHandler implements DiscoverableAnnotationHandler
{
+
+ private final String annotationName;
public final Set<String> foundClasses;
- public TrackingAnnotationHandler()
+ public TrackingAnnotationHandler(String annotationName)
{
+ this.annotationName = annotationName;
this.foundClasses = new HashSet<String>();
}
@@ -80,6 +83,13 @@
{
/* ignore */
}
+
+
+ @Override
+ public String getAnnotationName()
+ {
+ return this.annotationName;
+ }
}
@@ -95,6 +105,9 @@
{
private List<String> methods = Arrays.asList("a", "b", "c", "d", "l");
+
+
+
public void handleClass(String className, int version, int access, String signature, String superName, String[] interfaces, String annotation,
List<Value> values)
{
@@ -120,9 +133,15 @@
assertTrue(methods.contains(methodName));
assertEquals("org.eclipse.jetty.annotations.Sample", annotation);
}
+
+ @Override
+ public String getAnnotationName()
+ {
+ return "org.eclipse.jetty.annotations.Sample";
+ }
}
- parser.registerAnnotationHandler("org.eclipse.jetty.annotations.Sample", new SampleAnnotationHandler());
+ parser.registerHandler(new SampleAnnotationHandler());
long start = System.currentTimeMillis();
parser.parse(classNames, new ClassNameResolver ()
@@ -169,9 +188,17 @@
assertTrue("org.eclipse.jetty.annotations.ClassB".equals(className));
assertTrue("a".equals(methodName));
}
+
+ @Override
+ public String getAnnotationName()
+ {
+ return "org.eclipse.jetty.annotations.Multi";
+ }
+
+
}
- parser.registerAnnotationHandler("org.eclipse.jetty.annotations.Multi", new MultiAnnotationHandler());
+ parser.registerHandler(new MultiAnnotationHandler());
parser.parse(classNames, null);
}
@@ -200,11 +227,11 @@
copyClass(ClassA.class,basedir);
// Setup Tracker
- TrackingAnnotationHandler tracker = new TrackingAnnotationHandler();
+ TrackingAnnotationHandler tracker = new TrackingAnnotationHandler(Sample.class.getName());
// Setup annotation scanning
AnnotationParser parser = new AnnotationParser();
- parser.registerAnnotationHandler(Sample.class.getName(), tracker);
+ parser.registerHandler(tracker);
// Parse
parser.parse(Resource.newResource(basedir),null);
diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestSecurityAnnotationConversions.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestSecurityAnnotationConversions.java
new file mode 100644
index 0000000..bfce6f5
--- /dev/null
+++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestSecurityAnnotationConversions.java
@@ -0,0 +1,334 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.annotations;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.annotation.ServletSecurity;
+import javax.servlet.annotation.HttpConstraint;
+import javax.servlet.annotation.HttpMethodConstraint;
+import javax.servlet.annotation.ServletSecurity.TransportGuarantee;
+import javax.servlet.annotation.ServletSecurity.EmptyRoleSemantic;
+import javax.servlet.http.HttpServlet;
+
+import org.eclipse.jetty.security.ConstraintAware;
+import org.eclipse.jetty.security.ConstraintMapping;
+import org.eclipse.jetty.servlet.Holder;
+import org.eclipse.jetty.security.ConstraintSecurityHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.servlet.ServletMapping;
+import org.eclipse.jetty.util.LazyList;
+import org.eclipse.jetty.util.security.Constraint;
+import org.eclipse.jetty.webapp.WebAppContext;
+
+import junit.framework.TestCase;
+
+public class TestSecurityAnnotationConversions extends TestCase
+{
+ @ServletSecurity(value=@HttpConstraint(value=EmptyRoleSemantic.DENY))
+ public static class DenyServlet extends HttpServlet
+ {}
+
+ @ServletSecurity
+ public static class PermitServlet extends HttpServlet
+ {}
+
+ @ServletSecurity(value=@HttpConstraint(value=EmptyRoleSemantic.PERMIT, transportGuarantee=TransportGuarantee.CONFIDENTIAL, rolesAllowed={"tom", "dick", "harry"}))
+ public static class RolesServlet extends HttpServlet
+ {}
+
+ @ServletSecurity(value=@HttpConstraint(value=EmptyRoleSemantic.PERMIT, transportGuarantee=TransportGuarantee.CONFIDENTIAL, rolesAllowed={"tom", "dick", "harry"}),
+ httpMethodConstraints={@HttpMethodConstraint(value="GET")})
+ public static class Method1Servlet extends HttpServlet
+ {}
+
+ @ServletSecurity(value=@HttpConstraint(value=EmptyRoleSemantic.PERMIT, transportGuarantee=TransportGuarantee.CONFIDENTIAL, rolesAllowed={"tom", "dick", "harry"}),
+ httpMethodConstraints={@HttpMethodConstraint(value="GET", transportGuarantee=TransportGuarantee.CONFIDENTIAL)})
+ public static class Method2Servlet extends HttpServlet
+ {}
+
+
+ public void setUp()
+ {
+ }
+
+ public void testDenyAllOnClass ()
+ throws Exception
+ {
+
+ WebAppContext wac = makeWebAppContext(DenyServlet.class.getCanonicalName(), "denyServlet", new String[]{"/foo/*", "*.foo"});
+
+ //Assume we found 1 servlet with a @HttpConstraint with value=EmptyRoleSemantic.DENY security annotation
+ ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac);
+ AnnotationIntrospector introspector = new AnnotationIntrospector();
+ introspector.registerHandler(annotationHandler);
+
+ //set up the expected outcomes:
+ //1 ConstraintMapping per ServletMapping pathSpec
+ Constraint expectedConstraint = new Constraint();
+ expectedConstraint.setAuthenticate(true);
+ expectedConstraint.setDataConstraint(Constraint.DC_NONE);
+
+ ConstraintMapping[] expectedMappings = new ConstraintMapping[2];
+
+ expectedMappings[0] = new ConstraintMapping();
+ expectedMappings[0].setConstraint(expectedConstraint);
+ expectedMappings[0].setPathSpec("/foo/*");
+
+ expectedMappings[1] = new ConstraintMapping();
+ expectedMappings[1].setConstraint(expectedConstraint);
+ expectedMappings[1].setPathSpec("*.foo");
+
+ introspector.introspect(DenyServlet.class);
+
+ compareResults(expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings());
+ }
+
+
+ public void testPermitAll()
+ throws Exception
+ {
+ //Assume we found 1 servlet with a @ServletSecurity security annotation
+ WebAppContext wac = makeWebAppContext(PermitServlet.class.getCanonicalName(), "permitServlet", new String[]{"/foo/*", "*.foo"});
+
+ ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac);
+ AnnotationIntrospector introspector = new AnnotationIntrospector();
+ introspector.registerHandler(annotationHandler);
+
+
+ //set up the expected outcomes:
+ //1 ConstraintMapping per ServletMapping pathSpec
+ Constraint expectedConstraint = new Constraint();
+ expectedConstraint.setAuthenticate(false);
+ expectedConstraint.setDataConstraint(Constraint.DC_NONE);
+
+ ConstraintMapping[] expectedMappings = new ConstraintMapping[2];
+ expectedMappings[0] = new ConstraintMapping();
+ expectedMappings[0].setConstraint(expectedConstraint);
+ expectedMappings[0].setPathSpec("/foo/*");
+
+ expectedMappings[1] = new ConstraintMapping();
+ expectedMappings[1].setConstraint(expectedConstraint);
+ expectedMappings[1].setPathSpec("*.foo");
+
+ introspector.introspect(PermitServlet.class);
+
+ compareResults (expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings());
+ }
+
+ public void testRolesAllowedWithTransportGuarantee ()
+ throws Exception
+ {
+ //Assume we found 1 servlet with annotation with roles defined and
+ //and a TransportGuarantee
+
+ WebAppContext wac = makeWebAppContext(RolesServlet.class.getCanonicalName(), "rolesServlet", new String[]{"/foo/*", "*.foo"});
+
+ ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac);
+ AnnotationIntrospector introspector = new AnnotationIntrospector();
+ introspector.registerHandler(annotationHandler);
+
+ //set up the expected outcomes:compareResults
+ //1 ConstraintMapping per ServletMapping
+ Constraint expectedConstraint = new Constraint();
+ expectedConstraint.setAuthenticate(true);
+ expectedConstraint.setRoles(new String[]{"tom", "dick", "harry"});
+ expectedConstraint.setDataConstraint(Constraint.DC_CONFIDENTIAL);
+
+ ConstraintMapping[] expectedMappings = new ConstraintMapping[2];
+ expectedMappings[0] = new ConstraintMapping();
+ expectedMappings[0].setConstraint(expectedConstraint);
+ expectedMappings[0].setPathSpec("/foo/*");
+
+ expectedMappings[1] = new ConstraintMapping();
+ expectedMappings[1].setConstraint(expectedConstraint);
+ expectedMappings[1].setPathSpec("*.foo");
+
+ introspector.introspect(RolesServlet.class);
+ compareResults (expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings());
+ }
+
+
+ public void testMethodAnnotation ()
+ throws Exception
+ {
+ //ServletSecurity annotation with HttpConstraint of TransportGuarantee.CONFIDENTIAL, and a list of rolesAllowed, and
+ //a HttpMethodConstraint for GET method that permits all and has TransportGuarantee.NONE (ie is default)
+
+ WebAppContext wac = makeWebAppContext(Method1Servlet.class.getCanonicalName(), "method1Servlet", new String[]{"/foo/*", "*.foo"});
+
+ //set up the expected outcomes: - a Constraint for the RolesAllowed on the class
+ //with userdata constraint of DC_CONFIDENTIAL
+ //and mappings for each of the pathSpecs
+ Constraint expectedConstraint1 = new Constraint();
+ expectedConstraint1.setAuthenticate(true);
+ expectedConstraint1.setRoles(new String[]{"tom", "dick", "harry"});
+ expectedConstraint1.setDataConstraint(Constraint.DC_CONFIDENTIAL);
+
+ //a Constraint for the PermitAll on the doGet method with a userdata
+ //constraint of DC_CONFIDENTIAL inherited from the class
+ Constraint expectedConstraint2 = new Constraint();
+ expectedConstraint2.setDataConstraint(Constraint.DC_NONE);
+
+ ConstraintMapping[] expectedMappings = new ConstraintMapping[4];
+ expectedMappings[0] = new ConstraintMapping();
+ expectedMappings[0].setConstraint(expectedConstraint1);
+ expectedMappings[0].setPathSpec("/foo/*");
+ expectedMappings[0].setMethodOmissions(new String[]{"GET"});
+ expectedMappings[1] = new ConstraintMapping();
+ expectedMappings[1].setConstraint(expectedConstraint1);
+ expectedMappings[1].setPathSpec("*.foo");
+ expectedMappings[1].setMethodOmissions(new String[]{"GET"});
+
+ expectedMappings[2] = new ConstraintMapping();
+ expectedMappings[2].setConstraint(expectedConstraint2);
+ expectedMappings[2].setPathSpec("/foo/*");
+ expectedMappings[2].setMethod("GET");
+ expectedMappings[3] = new ConstraintMapping();
+ expectedMappings[3].setConstraint(expectedConstraint2);
+ expectedMappings[3].setPathSpec("*.foo");
+ expectedMappings[3].setMethod("GET");
+
+ AnnotationIntrospector introspector = new AnnotationIntrospector();
+ ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac);
+ introspector.registerHandler(annotationHandler);
+ introspector.introspect(Method1Servlet.class);
+ compareResults (expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings());
+ }
+
+ public void testMethodAnnotation2 ()
+ throws Exception
+ {
+ //A ServletSecurity annotation that has HttpConstraint of CONFIDENTIAL with defined roles, but a
+ //HttpMethodConstraint for GET that permits all, but also requires CONFIDENTIAL
+ WebAppContext wac = makeWebAppContext(Method2Servlet.class.getCanonicalName(), "method2Servlet", new String[]{"/foo/*", "*.foo"});
+
+ AnnotationIntrospector introspector = new AnnotationIntrospector();
+ ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac);
+ introspector.registerHandler(annotationHandler);
+
+ //set up the expected outcomes: - a Constraint for the RolesAllowed on the class
+ //with userdata constraint of DC_CONFIDENTIAL
+ //and mappings for each of the pathSpecs
+ Constraint expectedConstraint1 = new Constraint();
+ expectedConstraint1.setAuthenticate(true);
+ expectedConstraint1.setRoles(new String[]{"tom", "dick", "harry"});
+ expectedConstraint1.setDataConstraint(Constraint.DC_CONFIDENTIAL);
+
+ //a Constraint for the Permit on the GET method with a userdata
+ //constraint of DC_CONFIDENTIAL
+ Constraint expectedConstraint2 = new Constraint();
+ expectedConstraint2.setDataConstraint(Constraint.DC_CONFIDENTIAL);
+
+ ConstraintMapping[] expectedMappings = new ConstraintMapping[4];
+ expectedMappings[0] = new ConstraintMapping();
+ expectedMappings[0].setConstraint(expectedConstraint1);
+ expectedMappings[0].setPathSpec("/foo/*");
+ expectedMappings[0].setMethodOmissions(new String[]{"GET"});
+ expectedMappings[1] = new ConstraintMapping();
+ expectedMappings[1].setConstraint(expectedConstraint1);
+ expectedMappings[1].setPathSpec("*.foo");
+ expectedMappings[1].setMethodOmissions(new String[]{"GET"});
+
+ expectedMappings[2] = new ConstraintMapping();
+ expectedMappings[2].setConstraint(expectedConstraint2);
+ expectedMappings[2].setPathSpec("/foo/*");
+ expectedMappings[2].setMethod("GET");
+ expectedMappings[3] = new ConstraintMapping();
+ expectedMappings[3].setConstraint(expectedConstraint2);
+ expectedMappings[3].setPathSpec("*.foo");
+ expectedMappings[3].setMethod("GET");
+
+ introspector.introspect(Method2Servlet.class);
+ compareResults (expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings());
+ }
+
+ private void compareResults (ConstraintMapping[] expectedMappings, List<ConstraintMapping> actualMappings)
+ {
+ assertNotNull(actualMappings);
+ assertEquals(expectedMappings.length, actualMappings.size());
+
+ for (int k=0; k < actualMappings.size(); k++)
+ {
+ ConstraintMapping am = actualMappings.get(k);
+ boolean matched = false;
+
+ for (int i=0; i< expectedMappings.length && !matched; i++)
+ {
+ ConstraintMapping em = expectedMappings[i];
+ if (em.getPathSpec().equals(am.getPathSpec()))
+ {
+ if ((em.getMethod()==null && am.getMethod() == null) || em.getMethod() != null && em.getMethod().equals(am.getMethod()))
+ {
+ matched = true;
+
+ assertEquals(em.getConstraint().getAuthenticate(), am.getConstraint().getAuthenticate());
+ assertEquals(em.getConstraint().getDataConstraint(), am.getConstraint().getDataConstraint());
+ if (em.getMethodOmissions() == null)
+ {
+ assertNull(am.getMethodOmissions());
+ }
+ else
+ {
+ assertTrue(Arrays.equals(am.getMethodOmissions(), em.getMethodOmissions()));
+ }
+
+ if (em.getConstraint().getRoles() == null)
+ {
+ assertNull(am.getConstraint().getRoles());
+ }
+ else
+ {
+ assertTrue(Arrays.equals(em.getConstraint().getRoles(), am.getConstraint().getRoles()));
+ }
+ }
+ }
+ }
+
+ if (!matched)
+ fail("No expected ConstraintMapping matching method:"+am.getMethod()+" pathSpec: "+am.getPathSpec());
+ }
+ }
+
+
+ private WebAppContext makeWebAppContext (String className, String servletName, String[] paths)
+ {
+ WebAppContext wac = new WebAppContext();
+
+ ServletHolder[] holders = new ServletHolder[1];
+ holders[0] = new ServletHolder();
+ holders[0].setClassName(className);
+ holders[0].setName(servletName);
+ holders[0].setServletHandler(wac.getServletHandler());
+ wac.getServletHandler().setServlets(holders);
+ wac.setSecurityHandler(new ConstraintSecurityHandler());
+
+ ServletMapping[] servletMappings = new ServletMapping[1];
+ servletMappings[0] = new ServletMapping();
+
+ servletMappings[0].setPathSpecs(paths);
+ servletMappings[0].setServletName(servletName);
+ wac.getServletHandler().setServletMappings(servletMappings);
+ return wac;
+ }
+}
diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestServletAnnotations.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestServletAnnotations.java
index 0bdd18a..bc43260 100644
--- a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestServletAnnotations.java
+++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestServletAnnotations.java
@@ -18,12 +18,19 @@
package org.eclipse.jetty.annotations;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
+import java.util.List;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.servlet.ServletMapping;
import org.eclipse.jetty.webapp.WebAppContext;
import org.junit.Test;
@@ -34,8 +41,50 @@
*/
public class TestServletAnnotations
{
-
@Test
+ public void testServletAnnotation() throws Exception
+ {
+ List<String> classes = new ArrayList<String>();
+ classes.add("org.eclipse.jetty.annotations.ServletC");
+ AnnotationParser parser = new AnnotationParser();
+
+ WebAppContext wac = new WebAppContext();
+ WebServletAnnotationHandler handler = new WebServletAnnotationHandler(wac);
+ parser.registerAnnotationHandler("javax.servlet.annotation.WebServlet", handler);
+
+ parser.parse(classes, new ClassNameResolver ()
+ {
+ public boolean isExcluded(String name)
+ {
+ return false;
+ }
+
+ public boolean shouldOverride(String name)
+ {
+ return false;
+ }
+ });
+
+ assertEquals(1, handler.getAnnotationList().size());
+ assertTrue(handler.getAnnotationList().get(0) instanceof WebServletAnnotation);
+
+ handler.getAnnotationList().get(0).apply();
+
+ ServletHolder[] holders = wac.getServletHandler().getServlets();
+ assertNotNull(holders);
+ assertEquals(1, holders.length);
+ assertEquals("CServlet", holders[0].getName());
+ ServletMapping[] mappings = wac.getServletHandler().getServletMappings();
+ assertNotNull(mappings);
+ assertEquals(1, mappings.length);
+ String[] paths = mappings[0].getPathSpecs();
+ assertNotNull(paths);
+ assertEquals(2, paths.length);
+ assertEquals("y", holders[0].getInitParameter("x"));
+ assertEquals(2,holders[0].getInitOrder());
+ assertFalse(holders[0].isAsyncSupported());
+ }
+
public void testDeclareRoles ()
throws Exception
{
diff --git a/jetty-client/pom.xml b/jetty-client/pom.xml
index e9455c6..586113d 100644
--- a/jetty-client/pom.xml
+++ b/jetty-client/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -76,7 +76,45 @@
</configuration>
</execution>
</executions>
- </plugin>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <version>2.0</version>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ <configuration>
+ <shadedArtifactAttached>true</shadedArtifactAttached>
+ <shadedClassifierName>hybrid</shadedClassifierName>
+ <artifactSet>
+ <includes>
+ <include>org.eclipse.jetty:jetty-http</include>
+ <include>org.eclipse.jetty:jetty-io</include>
+ <include>org.eclipse.jetty:jetty-util</include>
+ </includes>
+ </artifactSet>
+ <relocations>
+ <relocation>
+ <pattern>org.eclipse.jetty.http</pattern>
+ <shadedPattern>org.eclipse.jetty.client.shaded.http</shadedPattern>
+ </relocation>
+ <relocation>
+ <pattern>org.eclipse.jetty.io</pattern>
+ <shadedPattern>org.eclipse.jetty.client.shaded.io</shadedPattern>
+ </relocation>
+ <relocation>
+ <pattern>org.eclipse.jetty.util</pattern>
+ <shadedPattern>org.eclipse.jetty.client.shaded.util</shadedPattern>
+ </relocation>
+ </relocations>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/AsyncHttpConnection.java b/jetty-client/src/main/java/org/eclipse/jetty/client/AsyncHttpConnection.java
index 37e39f0..0b968f8 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/AsyncHttpConnection.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/AsyncHttpConnection.java
@@ -106,6 +106,8 @@
LOG.debug("complete {}",exchange);
progress=true;
_generator.complete();
+ if (exchange.getStatus() < HttpExchange.STATUS_WAITING_FOR_RESPONSE)
+ exchange.setStatus(HttpExchange.STATUS_WAITING_FOR_RESPONSE);
}
else if (_generator.isEmpty())
{
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java
index 81998f7..6ecf242 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java
@@ -21,11 +21,14 @@
import java.io.IOException;
import java.io.InputStream;
import java.net.UnknownHostException;
+import java.util.Collection;
+import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+
import javax.net.ssl.SSLContext;
import org.eclipse.jetty.client.security.Authentication;
@@ -79,6 +82,7 @@
private int _connectorType = CONNECTOR_SELECT_CHANNEL;
private boolean _useDirectBuffers = true;
private boolean _connectBlocking = true;
+ private boolean _removeIdleDestinations=false;
private int _maxConnectionsPerAddress = Integer.MAX_VALUE;
private int _maxQueueSizePerAddress = Integer.MAX_VALUE;
private ConcurrentMap<Address, HttpDestination> _destinations = new ConcurrentHashMap<Address, HttpDestination>();
@@ -157,6 +161,18 @@
}
/* ------------------------------------------------------------------------------- */
+ public boolean isRemoveIdleDestinations()
+ {
+ return _removeIdleDestinations;
+ }
+
+ /* ------------------------------------------------------------------------------- */
+ public void setRemoveIdleDestinations(boolean removeIdleDestinations)
+ {
+ _removeIdleDestinations = removeIdleDestinations;
+ }
+
+ /* ------------------------------------------------------------------------------- */
public void send(HttpExchange exchange) throws IOException
{
boolean ssl = HttpSchemes.HTTPS_BUFFER.equalsIgnoreCase(exchange.getScheme());
@@ -262,7 +278,19 @@
}
return destination;
}
-
+
+ /* ------------------------------------------------------------------------------- */
+ public Collection<Address> getDestinations()
+ {
+ return Collections.unmodifiableCollection(_destinations.keySet());
+ }
+
+ /* ------------------------------------------------------------------------------- */
+ public void removeDestination(HttpDestination destination)
+ {
+ _destinations.remove(destination.getAddress(),destination);
+ }
+
/* ------------------------------------------------------------ */
public void schedule(Timeout.Task task)
{
@@ -908,4 +936,5 @@
private static class LocalQueuedThreadPool extends QueuedThreadPool
{
}
+
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java
index 8bbef8c..fb83307 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java
@@ -169,6 +169,15 @@
// TODO query, remove and age methods
}
+
+ public void clearCookies()
+ {
+ synchronized (this)
+ {
+ _cookies.clear();
+ }
+
+ }
/**
* Get a connection. We either get an idle connection if one is available, or
@@ -442,15 +451,20 @@
else
{
boolean startConnection = false;
+ boolean remove = false;
synchronized (this)
{
_connections.remove(connection);
- if (!_exchanges.isEmpty())
+ if (_exchanges.isEmpty())
+ remove=_client.isRemoveIdleDestinations() && ( _cookies==null || _cookies.isEmpty() ) &&_connections.isEmpty() && _idleConnections.isEmpty();
+ else if (_client.isStarted())
startConnection = true;
}
if (startConnection)
startNewConnection();
+ if (remove)
+ _client.removeDestination(this);
}
}
@@ -461,17 +475,22 @@
connection.onIdleExpired(idleForMs);
boolean startConnection = false;
+ boolean remove = false;
synchronized (this)
{
_idleConnections.remove(connection);
_connections.remove(connection);
- if (!_exchanges.isEmpty() && _client.isStarted())
+ if (_exchanges.isEmpty())
+ remove=_client.isRemoveIdleDestinations() && ( _cookies==null || _cookies.isEmpty() ) &&_connections.isEmpty() && _idleConnections.isEmpty();
+ else if (_client.isStarted())
startConnection = true;
}
if (startConnection)
startNewConnection();
+ if (remove)
+ _client.removeDestination(this);
}
public void send(HttpExchange ex) throws IOException
@@ -524,21 +543,24 @@
{
// add cookies
// TODO handle max-age etc.
- if (_cookies != null)
+ synchronized (this)
{
- StringBuilder buf = null;
- for (HttpCookie cookie : _cookies)
+ if (_cookies != null)
{
- if (buf == null)
- buf = new StringBuilder();
- else
- buf.append("; ");
- buf.append(cookie.getName()); // TODO quotes
- buf.append("=");
- buf.append(cookie.getValue()); // TODO quotes
- }
- if (buf != null)
- ex.addRequestHeader(HttpHeaders.COOKIE, buf.toString());
+ StringBuilder buf = null;
+ for (HttpCookie cookie : _cookies)
+ {
+ if (buf == null)
+ buf = new StringBuilder();
+ else
+ buf.append("; ");
+ buf.append(cookie.getName()); // TODO quotes
+ buf.append("=");
+ buf.append(cookie.getValue()); // TODO quotes
+ }
+ if (buf != null)
+ ex.addRequestHeader(HttpHeaders.COOKIE, buf.toString());
+ }
}
// Add any known authorizations
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ExpireTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ExpireTest.java
index 5676289..b0c7f59 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/ExpireTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ExpireTest.java
@@ -33,6 +33,7 @@
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.junit.After;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -58,10 +59,12 @@
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws IOException, ServletException
{
+ httpResponse.setStatus(200);
request.setHandled(true);
try
{
- Thread.sleep(2000);
+ if (request.getRequestURI().contains("/sleep"))
+ Thread.sleep(2000);
}
catch (InterruptedException x)
{
@@ -76,7 +79,6 @@
client.setTimeout(200);
client.setMaxRetries(0);
client.setMaxConnectionsPerAddress(100);
- client.start();
}
@After
@@ -90,7 +92,10 @@
@Test
public void testExpire() throws Exception
{
- String baseUrl = "http://" + "localhost" + ":" + port + "/";
+ client.setIdleTimeout(5000);
+ client.start();
+
+ String baseUrl = "http://" + "localhost" + ":" + port + "/sleep";
int count = 200;
final CountDownLatch expires = new CountDownLatch(count);
@@ -114,4 +119,41 @@
// Wait to be sure that all exchanges have expired
assertTrue(expires.await(5, TimeUnit.SECONDS));
}
+
+ @Test
+ public void testRemoveIdleDestination() throws Exception
+ {
+ client.setIdleTimeout(200);
+ client.setRemoveIdleDestinations(true);
+ client.start();
+
+ String baseUrl = "http://" + "localhost" + ":" + port + "/other";
+
+ int count = 5;
+ final CountDownLatch latch = new CountDownLatch(count);
+
+ for (int i=0;i<count;i++)
+ {
+ final ContentExchange exchange = new ContentExchange()
+ {
+ @Override
+ protected void onResponseComplete()
+ {
+ latch.countDown();
+ }
+ };
+ exchange.setMethod("GET");
+ exchange.setURL(baseUrl);
+
+ client.send(exchange);
+ }
+
+ // Wait to be sure that all exchanges have expired
+ assertTrue(latch.await(1, TimeUnit.SECONDS));
+
+ Assert.assertEquals(1,client.getDestinations().size());
+ Thread.sleep(500);
+ Assert.assertEquals(0,client.getDestinations().size());
+
+ }
}
diff --git a/jetty-continuation/pom.xml b/jetty-continuation/pom.xml
index 63f2242..b0eba22 100644
--- a/jetty-continuation/pom.xml
+++ b/jetty-continuation/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-continuation</artifactId>
@@ -25,7 +25,7 @@
</goals>
<configuration>
<instructions>
- <Import-Package>javax.servlet.*;version="[2.5,3.1)",org.mortbay.log.*;version="[6.1,7)";resolution:=optional,org.mortbay.util.ajax.*;version="[6.1,7)";resolution:=optional,*</Import-Package>
+ <Import-Package>javax.servlet.*;version="2.6.0",org.mortbay.log.*;version="[6.1,7)";resolution:=optional,org.mortbay.util.ajax.*;version="[6.1,7)";resolution:=optional,*</Import-Package>
</instructions>
</configuration>
</execution>
diff --git a/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/ContinuationSupport.java b/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/ContinuationSupport.java
index ab1ad05..52666cd 100644
--- a/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/ContinuationSupport.java
+++ b/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/ContinuationSupport.java
@@ -19,7 +19,6 @@
package org.eclipse.jetty.continuation;
import java.lang.reflect.Constructor;
-
import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestWrapper;
import javax.servlet.ServletResponse;
@@ -59,7 +58,7 @@
__servlet3=servlet3Support;
__newServlet3Continuation=s3cc;
}
-
+
boolean jetty6Support=false;
Constructor<? extends Continuation>j6cc=null;
try
@@ -80,7 +79,7 @@
__jetty6=jetty6Support;
__newJetty6Continuation=j6cc;
}
-
+
Class<?> waiting=null;
try
{
@@ -98,12 +97,12 @@
/* ------------------------------------------------------------ */
/**
* Get a Continuation. The type of the Continuation returned may
- * vary depending on the container in which the application is
+ * vary depending on the container in which the application is
* deployed. It may be an implementation native to the container (eg
* org.eclipse.jetty.server.AsyncContinuation) or one of the utility
* implementations provided such as an internal <code>FauxContinuation</code>
* or a real implementation like {@link org.eclipse.jetty.continuation.Servlet3Continuation}.
- * @param request The request
+ * @param request The request
* @return a Continuation instance
*/
public static Continuation getContinuation(ServletRequest request)
@@ -111,10 +110,10 @@
Continuation continuation = (Continuation) request.getAttribute(Continuation.ATTRIBUTE);
if (continuation!=null)
return continuation;
-
+
while (request instanceof ServletRequestWrapper)
request=((ServletRequestWrapper)request).getRequest();
-
+
if (__servlet3 )
{
try
diff --git a/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/Servlet3Continuation.java b/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/Servlet3Continuation.java
index e0dfb14..fe16119 100644
--- a/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/Servlet3Continuation.java
+++ b/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/Servlet3Continuation.java
@@ -21,7 +21,6 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
-
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
@@ -34,7 +33,7 @@
/* ------------------------------------------------------------ */
/**
* This implementation of Continuation is used by {@link ContinuationSupport}
- * when it detects that the application has been deployed in a non-jetty Servlet 3
+ * when it detects that the application has been deployed in a non-jetty Servlet 3
* server.
*/
public class Servlet3Continuation implements Continuation
@@ -42,11 +41,11 @@
// Exception reused for all continuations
// Turn on debug in ContinuationFilter to see real stack trace.
private final static ContinuationThrowable __exception = new ContinuationThrowable();
-
+
private final ServletRequest _request;
private ServletResponse _response;
private AsyncContext _context;
- private List<AsyncListener> _listeners=new ArrayList<AsyncListener>();
+ private List<AsyncListener> _listeners=new ArrayList<AsyncListener>();
private volatile boolean _initial=true;
private volatile boolean _resumed=false;
private volatile boolean _expired=false;
@@ -108,7 +107,7 @@
listener.onTimeout(Servlet3Continuation.this);
}
};
-
+
if (_context!=null)
_context.addListener(wrapped);
else
@@ -188,7 +187,7 @@
_expired=false;
_context=_request.startAsync();
_context.setTimeout(_timeoutMs);
-
+
for (AsyncListener listener:_listeners)
_context.addListener(listener);
_listeners.clear();
@@ -201,7 +200,7 @@
_expired=false;
_context=_request.startAsync();
_context.setTimeout(_timeoutMs);
-
+
for (AsyncListener listener:_listeners)
_context.addListener(listener);
_listeners.clear();
diff --git a/jetty-deploy/pom.xml b/jetty-deploy/pom.xml
index 5e2e3ac..1afd666 100644
--- a/jetty-deploy/pom.xml
+++ b/jetty-deploy/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-deploy</artifactId>
@@ -25,7 +25,7 @@
</goals>
<configuration>
<instructions>
- <Import-Package>org.eclipse.jetty.jmx.*;version="[7.3,8)";resolution:=optional,*</Import-Package>
+ <Import-Package>org.eclipse.jetty.jmx.*;version="8.0";resolution:=optional,*</Import-Package>
</instructions>
</configuration>
</execution>
diff --git a/jetty-deploy/src/main/config/etc/jetty-deploy.xml b/jetty-deploy/src/main/config/etc/jetty-deploy.xml
index dd25f7f..1b3fd66 100644
--- a/jetty-deploy/src/main/config/etc/jetty-deploy.xml
+++ b/jetty-deploy/src/main/config/etc/jetty-deploy.xml
@@ -22,7 +22,7 @@
</Set>
<Call name="setContextAttribute">
<Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
- <Arg>.*/.*jsp-api-[^/]*\.jar$|.*/.*jsp-[^/]*\.jar$|.*/.*taglibs[^/]*\.jar$</Arg>
+ <Arg>.*/servlet-api-[^/]*\.jar$</Arg>
</Call>
diff --git a/jetty-distribution/pom.xml b/jetty-distribution/pom.xml
index 18bd90f..f2fe278 100644
--- a/jetty-distribution/pom.xml
+++ b/jetty-distribution/pom.xml
@@ -3,7 +3,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<artifactId>jetty-distribution</artifactId>
<name>Jetty :: Distribution Assemblies</name>
@@ -145,7 +145,7 @@
<version>${orbit-servlet-api-version}</version>
<overWrite>true</overWrite>
<outputDirectory>${assembly-directory}/lib</outputDirectory>
- <destFileName>servlet-api-2.5.jar</destFileName>
+ <destFileName>servlet-api-3.0.jar</destFileName>
</artifactItem>
</artifactItems>
</configuration>
diff --git a/jetty-distribution/src/main/resources/bin/jetty-cygwin.sh b/jetty-distribution/src/main/resources/bin/jetty-cygwin.sh
index 550390e..16371be 100755
--- a/jetty-distribution/src/main/resources/bin/jetty-cygwin.sh
+++ b/jetty-distribution/src/main/resources/bin/jetty-cygwin.sh
@@ -5,7 +5,7 @@
# To get the service to restart correctly on reboot, uncomment below (3 lines):
# ========================
# chkconfig: 3 99 99
-# description: Jetty 7 webserver
+# description: Jetty 8 webserver
# processname: jetty
# ========================
@@ -134,8 +134,8 @@
##################################################
# See if there's a default configuration file
##################################################
-if [ -f /etc/default/jetty7 ] ; then
- . /etc/default/jetty7
+if [ -f /etc/default/jetty8 ] ; then
+ . /etc/default/jetty8
elif [ -f /etc/default/jetty ] ; then
. /etc/default/jetty
fi
@@ -196,13 +196,13 @@
/home \
"
JETTY_DIR_NAMES=" \
- jetty-7 \
- jetty7 \
- jetty-7.* \
+ jetty-8 \
+ jetty8 \
+ jetty-8.* \
jetty \
- Jetty-7 \
- Jetty7 \
- Jetty-7.* \
+ Jetty-8 \
+ Jetty8 \
+ Jetty-8.* \
Jetty \
"
@@ -511,7 +511,7 @@
echo -n "Starting Jetty: "
if [ "$NO_START" = "1" ]; then
- echo "Not starting jetty - NO_START=1 in /etc/default/jetty7";
+ echo "Not starting jetty - NO_START=1 in /etc/default/jetty8";
exit 0;
fi
diff --git a/jetty-distribution/src/main/resources/bin/jetty.sh b/jetty-distribution/src/main/resources/bin/jetty.sh
index c9e2323..baafc4d 100755
--- a/jetty-distribution/src/main/resources/bin/jetty.sh
+++ b/jetty-distribution/src/main/resources/bin/jetty.sh
@@ -5,7 +5,7 @@
# To get the service to restart correctly on reboot, uncomment below (3 lines):
# ========================
# chkconfig: 3 99 99
-# description: Jetty 7 webserver
+# description: Jetty 8 webserver
# processname: jetty
# ========================
@@ -161,7 +161,7 @@
ETC=$HOME/etc
fi
-for CONFIG in $ETC/default/jetty{,7} $HOME/.jettyrc; do
+for CONFIG in $ETC/default/jetty{,8} $HOME/.jettyrc; do
if [ -f "$CONFIG" ] ; then
readConfig "$CONFIG"
fi
@@ -217,13 +217,13 @@
"/home"
)
JETTY_DIR_NAMES=(
- "jetty-7"
- "jetty7"
- "jetty-7.*"
+ "jetty-8"
+ "jetty8"
+ "jetty-8.*"
"jetty"
- "Jetty-7"
- "Jetty7"
- "Jetty-7.*"
+ "Jetty-8"
+ "Jetty8"
+ "Jetty-8.*"
"Jetty"
)
diff --git a/jetty-distribution/src/main/resources/start.ini b/jetty-distribution/src/main/resources/start.ini
index 0ada4c0..dfc2783 100644
--- a/jetty-distribution/src/main/resources/start.ini
+++ b/jetty-distribution/src/main/resources/start.ini
@@ -46,7 +46,7 @@
# for a full listing do
# java -jar start.jar --list-options
#-----------------------------------------------------------
-OPTIONS=Server,jsp,jmx,resources,websocket,ext
+OPTIONS=Server,jsp,jmx,resources,websocket,ext,plus,annotations
#-----------------------------------------------------------
@@ -57,6 +57,7 @@
#-----------------------------------------------------------
#etc/jetty-jmx.xml
etc/jetty.xml
+etc/jetty-annotations.xml
# etc/jetty-ssl.xml
# etc/jetty-requestlog.xml
etc/jetty-deploy.xml
diff --git a/jetty-http-spi/pom.xml b/jetty-http-spi/pom.xml
index 5c6d2ec..f466790 100644
--- a/jetty-http-spi/pom.xml
+++ b/jetty-http-spi/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-http-spi</artifactId>
diff --git a/jetty-http/pom.xml b/jetty-http/pom.xml
index 02e927e..7fe0716 100644
--- a/jetty-http/pom.xml
+++ b/jetty-http/pom.xml
@@ -3,7 +3,7 @@
<parent>
<artifactId>jetty-project</artifactId>
<groupId>org.eclipse.jetty</groupId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-http</artifactId>
@@ -42,7 +42,7 @@
</goals>
<configuration>
<instructions>
- <Import-Package>javax.net.*,*</Import-Package>
+ <Import-Package>javax.servlet.*;version="2.6.0",javax.net.*,*</Import-Package>
</instructions>
</configuration>
</execution>
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/AbstractGenerator.java b/jetty-http/src/main/java/org/eclipse/jetty/http/AbstractGenerator.java
index c615b18..3922410 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/AbstractGenerator.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/AbstractGenerator.java
@@ -444,8 +444,10 @@
{
flushBuffer();
- while (now<end && (content!=null && content.length()>0 ||buffer!=null && buffer.length()>0) && _endp.isOpen()&& !_endp.isOutputShutdown())
+ while (now<end && (content!=null && content.length()>0 ||buffer!=null && buffer.length()>0))
{
+ if (!_endp.isOpen() || _endp.isOutputShutdown())
+ throw new EofException();
blockForOutput(end-now);
now=System.currentTimeMillis();
}
@@ -480,6 +482,11 @@
completeHeader(null, false);
addContent(new View(new ByteArrayBuffer(content)), Generator.LAST);
}
+ else if (code>=400)
+ {
+ completeHeader(null, false);
+ addContent(new View(new ByteArrayBuffer("Error: "+(reason==null?(""+code):reason))), Generator.LAST);
+ }
else
{
completeHeader(null, true);
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java
index a46ef08..d915582 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java
@@ -24,10 +24,12 @@
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
+import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.GregorianCalendar;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -341,6 +343,22 @@
/* -------------------------------------------------------------- */
/**
+ * Get Collection of header names.
+ */
+ public Collection<String> getFieldNamesCollection()
+ {
+ final List<String> list = new ArrayList<String>(_fields.size());
+
+ for (Field f : _fields)
+ {
+ if (f!=null)
+ list.add(BufferUtil.to8859_1_String(f._name));
+ }
+ return list;
+ }
+
+ /* -------------------------------------------------------------- */
+ /**
* Get enumeration of header _names. Returns an enumeration of strings representing the header
* _names for this request.
*/
@@ -371,7 +389,7 @@
/**
* Get a Field by index.
* @return A Field value or null if the Field value has not been set
- * for this revision of the fields.
+ *
*/
public Field getField(int i)
{
@@ -438,6 +456,30 @@
return field==null?null:field._value;
}
+
+ /* -------------------------------------------------------------- */
+ /**
+ * Get multi headers
+ *
+ * @return Enumeration of the values, or null if no such header.
+ * @param name the case-insensitive field name
+ */
+ public Collection<String> getValuesCollection(String name)
+ {
+ Field field = getField(name);
+ if (field==null)
+ return null;
+
+ final List<String> list = new ArrayList<String>();
+
+ while(field!=null)
+ {
+ list.add(field.getValue());
+ field=field._next;
+ }
+ return list;
+ }
+
/* -------------------------------------------------------------- */
/**
* Get multi headers
@@ -1361,5 +1403,4 @@
return ("[" + getName() + "=" + _value + (_next == null ? "" : "->") + "]");
}
}
-
}
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java
index e38dcb7..ad7494e 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java
@@ -660,8 +660,11 @@
// written yet?
// Response known not to have a body
- if (_contentWritten == 0 && isResponse() && (_status < 200 || _status == 204 || _status == 304))
+ if (isResponse() && _noContent)
+ {
_contentLength = HttpTokens.NO_CONTENT;
+ _contentWritten=0;
+ }
else if (_last)
{
// we have seen all the _content there is
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java
index bc7725d..1effdad 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java
@@ -1032,7 +1032,7 @@
{
LOG.warn("HttpParser Full for {} ",_endp);
_buffer.clear();
- throw new HttpException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413, "FULL "+(_buffer==_body?"body":"head"));
+ throw new HttpException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413, "Request Entity Too Large: "+(_buffer==_body?"body":"head"));
}
try
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/PathMap.java b/jetty-http/src/main/java/org/eclipse/jetty/http/PathMap.java
index 01b0397..50c67dc 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/PathMap.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/PathMap.java
@@ -151,7 +151,16 @@
@Override
public Object put(Object pathSpec, Object object)
{
- StringTokenizer tok = new StringTokenizer(pathSpec.toString(),__pathSpecSeparators);
+ String str = pathSpec.toString();
+ if ("".equals(str.trim()))
+ {
+ Entry entry = new Entry("",object);
+ entry.setMapped("");
+ _exactMap.put("", entry);
+ return super.put("", object);
+ }
+
+ StringTokenizer tok = new StringTokenizer(str,__pathSpecSeparators);
Object old =null;
while (tok.hasMoreTokens())
@@ -223,13 +232,21 @@
*/
public Entry getMatch(String path)
{
- Map.Entry entry;
+ Map.Entry entry=null;
if (path==null)
return null;
int l=path.length();
-
+
+ //special case
+ if (l == 1 && path.charAt(0)=='/')
+ {
+ entry = (Map.Entry)_exactMap.get("");
+ if (entry != null)
+ return (Entry)entry;
+ }
+
// try exact match
entry=_exactMap.getEntry(path,0,l);
if (entry!=null)
@@ -397,6 +414,9 @@
public static boolean match(String pathSpec, String path, boolean noDefault)
throws IllegalArgumentException
{
+ if (pathSpec.length()==0)
+ return "/".equals(path);
+
char c = pathSpec.charAt(0);
if (c=='/')
{
@@ -460,6 +480,9 @@
*/
public static String pathInfo(String pathSpec, String path)
{
+ if ("".equals(pathSpec))
+ return path; //servlet 3 spec sec 12.2 will be '/'
+
char c = pathSpec.charAt(0);
if (c=='/')
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorClientTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorClientTest.java
index 4497f42..2f84911 100644
--- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorClientTest.java
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorClientTest.java
@@ -18,9 +18,6 @@
package org.eclipse.jetty.http;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
@@ -33,6 +30,9 @@
import org.eclipse.jetty.io.View;
import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
public class HttpGeneratorClientTest
{
public final static String CONTENT="The quick brown fox jumped over the lazy dog.\nNow is the time for all good men to come to the aid of the party\nThe moon is blue to a fish in love.\n";
@@ -125,7 +125,7 @@
* screw up the chunking by leaving out the second chunk header.
*/
@Test
- public void testChunkedWithBackPressure() throws Exception
+ public void testChunkedWithBackPressure() throws Exception
{
final AtomicInteger availableChannelBytes = new AtomicInteger(500);
ByteArrayEndPoint endp = new ByteArrayEndPoint(new byte[0],4096)
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java
index 530849d..721f702 100644
--- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java
@@ -444,7 +444,7 @@
{
if (t+1 < tests.length)
throw e;
- assertTrue(e.toString().indexOf("FULL")>=0);
+ assertTrue(e.toString().indexOf("Request Entity Too Large")>=0);
}
}
}
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/PathMapTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/PathMapTest.java
index 4cae3f1..887662e 100644
--- a/jetty-http/src/test/java/org/eclipse/jetty/http/PathMapTest.java
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/PathMapTest.java
@@ -41,6 +41,7 @@
p.put("*.gz", "7");
p.put("/", "8");
p.put("/XXX:/YYY", "9");
+ p.put("", "10");
String[][] tests = {
{ "/abs/path", "1"},
@@ -73,7 +74,7 @@
assertEquals("Dir matches", "[/animal/fish/*=4, /animal/*=5, /=8]", p.getMatches("/animal/fish/").toString());
assertEquals("Dir matches", "[/animal/fish/*=4, /animal/*=5, /=8]", p.getMatches("/animal/fish").toString());
assertEquals("Dir matches", "[/=8]", p.getMatches("/").toString());
- assertEquals("Dir matches", "[/=8]", p.getMatches("").toString());
+ assertEquals("Dir matches", "[=10, /=8]", p.getMatches("").toString());
assertEquals("pathMatch exact", "/Foo/bar", PathMap.pathMatch("/Foo/bar", "/Foo/bar"));
assertEquals("pathMatch prefix", "/Foo", PathMap.pathMatch("/Foo/*", "/Foo/bar"));
@@ -130,6 +131,10 @@
assertTrue("!match /foo/*", !PathMap.match("/foo/*", "/bar/anything"));
assertTrue("match *.foo", PathMap.match("*.foo", "anything.foo"));
assertTrue("!match *.foo", !PathMap.match("*.foo", "anything.bar"));
+
+ assertEquals("match / with ''", "10", p.getMatch("/").getValue());
+
+ assertTrue("match \"\"", PathMap.match("", "/"));
}
/**
diff --git a/jetty-io/pom.xml b/jetty-io/pom.xml
index 17ec0a1..5d018e6 100644
--- a/jetty-io/pom.xml
+++ b/jetty-io/pom.xml
@@ -2,7 +2,7 @@
<parent>
<artifactId>jetty-project</artifactId>
<groupId>org.eclipse.jetty</groupId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-io</artifactId>
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractBuffer.java b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractBuffer.java
index ea0cc10..0569e1b 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractBuffer.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractBuffer.java
@@ -21,6 +21,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.charset.Charset;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.log.Log;
@@ -505,43 +506,37 @@
public void setGetIndex(int getIndex)
{
- /* bounds checking */
- if (__boundsChecking)
- {
- if (isImmutable())
- throw new IllegalStateException(__IMMUTABLE);
- if (getIndex < 0)
- throw new IllegalArgumentException("getIndex<0: " + getIndex + "<0");
- if (getIndex > putIndex())
- throw new IllegalArgumentException("getIndex>putIndex: " + getIndex + ">" + putIndex());
- }
-
+ /* bounds checking
+ if (isImmutable())
+ throw new IllegalStateException(__IMMUTABLE);
+ if (getIndex < 0)
+ throw new IllegalArgumentException("getIndex<0: " + getIndex + "<0");
+ if (getIndex > putIndex())
+ throw new IllegalArgumentException("getIndex>putIndex: " + getIndex + ">" + putIndex());
+ */
_get = getIndex;
_hash=0;
}
public void setMarkIndex(int index)
{
-
+ /*
if (index>=0 && isImmutable())
throw new IllegalStateException(__IMMUTABLE);
-
+ */
_mark = index;
}
public void setPutIndex(int putIndex)
{
- if (__boundsChecking)
- {
- /* bounds checking */
- if (isImmutable())
- throw new IllegalStateException(__IMMUTABLE);
- if (putIndex > capacity())
+ /* bounds checking
+ if (isImmutable())
+ throw new IllegalStateException(__IMMUTABLE);
+ if (putIndex > capacity())
throw new IllegalArgumentException("putIndex>capacity: " + putIndex + ">" + capacity());
- if (getIndex() > putIndex)
+ if (getIndex() > putIndex)
throw new IllegalArgumentException("getIndex>putIndex: " + getIndex() + ">" + putIndex);
- }
-
+ */
_put = putIndex;
_hash=0;
}
@@ -651,6 +646,23 @@
}
/* ------------------------------------------------------------ */
+ public String toString(Charset charset)
+ {
+ try
+ {
+ byte[] bytes=array();
+ if (bytes!=null)
+ return new String(bytes,getIndex(),length(),charset);
+ return new String(asArray(), 0, length(),charset);
+ }
+ catch(Exception e)
+ {
+ LOG.warn(e);
+ return new String(asArray(), 0, length());
+ }
+ }
+
+ /* ------------------------------------------------------------ */
public String toDebugString()
{
return getClass()+"@"+super.hashCode();
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/Buffer.java b/jetty-io/src/main/java/org/eclipse/jetty/io/Buffer.java
index 8f0ac59..5bb42ef 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/Buffer.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/Buffer.java
@@ -21,6 +21,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.charset.Charset;
/**
@@ -365,7 +366,10 @@
/* ------------------------------------------------------------ */
String toString(String charset);
- /*
+ /* ------------------------------------------------------------ */
+ String toString(Charset charset);
+
+ /*
* Buffers implementing this interface should be compared with case insensitive equals
*
*/
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/BufferUtil.java b/jetty-io/src/main/java/org/eclipse/jetty/io/BufferUtil.java
index e08e508..c778b62 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/BufferUtil.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/BufferUtil.java
@@ -354,6 +354,6 @@
{
if (buffer instanceof CachedBuffer)
return buffer.toString();
- return buffer.toString(StringUtil.__ISO_8859_1);
+ return buffer.toString(StringUtil.__ISO_8859_1_CHARSET);
}
}
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/UncheckedIOException.java b/jetty-io/src/main/java/org/eclipse/jetty/io/UncheckedIOException.java
new file mode 100644
index 0000000..47b41ba
--- /dev/null
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/UncheckedIOException.java
@@ -0,0 +1,48 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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;
+
+/* ------------------------------------------------------------ */
+/**
+ * Subclass of {@link java.lang.RuntimeException} used to signal that there
+ * was an {@link java.io.IOException} thrown by underlying {@link UncheckedPrintWriter}
+ */
+public class UncheckedIOException extends RuntimeException
+{
+ public UncheckedIOException()
+ {
+ super();
+ }
+
+ public UncheckedIOException(String message)
+ {
+ super(message);
+ }
+
+ public UncheckedIOException(Throwable cause)
+ {
+ super(cause);
+ }
+
+ public UncheckedIOException(String message, Throwable cause)
+ {
+ super(message,cause);
+ }
+}
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectChannelEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectChannelEndPoint.java
index 4b9771b..07e82c2 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectChannelEndPoint.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectChannelEndPoint.java
@@ -19,6 +19,7 @@
package org.eclipse.jetty.io.nio;
import java.io.IOException;
+import java.io.InterruptedIOException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
@@ -86,6 +87,8 @@
private volatile long _idleTimestamp;
private volatile boolean _checkIdle;
+
+ private boolean _interruptable;
private boolean _ishut;
@@ -445,9 +448,11 @@
updateKey();
this.wait(timeoutMs>0?(end-now):10000);
}
- catch (InterruptedException e)
+ catch (final InterruptedException e)
{
LOG.warn(e);
+ if (_interruptable)
+ throw new InterruptedIOException(){{this.initCause(e);}};
}
finally
{
@@ -493,9 +498,11 @@
updateKey();
this.wait(timeoutMs>0?(end-now):10000);
}
- catch (InterruptedException e)
+ catch (final InterruptedException e)
{
LOG.warn(e);
+ if (_interruptable)
+ throw new InterruptedIOException(){{this.initCause(e);}};
}
finally
{
@@ -515,6 +522,28 @@
}
/* ------------------------------------------------------------ */
+ /** Set the interruptable mode of the endpoint.
+ * If set to false (default), then interrupts are assumed to be spurious
+ * and blocking operations continue unless the endpoint has been closed.
+ * If true, then interrupts of blocking operations result in InterruptedIOExceptions
+ * being thrown.
+ * @param interupable
+ */
+ public void setInterruptable(boolean interupable)
+ {
+ synchronized (this)
+ {
+ _interruptable=interupable;
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ public boolean isInterruptable()
+ {
+ return _interruptable;
+ }
+
+ /* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.io.AsyncEndPoint#scheduleWrite()
*/
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SslConnection.java
index 42c9dab..c72d26b 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SslConnection.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SslConnection.java
@@ -330,11 +330,11 @@
try
{
// Read any available data
- if (_inbound.space()>0 && (filled=_endp.fill(_inbound))>0)
+ if (_inbound.space() > 0 && (filled = _endp.fill(_inbound)) > 0)
progress = true;
// flush any output data
- if (_outbound.hasContent() && (flushed=_endp.flush(_outbound))>0)
+ if (_outbound.hasContent() && (flushed=_endp.flush(_outbound)) > 0)
progress = true;
}
catch (IOException e)
@@ -444,7 +444,8 @@
{
ByteBuffer bbuf=extractByteBuffer(buffer);
final SSLEngineResult result;
-
+ int encrypted_produced = 0;
+ int decrypted_consumed = 0;
synchronized(bbuf)
{
_outbound.compact();
@@ -455,8 +456,12 @@
{
bbuf.position(buffer.getIndex());
bbuf.limit(buffer.putIndex());
+ int decrypted_position = bbuf.position();
+
out_buffer.position(_outbound.putIndex());
out_buffer.limit(out_buffer.capacity());
+ int encrypted_position = out_buffer.position();
+
result=_engine.wrap(bbuf,out_buffer);
if (_logger.isDebugEnabled())
_logger.debug("{} wrap {} {} consumed={} produced={}",
@@ -466,9 +471,11 @@
result.bytesConsumed(),
result.bytesProduced());
+ decrypted_consumed = bbuf.position() - decrypted_position;
+ buffer.skip(decrypted_consumed);
- buffer.skip(result.bytesConsumed());
- _outbound.setPutIndex(_outbound.putIndex()+result.bytesProduced());
+ encrypted_produced = out_buffer.position() - encrypted_position;
+ _outbound.setPutIndex(_outbound.putIndex() + encrypted_produced);
}
catch(SSLException e)
{
@@ -476,6 +483,14 @@
_endp.close();
throw e;
}
+ catch (IOException x)
+ {
+ throw x;
+ }
+ catch (Exception x)
+ {
+ throw new IOException(x);
+ }
finally
{
out_buffer.position(0);
@@ -510,7 +525,7 @@
throw new IOException(result.toString());
}
- return result.bytesConsumed()>0 || result.bytesProduced()>0;
+ return decrypted_consumed > 0 || encrypted_produced > 0;
}
private synchronized boolean unwrap(final Buffer buffer) throws IOException
@@ -520,7 +535,8 @@
ByteBuffer bbuf=extractByteBuffer(buffer);
final SSLEngineResult result;
-
+ int encrypted_consumed = 0;
+ int decrypted_produced = 0;
synchronized(bbuf)
{
ByteBuffer in_buffer=_inbound.getByteBuffer();
@@ -530,8 +546,11 @@
{
bbuf.position(buffer.putIndex());
bbuf.limit(buffer.capacity());
+ int decrypted_position = bbuf.position();
+
in_buffer.position(_inbound.getIndex());
in_buffer.limit(_inbound.putIndex());
+ int encrypted_position = in_buffer.position();
result=_engine.unwrap(in_buffer,bbuf);
if (_logger.isDebugEnabled())
@@ -542,9 +561,12 @@
result.bytesConsumed(),
result.bytesProduced());
- _inbound.skip(result.bytesConsumed());
+ encrypted_consumed = in_buffer.position() - encrypted_position;
+ _inbound.skip(encrypted_consumed);
_inbound.compact();
- buffer.setPutIndex(buffer.putIndex()+result.bytesProduced());
+
+ decrypted_produced = bbuf.position() - decrypted_position;
+ buffer.setPutIndex(buffer.putIndex() + decrypted_produced);
}
catch(SSLException e)
{
@@ -552,6 +574,14 @@
_endp.close();
throw e;
}
+ catch (IOException x)
+ {
+ throw x;
+ }
+ catch (Exception x)
+ {
+ throw new IOException(x);
+ }
finally
{
in_buffer.position(0);
@@ -592,7 +622,7 @@
//if (LOG.isDebugEnabled() && result.bytesProduced()>0)
// LOG.debug("{} unwrapped '{}'",_session,buffer);
- return result.bytesConsumed()>0 || result.bytesProduced()>0;
+ return encrypted_consumed > 0 || decrypted_produced > 0;
}
@@ -634,9 +664,16 @@
{
synchronized (SslConnection.this)
{
- _logger.debug("{} ssl endp.oshut {}",_session,this);
- _engine.closeOutbound();
- _oshut=true;
+ try
+ {
+ _logger.debug("{} ssl endp.oshut {}",_session,this);
+ _oshut=true;
+ _engine.closeOutbound();
+ }
+ catch (Exception x)
+ {
+ throw new IOException(x);
+ }
}
flush();
}
diff --git a/jetty-jaspi/pom.xml b/jetty-jaspi/pom.xml
index 05f58e0..e91985b 100644
--- a/jetty-jaspi/pom.xml
+++ b/jetty-jaspi/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-jaspi</artifactId>
@@ -23,6 +23,12 @@
<goals>
<goal>manifest</goal>
</goals>
+ <configuration>
+ <instructions>
+ <Import-Package>javax.servlet.*;version="2.6.0",*</Import-Package>
+ <Export-Package>org.eclipse.jetty.security.jaspi.*;version="${parsedVersion.osgiVersion}"</Export-Package>
+ </instructions>
+ </configuration>
</execution>
</executions>
</plugin>
diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticator.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticator.java
index bcb88be..ba43830 100644
--- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticator.java
+++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/JaspiAuthenticator.java
@@ -31,6 +31,8 @@
import javax.security.auth.message.config.ServerAuthContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
import org.eclipse.jetty.security.Authenticator;
import org.eclipse.jetty.security.IdentityService;
@@ -38,15 +40,21 @@
import org.eclipse.jetty.security.UserAuthentication;
import org.eclipse.jetty.security.authentication.DeferredAuthentication;
import org.eclipse.jetty.security.authentication.LoginAuthenticator;
+import org.eclipse.jetty.security.authentication.SessionAuthentication;
+import org.eclipse.jetty.security.jaspi.modules.BaseAuthModule;
import org.eclipse.jetty.server.Authentication;
import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.server.Authentication.User;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
/**
* @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $
*/
public class JaspiAuthenticator extends LoginAuthenticator
{
+ private static final Logger LOG = Log.getLogger(JaspiAuthenticator.class.getName());
+
private final ServerAuthConfig _authConfig;
private final Map _authProperties;
@@ -107,6 +115,28 @@
}
+ /**
+ * @see org.eclipse.jetty.security.authentication.LoginAuthenticator#login(java.lang.String, java.lang.Object, javax.servlet.ServletRequest)
+ */
+ @Override
+ public UserIdentity login(String username, Object password, ServletRequest request)
+ {
+ UserIdentity user = _loginService.login(username, password);
+ if (user != null)
+ {
+ renewSession((HttpServletRequest)request, null);
+ HttpSession session = ((HttpServletRequest)request).getSession(true);
+ if (session != null)
+ {
+ SessionAuthentication sessionAuth = new SessionAuthentication(getAuthMethod(), user, password);
+ session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, sessionAuth);
+ }
+ }
+ return user;
+ }
+
+
+
public Authentication validateRequest(JaspiMessageInfo messageInfo) throws ServerAuthException
{
try
@@ -151,6 +181,12 @@
String[] groups = groupPrincipalCallback == null ? null : groupPrincipalCallback.getGroups();
userIdentity = _identityService.newUserIdentity(clientSubject, principal, groups);
}
+
+ HttpSession session = ((HttpServletRequest)messageInfo.getRequestMessage()).getSession(false);
+ Authentication cached = (session == null?null:(SessionAuthentication)session.getAttribute(SessionAuthentication.__J_AUTHENTICATED));
+ if (cached != null)
+ return cached;
+
return new UserAuthentication(getAuthMethod(), userIdentity);
}
if (authStatus == AuthStatus.SEND_SUCCESS)
diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/SimpleAuthConfig.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/SimpleAuthConfig.java
index d6897b8..204a282 100644
--- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/SimpleAuthConfig.java
+++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/SimpleAuthConfig.java
@@ -70,7 +70,7 @@
return true;
}
- public void refresh()
+ public void refresh()
{
}
}
diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/FormAuthModule.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/FormAuthModule.java
index af2be3c..9de5370 100644
--- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/FormAuthModule.java
+++ b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/FormAuthModule.java
@@ -43,7 +43,10 @@
import org.eclipse.jetty.security.CrossContextPsuedoSession;
import org.eclipse.jetty.security.authentication.DeferredAuthentication;
import org.eclipse.jetty.security.authentication.LoginCallbackImpl;
+import org.eclipse.jetty.security.authentication.SessionAuthentication;
+import org.eclipse.jetty.security.jaspi.callback.CredentialValidationCallback;
import org.eclipse.jetty.server.Authentication;
+import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.log.Log;
@@ -214,21 +217,22 @@
// Check if the session is already authenticated.
- FormCredential form_cred = (FormCredential) session.getAttribute(__J_AUTHENTICATED);
- if (form_cred != null)
+ SessionAuthentication sessionAuth = (SessionAuthentication)session.getAttribute(SessionAuthentication.__J_AUTHENTICATED);
+ if (sessionAuth != null)
{
//TODO: ideally we would like the form auth module to be able to invoke the
//loginservice.validate() method to check the previously authed user, but it is not visible
//to FormAuthModule
- if (form_cred._subject == null)
+ if (sessionAuth.getUserIdentity().getSubject() == null)
return AuthStatus.SEND_FAILURE;
- Set<Object> credentials = form_cred._subject.getPrivateCredentials();
+
+ Set<Object> credentials = sessionAuth.getUserIdentity().getSubject().getPrivateCredentials();
if (credentials == null || credentials.isEmpty())
return AuthStatus.SEND_FAILURE; //if no private credentials, assume it cannot be authenticated
clientSubject.getPrivateCredentials().addAll(credentials);
+ clientSubject.getPrivateCredentials().add(sessionAuth.getUserIdentity());
- //boolean success = tryLogin(messageInfo, clientSubject, response, session, form_cred._jUserName, new Password(new String(form_cred._jPassword)));
return AuthStatus.SUCCESS;
}
else if (ssoSource != null)
@@ -300,8 +304,14 @@
if (!loginCallbacks.isEmpty())
{
LoginCallbackImpl loginCallback = loginCallbacks.iterator().next();
- FormCredential form_cred = new FormCredential(username, pwdChars, loginCallback.getUserPrincipal(), loginCallback.getSubject());
- session.setAttribute(__J_AUTHENTICATED, form_cred);
+ Set<UserIdentity> userIdentities = clientSubject.getPrivateCredentials(UserIdentity.class);
+ if (!userIdentities.isEmpty())
+ {
+ UserIdentity userIdentity = userIdentities.iterator().next();
+
+ SessionAuthentication sessionAuth = new SessionAuthentication(Constraint.__FORM_AUTH, userIdentity, password);
+ session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, sessionAuth);
+ }
}
// Sign-on to SSO mechanism
@@ -320,61 +330,4 @@
return pathInContext != null && (pathInContext.equals(_formErrorPath) || pathInContext.equals(_formLoginPath));
}
- /* ------------------------------------------------------------ */
- /**
- * FORM Authentication credential holder.
- */
- private static class FormCredential implements Serializable, HttpSessionBindingListener
- {
- String _jUserName;
-
- char[] _jPassword;
-
- transient Principal _userPrincipal;
-
- transient Subject _subject;
-
- private FormCredential(String _jUserName, char[] _jPassword, Principal _userPrincipal, Subject subject)
- {
- this._jUserName = _jUserName;
- this._jPassword = _jPassword;
- this._userPrincipal = _userPrincipal;
- this._subject = subject;
- }
-
- public void valueBound(HttpSessionBindingEvent event)
- {
- }
-
- public void valueUnbound(HttpSessionBindingEvent event)
- {
- if (LOG.isDebugEnabled()) LOG.debug("Logout " + _jUserName);
-
- // TODO jaspi call cleanSubject()
- // if (_realm instanceof SSORealm)
- // ((SSORealm) _realm).clearSingleSignOn(_jUserName);
- //
- // if (_realm != null && _userPrincipal != null)
- // _realm.logout(_userPrincipal);
- }
-
- public int hashCode()
- {
- return _jUserName.hashCode() + _jPassword.hashCode();
- }
-
- public boolean equals(Object o)
- {
- if (!(o instanceof FormCredential)) return false;
- FormCredential fc = (FormCredential) o;
- return _jUserName.equals(fc._jUserName) && Arrays.equals(_jPassword, fc._jPassword);
- }
-
- public String toString()
- {
- return "Cred[" + _jUserName + "]";
- }
-
- }
-
}
diff --git a/jetty-jmx/pom.xml b/jetty-jmx/pom.xml
index 8d9c2db..adc9675 100644
--- a/jetty-jmx/pom.xml
+++ b/jetty-jmx/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-jmx</artifactId>
diff --git a/jetty-jndi/pom.xml b/jetty-jndi/pom.xml
index 3bd2171..f634bab 100644
--- a/jetty-jndi/pom.xml
+++ b/jetty-jndi/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-jndi</artifactId>
diff --git a/jetty-jsp/pom.xml b/jetty-jsp/pom.xml
index 1c79e3d..3fc9a10 100644
--- a/jetty-jsp/pom.xml
+++ b/jetty-jsp/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-jsp</artifactId>
@@ -17,13 +17,13 @@
<dependency>
<groupId>org.eclipse.jetty.orbit</groupId>
<artifactId>javax.servlet.jsp</artifactId>
- <version>2.1.0.v201105211820</version>
+ <version>2.2.0.v201112011158</version>
</dependency>
<!-- JSP Impl -->
<dependency>
<groupId>org.eclipse.jetty.orbit</groupId>
<artifactId>org.apache.jasper.glassfish</artifactId>
- <version>2.1.0.v201110031002</version>
+ <version>2.2.2.v201112011158</version>
</dependency>
<!-- JSTL Api -->
<dependency>
@@ -41,13 +41,13 @@
<dependency>
<groupId>org.eclipse.jetty.orbit</groupId>
<artifactId>javax.el</artifactId>
- <version>2.1.0.v201105211819</version>
+ <version>2.2.0.v201108011116</version>
</dependency>
<!-- EL Impl -->
<dependency>
<groupId>org.eclipse.jetty.orbit</groupId>
<artifactId>com.sun.el</artifactId>
- <version>1.0.0.v201105211818</version>
+ <version>2.2.0.v201108011116</version>
</dependency>
<!-- Eclipse Java Compiler (for JSP Compilation) -->
<dependency>
diff --git a/jetty-monitor/pom.xml b/jetty-monitor/pom.xml
index 69164a9..299c9fd 100644
--- a/jetty-monitor/pom.xml
+++ b/jetty-monitor/pom.xml
@@ -19,7 +19,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-monitor</artifactId>
diff --git a/jetty-nested/pom.xml b/jetty-nested/pom.xml
index d74e1dc..644ea0f 100644
--- a/jetty-nested/pom.xml
+++ b/jetty-nested/pom.xml
@@ -4,7 +4,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<artifactId>jetty-nested</artifactId>
<name>Jetty :: Nested</name>
@@ -27,7 +27,7 @@
</goals>
<configuration>
<instructions>
- <Import-Package>javax.servlet*;version="2.5.0",*</Import-Package>
+ <Import-Package>javax.servlet.*;version="2.6.0",*</Import-Package>
</instructions>
</configuration>
</execution>
diff --git a/jetty-nested/src/main/java/org/eclipse/jetty/nested/NestedConnection.java b/jetty-nested/src/main/java/org/eclipse/jetty/nested/NestedConnection.java
index 345b9ab..69a66c7 100644
--- a/jetty-nested/src/main/java/org/eclipse/jetty/nested/NestedConnection.java
+++ b/jetty-nested/src/main/java/org/eclipse/jetty/nested/NestedConnection.java
@@ -21,6 +21,7 @@
import java.io.IOException;
import java.util.Enumeration;
+import javax.servlet.DispatcherType;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
@@ -30,7 +31,6 @@
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.server.AbstractHttpConnection;
-import org.eclipse.jetty.server.DispatcherType;
public class NestedConnection extends AbstractHttpConnection
diff --git a/jetty-nosql/pom.xml b/jetty-nosql/pom.xml
index da6db62..13cbdbc 100644
--- a/jetty-nosql/pom.xml
+++ b/jetty-nosql/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-nosql</artifactId>
@@ -29,7 +29,7 @@
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
- <Import-Package>javax.servlet.*;version="[2.5,3.0)",org.eclipse.jetty.server.session.jmx;version="[7.5,8)";resolution:=optional,,org.eclipse.jetty.*;version="[7.5,8)",*</Import-Package>
+ <Import-Package>javax.servlet.*;version="2.6.0",org.eclipse.jetty.server.session.jmx;version="8.0.0";resolution:=optional,,org.eclipse.jetty.*;version="8.0.0",*</Import-Package>
</instructions>
</configuration>
<extensions>true</extensions>
diff --git a/jetty-osgi/jetty-osgi-boot-jsp/pom.xml b/jetty-osgi/jetty-osgi-boot-jsp/pom.xml
index fce28ce..2153ac8 100644
--- a/jetty-osgi/jetty-osgi-boot-jsp/pom.xml
+++ b/jetty-osgi/jetty-osgi-boot-jsp/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -94,28 +94,27 @@
com.sun.el.lang;resolution:=optional,
com.sun.el.parser;resolution:=optional,
com.sun.el.util;resolution:=optional,
- com.sun.org.apache.commons.logging;split=glassfish;version="[2.1,3)";resolution:=optional,
- javax.el;version="1.0.0";resolution:=optional,
- javax.servlet;version="2.5.0",
- javax.servlet.jsp;version="2.1.0",
- javax.servlet.jsp.el;version="2.1.0",
+ javax.el;version="2.2.0";resolution:=optional,
+ javax.servlet;version="2.6.0",
+ javax.servlet.jsp;version="2.2.0",
+ javax.servlet.jsp.el;version="2.2.0",
javax.servlet.jsp.jstl.core;version="1.2.0";resolution:=optional,
javax.servlet.jsp.jstl.fmt;version="1.2.0";resolution:=optional,
javax.servlet.jsp.jstl.sql;version="1.2.0";resolution:=optional,
javax.servlet.jsp.jstl.tlv;version="1.2.0";resolution:=optional,
- javax.servlet.jsp.resources;version="2.1.0",
- javax.servlet.jsp.tagext;version="2.1.0",
- javax.servlet.resources;version="2.5.0",
- org.apache.jasper;version="6.0.0";resolution:=optional,
- org.apache.jasper.compiler;version="6.0.0";resolution:=optional,
- org.apache.jasper.compiler.tagplugin;version="6.0.0";resolution:=optional,
- org.apache.jasper.runtime;version="6.0.0";resolution:=optional,
- org.apache.jasper.security;version="6.0.0";resolution:=optional,
- org.apache.jasper.servlet;version="6.0.0";resolution:=optional,
- org.apache.jasper.tagplugins.jstl;version="6.0.0";resolution:=optional,
- org.apache.jasper.util;version="6.0.0";resolution:=optional,
- org.apache.jasper.xmlparser;version="6.0.0";resolution:=optional,
- org.glassfish.jsp.api;version="2.1.3";resolution:=optional,
+ javax.servlet.jsp.resources;version="2.2.0",
+ javax.servlet.jsp.tagext;version="2.2.0",
+ javax.servlet.resources;version="2.6.0",
+ org.apache.jasper;version="2.2.2";resolution:=optional,
+ org.apache.jasper.compiler;version="2.2.2";resolution:=optional,
+ org.apache.jasper.compiler.tagplugin;version="2.2.2";resolution:=optional,
+ org.apache.jasper.runtime;version="2.2.2";resolution:=optional,
+ org.apache.jasper.security;version="2.2.2";resolution:=optional,
+ org.apache.jasper.servlet;version="2.2.2";resolution:=optional,
+ org.apache.jasper.tagplugins.jstl;version="2.2.2";resolution:=optional,
+ org.apache.jasper.util;version="2.2.2";resolution:=optional,
+ org.apache.jasper.xmlparser;version="2.2.2";resolution:=optional,
+ org.glassfish.jsp.api;version="2.2.2";resolution:=optional,
org.apache.taglibs.standard;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.extra.spath;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.functions;version="1.2.0";resolution:=optional,
@@ -139,13 +138,13 @@
org.apache.taglibs.standard.tag.rt.xml;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.tei;version="1.2.0";resolution:=optional,
org.apache.taglibs.standard.tlv;version="1.2.0";resolution:=optional,
- org.eclipse.jetty.jsp;version="[7.0,8.0)";resolution:=optional,
!org.osgi.*,
!org.xml.*,
!org.eclipse.jetty.*,
*
</Import-Package>
<_nouses>true</_nouses>
+ <!-- DynamicImport-Package>org.apache.jasper.*;version="2.2.2"</DynamicImport-Package -->
</instructions>
</configuration>
</plugin>
diff --git a/jetty-osgi/jetty-osgi-boot-logback/META-INF/readme.txt b/jetty-osgi/jetty-osgi-boot-logback/META-INF/readme.txt
deleted file mode 100644
index 20960b4..0000000
--- a/jetty-osgi/jetty-osgi-boot-logback/META-INF/readme.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-This bundle is made to inject the logback dependencies along with the slf4j dependencies to support log4j and commons-logging.
-It will read the configuration in the jettyhome/resources/logback-test.xml or jettyhome/resources/logback.xml folder.
-
-
-It was tested with these bundles:
-#this provides lg4j and commons-logging via slf4j
-SLF4J = group("com.springsource.slf4j.api", "com.springsource.slf4j.org.apache.log4j", "com.springsource.slf4j.org.apache.commons.logging",
- :under=>"org.slf4j", :version=>"1.5.6")
-
-#logback is not exporting enough packages for us to be able to configure logback classic programatically.. on the springsource version they are fine...
-LOGBACK = group("com.springsource.ch.qos.logback.core", "com.springsource.ch.qos.logback.classic",
- :under=>"ch.qos.logback", :version=>"0.9.15")
\ No newline at end of file
diff --git a/jetty-osgi/jetty-osgi-boot-logback/build.properties b/jetty-osgi/jetty-osgi-boot-logback/build.properties
deleted file mode 100644
index 6d10c98..0000000
--- a/jetty-osgi/jetty-osgi-boot-logback/build.properties
+++ /dev/null
@@ -1,5 +0,0 @@
-source.. = src/main/java/
-output.. = target/classes/
-bin.includes = META-INF/,\
- .
-src.includes = META-INF/
diff --git a/jetty-osgi/jetty-osgi-boot-logback/pom.xml b/jetty-osgi/jetty-osgi-boot-logback/pom.xml
index 26c2c00..83a619f 100644
--- a/jetty-osgi/jetty-osgi-boot-logback/pom.xml
+++ b/jetty-osgi/jetty-osgi-boot-logback/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.18-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/jetty-osgi/jetty-osgi-boot-warurl/pom.xml b/jetty-osgi/jetty-osgi-boot-warurl/pom.xml
index 9a5e356..066c364 100644
--- a/jetty-osgi/jetty-osgi-boot-warurl/pom.xml
+++ b/jetty-osgi/jetty-osgi-boot-warurl/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -80,6 +80,4 @@
</plugin>
</plugins>
</build>
-
-
</project>
diff --git a/jetty-osgi/jetty-osgi-boot-warurl/pom.xml.tycho b/jetty-osgi/jetty-osgi-boot-warurl/pom.xml.tycho
deleted file mode 100644
index b30ae7a..0000000
--- a/jetty-osgi/jetty-osgi-boot-warurl/pom.xml.tycho
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <artifactId>jetty-osgi</artifactId>
- <version>7.0.1-SNAPSHOT</version>
- <groupId>org.eclipse.jetty.osgi</groupId>
- </parent>
- <groupId>org.eclipse.jetty.osgi</groupId>
- <artifactId>org.eclipse.jetty.osgi.boot.warurl</artifactId>
- <version>7.0.1.qualifier</version>
- <packaging>eclipse-plugin</packaging>
-</project>
diff --git a/jetty-osgi/jetty-osgi-boot/pom.xml b/jetty-osgi/jetty-osgi-boot/pom.xml
index 1d555e1..913daf0 100644
--- a/jetty-osgi/jetty-osgi-boot/pom.xml
+++ b/jetty-osgi/jetty-osgi-boot/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -16,6 +16,10 @@
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-annotations</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
</dependency>
<dependency>
@@ -115,10 +119,12 @@
javax.mail.internet;version="1.4.0";resolution:=optional,
javax.mail.search;version="1.4.0";resolution:=optional,
javax.mail.util;version="1.4.0";resolution:=optional,
- javax.servlet;version="2.5.0",
- javax.servlet.http;version="2.5.0",
+ javax.servlet;version="2.6.0",
+ javax.servlet.http;version="2.6.0",
javax.transaction;version="1.1.0";resolution:=optional,
javax.transaction.xa;version="1.1.0";resolution:=optional,
+ org.eclipse.jetty.nested;version="[8.1,9)";resolution:=optional,
+ org.eclipse.jetty.annotations;version="[8.1,9)";resolution:=optional,
org.osgi.framework,
org.osgi.service.cm;version="1.2.0",
org.osgi.service.packageadmin,
@@ -130,12 +136,11 @@
org.slf4j.helpers;resolution:=optional,
org.xml.sax,
org.xml.sax.helpers,
- org.eclipse.jetty.nested;resolution:=optional,
*
</Import-Package>
- <DynamicImport-Package>org.eclipse.jetty.*;version="[7.6,8)"</DynamicImport-Package>
+ <DynamicImport-Package>org.eclipse.jetty.*;version="[8.1,9)"</DynamicImport-Package>
<!--Require-Bundle/-->
- <!-- Bundle-RequiredExecutionEnvironment>J2SE-1.5</Bundle-RequiredExecutionEnvironment -->
+ <!-- Bundle-RequiredExecutionEnvironment>JavaSE-1.6</Bundle-RequiredExecutionEnvironment-->
</instructions>
</configuration>
</plugin>
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java
new file mode 100644
index 0000000..945b235
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java
@@ -0,0 +1,210 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.osgi.annotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jetty.annotations.AbstractDiscoverableAnnotationHandler;
+import org.eclipse.jetty.annotations.AnnotationParser.DiscoverableAnnotationHandler;
+import org.eclipse.jetty.annotations.ClassNameResolver;
+import org.eclipse.jetty.osgi.boot.OSGiWebappConstants;
+import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.webapp.DiscoveredAnnotation;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+
+/**
+ * Extend the AnnotationConfiguration to support OSGi:
+ * Look for annotations inside WEB-INF/lib and also in the fragments and required bundles.
+ * Discover them using a scanner adapted to OSGi instead of the jarscanner.
+ */
+public class AnnotationConfiguration extends org.eclipse.jetty.annotations.AnnotationConfiguration
+{
+
+ /**
+ * This parser scans the bundles using the OSGi APIs instead of assuming a jar.
+ */
+ @Override
+ protected org.eclipse.jetty.annotations.AnnotationParser createAnnotationParser()
+ {
+ return new AnnotationParser();
+ }
+
+ /**
+ * Here is the order in which jars and osgi artifacts are scanned for discoverable annotations.
+ * <ol>
+ * <li>The container jars are scanned.</li>
+ * <li>The WEB-INF/classes are scanned</li>
+ * <li>The osgi fragment to the web bundle are parsed.</li>
+ * <li>The WEB-INF/lib are scanned</li>
+ * <li>The required bundles are parsed</li>
+ * </ol>
+ */
+ @Override
+ public void parseWebInfLib (WebAppContext context, org.eclipse.jetty.annotations.AnnotationParser parser)
+ throws Exception
+ {
+ AnnotationParser oparser = (AnnotationParser)parser;
+
+ Bundle webbundle = (Bundle) context.getAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE);
+ Bundle[] fragAndRequiredBundles = PackageAdminServiceTracker.INSTANCE.getFragmentsAndRequiredBundles(webbundle);
+ if (fragAndRequiredBundles != null)
+ {
+ //index:
+ for (Bundle bundle : fragAndRequiredBundles)
+ {
+ Resource bundleRes = oparser.indexBundle(bundle);
+ if (!context.getMetaData().getWebInfJars().contains(bundleRes))
+ {
+ context.getMetaData().addWebInfJar(bundleRes);
+ }
+ }
+
+ //scan the fragments
+ for (Bundle fragmentBundle : fragAndRequiredBundles)
+ {
+ if (fragmentBundle.getHeaders().get(Constants.FRAGMENT_HOST) != null)
+ {
+ //a fragment indeed:
+ parseFragmentBundle(context,oparser,webbundle,fragmentBundle);
+ }
+ }
+ }
+ //scan ourselves
+ parseWebBundle(context,oparser,webbundle);
+
+ //scan the WEB-INF/lib
+ super.parseWebInfLib(context,parser);
+ if (fragAndRequiredBundles != null)
+ {
+ //scan the required bundles
+ for (Bundle requiredBundle : fragAndRequiredBundles)
+ {
+ if (requiredBundle.getHeaders().get(Constants.FRAGMENT_HOST) == null)
+ {
+ //a bundle indeed:
+ parseRequiredBundle(context,oparser,webbundle,requiredBundle);
+ }
+ }
+ }
+ }
+
+ /**
+ * Scan a fragment bundle for servlet annotations
+ * @param context The webapp context
+ * @param parser The parser
+ * @param webbundle The current webbundle
+ * @param fragmentBundle The OSGi fragment bundle to scan
+ * @throws Exception
+ */
+ protected void parseFragmentBundle(WebAppContext context, AnnotationParser parser,
+ Bundle webbundle, Bundle fragmentBundle) throws Exception
+ {
+ parseBundle(context,parser,webbundle,fragmentBundle);
+ }
+
+ /**
+ * Scan a bundle required by the webbundle for servlet annotations
+ * @param context The webapp context
+ * @param parser The parser
+ * @param webbundle The current webbundle
+ * @param fragmentBundle The OSGi required bundle to scan
+ * @throws Exception
+ */
+ protected void parseWebBundle(WebAppContext context, AnnotationParser parser, Bundle webbundle)
+ throws Exception
+ {
+ parseBundle(context,parser,webbundle,webbundle);
+ }
+
+ /**
+ * Scan a bundle required by the webbundle for servlet annotations
+ * @param context The webapp context
+ * @param parser The parser
+ * @param webbundle The current webbundle
+ * @param fragmentBundle The OSGi required bundle to scan
+ * @throws Exception
+ */
+ protected void parseRequiredBundle(WebAppContext context, AnnotationParser parser,
+ Bundle webbundle, Bundle requiredBundle) throws Exception
+ {
+ parseBundle(context,parser,webbundle,requiredBundle);
+ }
+
+ protected void parseBundle(WebAppContext context, AnnotationParser parser,
+ Bundle webbundle, Bundle bundle) throws Exception
+ {
+
+ Resource bundleRes = parser.getResource(bundle);
+
+ parser.clearHandlers();
+ for (DiscoverableAnnotationHandler h:_discoverableAnnotationHandlers)
+ {
+ if (h instanceof AbstractDiscoverableAnnotationHandler)
+ {
+ if (webbundle == bundle)
+ ((AbstractDiscoverableAnnotationHandler)h).setResource(null);
+ else
+ ((AbstractDiscoverableAnnotationHandler)h).setResource(bundleRes);
+ }
+ }
+ parser.registerHandlers(_discoverableAnnotationHandlers);
+ parser.registerHandler(_classInheritanceHandler);
+ parser.registerHandlers(_containerInitializerAnnotationHandlers);
+
+ parser.parse(bundle,createClassNameResolver(context));
+ }
+
+ /**
+ * Returns the same classname resolver than for the webInfjar scanner
+ * @param context
+ * @return
+ */
+ protected ClassNameResolver createClassNameResolver(final WebAppContext context)
+ {
+ return createClassNameResolver(context,true,false,false,false);
+ }
+
+ protected ClassNameResolver createClassNameResolver(final WebAppContext context,
+ final boolean excludeSysClass, final boolean excludeServerClass, final boolean excludeEverythingElse,
+ final boolean overrideIsParenLoaderIsPriority)
+ {
+ return new ClassNameResolver ()
+ {
+ public boolean isExcluded (String name)
+ {
+ if (context.isSystemClass(name)) return excludeSysClass;
+ if (context.isServerClass(name)) return excludeServerClass;
+ return excludeEverythingElse;
+ }
+
+ public boolean shouldOverride (String name)
+ {
+ //looking at system classpath
+ if (context.isParentLoaderPriority())
+ return overrideIsParenLoaderIsPriority;
+ return !overrideIsParenLoaderIsPriority;
+ }
+ };
+ }
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationParser.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationParser.java
new file mode 100644
index 0000000..4fc274d
--- /dev/null
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationParser.java
@@ -0,0 +1,197 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.osgi.annotations;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URL;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+
+import org.eclipse.jetty.annotations.ClassNameResolver;
+import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelper;
+import org.eclipse.jetty.util.resource.Resource;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+
+/**
+ *
+ */
+public class AnnotationParser extends org.eclipse.jetty.annotations.AnnotationParser
+{
+ private Set<URI> _alreadyParsed = new HashSet<URI>();
+
+ private Map<URI,Bundle> _uriToBundle = new HashMap<URI, Bundle>();
+ private Map<Bundle,Resource> _resourceToBundle = new HashMap<Bundle,Resource>();
+ private Map<Bundle,URI> _bundleToUri = new HashMap<Bundle, URI>();
+
+ /**
+ * Keep track of a jetty URI Resource and its associated OSGi bundle.
+ * @param uri
+ * @param bundle
+ * @throws Exception
+ */
+ protected Resource indexBundle(Bundle bundle) throws Exception
+ {
+ File bundleFile = BundleFileLocatorHelper.DEFAULT.getBundleInstallLocation(bundle);
+ Resource resource = Resource.newResource(bundleFile.toURI());
+ URI uri = resource.getURI();
+ _uriToBundle.put(uri,bundle);
+ _bundleToUri.put(bundle,uri);
+ _resourceToBundle.put(bundle,resource);
+ return resource;
+ }
+ protected URI getURI(Bundle bundle)
+ {
+ return _bundleToUri.get(bundle);
+ }
+ protected Resource getResource(Bundle bundle)
+ {
+ return _resourceToBundle.get(bundle);
+ }
+ /**
+ *
+ */
+ @Override
+ public void parse (URI[] uris, ClassNameResolver resolver)
+ throws Exception
+ {
+ for (URI uri : uris)
+ {
+ Bundle associatedBundle = _uriToBundle.get(uri);
+ if (associatedBundle == null)
+ {
+ if (!_alreadyParsed.add(uri))
+ {
+ continue;
+ }
+ //a jar in WEB-INF/lib or the WEB-INF/classes
+ //use the behavior of the super class for a standard jar.
+ super.parse(new URI[] {uri},resolver);
+ }
+ else
+ {
+ parse(associatedBundle,resolver);
+ }
+ }
+ }
+
+ protected void parse(Bundle bundle, ClassNameResolver resolver)
+ throws Exception
+ {
+ URI uri = _bundleToUri.get(bundle);
+ if (!_alreadyParsed.add(uri))
+ {
+ return;
+ }
+
+ String bundleClasspath = (String)bundle.getHeaders().get(Constants.BUNDLE_CLASSPATH);
+ if (bundleClasspath == null)
+ {
+ bundleClasspath = ".";
+ }
+ //order the paths first by the number of tokens in the path second alphabetically.
+ TreeSet<String> paths = new TreeSet<String>(
+ new Comparator<String>()
+ {
+ public int compare(String o1, String o2)
+ {
+ int paths1 = new StringTokenizer(o1,"/",false).countTokens();
+ int paths2 = new StringTokenizer(o2,"/",false).countTokens();
+ if (paths1 == paths2)
+ {
+ return o1.compareTo(o2);
+ }
+ return paths2 - paths1;
+ }
+ });
+ boolean hasDotPath = false;
+ StringTokenizer tokenizer = new StringTokenizer(bundleClasspath, ",;", false);
+ while (tokenizer.hasMoreTokens())
+ {
+ String token = tokenizer.nextToken().trim();
+ if (!token.startsWith("/"))
+ {
+ token = "/" + token;
+ }
+ if (token.equals("/."))
+ {
+ hasDotPath = true;
+ }
+ else if (!token.endsWith(".jar") && !token.endsWith("/"))
+ {
+ paths.add(token+"/");
+ }
+ else
+ {
+ paths.add(token);
+ }
+ }
+ //support the development environment: maybe the classes are inside bin or target/classes
+ //this is certainly not useful in production.
+ //however it makes our life so much easier during development.
+ if (bundle.getEntry("/.classpath") != null)
+ {
+ if (bundle.getEntry("/bin/") != null)
+ {
+ paths.add("/bin/");
+ }
+ else if (bundle.getEntry("/target/classes/") != null)
+ {
+ paths.add("/target/classes/");
+ }
+ }
+ Enumeration classes = bundle.findEntries("/","*.class",true);
+ if (classes == null)
+ {
+ return;
+ }
+ while (classes.hasMoreElements())
+ {
+ URL classUrl = (URL) classes.nextElement();
+ String path = classUrl.getPath();
+ //remove the longest path possible:
+ String name = null;
+ for (String prefixPath : paths)
+ {
+ if (path.startsWith(prefixPath))
+ {
+ name = path.substring(prefixPath.length());
+ break;
+ }
+ }
+ if (name == null && hasDotPath)
+ {
+ //remove the starting '/'
+ name = path.substring(1);
+ }
+ //transform into a classname to pass to the resolver
+ String shortName = name.replace('/', '.').substring(0,name.length()-6);
+ if ((resolver == null)|| (!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
+ scanClass(classUrl.openStream());
+ }
+ }
+
+}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java
index bfc2295..ce430ef 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/JettyBootstrapActivator.java
@@ -105,8 +105,9 @@
DefaultJettyAtJettyHomeHelper.startJettyAtJettyHome(context);
// track Bundles and deploy those that represent webapps to one of the known Servers
- _webBundleTracker = new BundleTracker(context, Bundle.ACTIVE | Bundle.STOPPING, new WebBundleTrackerCustomizer());
- _webBundleTracker.open();
+ WebBundleTrackerCustomizer customizer = new WebBundleTrackerCustomizer();
+ _webBundleTracker = new BundleTracker(context, Bundle.ACTIVE | Bundle.STOPPING, customizer);
+ customizer.setAndOpenWebBundleTracker(_webBundleTracker);
}
/**
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/jsp/TldLocatableURLClassloader.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/jsp/TldLocatableURLClassloader.java
index 73564b0..3100423 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/jsp/TldLocatableURLClassloader.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/jsp/TldLocatableURLClassloader.java
@@ -48,4 +48,18 @@
{
return _jarsWithTldsInside;
}
+
+ public String toString()
+ {
+ StringBuilder builder = new StringBuilder();
+
+ if (_jarsWithTldsInside != null)
+ {
+ for (URL u:_jarsWithTldsInside)
+ builder.append(" "+u.toString());
+ return builder.toString();
+ }
+ else
+ return super.toString();
+ }
}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java
index bb965f6..22de401 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/serverfactory/DefaultJettyAtJettyHomeHelper.java
@@ -232,7 +232,7 @@
// can define their own configuration.
if ((enUrls == null || !enUrls.hasMoreElements()))
{
- String tmp = DEFAULT_JETTYHOME+etcFile;
+ String tmp = DEFAULT_JETTYHOME+(DEFAULT_JETTYHOME.endsWith("/")?"":"/")+etcFile;
enUrls = BundleFileLocatorHelperFactory.getFactory().getHelper().findEntries(configurationBundle, tmp);
LOG.info("Configuring jetty from bundle: "
+ configurationBundle.getSymbolicName()
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/LibExtClassLoaderHelper.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/LibExtClassLoaderHelper.java
index c811839..40fbd2b 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/LibExtClassLoaderHelper.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/LibExtClassLoaderHelper.java
@@ -26,6 +26,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -109,7 +110,7 @@
for (File f : jettyResources.listFiles())
{
jettyResFiles.put(f.getName(), f);
- if (f.getName().toLowerCase().startsWith("readme"))
+ if (f.getName().toLowerCase(Locale.ENGLISH).startsWith("readme"))
{
continue;
}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleTrackerCustomizer.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleTrackerCustomizer.java
index 7022757..50e5614 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleTrackerCustomizer.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/WebBundleTrackerCustomizer.java
@@ -30,6 +30,7 @@
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.BundleTracker;
import org.osgi.util.tracker.BundleTrackerCustomizer;
import org.osgi.util.tracker.ServiceTracker;
@@ -56,7 +57,7 @@
"("+OSGiServerConstants.MANAGED_JETTY_SERVER_NAME+"="+OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME+"))";
private ServiceTracker _serviceTracker;
-
+ private BundleTracker _bundleTracker;
/* ------------------------------------------------------------ */
/**
@@ -68,8 +69,16 @@
Bundle myBundle = FrameworkUtil.getBundle(this.getClass());
//track all instances of deployers of webapps/contexts as bundles
- _serviceTracker = new ServiceTracker(myBundle.getBundleContext(), FrameworkUtil.createFilter(FILTER),null);
+ _serviceTracker = new ServiceTracker(myBundle.getBundleContext(), FrameworkUtil.createFilter(FILTER),null) {
+ public Object addingService(ServiceReference reference) {
+ Object object = super.addingService(reference);
+ LOG.debug("Deployer registered {}", reference);
+ openBundleTracker();
+ return object;
+ }
+ };
_serviceTracker.open();
+
}
@@ -98,7 +107,7 @@
{
if (bundle.getState() == Bundle.ACTIVE)
{
- register(bundle);
+ register(bundle);
}
else if (bundle.getState() == Bundle.STOPPING)
{
@@ -221,4 +230,21 @@
}
}
}
+
+ public void setAndOpenWebBundleTracker(BundleTracker bundleTracker) {
+ if(_bundleTracker == null) {
+ _bundleTracker = bundleTracker;
+ LOG.debug("Bundle tracker is set");
+ openBundleTracker();
+ }
+ }
+
+ private void openBundleTracker() {
+ if(_bundleTracker != null && _serviceTracker.getServices() != null &&
+ _serviceTracker.getServices().length > 0) {
+ _bundleTracker.open();
+ LOG.debug("Bundle tracker has been opened");
+ }
+ }
+
}
diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/PackageAdminServiceTracker.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/PackageAdminServiceTracker.java
index 89b2bfd..a5b5079 100644
--- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/PackageAdminServiceTracker.java
+++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/PackageAdminServiceTracker.java
@@ -380,3 +380,4 @@
}
}
+
diff --git a/jetty-osgi/jetty-osgi-equinoxtools/build.properties b/jetty-osgi/jetty-osgi-equinoxtools/build.properties
deleted file mode 100644
index 05786b0..0000000
--- a/jetty-osgi/jetty-osgi-equinoxtools/build.properties
+++ /dev/null
@@ -1,5 +0,0 @@
-source.. = src/
-output.. = bin/
-bin.includes = META-INF/,\
- equinoxconsole/,\
- .
diff --git a/jetty-osgi/jetty-osgi-equinoxtools/equinoxconsole/index.html b/jetty-osgi/jetty-osgi-equinoxtools/equinoxconsole/index.html
deleted file mode 100644
index 4deba32..0000000
--- a/jetty-osgi/jetty-osgi-equinoxtools/equinoxconsole/index.html
+++ /dev/null
@@ -1,85 +0,0 @@
-<html><head>
- <title>Async Equinox Console</title>
- <script type='text/javascript'>
- function $() { return document.getElementById(arguments[0]); }
- function $F() { return document.getElementById(arguments[0]).value; }
- function getKeyCode(ev) { if (window.event) return window.event.keyCode; return ev.keyCode; }
- function xhr(method,uri,body,handler) {
- var req=(window.XMLHttpRequest)?new XMLHttpRequest():new ActiveXObject('Microsoft.XMLHTTP');
- req.onreadystatechange=function() { if (req.readyState==4 && handler) { eval('var o='+req.responseText);handler(o);} }
- req.open(method,uri,true);
- req.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
- req.send(body);
- };
- function send(action,user,message,handler){
- if (message) message=message.replace('%','%25').replace('&','%26').replace('=','%3D');
- if (user) user=user.replace('%','%25').replace('&','%26').replace('=','%3D');
- xhr('POST','chat','action='+action+'&user='+user+'&message='+message,handler);
- };
-
- var room = {
- join: function(name) {
- this._username=name;
- $('join').className='hidden';
- $('joined').className='';
- $('phrase').focus();
- send('join', room._username,null);
- send('chat', room._username,'has joined!');
- send('poll', room._username,null, room._poll);
- },
- chat: function(text) {
- if (text != null && text.length>0 )
- send('chat',room._username,text);
- },
- _poll: function(m) {
- //console.debug(m);
- if (m.chat){
- var chat=document.getElementById('chat');
- var spanFrom = document.createElement('span');
- spanFrom.className='from';
- spanFrom.innerHTML=m.from+' ';
- var spanText = document.createElement('span');
- spanText.className='text';
- spanText.innerHTML=m.chat;
- var lineBreak = document.createElement('br');
- chat.appendChild(spanFrom);
- chat.appendChild(spanText);
- chat.appendChild(lineBreak);
- chat.scrollTop = chat.scrollHeight - chat.clientHeight;
- }
- if (m.action=='poll')
- send('poll', room._username,null, room._poll);
- },
- _end:''
- };
- </script>
- <style type='text/css'>
- div { border: 0px solid black; }
- div#chat { clear: both; width: 40em; height: 20ex; overflow: auto; background-color: #f0f0f0; padding: 4px; border: 1px solid black; }
- div#input { clear: both; width: 40em; padding: 4px; background-color: #e0e0e0; border: 1px solid black; border-top: 0px }
- input#phrase { width:30em; background-color: #e0f0f0; }
- input#username { width:14em; background-color: #e0f0f0; }
- div.hidden { display: none; }
- span.from { font-weight: bold; }
- span.alert { font-style: italic; }
- </style>
-</head><body>
-<div id='chat'></div>
-<div id='input'>
- <div id='join' >
- Username: <input id='username' type='text'/><input id='joinB' class='button' type='submit' name='join' value='Join'/>
- </div>
- <div id='joined' class='hidden'>
- OSGi: <input id='phrase' type='text'/>
- <input id='sendB' class='button' type='submit' name='join' value='Send'/>
- </div>
-</div>
-<script type='text/javascript'>
-$('username').setAttribute('autocomplete','OFF');
-$('username').onkeyup = function(ev) { var keyc=getKeyCode(ev); if (keyc==13 || keyc==10) { room.join($F('username')); return false; } return true; } ;
-$('joinB').onclick = function(event) { room.join($F('username')); return false; };
-$('phrase').setAttribute('autocomplete','OFF');
-$('phrase').onkeyup = function(ev) { var keyc=getKeyCode(ev); if (keyc==13 || keyc==10) { room.chat($F('phrase')); $('phrase').value=''; return false; } return true; };
-$('sendB').onclick = function(event) { room.chat($F('phrase')); $('phrase').value=''; return false; };
-</script>
-</body></html>
diff --git a/jetty-osgi/jetty-osgi-equinoxtools/equinoxconsole/ws/index.html b/jetty-osgi/jetty-osgi-equinoxtools/equinoxconsole/ws/index.html
deleted file mode 100644
index 4d5acc9..0000000
--- a/jetty-osgi/jetty-osgi-equinoxtools/equinoxconsole/ws/index.html
+++ /dev/null
@@ -1,109 +0,0 @@
-<html><head>
- <title>Equinox Console (WebSocket)</title>
- <script type='text/javascript'>
-
- if (!window.WebSocket)
- alert("WebSocket not supported by this browser");
-
- function $() { return document.getElementById(arguments[0]); }
- function $F() { return document.getElementById(arguments[0]).value; }
-
- function getKeyCode(ev) { if (window.event) return window.event.keyCode; return ev.keyCode; }
-
- var room = {
- join: function(name) {
- this._username=name;
- var location = document.location.toString().replace('http://','ws://').replace('https://','wss://').replace('/index.html','');
- this._ws=new WebSocket(location);
- this._ws.onopen=this._onopen;
- this._ws.onmessage=this._onmessage;
- this._ws.onclose=this._onclose;
- },
-
- _onopen: function(){
- $('join').className='hidden';
- $('joined').className='';
- $('phrase').focus();
- room._send(room._username,'has joined!');
- },
-
- _send: function(user,message){
- user=user.replace(':','_');
- if (this._ws)
- this._ws.send(user+':'+message);
- },
-
- chat: function(text) {
- if (text != null && text.length>0 )
- room._send(room._username,text);
- },
-
- _onmessage: function(m) {
- if (m.data){
- var c=m.data.indexOf(':');
- var from=m.data.substring(0,c).replace('<','<').replace('>','>');
- var text=m.data.substring(c+1).replace('<','<').replace('>','>');
-
- var chat=$('chat');
- var spanFrom = document.createElement('span');
- spanFrom.className='from';
- spanFrom.innerHTML=from+': ';
- var spanText = document.createElement('span');
- spanText.className='text';
- spanText.innerHTML=text;
- var lineBreak = document.createElement('br');
- chat.appendChild(spanFrom);
- chat.appendChild(spanText);
- chat.appendChild(lineBreak);
- chat.scrollTop = chat.scrollHeight - chat.clientHeight;
- }
- },
-
- _onclose: function(m) {
- this._ws=null;
- $('join').className='';
- $('joined').className='hidden';
- $('username').focus();
- $('chat').innerHTML='';
- }
-
- };
-
- </script>
- <style type='text/css'>
- div { border: 0px solid black; }
- div#chat { clear: both; width: 40em; height: 20ex; overflow: auto; background-color: #f0f0f0; padding: 4px; border: 1px solid black; }
- div#input { clear: both; width: 40em; padding: 4px; background-color: #e0e0e0; border: 1px solid black; border-top: 0px }
- input#phrase { width:30em; background-color: #e0f0f0; }
- input#username { width:14em; background-color: #e0f0f0; }
- div.hidden { display: none; }
- span.from { font-weight: bold; }
- span.alert { font-style: italic; }
- </style>
-</head><body>
-<div id='chat'></div>
-<div id='input'>
- <div id='join' >
- Username: <input id='username' type='text'/><input id='joinB' class='button' type='submit' name='join' value='Join'/>
- </div>
- <div id='joined' class='hidden'>
- Chat: <input id='phrase' type='text'/>
- <input id='sendB' class='button' type='submit' name='join' value='Send'/>
- </div>
-</div>
-<script type='text/javascript'>
-$('username').setAttribute('autocomplete','OFF');
-$('username').onkeyup = function(ev) { var keyc=getKeyCode(ev); if (keyc==13 || keyc==10) { room.join($F('username')); return false; } return true; } ;
-$('joinB').onclick = function(event) { room.join($F('username')); return false; };
-$('phrase').setAttribute('autocomplete','OFF');
-$('phrase').onkeyup = function(ev) { var keyc=getKeyCode(ev); if (keyc==13 || keyc==10) { room.chat($F('phrase')); $('phrase').value=''; return false; } return true; };
-$('sendB').onclick = function(event) { room.chat($F('phrase')); $('phrase').value=''; return false; };
-</script>
-
-<p>
-This is a demonstration of the Jetty websocket server.
-</p>
-</body></html>
-
-
-
diff --git a/jetty-osgi/jetty-osgi-equinoxtools/pom.xml b/jetty-osgi/jetty-osgi-equinoxtools/pom.xml
index cf84d89..e661375 100644
--- a/jetty-osgi/jetty-osgi-equinoxtools/pom.xml
+++ b/jetty-osgi/jetty-osgi-equinoxtools/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.18-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/jetty-osgi/jetty-osgi-httpservice/contexts/httpservice.xml b/jetty-osgi/jetty-osgi-httpservice/contexts/httpservice.xml
index 95757f6..211a8e8 100644
--- a/jetty-osgi/jetty-osgi-httpservice/contexts/httpservice.xml
+++ b/jetty-osgi/jetty-osgi-httpservice/contexts/httpservice.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
<!--
- Copyright (c) 2009 Intalio, Inc.
+ Copyright (c) 2009-2011 Intalio, Inc.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
@@ -29,4 +29,4 @@
<Set name="ErrorHandler">
<New class="org.eclipse.jetty.osgi.httpservice.HttpServiceErrorPageErrorHandler"/>
</Set>
-</Configure>
\ No newline at end of file
+</Configure>
diff --git a/jetty-osgi/jetty-osgi-httpservice/pom.xml b/jetty-osgi/jetty-osgi-httpservice/pom.xml
index 44ab9a9..1d86a32 100644
--- a/jetty-osgi/jetty-osgi-httpservice/pom.xml
+++ b/jetty-osgi/jetty-osgi-httpservice/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -97,8 +97,8 @@
<Bundle-SymbolicName>org.eclipse.jetty.osgi.httpservice</Bundle-SymbolicName>
<Bundle-Name>OSGi HttpService</Bundle-Name>
<Jetty-ContextFilePath>contexts/httpservice.xml</Jetty-ContextFilePath>
- <Import-Package>org.eclipse.jetty.server.handler;version="[7.6,8)",
-org.eclipse.jetty.util.component;version="[7.6,8)",
+ <Import-Package>org.eclipse.jetty.server.handler;version="[8.1,9)",
+org.eclipse.jetty.util.component;version="[8.1,9)",
org.eclipse.equinox.http.servlet,
*
</Import-Package>
@@ -115,6 +115,4 @@
</plugin>
</plugins>
</build>
-
-
</project>
diff --git a/jetty-osgi/jetty-osgi-httpservice/pom.xml.tycho b/jetty-osgi/jetty-osgi-httpservice/pom.xml.tycho
deleted file mode 100644
index c6efbeb..0000000
--- a/jetty-osgi/jetty-osgi-httpservice/pom.xml.tycho
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <artifactId>jetty-osgi</artifactId>
- <groupId>org.eclipse.jetty.osgi</groupId>
- <version>7.0.1-SNAPSHOT</version>
- </parent>
- <artifactId>org.eclipse.jetty.osgi.httpservice</artifactId>
- <packaging>eclipse-plugin</packaging>
-</project>
diff --git a/jetty-osgi/jetty-osgi-servletbridge/pom.xml b/jetty-osgi/jetty-osgi-servletbridge/pom.xml
deleted file mode 100644
index 4da36c2..0000000
--- a/jetty-osgi/jetty-osgi-servletbridge/pom.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<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.osgi</groupId>
- <artifactId>jetty-osgi-project</artifactId>
- <version>7.6.5-SNAPSHOT</version>
- <relativePath>../pom.xml</relativePath>
- </parent>
- <modelVersion>4.0.0</modelVersion>
- <groupId>org.eclipse.jetty.osgi</groupId>
- <artifactId>jetty-osgi-servletbridge</artifactId>
- <name>Jetty :: OSGi :: Servletbridge</name>
- <description>Jetty OSGi Servletbridge webapp</description>
- <url>http://www.eclipse.org/jetty</url>
- <packaging>war</packaging>
- <properties><eclipse.pde>false</eclipse.pde></properties>
- <dependencies>
- <dependency>
- <groupId>org.eclipse.equinox</groupId>
- <artifactId>org.eclipse.equinox.servletbridge</artifactId>
- <version>1.2.0.v20100503</version>
- </dependency>
- <dependency>
- <groupId>javax.servlet</groupId>
- <artifactId>servlet-api</artifactId>
- <version>2.5</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.eclipse.osgi</groupId>
- <artifactId>org.eclipse.osgi</artifactId>
- <version>3.6.0.v20100517</version>
- <scope>provided</scope>
- </dependency>
- </dependencies>
- <repositories>
- <!-- can't find equinox servlet bridge jar on maven central.
- uploaded it to intalio.org for now. -->
- <repository>
- <id>intalio-org</id>
- <url>http://intalio.org/public/maven2</url>
- </repository>
- </repositories>
-
- <build>
- <plugins><plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-eclipse-plugin</artifactId>
- <configuration>
- <pde>false</pde>
- </configuration>
- </plugin></plugins>
- </build>
-
-</project>
diff --git a/jetty-osgi/jetty-osgi-servletbridge/src/main/java/org/eclipse/jetty/nested/Dump.java b/jetty-osgi/jetty-osgi-servletbridge/src/main/java/org/eclipse/jetty/nested/Dump.java
deleted file mode 100644
index 02434a7..0000000
--- a/jetty-osgi/jetty-osgi-servletbridge/src/main/java/org/eclipse/jetty/nested/Dump.java
+++ /dev/null
@@ -1,1017 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2016 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.nested;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.io.Reader;
-import java.lang.reflect.Array;
-import java.lang.reflect.Field;
-import java.sql.Connection;
-import java.sql.DriverManager;
-import java.sql.Statement;
-import java.util.Date;
-import java.util.Enumeration;
-import java.util.Locale;
-import java.util.Map.Entry;
-
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletRequestWrapper;
-import javax.servlet.ServletResponse;
-import javax.servlet.ServletResponseWrapper;
-import javax.servlet.UnavailableException;
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletRequestWrapper;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpServletResponseWrapper;
-import javax.sql.DataSource;
-
-/* ------------------------------------------------------------ */
-/**
- * Dump Servlet Request.
- *
- * Copied from test-jetty-webapp's Dump servlet.
- */
-public class Dump extends HttpServlet
-{
- boolean fixed;
-
- /* ------------------------------------------------------------ */
- @Override
- public void init(ServletConfig config) throws ServletException
- {
- super.init(config);
-
- if (config.getInitParameter("unavailable") != null && !fixed)
- {
-
- fixed = true;
- throw new UnavailableException("Unavailable test", Integer.parseInt(config.getInitParameter("unavailable")));
- }
- }
-
- /* ------------------------------------------------------------ */
- @Override
- public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
- {
- doGet(request, response);
- }
-
- /* ------------------------------------------------------------ */
- @Override
- public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
- {
- // Handle a dump of data
- final String data = request.getParameter("data");
- final String chars = request.getParameter("chars");
- final String block = request.getParameter("block");
- final String dribble = request.getParameter("dribble");
- final boolean flush = request.getParameter("flush") != null ? Boolean.parseBoolean(request.getParameter("flush")) : false;
-
- if (request.getPathInfo() != null && request.getPathInfo().toLowerCase().indexOf("script") != -1)
- {
- response.sendRedirect(response.encodeRedirectURL(getServletContext().getContextPath() + "/dump/info"));
- return;
- }
-
- request.setCharacterEncoding("UTF-8");
-
- if (request.getParameter("empty") != null)
- {
- response.setStatus(200);
- response.flushBuffer();
- return;
- }
-
- request.setAttribute("Dump", this);
- getServletContext().setAttribute("Dump", this);
-
- // Force a content length response
- String length = request.getParameter("length");
- if (length != null && length.length() > 0)
- {
- response.setContentLength(Integer.parseInt(length));
- }
-
- // Handle a dump of data
- if (dump(response, data, chars, block, dribble, flush)) return;
-
- // handle an exception
- String info = request.getPathInfo();
- if (info != null && info.endsWith("Exception"))
- {
- try
- {
- throw (Throwable) Thread.currentThread().getContextClassLoader().loadClass(info.substring(1)).newInstance();
- }
- catch (Throwable th)
- {
- throw new ServletException(th);
- }
- }
-
- // test a reset
- String reset = request.getParameter("reset");
- if (reset != null && reset.length() > 0)
- {
- response.getOutputStream().println("THIS SHOULD NOT BE SEEN!");
- response.setHeader("SHOULD_NOT", "BE SEEN");
- response.reset();
- }
-
- // handle an redirect
- String redirect = request.getParameter("redirect");
- if (redirect != null && redirect.length() > 0)
- {
- response.getOutputStream().println("THIS SHOULD NOT BE SEEN!");
- response.sendRedirect(response.encodeRedirectURL(redirect));
- try
- {
- response.getOutputStream().println("THIS SHOULD NOT BE SEEN!");
- }
- catch (IOException e)
- {
- // ignored as stream is closed.
- }
- return;
- }
-
- // handle an error
- String error = request.getParameter("error");
- if (error != null && error.length() > 0 && request.getAttribute("javax.servlet.error.status_code") == null)
- {
- response.getOutputStream().println("THIS SHOULD NOT BE SEEN!");
- response.sendError(Integer.parseInt(error));
- try
- {
- response.getOutputStream().println("THIS SHOULD NOT BE SEEN!");
- }
- catch (IllegalStateException e)
- {
- try
- {
- response.getWriter().println("NOR THIS!!");
- }
- catch (IOException e2)
- {
- }
- }
- catch (IOException e)
- {
- }
- return;
- }
-
- // Handle a extra headers
- String headers = request.getParameter("headers");
- if (headers != null && headers.length() > 0)
- {
- long h = Long.parseLong(headers);
- for (int i = 0; i < h; i++)
- response.addHeader("Header" + i, "Value" + i);
- }
-
- String buffer = request.getParameter("buffer");
- if (buffer != null && buffer.length() > 0) response.setBufferSize(Integer.parseInt(buffer));
-
- String charset = request.getParameter("charset");
- if (charset == null) charset = "UTF-8";
- response.setCharacterEncoding(charset);
- response.setContentType("text/html");
-
- if (info != null && info.indexOf("Locale/") >= 0)
- {
- try
- {
- String locale_name = info.substring(info.indexOf("Locale/") + 7);
- Field f = java.util.Locale.class.getField(locale_name);
- response.setLocale((Locale) f.get(null));
- }
- catch (Exception e)
- {
- e.printStackTrace();
- response.setLocale(Locale.getDefault());
- }
- }
-
- String cn = request.getParameter("cookie");
- String cv = request.getParameter("cookiev");
- if (cn != null && cv != null)
- {
- Cookie cookie = new Cookie(cn, cv);
- if (request.getParameter("version") != null) cookie.setVersion(Integer.parseInt(request.getParameter("version")));
- cookie.setComment("Cookie from dump servlet");
- response.addCookie(cookie);
- }
-
- String pi = request.getPathInfo();
- if (pi != null && pi.startsWith("/ex"))
- {
- OutputStream out = response.getOutputStream();
- out.write("</H1>This text should be reset</H1>".getBytes());
- if ("/ex0".equals(pi))
- throw new ServletException("test ex0", new Throwable());
- else if ("/ex1".equals(pi))
- throw new IOException("test ex1");
- else if ("/ex2".equals(pi))
- throw new UnavailableException("test ex2");
- else if (pi.startsWith("/ex3/")) throw new UnavailableException("test ex3", Integer.parseInt(pi.substring(5)));
- throw new RuntimeException("test");
- }
-
- if ("true".equals(request.getParameter("close"))) response.setHeader("Connection", "close");
-
- String buffered = request.getParameter("buffered");
-
- PrintWriter pout = null;
-
- try
- {
- pout = response.getWriter();
- }
- catch (IllegalStateException e)
- {
- pout = new PrintWriter(new OutputStreamWriter(response.getOutputStream(), charset));
- }
- if (buffered != null) pout = new PrintWriter(new BufferedWriter(pout, Integer.parseInt(buffered)));
-
- try
- {
- pout.write("<html>\n<body>\n");
- pout.write("<h1>Dump Servlet</h1>\n");
- pout.write("<table width=\"95%\">");
- pout.write("<tr>\n");
- pout.write("<th align=\"right\">getMethod: </th>");
- pout.write("<td>" + notag(request.getMethod()) + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">getContentLength: </th>");
- pout.write("<td>" + Integer.toString(request.getContentLength()) + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">getContentType: </th>");
- pout.write("<td>" + notag(request.getContentType()) + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">getRequestURI: </th>");
- pout.write("<td>" + notag(request.getRequestURI()) + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">getRequestURL: </th>");
- pout.write("<td>" + notag(request.getRequestURL().toString()) + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">getContextPath: </th>");
- pout.write("<td>" + request.getContextPath() + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">getServletPath: </th>");
- pout.write("<td>" + notag(request.getServletPath()) + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">getPathInfo: </th>");
- pout.write("<td>" + notag(request.getPathInfo()) + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">getPathTranslated: </th>");
- pout.write("<td>" + notag(request.getPathTranslated()) + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">getQueryString: </th>");
- pout.write("<td>" + notag(request.getQueryString()) + "</td>");
- pout.write("</tr><tr>\n");
-
- pout.write("<th align=\"right\">getProtocol: </th>");
- pout.write("<td>" + request.getProtocol() + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">getScheme: </th>");
- pout.write("<td>" + request.getScheme() + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">getServerName: </th>");
- pout.write("<td>" + notag(request.getServerName()) + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">getServerPort: </th>");
- pout.write("<td>" + Integer.toString(request.getServerPort()) + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">getLocalName: </th>");
- pout.write("<td>" + request.getLocalName() + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">getLocalAddr: </th>");
- pout.write("<td>" + request.getLocalAddr() + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">getLocalPort: </th>");
- pout.write("<td>" + Integer.toString(request.getLocalPort()) + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">getRemoteUser: </th>");
- pout.write("<td>" + request.getRemoteUser() + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">getUserPrincipal: </th>");
- pout.write("<td>" + request.getUserPrincipal() + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">getRemoteAddr: </th>");
- pout.write("<td>" + request.getRemoteAddr() + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">getRemoteHost: </th>");
- pout.write("<td>" + request.getRemoteHost() + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">getRemotePort: </th>");
- pout.write("<td>" + request.getRemotePort() + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">getRequestedSessionId: </th>");
- pout.write("<td>" + request.getRequestedSessionId() + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">isSecure(): </th>");
- pout.write("<td>" + request.isSecure() + "</td>");
-
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">isUserInRole(admin): </th>");
- pout.write("<td>" + request.isUserInRole("admin") + "</td>");
-
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">getLocale: </th>");
- pout.write("<td>" + request.getLocale() + "</td>");
-
- Enumeration locales = request.getLocales();
- while (locales.hasMoreElements())
- {
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">getLocales: </th>");
- pout.write("<td>" + locales.nextElement() + "</td>");
- }
- pout.write("</tr><tr>\n");
-
- pout.write("<th align=\"left\" colspan=\"2\"><big><br/>Other HTTP Headers:</big></th>");
- Enumeration h = request.getHeaderNames();
- String name;
- while (h.hasMoreElements())
- {
- name = (String) h.nextElement();
-
- Enumeration h2 = request.getHeaders(name);
- while (h2.hasMoreElements())
- {
- String hv = (String) h2.nextElement();
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">" + notag(name) + ": </th>");
- pout.write("<td>" + notag(hv) + "</td>");
- }
- }
-
- // Test the system properties
- if ("true".equals(request.getParameter("env")))
- {
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"left\" colspan=\"2\"><big><br/>Environment: </big></th>");
- for (Entry e : System.getenv().entrySet())
- {
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">" + notag(String.valueOf(e.getKey())) + ": </th>");
- pout.write("<td>" + notag(String.valueOf(e.getValue())) + "</td>");
- }
-
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"left\" colspan=\"2\"><big><br/>System-Properties: </big></th>");
-
- for (Entry<Object, Object> e : System.getProperties().entrySet())
- {
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">" + notag(String.valueOf(e.getKey())) + ": </th>");
- pout.write("<td>" + notag(String.valueOf(e.getValue())) + "</td>");
- }
- }
-
- // handle testing jdbc connections:
- String jdbcUrl = request.getParameter("jdbc-url");
- String jdbcDriver = request.getParameter("jdbc-driver");
- if (jdbcUrl != null)
- {
- Connection con = null;
- try
- {
- String user = request.getParameter("jdbc-user");
- String pass = request.getParameter("jdbc-pass");
- String query = request.getParameter("jdbc-query");
- if (user.length() == 0) user = null;
- if (pass.length() == 0) pass = null;
- if (query == null || query.length() == 0) query = "show tables;";
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"left\" colspan=\"2\"><big><br/>JDBC test: </big></th>");
-
- Class driver = Thread.currentThread().getContextClassLoader().loadClass(jdbcDriver);
-
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">Driver class: </th>");
- pout.write("<td>" + driver.getName() + " loaded by " + driver.getClassLoader() + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">Connection url: </th>");
- pout.write("<td>" + jdbcUrl + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">User (optional): </th>");
- pout.write("<td>" + user + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">Is there a password (optional): </th>");
- pout.write("<td>" + (pass != null ? "yes" : "no") + "</td>");
-
- con = user != null ? DriverManager.getConnection(jdbcUrl, user, pass) : DriverManager.getConnection(jdbcUrl);
-
- Statement statement = con.createStatement();
- boolean success = statement.execute(query);
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">Query: </th>");
- pout.write("<td>" + query + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">Successful query: </th>");
- pout.write("<td>" + success + "</td>");
-
- }
- catch (Throwable t)
- {
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">JDBC test error: </th>");
- pout.write("<td>" + t + "</td>");
- }
- finally
- {
- if (con != null)
- {
- try
- {
- con.close();
- }
- catch (Throwable ee)
- {
- }
- }
- }
- }
-
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"left\" colspan=\"2\"><big><br/>Request Parameters:</big></th>");
- h = request.getParameterNames();
- while (h.hasMoreElements())
- {
- name = (String) h.nextElement();
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">" + notag(name) + ": </th>");
- pout.write("<td>" + notag(request.getParameter(name)) + "</td>");
- String[] values = request.getParameterValues(name);
- if (values == null)
- {
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">" + notag(name) + " Values: </th>");
- pout.write("<td>" + "NULL!" + "</td>");
- }
- else if (values.length > 1)
- {
- for (int i = 0; i < values.length; i++)
- {
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">" + notag(name) + "[" + i + "]: </th>");
- pout.write("<td>" + notag(values[i]) + "</td>");
- }
- }
- }
-
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"left\" colspan=\"2\"><big><br/>Cookies:</big></th>");
- Cookie[] cookies = request.getCookies();
- for (int i = 0; cookies != null && i < cookies.length; i++)
- {
- Cookie cookie = cookies[i];
-
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">" + notag(cookie.getName()) + ": </th>");
- pout.write("<td>" + notag(cookie.getValue()) + "</td>");
- }
-
- String content_type = request.getContentType();
- if (content_type != null && !content_type.startsWith("application/x-www-form-urlencoded") && !content_type.startsWith("multipart/form-data"))
- {
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"left\" valign=\"top\" colspan=\"2\"><big><br/>Content:</big></th>");
- pout.write("</tr><tr>\n");
- pout.write("<td><pre>");
- char[] content = new char[4096];
- int len;
- try
- {
- Reader in = request.getReader();
-
- while ((len = in.read(content)) >= 0)
- pout.write(notag(new String(content, 0, len)));
- }
- catch (IOException e)
- {
- pout.write(e.toString());
- }
-
- pout.write("</pre></td>");
- }
-
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"left\" colspan=\"2\"><big><br/>Request Attributes:</big></th>");
- Enumeration a = request.getAttributeNames();
- while (a.hasMoreElements())
- {
- name = (String) a.nextElement();
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\" valign=\"top\">" + name.replace(".", " .") + ": </th>");
- Object value = request.getAttribute(name);
- if (value instanceof File)
- {
- File file = (File) value;
- pout.write("<td>" + "<pre>" + file.getName() + " (" + file.length() + " " + new Date(file.lastModified()) + ")</pre>" + "</td>");
- }
- else
- pout.write("<td>" + "<pre>" + toString(request.getAttribute(name)) + "</pre>" + "</td>");
- }
- request.setAttribute("org.eclipse.jetty.servlet.MultiPartFilter.files", null);
-
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"left\" colspan=\"2\"><big><br/>Servlet InitParameters:</big></th>");
- a = getInitParameterNames();
- while (a.hasMoreElements())
- {
- name = (String) a.nextElement();
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">" + name + ": </th>");
- pout.write("<td>" + toString(getInitParameter(name)) + "</td>");
- }
-
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"left\" colspan=\"2\"><big><br/>ServletContext Misc:</big></th>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\" valign=\"top\">" + "servletContext.getContextPath()" + ": </th>");
- pout.write("<td>" + getServletContext().getContextPath() + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\" valign=\"top\">" + "getServletContext().getRealPath(\"/WEB-INF/\")" + ": </th>");
- pout.write("<td>" + getServletContext().getRealPath("/WEB-INF/") + "</td>");
-
- String webinfRealPath = getServletContext().getRealPath("/WEB-INF/");
- if (webinfRealPath != null)
- {
- try
- {
- File webInfRealPathFile = new File(webinfRealPath);
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\" valign=\"top\">" + "new File(getServletContext().getRealPath(\"/WEB-INF/\"))" + ": </th>");
- pout.write("<td>exists()=" + webInfRealPathFile.exists()
- + "; isFile()="
- + webInfRealPathFile.isFile()
- + "; isDirectory()="
- + webInfRealPathFile.isDirectory()
- + "; isAbsolute()="
- + webInfRealPathFile.isAbsolute()
- + "; canRead()="
- + webInfRealPathFile.canRead()
- + "; canWrite()="
- + webInfRealPathFile.canWrite()
- + "</td>");
- if (webInfRealPathFile.exists() && webInfRealPathFile.isDirectory())
- {
- File webxml = new File(webInfRealPathFile, "web.xml");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\" valign=\"top\">" + "new File(getServletContext().getRealPath(\"/WEB-INF/web.xml\"))" + ": </th>");
- pout.write("<td>exists()=" + webxml.exists()
- + "; isFile()="
- + webxml.isFile()
- + "; isDirectory()="
- + webxml.isDirectory()
- + "; isAbsolute()="
- + webxml.isAbsolute()
- + "; canRead()="
- + webxml.canRead()
- + "; canWrite()="
- + webxml.canWrite()
- + "</td>");
- }
- }
- catch (Throwable t)
- {
- pout.write("<th align=\"right\" valign=\"top\">" + "Error probing the java.io.File(getServletContext().getRealPath(\"/WEB-INF/\"))"
- + ": </th>");
- pout.write("<td>" + t + "</td>");
- }
- }
-
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\" valign=\"top\">" + "getServletContext().getServerInfo()" + ": </th>");
- pout.write("<td>" + getServletContext().getServerInfo() + "</td>");
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\" valign=\"top\">" + "getServletContext().getServletContextName()" + ": </th>");
- pout.write("<td>" + getServletContext().getServletContextName() + "</td>");
-
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"left\" colspan=\"2\"><big><br/>Context InitParameters:</big></th>");
- a = getServletContext().getInitParameterNames();
- while (a.hasMoreElements())
- {
- name = (String) a.nextElement();
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\" valign=\"top\">" + name.replace(".", " .") + ": </th>");
- pout.write("<td>" + toString(getServletContext().getInitParameter(name)) + "</td>");
- }
-
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"left\" colspan=\"2\"><big><br/>Context Attributes:</big></th>");
- a = getServletContext().getAttributeNames();
- while (a.hasMoreElements())
- {
- name = (String) a.nextElement();
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\" valign=\"top\">" + name.replace(".", " .") + ": </th>");
- pout.write("<td>" + "<pre>" + toString(getServletContext().getAttribute(name)) + "</pre>" + "</td>");
- }
-
- String res = request.getParameter("resource");
- if (res != null && res.length() > 0)
- {
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"left\" colspan=\"2\"><big><br/>Get Resource: \"" + res + "\"</big></th>");
-
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">getServletContext().getContext(...): </th>");
-
- ServletContext context = getServletContext().getContext(res);
- pout.write("<td>" + context + "</td>");
-
- if (context != null)
- {
- String cp = context.getContextPath();
- if (cp == null || "/".equals(cp)) cp = "";
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">getServletContext().getContext(...),getRequestDispatcher(...): </th>");
- pout.write("<td>" + getServletContext().getContext(res).getRequestDispatcher(res.substring(cp.length())) + "</td>");
- }
-
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">this.getClass().getResource(...): </th>");
- pout.write("<td>" + this.getClass().getResource(res) + "</td>");
-
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">this.getClass().getClassLoader().getResource(...): </th>");
- pout.write("<td>" + this.getClass().getClassLoader().getResource(res) + "</td>");
-
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">Thread.currentThread().getContextClassLoader().getResource(...): </th>");
- pout.write("<td>" + Thread.currentThread().getContextClassLoader().getResource(res) + "</td>");
-
- pout.write("</tr><tr>\n");
- pout.write("<th align=\"right\">getServletContext().getResource(...): </th>");
- try
- {
- pout.write("<td>" + getServletContext().getResource(res) + "</td>");
- }
- catch (Exception e)
- {
- pout.write("<td>" + "" + e + "</td>");
- }
- }
-
- pout.write("</tr></table>\n");
-
- /* ------------------------------------------------------------ */
- pout.write("<h2>Request Wrappers</h2>\n");
- ServletRequest rw = request;
- int w = 0;
- while (rw != null)
- {
- pout.write((w++) + ": " + rw.getClass().getName() + "<br/>");
- if (rw instanceof HttpServletRequestWrapper)
- rw = ((HttpServletRequestWrapper) rw).getRequest();
- else if (rw instanceof ServletRequestWrapper)
- rw = ((ServletRequestWrapper) rw).getRequest();
- else
- rw = null;
- }
-
- /* ------------------------------------------------------------ */
- pout.write("<h2>Response Wrappers</h2>\n");
- ServletResponse rsw = response;
- w = 0;
- while (rsw != null)
- {
- pout.write((w++) + ": " + rsw.getClass().getName() + "<br/>");
- if (rsw instanceof HttpServletResponseWrapper)
- rsw = ((HttpServletResponseWrapper) rsw).getResponse();
- else if (rsw instanceof ServletResponseWrapper)
- rsw = ((ServletResponseWrapper) rsw).getResponse();
- else
- rsw = null;
- }
-
- pout.write("<br/>");
- pout.write("<h2>International Characters (UTF-8)</h2>");
- pout.write("LATIN LETTER SMALL CAPITAL AE<br/>\n");
- pout.write("Directly uni encoded(\\u1d01): \u1d01<br/>");
- pout.write("HTML reference (&AElig;): Æ<br/>");
- pout.write("Decimal (&#7425;): ᴁ<br/>");
- pout.write("Javascript unicode (\\u1d01) : <script language='javascript'>document.write(\"\u1d01\");</script><br/>");
- pout.write("<br/>");
- pout.write("<h2>Form to generate GET content</h2>");
- pout.write("<form method=\"GET\" action=\"" + response.encodeURL(getURI(request)) + "\">");
- pout.write("TextField: <input type=\"text\" name=\"TextField\" value=\"value\"/><br/>\n");
- pout.write("<input type=\"submit\" name=\"Action\" value=\"Submit\">");
- pout.write("</form>");
-
- pout.write("<br/>");
-
- pout.write("<h2>Form to generate POST content</h2>");
- pout.write("<form method=\"POST\" accept-charset=\"utf-8\" action=\"" + response.encodeURL(getURI(request)) + "\">");
- pout.write("TextField: <input type=\"text\" name=\"TextField\" value=\"value\"/><br/>\n");
- pout.write("Select: <select multiple name=\"Select\">\n");
- pout.write("<option>ValueA</option>");
- pout.write("<option>ValueB1,ValueB2</option>");
- pout.write("<option>ValueC</option>");
- pout.write("</select><br/>");
- pout.write("<input type=\"submit\" name=\"Action\" value=\"Submit\"><br/>");
- pout.write("</form>");
- pout.write("<br/>");
-
- pout.write("<h2>Form to generate UPLOAD content</h2>");
- pout.write("<form method=\"POST\" enctype=\"multipart/form-data\" accept-charset=\"utf-8\" action=\"" + response.encodeURL(getURI(request))
- + (request.getQueryString() == null ? "" : ("?" + request.getQueryString()))
- + "\">");
- pout.write("TextField: <input type=\"text\" name=\"TextField\" value=\"comment\"/><br/>\n");
- pout.write("File 1: <input type=\"file\" name=\"file1\" /><br/>\n");
- pout.write("File 2: <input type=\"file\" name=\"file2\" /><br/>\n");
- pout.write("<input type=\"submit\" name=\"Action\" value=\"Submit\"><br/>");
- pout.write("</form>");
-
- pout.write("<h2>Form to set Cookie</h2>");
- pout.write("<form method=\"POST\" action=\"" + response.encodeURL(getURI(request)) + "\">");
- pout.write("cookie: <input type=\"text\" name=\"cookie\" /><br/>\n");
- pout.write("value: <input type=\"text\" name=\"cookiev\" /><br/>\n");
- pout.write("<input type=\"submit\" name=\"Action\" value=\"setCookie\">");
- pout.write("</form>\n");
-
- pout.write("<h2>Form to get Resource</h2>");
- pout.write("<form method=\"POST\" action=\"" + response.encodeURL(getURI(request)) + "\">");
- pout.write("resource: <input type=\"text\" name=\"resource\" /><br/>\n");
- pout.write("<input type=\"submit\" name=\"Action\" value=\"getResource\">");
- pout.write("</form>\n");
-
- pout.write("<h2>Form to test a JDBC connection URL</h2>");
- String jdbcUser = request.getParameter("jdbc-user");
- if (jdbcUser == null || jdbcUser.length() == 0) jdbcUser = "root";
- String jdbcPass = request.getParameter("jdbc-pass");
- if (jdbcPass == null) jdbcPass = "";
- String jdbcDriverr = request.getParameter("jdbc-driver");
- if (jdbcDriverr == null || jdbcDriverr.length() == 0) jdbcDriverr = "com.mysql.jdbc.Driver";
- String jdbcQuery = request.getParameter("jdbc-query");
- if (jdbcQuery == null || jdbcQuery.length() == 0) jdbcQuery = "show tables;";
- String jdbcUrll = request.getParameter("jdbc-url");
- if (jdbcUrll == null || jdbcUrll.length() == 0) jdbcUrll = "jdbc:mysql://127.0.0.1:3306/example";
- pout.write("<form method=\"POST\" accept-charset=\"utf-8\" action=\"" + response.encodeURL(getURI(request)) + "\">");
- pout.write("JDBC Driver class: <input type=\"text\" name=\"jdbc-driver\" value=\"" + jdbcDriverr + "\"/><br/>\n");
- pout.write("JDBC URL: <input type=\"text\" name=\"jdbc-url\" value=\"" + jdbcUrll + "\"/><br/>\n");
- pout.write("JDBC Username: <input type=\"text\" name=\"jdbc-user\" value=\"" + jdbcUser + "\"/><br/>\n");
- pout.write("JDBC Password: <input type=\"text\" name=\"jdbc-pass\" value=\"" + jdbcPass + "\"/><br/>\n");
- pout.write("JDBC Query: <input type=\"text\" name=\"jdbc-query\" value=\"" + jdbcQuery + "\"/><br/>\n");
- pout.write("<input type=\"submit\" name=\"Action\" value=\"Submit\"><br/>");
- pout.write("</form>");
- pout.write("<br/>");
-
- }
- catch (Exception e)
- {
- getServletContext().log("dump", e);
- }
-
- String lines = request.getParameter("lines");
- if (lines != null)
- {
- char[] line = "<span>A line of characters. Blah blah blah blah. blooble blooble</span></br>\n".toCharArray();
- for (int l = Integer.parseInt(lines); l-- > 0;)
- {
- pout.write("<span>" + l + " </span>");
- pout.write(line);
- }
- }
-
- pout.write("</body>\n</html>\n");
-
- pout.close();
-
- if (pi != null)
- {
- if ("/ex4".equals(pi)) throw new ServletException("test ex4", new Throwable());
- if ("/ex5".equals(pi)) throw new IOException("test ex5");
- if ("/ex6".equals(pi)) throw new UnavailableException("test ex6");
- }
-
- }
-
- /* ------------------------------------------------------------ */
- @Override
- public String getServletInfo()
- {
- return "Dump Servlet";
- }
-
- /* ------------------------------------------------------------ */
- @Override
- public synchronized void destroy()
- {
- }
-
- /* ------------------------------------------------------------ */
- private String getURI(HttpServletRequest request)
- {
- String uri = (String) request.getAttribute("javax.servlet.forward.request_uri");
- if (uri == null) uri = request.getRequestURI();
- return uri;
- }
-
- /* ------------------------------------------------------------ */
- private static String toString(Object o)
- {
- if (o == null) return null;
-
- try
- {
- if (o.getClass().isArray())
- {
- StringBuffer sb = new StringBuffer();
- if (!o.getClass().getComponentType().isPrimitive())
- {
- Object[] array = (Object[]) o;
- for (int i = 0; i < array.length; i++)
- {
- if (i > 0) sb.append("\n");
- sb.append(array.getClass().getComponentType().getName());
- sb.append("[");
- sb.append(i);
- sb.append("]=");
- sb.append(toString(array[i]));
- }
- return sb.toString();
- }
- else
- {
- int length = Array.getLength(o);
- for (int i = 0; i < length; i++)
- {
- if (i > 0) sb.append("\n");
- sb.append(o.getClass().getComponentType().getName());
- sb.append("[");
- sb.append(i);
- sb.append("]=");
- sb.append(toString(Array.get(o, i)));
- }
- return sb.toString();
- }
- }
- else
- return o.toString();
- }
- catch (Exception e)
- {
- return e.toString();
- }
- }
-
- private boolean dump(HttpServletResponse response, String data, String chars, String block, String dribble, boolean flush) throws IOException
- {
- if (data != null && data.length() > 0)
- {
- long d = Long.parseLong(data);
- int b = (block != null && block.length() > 0) ? Integer.parseInt(block) : 50;
- byte[] buf = new byte[b];
- for (int i = 0; i < b; i++)
- {
-
- buf[i] = (byte) ('0' + (i % 10));
- if (i % 10 == 9) buf[i] = (byte) '\n';
- }
- buf[0] = 'o';
- OutputStream out = response.getOutputStream();
- response.setContentType("text/plain");
- while (d > 0)
- {
- if (b == 1)
- {
- out.write(d % 80 == 0 ? '\n' : '.');
- d--;
- }
- else if (d >= b)
- {
- out.write(buf);
- d = d - b;
- }
- else
- {
- out.write(buf, 0, (int) d);
- d = 0;
- }
-
- if (dribble != null)
- {
- out.flush();
- try
- {
- Thread.sleep(Long.parseLong(dribble));
- }
- catch (Exception e)
- {
- e.printStackTrace();
- break;
- }
- }
-
- }
-
- if (flush) out.flush();
-
- return true;
- }
-
- // Handle a dump of data
- if (chars != null && chars.length() > 0)
- {
- long d = Long.parseLong(chars);
- int b = (block != null && block.length() > 0) ? Integer.parseInt(block) : 50;
- char[] buf = new char[b];
- for (int i = 0; i < b; i++)
- {
- buf[i] = (char) ('0' + (i % 10));
- if (i % 10 == 9) buf[i] = '\n';
- }
- buf[0] = 'o';
- response.setContentType("text/plain");
- PrintWriter out = response.getWriter();
- while (d > 0 && !out.checkError())
- {
- if (b == 1)
- {
- out.write(d % 80 == 0 ? '\n' : '.');
- d--;
- }
- else if (d >= b)
- {
- out.write(buf);
- d = d - b;
- }
- else
- {
- out.write(buf, 0, (int) d);
- d = 0;
- }
- }
- return true;
- }
- return false;
- }
-
- private String notag(String s)
- {
- if (s == null) return "null";
- s = replace(s, "&", "&");
- s = replace(s, "<", "<");
- s = replace(s, ">", ">");
- return s;
- }
-
- /**
- * replace substrings within string.
- */
- public static String replace(String s, String sub, String with)
- {
- int c = 0;
- int i = s.indexOf(sub, c);
- if (i == -1) return s;
-
- StringBuffer buf = new StringBuffer(s.length() + with.length());
-
- synchronized (buf)
- {
- do
- {
- buf.append(s.substring(c, i));
- buf.append(with);
- c = i + sub.length();
- }
- while ((i = s.indexOf(sub, c)) != -1);
-
- if (c < s.length()) buf.append(s.substring(c, s.length()));
-
- return buf.toString();
- }
- }
-}
diff --git a/jetty-osgi/jetty-osgi-servletbridge/src/main/java/org/eclipse/jetty/osgi/servletbridge/BridgeServletExtended.java b/jetty-osgi/jetty-osgi-servletbridge/src/main/java/org/eclipse/jetty/osgi/servletbridge/BridgeServletExtended.java
deleted file mode 100644
index 99cabf6..0000000
--- a/jetty-osgi/jetty-osgi-servletbridge/src/main/java/org/eclipse/jetty/osgi/servletbridge/BridgeServletExtended.java
+++ /dev/null
@@ -1,56 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2016 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.
-// ========================================================================
-//
-
-import java.io.IOException;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.equinox.servletbridge.BridgeServlet;
-
-/**
- * Override the BridgeServlet to report on whether equinox is actually started
- * or not in case it is started asynchronously.
- *
- * @author hmalphettes
- */
-public class BridgeServletExtended extends BridgeServlet
-{
-
- @Override
- protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
- {
- if (FrameworkLauncherExtended.ASYNCH_START_IN_PROGRESS != null && req.getMethod().equals("GET"))
- {
- if (FrameworkLauncherExtended.ASYNCH_START_IN_PROGRESS)
- {
- resp.getWriter().append("Equinox is currently starting...\n");
- return;
- }
- else if (FrameworkLauncherExtended.ASYNCH_START_FAILURE != null)
- {
- resp.getWriter().append("Equinox failed to start:\n");
- FrameworkLauncherExtended.ASYNCH_START_FAILURE.printStackTrace(resp.getWriter());
- return;
- }
- }
- super.service(req, resp);
- }
-
-}
diff --git a/jetty-osgi/jetty-osgi-servletbridge/src/main/java/org/eclipse/jetty/osgi/servletbridge/FrameworkLauncherExtended.java b/jetty-osgi/jetty-osgi-servletbridge/src/main/java/org/eclipse/jetty/osgi/servletbridge/FrameworkLauncherExtended.java
deleted file mode 100644
index e35f414..0000000
--- a/jetty-osgi/jetty-osgi-servletbridge/src/main/java/org/eclipse/jetty/osgi/servletbridge/FrameworkLauncherExtended.java
+++ /dev/null
@@ -1,716 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2016 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.
-// ========================================================================
-//
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileFilter;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.Properties;
-import java.util.TreeMap;
-import java.util.jar.Attributes;
-import java.util.jar.JarOutputStream;
-import java.util.jar.Manifest;
-import java.util.zip.ZipEntry;
-
-import org.eclipse.equinox.servletbridge.FrameworkLauncher;
-
-/**
- * Extend the servletbridge FrameworkLauncher to support launching an equinox
- * installation made by p2director.
- */
-public class FrameworkLauncherExtended extends FrameworkLauncher
-{
-
- /**
- * if the OSGI_INSTALL_AREA installed area is specified as a sytem property
- * and matches a Folder on the file system, we don't copy the whole eclipse
- * installation instead we use that folder as it is
- */
- private static final String DEPLOY_IN_PLACE_WHEN_INSTALL_AREA_IS_FOLDER = "org.eclipse.equinox.servletbridge.deployinplace"; //$NON-NLS-1$
-
- public static final String FRAMEWORK_BOOTDELEGATION = "org.osgi.framework.bootdelegation";
-
- private boolean deployedInPlace = false;
-
- private URL resourceBaseAsURL = null;
-
- protected static Boolean ASYNCH_START_IN_PROGRESS;
-
- protected static Throwable ASYNCH_START_FAILURE = null;
-
- /**
- * If the start is asynch we do it in a different thread and return
- * immediately.
- */
- @Override
- public synchronized void start()
- {
- if (ASYNCH_START_IN_PROGRESS == null && "true".equals(super.config.getInitParameter("asyncStart")))
- {
- final ClassLoader webappCl = Thread.currentThread().getContextClassLoader();
- Thread th = new Thread()
- {
- public void run()
- {
- Thread.currentThread().setContextClassLoader(webappCl);
- System.out.println("Jetty-Nested: Starting equinox asynchronously.");
- FrameworkLauncherExtended.this.start();
- System.out.println("Jetty-Nested: Finished starting equinox asynchronously.");
- }
- };
- ASYNCH_START_IN_PROGRESS = true;
- try
- {
- th.start();
- }
- catch (Throwable t)
- {
- ASYNCH_START_FAILURE = t;
- if (t instanceof RuntimeException)
- {
- throw (RuntimeException) t;
- }
- else
- {
- throw new RuntimeException("Equinox failed to start", t);
- }
- }
- finally
- {
- ASYNCH_START_IN_PROGRESS = false;
- }
- }
- else
- {
- System.out.println("Jetty-Nested: Starting equinox synchronously.");
- super.start();
- System.out.println("Jetty-Nested: Finished starting equinox synchronously.");
- }
- }
-
- /**
- * try to find the resource base for this webapp by looking for the launcher
- * initialization file.
- */
- protected void initResourceBase()
- {
- try
- {
- String resourceBaseStr = System.getProperty(OSGI_INSTALL_AREA);
- if (resourceBaseStr == null || resourceBaseStr.length() == 0)
- {
- resourceBaseStr = config.getInitParameter(OSGI_INSTALL_AREA);
- }
- if (resourceBaseStr != null && resourceBaseStr.length() != 0)
- {
- // If the path starts with a reference to a system property,
- // resolve it.
- resourceBaseStr = resolveSystemProperty(resourceBaseStr);
- if (resourceBaseStr.startsWith("/WEB-INF/"))
- {
- String rpath = context.getRealPath(resourceBaseStr);
- if (rpath != null)
- {
- File rpathFile = new File(rpath);
- if (rpathFile.exists() && rpathFile.isDirectory() && rpathFile.canWrite())
- {
- resourceBaseStr = rpath;
- }
- }
- }
-
- if (resourceBaseStr.startsWith("file://"))
- {
- resourceBaseAsURL = new URL(resourceBaseStr.replace(" ", "%20")); //$NON-NLS-1$ //$NON-NLS-2$
- }
- else if (new File(resourceBaseStr).exists())
- {
- resourceBaseAsURL = new URL("file://" + new File(resourceBaseStr).getCanonicalPath().replace(" ", "%20")); //$NON-NLS-1$ //$NON-NLS-2$
- }
- else
- {
- resourceBaseAsURL = context.getResource(resourceBaseStr);
- }
- }
- else
- {
- if (context.getResource(RESOURCE_BASE + ECLIPSE) != null)
- {
- resourceBase = RESOURCE_BASE + ECLIPSE;
- }
- else
- {
- super.initResourceBase();
- }
- resourceBaseAsURL = context.getResource(resourceBase);
- }
- }
- catch (MalformedURLException e)
- {
- // ignore
- }
- catch (IOException e)
- {
- // ignore
- }
- if (resourceBaseAsURL != null && resourceBaseAsURL.getProtocol().equals("file"))
- {
- File resBase = new File(resourceBaseAsURL.getPath());
- if (resBase.exists() && resBase.isDirectory()
- && !Boolean.FALSE.toString().equalsIgnoreCase(System.getProperty(DEPLOY_IN_PLACE_WHEN_INSTALL_AREA_IS_FOLDER)))
- {
- __setPlatformDirectory(resBase);
- deployedInPlace = true;
- }
- }
- }
-
- /**
- * Override this method to be able to set default system properties computed
- * on the fly depending on the environment where equinox and jetty-osgi are
- * deployed.
- *
- * @param resource - The target to read properties from
- * @return the properties
- */
- protected Properties loadProperties(String resource)
- {
- Properties props = super.loadProperties(resource);
- if (resource.equals(resourceBase + LAUNCH_INI) && deployedInPlace)
- {
- String osgiInstall = props.getProperty(OSGI_INSTALL_AREA);
- if (osgiInstall == null)
- {
- // compute the osgi install dynamically.
- props.put(OSGI_INSTALL_AREA, getPlatformDirectory().getAbsolutePath());
- }
- String osgiFramework = props.getProperty(OSGI_FRAMEWORK);
- File pluginsFolder = null;
- if (osgiFramework == null && getPlatformDirectory() != null)
- {
- File osgiFrameworkF = findOsgiFramework(getPlatformDirectory());
- pluginsFolder = osgiFrameworkF.getParentFile();
- props.put(OSGI_FRAMEWORK, osgiFrameworkF.getAbsoluteFile().getAbsolutePath());
- }
- String osgiFrameworkExtensions = props.getProperty(OSGI_FRAMEWORK_EXTENSIONS);
- if (osgiFrameworkExtensions == null)
- {
- // this bundle will make the javax.servlet and
- // javax.servlet.http packages passed from
- // the bootstrap classloader into equinox
- osgiFrameworkExtensions = "org.eclipse.equinox.servletbridge.extensionbundle";
- }
- File configIni = new File(getPlatformDirectory(), "configuration/config.ini");
- Properties configIniProps = new Properties();
- if (configIni.exists())
- {
- System.out.println("Got the " + configIni.getAbsolutePath());
- InputStream configIniStream = null;
- try
- {
- configIniStream = new FileInputStream(configIni);
- configIniProps.load(configIniStream);
- }
- catch (IOException ioe)
- {
-
- }
- finally
- {
- try
- {
- configIniStream.close();
- }
- catch (IOException ioe2)
- {
- }
- }
- String confIniFrameworkExt = configIniProps.getProperty(OSGI_FRAMEWORK_EXTENSIONS);
- if (confIniFrameworkExt != null)
- {
- osgiFrameworkExtensions = osgiFrameworkExtensions + "," + confIniFrameworkExt;
- }
- }
- else
- {
- System.out.println("Unable to locate the " + configIni.getAbsolutePath());
- }
- props.setProperty(OSGI_FRAMEWORK_EXTENSIONS, osgiFrameworkExtensions);
- // __deployExtensionBundle(pluginsFolder);
- deployExtensionBundle(pluginsFolder, true);
-
- String bootDeleg = props.getProperty(FRAMEWORK_BOOTDELEGATION);
- if (bootDeleg == null)
- {
- bootDeleg = configIniProps.getProperty(FRAMEWORK_BOOTDELEGATION);
- }
- if (bootDeleg == null || bootDeleg.indexOf("javax.servlet.http") == -1)
- {
- String add = "javax.servlet,javax.servlet.http,javax.servlet.resources";
- if (bootDeleg != null)
- {
- bootDeleg += add;
- }
- else
- {
- bootDeleg = add;
- }
- props.setProperty(FRAMEWORK_BOOTDELEGATION, bootDeleg);
- }
-
- String jettyHome = System.getProperty("jetty.home");
- if (jettyHome == null)
- {
- jettyHome = getPlatformDirectory().getAbsolutePath();
- System.setProperty("jetty.home", jettyHome);
- props.setProperty("jetty.home", jettyHome);
- }
- else
- {
- jettyHome = resolveSystemProperty(jettyHome);
- }
- String etcJettyXml = System.getProperty("jetty.etc.config.urls");
- if (etcJettyXml == null)
- {
- etcJettyXml = "etc/jetty.xml";
- if (new File(jettyHome, "etc/jetty-nested.xml").exists())
- {
- etcJettyXml += ",etc/jetty-nested.xml";
- }
- System.setProperty("jetty.etc.config.urls", etcJettyXml);
- props.setProperty("jetty.etc.config.urls", etcJettyXml);
- }
- String startLevel = System.getProperty("osgi.startLevel");
- if (startLevel == null)
- {
- startLevel = props.getProperty("osgi.startLevel");
- if (startLevel == null)
- {
- startLevel = configIniProps.getProperty("osgi.startLevel");
- }
- if (startLevel != null)
- {
- props.setProperty("osgi.startLevel", startLevel);
- System.setProperty("osgi.startLevel", startLevel);
- }
- }
- String logback = System.getProperty("logback.configurationFile");
- if (logback == null)
- {
- File etcLogback = new File(jettyHome, "etc/logback-nested.xml");
- if (!etcLogback.exists())
- {
- etcLogback = new File(jettyHome, "etc/logback.xml");
- }
- if (etcLogback.exists())
- {
- System.setProperty("logback.configurationFile", etcLogback.getAbsolutePath());
- props.setProperty("logback.configurationFile", etcLogback.getAbsolutePath());
- }
- }
- else
- {
- logback = resolveSystemProperty(logback);
- }
- System.out.println("sysout: logback.configurationFile=" + System.getProperty("logback.configurationFile"));
- }
- return props;
- }
-
- /**
- * Look for the eclipse.ini file. or any *.ini Search for the argument
- * -startup The next line is a relative path to the launcher osgi bundle:
- * ../bundlepool/plugins/org.eclipse.equinox.launcher_1.1.0.v20100507.jar
- * Get that file, get the parent folder. This is where the plugins are
- * located. In that folder look for the
- *
- * @param installFolder
- * @return The osgi framework bundle.
- */
- private File findOsgiFramework(File installFolder)
- {
- File[] fs = installFolder.listFiles();
- for (int i = 0; i < fs.length; i++)
- {
- File f = fs[i];
- if (f.isFile() && f.getName().endsWith(".ini") && !f.getName().equals(LAUNCH_INI))
- {
- BufferedReader br = null;
- try
- {
- br = new BufferedReader(new InputStreamReader(new FileInputStream(f)));
- String line = null;
- String pathToLauncherJar = null;
- boolean gotStartArg = false;
- while ((line = br.readLine()) != null)
- {
- if (gotStartArg)
- {
- pathToLauncherJar = line.trim();
- if (pathToLauncherJar.length() == 0)
- {
- continue;
- }
- break;
- }
- else if (line.trim().equals("-startup"))
- {
- gotStartArg = true;
- }
- }
- if (pathToLauncherJar != null)
- {
- File currFolder = getPlatformDirectory();
- String oriStartup = pathToLauncherJar;
- while (pathToLauncherJar.startsWith("../"))
- {
- currFolder = currFolder.getParentFile();
- pathToLauncherJar = pathToLauncherJar.substring(3);
- }
- File pluginsfolder = new File(currFolder, pathToLauncherJar).getParentFile();
- // System.err.println("Got the pluginsfolder " +
- // pluginsfolder);
- if (!pluginsfolder.exists()) { throw new IllegalStateException("The -startup argument in " + f.getPath()
- + " is "
- + oriStartup
- + ". It points to "
- + pluginsfolder.getPath()
- + " plugins directory that does not exists."); }
- TreeMap osgis = new TreeMap();
- File[] plugins = pluginsfolder.listFiles();
- for (int j = 0; j < plugins.length; j++)
- {
- File b = plugins[j];
- if (b.isFile() && b.getName().startsWith(FRAMEWORK_BUNDLE_NAME + "_") && b.getName().endsWith(".jar"))
- {
- osgis.put(b.getName(), b);
- }
- }
- if (osgis.isEmpty()) { throw new IllegalStateException("The -startup argument in " + f.getPath()
- + " is "
- + oriStartup
- + ". It points to "
- + pluginsfolder.getPath()
- + " plugins directory but there is no org.eclipse.osgi.*.jar files there."); }
- File osgiFramework = (File) osgis.values().iterator().next();
- String path = osgiFramework.getPath();
- System.err.println("Using " + path + " for the osgi framework.");
- return osgiFramework;
- }
- }
- catch (IOException ioe)
- {
- //
- }
- finally
- {
- if (br != null) try
- {
- br.close();
- }
- catch (IOException ii)
- {
- }
- }
-
- }
- }
- return null;
- }
-
- /**
- * recursively substitute the ${sysprop} by their actual system property.
- * ${sysprop,defaultvalue} will use 'defaultvalue' as the value if no
- * sysprop is defined. Not the most efficient code but we are shooting for
- * simplicity and speed of development here.
- *
- * @param value
- * @return
- */
- public static String resolveSystemProperty(String value)
- {
- int ind = value.indexOf("${");
- if (ind == -1) { return value; }
- int ind2 = value.indexOf('}', ind);
- if (ind2 == -1) { return value; }
- String sysprop = value.substring(ind + 2, ind2);
- String defaultValue = null;
- int comma = sysprop.indexOf(',');
- if (comma != -1 && comma + 1 != sysprop.length())
- {
- defaultValue = sysprop.substring(comma + 1);
- defaultValue = resolveSystemProperty(defaultValue);
- sysprop = sysprop.substring(0, comma);
- }
- else
- {
- defaultValue = "${" + sysprop + "}";
- }
-
- String v = System.getProperty(sysprop);
-
- String reminder = value.length() > ind2 + 1 ? value.substring(ind2 + 1) : "";
- reminder = resolveSystemProperty(reminder);
- if (v != null)
- {
- return value.substring(0, ind) + v + reminder;
- }
- else
- {
- return value.substring(0, ind) + defaultValue + reminder;
- }
- }
-
- // introspection trick to be able to set the private field platformDirectory
- private static Field _field;
-
- void __setPlatformDirectory(File platformDirectory)
- {
- try
- {
- if (_field == null)
- {
- _field = org.eclipse.equinox.servletbridge.FrameworkLauncher.class.getDeclaredField("platformDirectory");
- _field.setAccessible(true);
- }
- _field.set(this, platformDirectory);
- }
- catch (SecurityException e)
- {
- e.printStackTrace();
- }
- catch (NoSuchFieldException e)
- {
- e.printStackTrace();
- }
- catch (IllegalArgumentException e)
- {
- e.printStackTrace();
- }
- catch (IllegalAccessException e)
- {
- e.printStackTrace();
- }
- }
-
- // introspection trick to invoke the generateExtensionBundle method
- private static Method _deployExtensionBundleMethod;
-
- private void __deployExtensionBundle(File plugins)
- {
- // look for the extensionbundle
- // if it is already there no need to do something:
- for (String file : plugins.list())
- {
- if (file.startsWith("org.eclipse.equinox.servletbridge.extensionbundle"))// EXTENSIONBUNDLE_DEFAULT_BSN
- { return; }
- }
-
- try
- {
- // invoke deployExtensionBundle(File plugins)
- if (_deployExtensionBundleMethod == null)
- {
- _deployExtensionBundleMethod = FrameworkLauncher.class.getDeclaredMethod("deployExtensionBundle", File.class);
- _deployExtensionBundleMethod.setAccessible(true);
- }
- _deployExtensionBundleMethod.invoke(this, plugins);
- }
- catch (Throwable t)
- {
- t.printStackTrace();
- }
- }
-
- // --end of introspection to invoke deployExtensionBundle
-
- // from Framework with support for the equinox hook
- private static final String EXTENSIONBUNDLE_DEFAULT_BSN = "org.eclipse.equinox.servletbridge.extensionbundle"; //$NON-NLS-1$
-
- private static final String EXTENSIONBUNDLE_DEFAULT_VERSION = "1.2.0"; //$NON-NLS-1$
-
- private static final String MANIFEST_VERSION = "Manifest-Version"; //$NON-NLS-1$
-
- private static final String BUNDLE_MANIFEST_VERSION = "Bundle-ManifestVersion"; //$NON-NLS-1$
-
- private static final String BUNDLE_NAME = "Bundle-Name"; //$NON-NLS-1$
-
- private static final String BUNDLE_SYMBOLIC_NAME = "Bundle-SymbolicName"; //$NON-NLS-1$
-
- private static final String BUNDLE_VERSION = "Bundle-Version"; //$NON-NLS-1$
-
- private static final String FRAGMENT_HOST = "Fragment-Host"; //$NON-NLS-1$
-
- private static final String EXPORT_PACKAGE = "Export-Package"; //$NON-NLS-1$
-
- private static final String CONFIG_EXTENDED_FRAMEWORK_EXPORTS = "extendedFrameworkExports"; //$NON-NLS-1$
-
- private void deployExtensionBundle(File plugins, boolean configureEquinoxHook)
- {
- // we might want to parameterize the extension bundle BSN in the future
- final String extensionBundleBSN = EXTENSIONBUNDLE_DEFAULT_BSN;
- File extensionBundleFile = findExtensionBundleFile(plugins, extensionBundleBSN);
-
- if (extensionBundleFile == null)
- generateExtensionBundle(plugins, extensionBundleBSN, EXTENSIONBUNDLE_DEFAULT_VERSION, configureEquinoxHook);
- else
- /*
- * if (Boolean.valueOf(config.getInitParameter(
- * CONFIG_OVERRIDE_AND_REPLACE_EXTENSION_BUNDLE)).booleanValue())
- */{
- String extensionBundleVersion = findExtensionBundleVersion(extensionBundleFile, extensionBundleBSN);
- if (extensionBundleFile.isDirectory())
- {
- deleteDirectory(extensionBundleFile);
- }
- else
- {
- extensionBundleFile.delete();
- }
- generateExtensionBundle(plugins, extensionBundleBSN, extensionBundleVersion, true);
- // } else {
- // processExtensionBundle(extensionBundleFile);
- }
- }
-
- private File findExtensionBundleFile(File plugins, final String extensionBundleBSN)
- {
- FileFilter extensionBundleFilter = new FileFilter()
- {
- public boolean accept(File candidate)
- {
- return candidate.getName().startsWith(extensionBundleBSN + "_"); //$NON-NLS-1$
- }
- };
- File[] extensionBundles = plugins.listFiles(extensionBundleFilter);
- if (extensionBundles.length == 0) return null;
-
- if (extensionBundles.length > 1)
- {
- for (int i = 1; i < extensionBundles.length; i++)
- {
- if (extensionBundles[i].isDirectory())
- {
- deleteDirectory(extensionBundles[i]);
- }
- else
- {
- extensionBundles[i].delete();
- }
- }
- }
- return extensionBundles[0];
- }
-
- private String findExtensionBundleVersion(File extensionBundleFile, String extensionBundleBSN)
- {
- String fileName = extensionBundleFile.getName();
- if (fileName.endsWith(".jar")) { return fileName.substring(extensionBundleBSN.length() + 1, fileName.length() - ".jar".length()); }
- return fileName.substring(extensionBundleBSN.length() + 1);
- }
-
- private void generateExtensionBundle(File plugins, String extensionBundleBSN, String extensionBundleVersion, boolean configureEquinoxHook)
- {
- Manifest mf = new Manifest();
- Attributes attribs = mf.getMainAttributes();
- attribs.putValue(MANIFEST_VERSION, "1.0"); //$NON-NLS-1$
- attribs.putValue(BUNDLE_MANIFEST_VERSION, "2"); //$NON-NLS-1$
- attribs.putValue(BUNDLE_NAME, "Servletbridge Extension Bundle"); //$NON-NLS-1$
- attribs.putValue(BUNDLE_SYMBOLIC_NAME, extensionBundleBSN);
- attribs.putValue(BUNDLE_VERSION, extensionBundleVersion);
- attribs.putValue(FRAGMENT_HOST, "system.bundle; extension:=framework"); //$NON-NLS-1$
-
- String servletVersion = context.getMajorVersion() + "." + context.getMinorVersion(); //$NON-NLS-1$
- String packageExports = "org.eclipse.equinox.servletbridge; version=1.1"
- + ", javax.servlet; version="
- + servletVersion
- + ", javax.servlet.http; version="
- + servletVersion
- + ", javax.servlet.resources; version="
- + servletVersion;
-
- String extendedExports = config.getInitParameter(CONFIG_EXTENDED_FRAMEWORK_EXPORTS);
- if (extendedExports != null && extendedExports.trim().length() != 0) packageExports += ", " + extendedExports; //$NON-NLS-1$
-
- attribs.putValue(EXPORT_PACKAGE, packageExports);
- writeJarFile(new File(plugins, extensionBundleBSN + "_" + extensionBundleVersion + ".jar"), mf, configureEquinoxHook); //$NON-NLS-1$
- }
-
- private void writeJarFile(File jarFile, Manifest mf, boolean configureEquinoxHook)
- {
- try
- {
- JarOutputStream jos = null;
- try
- {
- jos = new JarOutputStream(new FileOutputStream(jarFile), mf);
-
- if (configureEquinoxHook)
- {
- // hook configurator properties:
- ZipEntry e = new ZipEntry("hookconfigurators.properties");
- jos.putNextEntry(e);
- Properties props = new Properties();
- props.put("hook.configurators", "org.eclipse.jetty.osgi.servletbridge.hook.ServletBridgeClassLoaderDelegateHook");
- props.store(jos, "");
- jos.closeEntry();
-
- // the hook class
- e = new ZipEntry("org/eclipse/jetty/osgi/servletbridge/hook/ServletBridgeClassLoaderDelegateHook.class");
- jos.putNextEntry(e);
- InputStream in = getClass().getResourceAsStream("/org/eclipse/jetty/osgi/servletbridge/hook/ServletBridgeClassLoaderDelegateHook.class");
-
- byte[] buffer = new byte[512];
- try
- {
- int n;
- while ((n = in.read(buffer)) != -1)
- {
- jos.write(buffer, 0, n);
- }
- }
- finally
- {
- in.close();
- }
- jos.closeEntry();
- }
-
- jos.finish();
- }
- finally
- {
- if (jos != null) jos.close();
- }
- }
- catch (IOException e)
- {
- context.log("Error writing extension bundle", e); //$NON-NLS-1$
- }
- }
- // --from Framework with support for the equinox hook
-
-}
diff --git a/jetty-osgi/jetty-osgi-servletbridge/src/main/java/org/eclipse/jetty/osgi/servletbridge/hook/ServletBridgeClassLoaderDelegateHook.java b/jetty-osgi/jetty-osgi-servletbridge/src/main/java/org/eclipse/jetty/osgi/servletbridge/hook/ServletBridgeClassLoaderDelegateHook.java
deleted file mode 100644
index 523240c..0000000
--- a/jetty-osgi/jetty-osgi-servletbridge/src/main/java/org/eclipse/jetty/osgi/servletbridge/hook/ServletBridgeClassLoaderDelegateHook.java
+++ /dev/null
@@ -1,97 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2016 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.
-// ========================================================================
-//
-
-import java.io.FileNotFoundException;
-import java.net.URL;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.Set;
-
-import org.eclipse.core.runtime.adaptor.EclipseStarter;
-import org.eclipse.osgi.baseadaptor.HookConfigurator;
-import org.eclipse.osgi.baseadaptor.HookRegistry;
-import org.eclipse.osgi.framework.adaptor.BundleClassLoader;
-import org.eclipse.osgi.framework.adaptor.BundleData;
-import org.eclipse.osgi.framework.adaptor.ClassLoaderDelegateHook;
-import org.eclipse.osgi.internal.loader.BundleLoader;
-
-/**
- * With some complex osgi products, experience shows that using a system bundle
- * extension to pass certain packages from the bootstrapping server to equinox
- * fails. The bundles keep loading javax.servlet.http from the javax.servlet
- * bundle. This class is in fact copied into the servletbridge.extensionbundle;
- * it is not loaded by the webapp.
- */
-public class ServletBridgeClassLoaderDelegateHook implements ClassLoaderDelegateHook, HookConfigurator
-{
-
- private static Set<String> packagesInBootstrapClassLoader = new HashSet<String>();
- static
- {
- packagesInBootstrapClassLoader.add("javax.servlet");
- packagesInBootstrapClassLoader.add("javax.servlet.http");
- }
-
- public void addHooks(HookRegistry hookRegistry)
- {
- hookRegistry.addClassLoaderDelegateHook(this);
- }
-
- public Class preFindClass(String name, BundleClassLoader classLoader, BundleData data) throws ClassNotFoundException
- {
- String pkgName = BundleLoader.getPackageName(name);
- if (packagesInBootstrapClassLoader.contains(pkgName)) { return EclipseStarter.class.getClassLoader().loadClass(name); }
- return null;
- }
-
- public Class postFindClass(String name, BundleClassLoader classLoader, BundleData data) throws ClassNotFoundException
- {
- return null;
- }
-
- public URL preFindResource(String name, BundleClassLoader classLoader, BundleData data) throws FileNotFoundException
- {
- return null;
- }
-
- public URL postFindResource(String name, BundleClassLoader classLoader, BundleData data) throws FileNotFoundException
- {
- return null;
- }
-
- public Enumeration preFindResources(String name, BundleClassLoader classLoader, BundleData data) throws FileNotFoundException
- {
- return null;
- }
-
- public Enumeration postFindResources(String name, BundleClassLoader classLoader, BundleData data) throws FileNotFoundException
- {
- return null;
- }
-
- public String preFindLibrary(String name, BundleClassLoader classLoader, BundleData data) throws FileNotFoundException
- {
- return null;
- }
-
- public String postFindLibrary(String name, BundleClassLoader classLoader, BundleData data)
- {
- return null;
- }
-
-}
diff --git a/jetty-osgi/jetty-osgi-servletbridge/src/main/webapp/WEB-INF/web.xml b/jetty-osgi/jetty-osgi-servletbridge/src/main/webapp/WEB-INF/web.xml
deleted file mode 100644
index c61dee5..0000000
--- a/jetty-osgi/jetty-osgi-servletbridge/src/main/webapp/WEB-INF/web.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
- <display-name>jettyservletbridge</display-name>
- <servlet id="proxy">
- <servlet-name>proxy</servlet-name>
- <display-name>Transparent Proxy Servlet and Equinox Framework Controller</display-name>
- <description>Transparent Proxy Servlet and Equinox Framework Controller</description>
- <servlet-class>org.eclipse.jetty.osgi.servletbridge.BridgeServletExtended</servlet-class>
- <!-- Point to a folder where an equinox installation exists.
- When this parameter is defined, that equinox installation is launched in place.
- The parameter can also be passed as a system property. -->
- <init-param>
- <param-name>osgi.install.area</param-name>
- <param-value>/WEB-INF/eclipse</param-value>
- </init-param>
- <!-- Start equinox in a different thread and display a simple
- a simple status message until it is started. -->
- <init-param>
- <param-name>asyncStart</param-name>
- <param-value>true</param-value>
- </init-param>
- <init-param>
- <param-name>commandline</param-name>
- <param-value>-console</param-value>
- </init-param>
- <init-param>
- <param-name>enableFrameworkControls</param-name>
- <param-value>true</param-value>
- </init-param>
- <!--
- org.eclipse.equinox.servletbridge and the Servlet API are exported automatically to the underlying OSGi framework.
- The extendedFrameworkExports parameter allows the specification of additional java package exports.
- The format is a comma separated list of exports as specified by the "Export-Package" bundle manifest header.
- For example: com.mycompany.exports; version=1.0.0, com.mycompany.otherexports; version=1.0.0
- -->
- <init-param>
- <param-name>extendedFrameworkExports</param-name>
- <param-value></param-value>
- </init-param>
-
- <!--
- You can specify your own framework launcher here.
- The default is: org.eclipse.equinox.servletbridge.FrameworkLauncher -->
- <init-param>
- <param-name>frameworkLauncherClass</param-name>
- <param-value>org.eclipse.jetty.osgi.servletbridge.FrameworkLauncherExtended</param-value>
- <!--param-value>org.eclipse.equinox.servletbridge.FrameworkLauncher</param-value-->
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
-
- <servlet>
- <servlet-name>dump</servlet-name>
- <servlet-class>org.eclipse.jetty.nested.Dump</servlet-class>
- <load-on-startup>1</load-on-startup>
- </servlet>
-
- <servlet-mapping>
- <servlet-name>dump</servlet-name>
- <url-pattern>/dump/*</url-pattern>
- </servlet-mapping>
- <servlet-mapping>
- <servlet-name>proxy</servlet-name>
- <url-pattern>/*</url-pattern>
- </servlet-mapping>
-
-</web-app>
\ No newline at end of file
diff --git a/jetty-osgi/pom.xml b/jetty-osgi/pom.xml
index 6dc3e22..65c947e 100644
--- a/jetty-osgi/pom.xml
+++ b/jetty-osgi/pom.xml
@@ -3,7 +3,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>org.eclipse.jetty.osgi</groupId>
@@ -22,10 +22,8 @@
<modules>
<module>jetty-osgi-boot</module>
<module>jetty-osgi-boot-jsp</module>
- <module>jetty-osgi-boot-logback</module>
<module>jetty-osgi-boot-warurl</module>
<module>jetty-osgi-httpservice</module>
- <module>jetty-osgi-equinoxtools</module>
<module>test-jetty-osgi-webapp</module>
<module>test-jetty-osgi-context</module>
<module>test-jetty-osgi</module>
@@ -60,6 +58,13 @@
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <skipTests>true</skipTests>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.8</version>
<configuration>
@@ -77,7 +82,7 @@
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
+ <artifactId>jetty-annotations</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
@@ -145,6 +150,23 @@
<artifactId>servletbridge</artifactId>
<version>${equinox-servletbridge-version}</version>
</dependency-->
+ <!-- not ready <dependency>
+ <groupId>org.mortbay.jetty</groupId>
+ <artifactId>jsp-impl</artifactId>
+ <version>${jsp-impl-2.2-glassfish-version}</version>
+ </dependency-->
+<!--
+ <dependency>
+ <groupId>org.mortbay.jetty</groupId>
+ <artifactId>jsp-2.1-glassfish</artifactId>
+ <version>${jsp-2.1-glassfish-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.mortbay.jetty</groupId>
+ <artifactId>jsp-api-2.1-glassfish</artifactId>
+ <version>${jsp-2.1-glassfish-version}</version>
+ </dependency>
+-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
diff --git a/jetty-osgi/test-jetty-osgi-context/pom.xml b/jetty-osgi/test-jetty-osgi-context/pom.xml
index a1d2cb5..095f6ae 100644
--- a/jetty-osgi/test-jetty-osgi-context/pom.xml
+++ b/jetty-osgi/test-jetty-osgi-context/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -17,6 +17,7 @@
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
+ <version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.osgi</groupId>
@@ -108,8 +109,8 @@
compilation time. -->
<_nouses>true</_nouses>
<Import-Package>
- javax.servlet;version="2.5.0",
- javax.servlet.resources;version="2.5.0",
+ javax.servlet;version="2.6.0",
+ javax.servlet.resources;version="2.6.0",
org.osgi.framework,
org.osgi.service.cm;version="1.2.0",
org.osgi.service.packageadmin,
@@ -123,7 +124,7 @@
org.xml.sax.helpers,
*
</Import-Package>
- <DynamicImport-Package>org.eclipse.jetty.*;version="[7.6,8)"</DynamicImport-Package>
+ <DynamicImport-Package>org.eclipse.jetty.*;version="[8.1,9)"</DynamicImport-Package>
<!--Require-Bundle/-->
<!-- Bundle-RequiredExecutionEnvironment>J2SE-1.5</Bundle-RequiredExecutionEnvironment -->
</instructions>
diff --git a/jetty-osgi/test-jetty-osgi-webapp/pom.xml b/jetty-osgi/test-jetty-osgi-webapp/pom.xml
index 5eb44cb..09f0f83 100644
--- a/jetty-osgi/test-jetty-osgi-webapp/pom.xml
+++ b/jetty-osgi/test-jetty-osgi-webapp/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -118,7 +118,7 @@
org.xml.sax.helpers,
*
</Import-Package>
- <DynamicImport-Package>org.eclipse.jetty.*;version="[7.6,8)"</DynamicImport-Package>
+ <DynamicImport-Package>org.eclipse.jetty.*;version="[8.1,9)"</DynamicImport-Package>
<!--Require-Bundle/-->
<!-- Bundle-RequiredExecutionEnvironment>J2SE-1.5</Bundle-RequiredExecutionEnvironment -->
</instructions>
diff --git a/jetty-osgi/test-jetty-osgi/pom.xml b/jetty-osgi/test-jetty-osgi/pom.xml
index 39bd34e..934161e 100644
--- a/jetty-osgi/test-jetty-osgi/pom.xml
+++ b/jetty-osgi/test-jetty-osgi/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -12,6 +12,7 @@
<url>http://www.eclipse.org/jetty</url>
<properties>
<bundle-symbolic-name>${project.groupId}.boot.test</bundle-symbolic-name>
+ <jetty-orbit-url>http://download.eclipse.org/jetty/orbit/</jetty-orbit-url>
<assembly-directory>target/distribution</assembly-directory>
<paxexam-version>1.2.0</paxexam-version>
</properties>
@@ -224,7 +225,7 @@
<version>${orbit-servlet-api-version}</version>
<overWrite>true</overWrite>
<outputDirectory>${assembly-directory}/lib</outputDirectory>
- <destFileName>servlet-api-2.5.jar</destFileName>
+ <destFileName>servlet-api-3.0.jar</destFileName>
</artifactItem>
</artifactItems>
</configuration>
diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/boot/TestJettyOSGiBootWebAppAsService.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/boot/TestJettyOSGiBootWebAppAsService.java
index ca8c1c6..d4fdec4 100644
--- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/boot/TestJettyOSGiBootWebAppAsService.java
+++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/boot/TestJettyOSGiBootWebAppAsService.java
@@ -39,6 +39,7 @@
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.CoreOptions;
import org.ops4j.pax.exam.Inject;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.container.def.PaxRunnerOptions;
@@ -72,7 +73,8 @@
{
ArrayList<Option> options = new ArrayList<Option>();
options.addAll(TestJettyOSGiBootCore.provisionCoreJetty());
-
+ options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*",
+ "org.w3c.*", "javax.xml.*"));
File base = MavenTestingUtils.getBasedir();
File src = new File (base, "src");
File tst = new File (src, "test");
diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/boot/TestJettyOSGiBootWithJsp.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/boot/TestJettyOSGiBootWithJsp.java
index e7dd40d..0c26ade 100644
--- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/boot/TestJettyOSGiBootWithJsp.java
+++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/boot/TestJettyOSGiBootWithJsp.java
@@ -134,14 +134,13 @@
* plus your testcase, wrapped into a bundle called pax-exam-probe
*/
@Test
-
public void listBundles() throws Exception
{
Map<String,Bundle> bundlesIndexedBySymbolicName = new HashMap<String, Bundle>();
for( Bundle b : bundleContext.getBundles() )
{
bundlesIndexedBySymbolicName.put(b.getSymbolicName(), b);
- System.err.println("Got " + b.getSymbolicName() + " " + b.getVersion().toString() + " " + b.getState());
+ System.err.println("Got " + b.getSymbolicName());
}
Bundle osgiBoot = bundlesIndexedBySymbolicName.get("org.eclipse.jetty.osgi.boot");
@@ -150,11 +149,11 @@
Bundle osgiBootJsp = bundlesIndexedBySymbolicName.get("org.eclipse.jetty.osgi.boot.jsp");
Assert.assertNotNull("Could not find the org.eclipse.jetty.osgi.boot.jsp bundle", osgiBootJsp);
- Assert.assertTrue("The fragment jsp is not correctly resolved " + osgiBootJsp.getState(), osgiBootJsp.getState() == Bundle.RESOLVED);
+ Assert.assertTrue("The fragment jsp is not correctly resolved", osgiBootJsp.getState() == Bundle.RESOLVED);
Bundle testWebBundle = bundlesIndexedBySymbolicName.get("org.eclipse.jetty.test-jetty-webapp");
- Assert.assertNotNull("Could not find the org.eclipse.jetty.test-jetty-webapp bundle", osgiBootJsp);
- Assert.assertTrue("The bundle org.eclipse.jetty.test-jetty-webapp is not correctly resolved", testWebBundle.getState() == Bundle.ACTIVE);
+ Assert.assertNotNull("Could not find the test-jetty-webapp bundle", testWebBundle);
+ Assert.assertTrue("The test-jetty-webapp bundle is not correctly resolved", testWebBundle.getState() == Bundle.ACTIVE);
//now test the jsp/dump.jsp
HttpClient client = new HttpClient();
diff --git a/jetty-overlay-deployer/pom.xml b/jetty-overlay-deployer/pom.xml
index 469db75..443d880 100644
--- a/jetty-overlay-deployer/pom.xml
+++ b/jetty-overlay-deployer/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-overlay-deployer</artifactId>
diff --git a/jetty-overlay-deployer/src/main/java/org/eclipse/jetty/overlays/OverlayedAppProvider.java b/jetty-overlay-deployer/src/main/java/org/eclipse/jetty/overlays/OverlayedAppProvider.java
index fa1c9b8..eb4b1f4 100644
--- a/jetty-overlay-deployer/src/main/java/org/eclipse/jetty/overlays/OverlayedAppProvider.java
+++ b/jetty-overlay-deployer/src/main/java/org/eclipse/jetty/overlays/OverlayedAppProvider.java
@@ -32,6 +32,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
@@ -389,7 +390,7 @@
List<URL> libs = new ArrayList<URL>();
for (String jar :instance_lib.list())
{
- if (!jar.toLowerCase().endsWith(".jar"))
+ if (!jar.toLowerCase(Locale.ENGLISH).endsWith(".jar"))
continue;
libs.add(instance_lib.addPath(jar).getURL());
}
@@ -610,7 +611,7 @@
{
for (String jar :lib.list())
{
- if (!jar.toLowerCase().endsWith(".jar"))
+ if (!jar.toLowerCase(Locale.ENGLISH).endsWith(".jar"))
continue;
libs.add(lib.addPath(jar).getURL());
}
@@ -832,12 +833,12 @@
File origin = new File(new URI(_scanDir.toURI()+ruri));
String name=origin.getName();
- Monitor monitor = Monitor.valueOf(origin.getParentFile().getName().toUpperCase());
+ Monitor monitor = Monitor.valueOf(origin.getParentFile().getName().toUpperCase(Locale.ENGLISH));
String ext=".war";
// check directory vs archive
- if (origin.isDirectory() || !origin.exists() && !ruri.toLowerCase().endsWith(ext))
+ if (origin.isDirectory() || !origin.exists() && !ruri.toLowerCase(Locale.ENGLISH).endsWith(ext))
{
// directories have priority over archives
directory=origin;
@@ -846,7 +847,7 @@
else
{
// check extension name
- if (!ruri.toLowerCase().endsWith(ext))
+ if (!ruri.toLowerCase(Locale.ENGLISH).endsWith(ext))
continue;
name=name.substring(0,name.length()-4);
diff --git a/jetty-plus/pom.xml b/jetty-plus/pom.xml
index c688b05..58ff98b 100644
--- a/jetty-plus/pom.xml
+++ b/jetty-plus/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-plus</artifactId>
@@ -29,7 +29,7 @@
<configuration>
<instructions>
<_versionpolicy> </_versionpolicy>
- <Import-Package>javax.sql.*,javax.security.*,javax.naming.*,javax.servlet.*;version="[2.5,3.0)",javax.transaction.*;version="[1.1,1.2)",*</Import-Package>
+ <Import-Package>javax.sql.*,javax.security.*,javax.naming.*,javax.servlet.*;version="2.6.0",javax.transaction.*;version="[1.1,1.2)",*</Import-Package>
</instructions>
</configuration>
</execution>
diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/ContainerInitializer.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/ContainerInitializer.java
new file mode 100644
index 0000000..7ed4ead
--- /dev/null
+++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/ContainerInitializer.java
@@ -0,0 +1,114 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.plus.annotation;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.servlet.ServletContainerInitializer;
+import javax.servlet.ServletContext;
+
+import org.eclipse.jetty.util.Loader;
+import org.eclipse.jetty.webapp.WebAppContext;
+
+public class ContainerInitializer
+{
+ protected ServletContainerInitializer _target;
+ protected Class[] _interestedTypes;
+ protected Set<String> _applicableTypeNames;
+ protected Set<String> _annotatedTypeNames;
+
+
+ public void setTarget (ServletContainerInitializer target)
+ {
+ _target = target;
+ }
+
+ public ServletContainerInitializer getTarget ()
+ {
+ return _target;
+ }
+
+ public Class[] getInterestedTypes ()
+ {
+ return _interestedTypes;
+ }
+
+ public void setInterestedTypes (Class[] interestedTypes)
+ {
+ _interestedTypes = interestedTypes;
+ }
+
+ /**
+ * A class has been found that has an annotation of interest
+ * to this initializer.
+ * @param className
+ */
+ public void addAnnotatedTypeName (String className)
+ {
+ if (_annotatedTypeNames == null)
+ _annotatedTypeNames = new HashSet<String>();
+ _annotatedTypeNames.add(className);
+ }
+
+ public Set<String> getAnnotatedTypeNames ()
+ {
+ return _annotatedTypeNames;
+ }
+
+ public void addApplicableTypeName (String className)
+ {
+ if (_applicableTypeNames == null)
+ _applicableTypeNames = new HashSet<String>();
+ _applicableTypeNames.add(className);
+ }
+
+ public Set<String> getApplicableTypeNames ()
+ {
+ return _applicableTypeNames;
+ }
+
+
+ public void callStartup(WebAppContext context)
+ throws Exception
+ {
+ if (_target != null)
+ {
+ Set<Class<?>> classes = new HashSet<Class<?>>();
+
+ ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(context.getClassLoader());
+
+ try
+ {
+ if (_applicableTypeNames != null)
+ {
+ for (String s : _applicableTypeNames)
+ classes.add(Loader.loadClass(context.getClass(), s));
+ }
+
+ _target.onStartup(classes, context.getServletContext());
+ }
+ finally
+ {
+ Thread.currentThread().setContextClassLoader(oldLoader);
+ }
+ }
+ }
+}
diff --git a/jetty-plus/src/test/java/org/eclipse/jetty/plus/webapp/PlusDescriptorProcessorTest.java b/jetty-plus/src/test/java/org/eclipse/jetty/plus/webapp/PlusDescriptorProcessorTest.java
new file mode 100644
index 0000000..df208ed
--- /dev/null
+++ b/jetty-plus/src/test/java/org/eclipse/jetty/plus/webapp/PlusDescriptorProcessorTest.java
@@ -0,0 +1,176 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.plus.webapp;
+
+
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.net.URL;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+
+import org.eclipse.jetty.webapp.Descriptor;
+import org.eclipse.jetty.webapp.FragmentDescriptor;
+import org.eclipse.jetty.webapp.Origin;
+import org.eclipse.jetty.webapp.WebAppClassLoader;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.eclipse.jetty.webapp.WebDescriptor;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * PlusDescriptorProcessorTest
+ *
+ *
+ */
+public class PlusDescriptorProcessorTest
+{
+ protected WebDescriptor webDescriptor;
+ protected FragmentDescriptor fragDescriptor1;
+ protected FragmentDescriptor fragDescriptor2;
+ protected FragmentDescriptor fragDescriptor3;
+ protected WebAppContext context;
+ /**
+ * @throws java.lang.Exception
+ */
+ @Before
+ public void setUp() throws Exception
+ {
+ context = new WebAppContext();
+ context.setClassLoader(new WebAppClassLoader(Thread.currentThread().getContextClassLoader(), context));
+ ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(context.getClassLoader());
+ Context icontext = new InitialContext();
+ Context compCtx = (Context)icontext.lookup ("java:comp");
+ compCtx.createSubcontext("env");
+ Thread.currentThread().setContextClassLoader(oldLoader);
+
+ org.eclipse.jetty.plus.jndi.Resource ds = new org.eclipse.jetty.plus.jndi.Resource (context, "jdbc/mydatasource", new Object());
+
+ URL webXml = Thread.currentThread().getContextClassLoader().getResource("web.xml");
+ webDescriptor = new WebDescriptor(org.eclipse.jetty.util.resource.Resource.newResource(webXml));
+ webDescriptor.parse();
+
+ URL frag1Xml = Thread.currentThread().getContextClassLoader().getResource("web-fragment-1.xml");
+ fragDescriptor1 = new FragmentDescriptor(org.eclipse.jetty.util.resource.Resource.newResource(frag1Xml));
+ fragDescriptor1.parse();
+ URL frag2Xml = Thread.currentThread().getContextClassLoader().getResource("web-fragment-2.xml");
+ fragDescriptor2 = new FragmentDescriptor(org.eclipse.jetty.util.resource.Resource.newResource(frag2Xml));
+ fragDescriptor2.parse();
+ URL frag3Xml = Thread.currentThread().getContextClassLoader().getResource("web-fragment-3.xml");
+ fragDescriptor3 = new FragmentDescriptor(org.eclipse.jetty.util.resource.Resource.newResource(frag3Xml));
+ fragDescriptor3.parse();
+ }
+
+ @After
+ public void tearDown() throws Exception
+ {
+ ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(context.getClassLoader());
+ Context ic = new InitialContext();
+ Context compCtx = (Context)ic.lookup ("java:comp");
+ compCtx.destroySubcontext("env");
+ Thread.currentThread().setContextClassLoader(oldLoader);
+ }
+
+ @Test
+ public void testWebXmlResourceDeclarations()
+ throws Exception
+ {
+ //if declared in web.xml, fragment declarations ignored
+ ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(context.getClassLoader());
+ try
+ {
+ PlusDescriptorProcessor pdp = new PlusDescriptorProcessor();
+ pdp.process(context, webDescriptor);
+ Descriptor d = context.getMetaData().getOriginDescriptor("resource-ref.jdbc/mydatasource");
+ assertNotNull(d);
+ assertTrue(d == webDescriptor);
+
+ pdp.process(context, fragDescriptor1);
+ pdp.process(context, fragDescriptor2);
+ }
+ finally
+ {
+ Thread.currentThread().setContextClassLoader(oldLoader);
+ }
+ }
+
+
+ @Test
+ public void testMismatchedFragmentResourceDeclarations ()
+ throws Exception
+ {
+ //if declared in more than 1 fragment, declarations must be the same
+ ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(context.getClassLoader());
+ try
+ {
+ PlusDescriptorProcessor pdp = new PlusDescriptorProcessor();
+ pdp.process(context, fragDescriptor1);
+ Descriptor d = context.getMetaData().getOriginDescriptor("resource-ref.jdbc/mydatasource");
+ assertNotNull(d);
+ assertTrue(d == fragDescriptor1);
+ assertEquals(Origin.WebFragment, context.getMetaData().getOrigin("resource-ref.jdbc/mydatasource"));
+
+ pdp.process(context, fragDescriptor2);
+ fail("Expected conflicting resource-ref declaration");
+ }
+ catch (Exception e)
+ {
+ //expected
+ }
+ finally
+ {
+ Thread.currentThread().setContextClassLoader(oldLoader);
+ }
+ }
+
+ @Test
+ public void testMatchingFragmentResourceDeclarations ()
+ throws Exception
+ {
+ //if declared in more than 1 fragment, declarations must be the same
+ ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(context.getClassLoader());
+ try
+ {
+ PlusDescriptorProcessor pdp = new PlusDescriptorProcessor();
+ pdp.process(context, fragDescriptor1);
+ Descriptor d = context.getMetaData().getOriginDescriptor("resource-ref.jdbc/mydatasource");
+ assertNotNull(d);
+ assertTrue(d == fragDescriptor1);
+ assertEquals(Origin.WebFragment, context.getMetaData().getOrigin("resource-ref.jdbc/mydatasource"));
+ pdp.process(context, fragDescriptor3);
+ }
+
+ finally
+ {
+ Thread.currentThread().setContextClassLoader(oldLoader);
+ }
+ }
+}
diff --git a/jetty-plus/src/test/resources/web-fragment-1.xml b/jetty-plus/src/test/resources/web-fragment-1.xml
new file mode 100644
index 0000000..b213172
--- /dev/null
+++ b/jetty-plus/src/test/resources/web-fragment-1.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<web-fragment
+ xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd"
+ version="3.0">
+
+ <name>Fragment1</name>
+
+ <ordering>
+ <after>others</after>
+ </ordering>
+
+ <resource-ref>
+ <res-ref-name>jdbc/mydatasource</res-ref-name>
+ <res-type>javax.sql.DataSource</res-type>
+ <res-auth>Container</res-auth>
+<!--
+ <injection-target>
+ <injection-target-class>com.acme.Bar</injection-target-class>
+ <injection-target-name>myDatasource</injection-target-name>
+ </injection-target>
+-->
+ </resource-ref>
+
+</web-fragment>
diff --git a/jetty-plus/src/test/resources/web-fragment-2.xml b/jetty-plus/src/test/resources/web-fragment-2.xml
new file mode 100644
index 0000000..e2fff67
--- /dev/null
+++ b/jetty-plus/src/test/resources/web-fragment-2.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<web-fragment
+ xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd"
+ version="3.0">
+
+ <name>Fragment2</name>
+
+ <ordering>
+ <after>others</after>
+ </ordering>
+
+ <resource-ref>
+ <res-ref-name>jdbc/mydatasource</res-ref-name>
+ <res-type>javax.sql.DataSource</res-type>
+ <res-auth>User</res-auth>
+<!--
+ <injection-target>
+ <injection-target-class>com.acme.Bar</injection-target-class>
+ <injection-target-name>myDatasource</injection-target-name>
+ </injection-target>
+-->
+ </resource-ref>
+
+</web-fragment>
diff --git a/jetty-plus/src/test/resources/web-fragment-3.xml b/jetty-plus/src/test/resources/web-fragment-3.xml
new file mode 100644
index 0000000..da1f3d5
--- /dev/null
+++ b/jetty-plus/src/test/resources/web-fragment-3.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<web-fragment
+ xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd"
+ version="3.0">
+
+ <name>Fragment3</name>
+
+ <ordering>
+ <after>others</after>
+ </ordering>
+
+ <resource-ref>
+ <res-ref-name>jdbc/mydatasource</res-ref-name>
+ <res-type>javax.sql.DataSource</res-type>
+ <res-auth>Container</res-auth>
+<!--
+ <injection-target>
+ <injection-target-class>com.acme.Bar</injection-target-class>
+ <injection-target-name>myDatasource</injection-target-name>
+ </injection-target>
+-->
+ </resource-ref>
+
+</web-fragment>
diff --git a/jetty-plus/src/test/resources/web.xml b/jetty-plus/src/test/resources/web.xml
new file mode 100644
index 0000000..aa40eb3
--- /dev/null
+++ b/jetty-plus/src/test/resources/web.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<web-app
+ xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+ metadata-complete="false"
+ version="3.0">
+
+ <display-name>Test WebApp</display-name>
+
+ <resource-ref>
+ <res-ref-name>jdbc/mydatasource</res-ref-name>
+ <res-type>javax.sql.DataSource</res-type>
+ <res-auth>Container</res-auth>
+<!--
+ <injection-target>
+ <injection-target-class>com.acme.JNDITest</injection-target-class>
+ <injection-target-name>myDatasource</injection-target-name>
+ </injection-target>
+-->
+ </resource-ref>
+
+</web-app>
diff --git a/jetty-policy/pom.xml b/jetty-policy/pom.xml
index e6ea883..1ab08ec 100644
--- a/jetty-policy/pom.xml
+++ b/jetty-policy/pom.xml
@@ -3,7 +3,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<artifactId>jetty-policy</artifactId>
<name>Jetty :: Policy Tool</name>
diff --git a/jetty-rewrite/pom.xml b/jetty-rewrite/pom.xml
index 332dfd1..f7653f9 100644
--- a/jetty-rewrite/pom.xml
+++ b/jetty-rewrite/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-rewrite</artifactId>
@@ -23,6 +23,11 @@
<goals>
<goal>manifest</goal>
</goals>
+ <configuration>
+ <instructions>
+ <Import-Package>javax.servlet.*;version="2.6.0",*</Import-Package>
+ </instructions>
+ </configuration>
</execution>
</executions>
</plugin>
diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/ProxyRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/ProxyRule.java
index 93cafdc..c9b7cee 100644
--- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/ProxyRule.java
+++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/ProxyRule.java
@@ -371,7 +371,7 @@
{
// TODO could be better than this!
String hdr = (String)enm.nextElement();
- String lhdr = hdr.toLowerCase();
+ String lhdr = hdr.toLowerCase(Locale.ENGLISH);
if (_DontProxyHeaders.contains(lhdr))
continue;
diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RuleContainer.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RuleContainer.java
index 975d67b..6a5c740 100644
--- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RuleContainer.java
+++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RuleContainer.java
@@ -26,6 +26,7 @@
import org.eclipse.jetty.server.AbstractHttpConnection;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.util.LazyList;
+import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@@ -38,11 +39,13 @@
public class RuleContainer extends Rule
{
+ public static final String ORIGINAL_QUERYSTRING_ATTRIBUTE_SUFFIX = ".QUERYSTRING";
private static final Logger LOG = Log.getLogger(RuleContainer.class);
protected Rule[] _rules;
protected String _originalPathAttribute;
+ protected String _originalQueryStringAttribute;
protected boolean _rewriteRequestURI=true;
protected boolean _rewritePathInfo=true;
@@ -172,6 +175,7 @@
public void setOriginalPathAttribute(String originalPathAttribte)
{
_originalPathAttribute=originalPathAttribte;
+ _originalQueryStringAttribute = originalPathAttribte + ORIGINAL_QUERYSTRING_ATTRIBUTE_SUFFIX;
}
/**
@@ -195,18 +199,26 @@
protected String apply(String target, HttpServletRequest request, HttpServletResponse response) throws IOException
{
boolean original_set=_originalPathAttribute==null;
+
+ target = URIUtil.compactPath(target);
for (Rule rule : _rules)
{
String applied=rule.matchAndApply(target,request, response);
if (applied!=null)
- {
+ {
+ applied = URIUtil.compactPath(applied);
+
LOG.debug("applied {}",rule);
LOG.debug("rewrote {} to {}",target,applied);
if (!original_set)
{
original_set=true;
request.setAttribute(_originalPathAttribute, target);
+
+ String query = request.getQueryString();
+ if (query != null)
+ request.setAttribute(_originalQueryStringAttribute,query);
}
if (_rewriteRequestURI)
diff --git a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/HeaderPatternRuleTest.java b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/HeaderPatternRuleTest.java
index fc5b948..4cb3473 100644
--- a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/HeaderPatternRuleTest.java
+++ b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/HeaderPatternRuleTest.java
@@ -19,7 +19,7 @@
package org.eclipse.jetty.rewrite.handler;
import java.io.IOException;
-import java.util.Enumeration;
+import java.util.Iterator;
import org.junit.Before;
import org.junit.Test;
@@ -86,11 +86,11 @@
};
assertHeaders(headers);
- Enumeration e = _response.getHeaders("size");
+ Iterator<String> e = _response.getHeaders("size").iterator();
int count = 0;
- while (e.hasMoreElements())
+ while (e.hasNext())
{
- e.nextElement();
+ e.next();
count++;
}
diff --git a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/ValidUrlRuleTest.java b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/ValidUrlRuleTest.java
index 997b15a..73692dd 100644
--- a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/ValidUrlRuleTest.java
+++ b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/ValidUrlRuleTest.java
@@ -108,7 +108,6 @@
assertEquals(200,_response.getStatus());
}
-
@Test
public void testCharacters() throws Exception
diff --git a/jetty-security/pom.xml b/jetty-security/pom.xml
index 5454f52..339bcf3 100644
--- a/jetty-security/pom.xml
+++ b/jetty-security/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-security</artifactId>
@@ -25,7 +25,7 @@
</goals>
<configuration>
<instructions>
- <Import-Package>javax.servlet.*;version="[2.5,3.0)",javax.security.cert,*</Import-Package>
+ <Import-Package>javax.servlet.*;version="2.6.0",javax.security.cert,*</Import-Package>
</instructions>
</configuration>
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintMapping.java b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintMapping.java
index f922bd6..9b25c06 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintMapping.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintMapping.java
@@ -23,6 +23,7 @@
public class ConstraintMapping
{
String _method;
+ String[] _methodOmissions;
String _pathSpec;
@@ -81,4 +82,19 @@
{
this._pathSpec = pathSpec;
}
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param omissions The http-method-omission
+ */
+ public void setMethodOmissions(String[] omissions)
+ {
+ _methodOmissions = omissions;
+ }
+
+ /* ------------------------------------------------------------ */
+ public String[] getMethodOmissions()
+ {
+ return _methodOmissions;
+ }
}
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java
index 24cdc55..635eb80 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java
@@ -19,17 +19,25 @@
package org.eclipse.jetty.security;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import org.eclipse.jetty.http.HttpSchemes;
+import javax.servlet.HttpConstraintElement;
+import javax.servlet.HttpMethodConstraintElement;
+import javax.servlet.ServletSecurityElement;
+import javax.servlet.annotation.ServletSecurity.EmptyRoleSemantic;
+import javax.servlet.annotation.ServletSecurity.TransportGuarantee;
+
import org.eclipse.jetty.http.PathMap;
import org.eclipse.jetty.server.AbstractHttpConnection;
import org.eclipse.jetty.server.Connector;
@@ -43,18 +51,226 @@
/* ------------------------------------------------------------ */
/**
* Handler to enforce SecurityConstraints. This implementation is servlet spec
- * 2.4 compliant and precomputes the constraint combinations for runtime
+ * 3.0 compliant and precomputes the constraint combinations for runtime
* efficiency.
*
*/
public class ConstraintSecurityHandler extends SecurityHandler implements ConstraintAware
{
+ private static final String OMISSION_SUFFIX = ".omission";
+
private final List<ConstraintMapping> _constraintMappings= new CopyOnWriteArrayList<ConstraintMapping>();
private final Set<String> _roles = new CopyOnWriteArraySet<String>();
private final PathMap _constraintMap = new PathMap();
private boolean _strict = true;
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return
+ */
+ public static Constraint createConstraint()
+ {
+ return new Constraint();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param constraint
+ * @return
+ */
+ public static Constraint createConstraint(Constraint constraint)
+ {
+ try
+ {
+ return (Constraint)constraint.clone();
+ }
+ catch (CloneNotSupportedException e)
+ {
+ throw new IllegalStateException (e);
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Create a security constraint
+ *
+ * @param name
+ * @param authenticate
+ * @param roles
+ * @param dataConstraint
+ * @return
+ */
+ public static Constraint createConstraint (String name, boolean authenticate, String[] roles, int dataConstraint)
+ {
+ Constraint constraint = createConstraint();
+ if (name != null)
+ constraint.setName(name);
+ constraint.setAuthenticate(authenticate);
+ constraint.setRoles(roles);
+ constraint.setDataConstraint(dataConstraint);
+ return constraint;
+ }
+
/* ------------------------------------------------------------ */
+ /**
+ * @param name
+ * @param element
+ * @return
+ */
+ public static Constraint createConstraint (String name, HttpConstraintElement element)
+ {
+ return createConstraint(name, element.getRolesAllowed(), element.getEmptyRoleSemantic(), element.getTransportGuarantee());
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param name
+ * @param rolesAllowed
+ * @param permitOrDeny
+ * @param transport
+ * @return
+ */
+ public static Constraint createConstraint (String name, String[] rolesAllowed, EmptyRoleSemantic permitOrDeny, TransportGuarantee transport)
+ {
+ Constraint constraint = createConstraint();
+
+ if (rolesAllowed == null || rolesAllowed.length==0)
+ {
+ if (permitOrDeny.equals(EmptyRoleSemantic.DENY))
+ {
+ //Equivalent to <auth-constraint> with no roles
+ constraint.setName(name+"-Deny");
+ constraint.setAuthenticate(true);
+ }
+ else
+ {
+ //Equivalent to no <auth-constraint>
+ constraint.setName(name+"-Permit");
+ constraint.setAuthenticate(false);
+ }
+ }
+ else
+ {
+ //Equivalent to <auth-constraint> with list of <security-role-name>s
+ constraint.setAuthenticate(true);
+ constraint.setRoles(rolesAllowed);
+ constraint.setName(name+"-RolesAllowed");
+ }
+
+ //Equivalent to //<user-data-constraint><transport-guarantee>CONFIDENTIAL</transport-guarantee></user-data-constraint>
+ constraint.setDataConstraint((transport.equals(TransportGuarantee.CONFIDENTIAL)?Constraint.DC_CONFIDENTIAL:Constraint.DC_NONE));
+ return constraint;
+ }
+
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param pathSpec
+ * @param constraintMappings
+ * @return
+ */
+ public static List<ConstraintMapping> getConstraintMappingsForPath(String pathSpec, List<ConstraintMapping> constraintMappings)
+ {
+ if (pathSpec == null || "".equals(pathSpec.trim()) || constraintMappings == null || constraintMappings.size() == 0)
+ return Collections.emptyList();
+
+ List<ConstraintMapping> mappings = new ArrayList<ConstraintMapping>();
+ for (ConstraintMapping mapping:constraintMappings)
+ {
+ if (pathSpec.equals(mapping.getPathSpec()))
+ {
+ mappings.add(mapping);
+ }
+ }
+ return mappings;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /** Take out of the constraint mappings those that match the
+ * given path.
+ *
+ * @param pathSpec
+ * @param constraintMappings a new list minus the matching constraints
+ * @return
+ */
+ public static List<ConstraintMapping> removeConstraintMappingsForPath(String pathSpec, List<ConstraintMapping> constraintMappings)
+ {
+ if (pathSpec == null || "".equals(pathSpec.trim()) || constraintMappings == null || constraintMappings.size() == 0)
+ return Collections.emptyList();
+
+ List<ConstraintMapping> mappings = new ArrayList<ConstraintMapping>();
+ for (ConstraintMapping mapping:constraintMappings)
+ {
+ //Remove the matching mappings by only copying in non-matching mappings
+ if (!pathSpec.equals(mapping.getPathSpec()))
+ {
+ mappings.add(mapping);
+ }
+ }
+ return mappings;
+ }
+
+
+
+ /* ------------------------------------------------------------ */
+ /** Generate Constraints and ContraintMappings for the given url pattern and ServletSecurityElement
+ *
+ * @param name
+ * @param pathSpec
+ * @param securityElement
+ * @return
+ */
+ public static List<ConstraintMapping> createConstraintsWithMappingsForPath (String name, String pathSpec, ServletSecurityElement securityElement)
+ {
+ List<ConstraintMapping> mappings = new ArrayList<ConstraintMapping>();
+
+ //Create a constraint that will describe the default case (ie if not overridden by specific HttpMethodConstraints)
+ Constraint constraint = ConstraintSecurityHandler.createConstraint(name, securityElement);
+
+ //Create a mapping for the pathSpec for the default case
+ ConstraintMapping defaultMapping = new ConstraintMapping();
+ defaultMapping.setPathSpec(pathSpec);
+ defaultMapping.setConstraint(constraint);
+ mappings.add(defaultMapping);
+
+
+ //See Spec 13.4.1.2 p127
+ List<String> methodOmissions = new ArrayList<String>();
+
+ //make constraint mappings for this url for each of the HttpMethodConstraintElements
+ Collection<HttpMethodConstraintElement> methodConstraints = securityElement.getHttpMethodConstraints();
+ if (methodConstraints != null)
+ {
+ for (HttpMethodConstraintElement methodConstraint:methodConstraints)
+ {
+ //Make a Constraint that captures the <auth-constraint> and <user-data-constraint> elements supplied for the HttpMethodConstraintElement
+ Constraint mconstraint = ConstraintSecurityHandler.createConstraint(name, methodConstraint);
+ ConstraintMapping mapping = new ConstraintMapping();
+ mapping.setConstraint(mconstraint);
+ mapping.setPathSpec(pathSpec);
+ if (methodConstraint.getMethodName() != null)
+ {
+ mapping.setMethod(methodConstraint.getMethodName());
+ //See spec 13.4.1.2 p127 - add an omission for every method name to the default constraint
+ methodOmissions.add(methodConstraint.getMethodName());
+ }
+ mappings.add(mapping);
+ }
+ }
+ //See spec 13.4.1.2 p127 - add an omission for every method name to the default constraint
+ if (methodOmissions.size() > 0)
+ defaultMapping.setMethodOmissions(methodOmissions.toArray(new String[methodOmissions.size()]));
+
+ return mappings;
+ }
+
+
+ /* ------------------------------------------------------------ */
/** Get the strict mode.
* @return true if the security handler is running in strict mode.
*/
@@ -137,8 +353,6 @@
*/
public void setConstraintMappings(List<ConstraintMapping> constraintMappings, Set<String> roles)
{
- if (isStarted())
- throw new IllegalStateException("Started");
_constraintMappings.clear();
_constraintMappings.addAll(constraintMappings);
@@ -157,6 +371,14 @@
}
}
setRoles(roles);
+
+ if (isStarted())
+ {
+ for (ConstraintMapping mapping : _constraintMappings)
+ {
+ processConstraintMapping(mapping);
+ }
+ }
}
/* ------------------------------------------------------------ */
@@ -169,9 +391,6 @@
*/
public void setRoles(Set<String> roles)
{
- if (isStarted())
- throw new IllegalStateException("Started");
-
_roles.clear();
_roles.addAll(roles);
}
@@ -233,7 +452,9 @@
}
super.doStart();
}
-
+
+
+ /* ------------------------------------------------------------ */
@Override
protected void doStop() throws Exception
{
@@ -242,7 +463,15 @@
_roles.clear();
super.doStop();
}
-
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Create and combine the constraint with the existing processed
+ * constraints.
+ *
+ * @param mapping
+ */
protected void processConstraintMapping(ConstraintMapping mapping)
{
Map<String, RoleInfo> mappings = (Map<String, RoleInfo>)_constraintMap.get(mapping.getPathSpec());
@@ -254,8 +483,15 @@
RoleInfo allMethodsRoleInfo = mappings.get(null);
if (allMethodsRoleInfo != null && allMethodsRoleInfo.isForbidden())
return;
+
+ if (mapping.getMethodOmissions() != null && mapping.getMethodOmissions().length > 0)
+ {
+
+ processConstraintMappingWithMethodOmissions(mapping, mappings);
+ return;
+ }
- String httpMethod = mapping.getMethod();
+ String httpMethod = mapping.getMethod();
RoleInfo roleInfo = mappings.get(httpMethod);
if (roleInfo == null)
{
@@ -269,10 +505,10 @@
if (roleInfo.isForbidden())
return;
- Constraint constraint = mapping.getConstraint();
- boolean forbidden = constraint.isForbidden();
- roleInfo.setForbidden(forbidden);
- if (forbidden)
+ //add in info from the constraint
+ configureRoleInfo(roleInfo, mapping);
+
+ if (roleInfo.isForbidden())
{
if (httpMethod == null)
{
@@ -282,36 +518,7 @@
}
else
{
- UserDataConstraint userDataConstraint = UserDataConstraint.get(constraint.getDataConstraint());
- roleInfo.setUserDataConstraint(userDataConstraint);
-
- boolean checked = constraint.getAuthenticate();
- roleInfo.setChecked(checked);
- if (roleInfo.isChecked())
- {
- if (constraint.isAnyRole())
- {
- if (_strict)
- {
- // * means "all defined roles"
- for (String role : _roles)
- roleInfo.addRole(role);
- }
- else
- // * means any role
- roleInfo.setAnyRole(true);
- }
- else
- {
- String[] newRoles = constraint.getRoles();
- for (String role : newRoles)
- {
- if (_strict &&!_roles.contains(role))
- throw new IllegalArgumentException("Attempt to use undeclared role: " + role + ", known roles: " + _roles);
- roleInfo.addRole(role);
- }
- }
- }
+ //combine with any entry that covers all methods
if (httpMethod == null)
{
for (Map.Entry<String, RoleInfo> entry : mappings.entrySet())
@@ -326,6 +533,105 @@
}
}
+ /* ------------------------------------------------------------ */
+ /** Constraints that name method omissions are dealt with differently.
+ * We create an entry in the mappings with key "method.omission". This entry
+ * is only ever combined with other omissions for the same method to produce a
+ * consolidated RoleInfo. Then, when we wish to find the relevant constraints for
+ * a given Request (in prepareConstraintInfo()), we consult 3 types of entries in
+ * the mappings: an entry that names the method of the Request specifically, an
+ * entry that names constraints that apply to all methods, entries of the form
+ * method.omission, where the method of the Request is not named in the omission.
+ * @param mapping
+ * @param mappings
+ */
+ protected void processConstraintMappingWithMethodOmissions (ConstraintMapping mapping, Map<String, RoleInfo> mappings)
+ {
+ String[] omissions = mapping.getMethodOmissions();
+
+ for (String omission:omissions)
+ {
+ //for each method omission, see if there is already a RoleInfo for it in mappings
+ RoleInfo ri = mappings.get(omission+OMISSION_SUFFIX);
+ if (ri == null)
+ {
+ //if not, make one
+ ri = new RoleInfo();
+ mappings.put(omission+OMISSION_SUFFIX, ri);
+ }
+
+ //initialize RoleInfo or combine from ConstraintMapping
+ configureRoleInfo(ri, mapping);
+ }
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Initialize or update the RoleInfo from the constraint
+ * @param ri
+ * @param mapping
+ */
+ protected void configureRoleInfo (RoleInfo ri, ConstraintMapping mapping)
+ {
+ Constraint constraint = mapping.getConstraint();
+ boolean forbidden = constraint.isForbidden();
+ ri.setForbidden(forbidden);
+
+ //set up the data constraint (NOTE: must be done after setForbidden, as it nulls out the data constraint
+ //which we need in order to do combining of omissions in prepareConstraintInfo
+ UserDataConstraint userDataConstraint = UserDataConstraint.get(mapping.getConstraint().getDataConstraint());
+ ri.setUserDataConstraint(userDataConstraint);
+
+
+ //if forbidden, no point setting up roles
+ if (!ri.isForbidden())
+ {
+ //add in the roles
+ boolean checked = mapping.getConstraint().getAuthenticate();
+ ri.setChecked(checked);
+ if (ri.isChecked())
+ {
+ if (mapping.getConstraint().isAnyRole())
+ {
+ if (_strict)
+ {
+ // * means "all defined roles"
+ for (String role : _roles)
+ ri.addRole(role);
+ }
+ else
+ // * means any role
+ ri.setAnyRole(true);
+ }
+ else
+ {
+ String[] newRoles = mapping.getConstraint().getRoles();
+ for (String role : newRoles)
+ {
+ if (_strict &&!_roles.contains(role))
+ throw new IllegalArgumentException("Attempt to use undeclared role: " + role + ", known roles: " + _roles);
+ ri.addRole(role);
+ }
+ }
+ }
+ }
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Find constraints that apply to the given path.
+ * In order to do this, we consult 3 different types of information stored in the mappings for each path - each mapping
+ * represents a merged set of user data constraints, roles etc -:
+ * <ol>
+ * <li>A mapping of an exact method name </li>
+ * <li>A mapping will null key that matches every method name</li>
+ * <li>Mappings with keys of the form "method.omission" that indicates it will match every method name EXCEPT that given</li>
+ * </ol>
+ *
+ * @see org.eclipse.jetty.security.SecurityHandler#prepareConstraintInfo(java.lang.String, org.eclipse.jetty.server.Request)
+ */
protected Object prepareConstraintInfo(String pathInContext, Request request)
{
Map<String, RoleInfo> mappings = (Map<String, RoleInfo>)_constraintMap.match(pathInContext);
@@ -335,13 +641,46 @@
String httpMethod = request.getMethod();
RoleInfo roleInfo = mappings.get(httpMethod);
if (roleInfo == null)
- roleInfo = mappings.get(null);
+ {
+ //No specific http-method names matched
+ List<RoleInfo> applicableConstraints = new ArrayList<RoleInfo>();
+
+ //Get info for constraint that matches all methods if it exists
+ RoleInfo all = mappings.get(null);
+ if (all != null)
+ applicableConstraints.add(all);
+
+
+ //Get info for constraints that name method omissions where target method name is not omitted
+ //(ie matches because target method is not omitted, hence considered covered by the constraint)
+ for (Entry<String, RoleInfo> entry: mappings.entrySet())
+ {
+ if (entry.getKey() != null && entry.getKey().contains(OMISSION_SUFFIX) && !(httpMethod+OMISSION_SUFFIX).equals(entry.getKey()))
+ applicableConstraints.add(entry.getValue());
+ }
+
+ if (applicableConstraints.size() == 1)
+ roleInfo = applicableConstraints.get(0);
+ else
+ {
+ roleInfo = new RoleInfo();
+ roleInfo.setUserDataConstraint(UserDataConstraint.None);
+
+ for (RoleInfo r:applicableConstraints)
+ roleInfo.combine(r);
+ }
+
+ }
return roleInfo;
}
-
return null;
}
-
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.security.SecurityHandler#checkUserDataPermissions(java.lang.String, org.eclipse.jetty.server.Request, org.eclipse.jetty.server.Response, java.lang.Object)
+ */
protected boolean checkUserDataPermissions(String pathInContext, Request request, Response response, Object constraintInfo) throws IOException
{
if (constraintInfo == null)
@@ -411,7 +750,11 @@
}
}
-
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.security.SecurityHandler#isAuthMandatory(org.eclipse.jetty.server.Request, org.eclipse.jetty.server.Response, java.lang.Object)
+ */
protected boolean isAuthMandatory(Request baseRequest, Response base_response, Object constraintInfo)
{
if (constraintInfo == null)
@@ -420,7 +763,12 @@
}
return ((RoleInfo)constraintInfo).isChecked();
}
-
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.security.SecurityHandler#checkWebResourcePermissions(java.lang.String, org.eclipse.jetty.server.Request, org.eclipse.jetty.server.Response, java.lang.Object, org.eclipse.jetty.server.UserIdentity)
+ */
@Override
protected boolean checkWebResourcePermissions(String pathInContext, Request request, Response response, Object constraintInfo, UserIdentity userIdentity)
throws IOException
@@ -461,4 +809,5 @@
getBeans(),
TypeUtil.asList(getHandlers()));
}
+
}
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/SecurityHandler.java b/jetty-security/src/main/java/org/eclipse/jetty/security/SecurityHandler.java
index d649de0..fab701b 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/SecurityHandler.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/SecurityHandler.java
@@ -335,6 +335,7 @@
if (_identityService==null)
{
+
if (_loginService!=null)
_identityService=_loginService.getIdentityService();
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/BasicAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/BasicAuthenticator.java
index eb65bd4..6a14613 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/BasicAuthenticator.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/BasicAuthenticator.java
@@ -54,6 +54,8 @@
return Constraint.__BASIC_AUTH;
}
+
+
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.security.Authenticator#validateRequest(javax.servlet.ServletRequest, javax.servlet.ServletResponse, boolean)
@@ -85,10 +87,9 @@
String username = credentials.substring(0,i);
String password = credentials.substring(i+1);
- UserIdentity user = _loginService.login(username,password);
+ UserIdentity user = login (username, password, request);
if (user!=null)
{
- renewSession(request,response);
return new UserAuthentication(getAuthMethod(),user);
}
}
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/ClientCertAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/ClientCertAuthenticator.java
index 56132ee..c22d190 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/ClientCertAuthenticator.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/ClientCertAuthenticator.java
@@ -81,6 +81,8 @@
return Constraint.__CERT_AUTH;
}
+
+
/**
* @return Authentication for request
* @throws ServerAuthException
@@ -121,10 +123,9 @@
final char[] credential = B64Code.encode(cert.getSignature());
- UserIdentity user = _loginService.login(username,credential);
+ UserIdentity user = login(username, credential, req);
if (user!=null)
{
- renewSession(request,response);
return new UserAuthentication(getAuthMethod(),user);
}
}
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DeferredAuthentication.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DeferredAuthentication.java
index de193e8..1197692 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DeferredAuthentication.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DeferredAuthentication.java
@@ -21,12 +21,15 @@
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.Collection;
+import java.util.Collections;
import java.util.Locale;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.security.Authenticator;
@@ -71,6 +74,7 @@
if (identity_service!=null)
_previousAssociation=identity_service.associate(((Authentication.User)authentication).getUserIdentity());
+
return authentication;
}
}
@@ -78,7 +82,8 @@
{
LOG.debug(e);
}
- return Authentication.UNAUTHENTICATED;
+
+ return this;
}
/* ------------------------------------------------------------ */
@@ -101,28 +106,23 @@
{
LOG.debug(e);
}
- return Authentication.UNAUTHENTICATED;
+ return this;
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.server.Authentication.Deferred#login(java.lang.String, java.lang.String)
*/
- public Authentication login(String username, String password)
+ public Authentication login(String username, Object password, ServletRequest request)
{
- LoginService login_service= _authenticator.getLoginService();
- IdentityService identity_service=login_service.getIdentityService();
-
- if (login_service!=null)
+ UserIdentity identity = _authenticator.login(username, password, request);
+ if (identity != null)
{
- UserIdentity user = login_service.login(username,password);
- if (user!=null)
- {
- UserAuthentication authentication = new UserAuthentication("API",user);
- if (identity_service!=null)
- _previousAssociation=identity_service.associate(user);
- return authentication;
- }
+ IdentityService identity_service = _authenticator.getLoginService().getIdentityService();
+ UserAuthentication authentication = new UserAuthentication("API",identity);
+ if (identity_service != null)
+ _previousAssociation=identity_service.associate(identity);
+ return authentication;
}
return null;
}
@@ -288,6 +288,29 @@
{
}
+ public Collection<String> getHeaderNames()
+ {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public String getHeader(String arg0)
+ {
+ return null;
+ }
+
+ @Override
+ public Collection<String> getHeaders(String arg0)
+ {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public int getStatus()
+ {
+ return 0;
+ }
+
};
/* ------------------------------------------------------------ */
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java
index d4ee800..bc9e5da 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java
@@ -148,6 +148,8 @@
{
return true;
}
+
+
/* ------------------------------------------------------------ */
public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory) throws ServerAuthException
@@ -217,10 +219,10 @@
if (n > 0)
{
- UserIdentity user = _loginService.login(digest.username,digest);
+ //UserIdentity user = _loginService.login(digest.username,digest);
+ UserIdentity user = login(digest.username, digest, req);
if (user!=null)
{
- renewSession(request,response);
return new UserAuthentication(getAuthMethod(),user);
}
}
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/FormAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/FormAuthenticator.java
index 1b8d555..506f90e 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/FormAuthenticator.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/FormAuthenticator.java
@@ -180,6 +180,22 @@
_formErrorPath = _formErrorPath.substring(0, _formErrorPath.indexOf('?'));
}
}
+
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public UserIdentity login(String username, Object password, ServletRequest request)
+ {
+
+ UserIdentity user = super.login(username,password,request);
+ if (user!=null)
+ {
+ HttpSession session = ((HttpServletRequest)request).getSession(true);
+ Authentication cached=new SessionAuthentication(getAuthMethod(),user,password);
+ session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, cached);
+ }
+ return user;
+ }
/* ------------------------------------------------------------ */
public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory) throws ServerAuthException
@@ -207,11 +223,10 @@
final String username = request.getParameter(__J_USERNAME);
final String password = request.getParameter(__J_PASSWORD);
- UserIdentity user = _loginService.login(username,password);
+ UserIdentity user = login(username, password, request);
+ session = request.getSession(true);
if (user!=null)
- {
- session=renewSession(request,response);
-
+ {
// Redirect to original request
String nuri;
synchronized(session)
@@ -224,9 +239,6 @@
if (nuri.length() == 0)
nuri = URIUtil.SLASH;
}
-
- Authentication cached=new SessionAuthentication(getAuthMethod(),user,password);
- session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, cached);
}
response.setContentLength(0);
response.sendRedirect(response.encodeRedirectURL(nuri));
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginAuthenticator.java
index 4079831..87ab6af 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginAuthenticator.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginAuthenticator.java
@@ -18,6 +18,7 @@
package org.eclipse.jetty.security.authentication;
+import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@@ -25,6 +26,8 @@
import org.eclipse.jetty.security.Authenticator;
import org.eclipse.jetty.security.IdentityService;
import org.eclipse.jetty.security.LoginService;
+import org.eclipse.jetty.server.Authentication;
+import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.server.session.AbstractSessionManager;
public abstract class LoginAuthenticator implements Authenticator
@@ -37,6 +40,20 @@
{
}
+
+ /* ------------------------------------------------------------ */
+ public UserIdentity login(String username, Object password, ServletRequest request)
+ {
+ UserIdentity user = _loginService.login(username,password);
+ if (user!=null)
+ {
+ renewSession((HttpServletRequest)request, null);
+ return user;
+ }
+ return null;
+ }
+
+
public void setConfiguration(AuthConfiguration configuration)
{
_loginService=configuration.getLoginService();
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java
index 49ace10..0edde7d 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java
@@ -98,8 +98,8 @@
{
if (_session!=null && _session.getAttribute(__J_AUTHENTICATED)!=null)
_session.removeAttribute(__J_AUTHENTICATED);
- else
- doLogout();
+
+ doLogout();
}
private void doLogout()
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SpnegoAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SpnegoAuthenticator.java
index 1fc20b4..4892b0e 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SpnegoAuthenticator.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SpnegoAuthenticator.java
@@ -60,6 +60,8 @@
return _authMethod;
}
+
+
public Authentication validateRequest(ServletRequest request, ServletResponse response, boolean mandatory) throws ServerAuthException
{
HttpServletRequest req = (HttpServletRequest)request;
@@ -96,7 +98,7 @@
{
String spnegoToken = header.substring(10);
- UserIdentity user = _loginService.login(null,spnegoToken);
+ UserIdentity user = login(null,spnegoToken, request);
if ( user != null )
{
diff --git a/jetty-security/src/test/java/org/eclipse/jetty/security/ConstraintTest.java b/jetty-security/src/test/java/org/eclipse/jetty/security/ConstraintTest.java
index 30072ac..e70e931 100644
--- a/jetty-security/src/test/java/org/eclipse/jetty/security/ConstraintTest.java
+++ b/jetty-security/src/test/java/org/eclipse/jetty/security/ConstraintTest.java
@@ -22,10 +22,12 @@
import static org.junit.Assert.assertTrue;
import java.io.IOException;
+import java.util.ArrayList;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
@@ -83,6 +85,8 @@
_loginService.putUser("user",new Password("password"));
_loginService.putUser("user2",new Password("password"), new String[] {"user"});
_loginService.putUser("admin",new Password("password"), new String[] {"user","administrator"});
+ _loginService.putUser("user3", new Password("password"), new String[] {"foo"});
+
_context.setContextPath("/ctx");
_server.setHandler(_context);
@@ -203,6 +207,46 @@
@Test
public void testBasic() throws Exception
{
+
+ List<ConstraintMapping> list = new ArrayList<ConstraintMapping>(_security.getConstraintMappings());
+
+ Constraint constraint6 = new Constraint();
+ constraint6.setAuthenticate(true);
+ constraint6.setName("omit POST and GET");
+ constraint6.setRoles(new String[]{"user"});
+ ConstraintMapping mapping6 = new ConstraintMapping();
+ mapping6.setPathSpec("/omit/*");
+ mapping6.setConstraint(constraint6);
+ mapping6.setMethodOmissions(new String[]{"GET", "HEAD"}); //requests for every method except GET and HEAD must be in role "user"
+ list.add(mapping6);
+
+ Constraint constraint7 = new Constraint();
+ constraint7.setAuthenticate(true);
+ constraint7.setName("non-omitted GET");
+ constraint7.setRoles(new String[]{"administrator"});
+ ConstraintMapping mapping7 = new ConstraintMapping();
+ mapping7.setPathSpec("/omit/*");
+ mapping7.setConstraint(constraint7);
+ mapping7.setMethod("GET"); //requests for GET must be in role "admin"
+ list.add(mapping7);
+
+ Constraint constraint8 = new Constraint();
+ constraint8.setAuthenticate(true);
+ constraint8.setName("non specific");
+ constraint8.setRoles(new String[]{"foo"});
+ ConstraintMapping mapping8 = new ConstraintMapping();
+ mapping8.setPathSpec("/omit/*");
+ mapping8.setConstraint(constraint8);//requests for all methods must be in role "foo"
+ list.add(mapping8);
+
+ Set<String> knownRoles=new HashSet<String>();
+ knownRoles.add("user");
+ knownRoles.add("administrator");
+ knownRoles.add("foo");
+
+ _security.setConstraintMappings(list, knownRoles);
+
+
_security.setAuthenticator(new BasicAuthenticator());
_security.setStrict(false);
_server.start();
@@ -210,10 +254,10 @@
String response;
response = _connector.getResponses("GET /ctx/noauth/info HTTP/1.0\r\n\r\n");
assertTrue(response.startsWith("HTTP/1.1 200 OK"));
-
+
response = _connector.getResponses("GET /ctx/forbid/info HTTP/1.0\r\n\r\n");
assertTrue(response.startsWith("HTTP/1.1 403 Forbidden"));
-
+
response = _connector.getResponses("GET /ctx/auth/info HTTP/1.0\r\n\r\n");
assertTrue(response.startsWith("HTTP/1.1 401 Unauthorized"));
assertTrue(response.indexOf("WWW-Authenticate: basic realm=\"TestRealm\"") > 0);
@@ -228,7 +272,7 @@
"Authorization: Basic " + B64Code.encode("user:password") + "\r\n" +
"\r\n");
assertTrue(response.startsWith("HTTP/1.1 200 OK"));
-
+
// test admin
response = _connector.getResponses("GET /ctx/admin/info HTTP/1.0\r\n\r\n");
assertTrue(response.startsWith("HTTP/1.1 401 Unauthorized"));
@@ -254,8 +298,32 @@
response = _connector.getResponses("GET /ctx/admin/relax/info HTTP/1.0\r\n\r\n");
assertTrue(response.startsWith("HTTP/1.1 200 OK"));
+
+ //check GET is in role administrator
+ response = _connector.getResponses("GET /ctx/omit/x HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("admin:password") + "\r\n" +
+ "\r\n");
+ assertTrue(response.startsWith("HTTP/1.1 200 OK"));
+
+ //check POST is in role user
+ response = _connector.getResponses("POST /ctx/omit/x HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("user2:password") + "\r\n" +
+ "\r\n");
+ assertTrue(response.startsWith("HTTP/1.1 200 OK"));
+
+ //check POST can be in role foo too
+ response = _connector.getResponses("POST /ctx/omit/x HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("user3:password") + "\r\n" +
+ "\r\n");
+ assertTrue(response.startsWith("HTTP/1.1 200 OK"));
+
+ //check HEAD cannot be in role user
+ response = _connector.getResponses("HEAD /ctx/omit/x HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("user2:password") + "\r\n" +
+ "\r\n");
+ assertTrue(response.startsWith("HTTP/1.1 200 OK"));
}
-
+
private static String CNONCE="1234567890";
private String digest(String nonce, String username,String password,String uri,String nc) throws Exception
@@ -1023,7 +1091,7 @@
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response ) throws IOException, ServletException
{
baseRequest.setHandled(true);
- if (request.getAuthType()==null || "user".equals(request.getRemoteUser()) || request.isUserInRole("user"))
+ if (request.getAuthType()==null || "user".equals(request.getRemoteUser()) || request.isUserInRole("user") || request.isUserInRole("foo"))
{
response.setStatus(200);
response.setContentType("text/plain; charset=UTF-8");
diff --git a/jetty-security/src/test/java/org/eclipse/jetty/security/SpecExampleConstraintTest.java b/jetty-security/src/test/java/org/eclipse/jetty/security/SpecExampleConstraintTest.java
new file mode 100644
index 0000000..cee57c5
--- /dev/null
+++ b/jetty-security/src/test/java/org/eclipse/jetty/security/SpecExampleConstraintTest.java
@@ -0,0 +1,311 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.security;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.security.authentication.BasicAuthenticator;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.LocalConnector;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.server.session.SessionHandler;
+import org.eclipse.jetty.util.B64Code;
+import org.eclipse.jetty.util.security.Constraint;
+import org.eclipse.jetty.util.security.Password;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * @version $Revision: 1441 $ $Date: 2010-04-02 12:28:17 +0200 (Fri, 02 Apr 2010) $
+ */
+public class SpecExampleConstraintTest
+{
+ private static final String TEST_REALM = "TestRealm";
+ private static Server _server;
+ private static LocalConnector _connector;
+ private static SessionHandler _session;
+ private ConstraintSecurityHandler _security;
+
+ @BeforeClass
+ public static void startServer()
+ {
+ _server = new Server();
+ _connector = new LocalConnector();
+ _server.setConnectors(new Connector[]{_connector});
+
+ ContextHandler _context = new ContextHandler();
+ _session = new SessionHandler();
+
+ HashLoginService _loginService = new HashLoginService(TEST_REALM);
+ _loginService.putUser("fred",new Password("password"));
+ _loginService.putUser("harry",new Password("password"), new String[] {"HOMEOWNER"});
+ _loginService.putUser("chris",new Password("password"), new String[] {"CONTRACTOR"});
+ _loginService.putUser("steven", new Password("password"), new String[] {"SALESCLERK"});
+
+
+ _context.setContextPath("/ctx");
+ _server.setHandler(_context);
+ _context.setHandler(_session);
+
+ _server.addBean(_loginService);
+ }
+
+ @Before
+ public void setupSecurity()
+ {
+ _security = new ConstraintSecurityHandler();
+ _session.setHandler(_security);
+ RequestHandler _handler = new RequestHandler();
+ _security.setHandler(_handler);
+
+
+ /*
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>precluded methods</web-resource-name>
+ <url-pattern>/*</url-pattern>
+ <url-pattern>/acme/wholesale/*</url-pattern>
+ <url-pattern>/acme/retail/*</url-pattern>
+ <http-method-exception>GET</http-method-exception>
+ <http-method-exception>POST</http-method-exception>
+ </web-resource-collection>
+ <auth-constraint/>
+ </security-constraint>
+ */
+
+ Constraint constraint0 = new Constraint();
+ constraint0.setAuthenticate(true);
+ constraint0.setName("precluded methods");
+ ConstraintMapping mapping0 = new ConstraintMapping();
+ mapping0.setPathSpec("/*");
+ mapping0.setConstraint(constraint0);
+ mapping0.setMethodOmissions(new String[]{"GET", "POST"});
+
+ ConstraintMapping mapping1 = new ConstraintMapping();
+ mapping1.setPathSpec("/acme/wholesale/*");
+ mapping1.setConstraint(constraint0);
+ mapping1.setMethodOmissions(new String[]{"GET", "POST"});
+
+ ConstraintMapping mapping2 = new ConstraintMapping();
+ mapping2.setPathSpec("/acme/retail/*");
+ mapping2.setConstraint(constraint0);
+ mapping2.setMethodOmissions(new String[]{"GET", "POST"});
+
+ /*
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>wholesale</web-resource-name>
+ <url-pattern>/acme/wholesale/*</url-pattern>
+ <http-method>GET</http-method>
+ <http-method>PUT</http-method>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>SALESCLERK</role-name>
+ </auth-constraint>
+ </security-constraint>
+ */
+ Constraint constraint1 = new Constraint();
+ constraint1.setAuthenticate(true);
+ constraint1.setName("wholesale");
+ constraint1.setRoles(new String[]{"SALESCLERK"});
+ ConstraintMapping mapping3 = new ConstraintMapping();
+ mapping3.setPathSpec("/acme/wholesale/*");
+ mapping3.setConstraint(constraint1);
+ mapping3.setMethod("GET");
+ ConstraintMapping mapping4 = new ConstraintMapping();
+ mapping4.setPathSpec("/acme/wholesale/*");
+ mapping4.setConstraint(constraint1);
+ mapping4.setMethod("PUT");
+
+ /*
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>wholesale 2</web-resource-name>
+ <url-pattern>/acme/wholesale/*</url-pattern>
+ <http-method>GET</http-method>
+ <http-method>POST</http-method>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>CONTRACTOR</role-name>
+ </auth-constraint>
+ <user-data-constraint>
+ <transport-guarantee>CONFIDENTIAL</transport-guarantee>
+ </user-data-constraint>
+ </security-constraint>
+ */
+ Constraint constraint2 = new Constraint();
+ constraint2.setAuthenticate(true);
+ constraint2.setName("wholesale 2");
+ constraint2.setRoles(new String[]{"CONTRACTOR"});
+ constraint2.setDataConstraint(Constraint.DC_CONFIDENTIAL);
+ ConstraintMapping mapping5 = new ConstraintMapping();
+ mapping5.setPathSpec("/acme/wholesale/*");
+ mapping5.setMethod("GET");
+ mapping5.setConstraint(constraint2);
+ ConstraintMapping mapping6 = new ConstraintMapping();
+ mapping6.setPathSpec("/acme/wholesale/*");
+ mapping6.setMethod("POST");
+ mapping6.setConstraint(constraint2);
+
+ /*
+<security-constraint>
+<web-resource-collection>
+<web-resource-name>retail</web-resource-name>
+<url-pattern>/acme/retail/*</url-pattern>
+<http-method>GET</http-method>
+<http-method>POST</http-method>
+</web-resource-collection>
+<auth-constraint>
+<role-name>CONTRACTOR</role-name>
+<role-name>HOMEOWNER</role-name>
+</auth-constraint>
+</security-constraint>
+*/
+ Constraint constraint4 = new Constraint();
+ constraint4.setName("retail");
+ constraint4.setAuthenticate(true);
+ constraint4.setRoles(new String[]{"CONTRACTOR", "HOMEOWNER"});
+ ConstraintMapping mapping7 = new ConstraintMapping();
+ mapping7.setPathSpec("/acme/retail/*");
+ mapping7.setMethod("GET");
+ mapping7.setConstraint(constraint4);
+ ConstraintMapping mapping8 = new ConstraintMapping();
+ mapping8.setPathSpec("/acme/retail/*");
+ mapping8.setMethod("POST");
+ mapping8.setConstraint(constraint4);
+
+
+
+
+ Set<String> knownRoles=new HashSet<String>();
+ knownRoles.add("CONTRACTOR");
+ knownRoles.add("HOMEOWNER");
+ knownRoles.add("SALESCLERK");
+
+ _security.setConstraintMappings(Arrays.asList(new ConstraintMapping[]
+ {
+ mapping0, mapping1, mapping2, mapping3, mapping4, mapping5, mapping6, mapping7, mapping8
+ }), knownRoles);
+ }
+
+ @After
+ public void stopServer() throws Exception
+ {
+ if (_server.isRunning())
+ {
+ _server.stop();
+ _server.join();
+ }
+ }
+
+
+
+ @Test
+ public void testBasic() throws Exception
+ {
+
+ _security.setAuthenticator(new BasicAuthenticator());
+ _security.setStrict(false);
+ _server.start();
+
+ String response;
+ /*
+ /star all methods except GET/POST forbidden
+ /acme/wholesale/star all methods except GET/POST forbidden
+ /acme/retail/star all methods except GET/POST forbidden
+ /acme/wholesale/star GET must be in role CONTRACTOR or SALESCLERK
+ /acme/wholesale/star POST must be in role CONTRACTOR and confidential transport
+ /acme/retail/star GET must be in role CONTRACTOR or HOMEOWNER
+ /acme/retail/star POST must be in role CONTRACTOR or HOMEOWNER
+ */
+
+ //a user in role HOMEOWNER is forbidden HEAD request
+ response = _connector.getResponses("HEAD /ctx/index.html HTTP/1.0\r\n\r\n");
+ assertTrue(response.startsWith("HTTP/1.1 403 Forbidden"));
+
+ response = _connector.getResponses("HEAD /ctx/index.html HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("harry:password") + "\r\n" +
+ "\r\n");
+ assertTrue(response.startsWith("HTTP/1.1 403 Forbidden"));
+
+ response = _connector.getResponses("HEAD /ctx/acme/wholesale/index.html HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("harry:password") + "\r\n" +
+ "\r\n");
+ assertTrue(response.startsWith("HTTP/1.1 403 Forbidden"));
+
+ response = _connector.getResponses("HEAD /ctx/acme/retail/index.html HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("harry:password") + "\r\n" +
+ "\r\n");
+ assertTrue(response.startsWith("HTTP/1.1 403 Forbidden"));
+
+ //a user in role CONTRACTOR can do a GET
+ response = _connector.getResponses("GET /ctx/acme/wholesale/index.html HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("chris:password") + "\r\n" +
+ "\r\n");
+
+ assertTrue(response.startsWith("HTTP/1.1 200 OK"));
+
+ //a user in role CONTRACTOR can only do a post if confidential
+ response = _connector.getResponses("POST /ctx/acme/wholesale/index.html HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("chris:password") + "\r\n" +
+ "\r\n");
+ assertTrue(response.startsWith("HTTP/1.1 403 !Confidential"));
+
+
+ //a user in role HOMEOWNER can do a GET
+ response = _connector.getResponses("GET /ctx/acme/retail/index.html HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("harry:password") + "\r\n" +
+ "\r\n");
+ assertTrue(response.startsWith("HTTP/1.1 200 OK"));
+ }
+
+
+ private class RequestHandler extends AbstractHandler
+ {
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response ) throws IOException, ServletException
+ {
+ baseRequest.setHandled(true);
+
+ response.setStatus(200);
+ response.setContentType("text/plain; charset=UTF-8");
+ response.getWriter().println("URI="+request.getRequestURI());
+ String user = request.getRemoteUser();
+ response.getWriter().println("user="+user);
+ if (request.getParameter("test_parameter")!=null)
+ response.getWriter().println(request.getParameter("test_parameter"));
+ }
+ }
+
+}
diff --git a/jetty-server/pom.xml b/jetty-server/pom.xml
index 6c87eef..cfb9819 100644
--- a/jetty-server/pom.xml
+++ b/jetty-server/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-server</artifactId>
@@ -26,7 +26,7 @@
</goals>
<configuration>
<instructions>
- <Import-Package>javax.servlet.*;version="[2.5,3.0)",org.eclipse.jetty.jmx.*;version="[7.3,8)";resolution:=optional,*</Import-Package>
+ <Import-Package>javax.servlet.*;version="2.6.0",org.eclipse.jetty.jmx.*;version="8.0";resolution:=optional,*</Import-Package>
</instructions>
</configuration>
</execution>
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractHttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractHttpConnection.java
index f740180..ccdd878 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractHttpConnection.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractHttpConnection.java
@@ -18,10 +18,45 @@
package org.eclipse.jetty.server;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
import org.eclipse.jetty.continuation.ContinuationThrowable;
-import org.eclipse.jetty.http.*;
-import org.eclipse.jetty.io.*;
+import org.eclipse.jetty.http.EncodedHttpURI;
+import org.eclipse.jetty.http.Generator;
+import org.eclipse.jetty.http.HttpBuffers;
+import org.eclipse.jetty.http.HttpContent;
+import org.eclipse.jetty.http.HttpException;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpGenerator;
+import org.eclipse.jetty.http.HttpHeaderValues;
+import org.eclipse.jetty.http.HttpHeaders;
+import org.eclipse.jetty.http.HttpMethods;
+import org.eclipse.jetty.http.HttpParser;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.HttpVersions;
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.http.Parser;
+import org.eclipse.jetty.http.PathMap;
+import org.eclipse.jetty.io.AbstractConnection;
+import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.io.BufferCache.CachedBuffer;
+import org.eclipse.jetty.io.Buffers;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.EofException;
+import org.eclipse.jetty.io.RuntimeIOException;
+import org.eclipse.jetty.io.UncheckedPrintWriter;
+import org.eclipse.jetty.server.handler.ErrorHandler;
import org.eclipse.jetty.server.nio.NIOConnector;
import org.eclipse.jetty.server.ssl.SslConnector;
import org.eclipse.jetty.util.QuotedStringTokenizer;
@@ -31,13 +66,6 @@
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
-import javax.servlet.ServletInputStream;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PrintWriter;
-
/**
* <p>A HttpConnection represents the connection of a HTTP client to the server
* and is created by an instance of a {@link Connector}. It's prime function is
@@ -401,6 +429,7 @@
boolean error = false;
String threadName=null;
+ Throwable async_exception=null;
try
{
if (LOG.isDebugEnabled())
@@ -419,6 +448,7 @@
// within the call to unhandle().
final Server server=_server;
+ boolean was_continuation=_request._async.isContinuation();
boolean handling=_request._async.handling() && server!=null && server.isRunning();
while (handling)
{
@@ -444,7 +474,7 @@
info=URIUtil.canonicalPath(path);
if (info==null && !_request.getMethod().equals(HttpMethods.CONNECT))
{
- if (_uri.getScheme()!=null && _uri.getHost()!=null)
+ if (path==null && _uri.getScheme()!=null && _uri.getHost()!=null)
{
info="/";
_request.setRequestURI("");
@@ -465,7 +495,27 @@
}
else
{
- _request.setDispatcherType(DispatcherType.ASYNC);
+ if (_request._async.isExpired()&&!was_continuation)
+ {
+ async_exception = (Throwable)_request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
+ _response.setStatus(500,async_exception==null?"Async Timeout":"Async Exception");
+ _request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,new Integer(500));
+ _request.setAttribute(RequestDispatcher.ERROR_MESSAGE, _response.getReason());
+ _request.setDispatcherType(DispatcherType.ERROR);
+
+ ErrorHandler eh = _request._async.getContextHandler().getErrorHandler();
+ if (eh instanceof ErrorHandler.ErrorPageMapper)
+ {
+ String error_page=((ErrorHandler.ErrorPageMapper)eh).getErrorPage((HttpServletRequest)_request._async.getRequest());
+ if (error_page!=null)
+ {
+ AsyncContinuation.AsyncEventState state = _request._async.getAsyncEventState();
+ state.setPath(error_page);
+ }
+ }
+ }
+ else
+ _request.setDispatcherType(DispatcherType.ASYNC);
server.handleAsync(this);
}
}
@@ -475,6 +525,7 @@
}
catch (EofException e)
{
+ async_exception=e;
LOG.debug(e);
error=true;
_request.setHandled(true);
@@ -483,6 +534,7 @@
}
catch (RuntimeIOException e)
{
+ async_exception=e;
LOG.debug(e);
error=true;
_request.setHandled(true);
@@ -496,13 +548,20 @@
}
catch (Throwable e)
{
+ async_exception=e;
LOG.warn(String.valueOf(_uri),e);
error=true;
_request.setHandled(true);
_generator.sendError(info==null?400:500, null, null, true);
+
}
finally
{
+ // Complete async requests
+ if (error && _request.isAsyncStarted())
+ _request.getAsyncContinuation().errorComplete();
+
+ was_continuation=_request._async.isContinuation();
handling = !_request._async.unhandle() && server.isRunning() && _server!=null;
}
}
@@ -514,7 +573,8 @@
if (_request._async.isUncompleted())
{
- _request._async.doComplete();
+
+ _request._async.doComplete(async_exception);
if (_expect100Continue)
{
@@ -844,7 +904,7 @@
_endp.close();
return;
}
-
+
_requests++;
_generator.setVersion(_version);
switch (_version)
@@ -1138,12 +1198,12 @@
if (lml!=-1)
_responseFields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER, lml);
}
-
+
Buffer etag=httpContent.getETag();
if (etag!=null)
_responseFields.put(HttpHeaders.ETAG_BUFFER,etag);
-
+
boolean direct=_connector instanceof NIOConnector && ((NIOConnector)_connector).getUseDirectBuffers() && !(_connector instanceof SslConnector);
content = direct?httpContent.getDirectBuffer():httpContent.getIndirectBuffer();
if (content==null)
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContext.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContext.java
deleted file mode 100644
index 0a10923..0000000
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContext.java
+++ /dev/null
@@ -1,48 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2016 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;
-
-import javax.servlet.ServletContext;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-
-import org.eclipse.jetty.continuation.ContinuationListener;
-
-/* temporary interface in anticipation of servlet 3.0 */
-public interface AsyncContext
-{
- static final String ASYNC_REQUEST_URI = "javax.servlet.async.request_uri";
- static final String ASYNC_CONTEXT_PATH = "javax.servlet.async.context_path";
- static final String ASYNC_PATH_INFO = "javax.servlet.async.path_info";
- static final String ASYNC_SERVLET_PATH = "javax.servlet.async.servlet_path";
- static final String ASYNC_QUERY_STRING = "javax.servlet.async.query_string";
-
- public ServletRequest getRequest();
- public ServletResponse getResponse();
- public boolean hasOriginalRequestAndResponse();
- public void dispatch();
- public void dispatch(String path);
- public void dispatch(ServletContext context, String path);
- public void complete();
- public void start(Runnable run);
- public void setTimeout(long ms);
- public void addContinuationListener(ContinuationListener listener);
-}
-
-
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContinuation.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContinuation.java
index 4605871..2309d95 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContinuation.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContinuation.java
@@ -18,6 +18,12 @@
package org.eclipse.jetty.server;
+import javax.servlet.AsyncContext;
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+
import java.util.ArrayList;
import java.util.List;
@@ -27,8 +33,8 @@
import javax.servlet.http.HttpServletRequest;
import org.eclipse.jetty.continuation.Continuation;
-import org.eclipse.jetty.continuation.ContinuationListener;
import org.eclipse.jetty.continuation.ContinuationThrowable;
+import org.eclipse.jetty.continuation.ContinuationListener;
import org.eclipse.jetty.io.AsyncEndPoint;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.handler.ContextHandler;
@@ -39,7 +45,7 @@
import org.eclipse.jetty.util.thread.Timeout;
/* ------------------------------------------------------------ */
-/** Implementation of Continuation and AsyncContext interfaces.
+/** Implementation of Continuation and AsyncContext interfaces
*
*/
public class AsyncContinuation implements AsyncContext, Continuation
@@ -74,9 +80,10 @@
private static final int __UNCOMPLETED=8; // Request is completable
private static final int __COMPLETED=9; // Request is complete
-
/* ------------------------------------------------------------ */
protected AbstractHttpConnection _connection;
+ private List<AsyncListener> _lastAsyncListeners;
+ private List<AsyncListener> _asyncListeners;
private List<ContinuationListener> _continuationListeners;
/* ------------------------------------------------------------ */
@@ -87,7 +94,8 @@
private volatile boolean _responseWrapped;
private long _timeoutMs=DEFAULT_TIMEOUT;
private AsyncEventState _event;
- private volatile long _expireAt;
+ private volatile long _expireAt;
+ private volatile boolean _continuation;
/* ------------------------------------------------------------ */
protected AsyncContinuation()
@@ -106,6 +114,29 @@
}
/* ------------------------------------------------------------ */
+ public void addListener(AsyncListener listener)
+ {
+ synchronized(this)
+ {
+ if (_asyncListeners==null)
+ _asyncListeners=new ArrayList<AsyncListener>();
+ _asyncListeners.add(listener);
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ public void addListener(AsyncListener listener,ServletRequest request, ServletResponse response)
+ {
+ synchronized(this)
+ {
+ // TODO handle the request/response ???
+ if (_asyncListeners==null)
+ _asyncListeners=new ArrayList<AsyncListener>();
+ _asyncListeners.add(listener);
+ }
+ }
+
+ /* ------------------------------------------------------------ */
public void addContinuationListener(ContinuationListener listener)
{
synchronized(this)
@@ -169,6 +200,11 @@
}
}
+ public boolean isContinuation()
+ {
+ return _continuation;
+ }
+
/* ------------------------------------------------------------ */
/* (non-Javadoc)
* @see javax.servlet.ServletRequest#isSuspended()
@@ -268,13 +304,22 @@
{
synchronized (this)
{
- _responseWrapped=false;
+ _continuation=false;
switch(_state)
{
case __IDLE:
_initial=true;
_state=__DISPATCHED;
+ if (_lastAsyncListeners!=null)
+ _lastAsyncListeners.clear();
+ if (_asyncListeners!=null)
+ _asyncListeners.clear();
+ else
+ {
+ _asyncListeners=_lastAsyncListeners;
+ _lastAsyncListeners=null;
+ }
return true;
case __COMPLETING:
@@ -311,7 +356,7 @@
_resumed=false;
_expired=false;
- if (_event==null || request!=_event.getRequest() || response != _event.getResponse() || context != _event.getServletContext())
+ if (_event==null || request!=_event.getSuppliedRequest() || response != _event.getSuppliedResponse() || context != _event.getServletContext())
_event=new AsyncEventState(context,request,response);
else
{
@@ -319,12 +364,32 @@
_event._pathInContext=null;
}
_state=__ASYNCSTARTED;
+ List<AsyncListener> recycle=_lastAsyncListeners;
+ _lastAsyncListeners=_asyncListeners;
+ _asyncListeners=recycle;
+ if (_asyncListeners!=null)
+ _asyncListeners.clear();
break;
default:
throw new IllegalStateException(this.getStatusString());
}
}
+
+ if (_lastAsyncListeners!=null)
+ {
+ for (AsyncListener listener : _lastAsyncListeners)
+ {
+ try
+ {
+ listener.onStartAsync(_event);
+ }
+ catch(Exception e)
+ {
+ LOG.warn(e);
+ }
+ }
+ }
}
/* ------------------------------------------------------------ */
@@ -339,8 +404,6 @@
{
synchronized (this)
{
- List<ContinuationListener> listeners=_continuationListeners;
-
switch(_state)
{
case __REDISPATCHED:
@@ -361,7 +424,7 @@
{
_state=__UNCOMPLETED;
return true;
- }
+ }
_initial=false;
_state=__REDISPATCHED;
return false;
@@ -419,27 +482,45 @@
/* ------------------------------------------------------------ */
protected void expired()
{
- final List<ContinuationListener> listeners;
+ final List<ContinuationListener> cListeners;
+ final List<AsyncListener> aListeners;
synchronized (this)
{
switch(_state)
{
case __ASYNCSTARTED:
case __ASYNCWAIT:
- listeners=_continuationListeners;
+ cListeners=_continuationListeners;
+ aListeners=_asyncListeners;
break;
default:
- listeners=null;
+ cListeners=null;
+ aListeners=null;
return;
}
_expired=true;
}
- if (listeners!=null)
+ if (aListeners!=null)
{
- for (int i=0;i<listeners.size();i++)
+ for (AsyncListener listener : aListeners)
{
- ContinuationListener listener=listeners.get(i);
+ try
+ {
+ listener.onTimeout(_event);
+ }
+ catch(Exception e)
+ {
+ LOG.debug(e);
+ _connection.getRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,e);
+ break;
+ }
+ }
+ }
+ if (cListeners!=null)
+ {
+ for (ContinuationListener listener : cListeners)
+ {
try
{
listener.onTimeout(this);
@@ -458,6 +539,11 @@
case __ASYNCSTARTED:
case __ASYNCWAIT:
dispatch();
+ break;
+
+ default:
+ if (!_continuation)
+ _expired=false;
}
}
@@ -500,37 +586,102 @@
scheduleDispatch();
}
}
-
/* ------------------------------------------------------------ */
/* (non-Javadoc)
* @see javax.servlet.ServletRequest#complete()
*/
- protected void doComplete()
+ public void errorComplete()
{
- final List<ContinuationListener> listeners;
+ // just like complete except can overrule a prior dispatch call;
+ synchronized (this)
+ {
+ switch(_state)
+ {
+ case __REDISPATCHING:
+ case __ASYNCSTARTED:
+ _state=__COMPLETING;
+ _resumed=false;
+ return;
+
+ case __COMPLETING:
+ return;
+
+ default:
+ throw new IllegalStateException(this.getStatusString());
+ }
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public <T extends AsyncListener> T createListener(Class<T> clazz) throws ServletException
+ {
+ try
+ {
+ // TODO inject
+ return clazz.newInstance();
+ }
+ catch(Exception e)
+ {
+ throw new ServletException(e);
+ }
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /* (non-Javadoc)
+ * @see javax.servlet.ServletRequest#complete()
+ */
+ protected void doComplete(Throwable ex)
+ {
+ final List<ContinuationListener> cListeners;
+ final List<AsyncListener> aListeners;
synchronized (this)
{
switch(_state)
{
case __UNCOMPLETED:
_state=__COMPLETED;
- listeners=_continuationListeners;
+ cListeners=_continuationListeners;
+ aListeners=_asyncListeners;
break;
default:
- listeners=null;
+ cListeners=null;
+ aListeners=null;
throw new IllegalStateException(this.getStatusString());
}
}
- if (listeners!=null)
+ if (aListeners!=null)
{
- for(int i=0;i<listeners.size();i++)
+ for (AsyncListener listener : aListeners)
{
try
{
- listeners.get(i).onComplete(this);
+ if (ex!=null)
+ {
+ _event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,ex);
+ _event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_MESSAGE,ex.getMessage());
+ listener.onError(_event);
+ }
+ else
+ listener.onComplete(_event);
+ }
+ catch(Exception e)
+ {
+ LOG.warn(e);
+ }
+ }
+ }
+ if (cListeners!=null)
+ {
+ for (ContinuationListener listener : cListeners)
+ {
+ try
+ {
+ listener.onComplete(this);
}
catch(Exception e)
{
@@ -545,7 +696,6 @@
{
synchronized (this)
{
-// _history.append("r\n");
switch(_state)
{
case __DISPATCHED:
@@ -617,7 +767,7 @@
}
else
{
- ((AsyncEndPoint)endp).scheduleTimeout(_event,_timeoutMs);
+ ((AsyncEndPoint)endp).scheduleTimeout(_event._timeout,_timeoutMs);
}
}
}
@@ -639,7 +789,7 @@
final AsyncEventState event=_event;
if (event!=null)
{
- ((AsyncEndPoint)endp).cancelTimeout(event);
+ ((AsyncEndPoint)endp).cancelTimeout(event._timeout);
}
}
}
@@ -715,14 +865,14 @@
public void dispatch(ServletContext context, String path)
{
_event._dispatchContext=context;
- _event._pathInContext=path;
+ _event.setPath(path);
dispatch();
}
/* ------------------------------------------------------------ */
public void dispatch(String path)
{
- _event._pathInContext=path;
+ _event.setPath(path);
dispatch();
}
@@ -736,15 +886,15 @@
public ServletRequest getRequest()
{
if (_event!=null)
- return _event.getRequest();
+ return _event.getSuppliedRequest();
return _connection.getRequest();
}
/* ------------------------------------------------------------ */
public ServletResponse getResponse()
{
- if (_event!=null)
- return _event.getResponse();
+ if (_responseWrapped && _event!=null && _event.getSuppliedResponse()!=null)
+ return _event.getSuppliedResponse();
return _connection.getResponse();
}
@@ -769,7 +919,7 @@
{
synchronized (this)
{
- return (_event!=null && _event.getRequest()==_connection._request && _event.getResponse()==_connection._response);
+ return (_event!=null && _event.getSuppliedRequest()==_connection._request && _event.getSuppliedResponse()==_connection._response);
}
}
@@ -818,7 +968,7 @@
/* ------------------------------------------------------------ */
- protected void suspend(final ServletContext context,
+ protected void startAsync(final ServletContext context,
final ServletRequest request,
final ServletResponse response)
{
@@ -833,6 +983,14 @@
}
}
+ /* ------------------------------------------------------------ */
+ protected void startAsync()
+ {
+ _responseWrapped=false;
+ _continuation=false;
+ doSuspend(_connection.getRequest().getServletContext(),_connection.getRequest(),_connection.getResponse());
+ }
+
/* ------------------------------------------------------------ */
/**
@@ -840,6 +998,7 @@
*/
public void suspend(ServletResponse response)
{
+ _continuation=true;
_responseWrapped=!(response instanceof Response);
doSuspend(_connection.getRequest().getServletContext(),_connection.getRequest(),response);
}
@@ -851,6 +1010,7 @@
public void suspend()
{
_responseWrapped=false;
+ _continuation=true;
doSuspend(_connection.getRequest().getServletContext(),_connection.getRequest(),_connection.getResponse());
}
@@ -860,8 +1020,8 @@
*/
public ServletResponse getServletResponse()
{
- if (_responseWrapped && _event!=null && _event.getResponse()!=null)
- return _event.getResponse();
+ if (_responseWrapped && _event!=null && _event.getSuppliedResponse()!=null)
+ return _event.getSuppliedResponse();
return _connection.getResponse();
}
@@ -910,21 +1070,34 @@
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
- public class AsyncEventState extends Timeout.Task implements Runnable
+ public class AsyncTimeout extends Timeout.Task implements Runnable
+ {
+ @Override
+ public void expired()
+ {
+ AsyncContinuation.this.expired();
+ }
+
+ @Override
+ public void run()
+ {
+ AsyncContinuation.this.expired();
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /* ------------------------------------------------------------ */
+ public class AsyncEventState extends AsyncEvent
{
private final ServletContext _suspendedContext;
- private final ServletRequest _request;
- private final ServletResponse _response;
private ServletContext _dispatchContext;
private String _pathInContext;
+ private Timeout.Task _timeout= new AsyncTimeout();
public AsyncEventState(ServletContext context, ServletRequest request, ServletResponse response)
{
+ super(AsyncContinuation.this, request,response);
_suspendedContext=context;
- _request=request;
- _response=response;
-
-
// Get the base request So we can remember the initial paths
Request r=_connection.getRequest();
@@ -935,14 +1108,14 @@
// they are only available after a call to AsyncContext.dispatch(...);
// have we been forwarded before?
- String uri=(String)r.getAttribute(Dispatcher.FORWARD_REQUEST_URI);
+ String uri=(String)r.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
if (uri!=null)
{
r.setAttribute(AsyncContext.ASYNC_REQUEST_URI,uri);
- r.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,r.getAttribute(Dispatcher.FORWARD_CONTEXT_PATH));
- r.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,r.getAttribute(Dispatcher.FORWARD_SERVLET_PATH));
- r.setAttribute(AsyncContext.ASYNC_PATH_INFO,r.getAttribute(Dispatcher.FORWARD_PATH_INFO));
- r.setAttribute(AsyncContext.ASYNC_QUERY_STRING,r.getAttribute(Dispatcher.FORWARD_QUERY_STRING));
+ r.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,r.getAttribute(RequestDispatcher.FORWARD_CONTEXT_PATH));
+ r.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,r.getAttribute(RequestDispatcher.FORWARD_SERVLET_PATH));
+ r.setAttribute(AsyncContext.ASYNC_PATH_INFO,r.getAttribute(RequestDispatcher.FORWARD_PATH_INFO));
+ r.setAttribute(AsyncContext.ASYNC_QUERY_STRING,r.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING));
}
else
{
@@ -969,15 +1142,10 @@
{
return _dispatchContext==null?_suspendedContext:_dispatchContext;
}
-
- public ServletRequest getRequest()
+
+ public void setPath(String path)
{
- return _request;
- }
-
- public ServletResponse getResponse()
- {
- return _response;
+ _pathInContext=path;
}
/* ------------------------------------------------------------ */
@@ -988,16 +1156,5 @@
{
return _pathInContext;
}
-
- @Override
- public void expired()
- {
- AsyncContinuation.this.expired();
- }
-
- public void run()
- {
- AsyncContinuation.this.expired();
- }
}
}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Authentication.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Authentication.java
index 56180ca..92df613 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Authentication.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Authentication.java
@@ -85,7 +85,7 @@
* @param password
* @return The new Authentication state
*/
- Authentication login(String username,String password);
+ Authentication login(String username,Object password,ServletRequest request);
}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java
index 50fff2c..fc6a4da 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java
@@ -25,6 +25,7 @@
import java.util.Iterator;
import java.util.Map;
+import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
@@ -45,24 +46,6 @@
*/
public class Dispatcher implements RequestDispatcher
{
- public static final String FORWARD_REQUEST_URI = "javax.servlet.forward.request_uri";
- public static final String FORWARD_CONTEXT_PATH = "javax.servlet.forward.context_path";
- public static final String FORWARD_PATH_INFO = "javax.servlet.forward.path_info";
- public static final String FORWARD_SERVLET_PATH = "javax.servlet.forward.servlet_path";
- public static final String FORWARD_QUERY_STRING = "javax.servlet.forward.query_string";
- public static final String INCLUDE_REQUEST_URI = "javax.servlet.include.request_uri";
- public static final String INCLUDE_CONTEXT_PATH = "javax.servlet.include.context_path";
- public static final String INCLUDE_PATH_INFO = "javax.servlet.include.path_info";
- public static final String INCLUDE_SERVLET_PATH = "javax.servlet.include.servlet_path";
- public static final String INCLUDE_QUERY_STRING = "javax.servlet.include.query_string";
-
- public static final String ERROR_EXCEPTION = "javax.servlet.error.exception";
- public static final String ERROR_EXCEPTION_TYPE = "javax.servlet.error.exception_type";
- public static final String ERROR_MESSAGE = "javax.servlet.error.message";
- public static final String ERROR_REQUEST_URI = "javax.servlet.error.request_uri";
- public static final String ERROR_SERVLET_NAME = "javax.servlet.error.servlet_name";
- public static final String ERROR_STATUS_CODE = "javax.servlet.error.status_code";
-
/** Dispatch include attribute names */
public final static String __INCLUDE_PREFIX="javax.servlet.include.";
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/DispatcherType.java b/jetty-server/src/main/java/org/eclipse/jetty/server/DispatcherType.java
deleted file mode 100644
index a1f0162..0000000
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/DispatcherType.java
+++ /dev/null
@@ -1,28 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2016 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;
-
-public enum DispatcherType
-{
- FORWARD,
- INCLUDE,
- REQUEST,
- ASYNC,
- ERROR
-}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
index d2ae982..f097c59 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
@@ -19,6 +19,8 @@
package org.eclipse.jetty.server;
import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -37,16 +39,25 @@
import java.util.Map;
import java.util.Map.Entry;
+import javax.servlet.AsyncContext;
+import javax.servlet.AsyncListener;
+import javax.servlet.DispatcherType;
+import javax.servlet.MultipartConfigElement;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
+import javax.servlet.ServletRequestEvent;
+import javax.servlet.ServletRequestListener;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
+import javax.servlet.http.Part;
import org.eclipse.jetty.continuation.Continuation;
import org.eclipse.jetty.continuation.ContinuationListener;
@@ -61,6 +72,7 @@
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.io.BufferUtil;
+import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.nio.DirectNIOBuffer;
import org.eclipse.jetty.io.nio.IndirectNIOBuffer;
@@ -69,8 +81,11 @@
import org.eclipse.jetty.server.handler.ContextHandler.Context;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.AttributesMap;
+import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.LazyList;
+import org.eclipse.jetty.util.MultiException;
import org.eclipse.jetty.util.MultiMap;
+import org.eclipse.jetty.util.MultiPartInputStream;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.UrlEncoded;
@@ -111,12 +126,51 @@
*/
public class Request implements HttpServletRequest
{
+ public static final String __MULTIPART_CONFIG_ELEMENT = "org.eclipse.multipartConfig";
+ public static final String __MULTIPART_INPUT_STREAM = "org.eclipse.multiPartInputStream";
+ public static final String __MULTIPART_CONTEXT = "org.eclipse.multiPartContext";
private static final Logger LOG = Log.getLogger(Request.class);
private static final String __ASYNC_FWD = "org.eclipse.asyncfwd";
private static final Collection __defaultLocale = Collections.singleton(Locale.getDefault());
private static final int __NONE = 0, _STREAM = 1, __READER = 2;
+ public static class MultiPartCleanerListener implements ServletRequestListener
+ {
+
+ @Override
+ public void requestDestroyed(ServletRequestEvent sre)
+ {
+ //Clean up any tmp files created by MultiPartInputStream
+ MultiPartInputStream mpis = (MultiPartInputStream)sre.getServletRequest().getAttribute(__MULTIPART_INPUT_STREAM);
+ if (mpis != null)
+ {
+ ContextHandler.Context context = (ContextHandler.Context)sre.getServletRequest().getAttribute(__MULTIPART_CONTEXT);
+
+ //Only do the cleanup if we are exiting from the context in which a servlet parsed the multipart files
+ if (context == sre.getServletContext())
+ {
+ try
+ {
+ mpis.deleteParts();
+ }
+ catch (MultiException e)
+ {
+ sre.getServletContext().log("Errors deleting multipart tmp files", e);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void requestInitialized(ServletRequestEvent sre)
+ {
+ //nothing to do, multipart config set up by ServletHolder.handle()
+ }
+
+ }
+
+
/* ------------------------------------------------------------ */
public static Request getRequest(HttpServletRequest request)
{
@@ -170,7 +224,9 @@
private Buffer _timeStampBuffer;
private HttpURI _uri;
-
+
+ private MultiPartInputStream _multiPartInputStream; //if the request is a multi-part mime
+
/* ------------------------------------------------------------ */
public Request()
{
@@ -188,7 +244,9 @@
if (listener instanceof ServletRequestAttributeListener)
_requestAttributeListeners = LazyList.add(_requestAttributeListeners,listener);
if (listener instanceof ContinuationListener)
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException(listener.getClass().toString());
+ if (listener instanceof AsyncListener)
+ throw new IllegalArgumentException(listener.getClass().toString());
}
/* ------------------------------------------------------------ */
@@ -306,6 +364,7 @@
}
}
}
+
}
if (_parameters == null)
@@ -323,6 +382,28 @@
_parameters.add(name,LazyList.get(values,i));
}
}
+
+ if (content_type != null && content_type.length()>0 && content_type.startsWith("multipart/form-data") && getAttribute(__MULTIPART_CONFIG_ELEMENT)!=null)
+ {
+ try
+ {
+ getParts();
+ }
+ catch (IOException e)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.warn(e);
+ else
+ LOG.warn(e.toString());
+ }
+ catch (ServletException e)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.warn(e);
+ else
+ LOG.warn(e.toString());
+ }
+ }
}
finally
{
@@ -345,7 +426,7 @@
{
return _async;
}
-
+
/* ------------------------------------------------------------ */
/*
* @see javax.servlet.ServletRequest#getAttribute(java.lang.String)
@@ -401,8 +482,8 @@
public String getAuthType()
{
if (_authentication instanceof Authentication.Deferred)
- _authentication = ((Authentication.Deferred)_authentication).authenticate(this);
-
+ setAuthentication(((Authentication.Deferred)_authentication).authenticate(this));
+
if (_authentication instanceof Authentication.User)
return ((Authentication.User)_authentication).getAuthMethod();
return null;
@@ -905,6 +986,8 @@
*/
public RequestDispatcher getRequestDispatcher(String path)
{
+ path = URIUtil.compactPath(path);
+
if (path == null || _context == null)
return null;
@@ -1279,6 +1362,7 @@
UserIdentity user = ((Authentication.User)_authentication).getUserIdentity();
return user.getUserPrincipal();
}
+
return null;
}
@@ -1299,6 +1383,12 @@
return _handled;
}
+ public boolean isAsyncStarted()
+ {
+ return _async.isAsyncStarted();
+ }
+
+
/* ------------------------------------------------------------ */
public boolean isAsyncSupported()
{
@@ -1434,7 +1524,8 @@
if (_savedNewSessions != null)
_savedNewSessions.clear();
- _savedNewSessions = null;
+ _savedNewSessions=null;
+ _multiPartInputStream = null;
}
/* ------------------------------------------------------------ */
@@ -1915,7 +2006,7 @@
{
if (!_asyncSupported)
throw new IllegalStateException("!asyncSupported");
- _async.suspend();
+ _async.startAsync();
return _async;
}
@@ -1924,7 +2015,7 @@
{
if (!_asyncSupported)
throw new IllegalStateException("!asyncSupported");
- _async.suspend(_context,servletRequest,servletResponse);
+ _async.startAsync(_context,servletRequest,servletResponse);
return _async;
}
@@ -1936,6 +2027,103 @@
}
/* ------------------------------------------------------------ */
+ public boolean authenticate(HttpServletResponse response) throws IOException, ServletException
+ {
+ if (_authentication instanceof Authentication.Deferred)
+ {
+ setAuthentication(((Authentication.Deferred)_authentication).authenticate(this,response));
+ return !(_authentication instanceof Authentication.ResponseSent);
+ }
+ response.sendError(HttpStatus.UNAUTHORIZED_401);
+ return false;
+ }
+
+ /* ------------------------------------------------------------ */
+ public Part getPart(String name) throws IOException, ServletException
+ {
+ getParts();
+ return _multiPartInputStream.getPart(name);
+ }
+
+ /* ------------------------------------------------------------ */
+ public Collection<Part> getParts() throws IOException, ServletException
+ {
+ if (getContentType() == null || !getContentType().startsWith("multipart/form-data"))
+ throw new ServletException("Content-Type != multipart/form-data");
+
+ if (_multiPartInputStream == null)
+ _multiPartInputStream = (MultiPartInputStream)getAttribute(__MULTIPART_INPUT_STREAM);
+
+ if (_multiPartInputStream == null)
+ {
+ MultipartConfigElement config = (MultipartConfigElement)getAttribute(__MULTIPART_CONFIG_ELEMENT);
+
+ if (config == null)
+ throw new IllegalStateException("No multipart config for servlet");
+
+ _multiPartInputStream = new MultiPartInputStream(getInputStream(),
+ getContentType(), config,
+ (_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null));
+
+ setAttribute(__MULTIPART_INPUT_STREAM, _multiPartInputStream);
+ setAttribute(__MULTIPART_CONTEXT, _context);
+ Collection<Part> parts = _multiPartInputStream.getParts(); //causes parsing
+ for (Part p:parts)
+ {
+ MultiPartInputStream.MultiPart mp = (MultiPartInputStream.MultiPart)p;
+ if (mp.getContentDispositionFilename() == null)
+ {
+ //Servlet Spec 3.0 pg 23, parts without filenames must be put into init params
+ String charset = null;
+ if (mp.getContentType() != null)
+ charset = MimeTypes.getCharsetFromContentType(new ByteArrayBuffer(mp.getContentType()));
+
+ ByteArrayOutputStream os = null;
+ InputStream is = mp.getInputStream(); //get the bytes regardless of being in memory or in temp file
+ try
+ {
+ os = new ByteArrayOutputStream();
+ IO.copy(is, os);
+ String content=new String(os.toByteArray(),charset==null?StringUtil.__UTF8:charset);
+ getParameter(""); //cause params to be evaluated
+ getParameters().add(mp.getName(), content);
+ }
+ finally
+ {
+ IO.close(os);
+ IO.close(is);
+ }
+ }
+ }
+ }
+
+ return _multiPartInputStream.getParts();
+ }
+
+ /* ------------------------------------------------------------ */
+ public void login(String username, String password) throws ServletException
+ {
+ if (_authentication instanceof Authentication.Deferred)
+ {
+ _authentication=((Authentication.Deferred)_authentication).login(username,password,this);
+ if (_authentication == null)
+ throw new ServletException();
+ }
+ else
+ {
+ throw new ServletException("Authenticated as "+_authentication);
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ public void logout() throws ServletException
+ {
+ if (_authentication instanceof Authentication.User)
+ ((Authentication.User)_authentication).logout();
+ _authentication=Authentication.UNAUTHENTICATED;
+ }
+
+ /* ------------------------------------------------------------ */
/**
* Merge in a new query string. The query string is merged with the existing parameters and {@link #setParameters(MultiMap)} and
* {@link #setQueryString(String)} are called with the result. The merge is according to the rules of the servlet dispatch forward method.
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java
index 936f4b0..161d051 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java
@@ -20,11 +20,13 @@
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.Collection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Locale;
+import javax.servlet.RequestDispatcher;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
@@ -58,7 +60,7 @@
public class Response implements HttpServletResponse
{
private static final Logger LOG = Log.getLogger(Response.class);
-
+ private final static int __MIN_BUFFER_SIZE = 1;
public static final int
NONE=0,
@@ -164,7 +166,7 @@
cookie.getMaxAge(),
comment,
cookie.getSecure(),
- http_only,// || cookie.isHttpOnly(),
+ http_only || cookie.isHttpOnly(),
cookie.getVersion());
}
@@ -211,7 +213,7 @@
return null;
// should not encode if cookies in evidence
- if (request.isRequestedSessionIdFromCookie())
+ if ((sessionManager.isUsingCookies() && request.isRequestedSessionIdFromCookie()) || !sessionManager.isUsingURLs())
{
int prefix=url.indexOf(sessionURLPrefix);
if (prefix!=-1)
@@ -340,10 +342,10 @@
error_handler = _connection.getConnector().getServer().getBean(ErrorHandler.class);
if (error_handler!=null)
{
- request.setAttribute(Dispatcher.ERROR_STATUS_CODE,new Integer(code));
- request.setAttribute(Dispatcher.ERROR_MESSAGE, message);
- request.setAttribute(Dispatcher.ERROR_REQUEST_URI, request.getRequestURI());
- request.setAttribute(Dispatcher.ERROR_SERVLET_NAME,request.getServletName());
+ request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,new Integer(code));
+ request.setAttribute(RequestDispatcher.ERROR_MESSAGE, message);
+ request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI, request.getRequestURI());
+ request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME,request.getServletName());
error_handler.handle(null,_connection.getRequest(),_connection.getRequest(),this );
}
else
@@ -379,7 +381,14 @@
writer.write(". Reason:\n<pre> ");
writer.write(message);
writer.write("</pre>");
- writer.write("</p>\n<hr /><i><small>Powered by Jetty://</small></i>");
+ writer.write("</p>\n");
+
+ if(_connection.getServer().getSendServerVersion())
+ {
+ writer.write("<hr /><i><small>Powered by Jetty:// ");
+ writer.write(Server.getVersion());
+ writer.write("</small></i>");
+ }
for (int i= 0; i < 20; i++)
writer.write("\n ");
@@ -409,10 +418,18 @@
*/
public void sendError(int sc) throws IOException
{
- if (sc==102)
- sendProcessing();
- else
- sendError(sc,null);
+ switch (sc)
+ {
+ case -1:
+ _connection.getEndPoint().close();
+ break;
+ case 102:
+ sendProcessing();
+ break;
+ default:
+ sendError(sc,null);
+ break;
+ }
}
/* ------------------------------------------------------------ */
@@ -445,49 +462,25 @@
{
StringBuilder buf = _connection.getRequest().getRootURL();
if (location.startsWith("/"))
- buf.append(location);
+ {
+ // absolute in context
+ location=URIUtil.canonicalPath(location);
+ }
else
{
+ // relative to request
String path=_connection.getRequest().getRequestURI();
String parent=(path.endsWith("/"))?path:URIUtil.parentPath(path);
- location=URIUtil.addPaths(parent,location);
- if(location==null)
- throw new IllegalStateException("path cannot be above root");
+ location=URIUtil.canonicalPath(URIUtil.addPaths(parent,location));
if (!location.startsWith("/"))
buf.append('/');
- buf.append(location);
}
-
+
+ if(location==null)
+ throw new IllegalStateException("path cannot be above root");
+ buf.append(location);
+
location=buf.toString();
- HttpURI uri = new HttpURI(location);
- String path=uri.getDecodedPath();
- String canonical=URIUtil.canonicalPath(path);
- if (canonical==null)
- throw new IllegalArgumentException();
- if (!canonical.equals(path))
- {
- buf = _connection.getRequest().getRootURL();
- buf.append(URIUtil.encodePath(canonical));
- String param=uri.getParam();
- if (param!=null)
- {
- buf.append(';');
- buf.append(param);
- }
- String query=uri.getQuery();
- if (query!=null)
- {
- buf.append('?');
- buf.append(query);
- }
- String fragment=uri.getFragment();
- if (fragment!=null)
- {
- buf.append('#');
- buf.append(fragment);
- }
- location=buf.toString();
- }
}
resetBuffer();
@@ -545,6 +538,14 @@
}
}
+
+ /* ------------------------------------------------------------ */
+ public Collection<String> getHeaderNames()
+ {
+ final HttpFields fields=_connection.getResponseFields();
+ return fields.getFieldNamesCollection();
+ }
+
/* ------------------------------------------------------------ */
/*
*/
@@ -556,12 +557,13 @@
/* ------------------------------------------------------------ */
/*
*/
- public Enumeration getHeaders(String name)
+ public Collection<String> getHeaders(String name)
{
- Enumeration e = _connection.getResponseFields().getValues(name);
- if (e==null)
- return Collections.enumeration(Collections.EMPTY_LIST);
- return e;
+ final HttpFields fields=_connection.getResponseFields();
+ Collection<String> i = fields.getValuesCollection(name);
+ if (i==null)
+ return Collections.EMPTY_LIST;
+ return i;
}
/* ------------------------------------------------------------ */
@@ -1032,6 +1034,8 @@
{
if (isCommitted() || getContentCount()>0)
throw new IllegalStateException("Committed or content written");
+ if (size <= 0)
+ size = __MIN_BUFFER_SIZE;
_connection.getGenerator().increaseContentBufferSize(size);
}
@@ -1106,7 +1110,7 @@
HttpFields response_fields=_connection.getResponseFields();
ArrayList<String> cookieValues = new ArrayList<String>(5);
- Enumeration vals = response_fields.getValues(HttpHeaders.SET_COOKIE);
+ Enumeration<String> vals = response_fields.getValues(HttpHeaders.SET_COOKIE);
while (vals.hasMoreElements())
cookieValues.add((String)vals.nextElement());
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java
index eb87512..e8be712 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java
@@ -22,6 +22,7 @@
import java.net.InetSocketAddress;
import java.util.Enumeration;
+import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -66,7 +67,7 @@
Server.class.getPackage().getImplementationVersion()!=null)
__version=Server.class.getPackage().getImplementationVersion();
else
- __version=System.getProperty("jetty.version","7.x.y-SNAPSHOT");
+ __version=System.getProperty("jetty.version","8.y.z-SNAPSHOT");
}
private final Container _container=new Container();
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ServletRequestHttpWrapper.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ServletRequestHttpWrapper.java
index 7bcd17a..cd6dce0 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/ServletRequestHttpWrapper.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ServletRequestHttpWrapper.java
@@ -19,14 +19,19 @@
package org.eclipse.jetty.server;
+import java.io.IOException;
import java.security.Principal;
+import java.util.Collection;
import java.util.Enumeration;
+import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestWrapper;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
+import javax.servlet.http.Part;
/* ------------------------------------------------------------ */
/** Class to tunnel a ServletRequest via a HttpServletRequest
@@ -163,5 +168,45 @@
return false;
}
+ /**
+ * @see javax.servlet.http.HttpServletRequest#authenticate(javax.servlet.http.HttpServletResponse)
+ */
+ public boolean authenticate(HttpServletResponse response) throws IOException, ServletException
+ {
+ return false;
+ }
+
+ /**
+ * @see javax.servlet.http.HttpServletRequest#getPart(java.lang.String)
+ */
+ public Part getPart(String name) throws IOException, ServletException
+ {
+ return null;
+ }
+
+ /**
+ * @see javax.servlet.http.HttpServletRequest#getParts()
+ */
+ public Collection<Part> getParts() throws IOException, ServletException
+ {
+ return null;
+ }
+
+ /**
+ * @see javax.servlet.http.HttpServletRequest#login(java.lang.String, java.lang.String)
+ */
+ public void login(String username, String password) throws ServletException
+ {
+
+ }
+
+ /**
+ * @see javax.servlet.http.HttpServletRequest#logout()
+ */
+ public void logout() throws ServletException
+ {
+
+ }
+
}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ServletResponseHttpWrapper.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ServletResponseHttpWrapper.java
index 7d5c37f..1748f36 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/ServletResponseHttpWrapper.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ServletResponseHttpWrapper.java
@@ -19,6 +19,7 @@
package org.eclipse.jetty.server;
import java.io.IOException;
+import java.util.Collection;
import javax.servlet.ServletResponse;
import javax.servlet.ServletResponseWrapper;
@@ -109,4 +110,36 @@
{
}
+ /**
+ * @see javax.servlet.http.HttpServletResponse#getHeader(java.lang.String)
+ */
+ public String getHeader(String name)
+ {
+ return null;
+ }
+
+ /**
+ * @see javax.servlet.http.HttpServletResponse#getHeaderNames()
+ */
+ public Collection<String> getHeaderNames()
+ {
+ return null;
+ }
+
+ /**
+ * @see javax.servlet.http.HttpServletResponse#getHeaders(java.lang.String)
+ */
+ public Collection<String> getHeaders(String name)
+ {
+ return null;
+ }
+
+ /**
+ * @see javax.servlet.http.HttpServletResponse#getStatus()
+ */
+ public int getStatus()
+ {
+ return 0;
+ }
+
}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SessionManager.java
index 1ccd468..b4f042e 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/SessionManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SessionManager.java
@@ -19,7 +19,10 @@
package org.eclipse.jetty.server;
import java.util.EventListener;
+import java.util.Set;
+import javax.servlet.SessionCookieConfig;
+import javax.servlet.SessionTrackingMode;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
@@ -109,11 +112,6 @@
*/
public HttpSession newHttpSession(HttpServletRequest request);
- /* ------------------------------------------------------------ */
- /**
- * @return true if session cookies should be secure
- */
- public boolean getSecureCookies();
/* ------------------------------------------------------------ */
/**
@@ -258,19 +256,6 @@
public void complete(HttpSession session);
/**
- * Sets the session cookie name.
- * @param cookieName the session cookie name
- * @see #getSessionCookie()
- */
- public void setSessionCookie(String cookieName);
-
- /**
- * @return the session cookie name, by default "JSESSIONID".
- * @see #setSessionCookie(String)
- */
- public String getSessionCookie();
-
- /**
* Sets the session id URL path parameter name.
*
* @param parameterName the URL path parameter name for session id URL rewriting (null or "none" for no rewriting).
@@ -293,50 +278,24 @@
public String getSessionIdPathParameterNamePrefix();
/**
- * Sets the domain to set on the session cookie
- * @param domain the domain to set on the session cookie
- * @see #getSessionDomain()
- */
- public void setSessionDomain(String domain);
-
- /**
- * @return the domain to set on the session cookie
- * @see #setSessionDomain(String)
- */
- public String getSessionDomain();
-
- /**
- * Sets the path to set on the session cookie
- * @param path the path to set on the session cookie
- * @see #getSessionPath()
- */
- public void setSessionPath(String path);
-
- /**
- * @return the path to set on the session cookie
- * @see #setSessionPath(String)
- */
- public String getSessionPath();
-
- /**
- * Sets the max age to set on the session cookie, in seconds
- * @param maxCookieAge the max age to set on the session cookie, in seconds
- * @see #getMaxCookieAge()
- */
- public void setMaxCookieAge(int maxCookieAge);
-
- /**
- * @return the max age to set on the session cookie, in seconds
- * @see #setMaxCookieAge(int)
- */
- public int getMaxCookieAge();
-
- /**
* @return whether the session management is handled via cookies.
*/
public boolean isUsingCookies();
/**
+ * @return whether the session management is handled via URLs.
+ */
+ public boolean isUsingURLs();
+
+ public Set<SessionTrackingMode> getDefaultSessionTrackingModes();
+
+ public Set<SessionTrackingMode> getEffectiveSessionTrackingModes();
+
+ public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes);
+
+ public SessionCookieConfig getSessionCookieConfig();
+
+ /**
* @return True if absolute URLs are check for remoteness before being session encoded.
*/
public boolean isCheckingRemoteSessionIdEncoding();
@@ -345,5 +304,4 @@
* @param remote True if absolute URLs are check for remoteness before being session encoded.
*/
public void setCheckingRemoteSessionIdEncoding(boolean remote);
-
}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java
index d235969..fd3874f 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java
@@ -24,6 +24,7 @@
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
+import java.security.AccessController;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -38,6 +39,7 @@
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
+import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
@@ -46,9 +48,16 @@
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletException;
+import javax.servlet.ServletRegistration;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
+import javax.servlet.SessionCookieConfig;
+import javax.servlet.SessionTrackingMode;
+import javax.servlet.Filter;
+import javax.servlet.FilterRegistration;
+import javax.servlet.FilterRegistration.Dynamic;
+import javax.servlet.descriptor.JspConfigDescriptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -57,7 +66,6 @@
import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.server.AbstractHttpConnection;
import org.eclipse.jetty.server.Dispatcher;
-import org.eclipse.jetty.server.DispatcherType;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HandlerContainer;
import org.eclipse.jetty.server.Request;
@@ -141,6 +149,7 @@
private Object _contextAttributeListeners;
private Object _requestListeners;
private Object _requestAttributeListeners;
+ private Object _durableListeners;
private Map<String, Object> _managedAttributes;
private String[] _protectedTargets;
private final CopyOnWriteArrayList<AliasCheck> _aliasChecks = new CopyOnWriteArrayList<ContextHandler.AliasCheck>();
@@ -583,8 +592,27 @@
*/
public void addEventListener(EventListener listener)
{
+ //Only listeners added before the context starts last through a stop/start cycle
+ if (!(isStarted() || isStarting()))
+ _durableListeners = LazyList.add(_durableListeners, listener);
+
setEventListeners((EventListener[])LazyList.addToArray(getEventListeners(),listener,EventListener.class));
}
+
+
+ /**
+ * Apply any necessary restrictions on a programmatically added
+ * listener.
+ *
+ * Superclasses should implement.
+ *
+ * @param listener
+ */
+ public void restrictEventListener (EventListener listener)
+ {
+ }
+
+
/* ------------------------------------------------------------ */
/**
@@ -743,14 +771,24 @@
ServletContextEvent event = new ServletContextEvent(_scontext);
for (int i = 0; i < LazyList.size(_contextListeners); i++)
{
- ((ServletContextListener)LazyList.get(_contextListeners,i)).contextInitialized(event);
+ callContextInitialized(((ServletContextListener)LazyList.get(_contextListeners, i)), event);
}
}
-
- LOG.info("started {}",this);
}
/* ------------------------------------------------------------ */
+ public void callContextInitialized (ServletContextListener l, ServletContextEvent e)
+ {
+ l.contextInitialized(e);
+ }
+
+ /* ------------------------------------------------------------ */
+ public void callContextDestroyed (ServletContextListener l, ServletContextEvent e)
+ {
+ l.contextDestroyed(e);
+ }
+
+ /* ------------------------------------------------------------ */
/*
* @see org.eclipse.thread.AbstractLifeCycle#doStop()
*/
@@ -785,6 +823,10 @@
((ServletContextListener)LazyList.get(_contextListeners,i)).contextDestroyed(event);
}
}
+
+ //remove all non-durable listeners
+ setEventListeners((EventListener[])LazyList.toArray(_durableListeners, EventListener.class));
+ _durableListeners = null;
if (_errorHandler != null)
_errorHandler.stop();
@@ -915,7 +957,7 @@
if (old_context != _scontext)
{
// check the target.
- if (DispatcherType.REQUEST.equals(dispatch) || DispatcherType.ASYNC.equals(dispatch))
+ if (DispatcherType.REQUEST.equals(dispatch) || DispatcherType.ASYNC.equals(dispatch) || (DispatcherType.ERROR.equals(dispatch) && baseRequest.getAsyncContinuation().isExpired()))
{
if (_compactPath)
target = URIUtil.compactPath(target);
@@ -1494,6 +1536,7 @@
_localeEncodingMap.put(locale,encoding);
}
+ /* ------------------------------------------------------------ */
public String getLocaleEncoding(String locale)
{
if (_localeEncodingMap == null)
@@ -1672,6 +1715,10 @@
*/
public class Context implements ServletContext
{
+ protected int _majorVersion = 3;
+ protected int _minorVersion = 0;
+ protected boolean _enabled = true; //whether or not the dynamic API is enabled for callers
+
/* ------------------------------------------------------------ */
protected Context()
{
@@ -1688,6 +1735,7 @@
/*
* @see javax.servlet.ServletContext#getContext(java.lang.String)
*/
+ @Override
public ServletContext getContext(String uripath)
{
List<ContextHandler> contexts = new ArrayList<ContextHandler>();
@@ -1773,15 +1821,18 @@
/*
* @see javax.servlet.ServletContext#getMajorVersion()
*/
+ @Override
public int getMajorVersion()
{
- return 2;
+ return 3;
}
+
/* ------------------------------------------------------------ */
/*
* @see javax.servlet.ServletContext#getMimeType(java.lang.String)
*/
+ @Override
public String getMimeType(String file)
{
if (_mimeTypes == null)
@@ -1796,15 +1847,17 @@
/*
* @see javax.servlet.ServletContext#getMinorVersion()
*/
+ @Override
public int getMinorVersion()
{
- return 5;
+ return 0;
}
/* ------------------------------------------------------------ */
/*
* @see javax.servlet.ServletContext#getNamedDispatcher(java.lang.String)
*/
+ @Override
public RequestDispatcher getNamedDispatcher(String name)
{
return null;
@@ -1814,6 +1867,7 @@
/*
* @see javax.servlet.ServletContext#getRequestDispatcher(java.lang.String)
*/
+ @Override
public RequestDispatcher getRequestDispatcher(String uriInContext)
{
if (uriInContext == null)
@@ -1831,13 +1885,14 @@
query = uriInContext.substring(q + 1);
uriInContext = uriInContext.substring(0,q);
}
- // if ((q = uriInContext.indexOf(';')) > 0)
- // uriInContext = uriInContext.substring(0,q);
String pathInContext = URIUtil.canonicalPath(URIUtil.decodePath(uriInContext));
- String uri = URIUtil.addPaths(getContextPath(),uriInContext);
- ContextHandler context = ContextHandler.this;
- return new Dispatcher(context,uri,pathInContext,query);
+ if (pathInContext!=null)
+ {
+ String uri = URIUtil.addPaths(getContextPath(),uriInContext);
+ ContextHandler context = ContextHandler.this;
+ return new Dispatcher(context,uri,pathInContext,query);
+ }
}
catch (Exception e)
{
@@ -1850,6 +1905,7 @@
/*
* @see javax.servlet.ServletContext#getRealPath(java.lang.String)
*/
+ @Override
public String getRealPath(String path)
{
if (path == null)
@@ -1878,6 +1934,7 @@
}
/* ------------------------------------------------------------ */
+ @Override
public URL getResource(String path) throws MalformedURLException
{
Resource resource = ContextHandler.this.getResource(path);
@@ -1890,6 +1947,7 @@
/*
* @see javax.servlet.ServletContext#getResourceAsStream(java.lang.String)
*/
+ @Override
public InputStream getResourceAsStream(String path)
{
try
@@ -1911,6 +1969,7 @@
/*
* @see javax.servlet.ServletContext#getResourcePaths(java.lang.String)
*/
+ @Override
public Set getResourcePaths(String path)
{
return ContextHandler.this.getResourcePaths(path);
@@ -1920,6 +1979,7 @@
/*
* @see javax.servlet.ServletContext#getServerInfo()
*/
+ @Override
public String getServerInfo()
{
return "jetty/" + Server.getVersion();
@@ -1929,6 +1989,8 @@
/*
* @see javax.servlet.ServletContext#getServlet(java.lang.String)
*/
+ @Override
+ @Deprecated
public Servlet getServlet(String name) throws ServletException
{
return null;
@@ -1939,6 +2001,8 @@
* @see javax.servlet.ServletContext#getServletNames()
*/
@SuppressWarnings("unchecked")
+ @Override
+ @Deprecated
public Enumeration getServletNames()
{
return Collections.enumeration(Collections.EMPTY_LIST);
@@ -1949,6 +2013,8 @@
* @see javax.servlet.ServletContext#getServlets()
*/
@SuppressWarnings("unchecked")
+ @Override
+ @Deprecated
public Enumeration getServlets()
{
return Collections.enumeration(Collections.EMPTY_LIST);
@@ -1958,6 +2024,7 @@
/*
* @see javax.servlet.ServletContext#log(java.lang.Exception, java.lang.String)
*/
+ @Override
public void log(Exception exception, String msg)
{
_logger.warn(msg,exception);
@@ -1967,6 +2034,7 @@
/*
* @see javax.servlet.ServletContext#log(java.lang.String)
*/
+ @Override
public void log(String msg)
{
_logger.info(msg);
@@ -1976,6 +2044,7 @@
/*
* @see javax.servlet.ServletContext#log(java.lang.String, java.lang.Throwable)
*/
+ @Override
public void log(String message, Throwable throwable)
{
_logger.warn(message,throwable);
@@ -1985,6 +2054,7 @@
/*
* @see javax.servlet.ServletContext#getInitParameter(java.lang.String)
*/
+ @Override
public String getInitParameter(String name)
{
return ContextHandler.this.getInitParameter(name);
@@ -1995,6 +2065,7 @@
* @see javax.servlet.ServletContext#getInitParameterNames()
*/
@SuppressWarnings("unchecked")
+ @Override
public Enumeration getInitParameterNames()
{
return ContextHandler.this.getInitParameterNames();
@@ -2004,6 +2075,7 @@
/*
* @see javax.servlet.ServletContext#getAttribute(java.lang.String)
*/
+ @Override
public synchronized Object getAttribute(String name)
{
Object o = ContextHandler.this.getAttribute(name);
@@ -2017,6 +2089,7 @@
* @see javax.servlet.ServletContext#getAttributeNames()
*/
@SuppressWarnings("unchecked")
+ @Override
public synchronized Enumeration getAttributeNames()
{
HashSet<String> set = new HashSet<String>();
@@ -2037,6 +2110,7 @@
/*
* @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object)
*/
+ @Override
public synchronized void setAttribute(String name, Object value)
{
checkManagedAttribute(name,value);
@@ -2069,6 +2143,7 @@
/*
* @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
*/
+ @Override
public synchronized void removeAttribute(String name)
{
checkManagedAttribute(name,null);
@@ -2098,6 +2173,7 @@
/*
* @see javax.servlet.ServletContext#getServletContextName()
*/
+ @Override
public String getServletContextName()
{
String name = ContextHandler.this.getDisplayName();
@@ -2107,6 +2183,7 @@
}
/* ------------------------------------------------------------ */
+ @Override
public String getContextPath()
{
if ((_contextPath != null) && _contextPath.equals(URIUtil.SLASH))
@@ -2123,6 +2200,7 @@
}
/* ------------------------------------------------------------ */
+ @Override
public boolean setInitParameter(String name, String value)
{
if (ContextHandler.this.getInitParameter(name) != null)
@@ -2131,6 +2209,243 @@
return true;
}
+ /* ------------------------------------------------------------ */
+ final private static String __unimplmented="Unimplemented - use org.eclipse.jetty.servlet.ServletContextHandler";
+
+ @Override
+ public Dynamic addFilter(String filterName, Class<? extends Filter> filterClass)
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public Dynamic addFilter(String filterName, Filter filter)
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public Dynamic addFilter(String filterName, String className)
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Class<? extends Servlet> servletClass)
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet)
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, String className)
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public <T extends Filter> T createFilter(Class<T> c) throws ServletException
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public <T extends Servlet> T createServlet(Class<T> c) throws ServletException
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public Set<SessionTrackingMode> getDefaultSessionTrackingModes()
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public Set<SessionTrackingMode> getEffectiveSessionTrackingModes()
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public FilterRegistration getFilterRegistration(String filterName)
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public Map<String, ? extends FilterRegistration> getFilterRegistrations()
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public ServletRegistration getServletRegistration(String servletName)
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public Map<String, ? extends ServletRegistration> getServletRegistrations()
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public SessionCookieConfig getSessionCookieConfig()
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ @Override
+ public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes)
+ {
+ LOG.warn(__unimplmented);
+ }
+
+ @Override
+ public void addListener(String className)
+ {
+ if (!_enabled)
+ throw new UnsupportedOperationException();
+
+ try
+ {
+ Class<? extends EventListener> clazz = _classLoader==null?Loader.loadClass(ContextHandler.class,className):_classLoader.loadClass(className);
+ addListener(clazz);
+ }
+ catch (ClassNotFoundException e)
+ {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ @Override
+ public <T extends EventListener> void addListener(T t)
+ {
+ if (!_enabled)
+ throw new UnsupportedOperationException();
+ ContextHandler.this.addEventListener(t);
+ ContextHandler.this.restrictEventListener(t);
+ }
+
+ @Override
+ public void addListener(Class<? extends EventListener> listenerClass)
+ {
+ if (!_enabled)
+ throw new UnsupportedOperationException();
+
+ try
+ {
+ EventListener e = createListener(listenerClass);
+ ContextHandler.this.addEventListener(e);
+ ContextHandler.this.restrictEventListener(e);
+ }
+ catch (ServletException e)
+ {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ @Override
+ public <T extends EventListener> T createListener(Class<T> clazz) throws ServletException
+ {
+ try
+ {
+ return clazz.newInstance();
+ }
+ catch (InstantiationException e)
+ {
+ throw new ServletException(e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new ServletException(e);
+ }
+ }
+
+ @Override
+ public ClassLoader getClassLoader()
+ {
+ AccessController.checkPermission(new RuntimePermission("getClassLoader"));
+ return _classLoader;
+ }
+
+ @Override
+ public int getEffectiveMajorVersion()
+ {
+ return _majorVersion;
+ }
+
+ @Override
+ public int getEffectiveMinorVersion()
+ {
+ return _minorVersion;
+ }
+
+ public void setEffectiveMajorVersion (int v)
+ {
+ _majorVersion = v;
+ }
+
+ public void setEffectiveMinorVersion (int v)
+ {
+ _minorVersion = v;
+ }
+
+ @Override
+ public JspConfigDescriptor getJspConfigDescriptor()
+ {
+ LOG.warn(__unimplmented);
+ return null;
+ }
+
+ public void setJspConfigDescriptor(JspConfigDescriptor d)
+ {
+
+ }
+
+ @Override
+ public void declareRoles(String... roleNames)
+ {
+ if (!isStarting())
+ throw new IllegalStateException ();
+ if (!_enabled)
+ throw new UnsupportedOperationException();
+
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setEnabled(boolean enabled)
+ {
+ _enabled = enabled;
+ }
+
+ public boolean isEnabled()
+ {
+ return _enabled;
+ }
}
private static class CLDump implements Dumpable
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java
index 571c708..3cb655f 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ErrorHandler.java
@@ -23,6 +23,7 @@
import java.io.StringWriter;
import java.io.Writer;
+import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -31,8 +32,11 @@
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.server.AbstractHttpConnection;
+import org.eclipse.jetty.server.Dispatcher;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.util.ByteArrayISO8859Writer;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */
/** Handler for Error pages
@@ -42,7 +46,10 @@
*
*/
public class ErrorHandler extends AbstractHandler
-{
+{
+ private static final Logger LOG = Log.getLogger(ErrorHandler.class);
+ public final static String ERROR_PAGE="org.eclipse.jetty.server.error_page";
+
boolean _showStacks=true;
boolean _showMessageInTitle=true;
String _cacheControl="must-revalidate,no-cache,no-store";
@@ -54,10 +61,43 @@
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{
AbstractHttpConnection connection = AbstractHttpConnection.getCurrentConnection();
- connection.getRequest().setHandled(true);
String method = request.getMethod();
if(!method.equals(HttpMethods.GET) && !method.equals(HttpMethods.POST) && !method.equals(HttpMethods.HEAD))
+ {
+ connection.getRequest().setHandled(true);
return;
+ }
+
+ if (this instanceof ErrorPageMapper)
+ {
+ String error_page=((ErrorPageMapper)this).getErrorPage(request);
+ if (error_page!=null && request.getServletContext()!=null)
+ {
+ String old_error_page=(String)request.getAttribute(ERROR_PAGE);
+ if (old_error_page==null || !old_error_page.equals(error_page))
+ {
+ request.setAttribute(ERROR_PAGE, error_page);
+
+ Dispatcher dispatcher = (Dispatcher) request.getServletContext().getRequestDispatcher(error_page);
+ try
+ {
+ if(dispatcher!=null)
+ {
+ dispatcher.error(request, response);
+ return;
+ }
+ LOG.warn("No error page "+error_page);
+ }
+ catch (ServletException e)
+ {
+ LOG.warn(Log.EXCEPTION, e);
+ return;
+ }
+ }
+ }
+ }
+
+ connection.getRequest().setHandled(true);
response.setContentType(MimeTypes.TEXT_HTML_8859_1);
if (_cacheControl!=null)
response.setHeader(HttpHeaders.CACHE_CONTROL, _cacheControl);
@@ -236,4 +276,10 @@
}
}
}
+
+ /* ------------------------------------------------------------ */
+ public interface ErrorPageMapper
+ {
+ String getErrorPage(HttpServletRequest request);
+ }
}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IPAccessHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IPAccessHandler.java
index e0fa96c..a6d6f12 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IPAccessHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IPAccessHandler.java
@@ -242,7 +242,7 @@
pathMap = new PathMap(true);
patternMap.put(addr,pathMap);
}
- if (path != null)
+ if (path != null && !"".equals(path))
pathMap.put(path,path);
if (deprecated)
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IdleTimeoutHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IdleTimeoutHandler.java
new file mode 100644
index 0000000..2e9c19c
--- /dev/null
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IdleTimeoutHandler.java
@@ -0,0 +1,138 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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 java.io.IOException;
+
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.server.AbstractHttpConnection;
+import org.eclipse.jetty.server.Request;
+
+/**
+ * Handler to adjust the idle timeout of requests while dispatched.
+ *
+ * <p>Can be applied in jetty.xml with
+ * <pre>
+ * <Get id='handler' name='Handler'/>
+ * <Set name='Handler'>
+ * <New id='idleTimeoutHandler' class='org.eclipse.jetty.server.handler.IdleTimeoutHandler'>
+ * <Set name='Handler'><Ref id='handler'/></Set>
+ * <Set name='IdleTimeoutMs'>5000</Set>
+ * </New>
+ * </Set>
+ * </pre>
+ */
+public class IdleTimeoutHandler extends HandlerWrapper
+{
+ private int _idleTimeoutMs = 1000;
+ private boolean _applyToAsync = false;
+
+
+ public boolean isApplyToAsync()
+ {
+ return _applyToAsync;
+ }
+
+ /**
+ * Should the adjusted idle time be maintained for asynchronous requests
+ * @param applyToAsync true if alternate idle timeout is applied to asynchronous requests
+ */
+ public void setApplyToAsync(boolean applyToAsync)
+ {
+ _applyToAsync = applyToAsync;
+ }
+
+ public long getIdleTimeoutMs()
+ {
+ return _idleTimeoutMs;
+ }
+
+ /**
+ * @param idleTimeoutMs The idle timeout in MS to apply while dispatched or async
+ */
+ public void setIdleTimeoutMs(int _idleTimeoutMs)
+ {
+ this._idleTimeoutMs = _idleTimeoutMs;
+ }
+
+
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ AbstractHttpConnection connection = AbstractHttpConnection.getCurrentConnection();
+ final EndPoint endp = connection==null?null:connection.getEndPoint();
+
+ final int idle_timeout;
+ if (endp==null)
+ idle_timeout=-1;
+ else
+ {
+ idle_timeout=endp.getMaxIdleTime();
+ endp.setMaxIdleTime(_idleTimeoutMs);
+ }
+
+ try
+ {
+ super.handle(target,baseRequest,request,response);
+ }
+ finally
+ {
+ if (endp!=null)
+ {
+ if (_applyToAsync && request.isAsyncStarted())
+ {
+ request.getAsyncContext().addListener(new AsyncListener()
+ {
+ @Override
+ public void onTimeout(AsyncEvent event) throws IOException
+ {
+ }
+
+ @Override
+ public void onStartAsync(AsyncEvent event) throws IOException
+ {
+ }
+
+ @Override
+ public void onError(AsyncEvent event) throws IOException
+ {
+ endp.setMaxIdleTime(idle_timeout);
+ }
+
+ @Override
+ public void onComplete(AsyncEvent event) throws IOException
+ {
+ endp.setMaxIdleTime(idle_timeout);
+ }
+ });
+ }
+ else
+ {
+ endp.setMaxIdleTime(idle_timeout);
+ }
+ }
+ }
+ }
+}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/RequestLogHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/RequestLogHandler.java
index cfca629..5b7d525 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/RequestLogHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/RequestLogHandler.java
@@ -20,6 +20,7 @@
import java.io.IOException;
+import javax.servlet.DispatcherType;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -27,7 +28,6 @@
import org.eclipse.jetty.continuation.Continuation;
import org.eclipse.jetty.continuation.ContinuationListener;
import org.eclipse.jetty.server.AsyncContinuation;
-import org.eclipse.jetty.server.DispatcherType;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.RequestLog;
import org.eclipse.jetty.server.Response;
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java
index b616300..768bba1 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSession.java
@@ -211,7 +211,6 @@
/* ------------------------------------------------------------- */
public int getMaxInactiveInterval()
{
- checkValid();
return (int)(_maxIdleMs/1000);
}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java
index 6415d20..e7ee052 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java
@@ -20,15 +20,20 @@
import static java.lang.Math.round;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.EventListener;
+import java.util.HashSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.servlet.ServletRequest;
+import javax.servlet.SessionCookieConfig;
+import javax.servlet.SessionTrackingMode;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
@@ -63,6 +68,12 @@
public abstract class AbstractSessionManager extends AbstractLifeCycle implements SessionManager
{
final static Logger __log = SessionHandler.LOG;
+
+ public Set<SessionTrackingMode> __defaultSessionTrackingModes =
+ Collections.unmodifiableSet(
+ new HashSet<SessionTrackingMode>(
+ Arrays.asList(new SessionTrackingMode[]{SessionTrackingMode.COOKIE,SessionTrackingMode.URL})));
+
public final static String SESSION_KNOWN_ONLY_TO_AUTHENTICATED="org.eclipse.jetty.security.sessionKnownOnlytoAuthenticated";
/* ------------------------------------------------------------ */
@@ -92,6 +103,8 @@
protected boolean _httpOnly=false;
protected SessionIdManager _sessionIdManager;
protected boolean _secureCookies=false;
+ protected boolean _secureRequestOnly=true;
+
protected final List<HttpSessionAttributeListener> _sessionAttributeListeners = new CopyOnWriteArrayList<HttpSessionAttributeListener>();
protected final List<HttpSessionListener> _sessionListeners= new CopyOnWriteArrayList<HttpSessionListener>();
@@ -106,7 +119,12 @@
protected int _refreshCookieAge;
protected boolean _nodeIdInSessionId;
protected boolean _checkingRemoteSessionIdEncoding;
+ protected String _sessionComment;
+ public Set<SessionTrackingMode> _sessionTrackingModes;
+
+ private boolean _usingURLs;
+
protected final CounterStatistic _sessionsStats = new CounterStatistic();
protected final SampleStatistic _sessionTimeStats = new SampleStatistic();
@@ -135,6 +153,7 @@
/* ------------------------------------------------------------ */
public AbstractSessionManager()
{
+ setSessionTrackingModes(__defaultSessionTrackingModes);
}
/* ------------------------------------------------------------ */
@@ -149,6 +168,16 @@
return _context.getContextHandler();
}
+ public String getSessionPath()
+ {
+ return _sessionPath;
+ }
+
+ public int getMaxCookieAge()
+ {
+ return _maxCookieAge;
+ }
+
/* ------------------------------------------------------------ */
public HttpCookie access(HttpSession session,boolean secure)
{
@@ -156,14 +185,14 @@
AbstractSession s = ((SessionIf)session).getSession();
- if (s.access(now))
- {
+ if (s.access(now))
+ {
// Do we need to refresh the cookie?
if (isUsingCookies() &&
- (s.isIdChanged() ||
- (getMaxCookieAge()>0 && getRefreshCookieAge()>0 && ((now-s.getCookieSetTime())/1000>getRefreshCookieAge()))
- )
- )
+ (s.isIdChanged() ||
+ (getSessionCookieConfig().getMaxAge()>0 && getRefreshCookieAge()>0 && ((now-s.getCookieSetTime())/1000>getRefreshCookieAge()))
+ )
+ )
{
HttpCookie cookie=getSessionCookie(session,_context==null?"/":(_context.getContextPath()),secure);
s.cookieSet();
@@ -305,16 +334,12 @@
return _sessionIdManager;
}
- /* ------------------------------------------------------------ */
- public int getMaxCookieAge()
- {
- return _maxCookieAge;
- }
/* ------------------------------------------------------------ */
/**
* @return seconds
*/
+ @Override
public int getMaxInactiveInterval()
{
return _dftMaxIdleSecs;
@@ -377,13 +402,38 @@
/* ------------------------------------------------------------ */
/**
- * @return Returns the secureCookies.
+ * @return same as SessionCookieConfig.getSecure(). If true, session
+ * cookies are ALWAYS marked as secure. If false, a session cookie is
+ * ONLY marked as secure if _secureRequestOnly == true and it is a HTTPS request.
*/
public boolean getSecureCookies()
{
return _secureCookies;
}
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return true if session cookie is to be marked as secure only on HTTPS requests
+ */
+ public boolean isSecureRequestOnly()
+ {
+ return _secureRequestOnly;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return if true, session cookie will be marked as secure only iff
+ * HTTPS request. Can be overridden by setting SessionCookieConfig.setSecure(true),
+ * in which case the session cookie will be marked as secure on both HTTPS and HTTP.
+ */
+ public void setSecureRequestOnly(boolean secureRequestOnly)
+ {
+ _secureRequestOnly = secureRequestOnly;
+ }
+
+
/* ------------------------------------------------------------ */
public String getSessionCookie()
{
@@ -391,6 +441,31 @@
}
/* ------------------------------------------------------------ */
+ /**
+ * A sessioncookie is marked as secure IFF any of the following conditions are true:
+ * <ol>
+ * <li>SessionCookieConfig.setSecure == true</li>
+ * <li>SessionCookieConfig.setSecure == false && _secureRequestOnly==true && request is HTTPS</li>
+ * </ol>
+ * According to SessionCookieConfig javadoc, case 1 can be used when:
+ * "... even though the request that initiated the session came over HTTP,
+ * is to support a topology where the web container is front-ended by an
+ * SSL offloading load balancer. In this case, the traffic between the client
+ * and the load balancer will be over HTTPS, whereas the traffic between the
+ * load balancer and the web container will be over HTTP."
+ *
+ * For case 2, you can use _secureRequestOnly to determine if you want the
+ * Servlet Spec 3.0 default behaviour when SessionCookieConfig.setSecure==false,
+ * which is:
+ * "they shall be marked as secure only if the request that initiated the
+ * corresponding session was also secure"
+ *
+ * The default for _secureRequestOnly is true, which gives the above behaviour. If
+ * you set it to false, then a session cookie is NEVER marked as secure, even if
+ * the initiating request was secure.
+ *
+ * @see org.eclipse.jetty.server.SessionManager#getSessionCookie(javax.servlet.http.HttpSession, java.lang.String, boolean)
+ */
public HttpCookie getSessionCookie(HttpSession session, String contextPath, boolean requestIsSecure)
{
if (isUsingCookies())
@@ -398,15 +473,32 @@
String sessionPath = (_sessionPath==null) ? contextPath : _sessionPath;
sessionPath = (sessionPath==null||sessionPath.length()==0) ? "/" : sessionPath;
String id = getNodeId(session);
- HttpCookie cookie=new HttpCookie(
- _sessionCookie,
- id,
- _sessionDomain,
- sessionPath,
- getMaxCookieAge(),
- getHttpOnly(),
- requestIsSecure&&getSecureCookies());
-
+ HttpCookie cookie = null;
+ if (_sessionComment == null)
+ {
+ cookie = new HttpCookie(
+ _sessionCookie,
+ id,
+ _sessionDomain,
+ sessionPath,
+ _cookieConfig.getMaxAge(),
+ _cookieConfig.isHttpOnly(),
+ _cookieConfig.isSecure() || (isSecureRequestOnly() && requestIsSecure));
+ }
+ else
+ {
+ cookie = new HttpCookie(
+ _sessionCookie,
+ id,
+ _sessionDomain,
+ sessionPath,
+ _cookieConfig.getMaxAge(),
+ _cookieConfig.isHttpOnly(),
+ _cookieConfig.isSecure() || (isSecureRequestOnly() && requestIsSecure),
+ _sessionComment,
+ 1);
+ }
+
return cookie;
}
return null;
@@ -436,11 +528,7 @@
throw new UnsupportedOperationException();
}
- /* ------------------------------------------------------------ */
- public String getSessionPath()
- {
- return _sessionPath;
- }
+
/* ------------------------------------------------------------ */
public int getSessions()
@@ -560,15 +648,7 @@
_sessionIdManager=metaManager;
}
- /* ------------------------------------------------------------ */
- public void setMaxCookieAge(int maxCookieAgeInSeconds)
- {
- _maxCookieAge=maxCookieAgeInSeconds;
- if (_maxCookieAge>0 && _refreshCookieAge==0)
- _refreshCookieAge=_maxCookieAge/3;
-
- }
/* ------------------------------------------------------------ */
/**
@@ -579,15 +659,6 @@
_dftMaxIdleSecs=seconds;
}
- /* ------------------------------------------------------------ */
- /**
- * @deprecated use {@link #setSessionIdManager(SessionIdManager)}
- */
- @Deprecated
- public void setMetaManager(SessionIdManager metaManager)
- {
- setSessionIdManager(metaManager);
- }
/* ------------------------------------------------------------ */
public void setRefreshCookieAge(int ageInSeconds)
@@ -596,34 +667,13 @@
}
- /* ------------------------------------------------------------ */
- /**
- * Set if the session manager should use SecureCookies.
- * A secure cookie will only be sent by a browser on a secure (https) connection to
- * avoid the concern of cookies being intercepted on non secure channels.
- * For the cookie to be issued as secure, the {@link ServletRequest#isSecure()} method must return true.
- * If SSL offload is used, then the {@link AbstractConnector#customize(org.eclipse.jetty.io.EndPoint, Request)}
- * method can be used to force the request to be https, or the {@link AbstractConnector#setForwarded(boolean)}
- * can be set to true, so that the X-Forwarded-Proto header is respected.
- * <p>
- * If secure session cookies are used, then a session may not be shared between http and https requests.
- *
- * @param secureCookies If true, use secure cookies.
- */
- public void setSecureCookies(boolean secureCookies)
- {
- _secureCookies=secureCookies;
- }
public void setSessionCookie(String cookieName)
{
_sessionCookie=cookieName;
}
- public void setSessionDomain(String domain)
- {
- _sessionDomain=domain;
- }
+
/* ------------------------------------------------------------ */
/**
@@ -635,15 +685,7 @@
_sessionHandler=sessionHandler;
}
- /* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.SessionManager#setSessionPath(java.lang.String)
- */
- public void setSessionPath(String path)
- {
- _sessionPath=path;
- }
-
+
/* ------------------------------------------------------------ */
public void setSessionIdPathParameterName(String param)
{
@@ -781,6 +823,132 @@
}
/* ------------------------------------------------------------ */
+ public Set<SessionTrackingMode> getDefaultSessionTrackingModes()
+ {
+ return __defaultSessionTrackingModes;
+ }
+
+ /* ------------------------------------------------------------ */
+ public Set<SessionTrackingMode> getEffectiveSessionTrackingModes()
+ {
+ return Collections.unmodifiableSet(_sessionTrackingModes);
+ }
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes)
+ {
+ _sessionTrackingModes=new HashSet<SessionTrackingMode>(sessionTrackingModes);
+ _usingCookies=_sessionTrackingModes.contains(SessionTrackingMode.COOKIE);
+ _usingURLs=_sessionTrackingModes.contains(SessionTrackingMode.URL);
+ }
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public boolean isUsingURLs()
+ {
+ return _usingURLs;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ public SessionCookieConfig getSessionCookieConfig()
+ {
+ return _cookieConfig;
+ }
+
+ /* ------------------------------------------------------------ */
+ private SessionCookieConfig _cookieConfig =
+ new SessionCookieConfig()
+ {
+ @Override
+ public String getComment()
+ {
+ return _sessionComment;
+ }
+
+ @Override
+ public String getDomain()
+ {
+ return _sessionDomain;
+ }
+
+ @Override
+ public int getMaxAge()
+ {
+ return _maxCookieAge;
+ }
+
+ @Override
+ public String getName()
+ {
+ return _sessionCookie;
+ }
+
+ @Override
+ public String getPath()
+ {
+ return _sessionPath;
+ }
+
+ @Override
+ public boolean isHttpOnly()
+ {
+ return _httpOnly;
+ }
+
+ @Override
+ public boolean isSecure()
+ {
+ return _secureCookies;
+ }
+
+ @Override
+ public void setComment(String comment)
+ {
+ _sessionComment = comment;
+ }
+
+ @Override
+ public void setDomain(String domain)
+ {
+ _sessionDomain=domain;
+ }
+
+ @Override
+ public void setHttpOnly(boolean httpOnly)
+ {
+ _httpOnly=httpOnly;
+ }
+
+ @Override
+ public void setMaxAge(int maxAge)
+ {
+ _maxCookieAge=maxAge;
+ }
+
+ @Override
+ public void setName(String name)
+ {
+ _sessionCookie=name;
+ }
+
+ @Override
+ public void setPath(String path)
+ {
+ _sessionPath=path;
+ }
+
+ @Override
+ public void setSecure(boolean secure)
+ {
+ _secureCookies=secure;
+ }
+
+ };
+
+
+ /* ------------------------------------------------------------ */
/**
* @return total amount of time all sessions remained valid
*/
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java
index 3e29bd7..0a9e0c0 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashSessionManager.java
@@ -28,6 +28,7 @@
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
+import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java
index aab7bba..f8d6dfb 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java
@@ -634,10 +634,21 @@
if (!(index1Exists && index2Exists))
{
Statement statement = connection.createStatement();
- if (!index1Exists)
- statement.executeUpdate("create index "+index1+" on "+_sessionTable+" (expiryTime)");
- if (!index2Exists)
- statement.executeUpdate("create index "+index2+" on "+_sessionTable+" (sessionId, contextPath)");
+ try
+ {
+ if (!index1Exists)
+ statement.executeUpdate("create index "+index1+" on "+_sessionTable+" (expiryTime)");
+ if (!index2Exists)
+ statement.executeUpdate("create index "+index2+" on "+_sessionTable+" (sessionId, contextPath)");
+ }
+ finally
+ {
+ if (statement!=null)
+ {
+ try { statement.close(); }
+ catch(Exception e) { LOG.warn(e); }
+ }
+ }
}
//set up some strings representing the statements for session manipulation
@@ -676,23 +687,37 @@
throws SQLException
{
Connection connection = null;
+ PreparedStatement statement = null;
+ PreparedStatement query = null;
try
{
connection = getConnection();
connection.setAutoCommit(true);
- PreparedStatement query = connection.prepareStatement(_queryId);
+ query = connection.prepareStatement(_queryId);
query.setString(1, id);
ResultSet result = query.executeQuery();
//only insert the id if it isn't in the db already
if (!result.next())
{
- PreparedStatement statement = connection.prepareStatement(_insertId);
+ statement = connection.prepareStatement(_insertId);
statement.setString(1, id);
statement.executeUpdate();
}
}
finally
{
+ if (query!=null)
+ {
+ try { query.close(); }
+ catch(Exception e) { LOG.warn(e); }
+ }
+
+ if (statement!=null)
+ {
+ try { statement.close(); }
+ catch(Exception e) { LOG.warn(e); }
+ }
+
if (connection != null)
connection.close();
}
@@ -708,16 +733,23 @@
throws SQLException
{
Connection connection = null;
+ PreparedStatement statement = null;
try
{
connection = getConnection();
connection.setAutoCommit(true);
- PreparedStatement statement = connection.prepareStatement(_deleteId);
+ statement = connection.prepareStatement(_deleteId);
statement.setString(1, id);
statement.executeUpdate();
}
finally
{
+ if (statement!=null)
+ {
+ try { statement.close(); }
+ catch(Exception e) { LOG.warn(e); }
+ }
+
if (connection != null)
connection.close();
}
@@ -735,17 +767,24 @@
throws SQLException
{
Connection connection = null;
+ PreparedStatement statement = null;
try
{
connection = getConnection();
connection.setAutoCommit(true);
- PreparedStatement statement = connection.prepareStatement(_queryId);
+ statement = connection.prepareStatement(_queryId);
statement.setString(1, id);
ResultSet result = statement.executeQuery();
return result.next();
}
finally
{
+ if (statement!=null)
+ {
+ try { statement.close(); }
+ catch(Exception e) { LOG.warn(e); }
+ }
+
if (connection != null)
connection.close();
}
@@ -765,6 +804,7 @@
private void scavenge ()
{
Connection connection = null;
+ PreparedStatement statement = null;
List<String> expiredSessionIds = new ArrayList<String>();
try
{
@@ -775,7 +815,7 @@
connection = getConnection();
connection.setAutoCommit(true);
//"select sessionId from JettySessions where expiryTime > (lastScavengeTime - scanInterval) and expiryTime < lastScavengeTime";
- PreparedStatement statement = connection.prepareStatement(_selectBoundedExpiredSessions);
+ statement = connection.prepareStatement(_selectBoundedExpiredSessions);
long lowerBound = (_lastScavengeTime - _scavengeIntervalMs);
long upperBound = _lastScavengeTime;
if (LOG.isDebugEnabled())
@@ -812,10 +852,21 @@
if (upperBound > 0)
{
if (LOG.isDebugEnabled()) LOG.debug("Deleting old expired sessions expired before "+upperBound);
- statement = connection.prepareStatement(_deleteOldExpiredSessions);
- statement.setLong(1, upperBound);
- int rows = statement.executeUpdate();
- if (LOG.isDebugEnabled()) LOG.debug("Deleted "+rows+" rows of old sessions expired before "+upperBound);
+ try
+ {
+ statement = connection.prepareStatement(_deleteOldExpiredSessions);
+ statement.setLong(1, upperBound);
+ int rows = statement.executeUpdate();
+ if (LOG.isDebugEnabled()) LOG.debug("Deleted "+rows+" rows of old sessions expired before "+upperBound);
+ }
+ finally
+ {
+ if (statement!=null)
+ {
+ try { statement.close(); }
+ catch(Exception e) { LOG.warn(e); }
+ }
+ }
}
}
}
@@ -849,17 +900,19 @@
* @throws Exception
*/
private void cleanExpiredSessions ()
- throws Exception
{
Connection connection = null;
+ PreparedStatement statement = null;
+ Statement sessionsTableStatement = null;
+ Statement sessionIdsTableStatement = null;
List<String> expiredSessionIds = new ArrayList<String>();
try
{
connection = getConnection();
- connection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
+ connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
connection.setAutoCommit(false);
- PreparedStatement statement = connection.prepareStatement(_selectExpiredSessions);
+ statement = connection.prepareStatement(_selectExpiredSessions);
long now = System.currentTimeMillis();
if (LOG.isDebugEnabled()) LOG.debug ("Searching for sessions expired before {}", now);
@@ -872,8 +925,8 @@
if (LOG.isDebugEnabled()) LOG.debug ("Found expired sessionId={}", sessionId);
}
- Statement sessionsTableStatement = null;
- Statement sessionIdsTableStatement = null;
+ sessionsTableStatement = null;
+ sessionIdsTableStatement = null;
if (!expiredSessionIds.isEmpty())
{
@@ -892,11 +945,35 @@
catch (Exception e)
{
if (connection != null)
- connection.rollback();
- throw e;
+ {
+ try
+ {
+ LOG.warn("Rolling back clean of expired sessions", e);
+ connection.rollback();
+ }
+ catch (Exception x) { LOG.warn("Rollback of expired sessions failed", x);}
+ }
}
finally
{
+ if (sessionIdsTableStatement!=null)
+ {
+ try { sessionIdsTableStatement.close(); }
+ catch(Exception e) { LOG.warn(e); }
+ }
+
+ if (sessionsTableStatement!=null)
+ {
+ try { sessionsTableStatement.close(); }
+ catch(Exception e) { LOG.warn(e); }
+ }
+
+ if (statement!=null)
+ {
+ try { statement.close(); }
+ catch(Exception e) { LOG.warn(e); }
+ }
+
try
{
if (connection != null)
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java
index 61ba4fa..fcd214b 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java
@@ -34,9 +34,11 @@
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
+import javax.servlet.SessionTrackingMode;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
@@ -532,7 +534,11 @@
}
else
+ {
+ //the session loaded from the db and the one in memory are the same, so keep using the one in memory
+ session = memSession;
LOG.debug("getSession({}): Session not stale {}", idInCluster,session);
+ }
}
else
{
@@ -841,6 +847,12 @@
}
finally
{
+ if (statement!=null)
+ {
+ try { statement.close(); }
+ catch(Exception e) { LOG.warn(e); }
+ }
+
if (connection!=null)
{
try { connection.close();}
@@ -918,6 +930,12 @@
}
finally
{
+ if (statement!=null)
+ {
+ try { statement.close(); }
+ catch(Exception e) { LOG.warn(e); }
+ }
+
if (connection!=null)
connection.close();
}
@@ -965,6 +983,12 @@
}
finally
{
+ if (statement!=null)
+ {
+ try { statement.close(); }
+ catch(Exception e) { LOG.warn(e); }
+ }
+
if (connection!=null)
connection.close();
}
@@ -996,6 +1020,12 @@
}
finally
{
+ if (statement!=null)
+ {
+ try { statement.close(); }
+ catch(Exception e) { LOG.warn(e); }
+ }
+
if (connection!=null)
connection.close();
}
@@ -1031,6 +1061,12 @@
}
finally
{
+ if (statement!=null)
+ {
+ try { statement.close(); }
+ catch(Exception e) { LOG.warn(e); }
+ }
+
if (connection!=null)
connection.close();
}
@@ -1062,6 +1098,12 @@
}
finally
{
+ if (statement!=null)
+ {
+ try { statement.close(); }
+ catch(Exception e) { LOG.warn(e); }
+ }
+
if (connection!=null)
connection.close();
}
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java
index 76d780a..8bd2f95 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java
@@ -19,15 +19,17 @@
package org.eclipse.jetty.server.session;
import java.io.IOException;
+import java.util.EnumSet;
import java.util.EventListener;
+import javax.servlet.DispatcherType;
import javax.servlet.ServletException;
+import javax.servlet.SessionTrackingMode;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.eclipse.jetty.http.HttpCookie;
-import org.eclipse.jetty.server.DispatcherType;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.SessionManager;
@@ -43,6 +45,8 @@
{
final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
+ public final static EnumSet<SessionTrackingMode> DEFAULT_TRACKING = EnumSet.of(SessionTrackingMode.COOKIE,SessionTrackingMode.URL);
+
/* -------------------------------------------------------------- */
private SessionManager _sessionManager;
@@ -260,9 +264,10 @@
Cookie[] cookies = request.getCookies();
if (cookies != null && cookies.length > 0)
{
+ final String sessionCookie=sessionManager.getSessionCookieConfig().getName();
for (int i = 0; i < cookies.length; i++)
{
- if (sessionManager.getSessionCookie().equalsIgnoreCase(cookies[i].getName()))
+ if (sessionCookie.equalsIgnoreCase(cookies[i].getName()))
{
requested_session_id = cookies[i].getValue();
requested_session_id_from_cookie = true;
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncStressTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncStressTest.java
index 2d55cfa..808d04a 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncStressTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncStressTest.java
@@ -27,7 +27,10 @@
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
-
+import javax.servlet.AsyncContext;
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
+import javax.servlet.DispatcherType;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -226,10 +229,9 @@
if (suspend_for>=0)
{
final AsyncContext asyncContext = baseRequest.startAsync();
- asyncContext.addContinuationListener(__asyncListener);
+ asyncContext.addListener(__asyncListener);
if (suspend_for>0)
asyncContext.setTimeout(suspend_for);
-
if (complete_after>0)
{
TimerTask complete = new TimerTask()
@@ -325,18 +327,31 @@
}
}
}
-
-
- private static ContinuationListener __asyncListener = new ContinuationListener()
+
+
+ private static AsyncListener __asyncListener = new AsyncListener()
{
- public void onComplete(Continuation continuation)
+ @Override
+ public void onComplete(AsyncEvent event) throws IOException
{
}
- public void onTimeout(Continuation continuation)
+ @Override
+ public void onTimeout(AsyncEvent event) throws IOException
{
- continuation.setAttribute("TIMEOUT",Boolean.TRUE);
- continuation.resume();
+ event.getSuppliedRequest().setAttribute("TIMEOUT",Boolean.TRUE);
+ event.getSuppliedRequest().getAsyncContext().dispatch();
+ }
+
+ @Override
+ public void onError(AsyncEvent event) throws IOException
+ {
+
+ }
+
+ @Override
+ public void onStartAsync(AsyncEvent event) throws IOException
+ {
}
};
}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java
index d41238c..603f533 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java
@@ -128,6 +128,66 @@
checkContains(response,offset,"pathInfo=/");
}
+ @Test
+ public void testBadNoPath() throws Exception
+ {
+ String response=connector.getResponses("GET http://localhost:80/../cheat HTTP/1.1\n"+
+ "Host: localhost:80\n"+
+ "\n");
+ int offset=0;
+ offset = checkContains(response,offset,"HTTP/1.1 400");
+ }
+
+ @Test
+ public void testOKPathDotDotPath() throws Exception
+ {
+ String response=connector.getResponses("GET /ooops/../path HTTP/1.0\nHost: localhost:80\n\n");
+ checkContains(response,0,"HTTP/1.1 200 OK");
+ checkContains(response,0,"pathInfo=/path");
+ }
+
+ @Test
+ public void testBadPathDotDotPath() throws Exception
+ {
+ String response=connector.getResponses("GET /ooops/../../path HTTP/1.0\nHost: localhost:80\n\n");
+ checkContains(response,0,"HTTP/1.1 400 Bad Request");
+ }
+
+ @Test
+ public void testOKPathEncodedDotDotPath() throws Exception
+ {
+ String response=connector.getResponses("GET /ooops/%2e%2e/path HTTP/1.0\nHost: localhost:80\n\n");
+ checkContains(response,0,"HTTP/1.1 200 OK");
+ checkContains(response,0,"pathInfo=/path");
+ }
+
+ @Test
+ public void testBadPathEncodedDotDotPath() throws Exception
+ {
+ String response=connector.getResponses("GET /ooops/%2e%2e/%2e%2e/path HTTP/1.0\nHost: localhost:80\n\n");
+ checkContains(response,0,"HTTP/1.1 400 Bad Request");
+ }
+
+ @Test
+ public void testBadDotDotPath() throws Exception
+ {
+ String response=connector.getResponses("GET ../path HTTP/1.0\nHost: localhost:80\n\n");
+ checkContains(response,0,"HTTP/1.1 400 Bad Request");
+ }
+
+ @Test
+ public void testBadSlashDotDotPath() throws Exception
+ {
+ String response=connector.getResponses("GET /../path HTTP/1.0\nHost: localhost:80\n\n");
+ checkContains(response,0,"HTTP/1.1 400 Bad Request");
+ }
+
+ @Test
+ public void testEncodedBadDotDotPath() throws Exception
+ {
+ String response=connector.getResponses("GET %2e%2e/path HTTP/1.0\nHost: localhost:80\n\n");
+ checkContains(response,0,"HTTP/1.1 400 Bad Request");
+ }
@Test
public void testEmpty() throws Exception
@@ -441,6 +501,7 @@
request.append("\015\012");
response = connector.getResponses(request.toString());
+ System.err.println(response);
checkContains(response, offset, "HTTP/1.1 413");
}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java
index aa54371..4fabe95 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java
@@ -18,6 +18,27 @@
package org.eclipse.jetty.server;
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Random;
+import java.util.concurrent.Exchanger;
+import java.util.concurrent.atomic.AtomicBoolean;
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.server.handler.AbstractHandler;
@@ -30,23 +51,14 @@
import org.junit.Test;
import org.junit.matchers.JUnitMatchers;
-import javax.servlet.ServletException;
-import javax.servlet.ServletInputStream;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.*;
-import java.net.Socket;
-import java.net.SocketException;
-import java.net.URL;
-import java.util.Arrays;
-import java.util.Random;
-import java.util.concurrent.Exchanger;
-import java.util.concurrent.atomic.AtomicBoolean;
-
+import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
/**
*
@@ -1427,6 +1439,72 @@
}
}
+
+ @Test
+ public void testWriteBodyAfterNoBodyREsponse() throws Exception
+ {
+ configureServer(new WriteBodyAfterNoBodyResponseHandler());
+ Socket client=newSocket(HOST,_connector.getLocalPort());
+ final OutputStream out=client.getOutputStream();
+
+ out.write("GET / HTTP/1.1\r\nHost: test\r\n\r\n".getBytes());
+ out.write("GET / HTTP/1.1\r\nHost: test\r\nConnection: close\r\n\r\n".getBytes());
+ out.flush();
+
+
+ BufferedReader in =new BufferedReader(new InputStreamReader(client.getInputStream()));
+
+ String line=in.readLine();
+ assertThat(line, containsString(" 304 "));
+ while (true)
+ {
+ line=in.readLine();
+ if (line==null)
+ throw new EOFException();
+ if (line.length()==0)
+ break;
+
+ assertThat(line, not(containsString("Content-Length")));
+ assertThat(line, not(containsString("Content-Type")));
+ assertThat(line, not(containsString("Transfer-Encoding")));
+ }
+
+ line=in.readLine();
+ assertThat(line, containsString(" 304 "));
+ while (true)
+ {
+ line=in.readLine();
+ if (line==null)
+ throw new EOFException();
+ if (line.length()==0)
+ break;
+
+ assertThat(line, not(containsString("Content-Length")));
+ assertThat(line, not(containsString("Content-Type")));
+ assertThat(line, not(containsString("Transfer-Encoding")));
+ }
+
+ do
+ {
+ line=in.readLine();
+ }
+ while (line!=null);
+
+ }
+
+ private class WriteBodyAfterNoBodyResponseHandler extends AbstractHandler
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ baseRequest.setHandled(true);
+ response.setStatus(304);
+ response.getOutputStream().print("yuck");
+ response.flushBuffer();
+ }
+ }
+
+
public class NoopHandler extends AbstractHandler
{
public void handle(String target, Request baseRequest,
@@ -1462,9 +1540,9 @@
"\r\n"
).getBytes());
os.flush();
-
+
Thread.sleep(200);
-
+
// write an pipelined request
os.write((
"GET / HTTP/1.1\r\n"+
@@ -1473,11 +1551,11 @@
"\r\n"
).getBytes());
os.flush();
-
+
String response=readResponse(client);
assertThat(response,JUnitMatchers.containsString("RESUMEDHTTP/1.1 200 OK"));
assertThat((System.currentTimeMillis()-start),greaterThanOrEqualTo(1999L));
-
+
// TODO This test should also check that that the CPU did not spin during the suspend.
}
finally
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/LocalAsyncContextTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/LocalAsyncContextTest.java
index 1b0c35a..8876f23 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/LocalAsyncContextTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/LocalAsyncContextTest.java
@@ -18,18 +18,26 @@
package org.eclipse.jetty.server;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.servlet.AsyncContext;
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
+import javax.servlet.DispatcherType;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
-
-
-import org.eclipse.jetty.continuation.Continuation;
-import org.eclipse.jetty.continuation.ContinuationListener;
+import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.server.session.SessionHandler;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
public class LocalAsyncContextTest
{
protected Server _server = new Server();
@@ -48,7 +56,7 @@
_server.setHandler(session);
_server.start();
}
-
+
protected Connector initConnector()
{
return new LocalConnector();
@@ -64,76 +72,123 @@
@Test
public void testSuspendResume() throws Exception
{
+ String response;
+ __completed.set(0);
+ __completed1.set(0);
_handler.setRead(0);
_handler.setSuspendFor(1000);
_handler.setResumeAfter(-1);
_handler.setCompleteAfter(-1);
- check("TIMEOUT",process(null));
+ response=process(null);
+ check(response,"TIMEOUT");
+ assertEquals(1,__completed.get());
+ assertEquals(1,__completed1.get());
_handler.setSuspendFor(10000);
_handler.setResumeAfter(0);
_handler.setCompleteAfter(-1);
- check("RESUMED",process(null));
+ response=process(null);
+ check(response,"DISPATCHED");
_handler.setResumeAfter(100);
_handler.setCompleteAfter(-1);
- check("RESUMED",process(null));
+ response=process(null);
+ check(response,"DISPATCHED");
_handler.setResumeAfter(-1);
_handler.setCompleteAfter(0);
- check("COMPLETED",process(null));
+ response=process(null);
+ check(response,"COMPLETED");
_handler.setResumeAfter(-1);
_handler.setCompleteAfter(200);
- check("COMPLETED",process(null));
+ response=process(null);
+ check(response,"COMPLETED");
_handler.setRead(-1);
_handler.setResumeAfter(0);
_handler.setCompleteAfter(-1);
- check("RESUMED",process("wibble"));
+ response=process("wibble");
+ check(response,"DISPATCHED");
_handler.setResumeAfter(100);
_handler.setCompleteAfter(-1);
- check("RESUMED",process("wibble"));
+ response=process("wibble");
+ check(response,"DISPATCHED");
_handler.setResumeAfter(-1);
_handler.setCompleteAfter(0);
- check("COMPLETED",process("wibble"));
+ response=process("wibble");
+ check(response,"COMPLETED");
_handler.setResumeAfter(-1);
_handler.setCompleteAfter(100);
- check("COMPLETED",process("wibble"));
+ response=process("wibble");
+ check(response,"COMPLETED");
_handler.setRead(6);
_handler.setResumeAfter(0);
_handler.setCompleteAfter(-1);
- check("RESUMED",process("wibble"));
+ response=process("wibble");
+ check(response,"DISPATCHED");
_handler.setResumeAfter(100);
_handler.setCompleteAfter(-1);
- check("RESUMED",process("wibble"));
+ response=process("wibble");
+ check(response,"DISPATCHED");
_handler.setResumeAfter(-1);
_handler.setCompleteAfter(0);
- check("COMPLETED",process("wibble"));
+ response=process("wibble");
+ check(response,"COMPLETED");
_handler.setResumeAfter(-1);
_handler.setCompleteAfter(100);
- check("COMPLETED",process("wibble"));
+ response=process("wibble");
+ check(response,"COMPLETED");
}
- protected void check(String content,String response)
+ @Test
+ public void testTwoCycles() throws Exception
+ {
+ String response;
+
+ __completed.set(0);
+ __completed1.set(0);
+
+ _handler.setRead(0);
+ _handler.setSuspendFor(1000);
+ _handler.setResumeAfter(100);
+ _handler.setCompleteAfter(-1);
+ _handler.setSuspendFor2(1000);
+ _handler.setResumeAfter2(200);
+ _handler.setCompleteAfter2(-1);
+ response=process(null);
+ check(response,"STARTASYNC","DISPATCHED","startasync","STARTASYNC","DISPATCHED");
+ assertEquals(1,__completed.get());
+ assertEquals(0,__completed1.get());
+
+ }
+
+ protected void check(String response,String... content)
{
assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
- assertTrue(response.contains(content));
+ int i=0;
+ for (String m:content)
+ {
+ i=response.indexOf(m,i);
+ assertTrue(i>=0);
+ i+=m.length();
+ }
+
}
private synchronized String process(String content) throws Exception
{
- String request = "GET / HTTP/1.1\r\n" +
+ String request = "GET / HTTP/1.1\r\n" +
"Host: localhost\r\n"+
"Connection: close\r\n";
@@ -144,23 +199,346 @@
return getResponse(request);
}
-
+
protected String getResponse(String request) throws Exception
{
return ((LocalConnector)_connector).getResponses(request);
}
-
- static ContinuationListener __asyncListener = new ContinuationListener()
+ private static class SuspendHandler extends HandlerWrapper
{
- public void onComplete(Continuation continuation)
+ private int _read;
+ private long _suspendFor=-1;
+ private long _resumeAfter=-1;
+ private long _completeAfter=-1;
+ private long _suspendFor2=-1;
+ private long _resumeAfter2=-1;
+ private long _completeAfter2=-1;
+
+ public SuspendHandler()
{
}
- public void onTimeout(Continuation continuation)
+ public int getRead()
{
- continuation.setAttribute("TIMEOUT",Boolean.TRUE);
- continuation.resume();
+ return _read;
}
+
+ public void setRead(int read)
+ {
+ _read = read;
+ }
+
+ public long getSuspendFor()
+ {
+ return _suspendFor;
+ }
+
+ public void setSuspendFor(long suspendFor)
+ {
+ _suspendFor = suspendFor;
+ }
+
+ public long getResumeAfter()
+ {
+ return _resumeAfter;
+ }
+
+ public void setResumeAfter(long resumeAfter)
+ {
+ _resumeAfter = resumeAfter;
+ }
+
+ public long getCompleteAfter()
+ {
+ return _completeAfter;
+ }
+
+ public void setCompleteAfter(long completeAfter)
+ {
+ _completeAfter = completeAfter;
+ }
+
+
+
+ /* ------------------------------------------------------------ */
+ /** Get the suspendFor2.
+ * @return the suspendFor2
+ */
+ public long getSuspendFor2()
+ {
+ return _suspendFor2;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /** Set the suspendFor2.
+ * @param suspendFor2 the suspendFor2 to set
+ */
+ public void setSuspendFor2(long suspendFor2)
+ {
+ _suspendFor2 = suspendFor2;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /** Get the resumeAfter2.
+ * @return the resumeAfter2
+ */
+ public long getResumeAfter2()
+ {
+ return _resumeAfter2;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /** Set the resumeAfter2.
+ * @param resumeAfter2 the resumeAfter2 to set
+ */
+ public void setResumeAfter2(long resumeAfter2)
+ {
+ _resumeAfter2 = resumeAfter2;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /** Get the completeAfter2.
+ * @return the completeAfter2
+ */
+ public long getCompleteAfter2()
+ {
+ return _completeAfter2;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /** Set the completeAfter2.
+ * @param completeAfter2 the completeAfter2 to set
+ */
+ public void setCompleteAfter2(long completeAfter2)
+ {
+ _completeAfter2 = completeAfter2;
+ }
+
+
+ @Override
+ public void handle(String target, final Request baseRequest, final HttpServletRequest request, final HttpServletResponse response) throws IOException, ServletException
+ {
+ try
+ {
+ if (DispatcherType.REQUEST.equals(baseRequest.getDispatcherType()))
+ {
+ if (_read>0)
+ {
+ byte[] buf=new byte[_read];
+ request.getInputStream().read(buf);
+ }
+ else if (_read<0)
+ {
+ InputStream in = request.getInputStream();
+ int b=in.read();
+ while(b!=-1)
+ b=in.read();
+ }
+
+
+ final AsyncContext asyncContext = baseRequest.startAsync();
+ response.getOutputStream().println("STARTASYNC");
+ asyncContext.addListener(__asyncListener);
+ asyncContext.addListener(__asyncListener1);
+ if (_suspendFor>0)
+ asyncContext.setTimeout(_suspendFor);
+
+
+ if (_completeAfter>0)
+ {
+ new Thread() {
+ @Override
+ public void run()
+ {
+ try
+ {
+ Thread.sleep(_completeAfter);
+ response.getOutputStream().println("COMPLETED");
+ response.setStatus(200);
+ baseRequest.setHandled(true);
+ asyncContext.complete();
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ }.start();
+ }
+ else if (_completeAfter==0)
+ {
+ response.getOutputStream().println("COMPLETED");
+ response.setStatus(200);
+ baseRequest.setHandled(true);
+ asyncContext.complete();
+ }
+
+ if (_resumeAfter>0)
+ {
+ new Thread() {
+ @Override
+ public void run()
+ {
+ try
+ {
+ Thread.sleep(_resumeAfter);
+ if(((HttpServletRequest)asyncContext.getRequest()).getSession(true).getId()!=null)
+ asyncContext.dispatch();
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ }.start();
+ }
+ else if (_resumeAfter==0)
+ {
+ asyncContext.dispatch();
+ }
+ }
+ else
+ {
+ if (request.getAttribute("TIMEOUT")!=null)
+ response.getOutputStream().println("TIMEOUT");
+ else
+ response.getOutputStream().println("DISPATCHED");
+
+ if (_suspendFor2>=0)
+ {
+ final AsyncContext asyncContext = baseRequest.startAsync();
+ response.getOutputStream().println("STARTASYNC2");
+ if (_suspendFor2>0)
+ asyncContext.setTimeout(_suspendFor2);
+ _suspendFor2=-1;
+
+ if (_completeAfter2>0)
+ {
+ new Thread() {
+ @Override
+ public void run()
+ {
+ try
+ {
+ Thread.sleep(_completeAfter2);
+ response.getOutputStream().println("COMPLETED2");
+ response.setStatus(200);
+ baseRequest.setHandled(true);
+ asyncContext.complete();
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ }.start();
+ }
+ else if (_completeAfter2==0)
+ {
+ response.getOutputStream().println("COMPLETED2");
+ response.setStatus(200);
+ baseRequest.setHandled(true);
+ asyncContext.complete();
+ }
+
+ if (_resumeAfter2>0)
+ {
+ new Thread() {
+ @Override
+ public void run()
+ {
+ try
+ {
+ Thread.sleep(_resumeAfter2);
+ asyncContext.dispatch();
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ }.start();
+ }
+ else if (_resumeAfter2==0)
+ {
+ asyncContext.dispatch();
+ }
+ }
+ else
+ {
+ response.setStatus(200);
+ baseRequest.setHandled(true);
+ }
+ }
+ }
+ finally
+ {
+ }
+ }
+ }
+
+ static AtomicInteger __completed = new AtomicInteger();
+ static AtomicInteger __completed1 = new AtomicInteger();
+
+ private static AsyncListener __asyncListener = new AsyncListener()
+ {
+
+ @Override
+ public void onComplete(AsyncEvent event) throws IOException
+ {
+ __completed.incrementAndGet();
+ }
+
+ @Override
+ public void onError(AsyncEvent event) throws IOException
+ {
+ __completed.incrementAndGet();
+ }
+
+ @Override
+ public void onStartAsync(AsyncEvent event) throws IOException
+ {
+ event.getSuppliedResponse().getOutputStream().println("startasync");
+ event.getAsyncContext().addListener(this);
+ }
+
+ @Override
+ public void onTimeout(AsyncEvent event) throws IOException
+ {
+ event.getSuppliedRequest().setAttribute("TIMEOUT",Boolean.TRUE);
+ event.getAsyncContext().dispatch();
+ }
+
+ };
+
+ private static AsyncListener __asyncListener1 = new AsyncListener()
+ {
+
+ @Override
+ public void onComplete(AsyncEvent event) throws IOException
+ {
+ __completed1.incrementAndGet();
+ }
+
+ @Override
+ public void onError(AsyncEvent event) throws IOException
+ {
+ }
+ @Override
+ public void onStartAsync(AsyncEvent event) throws IOException
+ {
+ }
+
+ @Override
+ public void onTimeout(AsyncEvent event) throws IOException
+ {
+ }
+
};
}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java
index c82deb3..b897f4f 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java
@@ -23,6 +23,7 @@
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNotNull;
import java.io.BufferedReader;
import java.io.File;
@@ -36,10 +37,13 @@
import java.util.HashMap;
import java.util.Map;
+import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletException;
+import javax.servlet.ServletRequestEvent;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.Part;
import junit.framework.Assert;
@@ -47,6 +51,7 @@
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.MultiPartInputStream;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.log.Log;
import org.junit.After;
@@ -127,6 +132,113 @@
assertTrue(responses.startsWith("HTTP/1.1 200"));
}
+
+ @Test
+ public void testMultiPartNoConfig() throws Exception
+ {
+ _handler._checker = new RequestTester()
+ {
+ public boolean check(HttpServletRequest request,HttpServletResponse response)
+ {
+ try
+ {
+ Part foo = request.getPart("stuff");
+ return false;
+ }
+ catch (IllegalStateException e)
+ {
+ //expected exception because no multipart config is set up
+ assertTrue(e.getMessage().startsWith("No multipart config"));
+ return true;
+ }
+ catch (Exception e)
+ {
+ return false;
+ }
+ }
+ };
+
+ String multipart = "--AaB03x\r\n"+
+ "content-disposition: form-data; name=\"field1\"\r\n"+
+ "\r\n"+
+ "Joe Blow\r\n"+
+ "--AaB03x\r\n"+
+ "content-disposition: form-data; name=\"stuff\"\r\n"+
+ "Content-Type: text/plain;charset=ISO-8859-1\r\n"+
+ "\r\n"+
+ "000000000000000000000000000000000000000000000000000\r\n"+
+ "--AaB03x--\r\n";
+
+ String request="GET / HTTP/1.1\r\n"+
+ "Host: whatever\r\n"+
+ "Content-Type: multipart/form-data; boundary=\"AaB03x\"\r\n"+
+ "Content-Length: "+multipart.getBytes().length+"\r\n"+
+ "\r\n"+
+ multipart;
+
+ String responses=_connector.getResponses(request);
+ assertTrue(responses.startsWith("HTTP/1.1 200"));
+ }
+
+
+ @Test
+ public void testMultiPart() throws Exception
+ {
+ final File tmpDir = new File (System.getProperty("java.io.tmpdir"));
+ final File testTmpDir = new File (tmpDir, "reqtest");
+ testTmpDir.deleteOnExit();
+ assertTrue(testTmpDir.mkdirs());
+ assertTrue(testTmpDir.list().length == 0);
+
+ ContextHandler contextHandler = new ContextHandler();
+ contextHandler.setContextPath("/foo");
+ contextHandler.setResourceBase(".");
+ contextHandler.setHandler(new MultiPartRequestHandler(testTmpDir));
+ contextHandler.addEventListener(new Request.MultiPartCleanerListener()
+ {
+
+ @Override
+ public void requestDestroyed(ServletRequestEvent sre)
+ {
+ MultiPartInputStream m = (MultiPartInputStream)sre.getServletRequest().getAttribute(Request.__MULTIPART_INPUT_STREAM);
+ ContextHandler.Context c = (ContextHandler.Context)sre.getServletRequest().getAttribute(Request.__MULTIPART_CONTEXT);
+ assertNotNull (m);
+ assertNotNull (c);
+ assertTrue(c == sre.getServletContext());
+ assertTrue(!m.getParsedParts().isEmpty());
+ assertTrue(testTmpDir.list().length == 2);
+ super.requestDestroyed(sre);
+ String[] files = testTmpDir.list();
+ assertTrue(files.length == 0);
+ }
+
+ });
+ _server.stop();
+ _server.setHandler(contextHandler);
+ _server.start();
+
+ String multipart = "--AaB03x\r\n"+
+ "content-disposition: form-data; name=\"field1\"\r\n"+
+ "\r\n"+
+ "Joe Blow\r\n"+
+ "--AaB03x\r\n"+
+ "content-disposition: form-data; name=\"stuff\"; filename=\"foo.upload\"\r\n"+
+ "Content-Type: text/plain;charset=ISO-8859-1\r\n"+
+ "\r\n"+
+ "000000000000000000000000000000000000000000000000000\r\n"+
+ "--AaB03x--\r\n";
+
+ String request="GET /foo/x.html HTTP/1.1\r\n"+
+ "Host: whatever\r\n"+
+ "Content-Type: multipart/form-data; boundary=\"AaB03x\"\r\n"+
+ "Content-Length: "+multipart.getBytes().length+"\r\n"+
+ "\r\n"+
+ multipart;
+
+ String responses=_connector.getResponses(request);
+ System.err.println(responses);
+ assertTrue(responses.startsWith("HTTP/1.1 200"));
+ }
@Test
public void testBadUtf8ParamExtraction() throws Exception
@@ -670,10 +782,12 @@
assertNotSame(cookies.get(1), cookies.get(3));
cookies.clear();
+//NOTE: the javax.servlet.http.Cookie class sets the system property org.glassfish.web.rfc2109_cookie_names_enforced
+//to TRUE by default, and rejects all cookie names containing punctuation.Therefore this test cannot use "name2".
response=_connector.getResponses(
"POST / HTTP/1.1\r\n"+
"Host: whatever\r\n"+
- "Cookie: name0=value0; name1 = value1 ; \"\\\"name2\\\"\" = \"\\\"value2\\\"\" \n" +
+ "Cookie: name0=value0; name1 = value1 ; \"name2\" = \"\\\"value2\\\"\" \n" +
"Cookie: $Version=2; name3=value3=value3;$path=/path;$domain=acme.com;$port=8080; name4=; name5 = ; name6\n" +
"Cookie: name7=value7;\n" +
"Connection: close\r\n"+
@@ -683,7 +797,7 @@
assertEquals("value0", cookies.get(0).getValue());
assertEquals("name1", cookies.get(1).getName());
assertEquals("value1", cookies.get(1).getValue());
- assertEquals("\"name2\"", cookies.get(2).getName());
+ assertEquals("name2", cookies.get(2).getName());
assertEquals("\"value2\"", cookies.get(2).getValue());
assertEquals("name3", cookies.get(3).getName());
assertEquals("value3=value3", cookies.get(3).getValue());
@@ -850,7 +964,9 @@
{
((Request)request).setHandled(true);
- if (request.getContentLength()>0 && !MimeTypes.FORM_ENCODED.equals(request.getContentType()))
+ if (request.getContentLength()>0
+ && !MimeTypes.FORM_ENCODED.equals(request.getContentType())
+ && !request.getContentType().startsWith("multipart/form-data"))
_content=IO.toString(request.getInputStream());
if (_checker!=null && _checker.check(request,response))
@@ -861,4 +977,45 @@
}
}
+
+ private class MultiPartRequestHandler extends AbstractHandler
+ {
+ File tmpDir;
+
+ public MultiPartRequestHandler(File tmpDir)
+ {
+ this.tmpDir = tmpDir;
+ }
+
+
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ ((Request)request).setHandled(true);
+ try
+ {
+
+ MultipartConfigElement mpce = new MultipartConfigElement(tmpDir.getAbsolutePath(),-1, -1, 2);
+ request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, mpce);
+
+ String field1 = request.getParameter("field1");
+ assertNotNull(field1);
+
+ Part foo = request.getPart("stuff");
+ assertNotNull(foo);
+ assertTrue(foo.getSize() > 0);
+ response.setStatus(200);
+ }
+ catch (IllegalStateException e)
+ {
+ //expected exception because no multipart config is set up
+ assertTrue(e.getMessage().startsWith("No multipart config"));
+ response.setStatus(200);
+ }
+ catch (Exception e)
+ {
+ response.sendError(500);
+ }
+ }
+ }
}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java
index 91bb91c..b853cf4 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java
@@ -32,6 +32,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
+import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
@@ -57,6 +58,7 @@
import org.eclipse.jetty.server.session.AbstractSessionManager;
import org.eclipse.jetty.server.session.HashSessionIdManager;
import org.eclipse.jetty.server.session.HashSessionManager;
+import org.eclipse.jetty.util.StringUtil;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -73,6 +75,7 @@
public void init() throws Exception
{
server = new Server();
+ server.setSendServerVersion(false);
connector = new LocalConnector();
server.addConnector(connector);
server.setHandler(new DumpHandler());
@@ -104,14 +107,15 @@
response.setContentType("foo2/bar2");
assertEquals("foo2/bar2;charset=ISO-8859-1",response.getContentType());
response.setHeader("name","foo");
- Enumeration<?> en=response.getHeaders("name");
- assertEquals("foo",en.nextElement());
- assertFalse(en.hasMoreElements());
+
+ Iterator<String> en = response.getHeaders("name").iterator();
+ assertEquals("foo",en.next());
+ assertFalse(en.hasNext());
response.addHeader("name","bar");
- en=response.getHeaders("name");
- assertEquals("foo",en.nextElement());
- assertEquals("bar",en.nextElement());
- assertFalse(en.hasMoreElements());
+ en=response.getHeaders("name").iterator();
+ assertEquals("foo",en.next());
+ assertEquals("bar",en.next());
+ assertFalse(en.hasNext());
response.recycle();
@@ -418,8 +422,45 @@
assertEquals("/foo;jsessionid=12345", response.encodeURL("/foo"));
assertEquals("/;jsessionid=12345", response.encodeURL("/"));
assertEquals("/foo.html;jsessionid=12345#target", response.encodeURL("/foo.html#target"));
- assertEquals(";jsessionid=12345", response.encodeURL(""));
-
+ assertEquals(";jsessionid=12345", response.encodeURL(""));
+ }
+
+ @Test
+ public void testCanonicalSendRedirect() throws Exception
+ {
+ ByteArrayEndPoint out=new ByteArrayEndPoint(new byte[]{},4096);
+ AbstractHttpConnection connection=new TestHttpConnection(connector,out, connector.getServer());
+ Response response = new Response(connection);
+ Request request = connection.getRequest();
+ request.setServerName("myhost");
+ request.setServerPort(8888);
+ request.setRequestURI("/path/test/info/");
+ request.setContextPath("/path");
+ request.setServletPath("/test");
+ request.setPathInfo("/info/");
+
+ response.sendRedirect("../other%2Fpath");
+ String location = response.getHeader("Location");
+ assertEquals("http://myhost:8888/path/test/other%2Fpath",location);
+ }
+
+ @Test
+ public void testSendServerVersion() throws Exception
+ {
+ ByteArrayEndPoint out=new ByteArrayEndPoint(new byte[]{},4096);
+ AbstractHttpConnection connection=new TestHttpConnection(connector,out, connector.getServer());
+ Response response = new Response(connection);
+ Request request = connection.getRequest();
+ request.setServerName("myhost");
+ request.setServerPort(8888);
+ request.setRequestURI("/oops/");
+ request.setContextPath("/");
+ request.setServletPath("/");
+ request.setPathInfo("/info/");
+
+ response.sendError(500, "Ooops");
+ ByteArrayBuffer output = out.getOut();
+ assertFalse("Contains jetty://", output.toString(StringUtil.__UTF8_CHARSET).contains("jetty://"));
}
@Test
@@ -442,7 +483,8 @@
{"l%20cation","http://myhost:8888/path/l%20cation"},
{"./l%20cation","http://myhost:8888/path/l%20cation"},
{"../l%20cation","http://myhost:8888/l%20cation"},
- {"../locati%C3%abn","http://myhost:8888/locati%C3%ABn"},
+ {"../locati%C3%abn","http://myhost:8888/locati%C3%abn"},
+ {"../other%2fplace","http://myhost:8888/other%2fplace"},
};
for (int i=0;i<tests.length;i++)
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/SelectChannelTimeoutTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/SelectChannelTimeoutTest.java
index fa3b2e2..3ebf08f 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/SelectChannelTimeoutTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/SelectChannelTimeoutTest.java
@@ -18,8 +18,6 @@
package org.eclipse.jetty.server;
-import static org.junit.Assert.assertTrue;
-
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
@@ -32,6 +30,8 @@
import org.junit.BeforeClass;
import org.junit.Test;
+import static org.junit.Assert.assertTrue;
+
public class SelectChannelTimeoutTest extends ConnectorTimeoutTest
{
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/SuspendHandler.java b/jetty-server/src/test/java/org/eclipse/jetty/server/SuspendHandler.java
index 1ffe892..fbca306 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/SuspendHandler.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/SuspendHandler.java
@@ -20,14 +20,17 @@
import java.io.IOException;
import java.io.InputStream;
-
+import javax.servlet.AsyncContext;
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
+import javax.servlet.DispatcherType;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.handler.HandlerWrapper;
-class SuspendHandler extends HandlerWrapper
+class SuspendHandler extends HandlerWrapper implements AsyncListener
{
private int _read;
private long _suspendFor=-1;
@@ -77,7 +80,7 @@
{
_completeAfter = completeAfter;
}
-
+
@Override
public void handle(String target, final Request baseRequest, final HttpServletRequest request, final HttpServletResponse response) throws IOException, ServletException
{
@@ -97,7 +100,7 @@
}
final AsyncContext asyncContext = baseRequest.startAsync();
- asyncContext.addContinuationListener(LocalAsyncContextTest.__asyncListener);
+ asyncContext.addListener(this);
if (_suspendFor>0)
asyncContext.setTimeout(_suspendFor);
@@ -110,7 +113,7 @@
try
{
Thread.sleep(_completeAfter);
- response.getOutputStream().print("COMPLETED");
+ response.getOutputStream().println("COMPLETED");
response.setStatus(200);
baseRequest.setHandled(true);
asyncContext.complete();
@@ -124,7 +127,7 @@
}
else if (_completeAfter==0)
{
- response.getOutputStream().print("COMPLETED");
+ response.getOutputStream().println("COMPLETED");
response.setStatus(200);
baseRequest.setHandled(true);
asyncContext.complete();
@@ -167,4 +170,25 @@
}
}
+ @Override
+ public void onComplete(AsyncEvent asyncEvent) throws IOException
+ {
+ }
+
+ @Override
+ public void onTimeout(AsyncEvent asyncEvent) throws IOException
+ {
+ asyncEvent.getSuppliedRequest().setAttribute("TIMEOUT",Boolean.TRUE);
+ asyncEvent.getAsyncContext().dispatch();
+ }
+
+ @Override
+ public void onError(AsyncEvent asyncEvent) throws IOException
+ {
+ }
+
+ @Override
+ public void onStartAsync(AsyncEvent asyncEvent) throws IOException
+ {
+ }
}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java
new file mode 100644
index 0000000..3a8af17
--- /dev/null
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java
@@ -0,0 +1,199 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.session;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import javax.servlet.SessionCookieConfig;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.eclipse.jetty.http.HttpCookie;
+import org.junit.Test;
+/**
+ * SessionCookieTest
+ *
+ *
+ */
+public class SessionCookieTest
+{
+
+ public class MockSession extends AbstractSession
+ {
+
+
+ /**
+ * @param abstractSessionManager
+ * @param created
+ * @param accessed
+ * @param clusterId
+ */
+ protected MockSession(AbstractSessionManager abstractSessionManager, long created, long accessed, String clusterId)
+ {
+ super(abstractSessionManager, created, accessed, clusterId);
+ }
+
+ }
+
+ public class MockSessionIdManager extends AbstractSessionIdManager
+ {
+
+ /**
+ * @see org.eclipse.jetty.server.SessionIdManager#idInUse(java.lang.String)
+ */
+ public boolean idInUse(String id)
+ {
+ return false;
+ }
+
+ /**
+ * @see org.eclipse.jetty.server.SessionIdManager#addSession(javax.servlet.http.HttpSession)
+ */
+ public void addSession(HttpSession session)
+ {
+
+ }
+
+ /**
+ * @see org.eclipse.jetty.server.SessionIdManager#removeSession(javax.servlet.http.HttpSession)
+ */
+ public void removeSession(HttpSession session)
+ {
+
+ }
+
+ /**
+ * @see org.eclipse.jetty.server.SessionIdManager#invalidateAll(java.lang.String)
+ */
+ public void invalidateAll(String id)
+ {
+
+ }
+
+ /**
+ * @see org.eclipse.jetty.server.SessionIdManager#getClusterId(java.lang.String)
+ */
+ public String getClusterId(String nodeId)
+ {
+ int dot=nodeId.lastIndexOf('.');
+ return (dot>0)?nodeId.substring(0,dot):nodeId;
+ }
+
+ /**
+ * @see org.eclipse.jetty.server.SessionIdManager#getNodeId(java.lang.String, javax.servlet.http.HttpServletRequest)
+ */
+ public String getNodeId(String clusterId, HttpServletRequest request)
+ {
+ return clusterId+'.'+_workerName;
+ }
+
+ }
+
+ public class MockSessionManager extends AbstractSessionManager
+ {
+
+ /**
+ * @see org.eclipse.jetty.server.session.AbstractSessionManager#addSession(org.eclipse.jetty.server.session.AbstractSession)
+ */
+ protected void addSession(AbstractSession session)
+ {
+
+ }
+
+ /**
+ * @see org.eclipse.jetty.server.session.AbstractSessionManager#getSession(java.lang.String)
+ */
+ public AbstractSession getSession(String idInCluster)
+ {
+ return null;
+ }
+
+ /**
+ * @see org.eclipse.jetty.server.session.AbstractSessionManager#invalidateSessions()
+ */
+ protected void invalidateSessions() throws Exception
+ {
+
+ }
+
+ /**
+ * @see org.eclipse.jetty.server.session.AbstractSessionManager#newSession(javax.servlet.http.HttpServletRequest)
+ */
+ protected AbstractSession newSession(HttpServletRequest request)
+ {
+ return null;
+ }
+
+ /**
+ * @see org.eclipse.jetty.server.session.AbstractSessionManager#removeSession(java.lang.String)
+ */
+ protected boolean removeSession(String idInCluster)
+ {
+ return false;
+ }
+
+ }
+
+ @Test
+ public void testSecureSessionCookie () throws Exception
+ {
+ MockSessionIdManager idMgr = new MockSessionIdManager();
+ idMgr.setWorkerName("node1");
+ MockSessionManager mgr = new MockSessionManager();
+ mgr.setSessionIdManager(idMgr);
+ MockSession session = new MockSession(mgr, System.currentTimeMillis(), System.currentTimeMillis(), "node1123"); //clusterId
+
+ SessionCookieConfig sessionCookieConfig = mgr.getSessionCookieConfig();
+ sessionCookieConfig.setSecure(true);
+
+ //sessionCookieConfig.secure == true, always mark cookie as secure, irrespective of if requestIsSecure
+ HttpCookie cookie = mgr.getSessionCookie(session, "/foo", true);
+ assertTrue(cookie.isSecure());
+ //sessionCookieConfig.secure == true, always mark cookie as secure, irrespective of if requestIsSecure
+ cookie = mgr.getSessionCookie(session, "/foo", false);
+ assertTrue(cookie.isSecure());
+
+ //sessionCookieConfig.secure==false, setSecureRequestOnly==true, requestIsSecure==true
+ //cookie should be secure: see SessionCookieConfig.setSecure() javadoc
+ sessionCookieConfig.setSecure(false);
+ cookie = mgr.getSessionCookie(session, "/foo", true);
+ assertTrue(cookie.isSecure());
+
+ //sessionCookieConfig.secure=false, setSecureRequestOnly==true, requestIsSecure==false
+ //cookie is not secure: see SessionCookieConfig.setSecure() javadoc
+ cookie = mgr.getSessionCookie(session, "/foo", false);
+ assertFalse(cookie.isSecure());
+
+ //sessionCookieConfig.secure=false, setSecureRequestOnly==false, requestIsSecure==false
+ //cookie is not secure: not a secure request
+ mgr.setSecureRequestOnly(false);
+ cookie = mgr.getSessionCookie(session, "/foo", false);
+ assertFalse(cookie.isSecure());
+
+ //sessionCookieConfig.secure=false, setSecureRequestOnly==false, requestIsSecure==true
+ //cookie is not secure: not on secured requests and request is secure
+ cookie = mgr.getSessionCookie(session, "/foo", true);
+ assertFalse(cookie.isSecure());
+
+
+ }
+
+}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionHandlerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionHandlerTest.java
index 690cbfe..82a6b18 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionHandlerTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionHandlerTest.java
@@ -26,20 +26,29 @@
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.Principal;
+import java.util.Collection;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.Locale;
import java.util.Map;
-
+import java.util.Set;
+import javax.servlet.AsyncContext;
import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.SessionCookieConfig;
+import javax.servlet.SessionTrackingMode;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
+import javax.servlet.http.Part;
+import javax.servlet.DispatcherType;
import org.eclipse.jetty.http.HttpCookie;
-import org.eclipse.jetty.server.DispatcherType;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.SessionIdManager;
import org.eclipse.jetty.server.SessionManager;
@@ -68,6 +77,98 @@
SessionHandler sessionHandler = new SessionHandler();
sessionHandler.setSessionManager(new MockSessionManager()
{
+
+
+ public SessionCookieConfig getSessionCookieConfig()
+ {
+ return new SessionCookieConfig()
+ {
+
+ public String getComment()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public String getDomain()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public int getMaxAge()
+ {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public String getName()
+ {
+ return cookieName;
+ }
+
+ public String getPath()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public boolean isHttpOnly()
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public boolean isSecure()
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public void setComment(String comment)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setDomain(String domain)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setHttpOnly(boolean httpOnly)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setMaxAge(int maxAge)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setName(String name)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setPath(String path)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setSecure(boolean secure)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ };
+ }
public boolean isUsingCookies()
{
return true;
@@ -104,7 +205,8 @@
SessionHandler sessionHandler = new SessionHandler();
sessionHandler.setSessionManager(new MockSessionManager()
- {
+ {
+
@Override
public String getSessionIdPathParameterName()
{
@@ -396,6 +498,114 @@
public void setCharacterEncoding(String env) throws UnsupportedEncodingException
{
}
+
+ /**
+ * @see javax.servlet.http.HttpServletRequest#authenticate(javax.servlet.http.HttpServletResponse)
+ */
+ public boolean authenticate(HttpServletResponse response) throws IOException, ServletException
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ /**
+ * @see javax.servlet.http.HttpServletRequest#getPart(java.lang.String)
+ */
+ public Part getPart(String name) throws IOException, ServletException
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /**
+ * @see javax.servlet.http.HttpServletRequest#getParts()
+ */
+ public Collection<Part> getParts() throws IOException, ServletException
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /**
+ * @see javax.servlet.http.HttpServletRequest#login(java.lang.String, java.lang.String)
+ */
+ public void login(String username, String password) throws ServletException
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ /**
+ * @see javax.servlet.http.HttpServletRequest#logout()
+ */
+ public void logout() throws ServletException
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ /**
+ * @see javax.servlet.ServletRequest#getAsyncContext()
+ */
+ public AsyncContext getAsyncContext()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /**
+ * @see javax.servlet.ServletRequest#getDispatcherType()
+ */
+ public DispatcherType getDispatcherType()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /**
+ * @see javax.servlet.ServletRequest#getServletContext()
+ */
+ public ServletContext getServletContext()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /**
+ * @see javax.servlet.ServletRequest#isAsyncStarted()
+ */
+ public boolean isAsyncStarted()
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ /**
+ * @see javax.servlet.ServletRequest#isAsyncSupported()
+ */
+ public boolean isAsyncSupported()
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ /**
+ * @see javax.servlet.ServletRequest#startAsync()
+ */
+ public AsyncContext startAsync() throws IllegalStateException
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /**
+ * @see javax.servlet.ServletRequest#startAsync(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
+ */
+ public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
}
/**
@@ -592,6 +802,50 @@
{
}
+ /**
+ * @see org.eclipse.jetty.server.SessionManager#getDefaultSessionTrackingModes()
+ */
+ public Set<SessionTrackingMode> getDefaultSessionTrackingModes()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /**
+ * @see org.eclipse.jetty.server.SessionManager#getEffectiveSessionTrackingModes()
+ */
+ public Set<SessionTrackingMode> getEffectiveSessionTrackingModes()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /**
+ * @see org.eclipse.jetty.server.SessionManager#getSessionCookieConfig()
+ */
+ public SessionCookieConfig getSessionCookieConfig()
+ {
+ return null;
+ }
+
+ /**
+ * @see org.eclipse.jetty.server.SessionManager#isUsingURLs()
+ */
+ public boolean isUsingURLs()
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ /**
+ * @see org.eclipse.jetty.server.SessionManager#setSessionTrackingModes(java.util.Set)
+ */
+ public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
private boolean _checkRemote=false;
public boolean isCheckingRemoteSessionIdEncoding()
diff --git a/jetty-servlet/pom.xml b/jetty-servlet/pom.xml
index af848b8..bd8f62d 100644
--- a/jetty-servlet/pom.xml
+++ b/jetty-servlet/pom.xml
@@ -3,7 +3,7 @@
<parent>
<artifactId>jetty-project</artifactId>
<groupId>org.eclipse.jetty</groupId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-servlet</artifactId>
@@ -26,7 +26,7 @@
</goals>
<configuration>
<instructions>
- <Import-Package>org.eclipse.jetty.jmx.*;version="[7.3,8)";resolution:=optional,*</Import-Package>
+ <Import-Package>javax.servlet.*;version="2.6.0",org.eclipse.jetty.jmx.*;version="8.0";resolution:=optional,*</Import-Package>
</instructions>
</configuration>
</execution>
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ErrorPageErrorHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ErrorPageErrorHandler.java
index c3ba685..580293c 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ErrorPageErrorHandler.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ErrorPageErrorHandler.java
@@ -18,25 +18,18 @@
package org.eclipse.jetty.servlet;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.eclipse.jetty.http.HttpMethods;
-import org.eclipse.jetty.server.AbstractHttpConnection;
-import org.eclipse.jetty.server.Dispatcher;
-import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ErrorHandler;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */
/** Error Page Error Handler
@@ -45,11 +38,8 @@
* the internal ERROR style of dispatch.
*
*/
-public class ErrorPageErrorHandler extends ErrorHandler
+public class ErrorPageErrorHandler extends ErrorHandler implements ErrorHandler.ErrorPageMapper
{
- private static final Logger LOG = Log.getLogger(ErrorPageErrorHandler.class);
-
- public final static String ERROR_PAGE="org.eclipse.jetty.server.error_page";
public final static String GLOBAL_ERROR_PAGE = "org.eclipse.jetty.server.error_page.global";
protected ServletContext _servletContext;
@@ -60,104 +50,63 @@
public ErrorPageErrorHandler()
{}
- /* ------------------------------------------------------------ */
- /**
- * @see org.eclipse.jetty.server.handler.ErrorHandler#handle(String, Request, HttpServletRequest, HttpServletResponse)
- */
@Override
- public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
+ public String getErrorPage(HttpServletRequest request)
{
- String method = request.getMethod();
- if(!method.equals(HttpMethods.GET) && !method.equals(HttpMethods.POST) && !method.equals(HttpMethods.HEAD))
+ String error_page= null;
+ Class<?> exClass= (Class<?>)request.getAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE);
+
+ if (ServletException.class.equals(exClass))
{
- AbstractHttpConnection.getCurrentConnection().getRequest().setHandled(true);
- return;
+ error_page= (String)_errorPages.get(exClass.getName());
+ if (error_page == null)
+ {
+ Throwable th= (Throwable)request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
+ while (th instanceof ServletException)
+ th= ((ServletException)th).getRootCause();
+ if (th != null)
+ exClass= th.getClass();
+ }
}
- if (_errorPages!=null)
+
+ while (error_page == null && exClass != null )
{
- String error_page= null;
- Class<?> exClass= (Class<?>)request.getAttribute(Dispatcher.ERROR_EXCEPTION_TYPE);
+ error_page= (String)_errorPages.get(exClass.getName());
+ exClass= exClass.getSuperclass();
+ }
- if (ServletException.class.equals(exClass))
+ if (error_page == null)
+ {
+ // look for an exact code match
+ Integer code=(Integer)request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
+ if (code!=null)
{
- error_page= (String)_errorPages.get(exClass.getName());
- if (error_page == null)
+ error_page= (String)_errorPages.get(Integer.toString(code));
+
+ // if still not found
+ if ((error_page == null) && (_errorPageList != null))
{
- Throwable th= (Throwable)request.getAttribute(Dispatcher.ERROR_EXCEPTION);
- while (th instanceof ServletException)
- th= ((ServletException)th).getRootCause();
- if (th != null)
- exClass= th.getClass();
- }
- }
-
- while (error_page == null && exClass != null )
- {
- error_page= (String)_errorPages.get(exClass.getName());
- exClass= exClass.getSuperclass();
- }
-
- if (error_page == null)
- {
- // look for an exact code match
- Integer code=(Integer)request.getAttribute(Dispatcher.ERROR_STATUS_CODE);
- if (code!=null)
- {
- error_page= (String)_errorPages.get(Integer.toString(code));
-
- // if still not found
- if ((error_page == null) && (_errorPageList != null))
+ // look for an error code range match.
+ for (int i = 0; i < _errorPageList.size(); i++)
{
- // look for an error code range match.
- for (int i = 0; i < _errorPageList.size(); i++)
+ ErrorCodeRange errCode = (ErrorCodeRange) _errorPageList.get(i);
+ if (errCode.isInRange(code))
{
- ErrorCodeRange errCode = (ErrorCodeRange) _errorPageList.get(i);
- if (errCode.isInRange(code))
- {
- error_page = errCode.getUri();
- break;
- }
+ error_page = errCode.getUri();
+ break;
}
}
}
}
-
- //try new servlet 3.0 global error page
- if (error_page == null)
- {
- error_page = _errorPages.get(GLOBAL_ERROR_PAGE);
- }
-
- if (error_page!=null)
- {
- String old_error_page=(String)request.getAttribute(ERROR_PAGE);
- if (old_error_page==null || !old_error_page.equals(error_page))
- {
- request.setAttribute(ERROR_PAGE, error_page);
-
- Dispatcher dispatcher = (Dispatcher) _servletContext.getRequestDispatcher(error_page);
- try
- {
- if(dispatcher!=null)
- {
- dispatcher.error(request, response);
- return;
- }
- else
- {
- LOG.warn("No error page "+error_page);
- }
- }
- catch (ServletException e)
- {
- LOG.warn(Log.EXCEPTION, e);
- return;
- }
- }
- }
}
- super.handle(target, baseRequest, request, response);
+ //try new servlet 3.0 global error page
+ if (error_page == null)
+ {
+ error_page = _errorPages.get(GLOBAL_ERROR_PAGE);
+ }
+
+ return error_page;
}
/* ------------------------------------------------------------ */
@@ -238,14 +187,6 @@
}
/* ------------------------------------------------------------ */
- @Override
- protected void doStop() throws Exception
- {
- // TODO Auto-generated method stub
- super.doStop();
- }
-
- /* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
private class ErrorCodeRange
{
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterHolder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterHolder.java
index 6e96ae8..5138899 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterHolder.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterHolder.java
@@ -24,12 +24,13 @@
import java.util.EnumSet;
import java.util.List;
+import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
+import javax.servlet.FilterRegistration;
import javax.servlet.ServletException;
-import org.eclipse.jetty.server.DispatcherType;
-import org.eclipse.jetty.servlet.api.FilterRegistration;
+import org.eclipse.jetty.servlet.Holder.Source;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@@ -52,14 +53,23 @@
*/
public FilterHolder()
{
+ this(Source.EMBEDDED);
}
-
+
+ /* ---------------------------------------------------------------- */
+ /** Constructor
+ */
+ public FilterHolder(Holder.Source source)
+ {
+ super(source);
+ }
/* ---------------------------------------------------------------- */
/** Constructor
*/
public FilterHolder(Class<? extends Filter> filter)
{
+ this(Source.EMBEDDED);
setHeldClass(filter);
}
@@ -68,6 +78,7 @@
*/
public FilterHolder(Filter filter)
{
+ this(Source.EMBEDDED);
setFilter(filter);
}
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterMapping.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterMapping.java
index fdab6b0..15c946e 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterMapping.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterMapping.java
@@ -21,8 +21,8 @@
import java.io.IOException;
import java.util.EnumSet;
+import javax.servlet.DispatcherType;
import org.eclipse.jetty.http.PathMap;
-import org.eclipse.jetty.server.DispatcherType;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.component.AggregateLifeCycle;
@@ -172,7 +172,22 @@
}
}
-
+ /* ------------------------------------------------------------ */
+ public EnumSet<DispatcherType> getDispatcherTypes()
+ {
+ EnumSet<DispatcherType> dispatcherTypes = EnumSet.noneOf(DispatcherType.class);
+ if ((_dispatches & ERROR) == ERROR)
+ dispatcherTypes.add(DispatcherType.ERROR);
+ if ((_dispatches & FORWARD) == FORWARD)
+ dispatcherTypes.add(DispatcherType.FORWARD);
+ if ((_dispatches & INCLUDE) == INCLUDE)
+ dispatcherTypes.add(DispatcherType.INCLUDE);
+ if ((_dispatches & REQUEST) == REQUEST)
+ dispatcherTypes.add(DispatcherType.REQUEST);
+ if ((_dispatches & ASYNC) == ASYNC)
+ dispatcherTypes.add(DispatcherType.ASYNC);
+ return dispatcherTypes;
+ }
/* ------------------------------------------------------------ */
/**
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Holder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Holder.java
index 1a83087..c19778c 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Holder.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Holder.java
@@ -26,11 +26,11 @@
import java.util.Map;
import java.util.Set;
+import javax.servlet.Registration;
import javax.servlet.ServletContext;
import javax.servlet.UnavailableException;
import org.eclipse.jetty.server.handler.ContextHandler;
-import org.eclipse.jetty.servlet.api.Registration;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.component.AggregateLifeCycle;
@@ -45,6 +45,8 @@
*/
public class Holder<T> extends AbstractLifeCycle implements Dumpable
{
+ public enum Source { EMBEDDED, JAVAX_API, DESCRIPTOR, ANNOTATION };
+ final private Source _source;
private static final Logger LOG = Log.getLogger(Holder.class);
protected transient Class<? extends T> _class;
@@ -52,17 +54,33 @@
protected String _className;
protected String _displayName;
protected boolean _extInstance;
- protected boolean _asyncSupported=true;
+ protected boolean _asyncSupported;
/* ---------------------------------------------------------------- */
protected String _name;
protected ServletHandler _servletHandler;
/* ---------------------------------------------------------------- */
- protected Holder()
+ protected Holder(Source source)
{
+ _source=source;
+ switch(_source)
+ {
+ case JAVAX_API:
+ case DESCRIPTOR:
+ case ANNOTATION:
+ _asyncSupported=false;
+ break;
+ default:
+ _asyncSupported=true;
+ }
}
-
+
+ public Source getSource()
+ {
+ return _source;
+ }
+
/* ------------------------------------------------------------ */
/**
* @return True if this holder was created for a specific instance.
@@ -79,7 +97,7 @@
{
//if no class already loaded and no classname, make servlet permanently unavailable
if (_class==null && (_className==null || _className.equals("")))
- throw new UnavailableException("No class for Servlet or Filter", -1);
+ throw new UnavailableException("No class for Servlet or Filter for "+_name);
//try to load class
if (_class==null)
@@ -93,7 +111,7 @@
catch (Exception e)
{
LOG.warn(e);
- throw new UnavailableException(e.getMessage(), -1);
+ throw new UnavailableException(e.getMessage());
}
}
}
@@ -343,6 +361,12 @@
public boolean setInitParameter(String name, String value)
{
illegalStateIfContextStarted();
+ if (name == null) {
+ throw new IllegalArgumentException("init parameter name required");
+ }
+ if (value == null) {
+ throw new IllegalArgumentException("non-null value required for init parameter " + name);
+ }
if (Holder.this.getInitParameter(name)!=null)
return false;
Holder.this.setInitParameter(name,value);
@@ -353,20 +377,28 @@
{
illegalStateIfContextStarted();
Set<String> clash=null;
- for (String name : initParameters.keySet())
+ for (Map.Entry<String, String> entry : initParameters.entrySet())
{
- if (Holder.this.getInitParameter(name)!=null)
+ if (entry.getKey() == null) {
+ throw new IllegalArgumentException("init parameter name required");
+ }
+ if (entry.getValue() == null) {
+ throw new IllegalArgumentException("non-null value required for init parameter " + entry.getKey());
+ }
+ if (Holder.this.getInitParameter(entry.getKey())!=null)
{
if (clash==null)
clash=new HashSet<String>();
- clash.add(name);
+ clash.add(entry.getKey());
}
}
if (clash!=null)
return clash;
- Holder.this.setInitParameters(initParameters);
+ Holder.this.getInitParameters().putAll(initParameters);
return Collections.emptySet();
- };
+ }
+
+
}
}
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java
index 59eeeae..a29b9dc 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java
@@ -20,6 +20,7 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.EventListener;
@@ -29,19 +30,28 @@
import java.util.Map;
import java.util.Set;
+import javax.servlet.DispatcherType;
import javax.servlet.Filter;
+import javax.servlet.FilterRegistration;
import javax.servlet.RequestDispatcher;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletException;
+import javax.servlet.ServletRegistration;
+import javax.servlet.ServletSecurityElement;
+import javax.servlet.SessionCookieConfig;
+import javax.servlet.SessionTrackingMode;
+import javax.servlet.descriptor.JspConfigDescriptor;
+import javax.servlet.descriptor.JspPropertyGroupDescriptor;
+import javax.servlet.descriptor.TaglibDescriptor;
import org.eclipse.jetty.security.ConstraintAware;
+import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.security.SecurityHandler;
import org.eclipse.jetty.server.Dispatcher;
-import org.eclipse.jetty.server.DispatcherType;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HandlerContainer;
import org.eclipse.jetty.server.handler.ContextHandler;
@@ -49,9 +59,8 @@
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.server.session.SessionHandler;
-import org.eclipse.jetty.servlet.api.FilterRegistration;
-import org.eclipse.jetty.servlet.api.ServletRegistration;
-import org.eclipse.jetty.util.Loader;
+import org.eclipse.jetty.util.LazyList;
+import org.eclipse.jetty.util.security.Constraint;
/* ------------------------------------------------------------ */
@@ -79,8 +88,10 @@
protected ServletHandler _servletHandler;
protected HandlerWrapper _wrapper;
protected int _options;
+ protected JspConfigDescriptor _jspConfig;
protected Object _restrictedContextListeners;
-
+ private boolean _restrictListeners = true;
+
/* ------------------------------------------------------------ */
public ServletContextHandler()
{
@@ -336,46 +347,96 @@
{
return getServletHandler().addFilterWithMapping(filterClass,pathSpec,dispatches);
}
+
+ /**
+ * notification that a ServletRegistration has been created so we can track the annotations
+ * @param holder new holder created through the api.
+ * @return the ServletRegistration.Dynamic
+ */
+ protected ServletRegistration.Dynamic dynamicHolderAdded(ServletHolder holder) {
+ return holder.getRegistration();
+ }
+
+ /**
+ * delegate for ServletContext.declareRole method
+ * @param roleNames role names to add
+ */
+ protected void addRoles(String... roleNames) {
+ //Get a reference to the SecurityHandler, which must be ConstraintAware
+ if (_securityHandler != null && _securityHandler instanceof ConstraintAware)
+ {
+ HashSet<String> union = new HashSet<String>();
+ Set<String> existing = ((ConstraintAware)_securityHandler).getRoles();
+ if (existing != null)
+ union.addAll(existing);
+ union.addAll(Arrays.asList(roleNames));
+ ((ConstraintSecurityHandler)_securityHandler).setRoles(union);
+ }
+ }
+
+ /**
+ * Delegate for ServletRegistration.Dynamic.setServletSecurity method
+ * @param registration ServletRegistration.Dynamic instance that setServletSecurity was called on
+ * @param servletSecurityElement new security info
+ * @return the set of exact URL mappings currently associated with the registration that are also present in the web.xml
+ * security constraints and thus will be unaffected by this call.
+ */
+ public Set<String> setServletSecurity(ServletRegistration.Dynamic registration, ServletSecurityElement servletSecurityElement)
+ {
+ //Default implementation is to just accept them all. If using a webapp, then this behaviour is overridden in WebAppContext.setServletSecurity
+ Collection<String> pathSpecs = registration.getMappings();
+ if (pathSpecs != null)
+ {
+ for (String pathSpec:pathSpecs)
+ {
+ List<ConstraintMapping> mappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath(registration.getName(), pathSpec, servletSecurityElement);
+ for (ConstraintMapping m:mappings)
+ ((ConstraintAware)getSecurityHandler()).addConstraintMapping(m);
+ }
+ }
+ return Collections.emptySet();
+ }
+
+
-
- /* ------------------------------------------------------------ */
- /** conveniance method to add a filter
- */
- public void addFilter(FilterHolder holder,String pathSpec,int dispatches)
+ public void restrictEventListener (EventListener e)
{
- getServletHandler().addFilterWithMapping(holder,pathSpec,dispatches);
+ if (_restrictListeners && e instanceof ServletContextListener)
+ _restrictedContextListeners = LazyList.add(_restrictedContextListeners, e);
}
- /* ------------------------------------------------------------ */
- /** convenience method to add a filter
- */
- public FilterHolder addFilter(Class<? extends Filter> filterClass,String pathSpec,int dispatches)
- {
- return getServletHandler().addFilterWithMapping(filterClass,pathSpec,dispatches);
+ public boolean isRestrictListeners() {
+ return _restrictListeners;
}
- /* ------------------------------------------------------------ */
- /** convenience method to add a filter
- */
- public FilterHolder addFilter(String filterClass,String pathSpec,int dispatches)
- {
- return getServletHandler().addFilterWithMapping(filterClass,pathSpec,dispatches);
+ public void setRestrictListeners(boolean restrictListeners) {
+ this._restrictListeners = restrictListeners;
}
-
-
public void callContextInitialized(ServletContextListener l, ServletContextEvent e)
- {
- l.contextInitialized(e);
+ {
+ try
+ {
+ //toggle state of the dynamic API so that the listener cannot use it
+ if (LazyList.contains(_restrictedContextListeners, l))
+ this.getServletContext().setEnabled(false);
+
+ super.callContextInitialized(l, e);
+ }
+ finally
+ {
+ //untoggle the state of the dynamic API
+ this.getServletContext().setEnabled(true);
+ }
}
-
+
public void callContextDestroyed(ServletContextListener l, ServletContextEvent e)
{
- l.contextDestroyed(e);
+ super.callContextDestroyed(l, e);
}
-
+
/* ------------------------------------------------------------ */
/**
@@ -456,6 +517,290 @@
}
/* ------------------------------------------------------------ */
+ public static class JspPropertyGroup implements JspPropertyGroupDescriptor
+ {
+ private List<String> _urlPatterns = new ArrayList<String>();
+ private String _elIgnored;
+ private String _pageEncoding;
+ private String _scriptingInvalid;
+ private String _isXml;
+ private List<String> _includePreludes = new ArrayList<String>();
+ private List<String> _includeCodas = new ArrayList<String>();
+ private String _deferredSyntaxAllowedAsLiteral;
+ private String _trimDirectiveWhitespaces;
+ private String _defaultContentType;
+ private String _buffer;
+ private String _errorOnUndeclaredNamespace;
+
+
+
+ /**
+ * @see javax.servlet.descriptor.JspPropertyGroupDescriptor#getUrlPatterns()
+ */
+ public Collection<String> getUrlPatterns()
+ {
+ return new ArrayList<String>(_urlPatterns); // spec says must be a copy
+ }
+
+ public void addUrlPattern (String s)
+ {
+ if (!_urlPatterns.contains(s))
+ _urlPatterns.add(s);
+ }
+
+ /**
+ * @see javax.servlet.descriptor.JspPropertyGroupDescriptor#getElIgnored()
+ */
+ public String getElIgnored()
+ {
+ return _elIgnored;
+ }
+
+ public void setElIgnored (String s)
+ {
+ _elIgnored = s;
+ }
+
+ /**
+ * @see javax.servlet.descriptor.JspPropertyGroupDescriptor#getPageEncoding()
+ */
+ public String getPageEncoding()
+ {
+ return _pageEncoding;
+ }
+
+ public void setPageEncoding(String pageEncoding)
+ {
+ _pageEncoding = pageEncoding;
+ }
+
+ public void setScriptingInvalid(String scriptingInvalid)
+ {
+ _scriptingInvalid = scriptingInvalid;
+ }
+
+ public void setIsXml(String isXml)
+ {
+ _isXml = isXml;
+ }
+
+ public void setDeferredSyntaxAllowedAsLiteral(String deferredSyntaxAllowedAsLiteral)
+ {
+ _deferredSyntaxAllowedAsLiteral = deferredSyntaxAllowedAsLiteral;
+ }
+
+ public void setTrimDirectiveWhitespaces(String trimDirectiveWhitespaces)
+ {
+ _trimDirectiveWhitespaces = trimDirectiveWhitespaces;
+ }
+
+ public void setDefaultContentType(String defaultContentType)
+ {
+ _defaultContentType = defaultContentType;
+ }
+
+ public void setBuffer(String buffer)
+ {
+ _buffer = buffer;
+ }
+
+ public void setErrorOnUndeclaredNamespace(String errorOnUndeclaredNamespace)
+ {
+ _errorOnUndeclaredNamespace = errorOnUndeclaredNamespace;
+ }
+
+ /**
+ * @see javax.servlet.descriptor.JspPropertyGroupDescriptor#getScriptingInvalid()
+ */
+ public String getScriptingInvalid()
+ {
+ return _scriptingInvalid;
+ }
+
+ /**
+ * @see javax.servlet.descriptor.JspPropertyGroupDescriptor#getIsXml()
+ */
+ public String getIsXml()
+ {
+ return _isXml;
+ }
+
+ /**
+ * @see javax.servlet.descriptor.JspPropertyGroupDescriptor#getIncludePreludes()
+ */
+ public Collection<String> getIncludePreludes()
+ {
+ return new ArrayList<String>(_includePreludes); //must be a copy
+ }
+
+ public void addIncludePrelude(String prelude)
+ {
+ if (!_includePreludes.contains(prelude))
+ _includePreludes.add(prelude);
+ }
+
+ /**
+ * @see javax.servlet.descriptor.JspPropertyGroupDescriptor#getIncludeCodas()
+ */
+ public Collection<String> getIncludeCodas()
+ {
+ return new ArrayList<String>(_includeCodas); //must be a copy
+ }
+
+ public void addIncludeCoda (String coda)
+ {
+ if (!_includeCodas.contains(coda))
+ _includeCodas.add(coda);
+ }
+
+ /**
+ * @see javax.servlet.descriptor.JspPropertyGroupDescriptor#getDeferredSyntaxAllowedAsLiteral()
+ */
+ public String getDeferredSyntaxAllowedAsLiteral()
+ {
+ return _deferredSyntaxAllowedAsLiteral;
+ }
+
+ /**
+ * @see javax.servlet.descriptor.JspPropertyGroupDescriptor#getTrimDirectiveWhitespaces()
+ */
+ public String getTrimDirectiveWhitespaces()
+ {
+ return _trimDirectiveWhitespaces;
+ }
+
+ /**
+ * @see javax.servlet.descriptor.JspPropertyGroupDescriptor#getDefaultContentType()
+ */
+ public String getDefaultContentType()
+ {
+ return _defaultContentType;
+ }
+
+ /**
+ * @see javax.servlet.descriptor.JspPropertyGroupDescriptor#getBuffer()
+ */
+ public String getBuffer()
+ {
+ return _buffer;
+ }
+
+ /**
+ * @see javax.servlet.descriptor.JspPropertyGroupDescriptor#getErrorOnUndeclaredNamespace()
+ */
+ public String getErrorOnUndeclaredNamespace()
+ {
+ return _errorOnUndeclaredNamespace;
+ }
+
+ public String toString ()
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("JspPropertyGroupDescriptor:");
+ sb.append(" el-ignored="+_elIgnored);
+ sb.append(" is-xml="+_isXml);
+ sb.append(" page-encoding="+_pageEncoding);
+ sb.append(" scripting-invalid="+_scriptingInvalid);
+ sb.append(" deferred-syntax-allowed-as-literal="+_deferredSyntaxAllowedAsLiteral);
+ sb.append(" trim-directive-whitespaces"+_trimDirectiveWhitespaces);
+ sb.append(" default-content-type="+_defaultContentType);
+ sb.append(" buffer="+_buffer);
+ sb.append(" error-on-undeclared-namespace="+_errorOnUndeclaredNamespace);
+ for (String prelude:_includePreludes)
+ sb.append(" include-prelude="+prelude);
+ for (String coda:_includeCodas)
+ sb.append(" include-coda="+coda);
+ return sb.toString();
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ public static class TagLib implements TaglibDescriptor
+ {
+ private String _uri;
+ private String _location;
+
+ /**
+ * @see javax.servlet.descriptor.TaglibDescriptor#getTaglibURI()
+ */
+ public String getTaglibURI()
+ {
+ return _uri;
+ }
+
+ public void setTaglibURI(String uri)
+ {
+ _uri = uri;
+ }
+
+ /**
+ * @see javax.servlet.descriptor.TaglibDescriptor#getTaglibLocation()
+ */
+ public String getTaglibLocation()
+ {
+ return _location;
+ }
+
+ public void setTaglibLocation(String location)
+ {
+ _location = location;
+ }
+
+ public String toString()
+ {
+ return ("TagLibDescriptor: taglib-uri="+_uri+" location="+_location);
+ }
+ }
+
+
+ /* ------------------------------------------------------------ */
+ public static class JspConfig implements JspConfigDescriptor
+ {
+ private List<TaglibDescriptor> _taglibs = new ArrayList<TaglibDescriptor>();
+ private List<JspPropertyGroupDescriptor> _jspPropertyGroups = new ArrayList<JspPropertyGroupDescriptor>();
+
+ public JspConfig() {}
+
+ /**
+ * @see javax.servlet.descriptor.JspConfigDescriptor#getTaglibs()
+ */
+ public Collection<TaglibDescriptor> getTaglibs()
+ {
+ return new ArrayList<TaglibDescriptor>(_taglibs);
+ }
+
+ public void addTaglibDescriptor (TaglibDescriptor d)
+ {
+ _taglibs.add(d);
+ }
+
+ /**
+ * @see javax.servlet.descriptor.JspConfigDescriptor#getJspPropertyGroups()
+ */
+ public Collection<JspPropertyGroupDescriptor> getJspPropertyGroups()
+ {
+ return new ArrayList<JspPropertyGroupDescriptor>(_jspPropertyGroups);
+ }
+
+ public void addJspPropertyGroup(JspPropertyGroupDescriptor g)
+ {
+ _jspPropertyGroups.add(g);
+ }
+
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("JspConfigDescriptor: \n");
+ for (TaglibDescriptor taglib:_taglibs)
+ sb.append(taglib+"\n");
+ for (JspPropertyGroupDescriptor jpg:_jspPropertyGroups)
+ sb.append(jpg+"\n");
+ return sb.toString();
+ }
+ }
+
+
+ /* ------------------------------------------------------------ */
public class Context extends ContextHandler.Context
{
/* ------------------------------------------------------------ */
@@ -466,7 +811,10 @@
public RequestDispatcher getNamedDispatcher(String name)
{
ContextHandler context=org.eclipse.jetty.servlet.ServletContextHandler.this;
- if (_servletHandler==null || _servletHandler.getServlet(name)==null)
+ if (_servletHandler==null)
+ return null;
+ ServletHolder holder = _servletHandler.getServlet(name);
+ if (holder==null || !holder.isEnabled())
return null;
return new Dispatcher(context, name);
}
@@ -475,34 +823,68 @@
/**
* @since servlet-api-3.0
*/
+ @Override
public FilterRegistration.Dynamic addFilter(String filterName, Class<? extends Filter> filterClass)
{
if (isStarted())
throw new IllegalStateException();
+
+ if (!_enabled)
+ throw new UnsupportedOperationException();
final ServletHandler handler = ServletContextHandler.this.getServletHandler();
- final FilterHolder holder= handler.newFilterHolder();
- holder.setName(filterName);
- holder.setHeldClass(filterClass);
- handler.addFilter(holder);
- return holder.getRegistration();
+ FilterHolder holder = handler.getFilter(filterName);
+ if (holder == null)
+ {
+ //new filter
+ holder = handler.newFilterHolder(Holder.Source.JAVAX_API);
+ holder.setName(filterName);
+ holder.setHeldClass(filterClass);
+ handler.addFilter(holder);
+ return holder.getRegistration();
+ }
+ if (holder.getClassName()==null && holder.getHeldClass()==null)
+ {
+ //preliminary filter registration completion
+ holder.setHeldClass(filterClass);
+ return holder.getRegistration();
+ }
+ else
+ return null; //existing filter
}
/* ------------------------------------------------------------ */
/**
* @since servlet-api-3.0
*/
+ @Override
public FilterRegistration.Dynamic addFilter(String filterName, String className)
{
if (isStarted())
throw new IllegalStateException();
+
+ if (!_enabled)
+ throw new UnsupportedOperationException();
final ServletHandler handler = ServletContextHandler.this.getServletHandler();
- final FilterHolder holder= handler.newFilterHolder();
- holder.setName(filterName);
- holder.setClassName(className);
- handler.addFilter(holder);
- return holder.getRegistration();
+ FilterHolder holder = handler.getFilter(filterName);
+ if (holder == null)
+ {
+ //new filter
+ holder = handler.newFilterHolder(Holder.Source.JAVAX_API);
+ holder.setName(filterName);
+ holder.setClassName(className);
+ handler.addFilter(holder);
+ return holder.getRegistration();
+ }
+ if (holder.getClassName()==null && holder.getHeldClass()==null)
+ {
+ //preliminary filter registration completion
+ holder.setClassName(className);
+ return holder.getRegistration();
+ }
+ else
+ return null; //existing filter
}
@@ -510,81 +892,158 @@
/**
* @since servlet-api-3.0
*/
+ @Override
public FilterRegistration.Dynamic addFilter(String filterName, Filter filter)
{
if (isStarted())
throw new IllegalStateException();
+ if (!_enabled)
+ throw new UnsupportedOperationException();
+
final ServletHandler handler = ServletContextHandler.this.getServletHandler();
- final FilterHolder holder= handler.newFilterHolder();
- holder.setName(filterName);
- holder.setFilter(filter);
- handler.addFilter(holder);
- return holder.getRegistration();
+ FilterHolder holder = handler.getFilter(filterName);
+ if (holder == null)
+ {
+ //new filter
+ holder = handler.newFilterHolder(Holder.Source.JAVAX_API);
+ holder.setName(filterName);
+ holder.setFilter(filter);
+ handler.addFilter(holder);
+ return holder.getRegistration();
+ }
+
+ if (holder.getClassName()==null && holder.getHeldClass()==null)
+ {
+ //preliminary filter registration completion
+ holder.setFilter(filter);
+ return holder.getRegistration();
+ }
+ else
+ return null; //existing filter
}
/* ------------------------------------------------------------ */
/**
* @since servlet-api-3.0
*/
+ @Override
public ServletRegistration.Dynamic addServlet(String servletName, Class<? extends Servlet> servletClass)
{
if (!isStarting())
throw new IllegalStateException();
-
+
+ if (!_enabled)
+ throw new UnsupportedOperationException();
+
final ServletHandler handler = ServletContextHandler.this.getServletHandler();
- final ServletHolder holder= handler.newServletHolder();
- holder.setName(servletName);
- holder.setHeldClass(servletClass);
- handler.addServlet(holder);
- return holder.getRegistration();
+ ServletHolder holder = handler.getServlet(servletName);
+ if (holder == null)
+ {
+ //new servlet
+ holder = handler.newServletHolder(Holder.Source.JAVAX_API);
+ holder.setName(servletName);
+ holder.setHeldClass(servletClass);
+ handler.addServlet(holder);
+ return dynamicHolderAdded(holder);
+ }
+
+ //complete a partial registration
+ if (holder.getClassName()==null && holder.getHeldClass()==null)
+ {
+ holder.setHeldClass(servletClass);
+ return holder.getRegistration();
+ }
+ else
+ return null; //existing completed registration for servlet name
}
/* ------------------------------------------------------------ */
/**
* @since servlet-api-3.0
*/
+ @Override
public ServletRegistration.Dynamic addServlet(String servletName, String className)
{
if (!isStarting())
throw new IllegalStateException();
- final ServletHandler handler = ServletContextHandler.this.getServletHandler();
- final ServletHolder holder= handler.newServletHolder();
- holder.setName(servletName);
- holder.setClassName(className);
- handler.addServlet(holder);
- return holder.getRegistration();
+ if (!_enabled)
+ throw new UnsupportedOperationException();
+
+
+ final ServletHandler handler = ServletContextHandler.this.getServletHandler();
+ ServletHolder holder = handler.getServlet(servletName);
+ if (holder == null)
+ {
+ //new servlet
+ holder = handler.newServletHolder(Holder.Source.JAVAX_API);
+ holder.setName(servletName);
+ holder.setClassName(className);
+ handler.addServlet(holder);
+ return dynamicHolderAdded(holder);
+ }
+
+ //complete a partial registration
+ if (holder.getClassName()==null && holder.getHeldClass()==null)
+ {
+ holder.setClassName(className);
+ return holder.getRegistration();
+ }
+ else
+ return null; //existing completed registration for servlet name
}
/* ------------------------------------------------------------ */
/**
* @since servlet-api-3.0
*/
+ @Override
public ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet)
{
if (!isStarting())
throw new IllegalStateException();
+ if (!_enabled)
+ throw new UnsupportedOperationException();
+
final ServletHandler handler = ServletContextHandler.this.getServletHandler();
- final ServletHolder holder= handler.newServletHolder();
- holder.setName(servletName);
- holder.setServlet(servlet);
- handler.addServlet(holder);
- return holder.getRegistration();
+ ServletHolder holder = handler.getServlet(servletName);
+ if (holder == null)
+ {
+ holder = handler.newServletHolder(Holder.Source.JAVAX_API);
+ holder.setName(servletName);
+ holder.setServlet(servlet);
+ handler.addServlet(holder);
+ return dynamicHolderAdded(holder);
+ }
+
+ //complete a partial registration
+ if (holder.getClassName()==null && holder.getHeldClass()==null)
+ {
+ holder.setServlet(servlet);
+ return holder.getRegistration();
+ }
+ else
+ return null; //existing completed registration for servlet name
}
/* ------------------------------------------------------------ */
+ @Override
public boolean setInitParameter(String name, String value)
{
// TODO other started conditions
if (!isStarting())
throw new IllegalStateException();
+ if (!_enabled)
+ throw new UnsupportedOperationException();
+
return super.setInitParameter(name,value);
}
/* ------------------------------------------------------------ */
+ @Override
public <T extends Filter> T createFilter(Class<T> c) throws ServletException
{
try
@@ -608,6 +1067,7 @@
}
/* ------------------------------------------------------------ */
+ @Override
public <T extends Servlet> T createServlet(Class<T> c) throws ServletException
{
try
@@ -629,16 +1089,39 @@
throw new ServletException(e);
}
}
-
+
+ @Override
+ public Set<SessionTrackingMode> getDefaultSessionTrackingModes()
+ {
+ if (_sessionHandler!=null)
+ return _sessionHandler.getSessionManager().getDefaultSessionTrackingModes();
+ return null;
+ }
+
+ @Override
+ public Set<SessionTrackingMode> getEffectiveSessionTrackingModes()
+ {
+ if (_sessionHandler!=null)
+ return _sessionHandler.getSessionManager().getEffectiveSessionTrackingModes();
+ return null;
+ }
+
+ @Override
public FilterRegistration getFilterRegistration(String filterName)
- {
+ {
+ if (!_enabled)
+ throw new UnsupportedOperationException();
+
final FilterHolder holder=ServletContextHandler.this.getServletHandler().getFilter(filterName);
return (holder==null)?null:holder.getRegistration();
}
-
+ @Override
public Map<String, ? extends FilterRegistration> getFilterRegistrations()
{
+ if (!_enabled)
+ throw new UnsupportedOperationException();
+
HashMap<String, FilterRegistration> registrations = new HashMap<String, FilterRegistration>();
ServletHandler handler=ServletContextHandler.this.getServletHandler();
FilterHolder[] holders=handler.getFilters();
@@ -650,16 +1133,22 @@
return registrations;
}
-
+ @Override
public ServletRegistration getServletRegistration(String servletName)
- {
+ {
+ if (!_enabled)
+ throw new UnsupportedOperationException();
+
final ServletHolder holder=ServletContextHandler.this.getServletHandler().getServlet(servletName);
return (holder==null)?null:holder.getRegistration();
}
-
+ @Override
public Map<String, ? extends ServletRegistration> getServletRegistrations()
- {
+ {
+ if (!_enabled)
+ throw new UnsupportedOperationException();
+
HashMap<String, ServletRegistration> registrations = new HashMap<String, ServletRegistration>();
ServletHandler handler=ServletContextHandler.this.getServletHandler();
ServletHolder[] holders=handler.getServlets();
@@ -671,67 +1160,71 @@
return registrations;
}
-
+ @Override
+ public SessionCookieConfig getSessionCookieConfig()
+ {
+ // TODO other started conditions
+ if (!_enabled)
+ throw new UnsupportedOperationException();
+
+ if (_sessionHandler!=null)
+ return _sessionHandler.getSessionManager().getSessionCookieConfig();
+ return null;
+ }
+
+ @Override
+ public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes)
+ {
+ // TODO other started conditions
+ if (!isStarting())
+ throw new IllegalStateException();
+ if (!_enabled)
+ throw new UnsupportedOperationException();
+
+
+ if (_sessionHandler!=null)
+ _sessionHandler.getSessionManager().setSessionTrackingModes(sessionTrackingModes);
+ }
+
+ @Override
public void addListener(String className)
{
// TODO other started conditions
if (!isStarting())
throw new IllegalStateException();
- try
- {
- Class<? extends EventListener> clazz = getClassLoader()==null?Loader.loadClass(ContextHandler.class,className):getClassLoader().loadClass(className);
- addListener(clazz);
- }
- catch (ClassNotFoundException e)
- {
- throw new IllegalArgumentException(e);
- }
+ if (!_enabled)
+ throw new UnsupportedOperationException();
+ super.addListener(className);
}
-
+ @Override
public <T extends EventListener> void addListener(T t)
{
+ // TODO other started conditions
if (!isStarting())
throw new IllegalStateException();
-
- ServletContextHandler.this.addEventListener(t);
+ if (!_enabled)
+ throw new UnsupportedOperationException();
+ super.addListener(t);
}
-
+ @Override
public void addListener(Class<? extends EventListener> listenerClass)
{
+ // TODO other started conditions
if (!isStarting())
throw new IllegalStateException();
-
- try
- {
- EventListener l = createListener(listenerClass);
- ServletContextHandler.this.addEventListener(l);
- }
- catch (ServletException e)
- {
- throw new IllegalStateException(e);
- }
+ if (!_enabled)
+ throw new UnsupportedOperationException();
+ super.addListener(listenerClass);
}
-
+ @Override
public <T extends EventListener> T createListener(Class<T> clazz) throws ServletException
{
try
{
- T l = null;
- try
- {
- l = clazz.newInstance();
- }
- catch (InstantiationException e)
- {
- throw new ServletException(e);
- }
- catch (IllegalAccessException e)
- {
- throw new ServletException(e);
- }
+ T l = super.createListener(clazz);
for (int i=_decorators.size()-1; i>=0; i--)
{
@@ -750,26 +1243,36 @@
}
}
-
+
+ @Override
+ public JspConfigDescriptor getJspConfigDescriptor()
+ {
+ return _jspConfig;
+ }
+
+ @Override
+ public void setJspConfigDescriptor(JspConfigDescriptor d)
+ {
+ _jspConfig = d;
+ }
+
+
+ @Override
public void declareRoles(String... roleNames)
{
if (!isStarting())
throw new IllegalStateException();
-
- //Get a reference to the SecurityHandler, which must be ConstraintAware
- if (_securityHandler != null && _securityHandler instanceof ConstraintAware)
- {
- HashSet<String> union = new HashSet<String>();
- Set<String> existing = ((ConstraintAware)_securityHandler).getRoles();
- if (existing != null)
- union.addAll(existing);
- union.addAll(Arrays.asList(roleNames));
- ((ConstraintSecurityHandler)_securityHandler).setRoles(union);
- }
+ if (!_enabled)
+ throw new UnsupportedOperationException();
+ addRoles(roleNames);
+
+
}
+
}
-
-
+
+
+
/* ------------------------------------------------------------ */
/** Interface to decorate loaded classes.
*/
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java
index 87708cf..bb160f2 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java
@@ -21,21 +21,29 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
+import java.util.ListIterator;
import java.util.Map;
import java.util.Queue;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap;
+
+import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
+import javax.servlet.RequestDispatcher;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
+import javax.servlet.ServletRegistration;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
+import javax.servlet.ServletSecurityElement;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -48,8 +56,8 @@
import org.eclipse.jetty.security.IdentityService;
import org.eclipse.jetty.security.SecurityHandler;
import org.eclipse.jetty.server.AbstractHttpConnection;
+import org.eclipse.jetty.server.AsyncContinuation;
import org.eclipse.jetty.server.Dispatcher;
-import org.eclipse.jetty.server.DispatcherType;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServletRequestHttpWrapper;
@@ -57,6 +65,7 @@
import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ScopedHandler;
+import org.eclipse.jetty.servlet.Holder.Source;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.MultiException;
import org.eclipse.jetty.util.MultiMap;
@@ -80,6 +89,7 @@
public class ServletHandler extends ScopedHandler
{
private static final Logger LOG = Log.getLogger(ServletHandler.class);
+ private static final Logger LOG_UNHANDLED = LOG.getLogger("unhandled");
/* ------------------------------------------------------------ */
public static final String __DEFAULT_SERVLET="default";
@@ -89,9 +99,11 @@
private ContextHandler.Context _servletContext;
private FilterHolder[] _filters=new FilterHolder[0];
private FilterMapping[] _filterMappings;
+ private int _matchBeforeIndex = -1; //index of last programmatic FilterMapping with isMatchAfter=false
+ private int _matchAfterIndex = -1; //index of 1st programmatic FilterMapping with isMatchAfter=true
private boolean _filterChainsCached=true;
private int _maxFilterChainsCacheSize=512;
- private boolean _startWithUnavailable=true;
+ private boolean _startWithUnavailable=false;
private IdentityService _identityService;
private ServletHolder[] _servlets=new ServletHolder[0];
@@ -186,33 +198,75 @@
throws Exception
{
super.doStop();
-
+
// Stop filters
+ List<FilterHolder> filterHolders = new ArrayList<FilterHolder>();
+ List<FilterMapping> filterMappings = LazyList.array2List(_filterMappings);
if (_filters!=null)
{
for (int i=_filters.length; i-->0;)
{
try { _filters[i].stop(); }catch(Exception e){LOG.warn(Log.EXCEPTION,e);}
+ if (_filters[i].getSource() != Source.EMBEDDED)
+ {
+ //remove all of the mappings that were for non-embedded filters
+ _filterNameMap.remove(_filters[i].getName());
+ //remove any mappings associated with this filter
+ ListIterator<FilterMapping> fmitor = filterMappings.listIterator();
+ while (fmitor.hasNext())
+ {
+ FilterMapping fm = fmitor.next();
+ if (fm.getFilterName().equals(_filters[i].getName()))
+ fmitor.remove();
+ }
+ }
+ else
+ filterHolders.add(_filters[i]); //only retain embedded
}
}
-
+ _filters = (FilterHolder[]) LazyList.toArray(filterHolders, FilterHolder.class);
+ _filterMappings = (FilterMapping[]) LazyList.toArray(filterMappings, FilterMapping.class);
+ _matchAfterIndex = (_filterMappings == null || _filterMappings.length == 0 ? -1 : _filterMappings.length-1);
+ _matchBeforeIndex = -1;
+
+
// Stop servlets
+ List<ServletHolder> servletHolders = new ArrayList<ServletHolder>(); //will be remaining servlets
+ List<ServletMapping> servletMappings = LazyList.array2List(_servletMappings); //will be remaining mappings
if (_servlets!=null)
{
for (int i=_servlets.length; i-->0;)
{
try { _servlets[i].stop(); }catch(Exception e){LOG.warn(Log.EXCEPTION,e);}
+ if (_servlets[i].getSource() != Source.EMBEDDED)
+ {
+ //remove from servlet name map
+ _servletNameMap.remove(_servlets[i].getName());
+ //remove any mappings associated with this servlet
+ ListIterator<ServletMapping> smitor = servletMappings.listIterator();
+ while (smitor.hasNext())
+ {
+ ServletMapping sm = smitor.next();
+ if (sm.getServletName().equals(_servlets[i].getName()))
+ smitor.remove();
+ }
+ }
+ else
+ servletHolders.add(_servlets[i]); //only retain embedded
}
}
+ _servlets = (ServletHolder[]) LazyList.toArray(servletHolders, ServletHolder.class);
+ _servletMappings = (ServletMapping[])LazyList.toArray(servletMappings, ServletMapping.class);
+
+ //will be regenerated on next start
_filterPathMappings=null;
- _filterNameMappings=null;
-
+ _filterNameMappings=null;
_servletPathMap=null;
}
/* ------------------------------------------------------------ */
- IdentityService getIdentityService()
+ protected IdentityService getIdentityService()
{
return _identityService;
}
@@ -277,6 +331,7 @@
*/
public ServletMapping getServletMapping(String pattern)
{
+ ServletMapping theMapping = null;
if (_servletMappings!=null)
{
for (ServletMapping m:_servletMappings)
@@ -287,12 +342,12 @@
for (String path:paths)
{
if (pattern.equals(path))
- return m;
+ theMapping = m;
}
}
}
}
- return null;
+ return theMapping;
}
/* ------------------------------------------------------------ */
@@ -334,8 +389,8 @@
String servlet_path_spec=(String)entry.getKey();
String servlet_path=entry.getMapped()!=null?entry.getMapped():PathMap.pathMatch(servlet_path_spec,target);
- String path_info=PathMap.pathInfo(servlet_path_spec,target);
-
+ String path_info=PathMap.pathInfo(servlet_path_spec,target);
+
if (DispatcherType.INCLUDE.equals(type))
{
baseRequest.setAttribute(Dispatcher.INCLUDE_SERVLET_PATH,servlet_path);
@@ -353,7 +408,7 @@
// look for a servlet by name!
servlet_holder=(ServletHolder)_servletNameMap.get(target);
}
-
+
if (LOG.isDebugEnabled())
LOG.debug("servlet {}|{}|{} -> {}",baseRequest.getContextPath(),baseRequest.getServletPath(),baseRequest.getPathInfo(),servlet_holder);
@@ -418,7 +473,8 @@
}
LOG.debug("chain={}",chain);
-
+
+ Throwable th=null;
try
{
if (servlet_holder==null)
@@ -470,7 +526,7 @@
}
// unwrap cause
- Throwable th=e;
+ th=e;
if (th instanceof UnavailableException)
{
LOG.debug(th);
@@ -502,13 +558,17 @@
}
else
{
- LOG.warn(request.getRequestURI(),th);
+ LOG.warn("Error Processing URI: {} - ({}) {}",request.getRequestURI(),th.getClass().getName(),th.getMessage());
+ if (LOG_UNHANDLED.isDebugEnabled())
+ {
+ LOG_UNHANDLED.debug(request.getRequestURI(),th);
+ }
}
+ request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,th.getClass());
+ request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,th);
if (!response.isCommitted())
{
- request.setAttribute(Dispatcher.ERROR_EXCEPTION_TYPE,th.getClass());
- request.setAttribute(Dispatcher.ERROR_EXCEPTION,th);
if (th instanceof UnavailableException)
{
UnavailableException ue = (UnavailableException)th;
@@ -522,21 +582,20 @@
}
else
LOG.debug("Response already committed for handling "+th);
+
}
catch(Error e)
{
if (!(DispatcherType.REQUEST.equals(type) || DispatcherType.ASYNC.equals(type)))
throw e;
+ th=e;
LOG.warn("Error for "+request.getRequestURI(),e);
if(LOG.isDebugEnabled())LOG.debug(request.toString());
- // TODO httpResponse.getHttpConnection().forceClose();
+ request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,e.getClass());
+ request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,e);
if (!response.isCommitted())
- {
- request.setAttribute(Dispatcher.ERROR_EXCEPTION_TYPE,e.getClass());
- request.setAttribute(Dispatcher.ERROR_EXCEPTION,e);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
- }
else
LOG.debug("Response already committed for handling ",e);
}
@@ -544,11 +603,15 @@
{
if (servlet_holder!=null)
baseRequest.setHandled(true);
+
+ // Complete async requests
+ if (th!=null && request.isAsyncStarted())
+ ((AsyncContinuation)request.getAsyncContext()).errorComplete();
}
}
/* ------------------------------------------------------------ */
- private FilterChain getFilterChain(Request baseRequest, String pathInContext, ServletHolder servletHolder)
+ protected FilterChain getFilterChain(Request baseRequest, String pathInContext, ServletHolder servletHolder)
{
String key=pathInContext==null?servletHolder.getName():pathInContext;
int dispatch = FilterMapping.dispatch(baseRequest.getDispatcherType());
@@ -605,7 +668,7 @@
if (_filterChainsCached)
{
if (LazyList.size(filters) > 0)
- chain= new CachedChain(filters, servletHolder);
+ chain = newCachedChain(filters, servletHolder);
final Map<String,FilterChain> cache=_chainCache[dispatch];
final Queue<String> lru=_chainLRU[dispatch];
@@ -635,7 +698,7 @@
}
/* ------------------------------------------------------------ */
- private void invalidateChainsCache()
+ protected void invalidateChainsCache()
{
if (_chainLRU[FilterMapping.REQUEST]!=null)
{
@@ -752,12 +815,22 @@
/**
* see also newServletHolder(Class)
*/
- public ServletHolder newServletHolder()
+ public ServletHolder newServletHolder(Holder.Source source)
{
- return new ServletHolder();
+ return new ServletHolder(source);
}
/* ------------------------------------------------------------ */
+ /**
+ * Create a new CachedChain
+ */
+ public CachedChain newCachedChain(Object filters, ServletHolder servletHolder)
+ {
+ return new CachedChain(filters, servletHolder);
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Convenience method to add a servlet Holder.
public ServletHolder newServletHolder(Class<? extends Servlet> servlet)
{
return new ServletHolder(servlet);
@@ -769,7 +842,7 @@
*/
public ServletHolder addServletWithMapping (String className,String pathSpec)
{
- ServletHolder holder = newServletHolder(null);
+ ServletHolder holder = newServletHolder(Holder.Source.EMBEDDED);
holder.setClassName(className);
addServletWithMapping(holder,pathSpec);
return holder;
@@ -781,9 +854,10 @@
*/
public ServletHolder addServletWithMapping (Class<? extends Servlet> servlet,String pathSpec)
{
- ServletHolder holder = newServletHolder();
+ ServletHolder holder = newServletHolder(Holder.Source.EMBEDDED);
holder.setHeldClass(servlet);
- setServlets((ServletHolder[])LazyList.addToArray(getServlets(), holder, ServletHolder.class));
+ //DUPLICATES adding servlet from addServletWithMapping(holder, pathSpec)?
+ //setServlets((ServletHolder[])LazyList.addToArray(getServlets(), holder, ServletHolder.class));
addServletWithMapping(holder,pathSpec);
return holder;
@@ -836,23 +910,23 @@
{
setServletMappings((ServletMapping[])LazyList.addToArray(getServletMappings(), mapping, ServletMapping.class));
}
-
- /* ------------------------------------------------------------ */
- public FilterHolder newFilterHolder(Class<? extends Filter> filter)
- {
- return new FilterHolder(filter);
+
+ public Set<String> setServletSecurity(ServletRegistration.Dynamic registration, ServletSecurityElement servletSecurityElement) {
+ if (_contextHandler != null) {
+ return _contextHandler.setServletSecurity(registration, servletSecurityElement);
+ }
+ return Collections.emptySet();
}
-
+
/* ------------------------------------------------------------ */
/**
* @see #newFilterHolder(Class)
*/
- public FilterHolder newFilterHolder()
+ public FilterHolder newFilterHolder(Holder.Source source)
{
- return new FilterHolder();
+ return new FilterHolder(source);
}
-
/* ------------------------------------------------------------ */
public FilterHolder getFilter(String name)
{
@@ -869,7 +943,7 @@
*/
public FilterHolder addFilterWithMapping (Class<? extends Filter> filter,String pathSpec,EnumSet<DispatcherType> dispatches)
{
- FilterHolder holder = newFilterHolder();
+ FilterHolder holder = newFilterHolder(Holder.Source.EMBEDDED);
holder.setHeldClass(filter);
addFilterWithMapping(holder,pathSpec,dispatches);
@@ -885,7 +959,7 @@
*/
public FilterHolder addFilterWithMapping (String className,String pathSpec,EnumSet<DispatcherType> dispatches)
{
- FilterHolder holder = newFilterHolder();
+ FilterHolder holder = newFilterHolder(Holder.Source.EMBEDDED);
holder.setClassName(className);
addFilterWithMapping(holder,pathSpec,dispatches);
@@ -912,7 +986,9 @@
mapping.setFilterName(holder.getName());
mapping.setPathSpec(pathSpec);
mapping.setDispatcherTypes(dispatches);
- setFilterMappings((FilterMapping[])LazyList.addToArray(getFilterMappings(), mapping, FilterMapping.class));
+ //setFilterMappings((FilterMapping[])LazyList.addToArray(getFilterMappings(), mapping, FilterMapping.class));
+ addFilterMapping(mapping);
+
}
catch (RuntimeException e)
{
@@ -927,7 +1003,6 @@
}
-
/* ------------------------------------------------------------ */
/** Convenience method to add a filter.
* @param filter class of filter to create
@@ -937,7 +1012,8 @@
*/
public FilterHolder addFilterWithMapping (Class<? extends Filter> filter,String pathSpec,int dispatches)
{
- FilterHolder holder = newFilterHolder(filter);
+ FilterHolder holder = newFilterHolder(Holder.Source.EMBEDDED);
+ holder.setHeldClass(filter);
addFilterWithMapping(holder,pathSpec,dispatches);
return holder;
@@ -952,7 +1028,7 @@
*/
public FilterHolder addFilterWithMapping (String className,String pathSpec,int dispatches)
{
- FilterHolder holder = newFilterHolder(null);
+ FilterHolder holder = newFilterHolder(Holder.Source.EMBEDDED);
holder.setClassName(className);
addFilterWithMapping(holder,pathSpec,dispatches);
@@ -979,7 +1055,8 @@
mapping.setFilterName(holder.getName());
mapping.setPathSpec(pathSpec);
mapping.setDispatches(dispatches);
- setFilterMappings((FilterMapping[])LazyList.addToArray(getFilterMappings(), mapping, FilterMapping.class));
+ //setFilterMappings((FilterMapping[])LazyList.addToArray(getFilterMappings(), mapping, FilterMapping.class));
+ addFilterMapping(mapping);
}
catch (RuntimeException e)
{
@@ -994,14 +1071,13 @@
}
-
/* ------------------------------------------------------------ */
/** Convenience method to add a filter with a mapping
* @param className
* @param pathSpec
* @param dispatches
* @return the filter holder created
- * @deprecated use {@link #addFilterWithMapping(Class, String, int)} instead
+ * @deprecated use {@link #addFilterWithMapping(Class, String, EnumSet<DispatcherType>)} instead
*/
public FilterHolder addFilter (String className,String pathSpec,EnumSet<DispatcherType> dispatches)
{
@@ -1019,7 +1095,8 @@
if (filter != null)
setFilters((FilterHolder[])LazyList.addToArray(getFilters(), filter, FilterHolder.class));
if (filterMapping != null)
- setFilterMappings((FilterMapping[])LazyList.addToArray(getFilterMappings(), filterMapping, FilterMapping.class));
+ //setFilterMappings((FilterMapping[])LazyList.addToArray(getFilterMappings(), filterMapping, FilterMapping.class));
+ addFilterMapping(filterMapping);
}
/* ------------------------------------------------------------ */
@@ -1039,9 +1116,43 @@
public void addFilterMapping (FilterMapping mapping)
{
if (mapping != null)
- setFilterMappings((FilterMapping[])LazyList.addToArray(getFilterMappings(), mapping, FilterMapping.class));
+ {
+ Source source = (mapping.getFilterHolder()==null?null:mapping.getFilterHolder().getSource());
+ FilterMapping[] mappings =getFilterMappings();
+ if (mappings==null || mappings.length==0)
+ {
+ setFilterMappings(insertFilterMapping(mapping,0,false));
+ if (source != null && source == Source.JAVAX_API)
+ _matchAfterIndex = 0;
+ }
+ else
+ {
+ //there are existing entries. If this is a programmatic filtermapping, it is added at the end of the list.
+ //If this is a normal filtermapping, it is inserted after all the other filtermappings (matchBefores and normals),
+ //but before the first matchAfter filtermapping.
+ if (source != null && Source.JAVAX_API == source)
+ {
+ setFilterMappings(insertFilterMapping(mapping,mappings.length-1, false));
+ if (_matchAfterIndex < 0)
+ _matchAfterIndex = getFilterMappings().length-1;
+ }
+ else
+ {
+ //insert non-programmatic filter mappings before any matchAfters, if any
+ if (_matchAfterIndex < 0)
+ setFilterMappings(insertFilterMapping(mapping,mappings.length-1, false));
+ else
+ {
+ FilterMapping[] new_mappings = insertFilterMapping(mapping, _matchAfterIndex, true);
+ ++_matchAfterIndex;
+ setFilterMappings(new_mappings);
+ }
+ }
+ }
+ }
}
+
/* ------------------------------------------------------------ */
/** Convenience method to add a preconstructed FilterMapping
* @param mapping
@@ -1050,20 +1161,100 @@
{
if (mapping != null)
{
- FilterMapping[] mappings =getFilterMappings();
+ Source source = mapping.getFilterHolder().getSource();
+
+ FilterMapping[] mappings = getFilterMappings();
if (mappings==null || mappings.length==0)
- setFilterMappings(new FilterMapping[] {mapping});
+ {
+ setFilterMappings(insertFilterMapping(mapping, 0, false));
+ if (source != null && Source.JAVAX_API == source)
+ _matchBeforeIndex = 0;
+ }
else
{
+ if (source != null && Source.JAVAX_API == source)
+ {
+ //programmatically defined filter mappings are prepended to mapping list in the order
+ //in which they were defined. In other words, insert this mapping at the tail of the
+ //programmatically prepended filter mappings, BEFORE the first web.xml defined filter mapping.
- FilterMapping[] new_mappings=new FilterMapping[mappings.length+1];
- System.arraycopy(mappings,0,new_mappings,1,mappings.length);
- new_mappings[0]=mapping;
- setFilterMappings(new_mappings);
+ if (_matchBeforeIndex < 0)
+ {
+ //no programmatically defined prepended filter mappings yet, prepend this one
+ _matchBeforeIndex = 0;
+ FilterMapping[] new_mappings = insertFilterMapping(mapping, 0, true);
+ setFilterMappings(new_mappings);
+ }
+ else
+ {
+ FilterMapping[] new_mappings = insertFilterMapping(mapping,_matchBeforeIndex, false);
+ ++_matchBeforeIndex;
+ setFilterMappings(new_mappings);
+ }
+ }
+ else
+ {
+ //non programmatically defined, just prepend to list
+ FilterMapping[] new_mappings = insertFilterMapping(mapping, 0, true);
+ setFilterMappings(new_mappings);
+ }
+
+ //adjust matchAfterIndex ptr to take account of the mapping we just prepended
+ if (_matchAfterIndex >= 0)
+ ++_matchAfterIndex;
}
}
}
+
+
+
+ /**
+ * Insert a filtermapping in the list
+ * @param mapping the FilterMapping to add
+ * @param pos the position in the existing arry at which to add it
+ * @param before if true, insert before pos, if false insert after it
+ * @return
+ */
+ protected FilterMapping[] insertFilterMapping (FilterMapping mapping, int pos, boolean before)
+ {
+ if (pos < 0)
+ throw new IllegalArgumentException("FilterMapping insertion pos < 0");
+ FilterMapping[] mappings = getFilterMappings();
+
+ if (mappings==null || mappings.length==0)
+ {
+ return new FilterMapping[] {mapping};
+ }
+ FilterMapping[] new_mappings = new FilterMapping[mappings.length+1];
+
+ if (before)
+ {
+ //copy existing filter mappings up to but not including the pos
+ System.arraycopy(mappings,0,new_mappings,0,pos);
+
+ //add in the new mapping
+ new_mappings[pos] = mapping;
+
+ //copy the old pos mapping and any remaining existing mappings
+ System.arraycopy(mappings,pos,new_mappings,pos+1, mappings.length-pos);
+
+ }
+ else
+ {
+ //copy existing filter mappings up to and including the pos
+ System.arraycopy(mappings,0,new_mappings,0,pos+1);
+ //add in the new mapping after the pos
+ new_mappings[pos+1] = mapping;
+
+ //copy the remaining existing mappings
+ if (mappings.length > pos+1)
+ System.arraycopy(mappings,pos+1,new_mappings,pos+2, mappings.length-(pos+1));
+ }
+ return new_mappings;
+ }
+
+
/* ------------------------------------------------------------ */
protected synchronized void updateNameMappings()
{
@@ -1140,7 +1331,7 @@
ServletHolder servlet_holder = (ServletHolder)_servletNameMap.get(_servletMappings[i].getServletName());
if (servlet_holder==null)
throw new IllegalStateException("No such servlet: "+_servletMappings[i].getServletName());
- else if (_servletMappings[i].getPathSpecs()!=null)
+ else if (servlet_holder.isEnabled() && _servletMappings[i].getPathSpecs()!=null)
{
String[] pathSpecs = _servletMappings[i].getPathSpecs();
for (int j=0;j<pathSpecs.length;j++)
@@ -1173,7 +1364,7 @@
try
{
- if (isStarted())
+ if (_contextHandler!=null && _contextHandler.isStarted() || _contextHandler==null && isStarted())
initialize();
}
catch (Exception e)
@@ -1250,23 +1441,22 @@
invalidateChainsCache();
}
-
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
- private class CachedChain implements FilterChain
+ protected class CachedChain implements FilterChain
{
FilterHolder _filterHolder;
CachedChain _next;
ServletHolder _servletHolder;
/* ------------------------------------------------------------ */
- CachedChain(Object filters, ServletHolder servletHolder)
+ protected CachedChain(Object filters, ServletHolder servletHolder)
{
if (LazyList.size(filters)>0)
{
_filterHolder=(FilterHolder)LazyList.get(filters, 0);
filters=LazyList.remove(filters,0);
- _next=new CachedChain(filters,servletHolder);
+ _next=newCachedChain(filters,servletHolder);
}
else
_servletHolder=servletHolder;
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java
index dc6df80..b416170 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java
@@ -19,9 +19,6 @@
package org.eclipse.jetty.servlet;
import java.io.IOException;
-import java.io.PrintStream;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -33,12 +30,15 @@
import java.util.Set;
import java.util.Stack;
+import javax.servlet.MultipartConfigElement;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
-import javax.servlet.ServletContext;
import javax.servlet.ServletException;
+import javax.servlet.ServletRegistration;
+import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
+import javax.servlet.ServletSecurityElement;
import javax.servlet.SingleThreadModel;
import javax.servlet.UnavailableException;
@@ -47,7 +47,6 @@
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.server.handler.ContextHandler;
-import org.eclipse.jetty.servlet.api.ServletRegistration;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@@ -82,6 +81,7 @@
private transient Servlet _servlet;
private transient Config _config;
private transient long _unavailable;
+ private transient boolean _enabled = true;
private transient UnavailableException _unavailableEx;
public static final Map<String,String> NO_MAPPED_ROLES = Collections.emptyMap();
@@ -90,32 +90,32 @@
*/
public ServletHolder()
{
+ this(Source.EMBEDDED);
}
-
/* ---------------------------------------------------------------- */
- /** Constructor for existing servlet.
+ /** Constructor .
*/
- public ServletHolder(String name,Servlet servlet)
+ public ServletHolder(Holder.Source creator)
{
- setName(name);
- setServlet(servlet);
+ super(creator);
}
-
/* ---------------------------------------------------------------- */
/** Constructor for existing servlet.
*/
public ServletHolder(Servlet servlet)
{
+ this(Source.EMBEDDED);
setServlet(servlet);
}
/* ---------------------------------------------------------------- */
/** Constructor for servlet class.
*/
- public ServletHolder(String name,Class<? extends Servlet> servlet)
+ public ServletHolder(String name, Class<? extends Servlet> servlet)
{
+ this(Source.EMBEDDED);
setName(name);
setHeldClass(servlet);
}
@@ -123,8 +123,19 @@
/* ---------------------------------------------------------------- */
/** Constructor for servlet class.
*/
+ public ServletHolder(String name, Servlet servlet)
+ {
+ this(Source.EMBEDDED);
+ setName(name);
+ setServlet(servlet);
+ }
+
+ /* ---------------------------------------------------------------- */
+ /** Constructor for servlet class.
+ */
public ServletHolder(Class<? extends Servlet> servlet)
{
+ this(Source.EMBEDDED);
setHeldClass(servlet);
}
@@ -260,20 +271,62 @@
_forcedPath = forcedPath;
}
+ public boolean isEnabled()
+ {
+ return _enabled;
+ }
+
+
+ public void setEnabled(boolean enabled)
+ {
+ _enabled = enabled;
+ }
+
+
/* ------------------------------------------------------------ */
public void doStart()
throws Exception
{
_unavailable=0;
+ if (!_enabled)
+ return;
+
+
+ //check servlet has a class (ie is not a preliminary registration). If preliminary, fail startup.
try
{
super.doStart();
+ }
+ catch (UnavailableException ue)
+ {
+ makeUnavailable(ue);
+ if (_servletHandler.isStartWithUnavailable())
+ {
+ LOG.ignore(ue);
+ return;
+ }
+ else
+ throw ue;
+ }
+
+
+ //servlet is not an instance of javax.servlet.Servlet
+ try
+ {
checkServletType();
}
catch (UnavailableException ue)
{
makeUnavailable(ue);
+ if (_servletHandler.isStartWithUnavailable())
+ {
+ LOG.ignore(ue);
+ return;
+ }
+ else
+ throw ue;
}
+
_identityService = _servletHandler.getIdentityService();
if (_identityService!=null && _runAsRole!=null)
@@ -470,12 +523,13 @@
// Handle configuring servlets that implement org.apache.jasper.servlet.JspServlet
if (isJspServlet())
+ {
initJspServlet();
+ }
- _servlet.init(_config);
+ initMultiPart();
- if (isJspServlet())
- postInitJspServlet();
+ _servlet.init(_config);
}
catch (UnavailableException e)
{
@@ -531,52 +585,26 @@
}
}
- protected void postInitJspServlet() throws Exception
+ /* ------------------------------------------------------------ */
+ /**
+ * Register a ServletRequestListener that will ensure tmp multipart
+ * files are deleted when the request goes out of scope.
+ *
+ * @throws Exception
+ */
+ protected void initMultiPart () throws Exception
{
- try
+ //if this servlet can handle multipart requests, ensure tmp files will be
+ //cleaned up correctly
+ if (((Registration)getRegistration()).getMultipartConfig() != null)
{
- //Check that jasper's SystemLogHandler class is on the classpath
- Class systemLogHandlerClass = Loader.loadClass(this.getClass(), "org.apache.jasper.util.SystemLogHandler");
- PrintStream rootSystemLogHandler = null;
- while (systemLogHandlerClass.isAssignableFrom(System.err.getClass()))
- {
- rootSystemLogHandler = System.err;
- Method getWrapped = systemLogHandlerClass.getMethod("getWrapped", new Class[]{});
- PrintStream ps = (PrintStream)getWrapped.invoke(System.err, new Object[]{});
- System.setErr(ps);
- }
-
- if (rootSystemLogHandler != null)
- System.setErr(rootSystemLogHandler);
+ //Register a listener to delete tmp files that are created as a result of this
+ //servlet calling Request.getPart() or Request.getParts()
+ ContextHandler ch = ((ContextHandler.Context)getServletHandler().getServletContext()).getContextHandler();
+ ch.addEventListener(new Request.MultiPartCleanerListener());
}
- catch (ClassNotFoundException e)
- {
- //jasper not on classpath, ignore
- }
- catch (NoSuchMethodException e)
- {
- LOG.info("Problem unwrapping SystemLogHandler from System.err", e);
- }
- catch (SecurityException e)
- {
- LOG.warn("Problem unwrapping SystemLogHandler from System.err", e);
- }
- catch (IllegalAccessException e)
- {
- LOG.warn("Problem unwrapping SystemLogHandler from System.err", e);
- }
- catch (IllegalArgumentException e)
- {
- LOG.warn("Problem unwrapping SystemLogHandler from System.err", e);
- }
- catch (InvocationTargetException e)
- {
- LOG.warn("Problem unwrapping SystemLogHandler from System.err", e);
- }
-
}
-
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.server.UserIdentity.Scope#getContextPath()
@@ -645,10 +673,14 @@
// Handle run as
if (_identityService!=null)
old_run_as=_identityService.setRunAs(baseRequest.getResolvedUserIdentity(),_runAsToken);
-
+
if (!isAsyncSupported())
baseRequest.setAsyncSupported(false);
-
+
+ MultipartConfigElement mpce = ((Registration)getRegistration()).getMultipartConfig();
+ if (mpce != null)
+ request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, mpce);
+
servlet.service(request,response);
servlet_error=false;
}
@@ -717,24 +749,33 @@
/* -------------------------------------------------------- */
/* -------------------------------------------------------- */
public class Registration extends HolderRegistration implements ServletRegistration.Dynamic
- {
+ {
+ protected MultipartConfigElement _multipartConfig;
+
public Set<String> addMapping(String... urlPatterns)
{
illegalStateIfContextStarted();
Set<String> clash=null;
for (String pattern : urlPatterns)
{
- if (_servletHandler.getServletMapping(pattern)!=null)
+ ServletMapping mapping = _servletHandler.getServletMapping(pattern);
+ if (mapping!=null)
{
- if (clash==null)
- clash=new HashSet<String>();
- clash.add(pattern);
+ //if the servlet mapping was from a default descriptor, then allow it to be overridden
+ if (!mapping.isDefault())
+ {
+ if (clash==null)
+ clash=new HashSet<String>();
+ clash.add(pattern);
+ }
}
}
+ //if there were any clashes amongst the urls, return them
if (clash!=null)
return clash;
+ //otherwise apply all of them
ServletMapping mapping = new ServletMapping();
mapping.setServletName(ServletHolder.this.getName());
mapping.setPathSpecs(urlPatterns);
@@ -747,22 +788,27 @@
{
ServletMapping[] mappings =_servletHandler.getServletMappings();
List<String> patterns=new ArrayList<String>();
- for (ServletMapping mapping : mappings)
+ if (mappings!=null)
{
- if (!mapping.getServletName().equals(getName()))
- continue;
- String[] specs=mapping.getPathSpecs();
- if (specs!=null && specs.length>0)
- patterns.addAll(Arrays.asList(specs));
+ for (ServletMapping mapping : mappings)
+ {
+ if (!mapping.getServletName().equals(getName()))
+ continue;
+ String[] specs=mapping.getPathSpecs();
+ if (specs!=null && specs.length>0)
+ patterns.addAll(Arrays.asList(specs));
+ }
}
return patterns;
}
+ @Override
public String getRunAsRole()
{
return _runAsRole;
}
+ @Override
public void setLoadOnStartup(int loadOnStartup)
{
illegalStateIfContextStarted();
@@ -773,17 +819,35 @@
{
return ServletHolder.this.getInitOrder();
}
-
+
+ @Override
+ public void setMultipartConfig(MultipartConfigElement element)
+ {
+ _multipartConfig = element;
+ }
+
+ public MultipartConfigElement getMultipartConfig()
+ {
+ return _multipartConfig;
+ }
+
+ @Override
public void setRunAsRole(String role)
{
_runAsRole = role;
}
+
+ @Override
+ public Set<String> setServletSecurity(ServletSecurityElement securityElement)
+ {
+ return _servletHandler.setServletSecurity(this, securityElement);
+ }
}
public ServletRegistration.Dynamic getRegistration()
{
if (_registration == null)
- _registration = new Registration();
+ _registration = new Registration();
return _registration;
}
@@ -903,8 +967,3 @@
}
}
}
-
-
-
-
-
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletMapping.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletMapping.java
index 5537e36..f2468bd 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletMapping.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletMapping.java
@@ -26,6 +26,8 @@
{
private String[] _pathSpecs;
private String _servletName;
+ private boolean _default;
+
/* ------------------------------------------------------------ */
public ServletMapping()
@@ -77,6 +79,25 @@
_servletName = servletName;
}
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return
+ */
+ public boolean isDefault()
+ {
+ return _default;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param default1
+ */
+ public void setDefault(boolean fromDefault)
+ {
+ _default = fromDefault;
+ }
/* ------------------------------------------------------------ */
public String toString()
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/api/FilterRegistration.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/api/FilterRegistration.java
deleted file mode 100644
index 37d9ae7..0000000
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/api/FilterRegistration.java
+++ /dev/null
@@ -1,45 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2016 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.servlet.api;
-
-import java.util.Collection;
-import java.util.EnumSet;
-
-import org.eclipse.jetty.server.DispatcherType;
-
-/**
- * FilterRegistration
- *
- * Mimics the javax.servlet.FilterRegistration class to ease
- * jetty-7/jetty-8 compatibility
- */
-public interface FilterRegistration
-{
- public void addMappingForServletNames(EnumSet<DispatcherType> dispatcherTypes, boolean isMatchAfter, String... servletNames);
-
- public Collection<String> getServletNameMappings();
-
- public void addMappingForUrlPatterns(EnumSet<DispatcherType> dispatcherTypes, boolean isMatchAfter, String... urlPatterns);
-
- public Collection<String> getUrlPatternMappings();
-
- interface Dynamic extends FilterRegistration, Registration.Dynamic
- {
- }
-}
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/api/Registration.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/api/Registration.java
deleted file mode 100644
index 7555001..0000000
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/api/Registration.java
+++ /dev/null
@@ -1,43 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2016 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.servlet.api;
-
-import java.util.Map;
-import java.util.Set;
-
-public interface Registration
-{
-
- public String getName();
-
- public String getClassName();
-
- public boolean setInitParameter(String name, String value);
-
- public String getInitParameter(String name);
-
- public Set<String> setInitParameters(Map<String, String> initParameters);
-
- public Map<String, String> getInitParameters();
-
- interface Dynamic extends Registration
- {
- public void setAsyncSupported(boolean isAsyncSupported);
- }
-}
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/api/ServletRegistration.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/api/ServletRegistration.java
deleted file mode 100644
index 4a1502b..0000000
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/api/ServletRegistration.java
+++ /dev/null
@@ -1,39 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2016 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.servlet.api;
-
-import java.util.Collection;
-import java.util.Set;
-
-public interface ServletRegistration
-{
- public Set<String> addMapping(String... urlPatterns);
-
- public Collection<String> getMappings();
-
- public String getRunAsRole();
-
- interface Dynamic extends ServletRegistration, Registration.Dynamic
- {
- public void setLoadOnStartup(int loadOnStartup);
-
- public void setRunAsRole(String roleName);
- }
-
-}
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/listener/ELContextCleaner.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/listener/ELContextCleaner.java
index 42943d2..16199be 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/listener/ELContextCleaner.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/listener/ELContextCleaner.java
@@ -20,6 +20,7 @@
import java.lang.reflect.Field;
import java.util.Iterator;
+import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.ServletContextEvent;
@@ -105,7 +106,7 @@
if (!properties.isAccessible())
properties.setAccessible(true);
- ConcurrentHashMap map = (ConcurrentHashMap) properties.get(null);
+ Map map = (Map) properties.get(null);
if (map == null)
return;
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java
index d9f8668..cc32240 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java
@@ -27,19 +27,19 @@
import java.io.IOException;
import java.io.StringReader;
+import javax.servlet.AsyncContext;
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
+import javax.servlet.DispatcherType;
+import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
-import junit.framework.Assert;
-
-import org.eclipse.jetty.continuation.ContinuationSupport;
-import org.eclipse.jetty.server.AsyncContext;
-import org.eclipse.jetty.server.AsyncContinuation;
+import org.junit.Assert;
import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.DispatcherType;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.Request;
@@ -65,16 +65,25 @@
@Before
public void setUp() throws Exception
{
- _connector.setMaxIdleTime(30000);
+ _connector.setMaxIdleTime(5000);
_server.setConnectors(new Connector[]
{ _connector });
- _contextHandler.setContextPath("/");
+ _contextHandler.setContextPath("/ctx");
_contextHandler.addServlet(new ServletHolder(new TestServlet()),"/servletPath");
_contextHandler.addServlet(new ServletHolder(new TestServlet()),"/path with spaces/servletPath");
_contextHandler.addServlet(new ServletHolder(new TestServlet2()),"/servletPath2");
+ _contextHandler.addServlet(new ServletHolder(new TestStartThrowServlet()),"/startthrow/*");
_contextHandler.addServlet(new ServletHolder(new ForwardingServlet()),"/forward");
_contextHandler.addServlet(new ServletHolder(new AsyncDispatchingServlet()),"/dispatchingServlet");
+ _contextHandler.addServlet(new ServletHolder(new ExpireServlet()),"/expire/*");
+ _contextHandler.addServlet(new ServletHolder(new BadExpireServlet()),"/badexpire/*");
+ _contextHandler.addServlet(new ServletHolder(new ErrorServlet()),"/error/*");
+
+ ErrorPageErrorHandler error_handler = new ErrorPageErrorHandler();
+ _contextHandler.setErrorHandler(error_handler);
+ error_handler.addErrorPage(500,"/error/500");
+ error_handler.addErrorPage(IOException.class.getName(),"/error/IOE");
HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[]
@@ -87,11 +96,11 @@
@Test
public void testSimpleAsyncContext() throws Exception
{
- String request = "GET /servletPath HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
+ String request = "GET /ctx/servletPath HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
+ "Connection: close\r\n" + "\r\n";
-
String responseString = _connector.getResponses(request);
+
BufferedReader br = parseHeader(responseString);
Assert.assertEquals("servlet gets right path","doGet:getServletPath:/servletPath",br.readLine());
@@ -100,9 +109,93 @@
}
@Test
+ public void testStartThrow() throws Exception
+ {
+ String request =
+ "GET /ctx/startthrow HTTP/1.1\r\n" +
+ "Host: localhost\r\n" +
+ "Connection: close\r\n" +
+ "\r\n";
+ String responseString = _connector.getResponses(request);
+
+ BufferedReader br = new BufferedReader(new StringReader(responseString));
+
+ assertEquals("HTTP/1.1 500 Server Error",br.readLine());
+ br.readLine();// connection close
+ br.readLine();// server
+ br.readLine();// empty
+
+ Assert.assertEquals("error servlet","ERROR: /error",br.readLine());
+ Assert.assertEquals("error servlet","PathInfo= /IOE",br.readLine());
+ Assert.assertEquals("error servlet","EXCEPTION: java.io.IOException: Test",br.readLine());
+ }
+
+ @Test
+ public void testStartDispatchThrow() throws Exception
+ {
+ String request = "GET /ctx/startthrow?dispatch=true HTTP/1.1\r\n" +
+ "Host: localhost\r\n" +
+ "Content-Type: application/x-www-form-urlencoded\r\n" +
+ "Connection: close\r\n" +
+ "\r\n";
+ String responseString = _connector.getResponses(request);
+
+ BufferedReader br = new BufferedReader(new StringReader(responseString));
+
+ assertEquals("HTTP/1.1 500 Server Error",br.readLine());
+ br.readLine();// connection close
+ br.readLine();// server
+ br.readLine();// empty
+ Assert.assertEquals("error servlet","ERROR: /error",br.readLine());
+ Assert.assertEquals("error servlet","PathInfo= /IOE",br.readLine());
+ Assert.assertEquals("error servlet","EXCEPTION: java.io.IOException: Test",br.readLine());
+ }
+
+ @Test
+ public void testStartCompleteThrow() throws Exception
+ {
+ String request = "GET /ctx/startthrow?complete=true HTTP/1.1\r\n" +
+ "Host: localhost\r\n" +
+ "Content-Type: application/x-www-form-urlencoded\r\n" +
+ "Connection: close\r\n" +
+ "\r\n";
+ String responseString = _connector.getResponses(request);
+
+ BufferedReader br = new BufferedReader(new StringReader(responseString));
+
+ assertEquals("HTTP/1.1 500 Server Error",br.readLine());
+ br.readLine();// connection close
+ br.readLine();// server
+ br.readLine();// empty
+ Assert.assertEquals("error servlet","ERROR: /error",br.readLine());
+ Assert.assertEquals("error servlet","PathInfo= /IOE",br.readLine());
+ Assert.assertEquals("error servlet","EXCEPTION: java.io.IOException: Test",br.readLine());
+ }
+
+ @Test
+ public void testStartFlushCompleteThrow() throws Exception
+ {
+ String request = "GET /ctx/startthrow?flush=true&complete=true HTTP/1.1\r\n" +
+ "Host: localhost\r\n" +
+ "Content-Type: application/x-www-form-urlencoded\r\n" +
+ "Connection: close\r\n" +
+ "\r\n";
+ String responseString = _connector.getResponses(request);
+
+ BufferedReader br = new BufferedReader(new StringReader(responseString));
+
+ assertEquals("HTTP/1.1 200 OK",br.readLine());
+ br.readLine();// connection close
+ br.readLine();// server
+ br.readLine();// empty
+
+ Assert.assertEquals("error servlet","completeBeforeThrow",br.readLine());
+ }
+
+ @Test
public void testDispatchAsyncContext() throws Exception
{
- String request = "GET /servletPath?dispatch=true HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
+ String request = "GET /ctx/servletPath?dispatch=true HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
+ "Connection: close\r\n" + "\r\n";
String responseString = _connector.getResponses(request);
@@ -113,14 +206,14 @@
Assert.assertEquals("servlet path attr is original","async:run:attr:servletPath:/servletPath",br.readLine());
Assert.assertEquals("path info attr is correct","async:run:attr:pathInfo:null",br.readLine());
Assert.assertEquals("query string attr is correct","async:run:attr:queryString:dispatch=true",br.readLine());
- Assert.assertEquals("context path attr is correct","async:run:attr:contextPath:",br.readLine());
- Assert.assertEquals("request uri attr is correct","async:run:attr:requestURI:/servletPath",br.readLine());
+ Assert.assertEquals("context path attr is correct","async:run:attr:contextPath:/ctx",br.readLine());
+ Assert.assertEquals("request uri attr is correct","async:run:attr:requestURI:/ctx/servletPath",br.readLine());
}
@Test
public void testDispatchAsyncContextEncodedPathAndQueryString() throws Exception
{
- String request = "GET /path%20with%20spaces/servletPath?dispatch=true&queryStringWithEncoding=space%20space HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
+ String request = "GET /ctx/path%20with%20spaces/servletPath?dispatch=true&queryStringWithEncoding=space%20space HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
+ "Connection: close\r\n" + "\r\n";
String responseString = _connector.getResponses(request);
@@ -131,16 +224,14 @@
assertThat("servlet path attr is original",br.readLine(),equalTo("async:run:attr:servletPath:/path with spaces/servletPath"));
assertThat("path info attr is correct",br.readLine(),equalTo("async:run:attr:pathInfo:null"));
assertThat("query string attr is correct",br.readLine(),equalTo("async:run:attr:queryString:dispatch=true&queryStringWithEncoding=space%20space"));
- assertThat("context path attr is correct",br.readLine(),equalTo("async:run:attr:contextPath:"));
- assertThat("request uri attr is correct",br.readLine(),equalTo("async:run:attr:requestURI:/path%20with%20spaces/servletPath"));
+ assertThat("context path attr is correct",br.readLine(),equalTo("async:run:attr:contextPath:/ctx"));
+ assertThat("request uri attr is correct",br.readLine(),equalTo("async:run:attr:requestURI:/ctx/path%20with%20spaces/servletPath"));
}
@Test
public void testSimpleWithContextAsyncContext() throws Exception
{
- _contextHandler.setContextPath("/foo");
-
- String request = "GET /foo/servletPath HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
+ String request = "GET /ctx/servletPath HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
+ "Connection: close\r\n" + "\r\n";
String responseString = _connector.getResponses(request);
@@ -155,9 +246,7 @@
@Test
public void testDispatchWithContextAsyncContext() throws Exception
{
- _contextHandler.setContextPath("/foo");
-
- String request = "GET /foo/servletPath?dispatch=true HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
+ String request = "GET /ctx/servletPath?dispatch=true HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
+ "Connection: close\r\n" + "\r\n";
String responseString = _connector.getResponses(request);
@@ -169,14 +258,14 @@
Assert.assertEquals("servlet path attr is original","async:run:attr:servletPath:/servletPath",br.readLine());
Assert.assertEquals("path info attr is correct","async:run:attr:pathInfo:null",br.readLine());
Assert.assertEquals("query string attr is correct","async:run:attr:queryString:dispatch=true",br.readLine());
- Assert.assertEquals("context path attr is correct","async:run:attr:contextPath:/foo",br.readLine());
- Assert.assertEquals("request uri attr is correct","async:run:attr:requestURI:/foo/servletPath",br.readLine());
+ Assert.assertEquals("context path attr is correct","async:run:attr:contextPath:/ctx",br.readLine());
+ Assert.assertEquals("request uri attr is correct","async:run:attr:requestURI:/ctx/servletPath",br.readLine());
}
@Test
public void testDispatch() throws Exception
{
- String request = "GET /forward HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "Connection: close\r\n"
+ String request = "GET /ctx/forward HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "Connection: close\r\n"
+ "\r\n";
String responseString = _connector.getResponses(request);
@@ -189,7 +278,7 @@
@Test
public void testDispatchRequestResponse() throws Exception
{
- String request = "GET /forward?dispatchRequestResponse=true HTTP/1.1\r\n" + "Host: localhost\r\n"
+ String request = "GET /ctx/forward?dispatchRequestResponse=true HTTP/1.1\r\n" + "Host: localhost\r\n"
+ "Content-Type: application/x-www-form-urlencoded\r\n" + "Connection: close\r\n" + "\r\n";
String responseString = _connector.getResponses(request);
@@ -261,6 +350,49 @@
}
}
+ @Test
+ public void testExpire() throws Exception
+ {
+ String request = "GET /ctx/expire HTTP/1.1\r\n" +
+ "Host: localhost\r\n" +
+ "Content-Type: application/x-www-form-urlencoded\r\n" +
+ "Connection: close\r\n" +
+ "\r\n";
+ String responseString = _connector.getResponses(request);
+
+ BufferedReader br = new BufferedReader(new StringReader(responseString));
+
+ assertEquals("HTTP/1.1 500 Async Timeout",br.readLine());
+
+ br.readLine();// connection close
+ br.readLine();// server
+ br.readLine();// empty
+
+ Assert.assertEquals("error servlet","ERROR: /error",br.readLine());
+ }
+
+ @Test
+ public void testBadExpire() throws Exception
+ {
+ String request = "GET /ctx/badexpire HTTP/1.1\r\n" +
+ "Host: localhost\r\n" +
+ "Content-Type: application/x-www-form-urlencoded\r\n" +
+ "Connection: close\r\n" +
+ "\r\n";
+ String responseString = _connector.getResponses(request);
+
+ BufferedReader br = new BufferedReader(new StringReader(responseString));
+
+ assertEquals("HTTP/1.1 500 Async Exception",br.readLine());
+ br.readLine();// connection close
+ br.readLine();// server
+ br.readLine();// empty
+
+ Assert.assertEquals("error servlet","ERROR: /error",br.readLine());
+ Assert.assertEquals("error servlet","PathInfo= /500",br.readLine());
+ Assert.assertEquals("error servlet","EXCEPTION: java.io.IOException: TEST",br.readLine());
+ }
+
private class DispatchingRunnable implements Runnable
{
private AsyncContext asyncContext;
@@ -287,6 +419,74 @@
_server.join();
}
+
+ private class ErrorServlet extends HttpServlet
+ {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ response.getOutputStream().print("ERROR: " + request.getServletPath() + "\n");
+ response.getOutputStream().print("PathInfo= " + request.getPathInfo() + "\n");
+ if (request.getAttribute(RequestDispatcher.ERROR_EXCEPTION)!=null)
+ response.getOutputStream().print("EXCEPTION: " + request.getAttribute(RequestDispatcher.ERROR_EXCEPTION) + "\n");
+ }
+ }
+
+ private class ExpireServlet extends HttpServlet
+ {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ if (request.getDispatcherType()==DispatcherType.REQUEST)
+ {
+ AsyncContext asyncContext = request.startAsync();
+ asyncContext.setTimeout(100);
+ }
+ }
+ }
+
+ private class BadExpireServlet extends HttpServlet
+ {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ if (request.getDispatcherType()==DispatcherType.REQUEST)
+ {
+ AsyncContext asyncContext = request.startAsync();
+ asyncContext.addListener(new AsyncListener()
+ {
+ @Override
+ public void onTimeout(AsyncEvent event) throws IOException
+ {
+ throw new IOException("TEST");
+ }
+
+ @Override
+ public void onStartAsync(AsyncEvent event) throws IOException
+ {
+ }
+
+ @Override
+ public void onError(AsyncEvent event) throws IOException
+ {
+ }
+
+ @Override
+ public void onComplete(AsyncEvent event) throws IOException
+ {
+ }
+ });
+ asyncContext.setTimeout(100);
+ }
+ }
+ }
+
private class TestServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;
@@ -294,27 +494,18 @@
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
- AsyncContinuation continuation = (AsyncContinuation)ContinuationSupport.getContinuation(request);
-
if (request.getParameter("dispatch") != null)
- {
- continuation.suspend();
- continuation.dispatch("/servletPath2");
- // AsyncContext asyncContext = request.startAsync(request,response);
+ {
+ AsyncContext asyncContext = request.startAsync(request,response);
+ asyncContext.dispatch("/servletPath2");
}
else
{
response.getOutputStream().print("doGet:getServletPath:" + request.getServletPath() + "\n");
+ AsyncContext asyncContext = request.startAsync(request,response);
+ response.getOutputStream().print("doGet:async:getServletPath:" + ((HttpServletRequest)asyncContext.getRequest()).getServletPath() + "\n");
+ asyncContext.start(new AsyncRunnable(asyncContext));
- continuation.suspend();
-
- // AsyncContext asyncContext = request.startAsync(request,response);
-
- response.getOutputStream().print("doGet:async:getServletPath:" + ((HttpServletRequest)continuation.getRequest()).getServletPath() + "\n");
-
- Runnable runable = new AsyncRunnable(continuation);
- new Thread(runable).start();
- // asyncContext.start(new AsyncRunnable(asyncContext));
}
return;
@@ -328,49 +519,70 @@
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
- AsyncContinuation continuation = (AsyncContinuation)ContinuationSupport.getContinuation(request);
-
response.getOutputStream().print("doGet:getServletPath:" + request.getServletPath() + "\n");
-
- continuation.suspend();
- // AsyncContext asyncContext = request.startAsync(request, response);
-
- response.getOutputStream().print("doGet:async:getServletPath:" + ((HttpServletRequest)continuation.getRequest()).getServletPath() + "\n");
- Runnable runable = new AsyncRunnable(continuation);
- new Thread(runable).start();
- // asyncContext.start(new AsyncRunnable(asyncContext));
-
+ AsyncContext asyncContext = request.startAsync(request, response);
+ response.getOutputStream().print("doGet:async:getServletPath:" + ((HttpServletRequest)asyncContext.getRequest()).getServletPath() + "\n");
+ asyncContext.start(new AsyncRunnable(asyncContext));
return;
}
}
+
+ private class TestStartThrowServlet extends HttpServlet
+ {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ if (request.getDispatcherType()==DispatcherType.REQUEST)
+ {
+ request.startAsync(request, response);
+
+ if (Boolean.valueOf(request.getParameter("dispatch")))
+ {
+ request.getAsyncContext().dispatch();
+ }
+
+ if (Boolean.valueOf(request.getParameter("complete")))
+ {
+ response.getOutputStream().write("completeBeforeThrow".getBytes());
+ if (Boolean.valueOf(request.getParameter("flush")))
+ response.flushBuffer();
+ request.getAsyncContext().complete();
+ }
+
+ throw new IOException("Test");
+ }
+ }
+ }
private class AsyncRunnable implements Runnable
{
- private AsyncContinuation _continuation;
+ private AsyncContext _context;
- public AsyncRunnable(AsyncContinuation continuation)
+ public AsyncRunnable(AsyncContext context)
{
- _continuation = continuation;
+ _context = context;
}
+ @Override
public void run()
{
- HttpServletRequest req = (HttpServletRequest)_continuation.getRequest();
-
+ HttpServletRequest req = (HttpServletRequest)_context.getRequest();
+
try
{
- _continuation.getResponse().getOutputStream().print("async:run:attr:servletPath:" + req.getAttribute(AsyncContext.ASYNC_SERVLET_PATH) + "\n");
- _continuation.getResponse().getOutputStream().print("async:run:attr:pathInfo:" + req.getAttribute(AsyncContext.ASYNC_PATH_INFO) + "\n");
- _continuation.getResponse().getOutputStream().print("async:run:attr:queryString:" + req.getAttribute(AsyncContext.ASYNC_QUERY_STRING) + "\n");
- _continuation.getResponse().getOutputStream().print("async:run:attr:contextPath:" + req.getAttribute(AsyncContext.ASYNC_CONTEXT_PATH) + "\n");
- _continuation.getResponse().getOutputStream().print("async:run:attr:requestURI:" + req.getAttribute(AsyncContext.ASYNC_REQUEST_URI) + "\n");
+ _context.getResponse().getOutputStream().print("async:run:attr:servletPath:" + req.getAttribute(AsyncContext.ASYNC_SERVLET_PATH) + "\n");
+ _context.getResponse().getOutputStream().print("async:run:attr:pathInfo:" + req.getAttribute(AsyncContext.ASYNC_PATH_INFO) + "\n");
+ _context.getResponse().getOutputStream().print("async:run:attr:queryString:" + req.getAttribute(AsyncContext.ASYNC_QUERY_STRING) + "\n");
+ _context.getResponse().getOutputStream().print("async:run:attr:contextPath:" + req.getAttribute(AsyncContext.ASYNC_CONTEXT_PATH) + "\n");
+ _context.getResponse().getOutputStream().print("async:run:attr:requestURI:" + req.getAttribute(AsyncContext.ASYNC_REQUEST_URI) + "\n");
}
catch (IOException e)
{
e.printStackTrace();
}
-
- _continuation.complete();
+ _context.complete();
}
}
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletTest.java
new file mode 100644
index 0000000..f5968e2
--- /dev/null
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletTest.java
@@ -0,0 +1,775 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.servlet;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.Socket;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
+import javax.servlet.DispatcherType;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequestWrapper;
+import javax.servlet.ServletResponseWrapper;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.nio.SelectChannelConnector;
+import org.eclipse.jetty.util.IO;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+
+public class AsyncServletTest
+{
+
+ protected AsyncServlet _servlet=new AsyncServlet();
+ protected int _port;
+
+ protected Server _server = new Server();
+ protected ServletHandler _servletHandler;
+ protected SelectChannelConnector _connector;
+
+ @Before
+ public void setUp() throws Exception
+ {
+ _connector = new SelectChannelConnector();
+ _server.setConnectors(new Connector[]{ _connector });
+ ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SECURITY|ServletContextHandler.NO_SESSIONS);
+ context.setContextPath("/ctx");
+ _server.setHandler(context);
+ _servletHandler=context.getServletHandler();
+ ServletHolder holder=new ServletHolder(_servlet);
+ holder.setAsyncSupported(true);
+ _servletHandler.addServletWithMapping(holder,"/path/*");
+ _servletHandler.addServletWithMapping(holder,"/path1/*");
+ _servletHandler.addServletWithMapping(holder,"/path2/*");
+ _servletHandler.addServletWithMapping(new ServletHolder(new FwdServlet()),"/fwd/*");
+ _server.start();
+ _port=_connector.getLocalPort();
+ }
+
+ @After
+ public void tearDown() throws Exception
+ {
+ _server.stop();
+ }
+
+ @Test
+ public void testNormal() throws Exception
+ {
+ String response=process(null,null);
+ assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
+ assertContains(
+ "history: REQUEST /path\r\n"+
+ "history: initial\r\n",response);
+ assertContains("NORMAL",response);
+ assertNotContains("history: onTimeout",response);
+ assertNotContains("history: onComplete",response);
+ }
+
+ @Test
+ public void testSleep() throws Exception
+ {
+ String response=process("sleep=200",null);
+ assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
+ assertContains(
+ "history: REQUEST /path\r\n"+
+ "history: initial\r\n",response);
+ assertContains("SLEPT",response);
+ assertNotContains("history: onTimeout",response);
+ assertNotContains("history: onComplete",response);
+ }
+
+ @Test
+ public void testSuspend() throws Exception
+ {
+ String response=process("suspend=200",null);
+ assertEquals("HTTP/1.1 500 Async Timeout",response.substring(0,26));
+ assertContains(
+ "history: REQUEST /path\r\n"+
+ "history: initial\r\n"+
+ "history: suspend\r\n"+
+ "history: onTimeout\r\n"+
+ "history: ERROR /path\r\n"+
+ "history: !initial\r\n"+
+ "history: onComplete\r\n",response);
+
+ assertContains("ERROR: /ctx/path/info",response);
+ }
+
+ @Test
+ public void testSuspendOnTimeoutDispatch() throws Exception
+ {
+ String response=process("suspend=200&timeout=dispatch",null);
+ assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
+ assertContains(
+ "history: REQUEST /path\r\n"+
+ "history: initial\r\n"+
+ "history: suspend\r\n"+
+ "history: onTimeout\r\n"+
+ "history: dispatch\r\n"+
+ "history: ASYNC /path\r\n"+
+ "history: !initial\r\n"+
+ "history: onComplete\r\n",response);
+
+ assertContains("DISPATCHED",response);
+ }
+
+ @Test
+ public void testSuspendOnTimeoutComplete() throws Exception
+ {
+ String response=process("suspend=200&timeout=complete",null);
+ assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
+ assertContains(
+ "history: REQUEST /path\r\n"+
+ "history: initial\r\n"+
+ "history: suspend\r\n"+
+ "history: onTimeout\r\n"+
+ "history: complete\r\n"+
+ "history: onComplete\r\n",response);
+
+ assertContains("COMPLETED",response);
+ }
+
+ @Test
+ public void testSuspendWaitResume() throws Exception
+ {
+ String response=process("suspend=200&resume=10",null);
+ assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
+ assertContains(
+ "history: REQUEST /path\r\n"+
+ "history: initial\r\n"+
+ "history: suspend\r\n"+
+ "history: resume\r\n"+
+ "history: ASYNC /path\r\n"+
+ "history: !initial\r\n"+
+ "history: onComplete\r\n",response);
+ assertNotContains("history: onTimeout",response);
+ }
+
+ @Test
+ public void testSuspendResume() throws Exception
+ {
+ String response=process("suspend=200&resume=0",null);
+ assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
+ assertContains(
+ "history: REQUEST /path\r\n"+
+ "history: initial\r\n"+
+ "history: suspend\r\n"+
+ "history: resume\r\n"+
+ "history: ASYNC /path\r\n"+
+ "history: !initial\r\n"+
+ "history: onComplete\r\n",response);
+ assertContains("history: onComplete",response);
+ }
+
+ @Test
+ public void testSuspendWaitComplete() throws Exception
+ {
+ String response=process("suspend=200&complete=50",null);
+ assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
+ assertContains(
+ "history: REQUEST /path\r\n"+
+ "history: initial\r\n"+
+ "history: suspend\r\n"+
+ "history: complete\r\n"+
+ "history: onComplete\r\n",response);
+ assertContains("COMPLETED",response);
+ assertNotContains("history: onTimeout",response);
+ assertNotContains("history: !initial",response);
+ }
+
+ @Test
+ public void testSuspendComplete() throws Exception
+ {
+ String response=process("suspend=200&complete=0",null);
+ assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
+ assertContains(
+ "history: REQUEST /path\r\n"+
+ "history: initial\r\n"+
+ "history: suspend\r\n"+
+ "history: complete\r\n"+
+ "history: onComplete\r\n",response);
+ assertContains("COMPLETED",response);
+ assertNotContains("history: onTimeout",response);
+ assertNotContains("history: !initial",response);
+ }
+
+ @Test
+ public void testSuspendWaitResumeSuspendWaitResume() throws Exception
+ {
+ String response=process("suspend=1000&resume=10&suspend2=1000&resume2=10",null);
+ assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
+ assertContains(
+ "history: REQUEST /path\r\n"+
+ "history: initial\r\n"+
+ "history: suspend\r\n"+
+ "history: resume\r\n"+
+ "history: ASYNC /path\r\n"+
+ "history: !initial\r\n"+
+ "history: suspend\r\n"+
+ "history: resume\r\n"+
+ "history: ASYNC /path\r\n"+
+ "history: !initial\r\n"+
+ "history: onComplete\r\n",response);
+ assertContains("DISPATCHED",response);
+ }
+
+ @Test
+ public void testSuspendWaitResumeSuspendComplete() throws Exception
+ {
+ String response=process("suspend=1000&resume=10&suspend2=1000&complete2=10",null);
+ assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
+ assertContains(
+ "history: REQUEST /path\r\n"+
+ "history: initial\r\n"+
+ "history: suspend\r\n"+
+ "history: resume\r\n"+
+ "history: ASYNC /path\r\n"+
+ "history: !initial\r\n"+
+ "history: suspend\r\n"+
+ "history: complete\r\n"+
+ "history: onComplete\r\n",response);
+ assertContains("COMPLETED",response);
+ }
+
+ @Test
+ public void testSuspendWaitResumeSuspend() throws Exception
+ {
+ String response=process("suspend=1000&resume=10&suspend2=10",null);
+ assertEquals("HTTP/1.1 500 Async Timeout",response.substring(0,26));
+ assertContains(
+ "history: REQUEST /path\r\n"+
+ "history: initial\r\n"+
+ "history: suspend\r\n"+
+ "history: resume\r\n"+
+ "history: ASYNC /path\r\n"+
+ "history: !initial\r\n"+
+ "history: suspend\r\n"+
+ "history: onTimeout\r\n"+
+ "history: ERROR /path\r\n"+
+ "history: !initial\r\n"+
+ "history: onComplete\r\n",response);
+ assertContains("ERROR: /ctx/path/info",response);
+ }
+
+ @Test
+ public void testSuspendTimeoutSuspendResume() throws Exception
+ {
+ String response=process("suspend=10&suspend2=1000&resume2=10",null);
+ assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
+ assertContains(
+ "history: REQUEST /path\r\n"+
+ "history: initial\r\n"+
+ "history: suspend\r\n"+
+ "history: onTimeout\r\n"+
+ "history: ERROR /path\r\n"+
+ "history: !initial\r\n"+
+ "history: suspend\r\n"+
+ "history: resume\r\n"+
+ "history: ASYNC /path\r\n"+
+ "history: !initial\r\n"+
+ "history: onComplete\r\n",response);
+ assertContains("DISPATCHED",response);
+ }
+
+ @Test
+ public void testSuspendTimeoutSuspendComplete() throws Exception
+ {
+ String response=process("suspend=10&suspend2=1000&complete2=10",null);
+ assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
+ assertContains(
+ "history: REQUEST /path\r\n"+
+ "history: initial\r\n"+
+ "history: suspend\r\n"+
+ "history: onTimeout\r\n"+
+ "history: ERROR /path\r\n"+
+ "history: !initial\r\n"+
+ "history: suspend\r\n"+
+ "history: complete\r\n"+
+ "history: onComplete\r\n",response);
+ assertContains("COMPLETED",response);
+ }
+
+ @Test
+ public void testSuspendTimeoutSuspend() throws Exception
+ {
+ String response=process("suspend=10&suspend2=10",null);
+ assertContains(
+ "history: REQUEST /path\r\n"+
+ "history: initial\r\n"+
+ "history: suspend\r\n"+
+ "history: onTimeout\r\n"+
+ "history: ERROR /path\r\n"+
+ "history: !initial\r\n"+
+ "history: suspend\r\n"+
+ "history: onTimeout\r\n"+
+ "history: ERROR /path\r\n"+
+ "history: !initial\r\n"+
+ "history: onComplete\r\n",response);
+ assertContains("ERROR: /ctx/path/info",response);
+ }
+
+
+ @Test
+ public void testWrapStartDispatch() throws Exception
+ {
+ String response=process("wrap=true&suspend=200&resume=20",null);
+ assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
+ assertContains(
+ "history: REQUEST /path\r\n"+
+ "history: initial\r\n"+
+ "history: suspend\r\n"+
+ "history: resume\r\n"+
+ "history: ASYNC /path\r\n"+
+ "history: wrapped REQ RSP\r\n"+
+ "history: !initial\r\n"+
+ "history: onComplete\r\n",response);
+ assertContains("DISPATCHED",response);
+ }
+
+ @Test
+ public void testFwdStartDispatch() throws Exception
+ {
+ String response=process("fwd","suspend=200&resume=20",null);
+ assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
+ assertContains(
+ "history: FWD REQUEST /fwd\r\n"+
+ "history: FORWARD /path1\r\n"+
+ "history: initial\r\n"+
+ "history: suspend\r\n"+
+ "history: resume\r\n"+
+ "history: FWD ASYNC /fwd\r\n"+
+ "history: FORWARD /path1\r\n"+
+ "history: !initial\r\n",response);
+ assertContains("DISPATCHED",response);
+ }
+
+ @Test
+ public void testFwdStartDispatchPath() throws Exception
+ {
+ String response=process("fwd","suspend=200&resume=20&path=/path2",null);
+ assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
+ assertContains(
+ "history: FWD REQUEST /fwd\r\n"+
+ "history: FORWARD /path1\r\n"+
+ "history: initial\r\n"+
+ "history: suspend\r\n"+
+ "history: resume\r\n"+
+ "history: ASYNC /path2\r\n"+
+ "history: !initial\r\n"+
+ "history: onComplete\r\n",response);
+ assertContains("DISPATCHED",response);
+ }
+
+ @Test
+ public void testFwdWrapStartDispatch() throws Exception
+ {
+ String response=process("fwd","wrap=true&suspend=200&resume=20",null);
+ assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
+ assertContains(
+ "history: FWD REQUEST /fwd\r\n"+
+ "history: FORWARD /path1\r\n"+
+ "history: initial\r\n"+
+ "history: suspend\r\n"+
+ "history: resume\r\n"+
+ "history: ASYNC /path1\r\n"+
+ "history: wrapped REQ RSP\r\n"+
+ "history: !initial\r\n"+
+ "history: onComplete\r\n",response);
+ assertContains("DISPATCHED",response);
+ }
+
+ @Test
+ public void testFwdWrapStartDispatchPath() throws Exception
+ {
+ String response=process("fwd","wrap=true&suspend=200&resume=20&path=/path2",null);
+ assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
+ assertContains(
+ "history: FWD REQUEST /fwd\r\n"+
+ "history: FORWARD /path1\r\n"+
+ "history: initial\r\n"+
+ "history: suspend\r\n"+
+ "history: resume\r\n"+
+ "history: ASYNC /path2\r\n"+
+ "history: wrapped REQ RSP\r\n"+
+ "history: !initial\r\n"+
+ "history: onComplete\r\n",response);
+ assertContains("DISPATCHED",response);
+ }
+
+
+
+ protected void assertContains(String content,String response)
+ {
+ Assert.assertThat(response,Matchers.containsString(content));
+ }
+
+ protected void assertNotContains(String content,String response)
+ {
+ Assert.assertThat(response,Matchers.not(Matchers.containsString(content)));
+ }
+
+ public synchronized String process(String query,String content) throws Exception
+ {
+ return process("path",query,content);
+ }
+
+ public synchronized String process(String path,String query,String content) throws Exception
+ {
+ String request = "GET /ctx/"+path+"/info";
+
+ if (query!=null)
+ request+="?"+query;
+ request+=" HTTP/1.1\r\n"+
+ "Host: localhost\r\n"+
+ "Connection: close\r\n";
+ if (content==null)
+ request+="\r\n";
+ else
+ {
+ request+="Content-Length: "+content.length()+"\r\n";
+ request+="\r\n" + content;
+ }
+
+ int port=_port;
+ String response=null;
+ try
+ {
+ Socket socket = new Socket("localhost",port);
+ socket.setSoTimeout(1000000);
+ socket.getOutputStream().write(request.getBytes("UTF-8"));
+
+ response = IO.toString(socket.getInputStream());
+ }
+ catch(Exception e)
+ {
+ System.err.println("failed on port "+port);
+ e.printStackTrace();
+ throw e;
+ }
+ return response;
+ }
+
+ private static class FwdServlet extends HttpServlet
+ {
+ @Override
+ public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
+ {
+ response.addHeader("history","FWD "+request.getDispatcherType()+" "+request.getServletPath());
+ if (request instanceof ServletRequestWrapper || response instanceof ServletResponseWrapper)
+ response.addHeader("history","wrapped"+((request instanceof ServletRequestWrapper)?" REQ":"")+((response instanceof ServletResponseWrapper)?" RSP":""));
+ request.getServletContext().getRequestDispatcher("/path1").forward(request,response);
+ }
+ }
+
+
+ private static class AsyncServlet extends HttpServlet
+ {
+ private static final long serialVersionUID = -8161977157098646562L;
+ private Timer _timer=new Timer();
+
+ public AsyncServlet()
+ {}
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
+ {
+ response.addHeader("history",request.getDispatcherType()+" "+request.getServletPath());
+ if (request instanceof ServletRequestWrapper || response instanceof ServletResponseWrapper)
+ response.addHeader("history","wrapped"+((request instanceof ServletRequestWrapper)?" REQ":"")+((response instanceof ServletResponseWrapper)?" RSP":""));
+
+ boolean wrap="true".equals(request.getParameter("wrap"));
+ int read_before=0;
+ long sleep_for=-1;
+ long suspend_for=-1;
+ long suspend2_for=-1;
+ long resume_after=-1;
+ long resume2_after=-1;
+ long complete_after=-1;
+ long complete2_after=-1;
+
+ if (request.getParameter("read")!=null)
+ read_before=Integer.parseInt(request.getParameter("read"));
+ if (request.getParameter("sleep")!=null)
+ sleep_for=Integer.parseInt(request.getParameter("sleep"));
+ if (request.getParameter("suspend")!=null)
+ suspend_for=Integer.parseInt(request.getParameter("suspend"));
+ if (request.getParameter("suspend2")!=null)
+ suspend2_for=Integer.parseInt(request.getParameter("suspend2"));
+ if (request.getParameter("resume")!=null)
+ resume_after=Integer.parseInt(request.getParameter("resume"));
+ final String path=request.getParameter("path");
+ if (request.getParameter("resume2")!=null)
+ resume2_after=Integer.parseInt(request.getParameter("resume2"));
+ if (request.getParameter("complete")!=null)
+ complete_after=Integer.parseInt(request.getParameter("complete"));
+ if (request.getParameter("complete2")!=null)
+ complete2_after=Integer.parseInt(request.getParameter("complete2"));
+
+ if (request.getAttribute("State")==null)
+ {
+ request.setAttribute("State",new Integer(1));
+
+ ((HttpServletResponse)response).addHeader("history","initial");
+ if (read_before>0)
+ {
+ byte[] buf=new byte[read_before];
+ request.getInputStream().read(buf);
+ }
+ else if (read_before<0)
+ {
+ InputStream in = request.getInputStream();
+ int b=in.read();
+ while(b!=-1)
+ b=in.read();
+ }
+
+ if (suspend_for>=0)
+ {
+ final AsyncContext async=wrap?request.startAsync(new HttpServletRequestWrapper(request),new HttpServletResponseWrapper(response)):request.startAsync();
+ if (suspend_for>0)
+ async.setTimeout(suspend_for);
+ async.addListener(__listener);
+ ((HttpServletResponse)response).addHeader("history","suspend");
+
+ if (complete_after>0)
+ {
+ TimerTask complete = new TimerTask()
+ {
+ @Override
+ public void run()
+ {
+ try
+ {
+ response.setStatus(200);
+ response.getOutputStream().println("COMPLETED\n");
+ response.addHeader("history","complete");
+ async.complete();
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ };
+ synchronized (_timer)
+ {
+ _timer.schedule(complete,complete_after);
+ }
+ }
+ else if (complete_after==0)
+ {
+ response.setStatus(200);
+ response.getOutputStream().println("COMPLETED\n");
+ response.addHeader("history","complete");
+ async.complete();
+ }
+ else if (resume_after>0)
+ {
+ TimerTask resume = new TimerTask()
+ {
+ @Override
+ public void run()
+ {
+ ((HttpServletResponse)async.getResponse()).addHeader("history","resume");
+ if (path!=null)
+ async.dispatch(path);
+ else
+ async.dispatch();
+ }
+ };
+ synchronized (_timer)
+ {
+ _timer.schedule(resume,resume_after);
+ }
+ }
+ else if (resume_after==0)
+ {
+ ((HttpServletResponse)async.getResponse()).addHeader("history","resume");
+ if (path!=null)
+ async.dispatch(path);
+ else
+ async.dispatch();
+ }
+
+ }
+ else if (sleep_for>=0)
+ {
+ try
+ {
+ Thread.sleep(sleep_for);
+ }
+ catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ }
+ response.setStatus(200);
+ response.getOutputStream().println("SLEPT\n");
+ }
+ else
+ {
+ response.setStatus(200);
+ response.getOutputStream().println("NORMAL\n");
+ }
+ }
+ else
+ {
+ ((HttpServletResponse)response).addHeader("history","!initial");
+
+ if (suspend2_for>=0 && request.getAttribute("2nd")==null)
+ {
+ final AsyncContext async=wrap?request.startAsync(new HttpServletRequestWrapper(request),new HttpServletResponseWrapper(response)):request.startAsync();
+ async.addListener(__listener);
+ request.setAttribute("2nd","cycle");
+
+ if (suspend2_for>0)
+ {
+ async.setTimeout(suspend2_for);
+ }
+ // continuation.addContinuationListener(__listener);
+ ((HttpServletResponse)response).addHeader("history","suspend");
+
+ if (complete2_after>0)
+ {
+ TimerTask complete = new TimerTask()
+ {
+ @Override
+ public void run()
+ {
+ try
+ {
+ response.setStatus(200);
+ response.getOutputStream().println("COMPLETED\n");
+ response.addHeader("history","complete");
+ async.complete();
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ };
+ synchronized (_timer)
+ {
+ _timer.schedule(complete,complete2_after);
+ }
+ }
+ else if (complete2_after==0)
+ {
+ response.setStatus(200);
+ response.getOutputStream().println("COMPLETED\n");
+ response.addHeader("history","complete");
+ async.complete();
+ }
+ else if (resume2_after>0)
+ {
+ TimerTask resume = new TimerTask()
+ {
+ @Override
+ public void run()
+ {
+ ((HttpServletResponse)response).addHeader("history","resume");
+ async.dispatch();
+ }
+ };
+ synchronized (_timer)
+ {
+ _timer.schedule(resume,resume2_after);
+ }
+ }
+ else if (resume2_after==0)
+ {
+ ((HttpServletResponse)response).addHeader("history","dispatch");
+ async.dispatch();
+ }
+ }
+ else if(request.getDispatcherType()==DispatcherType.ERROR)
+ {
+ response.getOutputStream().println("ERROR: "+request.getContextPath()+request.getServletPath()+request.getPathInfo());
+ }
+ else
+ {
+ response.setStatus(200);
+ response.getOutputStream().println("DISPATCHED");
+ }
+ }
+ }
+ }
+
+
+ private static AsyncListener __listener = new AsyncListener()
+ {
+ @Override
+ public void onTimeout(AsyncEvent event) throws IOException
+ {
+ ((HttpServletResponse)event.getSuppliedResponse()).addHeader("history","onTimeout");
+ String action=((HttpServletRequest)event.getSuppliedRequest()).getParameter("timeout");
+ if (action!=null)
+ {
+ ((HttpServletResponse)event.getSuppliedResponse()).addHeader("history",action);
+ if ("dispatch".equals(action))
+ event.getAsyncContext().dispatch();
+ if ("complete".equals(action))
+ {
+ ((HttpServletResponse)event.getSuppliedResponse()).getOutputStream().println("COMPLETED\n");
+ event.getAsyncContext().complete();
+ }
+ }
+ }
+
+ @Override
+ public void onStartAsync(AsyncEvent event) throws IOException
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void onError(AsyncEvent event) throws IOException
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void onComplete(AsyncEvent event) throws IOException
+ {
+ ((HttpServletResponse)event.getSuppliedResponse()).addHeader("history","onComplete");
+ }
+ };
+
+}
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DefaultServletTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DefaultServletTest.java
index ce403ba..9983ea8 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DefaultServletTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DefaultServletTest.java
@@ -23,9 +23,10 @@
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.util.EnumSet;
+import javax.servlet.DispatcherType;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
@@ -42,7 +43,6 @@
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.OS;
import org.eclipse.jetty.toolchain.test.TestingDir;
-import org.eclipse.jetty.util.DateCache;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.StringUtil;
import org.hamcrest.Matchers;
@@ -659,7 +659,7 @@
assertResponseContains("Content-Length: 12", response);
assertResponseNotContains("Extra Info", response);
- context.addFilter(OutputFilter.class, "/*", 0);
+ context.addFilter(OutputFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
response = connector.getResponses("GET /context/data0.txt HTTP/1.1\r\nHost:localhost:8080\r\n\r\n");
assertResponseContains("Content-Length: 2", response); // 20 something long
assertResponseContains("Extra Info", response);
@@ -668,7 +668,7 @@
context.getServletHandler().setFilterMappings(new FilterMapping[]{});
context.getServletHandler().setFilters(new FilterHolder[]{});
- context.addFilter(WriterFilter.class, "/*", 0);
+ context.addFilter(WriterFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
response = connector.getResponses("GET /context/data0.txt HTTP/1.1\r\nHost:localhost:8080\r\n\r\n");
assertResponseContains("Content-Length: 2", response); // 20 something long
assertResponseContains("Extra Info", response);
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java
index b31eaf4..ceec453 100644
--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java
@@ -26,9 +26,11 @@
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
-import java.util.Enumeration;
+import java.util.EnumSet;
+import java.util.HashMap;
import java.util.List;
+import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
@@ -225,6 +227,32 @@
}
@Test
+ public void testServletForwardDotDot() throws Exception
+ {
+ _contextHandler.addServlet(DispatchServletServlet.class, "/dispatch/*");
+ _contextHandler.addServlet(RogerThatServlet.class, "/roger/that");
+
+ String requests="GET /context/dispatch/test?forward=/%2e%2e/roger/that HTTP/1.0\n" + "Host: localhost\n\n";
+
+ String responses = _connector.getResponses(requests);
+
+ assertThat(responses,startsWith("HTTP/1.1 404 "));
+ }
+
+ @Test
+ public void testServletForwardEncodedDotDot() throws Exception
+ {
+ _contextHandler.addServlet(DispatchServletServlet.class, "/dispatch/*");
+ _contextHandler.addServlet(RogerThatServlet.class, "/roger/that");
+
+ String requests="GET /context/dispatch/test?forward=/%252e%252e/roger/that HTTP/1.0\n" + "Host: localhost\n\n";
+
+ String responses = _connector.getResponses(requests);
+
+ assertThat(responses,startsWith("HTTP/1.1 404 "));
+ }
+
+ @Test
public void testServletInclude() throws Exception
{
_contextHandler.addServlet(DispatchServletServlet.class, "/dispatch/*");
@@ -301,7 +329,7 @@
_contextHandler.addServlet(RogerThatServlet.class, "/*");
_contextHandler.addServlet(ReserveEchoServlet.class,"/recho/*");
_contextHandler.addServlet(EchoServlet.class, "/echo/*");
- _contextHandler.addFilter(ForwardFilter.class, "/*", FilterMapping.REQUEST);
+ _contextHandler.addFilter(ForwardFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
String rogerResponse = _connector.getResponses("GET /context/ HTTP/1.0\n" + "Host: localhost\n\n");
@@ -416,7 +444,10 @@
else if(request.getParameter("forward")!=null)
{
dispatcher = getServletContext().getRequestDispatcher(request.getParameter("forward"));
- dispatcher.forward(new ServletRequestWrapper(request), new ServletResponseWrapper(response));
+ if (dispatcher!=null)
+ dispatcher.forward(new ServletRequestWrapper(request), new ServletResponseWrapper(response));
+ else
+ response.sendError(404);
}
}
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/HolderTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/HolderTest.java
new file mode 100644
index 0000000..a6a3256
--- /dev/null
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/HolderTest.java
@@ -0,0 +1,95 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.
+// ========================================================================
+//
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+package org.eclipse.jetty.servlet;
+
+import java.util.Collections;
+import java.util.Set;
+
+import javax.servlet.Registration;
+import javax.servlet.ServletRegistration;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class HolderTest {
+
+ @Test
+ public void testInitParams() throws Exception {
+ ServletHolder holder = new ServletHolder(Holder.Source.JAVAX_API);
+ ServletRegistration reg = holder.getRegistration();
+ try {
+ reg.setInitParameter(null, "foo");
+ fail("null name accepted");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ reg.setInitParameter("foo", null);
+ fail("null value accepted");
+ } catch (IllegalArgumentException e) {
+ }
+ reg.setInitParameter("foo", "bar");
+ assertFalse(reg.setInitParameter("foo", "foo"));
+
+ Set<String> clash = reg.setInitParameters(Collections.singletonMap("foo", "bax"));
+ assertTrue("should be one clash", clash != null && clash.size() == 1);
+
+ try {
+ reg.setInitParameters(Collections.singletonMap((String)null, "bax"));
+ fail("null name in map accepted");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ reg.setInitParameters(Collections.singletonMap("foo", (String)null));
+ fail("null value in map accepted");
+ } catch (IllegalArgumentException e) {
+ }
+
+ Set<String> clash2 = reg.setInitParameters(Collections.singletonMap("FOO", "bax"));
+ assertTrue("should be no clash", clash2.isEmpty());
+ assertEquals("setInitParameters should not replace existing non-clashing init parameters", 2, reg.getInitParameters().size());
+
+ }
+}
diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletHandlerTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletHandlerTest.java
new file mode 100644
index 0000000..05acf62
--- /dev/null
+++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletHandlerTest.java
@@ -0,0 +1,425 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.servlet;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.EnumSet;
+
+import javax.servlet.DispatcherType;
+
+import org.eclipse.jetty.servlet.Holder.Source;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ServletHandlerTest
+{
+ FilterHolder fh1 = new FilterHolder(Holder.Source.DESCRIPTOR);
+ FilterMapping fm1 = new FilterMapping();
+
+ FilterHolder fh2 = new FilterHolder(Holder.Source.DESCRIPTOR);
+ FilterMapping fm2 = new FilterMapping();
+
+ FilterHolder fh3 = new FilterHolder(Holder.Source.JAVAX_API);
+ FilterMapping fm3 = new FilterMapping();
+
+ FilterHolder fh4 = new FilterHolder(Holder.Source.JAVAX_API);
+ FilterMapping fm4 = new FilterMapping();
+
+ FilterHolder fh5 = new FilterHolder(Holder.Source.JAVAX_API);
+ FilterMapping fm5 = new FilterMapping();
+
+
+
+ @Before
+ public void initMappings()
+ {
+ fh1.setName("fh1");
+ fm1.setPathSpec("/*");
+ fm1.setFilterHolder(fh1);
+
+ fh2.setName("fh2");
+ fm2.setPathSpec("/*");
+ fm2.setFilterHolder(fh2);
+
+ fh3.setName("fh3");
+ fm3.setPathSpec("/*");
+ fm3.setFilterHolder(fh3);
+
+ fh4.setName("fh4");
+ fm4.setPathSpec("/*");
+ fm4.setFilterHolder(fh4);
+
+ fh5.setName("fh5");
+ fm5.setPathSpec("/*");
+ fm5.setFilterHolder(fh5);
+ }
+
+ @Test
+ public void testAllNonProgrammaticFilterMappings() throws Exception
+ {
+ ServletHandler handler = new ServletHandler();
+ handler.addFilter(fh1);
+ handler.addFilter(fh2);
+
+ //add some ordinary filter mappings
+ handler.addFilterMapping(fm1);
+ handler.addFilterMapping(fm2);
+
+ FilterMapping[] mappings = handler.getFilterMappings();
+ assertNotNull(mappings);
+ assertTrue(fm1 == mappings[0]);
+ assertTrue(fm2 == mappings[1]);
+
+ //add another ordinary mapping
+ FilterHolder of1 = new FilterHolder(Source.DESCRIPTOR);
+ FilterMapping ofm1 = new FilterMapping();
+ ofm1.setFilterHolder(of1);
+ ofm1.setPathSpec("/*");
+ handler.addFilter(of1);
+ handler.addFilterMapping(ofm1);
+
+ mappings = handler.getFilterMappings();
+ assertNotNull(mappings);
+ assertTrue(fm1 == mappings[0]);
+ assertTrue(fm2 == mappings[1]);
+ assertTrue(ofm1 == mappings[2]);
+ }
+
+
+
+ @Test
+ public void testAllBeforeFilterMappings() throws Exception
+ {
+ ServletHandler handler = new ServletHandler();
+
+ //do equivalent of FilterRegistration.addMappingForUrlPatterns(isMatchAfter=false)
+ handler.addFilter(fh4);
+ handler.prependFilterMapping(fm4);
+
+ FilterMapping[] mappings = handler.getFilterMappings();
+ assertNotNull(mappings);
+ assertEquals(1, mappings.length);
+
+ //add another with isMatchAfter=false
+ handler.addFilter(fh5);
+ handler.prependFilterMapping(fm5);
+
+ mappings = handler.getFilterMappings();
+ assertNotNull(mappings);
+ assertEquals(2, mappings.length);
+
+ assertTrue(fm4 == mappings[0]);
+ assertTrue(fm5 == mappings[1]);
+ }
+
+ @Test
+ public void testAllAfterFilterMappings() throws Exception
+ {
+ ServletHandler handler = new ServletHandler();
+ //do equivalent of FilterRegistration.addMappingForUrlPatterns(isMatchAfter=true)
+ handler.addFilter(fh4);
+ handler.addFilterMapping(fm4);
+ FilterMapping[] mappings = handler.getFilterMappings();
+ assertEquals(1, mappings.length);
+ assertTrue(fm4 == mappings[0]);
+
+ //do equivalent of FilterRegistration.addMappingForUrlPatterns(isMatchAfter=true)
+ handler.addFilter(fh5);
+ handler.addFilterMapping(fm5);
+ mappings = handler.getFilterMappings();
+ assertEquals(2, mappings.length);
+ assertTrue(fm4 == mappings[0]);
+ assertTrue(fm5 == mappings[1]);
+ }
+
+
+ @Test
+ public void testMatchAfterAndBefore() throws Exception
+ {
+ ServletHandler handler = new ServletHandler();
+
+ //add a programmatic one, isMatchAfter=true
+ handler.addFilter(fh3);
+ handler.addFilterMapping(fm3);
+ FilterMapping[] mappings = handler.getFilterMappings();
+ assertNotNull(mappings);
+ assertEquals(1, mappings.length);
+ assertTrue(fm3 == mappings[0]);
+
+ //add a programmatic one, isMatchAfter=false
+ handler.addFilter(fh4);
+ handler.prependFilterMapping(fm4);
+ mappings = handler.getFilterMappings();
+ assertNotNull(mappings);
+ assertEquals(2, mappings.length);
+ assertTrue(fm4 == mappings[0]);
+ assertTrue(fm3 == mappings[1]);
+ }
+
+
+ @Test
+ public void testMatchBeforeAndAfter() throws Exception
+ {
+ ServletHandler handler = new ServletHandler();
+
+ //add a programmatic one, isMatchAfter=false
+ handler.addFilter(fh3);
+ handler.prependFilterMapping(fm3);
+ FilterMapping[] mappings = handler.getFilterMappings();
+ assertNotNull(mappings);
+ assertEquals(1, mappings.length);
+ assertTrue(fm3 == mappings[0]);
+
+ //add a programmatic one, isMatchAfter=true
+ handler.addFilter(fh4);
+ handler.addFilterMapping(fm4);
+ mappings = handler.getFilterMappings();
+ assertNotNull(mappings);
+ assertEquals(2, mappings.length);
+ assertTrue(fm3 == mappings[0]);
+ assertTrue(fm4 == mappings[1]);
+ }
+
+
+ @Test
+ public void testExistingFilterMappings() throws Exception
+ {
+ ServletHandler handler = new ServletHandler();
+ handler.addFilter(fh1);
+ handler.addFilter(fh2);
+
+ //add some ordinary filter mappings first
+ handler.addFilterMapping(fm1);
+ handler.addFilterMapping(fm2);
+
+ FilterMapping[] mappings = handler.getFilterMappings();
+ assertNotNull(mappings);
+ assertTrue(fm1 == mappings[0]);
+ assertTrue(fm2 == mappings[1]);
+
+ //do equivalent of FilterRegistration.addMappingForUrlPatterns(isMatchAfter=false)
+ handler.addFilter(fh4);
+ handler.prependFilterMapping(fm4);
+ mappings = handler.getFilterMappings();
+ assertEquals(3, mappings.length);
+ assertTrue(fm4 == mappings[0]);
+
+ //do equivalent of FilterRegistration.addMappingForUrlPatterns(isMatchAfter=true)
+ handler.addFilter(fh5);
+ handler.addFilterMapping(fm5);
+ mappings = handler.getFilterMappings();
+ assertEquals(4, mappings.length);
+ assertTrue(fm5 == mappings[mappings.length-1]);
+ }
+
+ @Test
+ public void testFilterMappingsMix() throws Exception
+ {
+ ServletHandler handler = new ServletHandler();
+
+ //add a non-programmatic one to begin with
+ handler.addFilter(fh1);
+ handler.addFilterMapping(fm1);
+ FilterMapping[] mappings = handler.getFilterMappings();
+ assertNotNull(mappings);
+ assertTrue(fm1 == mappings[0]);
+
+ //add a programmatic one, isMatchAfter=false
+ handler.addFilter(fh4);
+ handler.prependFilterMapping(fm4);
+ mappings = handler.getFilterMappings();
+ assertNotNull(mappings);
+ assertEquals(2, mappings.length);
+ assertTrue(fm4 == mappings[0]);
+ assertTrue(fm1 == mappings[1]);
+
+ //add a programmatic one, isMatchAfter=true
+ handler.addFilter(fh3);
+ handler.addFilterMapping(fm3);
+ mappings = handler.getFilterMappings();
+ assertNotNull(mappings);
+ assertEquals(3, mappings.length);
+ assertTrue(fm4 == mappings[0]);
+ assertTrue(fm1 == mappings[1]);
+ assertTrue(fm3 == mappings[2]);
+
+ //add a programmatic one, isMatchAfter=false
+ handler.addFilter(fh5);
+ handler.prependFilterMapping(fm5);
+ mappings = handler.getFilterMappings();
+ assertNotNull(mappings);
+ assertEquals(4, mappings.length);
+ assertTrue(fm4 == mappings[0]);//isMatchAfter = false;
+ assertTrue(fm5 == mappings[1]);//isMatchAfter = false;
+ assertTrue(fm1 == mappings[2]);//ordinary
+ assertTrue(fm3 == mappings[3]);//isMatchAfter = true;
+
+ //add a non-programmatic one
+ FilterHolder f = new FilterHolder(Source.EMBEDDED);
+ f.setName("non-programmatic");
+ FilterMapping fm = new FilterMapping();
+ fm.setFilterHolder(f);
+ fm.setPathSpec("/*");
+ handler.addFilter(f);
+ handler.addFilterMapping(fm);
+ mappings = handler.getFilterMappings();
+ assertNotNull(mappings);
+ assertEquals(5, mappings.length);
+ assertTrue(fm4 == mappings[0]); //isMatchAfter = false;
+ assertTrue(fm5 == mappings[1]); //isMatchAfter = false;
+ assertTrue(fm1 == mappings[2]); //ordinary
+ assertTrue(fm == mappings[3]); //ordinary
+ assertTrue(fm3 == mappings[4]); //isMatchAfter = true;
+
+ //add a programmatic one, isMatchAfter=true
+ FilterHolder pf = new FilterHolder(Source.JAVAX_API);
+ pf.setName("programmaticA");
+ FilterMapping pfm = new FilterMapping();
+ pfm.setFilterHolder(pf);
+ pfm.setPathSpec("/*");
+ handler.addFilter(pf);
+ handler.addFilterMapping(pfm);
+ mappings = handler.getFilterMappings();
+ assertNotNull(mappings);
+ assertEquals(6, mappings.length);
+ assertTrue(fm4 == mappings[0]); //isMatchAfter = false;
+ assertTrue(fm5 == mappings[1]); //isMatchAfter = false;
+ assertTrue(fm1 == mappings[2]); //ordinary
+ assertTrue(fm == mappings[3]); //ordinary
+ assertTrue(fm3 == mappings[4]); //isMatchAfter = true;
+ assertTrue(pfm == mappings[5]); //isMatchAfter = true;
+
+ //add a programmatic one, isMatchAfter=false
+ FilterHolder pf2 = new FilterHolder(Source.JAVAX_API);
+ pf2.setName("programmaticB");
+ FilterMapping pfm2 = new FilterMapping();
+ pfm2.setFilterHolder(pf2);
+ pfm2.setPathSpec("/*");
+ handler.addFilter(pf2);
+ handler.prependFilterMapping(pfm2);
+ mappings = handler.getFilterMappings();
+ assertNotNull(mappings);
+ assertEquals(7, mappings.length);
+ assertTrue(fm4 == mappings[0]); //isMatchAfter = false;
+ assertTrue(fm5 == mappings[1]); //isMatchAfter = false;
+ assertTrue(pfm2 == mappings[2]);//isMatchAfter = false;
+ assertTrue(fm1 == mappings[3]); //ordinary
+ assertTrue(fm == mappings[4]); //ordinary
+ assertTrue(fm3 == mappings[5]); //isMatchAfter = true;
+ assertTrue(pfm == mappings[6]); //isMatchAfter = true;
+ }
+
+ @Test
+ public void testAddFilterWithMappingAPI() throws Exception
+ {
+ ServletHandler handler = new ServletHandler();
+
+ //add a non-programmatic one to begin with
+ handler.addFilterWithMapping(fh1, "/*", EnumSet.allOf(DispatcherType.class));
+ FilterMapping[] mappings = handler.getFilterMappings();
+ assertNotNull(mappings);
+ assertTrue(fh1 == mappings[0].getFilterHolder());
+
+ //add a programmatic one, isMatchAfter=false
+ fh4.setServletHandler(handler);
+ handler.addFilter(fh4);
+ fh4.getRegistration().addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), false, "/*");
+ mappings = handler.getFilterMappings();
+ assertNotNull(mappings);
+ assertEquals(2, mappings.length);
+ assertTrue(fh4 == mappings[0].getFilterHolder());
+ assertTrue(fh1 == mappings[1].getFilterHolder());
+
+ //add a programmatic one, isMatchAfter=true
+ fh3.setServletHandler(handler);
+ handler.addFilter(fh3);
+ fh3.getRegistration().addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/*");
+ mappings = handler.getFilterMappings();
+ assertNotNull(mappings);
+ assertEquals(3, mappings.length);
+ assertTrue(fh4 == mappings[0].getFilterHolder());
+ assertTrue(fh1 == mappings[1].getFilterHolder());
+ assertTrue(fh3 == mappings[2].getFilterHolder());
+
+ //add a programmatic one, isMatchAfter=false
+ fh5.setServletHandler(handler);
+ handler.addFilter(fh5);
+ fh5.getRegistration().addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), false, "/*");
+ mappings = handler.getFilterMappings();
+ assertNotNull(mappings);
+ assertEquals(4, mappings.length);
+ assertTrue(fh4 == mappings[0].getFilterHolder());//isMatchAfter = false;
+ assertTrue(fh5 == mappings[1].getFilterHolder());//isMatchAfter = false;
+ assertTrue(fh1 == mappings[2].getFilterHolder());//ordinary
+ assertTrue(fh3 == mappings[3].getFilterHolder());//isMatchAfter = true;
+
+ //add a non-programmatic one
+ FilterHolder f = new FilterHolder(Source.EMBEDDED);
+ f.setName("non-programmatic");
+ handler.addFilterWithMapping(f, "/*", EnumSet.allOf(DispatcherType.class));
+ mappings = handler.getFilterMappings();
+ assertNotNull(mappings);
+ assertEquals(5, mappings.length);
+ assertTrue(fh4 == mappings[0].getFilterHolder()); //isMatchAfter = false;
+ assertTrue(fh5 == mappings[1].getFilterHolder()); //isMatchAfter = false;
+ assertTrue(fh1 == mappings[2].getFilterHolder()); //ordinary
+ assertTrue(f == mappings[3].getFilterHolder()); //ordinary
+ assertTrue(fh3 == mappings[4].getFilterHolder()); //isMatchAfter = true;
+
+ //add a programmatic one, isMatchAfter=true
+ FilterHolder pf = new FilterHolder(Source.JAVAX_API);
+ pf.setServletHandler(handler);
+ pf.setName("programmaticA");
+ handler.addFilter(pf);
+ pf.getRegistration().addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/*");
+ mappings = handler.getFilterMappings();
+ assertNotNull(mappings);
+ assertEquals(6, mappings.length);
+ assertTrue(fh4 == mappings[0].getFilterHolder()); //isMatchAfter = false;
+ assertTrue(fh5 == mappings[1].getFilterHolder()); //isMatchAfter = false;
+ assertTrue(fh1 == mappings[2].getFilterHolder()); //ordinary
+ assertTrue(f == mappings[3].getFilterHolder()); //ordinary
+ assertTrue(fh3 == mappings[4].getFilterHolder()); //isMatchAfter = true;
+ assertTrue(pf == mappings[5].getFilterHolder()); //isMatchAfter = true;
+
+ //add a programmatic one, isMatchAfter=false
+ FilterHolder pf2 = new FilterHolder(Source.JAVAX_API);
+ pf2.setServletHandler(handler);
+ pf2.setName("programmaticB");
+ handler.addFilter(pf2);
+ pf2.getRegistration().addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), false, "/*");
+ mappings = handler.getFilterMappings();
+
+ assertNotNull(mappings);
+ assertEquals(7, mappings.length);
+ assertTrue(fh4 == mappings[0].getFilterHolder()); //isMatchAfter = false;
+ assertTrue(fh5 == mappings[1].getFilterHolder()); //isMatchAfter = false;
+ assertTrue(pf2 == mappings[2].getFilterHolder());//isMatchAfter = false;
+ assertTrue(fh1 == mappings[3].getFilterHolder()); //ordinary
+ assertTrue(f == mappings[4].getFilterHolder()); //ordinary
+ assertTrue(fh3 == mappings[5].getFilterHolder()); //isMatchAfter = true;
+ assertTrue(pf == mappings[6].getFilterHolder()); //isMatchAfter = true;
+ }
+
+
+
+}
diff --git a/jetty-servlets/pom.xml b/jetty-servlets/pom.xml
index c21ec42..50b69df 100644
--- a/jetty-servlets/pom.xml
+++ b/jetty-servlets/pom.xml
@@ -3,7 +3,7 @@
<parent>
<artifactId>jetty-project</artifactId>
<groupId>org.eclipse.jetty</groupId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-servlets</artifactId>
@@ -24,6 +24,11 @@
<goals>
<goal>manifest</goal>
</goals>
+ <configuration>
+ <instructions>
+ <Import-Package>javax.servlet.*;version="2.6.0",*</Import-Package>
+ </instructions>
+ </configuration>
</execution>
</executions>
</plugin>
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java
index 4de1fa9..d8a5e19 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java
@@ -347,7 +347,6 @@
}
// We are over the limit.
- LOG.warn("DOS ALERT: ip=" + request.getRemoteAddr() + ",session=" + request.getRequestedSessionId() + ",user=" + request.getUserPrincipal());
// So either reject it, delay it or throttle it
long delayMs = getDelayMs();
@@ -357,6 +356,7 @@
case -1:
{
// Reject this request
+ LOG.warn("DOS ALERT: Request rejected ip=" + request.getRemoteAddr() + ",session=" + request.getRequestedSessionId() + ",user=" + request.getUserPrincipal());
if (insertHeaders)
response.addHeader("DoSFilter", "unavailable");
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
@@ -365,12 +365,14 @@
case 0:
{
// fall through to throttle code
+ LOG.warn("DOS ALERT: Request throttled ip=" + request.getRemoteAddr() + ",session=" + request.getRequestedSessionId() + ",user=" + request.getUserPrincipal());
request.setAttribute(__TRACKER, tracker);
break;
}
default:
{
// insert a delay before throttling the request
+ LOG.warn("DOS ALERT: Request delayed="+delayMs+"ms ip=" + request.getRemoteAddr() + ",session=" + request.getRequestedSessionId() + ",user=" + request.getUserPrincipal());
if (insertHeaders)
response.addHeader("DoSFilter", "delayed");
Continuation continuation = ContinuationSupport.getContinuation(request);
@@ -714,6 +716,7 @@
public void destroy()
{
+ LOG.debug("Destroy {}",this);
_running = false;
_timerThread.interrupt();
_requestTimeoutQ.cancelAll();
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java
index 044d314..b51afbd 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java
@@ -433,7 +433,30 @@
@Override
protected DeflaterOutputStream createStream() throws IOException
{
- return new GZIPOutputStream(_response.getOutputStream(),_bufferSize);
+ return new GZIPOutputStream(_response.getOutputStream(),_bufferSize)
+ {
+ /**
+ * Work around a bug in the jvm GzipOutputStream whereby it is not
+ * thread safe when thread A calls finish, but thread B is writing
+ * @see java.util.zip.GZIPOutputStream#finish()
+ */
+ @Override
+ public synchronized void finish() throws IOException
+ {
+ super.finish();
+ }
+
+ /**
+ * Work around a bug in the jvm GzipOutputStream whereby it is not
+ * thread safe when thread A calls close(), but thread B is writing
+ * @see java.util.zip.GZIPOutputStream#close()
+ */
+ @Override
+ public synchronized void close() throws IOException
+ {
+ super.close();
+ }
+ };
}
};
}
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java
index 065eb0e..8d6e16d 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java
@@ -18,18 +18,17 @@
package org.eclipse.jetty.servlets;
+import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
-import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
-import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
@@ -41,20 +40,22 @@
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
+import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.Part;
+import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.io.ByteArrayBuffer;
-import org.eclipse.jetty.util.B64Code;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.MultiMap;
+import org.eclipse.jetty.util.MultiPartInputStream;
import org.eclipse.jetty.util.QuotedStringTokenizer;
-import org.eclipse.jetty.util.ReadLineInputStream;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.log.Log;
@@ -79,16 +80,22 @@
* The init parameter deleteFiles controls if uploaded files are automatically deleted after the request
* completes.
*
+ * Use init parameter "maxFileSize" to set the max size file that can be uploaded.
+ *
+ * Use init parameter "maxRequestSize" to limit the size of the multipart request.
+ *
*/
public class MultiPartFilter implements Filter
{
private static final Logger LOG = Log.getLogger(MultiPartFilter.class);
public final static String CONTENT_TYPE_SUFFIX=".org.eclipse.jetty.servlet.contentType";
- private final static String FILES ="org.eclipse.jetty.servlet.MultiPartFilter.files";
+ private final static String MULTIPART = "org.eclipse.jetty.servlet.MultiPartFile.multiPartInputStream";
private File tempdir;
private boolean _deleteFiles;
private ServletContext _context;
private int _fileOutputBuffer = 0;
+ private long _maxFileSize = -1L;
+ private long _maxRequestSize = -1L;
private int _maxFormKeys = Integer.getInteger("org.eclipse.jetty.server.Request.maxFormKeys",1000).intValue();
/* ------------------------------------------------------------------------------- */
@@ -102,6 +109,13 @@
String fileOutputBuffer = filterConfig.getInitParameter("fileOutputBuffer");
if(fileOutputBuffer!=null)
_fileOutputBuffer = Integer.parseInt(fileOutputBuffer);
+ String maxFileSize = filterConfig.getInitParameter("maxFileSize");
+ if (maxFileSize != null)
+ _maxFileSize = Long.parseLong(maxFileSize.trim());
+ String maxRequestSize = filterConfig.getInitParameter("maxRequestSize");
+ if (maxRequestSize != null)
+ _maxRequestSize = Long.parseLong(maxRequestSize.trim());
+
_context=filterConfig.getServletContext();
String mfks = filterConfig.getInitParameter("maxFormKeys");
if (mfks!=null)
@@ -123,27 +137,14 @@
return;
}
- InputStream in = new ReadLineInputStream(request.getInputStream());
+ InputStream in = new BufferedInputStream(request.getInputStream());
String content_type=srequest.getContentType();
-
- // TODO - handle encodings
- String contentTypeBoundary = "";
- int bstart = content_type.indexOf("boundary=");
- if (bstart >= 0)
- {
- int bend = content_type.indexOf(";", bstart);
- bend = (bend < 0? content_type.length(): bend);
- contentTypeBoundary = QuotedStringTokenizer.unquote(value(content_type.substring(bstart,bend)).trim());
- }
-
- String boundary="--"+contentTypeBoundary;
- byte[] byteBoundary=(boundary+"--").getBytes(StringUtil.__ISO_8859_1);
-
- MultiMap params = new MultiMap();
- for (Iterator i = request.getParameterMap().entrySet().iterator();i.hasNext();)
+ //Get current parameters so we can merge into them
+ MultiMap<String> params = new MultiMap<String>();
+ for (Iterator<Map.Entry<String,String[]>> i = request.getParameterMap().entrySet().iterator();i.hasNext();)
{
- Map.Entry entry=(Map.Entry)i.next();
+ Map.Entry<String,String[]> entry=i.next();
Object value=entry.getValue();
if (value instanceof String[])
params.addValues(entry.getKey(),(String[])value);
@@ -151,327 +152,74 @@
params.add(entry.getKey(),value);
}
- boolean badFormatLogged = false;
+ MultipartConfigElement config = new MultipartConfigElement(tempdir.getCanonicalPath(), _maxFileSize, _maxRequestSize, _fileOutputBuffer);
+ MultiPartInputStream mpis = new MultiPartInputStream(in, content_type, config, tempdir);
+ mpis.setDeleteOnExit(_deleteFiles);
+ request.setAttribute(MULTIPART, mpis);
+
try
{
- // Get first boundary
- String line=((ReadLineInputStream)in).readLine();
-
- if (line == null)
- throw new IOException("Missing content for multipart request");
-
- line = line.trim();
-
- while (line != null && !line.equals(boundary))
+ Collection<Part> parts = mpis.getParts();
+ if (parts != null)
{
- if (!badFormatLogged)
+ Iterator<Part> itor = parts.iterator();
+ while (itor.hasNext() && params.size() < _maxFormKeys)
{
- LOG.warn("Badly formatted multipart request");
- badFormatLogged = true;
- }
- line=((ReadLineInputStream)in).readLine();
- line=(line==null?line:line.trim());
- }
-
- if (line == null)
- throw new IOException("Missing initial multi part boundary");
-
- // Read each part
- boolean lastPart=false;
-
- outer:while(!lastPart && params.size()<_maxFormKeys)
- {
- String type_content=null;
- String content_disposition=null;
- String content_transfer_encoding=null;
-
- while(true)
- {
- // read a line
- line=((ReadLineInputStream)in).readLine();
-
- //No more input
- if (line==null)
- break outer;
-
- // If blank line, end of part headers
- if("".equals(line))
- break;
-
- // place part header key and value in map
- int c=line.indexOf(':',0);
- if(c>0)
+ Part p = itor.next();
+ MultiPartInputStream.MultiPart mp = (MultiPartInputStream.MultiPart)p;
+ if (mp.getFile() != null)
{
- String key=line.substring(0,c).trim().toLowerCase(Locale.ENGLISH);
- String value=line.substring(c+1,line.length()).trim();
- if(key.equals("content-disposition"))
- content_disposition=value;
- else if(key.equals("content-transfer-encoding"))
- content_transfer_encoding=value;
- else if (key.equals("content-type"))
- type_content = value;
- }
- }
- // Extract content-disposition
- boolean form_data=false;
- if(content_disposition==null)
- {
- throw new IOException("Missing content-disposition");
- }
-
- LOG.debug("Content-Disposition: {}", content_disposition);
- QuotedStringTokenizer tok=new QuotedStringTokenizer(content_disposition,";",false,true);
- String name=null;
- String filename=null;
- while(tok.hasMoreTokens())
- {
- String t=tok.nextToken().trim();
- String tl=t.toLowerCase();
- if(t.startsWith("form-data"))
- form_data=true;
- else if(tl.startsWith("name="))
- name=value(t);
- else if(tl.startsWith("filename="))
- filename=filenameValue(t);
- }
-
- // Check disposition
- if(!form_data)
- {
- continue;
- }
- //It is valid for reset and submit buttons to have an empty name.
- //If no name is supplied, the browser skips sending the info for that field.
- //However, if you supply the empty string as the name, the browser sends the
- //field, with name as the empty string. So, only continue this loop if we
- //have not yet seen a name field.
- if(name==null)
- {
- continue;
- }
-
- OutputStream out=null;
- File file=null;
- try
- {
- if (filename!=null && filename.length()>0)
- {
- LOG.debug("filename = \"{}\"", filename);
- file = File.createTempFile("MultiPart", "", tempdir);
- out = new FileOutputStream(file);
- if(_fileOutputBuffer>0)
- out = new BufferedOutputStream(out, _fileOutputBuffer);
- request.setAttribute(name,file);
- params.add(name, filename);
- if (type_content != null)
- params.add(name+CONTENT_TYPE_SUFFIX, type_content);
-
- if (_deleteFiles)
+ request.setAttribute(mp.getName(),mp.getFile());
+ if (mp.getContentDispositionFilename() != null)
{
- file.deleteOnExit();
- ArrayList files = (ArrayList)request.getAttribute(FILES);
- if (files==null)
- {
- files=new ArrayList();
- request.setAttribute(FILES,files);
- }
- files.add(file);
- }
+ params.add(mp.getName(), mp.getContentDispositionFilename());
+ if (mp.getContentType() != null)
+ params.add(mp.getName()+CONTENT_TYPE_SUFFIX, mp.getContentType());
+ }
}
else
{
- out=new ByteArrayOutputStream();
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ IO.copy(p.getInputStream(), bytes);
+ params.add(p.getName(), bytes.toByteArray());
+ if (p.getContentType() != null)
+ params.add(p.getName()+CONTENT_TYPE_SUFFIX, p.getContentType());
}
-
-
- if ("base64".equalsIgnoreCase(content_transfer_encoding))
- {
- in = new Base64InputStream((ReadLineInputStream)in);
- }
- else if ("quoted-printable".equalsIgnoreCase(content_transfer_encoding))
- {
- in = new FilterInputStream(in)
- {
- @Override
- public int read() throws IOException
- {
- int c = in.read();
- if (c >= 0 && c == '=')
- {
- int hi = in.read();
- int lo = in.read();
- if (hi < 0 || lo < 0)
- {
- throw new IOException("Unexpected end to quoted-printable byte");
- }
- char[] chars = new char[] { (char)hi, (char)lo };
- c = Integer.parseInt(new String(chars),16);
- }
- return c;
- }
- };
- }
-
- int state=-2;
- int c;
- boolean cr=false;
- boolean lf=false;
-
- // loop for all lines`
- while(true)
- {
- int b=0;
- while((c=(state!=-2)?state:in.read())!=-1)
- {
- state=-2;
- // look for CR and/or LF
- if(c==13||c==10)
- {
- if(c==13)
- {
- in.mark(1);
- int tmp=in.read();
- if (tmp!=10)
- in.reset();
- else
- state=tmp;
- }
- break;
- }
- // look for boundary
- if(b>=0&&b<byteBoundary.length&&c==byteBoundary[b])
- b++;
- else
- {
- // this is not a boundary
- if(cr)
- out.write(13);
- if(lf)
- out.write(10);
- cr=lf=false;
- if(b>0)
- out.write(byteBoundary,0,b);
- b=-1;
- out.write(c);
- }
- }
- // check partial boundary
- if((b>0&&b<byteBoundary.length-2)||(b==byteBoundary.length-1))
- {
- if(cr)
- out.write(13);
- if(lf)
- out.write(10);
- cr=lf=false;
- out.write(byteBoundary,0,b);
- b=-1;
- }
- // boundary match
- if(b>0||c==-1)
- {
- if(b==byteBoundary.length)
- lastPart=true;
- if(state==10)
- state=-2;
- break;
- }
- // handle CR LF
- if(cr)
- out.write(13);
- if(lf)
- out.write(10);
- cr=(c==13);
- lf=(c==10||state==10);
- if(state==10)
- state=-2;
- }
- }
- finally
- {
- out.close();
- }
-
- if (file==null)
- {
- byte[] bytes = ((ByteArrayOutputStream)out).toByteArray();
- params.add(name,bytes);
- if (type_content != null)
- params.add(name+CONTENT_TYPE_SUFFIX, type_content);
}
}
-
+
// handle request
chain.doFilter(new Wrapper(srequest,params),response);
}
- catch (IOException e)
- {
- if (!badFormatLogged)
- LOG.warn("Badly formatted multipart request");
- throw e;
- }
finally
{
deleteFiles(request);
}
}
-
+
+
+ /* ------------------------------------------------------------ */
private void deleteFiles(ServletRequest request)
{
- ArrayList files = (ArrayList)request.getAttribute(FILES);
- if (files!=null)
+ if (!_deleteFiles)
+ return;
+
+ MultiPartInputStream mpis = (MultiPartInputStream)request.getAttribute(MULTIPART);
+ if (mpis != null)
{
- Iterator iter = files.iterator();
- while (iter.hasNext())
+ try
{
- File file=(File)iter.next();
- try
- {
- file.delete();
- }
- catch(Exception e)
- {
- _context.log("failed to delete "+file,e);
- }
+ mpis.deleteParts();
+ }
+ catch (Exception e)
+ {
+ _context.log("Error deleting multipart tmp files", e);
}
}
+ request.removeAttribute(MULTIPART);
}
- /* ------------------------------------------------------------ */
- private String value(String nameEqualsValue)
- {
- int idx = nameEqualsValue.indexOf('=');
- String value = nameEqualsValue.substring(idx+1).trim();
- return QuotedStringTokenizer.unquoteOnly(value);
- }
-
-
- /* ------------------------------------------------------------ */
- private String filenameValue(String nameEqualsValue)
- {
- int idx = nameEqualsValue.indexOf('=');
- String value = nameEqualsValue.substring(idx+1).trim();
-
- if (value.matches(".??[a-z,A-Z]\\:\\\\[^\\\\].*"))
- {
- //incorrectly escaped IE filenames that have the whole path
- //we just strip any leading & trailing quotes and leave it as is
- char first=value.charAt(0);
- if (first=='"' || first=='\'')
- value=value.substring(1);
- char last=value.charAt(value.length()-1);
- if (last=='"' || last=='\'')
- value = value.substring(0,value.length()-1);
-
- return value;
- }
- else
- //unquote the string, but allow any backslashes that don't
- //form a valid escape sequence to remain as many browsers
- //even on *nix systems will not escape a filename containing
- //backslashes
- return QuotedStringTokenizer.unquoteOnly(value, true);
- }
-
+
/* ------------------------------------------------------------------------------- */
/**
* @see javax.servlet.Filter#destroy()
@@ -621,44 +369,4 @@
return new String(bytes,contentType);
}
}
-
- private static class Base64InputStream extends InputStream
- {
- ReadLineInputStream _in;
- String _line;
- byte[] _buffer;
- int _pos;
-
- public Base64InputStream (ReadLineInputStream in)
- {
- _in = in;
- }
-
- @Override
- public int read() throws IOException
- {
- if (_buffer==null || _pos>= _buffer.length)
- {
- _line = _in.readLine();
- System.err.println("LINE: "+_line);
- if (_line==null)
- return -1;
- if (_line.startsWith("--"))
- _buffer=(_line+"\r\n").getBytes();
- else if (_line.length()==0)
- _buffer="\r\n".getBytes();
- else
- {
- ByteArrayOutputStream bout = new ByteArrayOutputStream(4*_line.length()/3);
- B64Code.decode(_line, bout);
- bout.write(13);
- bout.write(10);
- _buffer = bout.toByteArray();
- }
-
- _pos=0;
- }
- return _buffer[_pos++];
- }
- }
}
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/ProxyServlet.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/ProxyServlet.java
index e200dd4..e58f582 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/ProxyServlet.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/ProxyServlet.java
@@ -561,7 +561,7 @@
String connectionHdr = request.getHeader("Connection");
if (connectionHdr != null)
{
- connectionHdr = connectionHdr.toLowerCase();
+ connectionHdr = connectionHdr.toLowerCase(Locale.ENGLISH);
if (connectionHdr.indexOf("keep-alive") < 0 && connectionHdr.indexOf("close") < 0)
connectionHdr = null;
}
@@ -579,7 +579,7 @@
{
// TODO could be better than this!
String hdr = (String)enm.nextElement();
- String lhdr = hdr.toLowerCase();
+ String lhdr = hdr.toLowerCase(Locale.ENGLISH);
if ("transfer-encoding".equals(lhdr))
{
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/AbstractDoSFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/AbstractDoSFilterTest.java
index dc13b01..88c8055 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/AbstractDoSFilterTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/AbstractDoSFilterTest.java
@@ -23,7 +23,8 @@
import java.io.IOException;
import java.net.Socket;
-
+import java.util.EnumSet;
+import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
@@ -35,6 +36,7 @@
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.testing.ServletTester;
import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.log.Log;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
@@ -62,7 +64,7 @@
_tester.setContextPath("/ctx");
_tester.addServlet(TestServlet.class, "/*");
- _dosFilter = _tester.addFilter(filter, "/dos/*", 0);
+ _dosFilter = _tester.addFilter(filter, "/dos/*", EnumSet.allOf(DispatcherType.class));
_dosFilter.setInitParameter("maxRequestsPerSec", "4");
_dosFilter.setInitParameter("delayMs", "200");
_dosFilter.setInitParameter("throttledRequests", "1");
@@ -71,7 +73,7 @@
_dosFilter.setInitParameter("remotePort", "false");
_dosFilter.setInitParameter("insertHeaders", "true");
- _timeoutFilter = _tester.addFilter(filter, "/timeout/*", 0);
+ _timeoutFilter = _tester.addFilter(filter, "/timeout/*", EnumSet.allOf(DispatcherType.class));
_timeoutFilter.setInitParameter("maxRequestsPerSec", "4");
_timeoutFilter.setInitParameter("delayMs", "200");
_timeoutFilter.setInitParameter("throttledRequests", "1");
@@ -178,8 +180,8 @@
String last="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
String responses = doRequests(request+request+request+request+request,2,1100,1100,last);
- assertEquals(11,count(responses,"HTTP/1.1 200 OK"));
assertEquals(2,count(responses,"DoSFilter: delayed"));
+ assertEquals(11,count(responses,"HTTP/1.1 200 OK"));
}
@Test
@@ -209,7 +211,7 @@
String request="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\n\r\n";
String last="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
String responses = doRequests(request+request+request+request,1,0,0,last);
- //System.out.println("responses are " + responses);
+ System.out.println("responses are " + responses);
assertEquals("200 OK responses", 5,count(responses,"HTTP/1.1 200 OK"));
assertEquals("delayed responses", 1,count(responses,"DoSFilter: delayed"));
assertEquals("throttled responses", 1,count(responses,"DoSFilter: throttled"));
@@ -246,6 +248,8 @@
String last="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
String responses = doRequests(request+request+request+request,1,0,0,last);
+ System.err.println("RESPONSES: \n"+responses);
+
assertEquals(4,count(responses,"HTTP/1.1 200 OK"));
assertEquals(1,count(responses,"HTTP/1.1 503"));
assertEquals(1,count(responses,"DoSFilter: delayed"));
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/AsyncProxyServer.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/AsyncProxyServer.java
index 0c6d9f4..047b804 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/AsyncProxyServer.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/AsyncProxyServer.java
@@ -18,6 +18,10 @@
package org.eclipse.jetty.servlets;
+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;
@@ -37,7 +41,7 @@
ServletHandler handler=new ServletHandler();
server.setHandler(handler);
- //FilterHolder gzip = handler.addFilterWithMapping("org.eclipse.jetty.servlets.GzipFilter","/*",0);
+ //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","/");
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CloseableDoSFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CloseableDoSFilterTest.java
index 56e4fb5..47bcfce 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CloseableDoSFilterTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CloseableDoSFilterTest.java
@@ -18,6 +18,7 @@
package org.eclipse.jetty.servlets;
+
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CrossOriginFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CrossOriginFilterTest.java
index 732d022..9948950 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CrossOriginFilterTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CrossOriginFilterTest.java
@@ -19,8 +19,11 @@
package org.eclipse.jetty.servlets;
import java.io.IOException;
+import java.util.EnumSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+
+import javax.servlet.DispatcherType;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
@@ -56,7 +59,7 @@
@Test
public void testRequestWithNoOriginArrivesToApplication() throws Exception
{
- tester.getContext().addFilter(CrossOriginFilter.class, "/*", FilterMapping.DEFAULT);
+ tester.getContext().addFilter(CrossOriginFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
final CountDownLatch latch = new CountDownLatch(1);
tester.getContext().addServlet(new ServletHolder(new ResourceServlet(latch)), "/*");
@@ -76,7 +79,7 @@
FilterHolder filterHolder = new FilterHolder(new CrossOriginFilter());
String origin = "http://localhost";
filterHolder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, origin);
- tester.getContext().addFilter(filterHolder, "/*", FilterMapping.DEFAULT);
+ tester.getContext().addFilter(filterHolder, "/*", EnumSet.of(DispatcherType.REQUEST));
CountDownLatch latch = new CountDownLatch(1);
tester.getContext().addServlet(new ServletHolder(new ResourceServlet(latch)), "/*");
@@ -100,7 +103,7 @@
FilterHolder filterHolder = new FilterHolder(new CrossOriginFilter());
String origin = "http://subdomain.example.com";
filterHolder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "http://*.example.com");
- tester.getContext().addFilter(filterHolder, "/*", FilterMapping.DEFAULT);
+ tester.getContext().addFilter(filterHolder, "/*", EnumSet.of(DispatcherType.REQUEST));
CountDownLatch latch = new CountDownLatch(1);
tester.getContext().addServlet(new ServletHolder(new ResourceServlet(latch)), "/*");
@@ -123,7 +126,7 @@
FilterHolder filterHolder = new FilterHolder(new CrossOriginFilter());
String origin = "http://subdomain.subdomain.example.com";
filterHolder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "http://*.example.com");
- tester.getContext().addFilter(filterHolder, "/*", FilterMapping.DEFAULT);
+ tester.getContext().addFilter(filterHolder, "/*", EnumSet.of(DispatcherType.REQUEST));
CountDownLatch latch = new CountDownLatch(1);
tester.getContext().addServlet(new ServletHolder(new ResourceServlet(latch)), "/*");
@@ -146,7 +149,7 @@
FilterHolder filterHolder = new FilterHolder(new CrossOriginFilter());
String origin = "http://localhost";
filterHolder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, origin);
- tester.getContext().addFilter(filterHolder, "/*", FilterMapping.DEFAULT);
+ tester.getContext().addFilter(filterHolder, "/*", EnumSet.of(DispatcherType.REQUEST));
CountDownLatch latch = new CountDownLatch(1);
tester.getContext().addServlet(new ServletHolder(new ResourceServlet(latch)), "/*");
@@ -170,7 +173,7 @@
String origin = "http://localhost";
String otherOrigin = origin.replace("localhost", "127.0.0.1");
filterHolder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, origin + "," + otherOrigin);
- tester.getContext().addFilter(filterHolder, "/*", FilterMapping.DEFAULT);
+ tester.getContext().addFilter(filterHolder, "/*", EnumSet.of(DispatcherType.REQUEST));
CountDownLatch latch = new CountDownLatch(1);
tester.getContext().addServlet(new ServletHolder(new ResourceServlet(latch)), "/*");
@@ -193,7 +196,7 @@
{
FilterHolder filterHolder = new FilterHolder(new CrossOriginFilter());
filterHolder.setInitParameter(CrossOriginFilter.ALLOW_CREDENTIALS_PARAM, "false");
- tester.getContext().addFilter(filterHolder, "/*", FilterMapping.DEFAULT);
+ tester.getContext().addFilter(filterHolder, "/*", EnumSet.of(DispatcherType.REQUEST));
CountDownLatch latch = new CountDownLatch(1);
tester.getContext().addServlet(new ServletHolder(new ResourceServlet(latch)), "/*");
@@ -218,7 +221,7 @@
// will contain the CORS response headers.
FilterHolder filterHolder = new FilterHolder(new CrossOriginFilter());
- tester.getContext().addFilter(filterHolder, "/*", FilterMapping.DEFAULT);
+ tester.getContext().addFilter(filterHolder, "/*", EnumSet.of(DispatcherType.REQUEST));
CountDownLatch latch = new CountDownLatch(1);
tester.getContext().addServlet(new ServletHolder(new ResourceServlet(latch)), "/*");
@@ -243,7 +246,7 @@
// will contain the CORS response headers.
FilterHolder filterHolder = new FilterHolder(new CrossOriginFilter());
- tester.getContext().addFilter(filterHolder, "/*", FilterMapping.DEFAULT);
+ tester.getContext().addFilter(filterHolder, "/*", EnumSet.of(DispatcherType.REQUEST));
CountDownLatch latch = new CountDownLatch(1);
tester.getContext().addServlet(new ServletHolder(new ResourceServlet(latch)), "/*");
@@ -265,7 +268,7 @@
{
FilterHolder filterHolder = new FilterHolder(new CrossOriginFilter());
filterHolder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "PUT");
- tester.getContext().addFilter(filterHolder, "/*", FilterMapping.DEFAULT);
+ tester.getContext().addFilter(filterHolder, "/*", EnumSet.of(DispatcherType.REQUEST));
CountDownLatch latch = new CountDownLatch(1);
tester.getContext().addServlet(new ServletHolder(new ResourceServlet(latch)), "/*");
@@ -304,7 +307,7 @@
FilterHolder filterHolder = new FilterHolder(new CrossOriginFilter());
filterHolder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,HEAD,POST,PUT,DELETE");
filterHolder.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM, "X-Requested-With,Content-Type,Accept,Origin,X-Custom");
- tester.getContext().addFilter(filterHolder, "/*", FilterMapping.DEFAULT);
+ tester.getContext().addFilter(filterHolder, "/*",EnumSet.of(DispatcherType.REQUEST));
CountDownLatch latch = new CountDownLatch(1);
tester.getContext().addServlet(new ServletHolder(new ResourceServlet(latch)), "/*");
@@ -345,7 +348,7 @@
{
FilterHolder filterHolder = new FilterHolder(new CrossOriginFilter());
filterHolder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,HEAD,POST,PUT,DELETE");
- tester.getContext().addFilter(filterHolder, "/*", FilterMapping.DEFAULT);
+ tester.getContext().addFilter(filterHolder, "/*", EnumSet.of(DispatcherType.REQUEST));
CountDownLatch latch = new CountDownLatch(1);
tester.getContext().addServlet(new ServletHolder(new ResourceServlet(latch)), "/*");
@@ -370,7 +373,7 @@
public void testCrossOriginFilterDisabledForWebSocketUpgrade() throws Exception
{
FilterHolder filterHolder = new FilterHolder(new CrossOriginFilter());
- tester.getContext().addFilter(filterHolder, "/*", FilterMapping.DEFAULT);
+ tester.getContext().addFilter(filterHolder, "/*", EnumSet.of(DispatcherType.REQUEST));
CountDownLatch latch = new CountDownLatch(1);
tester.getContext().addServlet(new ServletHolder(new ResourceServlet(latch)), "/*");
@@ -394,7 +397,7 @@
{
FilterHolder filterHolder = new FilterHolder(new CrossOriginFilter());
filterHolder.setInitParameter("exposedHeaders", "Content-Length");
- tester.getContext().addFilter(filterHolder, "/*", FilterMapping.DEFAULT);
+ tester.getContext().addFilter(filterHolder, "/*", EnumSet.of(DispatcherType.REQUEST));
CountDownLatch latch = new CountDownLatch(1);
tester.getContext().addServlet(new ServletHolder(new ResourceServlet(latch)), "/*");
@@ -416,7 +419,7 @@
FilterHolder filterHolder = new FilterHolder(new CrossOriginFilter());
filterHolder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "PUT");
filterHolder.setInitParameter(CrossOriginFilter.CHAIN_PREFLIGHT_PARAM, "false");
- tester.getContext().addFilter(filterHolder, "/*", FilterMapping.DEFAULT);
+ tester.getContext().addFilter(filterHolder, "/*", EnumSet.of(DispatcherType.REQUEST));
CountDownLatch latch = new CountDownLatch(1);
tester.getContext().addServlet(new ServletHolder(new ResourceServlet(latch)), "/*");
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/DoSFilterJMXTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/DoSFilterJMXTest.java
index d458c93..3ad5cc3 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/DoSFilterJMXTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/DoSFilterJMXTest.java
@@ -24,10 +24,10 @@
import javax.management.Attribute;
import javax.management.MBeanServer;
import javax.management.ObjectName;
+import javax.servlet.DispatcherType;
import org.eclipse.jetty.jmx.MBeanContainer;
import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.DispatcherType;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.servlet.FilterHolder;
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/DoSFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/DoSFilterTest.java
index cf536d3..dd95fab 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/DoSFilterTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/DoSFilterTest.java
@@ -48,7 +48,8 @@
@Override
public void closeConnection(HttpServletRequest request, HttpServletResponse response, Thread thread)
{
- try {
+ try
+ {
response.getWriter().append("DoSFilter: timeout");
super.closeConnection(request,response,thread);
}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/IncludableGzipFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/IncludableGzipFilterTest.java
index 3094bfb..e73821c 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/IncludableGzipFilterTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/IncludableGzipFilterTest.java
@@ -104,7 +104,7 @@
tester.setContextPath("/context");
tester.setResourceBase(testdir.getDir().getCanonicalPath());
tester.addServlet(org.eclipse.jetty.servlet.DefaultServlet.class, "/");
- FilterHolder holder = tester.addFilter(IncludableGzipFilter.class,"/*",0);
+ FilterHolder holder = tester.addFilter(IncludableGzipFilter.class,"/*",null);
holder.setInitParameter("mimeTypes","text/plain");
tester.start();
}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java
index 6aaf889..23ed257 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java
@@ -18,17 +18,22 @@
package org.eclipse.jetty.servlets;
-import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.*;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
+import java.io.OutputStream;
import java.io.PrintWriter;
-import java.lang.reflect.Array;
+import java.net.Socket;
+import java.net.URL;
+import java.util.EnumSet;
+import java.util.Enumeration;
import java.util.Map;
+import javax.servlet.DispatcherType;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
@@ -75,7 +80,6 @@
assertNotNull(req.getParameter("fileup"));
assertNotNull(req.getParameter("fileup"+MultiPartFilter.CONTENT_TYPE_SUFFIX));
assertEquals(req.getParameter("fileup"+MultiPartFilter.CONTENT_TYPE_SUFFIX), "application/octet-stream");
-
super.doPost(req, resp);
}
@@ -96,7 +100,8 @@
tester.setContextPath("/context");
tester.setResourceBase(_dir.getCanonicalPath());
tester.addServlet(TestServlet.class, "/");
- FilterHolder multipartFilter = tester.addFilter(MultiPartFilter.class,"/*",FilterMapping.DEFAULT);
+ tester.setAttribute("javax.servlet.context.tempdir", _dir);
+ FilterHolder multipartFilter = tester.addFilter(MultiPartFilter.class,"/*", EnumSet.of(DispatcherType.REQUEST));
multipartFilter.setInitParameter("deleteFiles", "true");
tester.start();
}
@@ -135,7 +140,7 @@
response.parse(tester.getResponses(request.generate()));
assertTrue(response.getMethod()==null);
- assertEquals(HttpServletResponse.SC_OK,response.getStatus());
+ assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,response.getStatus());
}
@@ -791,14 +796,11 @@
*/
public static class TestServletParameterMap extends DumpServlet
{
-
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
- String content = (String)req.getParameter("\"strup\"Content-Type: application/octet-stream");
-
- assertThat(content, containsString("How now brown cow."));
-
+ String[] content = req.getParameterMap().get("\"strup\"Content-Type: application/octet-stream");
+ assertThat (content[0], containsString("How now brown cow."));
super.doPost(req, resp);
}
}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/PutFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/PutFilterTest.java
index e78d918..eebb08b 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/PutFilterTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/PutFilterTest.java
@@ -26,6 +26,8 @@
import java.io.OutputStream;
import java.net.Socket;
import java.net.URL;
+import java.util.EnumSet;
+import javax.servlet.DispatcherType;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Locale;
@@ -59,7 +61,7 @@
tester.setContextPath("/context");
tester.setResourceBase(_dir.getCanonicalPath());
tester.addServlet(org.eclipse.jetty.servlet.DefaultServlet.class, "/");
- FilterHolder holder = tester.addFilter(PutFilter.class,"/*",0);
+ FilterHolder holder = tester.addFilter(PutFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
holder.setInitParameter("delAllowed","true");
// Bloody Windows does not allow file renaming
if (!System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("windows"))
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/QoSFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/QoSFilterTest.java
index a4e00b6..d81c2a1 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/QoSFilterTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/QoSFilterTest.java
@@ -23,9 +23,10 @@
import java.io.IOException;
import java.net.URL;
+import java.util.EnumSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-
+import javax.servlet.DispatcherType;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
@@ -99,7 +100,7 @@
FilterHolder holder = new FilterHolder(QoSFilter2.class);
holder.setAsyncSupported(true);
holder.setInitParameter(QoSFilter.MAX_REQUESTS_INIT_PARAM, ""+MAX_QOS);
- _tester.getContext().getServletHandler().addFilterWithMapping(holder,"/*",FilterMapping.DEFAULT);
+ _tester.getContext().getServletHandler().addFilterWithMapping(holder,"/*",EnumSet.of(DispatcherType.REQUEST,DispatcherType.ASYNC));
for(int i = 0; i < NUM_CONNECTIONS; ++i )
{
@@ -117,8 +118,7 @@
FilterHolder holder = new FilterHolder(QoSFilter2.class);
holder.setAsyncSupported(true);
holder.setInitParameter(QoSFilter.MAX_REQUESTS_INIT_PARAM, ""+MAX_QOS);
- _tester.getContext().getServletHandler().addFilterWithMapping(holder,"/*",FilterMapping.DEFAULT);
-
+ _tester.getContext().getServletHandler().addFilterWithMapping(holder,"/*",EnumSet.of(DispatcherType.REQUEST,DispatcherType.ASYNC));
for(int i = 0; i < NUM_CONNECTIONS; ++i )
{
new Thread(new Worker2(i)).start();
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/TransparentProxyTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/TransparentProxyTest.java
new file mode 100644
index 0000000..c3535c9
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/TransparentProxyTest.java
@@ -0,0 +1,140 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.assertEquals;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.nio.SelectChannelConnector;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+
+/**
+ * TransparentProxyTest
+ *
+ *
+ */
+public class TransparentProxyTest
+{
+
+
+ protected Server server;
+ protected Server proxyServer;
+
+ public static class ServletA extends HttpServlet {
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ resp.setContentType("text/plain");
+ resp.getWriter().println("ok");
+ }
+ }
+
+ @Before
+ public void setUp () throws Exception
+ {
+ //set up the target server
+ server = new Server();
+ SelectChannelConnector connector = new SelectChannelConnector();
+ connector.setPort(8080);
+ server.addConnector(connector);
+ ServletContextHandler handler = new ServletContextHandler(server, "/");
+ handler.addServlet(ServletA.class, "/a");
+ server.setHandler(handler);
+ server.start();
+
+
+ //set up the server that proxies to the target server
+ proxyServer = new Server();
+ SelectChannelConnector proxyConnector = new SelectChannelConnector();
+ proxyConnector.setPort(8081);
+ proxyServer.addConnector(proxyConnector);
+ ServletContextHandler proxyHandler = new ServletContextHandler(proxyServer, "/");
+ proxyHandler.addServlet(new ServletHolder(new ProxyServlet.Transparent("/", "http", "127.0.0.1", 8080, "/")), "/");
+ proxyServer.setHandler(proxyHandler);
+ proxyServer.start();
+
+ }
+
+
+ @After
+ public void tearDown() throws Exception
+ {
+ server.stop();
+ proxyServer.stop();
+ }
+
+
+ @Test
+ public void testDirectNoContentType() throws Exception
+ {
+ // Direct request without Content-Type set works
+ URL url = new URL("http://localhost:8080/a");
+ HttpURLConnection con = (HttpURLConnection) url.openConnection();
+ assertEquals(200, con.getResponseCode());
+ }
+
+
+ @Test
+ public void testDirectWithContentType() throws Exception
+ {
+ // Direct request with Content-Type works
+ URL url = new URL("http://localhost:8080/a");
+ HttpURLConnection con = (HttpURLConnection) url.openConnection();
+ con.addRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
+ assertEquals(200, con.getResponseCode());
+ }
+
+ @Test
+ public void testProxiedWithoutContentType() throws Exception
+ {
+ // Proxied request without Content-Type set works
+ URL url = new URL("http://localhost:8081/a");
+ HttpURLConnection con = (HttpURLConnection) url.openConnection();
+ assertEquals(200, con.getResponseCode());
+ System.err.println (con.getContentType());
+ }
+
+ @Test
+ public void testProxiedWithContentType() throws Exception
+ {
+ // Proxied request with Content-Type set fails
+
+ URL url = new URL("http://localhost:8081/a");
+ HttpURLConnection con = (HttpURLConnection) url.openConnection();
+ con.addRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
+ assertEquals(200, con.getResponseCode());
+ System.err.println(con.getContentType());
+
+ }
+}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java
index dc4fdf0..4f73ffa 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java
@@ -30,11 +30,13 @@
import java.io.UnsupportedEncodingException;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
+import java.util.EnumSet;
import java.util.Enumeration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
import java.util.zip.Inflater;
+import javax.servlet.DispatcherType;
import java.util.zip.InflaterInputStream;
import javax.servlet.Servlet;
@@ -530,7 +532,7 @@
ServletHolder servletHolder = servletTester.addServlet(servletClass,"/");
servletHolder.setInitParameter("baseDir",testdir.getDir().getAbsolutePath());
servletHolder.setInitParameter("etags","true");
- FilterHolder holder = servletTester.addFilter(gzipFilterClass,"/*",0);
+ FilterHolder holder = servletTester.addFilter(gzipFilterClass,"/*",EnumSet.allOf(DispatcherType.class));
holder.setInitParameter("vary","Accept-Encoding");
return holder;
}
diff --git a/jetty-spdy/pom.xml b/jetty-spdy/pom.xml
index af92aa4..5390572 100644
--- a/jetty-spdy/pom.xml
+++ b/jetty-spdy/pom.xml
@@ -3,7 +3,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -58,6 +58,13 @@
<skip>true</skip>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <skipTests>true</skipTests>
+ </configuration>
+ </plugin>
</plugins>
</build>
@@ -216,6 +223,7 @@
</activation>
<properties>
<npn.version>1.1.8.v20141013</npn.version>
+ <alpn.version>7.1.0.v20141016</alpn.version>
</properties>
</profile>
<profile>
@@ -228,6 +236,7 @@
</activation>
<properties>
<npn.version>1.1.8.v20141013</npn.version>
+ <alpn.version>7.1.0.v20141016</alpn.version>
</properties>
</profile>
<profile>
@@ -240,6 +249,7 @@
</activation>
<properties>
<npn.version>1.1.8.v20141013</npn.version>
+ <alpn.version>7.1.0.v20141016</alpn.version>
</properties>
</profile>
<profile>
@@ -252,6 +262,7 @@
</activation>
<properties>
<npn.version>1.1.9.v20141016</npn.version>
+ <alpn.version>7.1.2.v20141202</alpn.version>
</properties>
</profile>
<profile>
@@ -264,6 +275,7 @@
</activation>
<properties>
<npn.version>1.1.9.v20141016</npn.version>
+ <alpn.version>7.1.2.v20141202</alpn.version>
</properties>
</profile>
<profile>
@@ -276,6 +288,7 @@
</activation>
<properties>
<npn.version>1.1.10.v20150130</npn.version>
+ <alpn.version>7.1.3.v20150130</alpn.version>
</properties>
</profile>
<profile>
@@ -288,6 +301,7 @@
</activation>
<properties>
<npn.version>1.1.10.v20150130</npn.version>
+ <alpn.version>7.1.3.v20150130</alpn.version>
</properties>
</profile>
<profile>
@@ -300,6 +314,7 @@
</activation>
<properties>
<npn.version>1.1.10.v20150130</npn.version>
+ <alpn.version>7.1.3.v20150130</alpn.version>
</properties>
</profile>
<profile>
@@ -312,6 +327,7 @@
</activation>
<properties>
<npn.version>1.1.11.v20150415</npn.version>
+ <alpn.version>7.1.3.v20150130</alpn.version>
</properties>
</profile>
</profiles>
diff --git a/jetty-spdy/spdy-core/pom.xml b/jetty-spdy/spdy-core/pom.xml
index 975459f..37e735a 100644
--- a/jetty-spdy/spdy-core/pom.xml
+++ b/jetty-spdy/spdy-core/pom.xml
@@ -3,7 +3,7 @@
<parent>
<groupId>org.eclipse.jetty.spdy</groupId>
<artifactId>spdy-parent</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git a/jetty-spdy/spdy-jetty-http-webapp/pom.xml b/jetty-spdy/spdy-jetty-http-webapp/pom.xml
index 41ff79e..e96b67c 100644
--- a/jetty-spdy/spdy-jetty-http-webapp/pom.xml
+++ b/jetty-spdy/spdy-jetty-http-webapp/pom.xml
@@ -3,7 +3,7 @@
<parent>
<groupId>org.eclipse.jetty.spdy</groupId>
<artifactId>spdy-parent</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spdy-jetty-http-webapp</artifactId>
diff --git a/jetty-spdy/spdy-jetty-http/pom.xml b/jetty-spdy/spdy-jetty-http/pom.xml
index cf05b5e..6664490 100644
--- a/jetty-spdy/spdy-jetty-http/pom.xml
+++ b/jetty-spdy/spdy-jetty-http/pom.xml
@@ -3,7 +3,7 @@
<parent>
<groupId>org.eclipse.jetty.spdy</groupId>
<artifactId>spdy-parent</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spdy-jetty-http</artifactId>
diff --git a/jetty-spdy/spdy-jetty/pom.xml b/jetty-spdy/spdy-jetty/pom.xml
index 806895e..a08d02d 100644
--- a/jetty-spdy/spdy-jetty/pom.xml
+++ b/jetty-spdy/spdy-jetty/pom.xml
@@ -3,7 +3,7 @@
<parent>
<groupId>org.eclipse.jetty.spdy</groupId>
<artifactId>spdy-parent</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spdy-jetty</artifactId>
diff --git a/jetty-start/pom.xml b/jetty-start/pom.xml
index ba97f08..3a8b601 100644
--- a/jetty-start/pom.xml
+++ b/jetty-start/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-start</artifactId>
diff --git a/jetty-start/src/main/resources/org/eclipse/jetty/start/start.config b/jetty-start/src/main/resources/org/eclipse/jetty/start/start.config
index 0438633..bf3c920 100644
--- a/jetty-start/src/main/resources/org/eclipse/jetty/start/start.config
+++ b/jetty-start/src/main/resources/org/eclipse/jetty/start/start.config
@@ -92,7 +92,7 @@
$(jetty.home)/lib/jetty-xml-$(version).jar ! available org.eclipse.jetty.xml.XmlParser
[Server,All,server,default]
-$(jetty.home)/lib/servlet-api-2.5.jar ! available javax.servlet.ServletContext
+$(jetty.home)/lib/servlet-api-3.0.jar ! available javax.servlet.ServletContext
$(jetty.home)/lib/jetty-http-$(version).jar ! available org.eclipse.jetty.http.HttpParser
$(jetty.home)/lib/jetty-continuation-$(version).jar ! available org.eclipse.jetty.continuation.Continuation
$(jetty.home)/lib/jetty-server-$(version).jar ! available org.eclipse.jetty.server.Server
@@ -101,7 +101,7 @@
$(jetty.home)/lib/jetty-security-$(version).jar ! available org.eclipse.jetty.security.LoginService
[Server,All,servlet,default]
-$(jetty.home)/lib/servlet-api-2.5.jar ! available javax.servlet.ServletContext
+$(jetty.home)/lib/servlet-api-3.0.jar ! available javax.servlet.ServletContext
$(jetty.home)/lib/jetty-servlet-$(version).jar ! available org.eclipse.jetty.servlet.ServletHandler
[Server,All,webapp,default]
@@ -141,7 +141,7 @@
[All,Client,client]
$(jetty.home)/lib/jetty-http-$(version).jar ! available org.eclipse.jetty.http.HttpParser
$(jetty.home)/lib/jetty-client-$(version).jar ! available org.eclipse.jetty.client.HttpClient
-
+
[Client]
$(jetty.home)/lib/jetty-http-$(version).jar ! available org.eclipse.jetty.http.HttpParser
diff --git a/jetty-util/pom.xml b/jetty-util/pom.xml
index 2528379..ae4da4b 100644
--- a/jetty-util/pom.xml
+++ b/jetty-util/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-util</artifactId>
@@ -25,7 +25,7 @@
</goals>
<configuration>
<instructions>
- <Import-Package>org.slf4j;version="[1.5,1.7)";resolution:=optional,org.slf4j.impl;version="[1.5,1.7)";resolution:=optional,*</Import-Package>
+ <Import-Package>javax.servlet.*;version="2.6.0",org.slf4j;version="[1.5,2.0)";resolution:=optional,org.slf4j.impl;version="[1.5,2.0)";resolution:=optional,*</Import-Package>
</instructions>
</configuration>
</execution>
@@ -71,6 +71,11 @@
</build>
<dependencies>
<dependency>
+ <groupId>org.eclipse.jetty.orbit</groupId>
+ <artifactId>javax.servlet</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/Loader.java b/jetty-util/src/main/java/org/eclipse/jetty/util/Loader.java
index 4935089..f88db18 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/Loader.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/Loader.java
@@ -21,9 +21,11 @@
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
+import java.util.HashSet;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
+import java.util.Set;
import org.eclipse.jetty.util.resource.Resource;
@@ -95,7 +97,9 @@
ClassNotFoundException ex=null;
Class<?> c =null;
ClassLoader loader=Thread.currentThread().getContextClassLoader();
- while (c==null && loader!=null )
+ Set<ClassLoader> seen = new HashSet<ClassLoader>();
+
+ while (c==null && loader!=null && seen.add(loader))
{
try { c=loader.loadClass(name); }
catch (ClassNotFoundException e) {if(ex==null)ex=e;}
@@ -103,14 +107,15 @@
}
loader=loadClass==null?null:loadClass.getClassLoader();
- while (c==null && loader!=null )
+ while (c==null && loader!=null && seen.add(loader))
{
try { c=loader.loadClass(name); }
catch (ClassNotFoundException e) {if(ex==null)ex=e;}
loader=(c==null&&checkParents)?loader.getParent():null;
}
- if (c==null)
+ loader=Loader.class.getClassLoader();
+ if (c==null && loader!=null && seen.add(loader))
{
try { c=Class.forName(name); }
catch (ClassNotFoundException e) {if(ex==null)ex=e;}
@@ -130,7 +135,9 @@
MissingResourceException ex=null;
ResourceBundle bundle =null;
ClassLoader loader=Thread.currentThread().getContextClassLoader();
- while (bundle==null && loader!=null )
+ Set<ClassLoader> seen = new HashSet<ClassLoader>();
+
+ while (bundle==null && loader!=null && seen.add(loader))
{
try { bundle=ResourceBundle.getBundle(name, locale, loader); }
catch (MissingResourceException e) {if(ex==null)ex=e;}
@@ -138,14 +145,15 @@
}
loader=loadClass==null?null:loadClass.getClassLoader();
- while (bundle==null && loader!=null )
+ while (bundle==null && loader!=null && seen.add(loader))
{
try { bundle=ResourceBundle.getBundle(name, locale, loader); }
catch (MissingResourceException e) {if(ex==null)ex=e;}
loader=(bundle==null&&checkParents)?loader.getParent():null;
}
- if (bundle==null)
+ loader = Loader.class.getClassLoader();
+ if (bundle==null && loader!=null && seen.add(loader))
{
try { bundle=ResourceBundle.getBundle(name, locale); }
catch (MissingResourceException e) {if(ex==null)ex=e;}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiMap.java b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiMap.java
index 61cfe00..04d50e9 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiMap.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiMap.java
@@ -86,7 +86,7 @@
* @param name The entry key.
* @return Unmodifieable List of values.
*/
- public List<Object> getValues(Object name)
+ public List getValues(Object name)
{
return LazyList.getList(_map.get(name),true);
}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStream.java b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStream.java
new file mode 100644
index 0000000..67848ba
--- /dev/null
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStream.java
@@ -0,0 +1,856 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.util;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import javax.servlet.MultipartConfigElement;
+import javax.servlet.ServletException;
+import javax.servlet.http.Part;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+
+/**
+ * MultiPartInputStream
+ *
+ * Handle a MultiPart Mime input stream, breaking it up on the boundary into files and strings.
+ */
+public class MultiPartInputStream
+{
+ private static final Logger LOG = Log.getLogger(MultiPartInputStream.class);
+
+ public static final MultipartConfigElement __DEFAULT_MULTIPART_CONFIG = new MultipartConfigElement(System.getProperty("java.io.tmpdir"));
+ protected InputStream _in;
+ protected MultipartConfigElement _config;
+ protected String _contentType;
+ protected MultiMap<String> _parts;
+ protected File _tmpDir;
+ protected File _contextTmpDir;
+ protected boolean _deleteOnExit;
+
+
+
+ public class MultiPart implements Part
+ {
+ protected String _name;
+ protected String _filename;
+ protected File _file;
+ protected OutputStream _out;
+ protected ByteArrayOutputStream2 _bout;
+ protected String _contentType;
+ protected MultiMap<String> _headers;
+ protected long _size = 0;
+ protected boolean _temporary = true;
+
+ public MultiPart (String name, String filename)
+ throws IOException
+ {
+ _name = name;
+ _filename = filename;
+ }
+
+ protected void setContentType (String contentType)
+ {
+ _contentType = contentType;
+ }
+
+
+ protected void open()
+ throws IOException
+ {
+ //We will either be writing to a file, if it has a filename on the content-disposition
+ //and otherwise a byte-array-input-stream, OR if we exceed the getFileSizeThreshold, we
+ //will need to change to write to a file.
+ if (_filename != null && _filename.trim().length() > 0)
+ {
+ createFile();
+ }
+ else
+ {
+ //Write to a buffer in memory until we discover we've exceed the
+ //MultipartConfig fileSizeThreshold
+ _out = _bout= new ByteArrayOutputStream2();
+ }
+ }
+
+ protected void close()
+ throws IOException
+ {
+ _out.close();
+ }
+
+
+ protected void write (int b)
+ throws IOException
+ {
+ if (MultiPartInputStream.this._config.getMaxFileSize() > 0 && _size + 1 > MultiPartInputStream.this._config.getMaxFileSize())
+ throw new IllegalStateException ("Multipart Mime part "+_name+" exceeds max filesize");
+
+ if (MultiPartInputStream.this._config.getFileSizeThreshold() > 0 && _size + 1 > MultiPartInputStream.this._config.getFileSizeThreshold() && _file==null)
+ createFile();
+ _out.write(b);
+ _size ++;
+ }
+
+ protected void write (byte[] bytes, int offset, int length)
+ throws IOException
+ {
+ if (MultiPartInputStream.this._config.getMaxFileSize() > 0 && _size + length > MultiPartInputStream.this._config.getMaxFileSize())
+ throw new IllegalStateException ("Multipart Mime part "+_name+" exceeds max filesize");
+
+ if (MultiPartInputStream.this._config.getFileSizeThreshold() > 0 && _size + length > MultiPartInputStream.this._config.getFileSizeThreshold() && _file==null)
+ createFile();
+
+ _out.write(bytes, offset, length);
+ _size += length;
+ }
+
+ protected void createFile ()
+ throws IOException
+ {
+ final boolean USER = true;
+ final boolean WORLD = false;
+
+ _file = File.createTempFile("MultiPart", "", MultiPartInputStream.this._tmpDir);
+ _file.setReadable(false,WORLD); // (reset) disable it for everyone first
+ _file.setReadable(true,USER); // enable for user only
+ if (_deleteOnExit)
+ _file.deleteOnExit();
+ FileOutputStream fos = new FileOutputStream(_file);
+ BufferedOutputStream bos = new BufferedOutputStream(fos);
+
+ if (_size > 0 && _out != null)
+ {
+ //already written some bytes, so need to copy them into the file
+ _out.flush();
+ _bout.writeTo(bos);
+ _out.close();
+ _bout = null;
+ }
+ _out = bos;
+ }
+
+
+
+ protected void setHeaders(MultiMap<String> headers)
+ {
+ _headers = headers;
+ }
+
+ /**
+ * @see javax.servlet.http.Part#getContentType()
+ */
+ public String getContentType()
+ {
+ return _contentType;
+ }
+
+ /**
+ * @see javax.servlet.http.Part#getHeader(java.lang.String)
+ */
+ public String getHeader(String name)
+ {
+ if (name == null)
+ return null;
+ return (String)_headers.getValue(name.toLowerCase(Locale.ENGLISH), 0);
+ }
+
+ /**
+ * @see javax.servlet.http.Part#getHeaderNames()
+ */
+ public Collection<String> getHeaderNames()
+ {
+ return _headers.keySet();
+ }
+
+ /**
+ * @see javax.servlet.http.Part#getHeaders(java.lang.String)
+ */
+ public Collection<String> getHeaders(String name)
+ {
+ return _headers.getValues(name);
+ }
+
+ /**
+ * @see javax.servlet.http.Part#getInputStream()
+ */
+ public InputStream getInputStream() throws IOException
+ {
+ if (_file != null)
+ {
+ //written to a file, whether temporary or not
+ return new BufferedInputStream (new FileInputStream(_file));
+ }
+ else
+ {
+ //part content is in memory
+ return new ByteArrayInputStream(_bout.getBuf(),0,_bout.size());
+ }
+ }
+
+ public byte[] getBytes()
+ {
+ if (_bout!=null)
+ return _bout.toByteArray();
+ return null;
+ }
+
+ /**
+ * @see javax.servlet.http.Part#getName()
+ */
+ public String getName()
+ {
+ return _name;
+ }
+
+ /**
+ * @see javax.servlet.http.Part#getSize()
+ */
+ public long getSize()
+ {
+ return _size;
+ }
+
+ /**
+ * @see javax.servlet.http.Part#write(java.lang.String)
+ */
+ public void write(String fileName) throws IOException
+ {
+ if (_file == null)
+ {
+ _temporary = false;
+
+ //part data is only in the ByteArrayOutputStream and never been written to disk
+ _file = new File (_tmpDir, fileName);
+
+ BufferedOutputStream bos = null;
+ try
+ {
+ bos = new BufferedOutputStream(new FileOutputStream(_file));
+ _bout.writeTo(bos);
+ bos.flush();
+ }
+ finally
+ {
+ if (bos != null)
+ bos.close();
+ _bout = null;
+ }
+ }
+ else
+ {
+ //the part data is already written to a temporary file, just rename it
+ _temporary = false;
+
+ File f = new File(_tmpDir, fileName);
+ if (_file.renameTo(f))
+ _file = f;
+ }
+ }
+
+ /**
+ * Remove the file, whether or not Part.write() was called on it
+ * (ie no longer temporary)
+ * @see javax.servlet.http.Part#delete()
+ */
+ public void delete() throws IOException
+ {
+ if (_file != null && _file.exists())
+ _file.delete();
+ }
+
+ /**
+ * Only remove tmp files.
+ *
+ * @throws IOException
+ */
+ public void cleanUp() throws IOException
+ {
+ if (_temporary && _file != null && _file.exists())
+ _file.delete();
+ }
+
+
+ /**
+ * Get the file, if any, the data has been written to.
+ * @return
+ */
+ public File getFile ()
+ {
+ return _file;
+ }
+
+
+ /**
+ * Get the filename from the content-disposition.
+ * @return null or the filename
+ */
+ public String getContentDispositionFilename ()
+ {
+ return _filename;
+ }
+ }
+
+
+
+
+ /**
+ * @param in Request input stream
+ * @param contentType Content-Type header
+ * @param config MultipartConfigElement
+ * @param contextTmpDir javax.servlet.context.tempdir
+ */
+ public MultiPartInputStream (InputStream in, String contentType, MultipartConfigElement config, File contextTmpDir)
+ {
+ _in = new ReadLineInputStream(in);
+ _contentType = contentType;
+ _config = config;
+ _contextTmpDir = contextTmpDir;
+ if (_contextTmpDir == null)
+ _contextTmpDir = new File (System.getProperty("java.io.tmpdir"));
+
+ if (_config == null)
+ _config = new MultipartConfigElement(_contextTmpDir.getAbsolutePath());
+ }
+
+ /**
+ * Get the already parsed parts.
+ *
+ * @return
+ */
+ public Collection<Part> getParsedParts()
+ {
+ if (_parts == null)
+ return Collections.emptyList();
+
+ Collection<Object> values = _parts.values();
+ List<Part> parts = new ArrayList<Part>();
+ for (Object o: values)
+ {
+ List<Part> asList = LazyList.getList(o, false);
+ parts.addAll(asList);
+ }
+ return parts;
+ }
+
+ /**
+ * Delete any tmp storage for parts, and clear out the parts list.
+ *
+ * @throws MultiException
+ */
+ public void deleteParts ()
+ throws MultiException
+ {
+ Collection<Part> parts = getParsedParts();
+ MultiException err = new MultiException();
+ for (Part p:parts)
+ {
+ try
+ {
+ ((MultiPartInputStream.MultiPart)p).cleanUp();
+ }
+ catch(Exception e)
+ {
+ err.add(e);
+ }
+ }
+ _parts.clear();
+
+ err.ifExceptionThrowMulti();
+ }
+
+
+ /**
+ * Parse, if necessary, the multipart data and return the list of Parts.
+ *
+ * @return
+ * @throws IOException
+ * @throws ServletException
+ */
+ public Collection<Part> getParts()
+ throws IOException, ServletException
+ {
+ parse();
+ Collection<Object> values = _parts.values();
+ List<Part> parts = new ArrayList<Part>();
+ for (Object o: values)
+ {
+ List<Part> asList = LazyList.getList(o, false);
+ parts.addAll(asList);
+ }
+ return parts;
+ }
+
+
+ /**
+ * Get the named Part.
+ *
+ * @param name
+ * @return
+ * @throws IOException
+ * @throws ServletException
+ */
+ public Part getPart(String name)
+ throws IOException, ServletException
+ {
+ parse();
+ return (Part)_parts.getValue(name, 0);
+ }
+
+
+ /**
+ * Parse, if necessary, the multipart stream.
+ *
+ * @throws IOException
+ * @throws ServletException
+ */
+ protected void parse ()
+ throws IOException, ServletException
+ {
+ //have we already parsed the input?
+ if (_parts != null)
+ return;
+
+ //initialize
+ long total = 0; //keep running total of size of bytes read from input and throw an exception if exceeds MultipartConfigElement._maxRequestSize
+ _parts = new MultiMap<String>();
+
+ //if its not a multipart request, don't parse it
+ if (_contentType == null || !_contentType.startsWith("multipart/form-data"))
+ return;
+
+ //sort out the location to which to write the files
+
+ if (_config.getLocation() == null)
+ _tmpDir = _contextTmpDir;
+ else if ("".equals(_config.getLocation()))
+ _tmpDir = _contextTmpDir;
+ else
+ {
+ File f = new File (_config.getLocation());
+ if (f.isAbsolute())
+ _tmpDir = f;
+ else
+ _tmpDir = new File (_contextTmpDir, _config.getLocation());
+ }
+
+ if (!_tmpDir.exists())
+ _tmpDir.mkdirs();
+
+ String contentTypeBoundary = "";
+ int bstart = _contentType.indexOf("boundary=");
+ if (bstart >= 0)
+ {
+ int bend = _contentType.indexOf(";", bstart);
+ bend = (bend < 0? _contentType.length(): bend);
+ contentTypeBoundary = QuotedStringTokenizer.unquote(value(_contentType.substring(bstart,bend), true).trim());
+ }
+
+ String boundary="--"+contentTypeBoundary;
+ byte[] byteBoundary=(boundary+"--").getBytes(StringUtil.__ISO_8859_1);
+
+ // Get first boundary
+ String line = null;
+ try
+ {
+ line=((ReadLineInputStream)_in).readLine();
+ }
+ catch (IOException e)
+ {
+ LOG.warn("Badly formatted multipart request");
+ throw e;
+ }
+
+ if (line == null)
+ throw new IOException("Missing content for multipart request");
+
+ boolean badFormatLogged = false;
+ line=line.trim();
+ while (line != null && !line.equals(boundary))
+ {
+ if (!badFormatLogged)
+ {
+ LOG.warn("Badly formatted multipart request");
+ badFormatLogged = true;
+ }
+ line=((ReadLineInputStream)_in).readLine();
+ line=(line==null?line:line.trim());
+ }
+
+ if (line == null || line.length() == 0)
+ throw new IOException("Missing initial multi part boundary");
+
+ // Read each part
+ boolean lastPart=false;
+
+ outer:while(!lastPart)
+ {
+ String contentDisposition=null;
+ String contentType=null;
+ String contentTransferEncoding=null;
+
+ MultiMap<String> headers = new MultiMap<String>();
+ while(true)
+ {
+ line=((ReadLineInputStream)_in).readLine();
+
+ //No more input
+ if(line==null)
+ break outer;
+
+ // If blank line, end of part headers
+ if("".equals(line))
+ break;
+
+ total += line.length();
+ if (_config.getMaxRequestSize() > 0 && total > _config.getMaxRequestSize())
+ throw new IllegalStateException ("Request exceeds maxRequestSize ("+_config.getMaxRequestSize()+")");
+
+ //get content-disposition and content-type
+ int c=line.indexOf(':',0);
+ if(c>0)
+ {
+ String key=line.substring(0,c).trim().toLowerCase(Locale.ENGLISH);
+ String value=line.substring(c+1,line.length()).trim();
+ headers.put(key, value);
+ if (key.equalsIgnoreCase("content-disposition"))
+ contentDisposition=value;
+ if (key.equalsIgnoreCase("content-type"))
+ contentType = value;
+ if(key.equals("content-transfer-encoding"))
+ contentTransferEncoding=value;
+
+ }
+ }
+
+ // Extract content-disposition
+ boolean form_data=false;
+ if(contentDisposition==null)
+ {
+ throw new IOException("Missing content-disposition");
+ }
+
+ QuotedStringTokenizer tok=new QuotedStringTokenizer(contentDisposition,";", false, true);
+ String name=null;
+ String filename=null;
+ while(tok.hasMoreTokens())
+ {
+ String t=tok.nextToken().trim();
+ String tl=t.toLowerCase(Locale.ENGLISH);
+ if(t.startsWith("form-data"))
+ form_data=true;
+ else if(tl.startsWith("name="))
+ name=value(t, true);
+ else if(tl.startsWith("filename="))
+ filename=filenameValue(t);
+ }
+
+ // Check disposition
+ if(!form_data)
+ {
+ continue;
+ }
+ //It is valid for reset and submit buttons to have an empty name.
+ //If no name is supplied, the browser skips sending the info for that field.
+ //However, if you supply the empty string as the name, the browser sends the
+ //field, with name as the empty string. So, only continue this loop if we
+ //have not yet seen a name field.
+ if(name==null)
+ {
+ continue;
+ }
+
+ //Have a new Part
+ MultiPart part = new MultiPart(name, filename);
+ part.setHeaders(headers);
+ part.setContentType(contentType);
+ _parts.add(name, part);
+ part.open();
+
+ InputStream partInput = null;
+ if ("base64".equalsIgnoreCase(contentTransferEncoding))
+ {
+ partInput = new Base64InputStream((ReadLineInputStream)_in);
+ }
+ else if ("quoted-printable".equalsIgnoreCase(contentTransferEncoding))
+ {
+ partInput = new FilterInputStream(_in)
+ {
+ @Override
+ public int read() throws IOException
+ {
+ int c = in.read();
+ if (c >= 0 && c == '=')
+ {
+ int hi = in.read();
+ int lo = in.read();
+ if (hi < 0 || lo < 0)
+ {
+ throw new IOException("Unexpected end to quoted-printable byte");
+ }
+ char[] chars = new char[] { (char)hi, (char)lo };
+ c = Integer.parseInt(new String(chars),16);
+ }
+ return c;
+ }
+ };
+ }
+ else
+ partInput = _in;
+
+ try
+ {
+ int state=-2;
+ int c;
+ boolean cr=false;
+ boolean lf=false;
+
+ // loop for all lines
+ while(true)
+ {
+ int b=0;
+ while((c=(state!=-2)?state:partInput.read())!=-1)
+ {
+ total ++;
+ if (_config.getMaxRequestSize() > 0 && total > _config.getMaxRequestSize())
+ throw new IllegalStateException("Request exceeds maxRequestSize ("+_config.getMaxRequestSize()+")");
+
+ state=-2;
+
+ // look for CR and/or LF
+ if(c==13||c==10)
+ {
+ if(c==13)
+ {
+ partInput.mark(1);
+ int tmp=partInput.read();
+ if (tmp!=10)
+ partInput.reset();
+ else
+ state=tmp;
+ }
+ break;
+ }
+
+ // Look for boundary
+ if(b>=0&&b<byteBoundary.length&&c==byteBoundary[b])
+ {
+ b++;
+ }
+ else
+ {
+ // Got a character not part of the boundary, so we don't have the boundary marker.
+ // Write out as many chars as we matched, then the char we're looking at.
+ if(cr)
+ part.write(13);
+
+ if(lf)
+ part.write(10);
+
+ cr=lf=false;
+ if(b>0)
+ part.write(byteBoundary,0,b);
+
+ b=-1;
+ part.write(c);
+ }
+ }
+
+ // Check for incomplete boundary match, writing out the chars we matched along the way
+ if((b>0&&b<byteBoundary.length-2)||(b==byteBoundary.length-1))
+ {
+ if(cr)
+ part.write(13);
+
+ if(lf)
+ part.write(10);
+
+ cr=lf=false;
+ part.write(byteBoundary,0,b);
+ b=-1;
+ }
+
+ // Boundary match. If we've run out of input or we matched the entire final boundary marker, then this is the last part.
+ if(b>0||c==-1)
+ {
+
+ if(b==byteBoundary.length)
+ lastPart=true;
+ if(state==10)
+ state=-2;
+ break;
+ }
+
+ // handle CR LF
+ if(cr)
+ part.write(13);
+
+ if(lf)
+ part.write(10);
+
+ cr=(c==13);
+ lf=(c==10||state==10);
+ if(state==10)
+ state=-2;
+ }
+ }
+ finally
+ {
+ part.close();
+ }
+ }
+ if (!lastPart)
+ throw new IOException("Incomplete parts");
+ }
+
+ public void setDeleteOnExit(boolean deleteOnExit)
+ {
+ _deleteOnExit = deleteOnExit;
+ }
+
+
+ public boolean isDeleteOnExit()
+ {
+ return _deleteOnExit;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ private String value(String nameEqualsValue, boolean splitAfterSpace)
+ {
+ /*
+ String value=nameEqualsValue.substring(nameEqualsValue.indexOf('=')+1).trim();
+ int i=value.indexOf(';');
+ if(i>0)
+ value=value.substring(0,i);
+ if(value.startsWith("\""))
+ {
+ value=value.substring(1,value.indexOf('"',1));
+ }
+ else if (splitAfterSpace)
+ {
+ i=value.indexOf(' ');
+ if(i>0)
+ value=value.substring(0,i);
+ }
+ return value;
+ */
+ int idx = nameEqualsValue.indexOf('=');
+ String value = nameEqualsValue.substring(idx+1).trim();
+ return QuotedStringTokenizer.unquoteOnly(value);
+ }
+
+
+ /* ------------------------------------------------------------ */
+ private String filenameValue(String nameEqualsValue)
+ {
+ int idx = nameEqualsValue.indexOf('=');
+ String value = nameEqualsValue.substring(idx+1).trim();
+
+ if (value.matches(".??[a-z,A-Z]\\:\\\\[^\\\\].*"))
+ {
+ //incorrectly escaped IE filenames that have the whole path
+ //we just strip any leading & trailing quotes and leave it as is
+ char first=value.charAt(0);
+ if (first=='"' || first=='\'')
+ value=value.substring(1);
+ char last=value.charAt(value.length()-1);
+ if (last=='"' || last=='\'')
+ value = value.substring(0,value.length()-1);
+
+ return value;
+ }
+ else
+ //unquote the string, but allow any backslashes that don't
+ //form a valid escape sequence to remain as many browsers
+ //even on *nix systems will not escape a filename containing
+ //backslashes
+ return QuotedStringTokenizer.unquoteOnly(value, true);
+ }
+
+ private static class Base64InputStream extends InputStream
+ {
+ ReadLineInputStream _in;
+ String _line;
+ byte[] _buffer;
+ int _pos;
+
+
+ public Base64InputStream(ReadLineInputStream rlis)
+ {
+ _in = rlis;
+ }
+
+ @Override
+ public int read() throws IOException
+ {
+ if (_buffer==null || _pos>= _buffer.length)
+ {
+ //Any CR and LF will be consumed by the readLine() call.
+ //We need to put them back into the bytes returned from this
+ //method because the parsing of the multipart content uses them
+ //as markers to determine when we've reached the end of a part.
+ _line = _in.readLine();
+ if (_line==null)
+ return -1; //nothing left
+ if (_line.startsWith("--"))
+ _buffer=(_line+"\r\n").getBytes(); //boundary marking end of part
+ else if (_line.length()==0)
+ _buffer="\r\n".getBytes(); //blank line
+ else
+ {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream((4*_line.length()/3)+2);
+ B64Code.decode(_line, baos);
+ baos.write(13);
+ baos.write(10);
+ _buffer = baos.toByteArray();
+ }
+
+ _pos=0;
+ }
+
+ return _buffer[_pos++];
+ }
+ }
+}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ReadLineInputStream.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ReadLineInputStream.java
index 02e8ba6..96625e4 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/ReadLineInputStream.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ReadLineInputStream.java
@@ -58,8 +58,8 @@
int m=markpos;
markpos=-1;
if (pos>m)
- return new String(buf,m,pos-m,StringUtil.__UTF8);
-
+ return new String(buf,m,pos-m,StringUtil.__UTF8_CHARSET);
+
return null;
}
@@ -77,7 +77,7 @@
_skipLF=true;
int m=markpos;
markpos=-1;
- return new String(buf,m,p-m-1,StringUtil.__UTF8);
+ return new String(buf,m,p-m-1,StringUtil.__UTF8_CHARSET);
}
if (b=='\n')
@@ -91,7 +91,7 @@
}
int m=markpos;
markpos=-1;
- return new String(buf,m,pos-m-1,StringUtil.__UTF8);
+ return new String(buf,m,pos-m-1,StringUtil.__UTF8_CHARSET);
}
}
}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java
index 74fa201..6f74e02 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java
@@ -487,6 +487,9 @@
/* ------------------------------------------------------------ */
+ /**
+ * @deprecated
+ */
public static byte[] readLine(InputStream in) throws IOException
{
byte[] buf = new byte[256];
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/UrlEncoded.java b/jetty-util/src/main/java/org/eclipse/jetty/util/UrlEncoded.java
index fa74d6b..b7c24c4 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/UrlEncoded.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/UrlEncoded.java
@@ -226,7 +226,7 @@
key = null;
value=null;
if (maxKeys>0 && map.size()>maxKeys)
- throw new IllegalStateException("Form too many keys");
+ throw new IllegalStateException(String.format("Form with too many keys [%d > %d]",map.size(),maxKeys));
break;
case '=':
if (key!=null)
@@ -414,7 +414,7 @@
key = null;
value=null;
if (maxKeys>0 && map.size()>maxKeys)
- throw new IllegalStateException("Form too many keys");
+ throw new IllegalStateException(String.format("Form with too many keys [%d > %d]",map.size(),maxKeys));
break;
case '=':
@@ -515,7 +515,7 @@
key = null;
value=null;
if (maxKeys>0 && map.size()>maxKeys)
- throw new IllegalStateException("Form too many keys");
+ throw new IllegalStateException(String.format("Form with too many keys [%d > %d]",map.size(),maxKeys));
break;
case '=':
@@ -656,7 +656,7 @@
key = null;
value=null;
if (maxKeys>0 && map.size()>maxKeys)
- throw new IllegalStateException("Form too many keys");
+ throw new IllegalStateException(String.format("Form with too many keys [%d > %d]",map.size(),maxKeys));
break;
case '=':
if (key!=null)
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ajax/JSONPojoConvertor.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ajax/JSONPojoConvertor.java
index a35a32e..a489d3f 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/ajax/JSONPojoConvertor.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ajax/JSONPojoConvertor.java
@@ -135,7 +135,7 @@
case 1:
if (name.startsWith("set") && name.length()>3)
{
- name=name.substring(3,4).toLowerCase()+name.substring(4);
+ name=name.substring(3,4).toLowerCase(Locale.ENGLISH)+name.substring(4);
if(includeField(name, m))
addSetter(name, m);
}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarFileResource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarFileResource.java
index 15d0367..7974448 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarFileResource.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarFileResource.java
@@ -65,19 +65,23 @@
_list=null;
_entry=null;
_file=null;
-
- if ( _jarFile != null )
+ //if the jvm is not doing url caching, then the JarFiles will not be cached either,
+ //and so they are safe to close
+ if (!getUseCaches())
{
- try
+ if ( _jarFile != null )
{
- _jarFile.close();
- }
- catch ( IOException ioe )
- {
- LOG.ignore(ioe);
+ try
+ {
+ LOG.debug("Closing JarFile "+_jarFile.getName());
+ _jarFile.close();
+ }
+ catch ( IOException ioe )
+ {
+ LOG.ignore(ioe);
+ }
}
}
-
_jarFile=null;
super.release();
}
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java
index 21742e3..c0b0a90 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java
@@ -18,6 +18,28 @@
package org.eclipse.jetty.util.ssl;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+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.security.CertificateUtils;
+import org.eclipse.jetty.util.security.CertificateValidator;
+import org.eclipse.jetty.util.security.Password;
+
+import javax.net.ssl.CertPathTrustManagerParameters;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509KeyManager;
+import javax.net.ssl.X509TrustManager;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -40,28 +62,6 @@
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
-import javax.net.ssl.CertPathTrustManagerParameters;
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLEngine;
-import javax.net.ssl.SSLServerSocket;
-import javax.net.ssl.SSLServerSocketFactory;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.SSLSocketFactory;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
-import javax.net.ssl.X509KeyManager;
-import javax.net.ssl.X509TrustManager;
-
-import org.eclipse.jetty.util.IO;
-import org.eclipse.jetty.util.component.AbstractLifeCycle;
-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.security.CertificateUtils;
-import org.eclipse.jetty.util.security.CertificateValidator;
-import org.eclipse.jetty.util.security.Password;
/* ------------------------------------------------------------ */
@@ -111,12 +111,12 @@
/** Excluded protocols. */
private final Set<String> _excludeProtocols = new LinkedHashSet<String>();
/** Included protocols. */
- private Set<String> _includeProtocols = null;
+ private Set<String> _includeProtocols = new LinkedHashSet<String>();
/** Excluded cipher suites. */
private final Set<String> _excludeCipherSuites = new LinkedHashSet<String>();
/** Included cipher suites. */
- private Set<String> _includeCipherSuites = null;
+ private Set<String> _includeCipherSuites = new LinkedHashSet<String>();
/** Keystore path. */
private String _keyStorePath;
@@ -252,7 +252,7 @@
}
SecureRandom secureRandom = (_secureRandomAlgorithm == null)?null:SecureRandom.getInstance(_secureRandomAlgorithm);
- _context = SSLContext.getInstance(_sslProtocol);
+ _context = (_sslProvider == null)?SSLContext.getInstance(_sslProtocol):SSLContext.getInstance(_sslProtocol,_sslProvider);
_context.init(null, trust_managers, secureRandom);
}
else
@@ -323,7 +323,6 @@
public void setExcludeProtocols(String... protocols)
{
checkNotStarted();
-
_excludeProtocols.clear();
_excludeProtocols.addAll(Arrays.asList(protocols));
}
@@ -357,8 +356,8 @@
public void setIncludeProtocols(String... protocols)
{
checkNotStarted();
-
- _includeProtocols = new LinkedHashSet<String>(Arrays.asList(protocols));
+ _includeProtocols.clear();
+ _includeProtocols.addAll(Arrays.asList(protocols));
}
/* ------------------------------------------------------------ */
@@ -413,8 +412,8 @@
public void setIncludeCipherSuites(String... cipherSuites)
{
checkNotStarted();
-
- _includeCipherSuites = new LinkedHashSet<String>(Arrays.asList(cipherSuites));
+ _includeCipherSuites.clear();
+ _includeCipherSuites.addAll(Arrays.asList(cipherSuites));
}
/* ------------------------------------------------------------ */
@@ -1214,7 +1213,7 @@
Set<String> selected_protocols = new LinkedHashSet<String>();
// Set the starting protocols - either from the included or enabled list
- if (_includeProtocols!=null)
+ if (!_includeProtocols.isEmpty())
{
// Use only the supported included protocols
for (String protocol : _includeProtocols)
@@ -1246,7 +1245,7 @@
Set<String> selected_ciphers = new LinkedHashSet<String>();
// Set the starting ciphers - either from the included or enabled list
- if (_includeCipherSuites!=null)
+ if (!_includeCipherSuites.isEmpty())
{
// Use only the supported included ciphers
for (String cipherSuite : _includeCipherSuites)
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java
index 9ec4413..6bc310f 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java
@@ -25,7 +25,6 @@
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
@@ -33,6 +32,7 @@
import java.util.concurrent.atomic.AtomicLong;
import org.eclipse.jetty.util.BlockingArrayQueue;
+import org.eclipse.jetty.util.ConcurrentHashSet;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.component.AggregateLifeCycle;
import org.eclipse.jetty.util.component.Dumpable;
@@ -48,7 +48,7 @@
private final AtomicInteger _threadsStarted = new AtomicInteger();
private final AtomicInteger _threadsIdle = new AtomicInteger();
private final AtomicLong _lastShrink = new AtomicLong();
- private final ConcurrentLinkedQueue<Thread> _threads=new ConcurrentLinkedQueue<Thread>();
+ private final ConcurrentHashSet<Thread> _threads=new ConcurrentHashSet<Thread>();
private final Object _joinLock = new Object();
private BlockingQueue<Runnable> _jobs;
private String _name;
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/MultiPartInputStreamTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/MultiPartInputStreamTest.java
new file mode 100644
index 0000000..9e617e0
--- /dev/null
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/MultiPartInputStreamTest.java
@@ -0,0 +1,858 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.util;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.util.Collection;
+
+import javax.servlet.MultipartConfigElement;
+import javax.servlet.ServletException;
+import javax.servlet.http.Part;
+
+import junit.framework.TestCase;
+
+import org.eclipse.jetty.util.MultiPartInputStream.MultiPart;
+import org.hamcrest.core.IsNot;
+
+/**
+ * MultiPartInputStreamTest
+ *
+ *
+ */
+public class MultiPartInputStreamTest extends TestCase
+{
+ private static final String FILENAME = "stuff.txt";
+ protected String _contentType = "multipart/form-data, boundary=AaB03x";
+ protected String _multi = createMultipartRequestString(FILENAME);
+ protected String _dirname = System.getProperty("java.io.tmpdir")+File.separator+"myfiles-"+System.currentTimeMillis();
+ protected File _tmpDir = new File(_dirname);
+
+ public MultiPartInputStreamTest ()
+ {
+ _tmpDir.deleteOnExit();
+ }
+
+ public void testBadMultiPartRequest()
+ throws Exception
+ {
+ String boundary = "X0Y0";
+ String str = "--" + boundary + "\r\n"+
+ "Content-Disposition: form-data; name=\"fileup\"; filename=\"test.upload\"\r\n"+
+ "Content-Type: application/octet-stream\r\n\r\n"+
+ "How now brown cow."+
+ "\r\n--" + boundary + "-\r\n\r\n";
+
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(str.getBytes()),
+ "multipart/form-data, boundary="+boundary,
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ try
+ {
+ mpis.getParts();
+ fail ("Multipart incomplete");
+ }
+ catch (IOException e)
+ {
+ assertTrue(e.getMessage().startsWith("Incomplete"));
+ }
+ }
+
+
+ public void testNoBoundaryRequest()
+ throws Exception
+ {
+ String str = "--\r\n"+
+ "Content-Disposition: form-data; name=\"fileName\"\r\n"+
+ "Content-Type: text/plain; charset=US-ASCII\r\n"+
+ "Content-Transfer-Encoding: 8bit\r\n"+
+ "\r\n"+
+ "abc\r\n"+
+ "--\r\n"+
+ "Content-Disposition: form-data; name=\"desc\"\r\n"+
+ "Content-Type: text/plain; charset=US-ASCII\r\n"+
+ "Content-Transfer-Encoding: 8bit\r\n"+
+ "\r\n"+
+ "123\r\n"+
+ "--\r\n"+
+ "Content-Disposition: form-data; name=\"title\"\r\n"+
+ "Content-Type: text/plain; charset=US-ASCII\r\n"+
+ "Content-Transfer-Encoding: 8bit\r\n"+
+ "\r\n"+
+ "ttt\r\n"+
+ "--\r\n"+
+ "Content-Disposition: form-data; name=\"datafile5239138112980980385.txt\"; filename=\"datafile5239138112980980385.txt\"\r\n"+
+ "Content-Type: application/octet-stream; charset=ISO-8859-1\r\n"+
+ "Content-Transfer-Encoding: binary\r\n"+
+ "\r\n"+
+ "000\r\n"+
+ "----\r\n";
+
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(str.getBytes()),
+ "multipart/form-data",
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ Collection<Part> parts = mpis.getParts();
+ assertThat(parts.size(), is(4));
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ Part fileName = mpis.getPart("fileName");
+ assertThat(fileName, notNullValue());
+ assertThat(fileName.getSize(), is(3L));
+ IO.copy(fileName.getInputStream(), baos);
+ assertThat(baos.toString("US-ASCII"), is("abc"));
+
+ baos = new ByteArrayOutputStream();
+ Part desc = mpis.getPart("desc");
+ assertThat(desc, notNullValue());
+ assertThat(desc.getSize(), is(3L));
+ IO.copy(desc.getInputStream(), baos);
+ assertThat(baos.toString("US-ASCII"), is("123"));
+
+ baos = new ByteArrayOutputStream();
+ Part title = mpis.getPart("title");
+ assertThat(title, notNullValue());
+ assertThat(title.getSize(), is(3L));
+ IO.copy(title.getInputStream(), baos);
+ assertThat(baos.toString("US-ASCII"), is("ttt"));
+ }
+
+ public void testNoBody()
+ throws Exception
+ {
+ String body = "";
+
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(body.getBytes()),
+ _contentType,
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ try
+ {
+ mpis.getParts();
+ fail ("Multipart missing body");
+ }
+ catch (IOException e)
+ {
+ assertTrue(e.getMessage().startsWith("Missing content"));
+ }
+ }
+
+ public void testWhitespaceBodyWithCRLF()
+ throws Exception
+ {
+ String whitespace = " \n\n\n\r\n\r\n\r\n\r\n";
+
+
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(whitespace.getBytes()),
+ _contentType,
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ try
+ {
+ mpis.getParts();
+ fail ("Multipart missing body");
+ }
+ catch (IOException e)
+ {
+ assertTrue(e.getMessage().startsWith("Missing initial"));
+ }
+ }
+
+ public void testWhitespaceBody()
+ throws Exception
+ {
+ String whitespace = " ";
+
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(whitespace.getBytes()),
+ _contentType,
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ try
+ {
+ mpis.getParts();
+ fail ("Multipart missing body");
+ }
+ catch (IOException e)
+ {
+ assertTrue(e.getMessage().startsWith("Missing initial"));
+ }
+ }
+
+
+ public void testLeadingWhitespaceBodyWithCRLF()
+ throws Exception
+ {
+ String body = " \n\n\n\r\n\r\n\r\n\r\n"+
+ "--AaB03x\r\n"+
+ "content-disposition: form-data; name=\"field1\"\r\n"+
+ "\r\n"+
+ "Joe Blow\r\n"+
+ "--AaB03x\r\n"+
+ "content-disposition: form-data; name=\"stuff\"; filename=\"" + "foo.txt" + "\"\r\n"+
+ "Content-Type: text/plain\r\n"+
+ "\r\n"+"aaaa"+
+ "bbbbb"+"\r\n" +
+ "--AaB03x--\r\n";
+
+
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(body.getBytes()),
+ _contentType,
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+
+ Collection<Part> parts = mpis.getParts();
+ assertThat(parts, notNullValue());
+ assertThat(parts.size(), is(2));
+ Part field1 = mpis.getPart("field1");
+ assertThat(field1, notNullValue());
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ IO.copy(field1.getInputStream(), baos);
+ assertThat(baos.toString("US-ASCII"), is("Joe Blow"));
+
+ Part stuff = mpis.getPart("stuff");
+ assertThat(stuff, notNullValue());
+ baos = new ByteArrayOutputStream();
+ IO.copy(stuff.getInputStream(), baos);
+ assertTrue(baos.toString("US-ASCII").contains("aaaa"));
+ }
+
+
+
+
+ public void testLeadingWhitespaceBodyWithoutCRLF()
+ throws Exception
+ {
+ String body = " "+
+ "--AaB03x\r\n"+
+ "content-disposition: form-data; name=\"field1\"\r\n"+
+ "\r\n"+
+ "Joe Blow\r\n"+
+ "--AaB03x\r\n"+
+ "content-disposition: form-data; name=\"stuff\"; filename=\"" + "foo.txt" + "\"\r\n"+
+ "Content-Type: text/plain\r\n"+
+ "\r\n"+"aaaa"+
+ "bbbbb"+"\r\n" +
+ "--AaB03x--\r\n";
+
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(body.getBytes()),
+ _contentType,
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+
+ Collection<Part> parts = mpis.getParts();
+ assertThat(parts, notNullValue());
+ assertThat(parts.size(), is(2));
+ Part field1 = mpis.getPart("field1");
+ assertThat(field1, notNullValue());
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ IO.copy(field1.getInputStream(), baos);
+ assertThat(baos.toString("US-ASCII"), is("Joe Blow"));
+
+ Part stuff = mpis.getPart("stuff");
+ assertThat(stuff, notNullValue());
+ baos = new ByteArrayOutputStream();
+ IO.copy(stuff.getInputStream(), baos);
+ assertTrue(baos.toString("US-ASCII").contains("bbbbb"));
+ }
+
+
+
+
+
+
+ public void testNonMultiPartRequest()
+ throws Exception
+ {
+
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(_multi.getBytes()),
+ "Content-type: text/plain",
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ assertTrue(mpis.getParts().isEmpty());
+ }
+
+ public void testNoLimits()
+ throws Exception
+ {
+ MultipartConfigElement config = new MultipartConfigElement(_dirname);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(_multi.getBytes()),
+ _contentType,
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ Collection<Part> parts = mpis.getParts();
+ assertFalse(parts.isEmpty());
+ }
+
+ public void testRequestTooBig ()
+ throws Exception
+ {
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 60, 100, 50);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(_multi.getBytes()),
+ _contentType,
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ Collection<Part> parts = null;
+ try
+ {
+ parts = mpis.getParts();
+ fail("Request should have exceeded maxRequestSize");
+ }
+ catch (IllegalStateException e)
+ {
+ assertTrue(e.getMessage().startsWith("Request exceeds maxRequestSize"));
+ }
+ }
+
+ public void testFileTooBig()
+ throws Exception
+ {
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 40, 1024, 30);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(_multi.getBytes()),
+ _contentType,
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ Collection<Part> parts = null;
+ try
+ {
+ parts = mpis.getParts();
+ fail("stuff.txt should have been larger than maxFileSize");
+ }
+ catch (IllegalStateException e)
+ {
+ assertTrue(e.getMessage().startsWith("Multipart Mime part"));
+ }
+ }
+
+ public void testPartFileNotDeleted () throws Exception
+ {
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(createMultipartRequestString("tptfd").getBytes()),
+ _contentType,
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ Collection<Part> parts = mpis.getParts();
+
+ MultiPart part = (MultiPart)mpis.getPart("stuff");
+ File stuff = ((MultiPartInputStream.MultiPart)part).getFile();
+ assertThat(stuff,notNullValue()); // longer than 100 bytes, should already be a tmp file
+ part.write("tptfd.txt");
+ File tptfd = new File (_dirname+File.separator+"tptfd.txt");
+ assertThat(tptfd.exists(), is(true));
+ assertThat(stuff.exists(), is(false)); //got renamed
+ part.cleanUp();
+ assertThat(tptfd.exists(), is(true)); //explicitly written file did not get removed after cleanup
+ tptfd.deleteOnExit(); //clean up test
+ }
+
+
+ public void testPartTmpFileDeletion () throws Exception
+ {
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(createMultipartRequestString("tptfd").getBytes()),
+ _contentType,
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ Collection<Part> parts = mpis.getParts();
+
+ MultiPart part = (MultiPart)mpis.getPart("stuff");
+ File stuff = ((MultiPartInputStream.MultiPart)part).getFile();
+ assertThat(stuff,notNullValue()); // longer than 100 bytes, should already be a tmp file
+ assertThat (stuff.exists(), is(true));
+ part.cleanUp();
+ assertThat(stuff.exists(), is(false)); //tmp file was removed after cleanup
+ }
+
+
+ public void testLFOnlyRequest()
+ throws Exception
+ {
+ String str = "--AaB03x\n"+
+ "content-disposition: form-data; name=\"field1\"\n"+
+ "\n"+
+ "Joe Blow\n"+
+ "--AaB03x\n"+
+ "content-disposition: form-data; name=\"field2\"\n"+
+ "\n"+
+ "Other\n"+
+ "--AaB03x--\n";
+
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(str.getBytes()),
+ _contentType,
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ Collection<Part> parts = mpis.getParts();
+ assertThat(parts.size(), is(2));
+ Part p1 = mpis.getPart("field1");
+ assertThat(p1, notNullValue());
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ IO.copy(p1.getInputStream(), baos);
+ assertThat(baos.toString("UTF-8"), is("Joe Blow"));
+
+ Part p2 = mpis.getPart("field2");
+ assertThat(p2, notNullValue());
+ baos = new ByteArrayOutputStream();
+ IO.copy(p2.getInputStream(), baos);
+ assertThat(baos.toString("UTF-8"), is("Other"));
+ }
+
+
+ public void testCROnlyRequest()
+ throws Exception
+ {
+ String str = "--AaB03x\r"+
+ "content-disposition: form-data; name=\"field1\"\r"+
+ "\r"+
+ "Joe Blow\r"+
+ "--AaB03x\r"+
+ "content-disposition: form-data; name=\"field2\"\r"+
+ "\r"+
+ "Other\r"+
+ "--AaB03x--\r";
+
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(str.getBytes()),
+ _contentType,
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ Collection<Part> parts = mpis.getParts();
+ assertThat(parts.size(), is(2));
+
+ assertThat(parts.size(), is(2));
+ Part p1 = mpis.getPart("field1");
+ assertThat(p1, notNullValue());
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ IO.copy(p1.getInputStream(), baos);
+ assertThat(baos.toString("UTF-8"), is("Joe Blow"));
+
+ Part p2 = mpis.getPart("field2");
+ assertThat(p2, notNullValue());
+ baos = new ByteArrayOutputStream();
+ IO.copy(p2.getInputStream(), baos);
+ assertThat(baos.toString("UTF-8"), is("Other"));
+ }
+
+
+ public void testCRandLFMixRequest()
+ throws Exception
+ {
+ String str = "--AaB03x\r"+
+ "content-disposition: form-data; name=\"field1\"\r"+
+ "\r"+
+ "\nJoe Blow\n"+
+ "\r"+
+ "--AaB03x\r"+
+ "content-disposition: form-data; name=\"field2\"\r"+
+ "\r"+
+ "Other\r"+
+ "--AaB03x--\r";
+
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(str.getBytes()),
+ _contentType,
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ Collection<Part> parts = mpis.getParts();
+ assertThat(parts.size(), is(2));
+
+ Part p1 = mpis.getPart("field1");
+ assertThat(p1, notNullValue());
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ IO.copy(p1.getInputStream(), baos);
+ assertThat(baos.toString("UTF-8"), is("\nJoe Blow\n"));
+
+ Part p2 = mpis.getPart("field2");
+ assertThat(p2, notNullValue());
+ baos = new ByteArrayOutputStream();
+ IO.copy(p2.getInputStream(), baos);
+ assertThat(baos.toString("UTF-8"), is("Other"));
+ }
+
+
+ public void testBufferOverflowNoCRLF () throws Exception
+ {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write("--AaB03x".getBytes());
+ for (int i=0; i< 8500; i++) //create content that will overrun default buffer size of BufferedInputStream
+ {
+ baos.write('a');
+ }
+
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(baos.toByteArray()),
+ _contentType,
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ try
+ {
+ mpis.getParts();
+ fail ("Multipart buffer overrun");
+ }
+ catch (IOException e)
+ {
+ assertTrue(e.getMessage().startsWith("Buffer size exceeded"));
+ }
+
+ }
+
+
+ public void testCharsetEncoding () throws Exception
+ {
+ String contentType = "multipart/form-data; boundary=TheBoundary; charset=ISO-8859-1";
+ String str = "--TheBoundary\r"+
+ "content-disposition: form-data; name=\"field1\"\r"+
+ "\r"+
+ "\nJoe Blow\n"+
+ "\r"+
+ "--TheBoundary--\r";
+
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(str.getBytes()),
+ contentType,
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ Collection<Part> parts = mpis.getParts();
+ assertThat(parts.size(), is(1));
+ }
+
+
+ public void testBadlyEncodedFilename() throws Exception
+ {
+
+ String contents = "--AaB03x\r\n"+
+ "content-disposition: form-data; name=\"stuff\"; filename=\"" +"Taken on Aug 22 \\ 2012.jpg" + "\"\r\n"+
+ "Content-Type: text/plain\r\n"+
+ "\r\n"+"stuff"+
+ "aaa"+"\r\n" +
+ "--AaB03x--\r\n";
+
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(contents.getBytes()),
+ _contentType,
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ Collection<Part> parts = mpis.getParts();
+ assertThat(parts.size(), is(1));
+ assertThat(((MultiPartInputStream.MultiPart)parts.iterator().next()).getContentDispositionFilename(), is("Taken on Aug 22 \\ 2012.jpg"));
+ }
+
+ public void testBadlyEncodedMSFilename() throws Exception
+ {
+
+ String contents = "--AaB03x\r\n"+
+ "content-disposition: form-data; name=\"stuff\"; filename=\"" +"c:\\this\\really\\is\\some\\path\\to\\a\\file.txt" + "\"\r\n"+
+ "Content-Type: text/plain\r\n"+
+ "\r\n"+"stuff"+
+ "aaa"+"\r\n" +
+ "--AaB03x--\r\n";
+
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(contents.getBytes()),
+ _contentType,
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ Collection<Part> parts = mpis.getParts();
+ assertThat(parts.size(), is(1));
+ assertThat(((MultiPartInputStream.MultiPart)parts.iterator().next()).getContentDispositionFilename(), is("c:\\this\\really\\is\\some\\path\\to\\a\\file.txt"));
+ }
+
+ public void testCorrectlyEncodedMSFilename() throws Exception
+ {
+ String contents = "--AaB03x\r\n"+
+ "content-disposition: form-data; name=\"stuff\"; filename=\"" +"c:\\\\this\\\\really\\\\is\\\\some\\\\path\\\\to\\\\a\\\\file.txt" + "\"\r\n"+
+ "Content-Type: text/plain\r\n"+
+ "\r\n"+"stuff"+
+ "aaa"+"\r\n" +
+ "--AaB03x--\r\n";
+
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(contents.getBytes()),
+ _contentType,
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ Collection<Part> parts = mpis.getParts();
+ assertThat(parts.size(), is(1));
+ assertThat(((MultiPartInputStream.MultiPart)parts.iterator().next()).getContentDispositionFilename(), is("c:\\this\\really\\is\\some\\path\\to\\a\\file.txt"));
+ }
+
+ public void testMulti ()
+ throws Exception
+ {
+ testMulti(FILENAME);
+ }
+
+ public void testMultiWithSpaceInFilename() throws Exception
+ {
+ testMulti("stuff with spaces.txt");
+ }
+
+
+
+
+ private void testMulti(String filename) throws IOException, ServletException
+ {
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(createMultipartRequestString(filename).getBytes()),
+ _contentType,
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ Collection<Part> parts = mpis.getParts();
+ assertThat(parts.size(), is(2));
+ Part field1 = mpis.getPart("field1"); //field 1 too small to go into tmp file, should be in internal buffer
+ assertThat(field1,notNullValue());
+ assertThat(field1.getName(),is("field1"));
+ InputStream is = field1.getInputStream();
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ IO.copy(is, os);
+ assertEquals("Joe Blow", new String(os.toByteArray()));
+ assertEquals(8, field1.getSize());
+
+ assertNotNull(((MultiPartInputStream.MultiPart)field1).getBytes());//in internal buffer
+ field1.write("field1.txt");
+ assertNull(((MultiPartInputStream.MultiPart)field1).getBytes());//no longer in internal buffer
+ File f = new File (_dirname+File.separator+"field1.txt");
+ assertTrue(f.exists());
+ field1.write("another_field1.txt"); //write after having already written
+ File f2 = new File(_dirname+File.separator+"another_field1.txt");
+ assertTrue(f2.exists());
+ assertFalse(f.exists()); //should have been renamed
+ field1.delete(); //file should be deleted
+ assertFalse(f.exists()); //original file was renamed
+ assertFalse(f2.exists()); //2nd written file was explicitly deleted
+
+ MultiPart stuff = (MultiPart)mpis.getPart("stuff");
+ assertThat(stuff.getContentDispositionFilename(), is(filename));
+ assertThat(stuff.getContentType(),is("text/plain"));
+ assertThat(stuff.getHeader("Content-Type"),is("text/plain"));
+ assertThat(stuff.getHeaders("content-type").size(),is(1));
+ assertThat(stuff.getHeader("content-disposition"),is("form-data; name=\"stuff\"; filename=\"" + filename + "\""));
+ assertThat(stuff.getHeaderNames().size(),is(2));
+ assertThat(stuff.getSize(),is(51L));
+ File tmpfile = ((MultiPartInputStream.MultiPart)stuff).getFile();
+ assertThat(tmpfile,notNullValue()); // longer than 100 bytes, should already be a tmp file
+ assertThat(((MultiPartInputStream.MultiPart)stuff).getBytes(),nullValue()); //not in an internal buffer
+ assertThat(tmpfile.exists(),is(true));
+ assertThat(tmpfile.getName(),is(not("stuff with space.txt")));
+ stuff.write(filename);
+ f = new File(_dirname+File.separator+filename);
+ assertThat(f.exists(),is(true));
+ assertThat(tmpfile.exists(), is(false));
+ try
+ {
+ stuff.getInputStream();
+ }
+ catch (Exception e)
+ {
+ fail("Part.getInputStream() after file rename operation");
+ }
+ f.deleteOnExit(); //clean up after test
+ }
+
+ public void testMultiSameNames ()
+ throws Exception
+ {
+ String sameNames = "--AaB03x\r\n"+
+ "content-disposition: form-data; name=\"stuff\"; filename=\"stuff1.txt\"\r\n"+
+ "Content-Type: text/plain\r\n"+
+ "\r\n"+
+ "00000\r\n"+
+ "--AaB03x\r\n"+
+ "content-disposition: form-data; name=\"stuff\"; filename=\"stuff2.txt\"\r\n"+
+ "Content-Type: text/plain\r\n"+
+ "\r\n"+
+ "110000000000000000000000000000000000000000000000000\r\n"+
+ "--AaB03x--\r\n";
+
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(sameNames.getBytes()),
+ _contentType,
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ Collection<Part> parts = mpis.getParts();
+ assertEquals(2, parts.size());
+ for (Part p:parts)
+ assertEquals("stuff", p.getName());
+
+ //if they all have the name name, then only retrieve the first one
+ Part p = mpis.getPart("stuff");
+ assertNotNull(p);
+ assertEquals(5, p.getSize());
+ }
+
+
+ public void testBase64EncodedContent () throws Exception
+ {
+ String contentWithEncodedPart =
+ "--AaB03x\r\n"+
+ "Content-disposition: form-data; name=\"other\"\r\n"+
+ "Content-Type: text/plain\r\n"+
+ "\r\n"+
+ "other" + "\r\n"+
+ "--AaB03x\r\n"+
+ "Content-disposition: form-data; name=\"stuff\"; filename=\"stuff.txt\"\r\n"+
+ "Content-Transfer-Encoding: base64\r\n"+
+ "Content-Type: application/octet-stream\r\n"+
+ "\r\n"+
+ B64Code.encode("hello jetty") + "\r\n"+
+ "--AaB03x\r\n"+
+ "Content-disposition: form-data; name=\"final\"\r\n"+
+ "Content-Type: text/plain\r\n"+
+ "\r\n"+
+ "the end" + "\r\n"+
+ "--AaB03x--\r\n";
+
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(contentWithEncodedPart.getBytes()),
+ _contentType,
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ Collection<Part> parts = mpis.getParts();
+ assertEquals(3, parts.size());
+
+ Part p1 = mpis.getPart("other");
+ assertNotNull(p1);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ IO.copy(p1.getInputStream(), baos);
+ assertEquals("other", baos.toString("US-ASCII"));
+
+ Part p2 = mpis.getPart("stuff");
+ assertNotNull(p2);
+ baos = new ByteArrayOutputStream();
+ IO.copy(p2.getInputStream(), baos);
+ assertEquals("hello jetty", baos.toString("US-ASCII"));
+
+ Part p3 = mpis.getPart("final");
+ assertNotNull(p3);
+ baos = new ByteArrayOutputStream();
+ IO.copy(p3.getInputStream(), baos);
+ assertEquals("the end", baos.toString("US-ASCII"));
+ }
+
+ public void testQuotedPrintableEncoding () throws Exception
+ {
+ String contentWithEncodedPart =
+ "--AaB03x\r\n"+
+ "Content-disposition: form-data; name=\"other\"\r\n"+
+ "Content-Type: text/plain\r\n"+
+ "\r\n"+
+ "other" + "\r\n"+
+ "--AaB03x\r\n"+
+ "Content-disposition: form-data; name=\"stuff\"; filename=\"stuff.txt\"\r\n"+
+ "Content-Transfer-Encoding: quoted-printable\r\n"+
+ "Content-Type: text/plain\r\n"+
+ "\r\n"+
+ "truth=3Dbeauty" + "\r\n"+
+ "--AaB03x--\r\n";
+ MultipartConfigElement config = new MultipartConfigElement(_dirname, 1024, 3072, 50);
+ MultiPartInputStream mpis = new MultiPartInputStream(new ByteArrayInputStream(contentWithEncodedPart.getBytes()),
+ _contentType,
+ config,
+ _tmpDir);
+ mpis.setDeleteOnExit(true);
+ Collection<Part> parts = mpis.getParts();
+ assertEquals(2, parts.size());
+
+ Part p1 = mpis.getPart("other");
+ assertNotNull(p1);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ IO.copy(p1.getInputStream(), baos);
+ assertEquals("other", baos.toString("US-ASCII"));
+
+ Part p2 = mpis.getPart("stuff");
+ assertNotNull(p2);
+ baos = new ByteArrayOutputStream();
+ IO.copy(p2.getInputStream(), baos);
+ assertEquals("truth=beauty", baos.toString("US-ASCII"));
+ }
+
+
+
+
+
+ private String createMultipartRequestString(String filename)
+ {
+ int length = filename.length();
+ String name = filename;
+ if (length > 10)
+ name = filename.substring(0,10);
+ StringBuffer filler = new StringBuffer();
+ int i = name.length();
+ while (i < 51)
+ {
+ filler.append("0");
+ i++;
+ }
+
+ return "--AaB03x\r\n"+
+ "content-disposition: form-data; name=\"field1\"\r\n"+
+ "\r\n"+
+ "Joe Blow\r\n"+
+ "--AaB03x\r\n"+
+ "content-disposition: form-data; name=\"stuff\"; filename=\"" + filename + "\"\r\n"+
+ "Content-Type: text/plain\r\n"+
+ "\r\n"+name+
+ filler.toString()+"\r\n" +
+ "--AaB03x--\r\n";
+ }
+}
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceTest.java
index 826bb1e..2992852 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceTest.java
@@ -118,11 +118,11 @@
/* ------------------------------------------------------------ */
@BeforeClass
public static void setUp()
- throws Exception
+ throws Exception
{
if (data!=null)
return;
-
+
File file = new File(__userDir);
file=new File(file.getCanonicalPath());
URI uri = file.toURI();
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/ssl/SslContextFactoryTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/ssl/SslContextFactoryTest.java
index 88780b3..3c23805 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/ssl/SslContextFactoryTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/ssl/SslContextFactoryTest.java
@@ -18,10 +18,6 @@
package org.eclipse.jetty.util.ssl;
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.KeyStore;
-
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.StdErrLog;
@@ -30,51 +26,56 @@
import org.junit.Before;
import org.junit.Test;
-import static junit.framework.Assert.assertTrue;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.KeyStore;
+
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
-public class SslContextFactoryTest
+public class SslContextFactoryTest
{
private SslContextFactory cf;
@Before
- public void setUp() throws Exception
+ public void setUp() throws Exception
{
cf = new SslContextFactory();
}
@Test
- public void testNoTsFileKs() throws Exception
+ public void testNoTsFileKs() throws Exception
{
- String keystorePath = System.getProperty("basedir",".") + "/src/test/resources/keystore";
+ String keystorePath = System.getProperty("basedir", ".") + "/src/test/resources/keystore";
cf.setKeyStorePassword("storepwd");
cf.setKeyManagerPassword("keypwd");
-
+
cf.start();
-
- assertTrue(cf.getSslContext()!=null);
+
+ assertTrue(cf.getSslContext() != null);
}
-
+
@Test
- public void testNoTsStreamKs() throws Exception
+ public void testNoTsStreamKs() throws Exception
{
InputStream keystoreInputStream = this.getClass().getResourceAsStream("keystore");
cf.setKeyStoreInputStream(keystoreInputStream);
cf.setKeyStorePassword("storepwd");
cf.setKeyManagerPassword("keypwd");
-
+
cf.start();
-
- assertTrue(cf.getSslContext()!=null);
+
+ assertTrue(cf.getSslContext() != null);
}
-
+
@Test
- public void testNoTsSetKs() throws Exception
+ public void testNoTsSetKs() throws Exception
{
InputStream keystoreInputStream = this.getClass().getResourceAsStream("keystore");
@@ -83,28 +84,28 @@
cf.setKeyStore(ks);
cf.setKeyManagerPassword("keypwd");
-
+
cf.start();
-
- assertTrue(cf.getSslContext()!=null);
- }
-
- @Test
- public void testNoTsNoKs() throws Exception
- {
- cf.start();
- assertTrue(cf.getSslContext()!=null);
- }
-
- @Test
- public void testTrustAll() throws Exception
- {
- cf.start();
- assertTrue(cf.getSslContext()!=null);
+
+ assertTrue(cf.getSslContext() != null);
}
@Test
- public void testNoTsResourceKs() throws Exception
+ public void testNoTsNoKs() throws Exception
+ {
+ cf.start();
+ assertTrue(cf.getSslContext() != null);
+ }
+
+ @Test
+ public void testTrustAll() throws Exception
+ {
+ cf.start();
+ assertTrue(cf.getSslContext() != null);
+ }
+
+ @Test
+ public void testNoTsResourceKs() throws Exception
{
Resource keystoreResource = Resource.newSystemResource("keystore");
@@ -114,11 +115,11 @@
cf.start();
- assertTrue(cf.getSslContext()!=null);
+ assertTrue(cf.getSslContext() != null);
}
@Test
- public void testResourceTsResourceKs() throws Exception
+ public void testResourceTsResourceKs() throws Exception
{
Resource keystoreResource = Resource.newSystemResource("keystore");
Resource truststoreResource = Resource.newSystemResource("keystore");
@@ -131,11 +132,11 @@
cf.start();
- assertTrue(cf.getSslContext()!=null);
+ assertTrue(cf.getSslContext() != null);
}
@Test
- public void testResourceTsResourceKsWrongPW() throws Exception
+ public void testResourceTsResourceKsWrongPW() throws Exception
{
Resource keystoreResource = Resource.newSystemResource("keystore");
Resource truststoreResource = Resource.newSystemResource("keystore");
@@ -146,19 +147,16 @@
cf.setKeyManagerPassword("wrong_keypwd");
cf.setTrustStorePassword("storepwd");
- try
- {
- ((StdErrLog)Log.getLogger(AbstractLifeCycle.class)).setHideStacks(true);
+ try {
+ ((StdErrLog) Log.getLogger(AbstractLifeCycle.class)).setHideStacks(true);
cf.start();
Assert.fail();
- }
- catch(java.security.UnrecoverableKeyException e)
- {
+ } catch (java.security.UnrecoverableKeyException e) {
}
}
@Test
- public void testResourceTsWrongPWResourceKs() throws Exception
+ public void testResourceTsWrongPWResourceKs() throws Exception
{
Resource keystoreResource = Resource.newSystemResource("keystore");
Resource truststoreResource = Resource.newSystemResource("keystore");
@@ -169,39 +167,31 @@
cf.setKeyManagerPassword("keypwd");
cf.setTrustStorePassword("wrong_storepwd");
- try
- {
- ((StdErrLog)Log.getLogger(AbstractLifeCycle.class)).setHideStacks(true);
+ try {
+ ((StdErrLog) Log.getLogger(AbstractLifeCycle.class)).setHideStacks(true);
cf.start();
Assert.fail();
- }
- catch(IOException e)
- {
+ } catch (IOException e) {
}
}
-
+
@Test
- public void testNoKeyConfig() throws Exception
+ public void testNoKeyConfig() throws Exception
{
- try
- {
- ((StdErrLog)Log.getLogger(AbstractLifeCycle.class)).setHideStacks(true);
+ try {
+ ((StdErrLog) Log.getLogger(AbstractLifeCycle.class)).setHideStacks(true);
cf.setTrustStore("/foo");
cf.start();
Assert.fail();
- }
- catch (IllegalStateException e)
- {
-
- }
- catch (Exception e)
- {
+ } catch (IllegalStateException e) {
+
+ } catch (Exception e) {
Assert.fail("Unexpected exception");
}
}
@Test
- public void testSetIncludeCipherSuitesPreservesOrder()
+ public void testSetIncludeCipherSuitesPreservesOrder()
{
String[] supportedCipherSuites = new String[]{"cipher4", "cipher2", "cipher1", "cipher3"};
String[] includeCipherSuites = {"cipher1", "cipher3", "cipher4"};
@@ -213,7 +203,7 @@
}
@Test
- public void testSetIncludeProtocolsPreservesOrder()
+ public void testSetIncludeProtocolsPreservesOrder()
{
String[] supportedProtocol = new String[]{"cipher4", "cipher2", "cipher1", "cipher3"};
String[] includeProtocol = {"cipher1", "cipher3", "cipher4"};
@@ -224,11 +214,20 @@
assertSelectedMatchesIncluded(includeProtocol, selectedProtocol);
}
- private void assertSelectedMatchesIncluded(String[] includeStrings, String[] selectedStrings)
+ private void assertSelectedMatchesIncluded(String[] includeStrings, String[] selectedStrings)
{
assertThat(includeStrings.length + " strings are selected", selectedStrings.length, is(includeStrings.length));
assertThat("order from includeStrings is preserved", selectedStrings[0], equalTo(includeStrings[0]));
assertThat("order from includeStrings is preserved", selectedStrings[1], equalTo(includeStrings[1]));
assertThat("order from includeStrings is preserved", selectedStrings[2], equalTo(includeStrings[2]));
}
+
+ @Test
+ public void testProtocolAndCipherSettingsAreNPESafe()
+ {
+ assertNotNull(cf.getExcludeProtocols());
+ assertNotNull(cf.getIncludeProtocols());
+ assertNotNull(cf.getExcludeCipherSuites());
+ assertNotNull(cf.getIncludeCipherSuites());
+ }
}
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/thread/TimeoutTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/thread/TimeoutTest.java
index 3c2faa6..7555a76 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/thread/TimeoutTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/thread/TimeoutTest.java
@@ -29,6 +29,8 @@
public class TimeoutTest
{
+ private boolean _stress=Boolean.getBoolean("STRESS");
+
Object lock = new Object();
Timeout timeout = new Timeout(null);
Timeout.Task[] tasks;
@@ -136,6 +138,9 @@
@Test
public void testStress() throws Exception
{
+ if ( !_stress )
+ return;
+
final int LOOP=250;
final AtomicBoolean running=new AtomicBoolean(true);
final AtomicIntegerArray count = new AtomicIntegerArray( 4 );
diff --git a/jetty-webapp/pom.xml b/jetty-webapp/pom.xml
index fdda808..0b2be79 100644
--- a/jetty-webapp/pom.xml
+++ b/jetty-webapp/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-webapp</artifactId>
@@ -53,6 +53,11 @@
<goals>
<goal>manifest</goal>
</goals>
+ <configuration>
+ <instructions>
+ <Import-Package>javax.servlet.*;version="2.6.0",*</Import-Package>
+ </instructions>
+ </configuration>
</execution>
</executions>
</plugin>
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/DiscoveredAnnotation.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/DiscoveredAnnotation.java
index e815ad6..753f844 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/DiscoveredAnnotation.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/DiscoveredAnnotation.java
@@ -21,6 +21,7 @@
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.Resource;
/**
* DiscoveredAnnotation
@@ -36,16 +37,28 @@
protected WebAppContext _context;
protected String _className;
protected Class<?> _clazz;
+ protected Resource _resource; //resource it was discovered on, can be null (eg from WEB-INF/classes)
public abstract void apply();
public DiscoveredAnnotation (WebAppContext context, String className)
{
- _context = context;
- _className = className;
+ this(context,className, null);
}
+ public DiscoveredAnnotation(WebAppContext context, String className, Resource resource)
+ {
+ _context = context;
+ _className = className;
+ _resource = resource;
+ }
+
+ public Resource getResource ()
+ {
+ return _resource;
+ }
+
public Class<?> getTargetClass()
{
if (_clazz != null)
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/FragmentDescriptor.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/FragmentDescriptor.java
index 52eeba7..0f9208d 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/FragmentDescriptor.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/FragmentDescriptor.java
@@ -136,7 +136,7 @@
node = (XmlParser.Node) o;
if (node.getTag().equalsIgnoreCase("others"))
{
- if (_otherType != OtherType.After)
+ if (_otherType != OtherType.None)
throw new IllegalStateException("Duplicate <other> clause detected in "+_xml.getURI());
_otherType = OtherType.After;
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/JettyWebXmlConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/JettyWebXmlConfiguration.java
index aaad463..f1e13e2 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/JettyWebXmlConfiguration.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/JettyWebXmlConfiguration.java
@@ -18,6 +18,7 @@
package org.eclipse.jetty.webapp;
+import java.util.HashMap;
import java.util.Map;
import org.eclipse.jetty.util.log.Log;
@@ -30,7 +31,7 @@
*
* JettyWebConfiguration.
*
- * Looks for Xmlconfiguration files in WEB-INF. Searches in order for the first of jettyX-web.xml, jetty-web.xml or web-jetty.xml
+ * Looks for Xmlconfiguration files in WEB-INF. Searches in order for the first of jetty6-web.xml, jetty-web.xml or web-jetty.xml
*
*
*
@@ -70,7 +71,7 @@
if(web_inf!=null&&web_inf.isDirectory())
{
// do jetty.xml file
- Resource jetty=web_inf.addPath("jetty7-web.xml");
+ Resource jetty=web_inf.addPath("jetty8-web.xml");
if(!jetty.exists())
jetty=web_inf.addPath(JETTY_WEB_XML);
if(!jetty.exists())
@@ -133,6 +134,13 @@
private void setupXmlConfiguration(XmlConfiguration jetty_config, Resource web_inf)
{
Map<String,String> props = jetty_config.getProperties();
+ if (props == null)
+ {
+ props = new HashMap<String, String>();
+ jetty_config.setProperties(props);
+ }
+
+ // TODO - should this be an id rather than a property?
props.put(PROPERTY_THIS_WEB_INF_URL, String.valueOf(web_inf.getURL()));
}
}
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java
index e306672..92ca93f 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java
@@ -24,6 +24,8 @@
import java.util.List;
import java.util.Map;
+import javax.servlet.ServletContext;
+
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
@@ -168,6 +170,8 @@
_webXmlRoot.parse();
_metaDataComplete=_webXmlRoot.getMetaDataComplete() == MetaDataComplete.True;
+
+
if (_webXmlRoot.isOrdered())
{
if (_ordering == null)
@@ -197,7 +201,7 @@
_metaDataComplete=true;
break;
case False:
- _metaDataComplete=true;
+ _metaDataComplete=false;
break;
case NotSet:
break;
@@ -267,12 +271,42 @@
*/
public void addDiscoveredAnnotations(List<DiscoveredAnnotation> annotations)
{
- _annotations.addAll(annotations);
+ if (annotations == null)
+ return;
+ for (DiscoveredAnnotation a:annotations)
+ {
+ Resource r = a.getResource();
+ if (r == null || !_webInfJars.contains(r))
+ _annotations.add(a);
+ else
+ addDiscoveredAnnotation(a.getResource(), a);
+
+ }
}
+
+
+ public void addDiscoveredAnnotation(Resource resource, DiscoveredAnnotation annotation)
+ {
+ List<DiscoveredAnnotation> list = _webFragmentAnnotations.get(resource);
+ if (list == null)
+ {
+ list = new ArrayList<DiscoveredAnnotation>();
+ _webFragmentAnnotations.put(resource, list);
+ }
+ list.add(annotation);
+ }
+
public void addDiscoveredAnnotations(Resource resource, List<DiscoveredAnnotation> annotations)
{
- _webFragmentAnnotations.put(resource, new ArrayList<DiscoveredAnnotation>(annotations));
+ List<DiscoveredAnnotation> list = _webFragmentAnnotations.get(resource);
+ if (list == null)
+ {
+ list = new ArrayList<DiscoveredAnnotation>();
+ _webFragmentAnnotations.put(resource, list);
+ }
+
+ list.addAll(annotations);
}
public void addDescriptorProcessor(DescriptorProcessor p)
@@ -317,7 +351,14 @@
int j = fullname.lastIndexOf("/", i);
orderedLibs.add(fullname.substring(j+1,i+4));
}
- context.setAttribute(ORDERED_LIBS, orderedLibs);
+ context.setAttribute(ServletContext.ORDERED_LIBS, orderedLibs);
+ }
+
+ // set the webxml version
+ if (_webXmlRoot != null)
+ {
+ context.getServletContext().setEffectiveMajorVersion(_webXmlRoot.getMajorVersion());
+ context.getServletContext().setEffectiveMinorVersion(_webXmlRoot.getMinorVersion());
}
for (DescriptorProcessor p:_descriptorProcessors)
@@ -497,6 +538,15 @@
OriginInfo x = new OriginInfo (name, Origin.Annotation);
_origins.put(name, x);
}
+
+ public void setOrigin(String name, Origin origin)
+ {
+ if (name == null)
+ return;
+
+ OriginInfo x = new OriginInfo (name, origin);
+ _origins.put(name, x);
+ }
public boolean isMetaDataComplete()
{
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaInfConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaInfConfiguration.java
index fb27992..81db172 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaInfConfiguration.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaInfConfiguration.java
@@ -84,6 +84,17 @@
scanner.scan(null, uris, true);
}
}
+ @Override
+ public void configure(WebAppContext context) throws Exception
+ {
+
+ }
+
+ @Override
+ public void deconfigure(WebAppContext context) throws Exception
+ {
+
+ }
@Override
public void postConfigure(WebAppContext context) throws Exception
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Ordering.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Ordering.java
index 7fb7fe9..7586d67 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Ordering.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Ordering.java
@@ -62,6 +62,7 @@
* Order the list of jars in WEB-INF/lib according to the ordering declarations in the descriptors
* @see org.eclipse.jetty.webapp.Ordering#order(java.util.List)
*/
+ @Override
public List<Resource> order(List<Resource> jars)
{
List<Resource> orderedList = new ArrayList<Resource>();
@@ -99,7 +100,7 @@
return orderedList;
}
-
+ @Override
public boolean isAbsolute()
{
return true;
@@ -119,7 +120,7 @@
_order.add(OTHER);
}
-
+ @Override
public boolean hasOther ()
{
return _hasOther;
@@ -146,7 +147,7 @@
* in the various web-fragment.xml files.
* @see org.eclipse.jetty.webapp.Ordering#order(java.util.List)
*/
-
+ @Override
public List<Resource> order(List<Resource> jars)
{
//for each jar, put it into the ordering according to the fragment ordering
@@ -216,13 +217,13 @@
return orderedList;
}
-
+ @Override
public boolean isAbsolute ()
{
return false;
}
-
+ @Override
public boolean hasOther ()
{
return !_beforeOthers.isEmpty() || !_afterOthers.isEmpty();
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Origin.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Origin.java
index 7043fe7..f54acf0 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Origin.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Origin.java
@@ -18,4 +18,4 @@
package org.eclipse.jetty.webapp;
-public enum Origin {NotSet, WebXml, WebDefaults, WebOverride, WebFragment, Annotation}
+public enum Origin {NotSet, WebXml, WebDefaults, WebOverride, WebFragment, Annotation, API}
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java
index a9b568b..e950233 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java
@@ -23,25 +23,38 @@
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.EnumSet;
import java.util.EventListener;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Set;
+import javax.servlet.DispatcherType;
+import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletException;
+import javax.servlet.ServletRegistration;
+import javax.servlet.SessionTrackingMode;
+import javax.servlet.descriptor.JspConfigDescriptor;
+import javax.servlet.descriptor.JspPropertyGroupDescriptor;
+import javax.servlet.descriptor.TaglibDescriptor;
import org.eclipse.jetty.security.ConstraintAware;
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.authentication.FormAuthenticator;
-import org.eclipse.jetty.server.DispatcherType;
import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.FilterMapping;
+import org.eclipse.jetty.servlet.Holder;
import org.eclipse.jetty.servlet.JspPropertyGroupServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHandler;
+import org.eclipse.jetty.servlet.ServletContextHandler.JspConfig;
+import org.eclipse.jetty.servlet.ServletContextHandler.JspPropertyGroup;
+import org.eclipse.jetty.servlet.ServletContextHandler.TagLib;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlet.ServletMapping;
import org.eclipse.jetty.util.LazyList;
@@ -196,7 +209,7 @@
*/
if (holder == null)
{
- holder = context.getServletHandler().newServletHolder();
+ holder = context.getServletHandler().newServletHolder(Holder.Source.DESCRIPTOR);
holder.setName(servlet_name);
context.getServletHandler().addServlet(holder);
}
@@ -462,6 +475,136 @@
}
}
}
+
+ String async=node.getString("async-supported",false,true);
+ if (async!=null)
+ {
+ boolean val = async.length()==0||Boolean.valueOf(async);
+ Origin o =context.getMetaData().getOrigin(servlet_name+".servlet.async-supported");
+ switch (o)
+ {
+ case NotSet:
+ {
+ //set it
+ holder.setAsyncSupported(val);
+ context.getMetaData().setOrigin(servlet_name+".servlet.async-supported", descriptor);
+ break;
+ }
+ case WebXml:
+ case WebDefaults:
+ case WebOverride:
+ {
+ //async-supported set by previous web xml descriptor, only allow override if we're parsing another web descriptor(web.xml/web-override.xml/web-default.xml)
+ if (!(descriptor instanceof FragmentDescriptor))
+ {
+ holder.setAsyncSupported(val);
+ context.getMetaData().setOrigin(servlet_name+".servlet.async-supported", descriptor);
+ }
+ break;
+ }
+ case WebFragment:
+ {
+ //async-supported set by another fragment, this fragment's value must match
+ if (holder.isAsyncSupported() != val)
+ throw new IllegalStateException("Conflicting async-supported="+async+" for servlet "+servlet_name+" in "+descriptor.getResource());
+ break;
+ }
+ }
+ }
+
+ String enabled = node.getString("enabled", false, true);
+ if (enabled!=null)
+ {
+ boolean is_enabled = enabled.length()==0||Boolean.valueOf(enabled);
+ Origin o = context.getMetaData().getOrigin(servlet_name+".servlet.enabled");
+ switch (o)
+ {
+ case NotSet:
+ {
+ //hasn't been set yet, so set it
+ holder.setEnabled(is_enabled);
+ context.getMetaData().setOrigin(servlet_name+".servlet.enabled", descriptor);
+ break;
+ }
+ case WebXml:
+ case WebDefaults:
+ case WebOverride:
+ {
+ //was set in a web xml descriptor, only allow override from another web xml descriptor
+ if (!(descriptor instanceof FragmentDescriptor))
+ {
+ holder.setEnabled(is_enabled);
+ context.getMetaData().setOrigin(servlet_name+".servlet.enabled", descriptor);
+ }
+ break;
+ }
+ case WebFragment:
+ {
+ //was set by another fragment, this fragment's value must match
+ if (holder.isEnabled() != is_enabled)
+ throw new IllegalStateException("Conflicting value of servlet enabled for servlet "+servlet_name+" in "+descriptor.getResource());
+ break;
+ }
+ }
+ }
+
+ /*
+ * If multipart config not set, then set it and record it was by the web.xml or fragment.
+ * If it was set by web.xml then if this is a fragment, ignore the settings.
+ * If it was set by a fragment, if this is a fragment and the values are different, error!
+ */
+ XmlParser.Node multipart = node.get("multipart-config");
+ if (multipart != null)
+ {
+ String location = multipart.getString("location", false, true);
+ String maxFile = multipart.getString("max-file-size", false, true);
+ String maxRequest = multipart.getString("max-request-size", false, true);
+ String threshold = multipart.getString("file-size-threshold",false,true);
+ MultipartConfigElement element = new MultipartConfigElement(location,
+ (maxFile==null||"".equals(maxFile)?-1L:Long.parseLong(maxFile)),
+ (maxRequest==null||"".equals(maxRequest)?-1L:Long.parseLong(maxRequest)),
+ (threshold==null||"".equals(threshold)?0:Integer.parseInt(threshold)));
+
+ Origin o = context.getMetaData().getOrigin(servlet_name+".servlet.multipart-config");
+ switch (o)
+ {
+ case NotSet:
+ {
+ //hasn't been set, so set it
+ holder.getRegistration().setMultipartConfig(element);
+ context.getMetaData().setOrigin(servlet_name+".servlet.multipart-config", descriptor);
+ break;
+ }
+ case WebXml:
+ case WebDefaults:
+ case WebOverride:
+ {
+ //was set in a web xml, only allow changes if we're parsing another web xml (web.xml/web-default.xml/web-override.xml)
+ if (!(descriptor instanceof FragmentDescriptor))
+ {
+ holder.getRegistration().setMultipartConfig(element);
+ context.getMetaData().setOrigin(servlet_name+".servlet.multipart-config", descriptor);
+ }
+ break;
+ }
+ case WebFragment:
+ {
+ //another fragment set the value, this fragment's values must match exactly or it is an error
+ MultipartConfigElement cfg = ((ServletHolder.Registration)holder.getRegistration()).getMultipartConfig();
+
+ if (cfg.getMaxFileSize() != element.getMaxFileSize())
+ throw new IllegalStateException("Conflicting multipart-config max-file-size for servlet "+servlet_name+" in "+descriptor.getResource());
+ if (cfg.getMaxRequestSize() != element.getMaxRequestSize())
+ throw new IllegalStateException("Conflicting multipart-config max-request-size for servlet "+servlet_name+" in "+descriptor.getResource());
+ if (cfg.getFileSizeThreshold() != element.getFileSizeThreshold())
+ throw new IllegalStateException("Conflicting multipart-config file-size-threshold for servlet "+servlet_name+" in "+descriptor.getResource());
+ if ((cfg.getLocation() != null && (element.getLocation() == null || element.getLocation().length()==0))
+ || (cfg.getLocation() == null && (element.getLocation()!=null || element.getLocation().length() > 0)))
+ throw new IllegalStateException("Conflicting multipart-config location for servlet "+servlet_name+" in "+descriptor.getResource());
+ break;
+ }
+ }
+ }
}
@@ -488,7 +631,8 @@
{
//no servlet mappings
context.getMetaData().setOrigin(servlet_name+".servlet.mappings", descriptor);
- addServletMapping(servlet_name, node, context);
+ ServletMapping mapping = addServletMapping(servlet_name, node, context, descriptor);
+ mapping.setDefault(context.getMetaData().getOrigin(servlet_name+".servlet.mappings") == Origin.WebDefaults);
break;
}
case WebXml:
@@ -499,14 +643,14 @@
//otherwise just ignore it
if (!(descriptor instanceof FragmentDescriptor))
{
- addServletMapping(servlet_name, node, context);
+ addServletMapping(servlet_name, node, context, descriptor);
}
break;
}
case WebFragment:
{
//mappings previously set by another web-fragment, so merge in this web-fragment's mappings
- addServletMapping(servlet_name, node, context);
+ addServletMapping(servlet_name, node, context, descriptor);
break;
}
}
@@ -526,6 +670,309 @@
int timeout = Integer.parseInt(tNode.toString(false, true));
context.getSessionHandler().getSessionManager().setMaxInactiveInterval(timeout * 60);
}
+
+ //Servlet Spec 3.0
+ // <tracking-mode>
+ // this is additive across web-fragments
+ Iterator iter = node.iterator("tracking-mode");
+ if (iter.hasNext())
+ {
+ Set<SessionTrackingMode> modes = null;
+ Origin o = context.getMetaData().getOrigin("session.tracking-mode");
+ switch (o)
+ {
+ case NotSet://not previously set, starting fresh
+ case WebDefaults://previously set in web defaults, allow this descriptor to start fresh
+ {
+
+ modes = new HashSet<SessionTrackingMode>();
+ context.getMetaData().setOrigin("session.tracking-mode", descriptor);
+ break;
+ }
+ case WebXml:
+ case WebFragment:
+ case WebOverride:
+ {
+ //if setting from an override descriptor, start afresh, otherwise add-in tracking-modes
+ if (descriptor instanceof OverrideDescriptor)
+ modes = new HashSet<SessionTrackingMode>();
+ else
+ modes = new HashSet<SessionTrackingMode>(context.getSessionHandler().getSessionManager().getEffectiveSessionTrackingModes());
+ context.getMetaData().setOrigin("session.tracking-mode", descriptor);
+ break;
+ }
+ }
+
+ while (iter.hasNext())
+ {
+ XmlParser.Node mNode = (XmlParser.Node) iter.next();
+ String trackMode = mNode.toString(false, true);
+ modes.add(SessionTrackingMode.valueOf(trackMode));
+ }
+ context.getSessionHandler().getSessionManager().setSessionTrackingModes(modes);
+ }
+
+
+ //Servlet Spec 3.0
+ //<cookie-config>
+ XmlParser.Node cookieConfig = node.get("cookie-config");
+ if (cookieConfig != null)
+ {
+ // <name>
+ String name = cookieConfig.getString("name", false, true);
+ if (name != null)
+ {
+ Origin o = context.getMetaData().getOrigin("cookie-config.name");
+ switch (o)
+ {
+ case NotSet:
+ {
+ //no <cookie-config><name> set yet, accept it
+ context.getSessionHandler().getSessionManager().getSessionCookieConfig().setName(name);
+ context.getMetaData().setOrigin("cookie-config.name", descriptor);
+ break;
+ }
+ case WebXml:
+ case WebDefaults:
+ case WebOverride:
+ {
+ //<cookie-config><name> set in a web xml, only allow web-default/web-override to change
+ if (!(descriptor instanceof FragmentDescriptor))
+ {
+ context.getSessionHandler().getSessionManager().getSessionCookieConfig().setName(name);
+ context.getMetaData().setOrigin("cookie-config.name", descriptor);
+ }
+ break;
+ }
+ case WebFragment:
+ {
+ //a web-fragment set the value, all web-fragments must have the same value
+ if (!context.getSessionHandler().getSessionManager().getSessionCookieConfig().getName().equals(name))
+ throw new IllegalStateException("Conflicting cookie-config name "+name+" in "+descriptor.getResource());
+ break;
+ }
+ }
+ }
+
+ // <domain>
+ String domain = cookieConfig.getString("domain", false, true);
+ if (domain != null)
+ {
+ Origin o = context.getMetaData().getOrigin("cookie-config.domain");
+ switch (o)
+ {
+ case NotSet:
+ {
+ //no <cookie-config><domain> set yet, accept it
+ context.getSessionHandler().getSessionManager().getSessionCookieConfig().setDomain(domain);
+ context.getMetaData().setOrigin("cookie-config.domain", descriptor);
+ break;
+ }
+ case WebXml:
+ case WebDefaults:
+ case WebOverride:
+ {
+ //<cookie-config><domain> set in a web xml, only allow web-default/web-override to change
+ if (!(descriptor instanceof FragmentDescriptor))
+ {
+ context.getSessionHandler().getSessionManager().getSessionCookieConfig().setDomain(domain);
+ context.getMetaData().setOrigin("cookie-config.domain", descriptor);
+ }
+ break;
+ }
+ case WebFragment:
+ {
+ //a web-fragment set the value, all web-fragments must have the same value
+ if (!context.getSessionHandler().getSessionManager().getSessionCookieConfig().getDomain().equals(domain))
+ throw new IllegalStateException("Conflicting cookie-config domain "+domain+" in "+descriptor.getResource());
+ break;
+ }
+ }
+ }
+
+ // <path>
+ String path = cookieConfig.getString("path", false, true);
+ if (path != null)
+ {
+ Origin o = context.getMetaData().getOrigin("cookie-config.path");
+ switch (o)
+ {
+ case NotSet:
+ {
+ //no <cookie-config><domain> set yet, accept it
+ context.getSessionHandler().getSessionManager().getSessionCookieConfig().setPath(path);
+ context.getMetaData().setOrigin("cookie-config.path", descriptor);
+ break;
+ }
+ case WebXml:
+ case WebDefaults:
+ case WebOverride:
+ {
+ //<cookie-config><domain> set in a web xml, only allow web-default/web-override to change
+ if (!(descriptor instanceof FragmentDescriptor))
+ {
+ context.getSessionHandler().getSessionManager().getSessionCookieConfig().setPath(path);
+ context.getMetaData().setOrigin("cookie-config.path", descriptor);
+ }
+ break;
+ }
+ case WebFragment:
+ {
+ //a web-fragment set the value, all web-fragments must have the same value
+ if (!context.getSessionHandler().getSessionManager().getSessionCookieConfig().getPath().equals(path))
+ throw new IllegalStateException("Conflicting cookie-config path "+path+" in "+descriptor.getResource());
+ break;
+ }
+ }
+ }
+
+ // <comment>
+ String comment = cookieConfig.getString("comment", false, true);
+ if (comment != null)
+ {
+ Origin o = context.getMetaData().getOrigin("cookie-config.comment");
+ switch (o)
+ {
+ case NotSet:
+ {
+ //no <cookie-config><comment> set yet, accept it
+ context.getSessionHandler().getSessionManager().getSessionCookieConfig().setComment(comment);
+ context.getMetaData().setOrigin("cookie-config.comment", descriptor);
+ break;
+ }
+ case WebXml:
+ case WebDefaults:
+ case WebOverride:
+ {
+ //<cookie-config><comment> set in a web xml, only allow web-default/web-override to change
+ if (!(descriptor instanceof FragmentDescriptor))
+ {
+ context.getSessionHandler().getSessionManager().getSessionCookieConfig().setComment(comment);
+ context.getMetaData().setOrigin("cookie-config.comment", descriptor);
+ }
+ break;
+ }
+ case WebFragment:
+ {
+ //a web-fragment set the value, all web-fragments must have the same value
+ if (!context.getSessionHandler().getSessionManager().getSessionCookieConfig().getComment().equals(comment))
+ throw new IllegalStateException("Conflicting cookie-config comment "+comment+" in "+descriptor.getResource());
+ break;
+ }
+ }
+ }
+
+ // <http-only>true/false
+ tNode = cookieConfig.get("http-only");
+ if (tNode != null)
+ {
+ boolean httpOnly = Boolean.parseBoolean(tNode.toString(false,true));
+ Origin o = context.getMetaData().getOrigin("cookie-config.http-only");
+ switch (o)
+ {
+ case NotSet:
+ {
+ //no <cookie-config><http-only> set yet, accept it
+ context.getSessionHandler().getSessionManager().getSessionCookieConfig().setHttpOnly(httpOnly);
+ context.getMetaData().setOrigin("cookie-config.http-only", descriptor);
+ break;
+ }
+ case WebXml:
+ case WebDefaults:
+ case WebOverride:
+ {
+ //<cookie-config><http-only> set in a web xml, only allow web-default/web-override to change
+ if (!(descriptor instanceof FragmentDescriptor))
+ {
+ context.getSessionHandler().getSessionManager().getSessionCookieConfig().setHttpOnly(httpOnly);
+ context.getMetaData().setOrigin("cookie-config.http-only", descriptor);
+ }
+ break;
+ }
+ case WebFragment:
+ {
+ //a web-fragment set the value, all web-fragments must have the same value
+ if (context.getSessionHandler().getSessionManager().getSessionCookieConfig().isHttpOnly() != httpOnly)
+ throw new IllegalStateException("Conflicting cookie-config http-only "+httpOnly+" in "+descriptor.getResource());
+ break;
+ }
+ }
+ }
+
+ // <secure>true/false
+ tNode = cookieConfig.get("secure");
+ if (tNode != null)
+ {
+ boolean secure = Boolean.parseBoolean(tNode.toString(false,true));
+ Origin o = context.getMetaData().getOrigin("cookie-config.secure");
+ switch (o)
+ {
+ case NotSet:
+ {
+ //no <cookie-config><secure> set yet, accept it
+ context.getSessionHandler().getSessionManager().getSessionCookieConfig().setSecure(secure);
+ context.getMetaData().setOrigin("cookie-config.secure", descriptor);
+ break;
+ }
+ case WebXml:
+ case WebDefaults:
+ case WebOverride:
+ {
+ //<cookie-config><secure> set in a web xml, only allow web-default/web-override to change
+ if (!(descriptor instanceof FragmentDescriptor))
+ {
+ context.getSessionHandler().getSessionManager().getSessionCookieConfig().setSecure(secure);
+ context.getMetaData().setOrigin("cookie-config.secure", descriptor);
+ }
+ break;
+ }
+ case WebFragment:
+ {
+ //a web-fragment set the value, all web-fragments must have the same value
+ if (context.getSessionHandler().getSessionManager().getSessionCookieConfig().isSecure() != secure)
+ throw new IllegalStateException("Conflicting cookie-config secure "+secure+" in "+descriptor.getResource());
+ break;
+ }
+ }
+ }
+
+ // <max-age>
+ tNode = cookieConfig.get("max-age");
+ if (tNode != null)
+ {
+ int maxAge = Integer.parseInt(tNode.toString(false,true));
+ Origin o = context.getMetaData().getOrigin("cookie-config.max-age");
+ switch (o)
+ {
+ case NotSet:
+ {
+ //no <cookie-config><max-age> set yet, accept it
+ context.getSessionHandler().getSessionManager().getSessionCookieConfig().setMaxAge(maxAge);
+ context.getMetaData().setOrigin("cookie-config.max-age", descriptor);
+ break;
+ }
+ case WebXml:
+ case WebDefaults:
+ case WebOverride:
+ {
+ //<cookie-config><max-age> set in a web xml, only allow web-default/web-override to change
+ if (!(descriptor instanceof FragmentDescriptor))
+ {
+ context.getSessionHandler().getSessionManager().getSessionCookieConfig().setMaxAge(maxAge);
+ context.getMetaData().setOrigin("cookie-config.max-age", descriptor);
+ }
+ break;
+ }
+ case WebFragment:
+ {
+ //a web-fragment set the value, all web-fragments must have the same value
+ if (context.getSessionHandler().getSessionManager().getSessionCookieConfig().getMaxAge() != maxAge)
+ throw new IllegalStateException("Conflicting cookie-config max-age "+maxAge+" in "+descriptor.getResource());
+ break;
+ }
+ }
+ }
+ }
}
@@ -666,7 +1113,7 @@
{
//a value was set by a web-fragment, all fragments must have the same value
if (!encoding.equals(context.getLocaleEncoding(locale)))
- throw new IllegalStateException("Conflicting locale-encoding mapping for locale "+locale+" in "+descriptor.getResource());
+ throw new IllegalStateException("Conflicting loacle-encoding mapping for locale "+locale+" in "+descriptor.getResource());
break;
}
}
@@ -763,7 +1210,7 @@
* @param node
* @param context
*/
- protected void addServletMapping (String servletName, XmlParser.Node node, WebAppContext context)
+ protected ServletMapping addServletMapping (String servletName, XmlParser.Node node, WebAppContext context, Descriptor descriptor)
{
ServletMapping mapping = new ServletMapping();
mapping.setServletName(servletName);
@@ -775,9 +1222,11 @@
String p = iter.next().toString(false, true);
p = normalizePattern(p);
paths.add(p);
+ context.getMetaData().setOrigin(servletName+".servlet.mapping."+p, descriptor);
}
mapping.setPathSpecs((String[]) paths.toArray(new String[paths.size()]));
context.getServletHandler().addServletMapping(mapping);
+ return mapping;
}
/**
@@ -785,7 +1234,7 @@
* @param node
* @param context
*/
- protected void addFilterMapping (String filterName, XmlParser.Node node, WebAppContext context)
+ protected void addFilterMapping (String filterName, XmlParser.Node node, WebAppContext context, Descriptor descriptor)
{
FilterMapping mapping = new FilterMapping();
mapping.setFilterName(filterName);
@@ -797,6 +1246,7 @@
String p = iter.next().toString(false, true);
p = normalizePattern(p);
paths.add(p);
+ context.getMetaData().setOrigin(filterName+".filter.mapping."+p, descriptor);
}
mapping.setPathSpecs((String[]) paths.toArray(new String[paths.size()]));
@@ -837,6 +1287,18 @@
String location = node.getString("taglib-location", false, true);
context.setResourceAlias(uri, location);
+
+ JspConfig config = (JspConfig)context.getServletContext().getJspConfigDescriptor();
+ if (config == null)
+ {
+ config = new JspConfig();
+ context.getServletContext().setJspConfigDescriptor(config);
+ }
+
+ TagLib tl = new TagLib();
+ tl.setTaglibLocation(location);
+ tl.setTaglibURI(uri);
+ config.addTaglibDescriptor(tl);
}
/**
@@ -845,7 +1307,16 @@
* @param node
*/
protected void visitJspConfig(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
- {
+ {
+ //Additive across web.xml and web-fragment.xml
+ JspConfig config = (JspConfig)context.getServletContext().getJspConfigDescriptor();
+ if (config == null)
+ {
+ config = new JspConfig();
+ context.getServletContext().setJspConfigDescriptor(config);
+ }
+
+
for (int i = 0; i < node.size(); i++)
{
Object o = node.get(i);
@@ -854,19 +1325,51 @@
}
// Map URLs from jsp property groups to JSP servlet.
- // this is more JSP stupidness creaping into the servlet spec
+ // this is more JSP stupidness creeping into the servlet spec
Iterator<XmlParser.Node> iter = node.iterator("jsp-property-group");
List<String> paths = new ArrayList<String>();
while (iter.hasNext())
{
+ JspPropertyGroup jpg = new JspPropertyGroup();
+ config.addJspPropertyGroup(jpg);
XmlParser.Node group = iter.next();
+
+ //url-patterns
Iterator<XmlParser.Node> iter2 = group.iterator("url-pattern");
while (iter2.hasNext())
{
String url = iter2.next().toString(false, true);
url = normalizePattern(url);
paths.add( url);
+ jpg.addUrlPattern(url);
}
+
+ jpg.setElIgnored(group.getString("el-ignored", false, true));
+ jpg.setPageEncoding(group.getString("page-encoding", false, true));
+ jpg.setScriptingInvalid(group.getString("scripting-invalid", false, true));
+ jpg.setIsXml(group.getString("is-xml", false, true));
+ jpg.setDeferredSyntaxAllowedAsLiteral(group.getString("deferred-syntax-allowed-as-literal", false, true));
+ jpg.setTrimDirectiveWhitespaces(group.getString("trim-directive-whitespaces", false, true));
+ jpg.setDefaultContentType(group.getString("default-content-type", false, true));
+ jpg.setBuffer(group.getString("buffer", false, true));
+ jpg.setErrorOnUndeclaredNamespace(group.getString("error-on-undeclared-namespace", false, true));
+
+ //preludes
+ Iterator<XmlParser.Node> preludes = group.iterator("include-prelude");
+ while (preludes.hasNext())
+ {
+ String prelude = preludes.next().toString(false, true);
+ jpg.addIncludePrelude(prelude);
+ }
+ //codas
+ Iterator<XmlParser.Node> codas = group.iterator("include-coda");
+ while (codas.hasNext())
+ {
+ String coda = codas.next().toString(false, true);
+ jpg.addIncludeCoda(coda);
+ }
+
+ if (LOG.isDebugEnabled()) LOG.debug(config.toString());
}
if (paths.size() > 0)
@@ -897,6 +1400,8 @@
//ServletSpec 3.0, p74 security-constraints, as minOccurs > 1, are additive
//across fragments
+
+ //TODO: need to remember origin of the constraints
try
{
XmlParser.Node auths = node.get("auth-constraint");
@@ -945,29 +1450,50 @@
{
String url = iter2.next().toString(false, true);
url = normalizePattern(url);
-
+ //remember origin so we can process ServletRegistration.Dynamic.setServletSecurityElement() correctly
+ context.getMetaData().setOrigin("constraint.url."+url, descriptor);
+
Iterator<XmlParser.Node> iter3 = collection.iterator("http-method");
+ Iterator<XmlParser.Node> iter4 = collection.iterator("http-method-omission");
+
if (iter3.hasNext())
{
+ if (iter4.hasNext())
+ throw new IllegalStateException ("web-resource-collection cannot contain both http-method and http-method-omission");
+
+ //configure all the http-method elements for each url
while (iter3.hasNext())
{
String method = ((XmlParser.Node) iter3.next()).toString(false, true);
ConstraintMapping mapping = new ConstraintMapping();
mapping.setMethod(method);
mapping.setPathSpec(url);
+ mapping.setConstraint(sc);
+ ((ConstraintAware)context.getSecurityHandler()).addConstraintMapping(mapping);
+ }
+ }
+ else if (iter4.hasNext())
+ {
+ //configure all the http-method-omission elements for each url
+ while (iter4.hasNext())
+ {
+ String method = ((XmlParser.Node)iter4.next()).toString(false, true);
+ ConstraintMapping mapping = new ConstraintMapping();
+ mapping.setMethodOmissions(new String[]{method});
+ mapping.setPathSpec(url);
mapping.setConstraint(sc);
-
((ConstraintAware)context.getSecurityHandler()).addConstraintMapping(mapping);
}
}
else
{
+ //No http-methods or http-method-omissions specified, the constraint applies to all
ConstraintMapping mapping = new ConstraintMapping();
mapping.setPathSpec(url);
mapping.setConstraint(sc);
((ConstraintAware)context.getSecurityHandler()).addConstraintMapping(mapping);
}
- }
+ }
}
}
catch (CloneNotSupportedException e)
@@ -1167,7 +1693,7 @@
FilterHolder holder = context.getServletHandler().getFilter(name);
if (holder == null)
{
- holder = context.getServletHandler().newFilterHolder();
+ holder = context.getServletHandler().newFilterHolder(Holder.Source.DESCRIPTOR);
holder.setName(name);
context.getServletHandler().addFilter(holder);
}
@@ -1313,7 +1839,7 @@
{
//no filtermappings for this filter yet defined
context.getMetaData().setOrigin(filter_name+".filter.mappings", descriptor);
- addFilterMapping(filter_name, node, context);
+ addFilterMapping(filter_name, node, context, descriptor);
break;
}
case WebDefaults:
@@ -1323,14 +1849,14 @@
//filter mappings defined in a web xml file. If we're processing a fragment, we ignore filter mappings.
if (!(descriptor instanceof FragmentDescriptor))
{
- addFilterMapping(filter_name, node, context);
+ addFilterMapping(filter_name, node, context, descriptor);
}
break;
}
case WebFragment:
{
//filter mappings first defined in a web-fragment, allow other fragments to add
- addFilterMapping(filter_name, node, context);
+ addFilterMapping(filter_name, node, context, descriptor);
break;
}
}
@@ -1408,7 +1934,7 @@
{
try
{
- return ((ServletContextHandler.Context)context.getServletContext()).createListener(clazz);
+ return context.getServletContext().createListener(clazz);
}
catch (ServletException se)
{
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppClassLoader.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppClassLoader.java
index 035c71d..8c4880c 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppClassLoader.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppClassLoader.java
@@ -446,5 +446,4 @@
{
return "WebAppClassLoader=" + _name+"@"+Long.toHexString(hashCode());
}
-
}
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java
index fcbe611..b078424 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java
@@ -25,18 +25,29 @@
import java.security.PermissionCollection;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.EventListener;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import javax.servlet.HttpMethodConstraintElement;
import javax.servlet.ServletContext;
+import javax.servlet.ServletRegistration.Dynamic;
+import javax.servlet.ServletSecurityElement;
+import javax.servlet.annotation.ServletSecurity.EmptyRoleSemantic;
+import javax.servlet.annotation.ServletSecurity.TransportGuarantee;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionListener;
+import org.eclipse.jetty.security.ConstraintAware;
+import org.eclipse.jetty.security.ConstraintMapping;
+import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.security.SecurityHandler;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HandlerContainer;
@@ -56,6 +67,7 @@
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.util.security.Constraint;
/* ------------------------------------------------------------ */
/** Web Application Context Handler.
@@ -90,8 +102,8 @@
"org.eclipse.jetty.webapp.WebXmlConfiguration",
"org.eclipse.jetty.webapp.MetaInfConfiguration",
"org.eclipse.jetty.webapp.FragmentConfiguration",
- "org.eclipse.jetty.webapp.JettyWebXmlConfiguration",
- "org.eclipse.jetty.webapp.TagLibConfiguration"
+ "org.eclipse.jetty.webapp.JettyWebXmlConfiguration"//,
+ //"org.eclipse.jetty.webapp.TagLibConfiguration"
} ;
// System classes are classes that cannot be replaced by
@@ -979,16 +991,6 @@
}
}
- /* ------------------------------------------------------------ */
- /** Add EventListener
- * Conveniance method that calls {@link #setEventListeners(EventListener[])}
- * @param listener
- */
- @Override
- public void addEventListener(EventListener listener)
- {
- setEventListeners(LazyList.addToArray(getEventListeners(), listener, EventListener.class));
- }
/* ------------------------------------------------------------ */
@@ -1239,6 +1241,79 @@
super.startContext();
}
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public Set<String> setServletSecurity(Dynamic registration, ServletSecurityElement servletSecurityElement)
+ {
+
+ Set<String> unchangedURLMappings = new HashSet<String>();
+ //From javadoc for ServletSecurityElement:
+ /*
+ If a URL pattern of this ServletRegistration is an exact target of a security-constraint that
+ was established via the portable deployment descriptor, then this method does not change the
+ security-constraint for that pattern, and the pattern will be included in the return value.
+
+ If a URL pattern of this ServletRegistration is an exact target of a security constraint
+ that was established via the ServletSecurity annotation or a previous call to this method,
+ then this method replaces the security constraint for that pattern.
+
+ If a URL pattern of this ServletRegistration is neither the exact target of a security constraint
+ that was established via the ServletSecurity annotation or a previous call to this method,
+ nor the exact target of a security-constraint in the portable deployment descriptor, then
+ this method establishes the security constraint for that pattern from the argument ServletSecurityElement.
+ */
+
+ Collection<String> pathMappings = registration.getMappings();
+ if (pathMappings != null)
+ {
+ Constraint constraint = ConstraintSecurityHandler.createConstraint(registration.getName(), servletSecurityElement);
+
+ for (String pathSpec:pathMappings)
+ {
+ Origin origin = getMetaData().getOrigin("constraint.url."+pathSpec);
+
+ switch (origin)
+ {
+ case NotSet:
+ {
+ //No mapping for this url already established
+ List<ConstraintMapping> mappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath(registration.getName(), pathSpec, servletSecurityElement);
+ for (ConstraintMapping m:mappings)
+ ((ConstraintAware)getSecurityHandler()).addConstraintMapping(m);
+ getMetaData().setOrigin("constraint.url."+pathSpec, Origin.API);
+ break;
+ }
+ case WebXml:
+ case WebDefaults:
+ case WebOverride:
+ case WebFragment:
+ {
+ //a mapping for this url was created in a descriptor, which overrides everything
+ unchangedURLMappings.add(pathSpec);
+ break;
+ }
+ case Annotation:
+ case API:
+ {
+ //mapping established via an annotation or by previous call to this method,
+ //replace the security constraint for this pattern
+ List<ConstraintMapping> constraintMappings = ConstraintSecurityHandler.removeConstraintMappingsForPath(pathSpec, ((ConstraintAware)getSecurityHandler()).getConstraintMappings());
+
+ List<ConstraintMapping> freshMappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath(registration.getName(), pathSpec, servletSecurityElement);
+ constraintMappings.addAll(freshMappings);
+
+ ((ConstraintSecurityHandler)getSecurityHandler()).setConstraintMappings(constraintMappings);
+ break;
+ }
+ }
+ }
+ }
+
+ return unchangedURLMappings;
+ }
+
+
/* ------------------------------------------------------------ */
public class Context extends ServletContextHandler.Context
@@ -1289,6 +1364,8 @@
}
}
+
+
}
/* ------------------------------------------------------------ */
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebXmlConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebXmlConfiguration.java
index 5b8ddd3..6b9ab15 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebXmlConfiguration.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebXmlConfiguration.java
@@ -59,6 +59,8 @@
if (webxml != null)
{
context.getMetaData().setWebXml(webxml);
+ context.getServletContext().setEffectiveMajorVersion(context.getMetaData().getWebXml().getMajorVersion());
+ context.getServletContext().setEffectiveMinorVersion(context.getMetaData().getWebXml().getMinorVersion());
}
//parse but don't process override-web.xml
@@ -119,16 +121,8 @@
@Override
public void deconfigure (WebAppContext context) throws Exception
{
- // TODO preserve any configuration that pre-existed.
-
ServletHandler _servletHandler = context.getServletHandler();
- _servletHandler.setFilters(null);
- _servletHandler.setFilterMappings(null);
- _servletHandler.setServlets(null);
- _servletHandler.setServletMappings(null);
-
- context.setEventListeners(null);
context.setWelcomeFiles(null);
if (context.getErrorHandler() instanceof ErrorPageErrorHandler)
diff --git a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/OrderingTest.java b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/OrderingTest.java
index bd82287..e926a53 100644
--- a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/OrderingTest.java
+++ b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/OrderingTest.java
@@ -853,6 +853,44 @@
}
@Test
+ public void testRelativeOrderingWithPlainJars2 ()
+ throws Exception
+ {
+ //web.xml has no ordering, jar A has fragment after others, jar B is plain, jar C is plain
+ List<Resource> resources = new ArrayList<Resource>();
+ WebAppContext wac = new WebAppContext();
+ MetaData metaData = new MetaData();
+ metaData._ordering = new RelativeOrdering(metaData);
+
+ //A has after others
+ TestResource jar1 = new TestResource("A");
+ resources.add(jar1);
+ TestResource r1 = new TestResource("A/web-fragment.xml");
+ FragmentDescriptor f1 = new FragmentDescriptor(r1);
+ f1._name = "A";
+ metaData._webFragmentNameMap.put(f1._name, f1);
+ metaData._webFragmentResourceMap.put(jar1, f1);
+ f1._otherType = FragmentDescriptor.OtherType.After;
+
+ //No fragment jar B
+ TestResource r4 = new TestResource("plainB");
+ resources.add(r4);
+
+ //No fragment jar C
+ TestResource r5 = new TestResource("plainC");
+ resources.add(r5);
+
+ List<Resource> orderedList = metaData._ordering.order(resources);
+ String[] outcomes = {"plainBplainCA"};
+ String result = "";
+ for (Resource r:orderedList)
+ result+=(((TestResource)r)._name);
+
+ if (!checkResult(result, outcomes))
+ fail ("No outcome matched "+result);
+ }
+
+ @Test
public void testAbsoluteOrderingWithPlainJars()
throws Exception
{
diff --git a/jetty-websocket/pom.xml b/jetty-websocket/pom.xml
index ff70d3b..8762297 100644
--- a/jetty-websocket/pom.xml
+++ b/jetty-websocket/pom.xml
@@ -3,7 +3,7 @@
<parent>
<artifactId>jetty-project</artifactId>
<groupId>org.eclipse.jetty</groupId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -65,6 +65,11 @@
<goals>
<goal>manifest</goal>
</goals>
+ <configuration>
+ <instructions>
+ <Import-Package>javax.servlet.*;version="2.6.0",*</Import-Package>
+ </instructions>
+ </configuration>
</execution>
</executions>
</plugin>
@@ -100,5 +105,4 @@
</plugin>
</plugins>
</build>
-
</project>
diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketClient.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketClient.java
index 497eaea..dea8fe0 100644
--- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketClient.java
+++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketClient.java
@@ -36,6 +36,7 @@
import java.util.concurrent.TimeoutException;
import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@@ -336,6 +337,8 @@
{
if (!_factory.isStarted())
throw new IllegalStateException("Factory !started");
+
+ __log.debug("Connecting to: {}", uri);
InetSocketAddress address = toSocketAddress(uri);
diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketClientFactory.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketClientFactory.java
index 35ee2ca..4dd8f6f 100644
--- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketClientFactory.java
+++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketClientFactory.java
@@ -423,9 +423,31 @@
StringBuilder request = new StringBuilder(512);
request.append("GET ").append(path).append(" HTTP/1.1\r\n")
- .append("Host: ").append(_future.getURI().getHost()).append(":")
- .append(_future.getURI().getPort()).append("\r\n")
- .append("Upgrade: websocket\r\n")
+ .append("Host: ").append(_future.getURI().getHost()).append(":");
+
+ int port = _future.getURI().getPort();
+ if (port <= 0)
+ {
+ // fix it
+ String scheme = _future.getURI().getScheme();
+
+ if ("ws".equalsIgnoreCase(scheme))
+ {
+ port = 80;
+ }
+ else if ("wss".equalsIgnoreCase(scheme))
+ {
+ port = 443;
+ }
+ else
+ {
+ throw new RuntimeException("No valid port provided for scheme [" + scheme + "]");
+ }
+ }
+
+ request.append(port).append("\r\n");
+
+ request.append("Upgrade: websocket\r\n")
.append("Connection: Upgrade\r\n")
.append("Sec-WebSocket-Key: ")
.append(_key).append("\r\n");
diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketParserD06Test.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketParserD06Test.java
index 84290e5..02fc306 100644
--- a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketParserD06Test.java
+++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketParserD06Test.java
@@ -29,6 +29,7 @@
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.io.ByteArrayEndPoint;
import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.Utf8StringBuilder;
import org.junit.Before;
import org.junit.Test;
@@ -130,7 +131,7 @@
_in.put((byte)0x84);
_in.put((byte)11);
_in.put("Hello World".getBytes(StringUtil.__UTF8));
- // System.err.println("tosend="+TypeUtil.toHexString(_in.asArray()));
+ System.err.println("tosend="+TypeUtil.toHexString(_in.asArray()));
int filled =_parser.parseNext();
diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/examples/EchoClientSocketExample.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/examples/EchoClientSocketExample.java
new file mode 100644
index 0000000..2bb4af0
--- /dev/null
+++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/examples/EchoClientSocketExample.java
@@ -0,0 +1,145 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.websocket.examples;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.eclipse.jetty.websocket.WebSocket;
+import org.eclipse.jetty.websocket.WebSocketClient;
+import org.eclipse.jetty.websocket.WebSocketClientFactory;
+
+public class EchoClientSocketExample
+{
+ /**
+ * A simple echo socket, on connect it sends a text message, then when it receives the message it tracks it and then closes the connection.
+ * <p>
+ * There are some fundamental latches provided that a WebSocketClient can use to wait till a socket is connected or even disconnected.
+ */
+ public static class EchoClientSocket implements WebSocket, WebSocket.OnTextMessage
+ {
+ private static final Logger LOG = Log.getLogger(EchoClientSocket.class);
+
+ public CountDownLatch connectLatch = new CountDownLatch(1);
+ public CountDownLatch disconnectLatch = new CountDownLatch(1);
+
+ public List<String> textMessages = new ArrayList<String>();
+ public List<Throwable> errors = new ArrayList<Throwable>();
+ private Connection connection;
+
+ @Override
+ public void onClose(int closeCode, String message)
+ {
+ LOG.info("Closed {} : {}",closeCode,message);
+ disconnectLatch.countDown();
+ }
+
+ @Override
+ public void onMessage(String message)
+ {
+ LOG.info("on Text : {}",message);
+ textMessages.add(message);
+ connection.close();
+ }
+
+ @Override
+ public void onOpen(Connection connection)
+ {
+ this.connection = connection;
+ LOG.info("Connection opened : {}",connection);
+ connectLatch.countDown();
+ try
+ {
+ connection.sendMessage("Hello WebSocket World");
+ }
+ catch (IOException e)
+ {
+ LOG.warn(e);
+ }
+ }
+ }
+
+ private static final Logger LOG = Log.getLogger(EchoClientSocketExample.class);
+
+ public static void main(String[] args)
+ {
+ QueuedThreadPool threadPool = new QueuedThreadPool();
+ // Since this is a standalone client, lets make the threadpool
+ // be able to shut itself down at System.exit()
+ threadPool.setDaemon(true);
+
+ WebSocketClientFactory factory = new WebSocketClientFactory(threadPool);
+
+ factory.getSslContextFactory().setTrustAll(true);
+
+ // disable vulnerable SSL protocols
+ factory.getSslContextFactory().addExcludeProtocols("SSL","SSLv2","SSLv2Hello","SSLv3");
+ // disable vulnerable SSL cipher suites
+ factory.getSslContextFactory().addExcludeCipherSuites(
+ "SSL_RSA_WITH_DES_CBC_SHA",
+ "SSL_DHE_RSA_WITH_DES_CBC_SHA",
+ "SSL_DHE_DSS_WITH_DES_CBC_SHA",
+ "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
+ "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
+ "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA");
+
+ try
+ {
+ factory.start();
+
+ WebSocketClient client = factory.newWebSocketClient();
+ client.setMaxTextMessageSize(50000);
+
+ // Change to "wss://" for secure (SSL) version.
+ URI wsUri = new URI("ws://echo.websocket.org/");
+
+ // Our socket endpoint (the client side)
+ EchoClientSocket socket = new EchoClientSocket();
+
+ // Open the connection to the destination, wait 10 seconds for it to succeed (toss exception otherwise).
+ client.open(wsUri,socket).get(10,TimeUnit.SECONDS);
+
+ // wait till the socket is disconnected
+ socket.disconnectLatch.await(2L,TimeUnit.SECONDS);
+ }
+ catch (Throwable t)
+ {
+ LOG.warn(t);
+ }
+ finally
+ {
+ try
+ {
+ factory.stop();
+ }
+ catch (Exception e)
+ {
+ LOG.ignore(e);
+ }
+ }
+ }
+}
diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/examples/EchoServerSocketExample.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/examples/EchoServerSocketExample.java
new file mode 100644
index 0000000..902ab58
--- /dev/null
+++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/examples/EchoServerSocketExample.java
@@ -0,0 +1,140 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.websocket.examples;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.ByteArrayBuffer;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.websocket.WebSocket;
+import org.eclipse.jetty.websocket.WebSocketServlet;
+
+public class EchoServerSocketExample
+{
+ public static class EchoSocket implements WebSocket, WebSocket.OnTextMessage, WebSocket.OnBinaryMessage
+ {
+ private static final Logger LOG = Log.getLogger(EchoSocket.class);
+
+ public List<Throwable> errors = new ArrayList<Throwable>();
+ private Connection connection;
+
+ @Override
+ public void onClose(int closeCode, String message)
+ {
+ LOG.info("Closed {} : {}",closeCode,message);
+ }
+
+ @Override
+ public void onMessage(byte[] data, int offset, int length)
+ {
+ // Wrap in Jetty Buffer (only for logging reasons)
+ Buffer buf = new ByteArrayBuffer(data,offset,length);
+ LOG.info("on Text : {}",buf.toDetailString());
+
+ try
+ {
+ // echo back this BINARY message
+ this.connection.sendMessage(data,offset,length);
+ }
+ catch (IOException e)
+ {
+ LOG.warn(e);
+ }
+ }
+
+ @Override
+ public void onMessage(String message)
+ {
+ LOG.info("on Text : {}",message);
+
+ try
+ {
+ // echo back this TEXT message
+ this.connection.sendMessage(message);
+ }
+ catch (IOException e)
+ {
+ LOG.warn(e);
+ }
+ }
+
+ @Override
+ public void onOpen(Connection connection)
+ {
+ this.connection = connection;
+ LOG.info("Connection opened : {}",connection);
+ }
+ }
+
+ @SuppressWarnings("serial")
+ public static class EchoSocketServlet extends WebSocketServlet
+ {
+ @Override
+ public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol)
+ {
+ return new EchoSocket();
+ }
+ }
+
+ private static final Logger LOG = Log.getLogger(EchoServerSocketExample.class);
+
+ public static void main(String[] args)
+ {
+ Server server = new Server(9090);
+
+ ServletContextHandler context = new ServletContextHandler();
+ context.setContextPath("/");
+ server.setHandler(context);
+
+ // Setup websocket echo socket, via servlet, on url pattern "/echo"
+ context.addServlet(EchoSocketServlet.class,"/echo");
+
+ try
+ {
+ // Start server
+ server.start();
+
+ Connector conn = server.getConnectors()[0];
+ String host = conn.getHost();
+ if (host == null)
+ {
+ host = "localhost";
+ }
+ int port = conn.getLocalPort();
+ URI serverUri = new URI(String.format("ws://%s:%d/echo",host,port));
+
+ LOG.info("WebSocket Echo Server started on {}",serverUri);
+ server.join();
+ }
+ catch (Throwable t)
+ {
+ LOG.warn(t);
+ }
+ }
+}
diff --git a/jetty-xml/pom.xml b/jetty-xml/pom.xml
index 7a5f2e8..331eeef 100644
--- a/jetty-xml/pom.xml
+++ b/jetty-xml/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-xml</artifactId>
diff --git a/pom.xml b/pom.xml
index 8418b07..add494b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,14 +6,14 @@
<version>20</version>
</parent>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
<name>Jetty :: Project</name>
<url>http://www.eclipse.org/jetty</url>
<packaging>pom</packaging>
<properties>
<jetty.url>http://www.eclipse.org/jetty</jetty.url>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <orbit-servlet-api-version>2.5.0.v201103041518</orbit-servlet-api-version>
+ <orbit-servlet-api-version>3.0.0.v201112011016</orbit-servlet-api-version>
<build-support-version>1.1</build-support-version>
<slf4j-version>1.6.1</slf4j-version>
<jetty-test-policy-version>1.2</jetty-test-policy-version>
@@ -22,7 +22,7 @@
<connection>scm:git:http://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.project.git</connection>
<developerConnection>scm:git:ssh://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.project.git</developerConnection>
<url>http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/tree</url>
- <tag>HEAD</tag>
+ <tag>jetty-8</tag>
</scm>
<build>
<defaultGoal>install</defaultGoal>
@@ -30,8 +30,8 @@
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
- <source>1.5</source>
- <target>1.5</target>
+ <source>1.6</source>
+ <target>1.6</target>
<verbose>false</verbose>
</configuration>
</plugin>
@@ -189,7 +189,7 @@
</execution>
</executions>
<configuration>
- <targetJdk>1.5</targetJdk>
+ <targetJdk>1.6</targetJdk>
<rulesets>
<ruleset>jetty/pmd_logging_ruleset.xml</ruleset>
</rulesets>
@@ -247,6 +247,14 @@
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <version>2.8.2</version>
+ <configuration>
+ <retryFailedDeploymentCount>10</retryFailedDeploymentCount>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
@@ -417,6 +425,7 @@
<module>jetty-start</module>
<module>jetty-nested</module>
<module>jetty-overlay-deployer</module>
+ <module>jetty-osgi</module>
<module>jetty-nosql</module>
<module>jetty-http-spi</module>
<module>jetty-jsp</module>
@@ -427,6 +436,7 @@
<module>test-jetty-webapp</module>
<module>test-jetty-nested</module>
<module>example-jetty-embedded</module>
+ <module>example-async-rest</module>
<module>tests</module>
</modules>
<dependencyManagement>
@@ -757,8 +767,8 @@
<configuration>
<excludePackageNames>com.acme</excludePackageNames>
<links>
- <link>http://java.sun.com/j2se/1.5.0/docs/api</link>
- <link>http://java.sun.com/javaee/5/docs/api</link>
+ <link>http://java.sun.com/javase/6/docs/api/</link>
+ <link>http://java.sun.com/javaee/6/docs/api</link>
<link>http://junit.sourceforge.net/javadoc/</link>
</links>
<tags>
diff --git a/settings.xml b/settings.xml
new file mode 100755
index 0000000..9b5ddef
--- /dev/null
+++ b/settings.xml
@@ -0,0 +1,21 @@
+<settings>
+ <localRepository>/tmp/jetty-builds/jetty8/localRepo</localRepository>
+ <interactiveMode>true</interactiveMode>
+ <offline>false</offline>
+<proxies>
+ <proxy>
+ <active>true</active>
+ <protocol>http</protocol>
+ <host>proxy.eclipse.org</host>
+ <port>9898</port>
+ </proxy>
+ </proxies>
+<mirrors>
+ <mirror>
+ <id>central</id>
+ <name>central</name>
+ <url>http://repo2.maven.org/maven2/</url>
+ <mirrorOf>*</mirrorOf>
+ </mirror>
+ </mirrors>
+</settings>
diff --git a/test-continuation-jetty6/pom.xml b/test-continuation-jetty6/pom.xml
index 9f7f4e1..86048b7 100644
--- a/test-continuation-jetty6/pom.xml
+++ b/test-continuation-jetty6/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.0-SNAPSHOT</version>
+ <version>8.1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>test-continuation-jetty6</artifactId>
@@ -29,16 +29,34 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.mortbay.jetty</groupId>
+ <artifactId>servlet-api</artifactId>
+ <version>2.5-20081211</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>${project.version}</version>
<scope>test</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>${servlet.spec.groupId}</groupId>
+ <artifactId>${servlet.spec.artifactId}</artifactId>
+ </exclusion>
+ </exclusions>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>test-continuation</artifactId>
<version>${project.version}</version>
<scope>test</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>${servlet.spec.groupId}</groupId>
+ <artifactId>${servlet.spec.artifactId}</artifactId>
+ </exclusion>
+ </exclusions>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
diff --git a/test-continuation-jetty6/src/test/java/org/eclipse/jetty/continuation/ContinuationBase.java b/test-continuation-jetty6/src/test/java/org/eclipse/jetty/continuation/ContinuationBase.java
new file mode 100644
index 0000000..aafecbc
--- /dev/null
+++ b/test-continuation-jetty6/src/test/java/org/eclipse/jetty/continuation/ContinuationBase.java
@@ -0,0 +1,428 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.continuation;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.Socket;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestCase;
+
+
+
+public abstract class ContinuationBase extends TestCase
+{
+ protected SuspendServlet _servlet=new SuspendServlet();
+ protected int _port;
+
+ protected void doit(String type) throws Exception
+ {
+ String response;
+
+ response=process(null,null);
+ assertContains(type,response);
+ assertContains("NORMAL",response);
+ assertNotContains("history: onTimeout",response);
+ assertNotContains("history: onComplete",response);
+
+ response=process("sleep=200",null);
+ assertContains("SLEPT",response);
+ assertNotContains("history: onTimeout",response);
+ assertNotContains("history: onComplete",response);
+
+ response=process("suspend=200",null);
+ assertContains("TIMEOUT",response);
+ assertContains("history: onTimeout",response);
+ assertContains("history: onComplete",response);
+
+ response=process("suspend=200&resume=10",null);
+ assertContains("RESUMED",response);
+ assertNotContains("history: onTimeout",response);
+ assertContains("history: onComplete",response);
+
+ response=process("suspend=200&resume=0",null);
+ assertContains("RESUMED",response);
+ assertNotContains("history: onTimeout",response);
+ assertContains("history: onComplete",response);
+
+ response=process("suspend=200&complete=10",null);
+ assertContains("COMPLETED",response);
+ assertNotContains("history: onTimeout",response);
+ assertContains("history: onComplete",response);
+
+ response=process("suspend=200&complete=0",null);
+ assertContains("COMPLETED",response);
+ assertNotContains("history: onTimeout",response);
+ assertContains("history: onComplete",response);
+
+
+ response=process("suspend=1000&resume=10&suspend2=1000&resume2=10",null);
+ assertEquals(2,count(response,"history: suspend"));
+ assertEquals(2,count(response,"history: resume"));
+ assertEquals(0,count(response,"history: onTimeout"));
+ assertEquals(1,count(response,"history: onComplete"));
+ assertContains("RESUMED",response);
+
+ response=process("suspend=1000&resume=10&suspend2=1000&resume2=10",null);
+ assertEquals(2,count(response,"history: suspend"));
+ assertEquals(2,count(response,"history: resume"));
+ assertEquals(0,count(response,"history: onTimeout"));
+ assertEquals(1,count(response,"history: onComplete"));
+ assertContains("RESUMED",response);
+
+ response=process("suspend=1000&resume=10&suspend2=1000&complete2=10",null);
+ assertEquals(2,count(response,"history: suspend"));
+ assertEquals(1,count(response,"history: resume"));
+ assertEquals(0,count(response,"history: onTimeout"));
+ assertEquals(1,count(response,"history: onComplete"));
+ assertContains("COMPLETED",response);
+
+ response=process("suspend=1000&resume=10&suspend2=10",null);
+ assertEquals(2,count(response,"history: suspend"));
+ assertEquals(1,count(response,"history: resume"));
+ assertEquals(1,count(response,"history: onTimeout"));
+ assertEquals(1,count(response,"history: onComplete"));
+ assertContains("TIMEOUT",response);
+
+
+
+ response=process("suspend=10&suspend2=1000&resume2=10",null);
+ assertEquals(2,count(response,"history: suspend"));
+ assertEquals(1,count(response,"history: resume"));
+ assertEquals(1,count(response,"history: onTimeout"));
+ assertEquals(1,count(response,"history: onComplete"));
+ assertContains("RESUMED",response);
+
+ response=process("suspend=10&suspend2=1000&resume2=10",null);
+ assertEquals(2,count(response,"history: suspend"));
+ assertEquals(1,count(response,"history: resume"));
+ assertEquals(1,count(response,"history: onTimeout"));
+ assertEquals(1,count(response,"history: onComplete"));
+ assertContains("RESUMED",response);
+
+ response=process("suspend=10&suspend2=1000&complete2=10",null);
+ assertEquals(2,count(response,"history: suspend"));
+ assertEquals(0,count(response,"history: resume"));
+ assertEquals(1,count(response,"history: onTimeout"));
+ assertEquals(1,count(response,"history: onComplete"));
+ assertContains("COMPLETED",response);
+
+ response=process("suspend=10&suspend2=10",null);
+ assertEquals(2,count(response,"history: suspend"));
+ assertEquals(0,count(response,"history: resume"));
+ assertEquals(2,count(response,"history: onTimeout"));
+ assertEquals(1,count(response,"history: onComplete"));
+ assertContains("TIMEOUT",response);
+
+ }
+
+
+ private int count(String responses,String substring)
+ {
+ int count=0;
+ int i=responses.indexOf(substring);
+ while (i>=0)
+ {
+ count++;
+ i=responses.indexOf(substring,i+substring.length());
+ }
+
+ return count;
+ }
+
+ protected void assertContains(String content,String response)
+ {
+ assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
+ if (response.indexOf(content,15)<0)
+ {
+ System.err.println(content+" NOT IN '"+response+"'");
+ assertTrue(false);
+ }
+ }
+
+ protected void assertNotContains(String content,String response)
+ {
+ assertEquals("HTTP/1.1 200 OK",response.substring(0,15));
+ if (response.indexOf(content,15)>=0)
+ {
+ System.err.println(content+" IS IN '"+response+"'");
+ assertTrue(false);
+ }
+ }
+
+ public synchronized String process(String query,String content) throws Exception
+ {
+ String request = "GET /";
+
+ if (query!=null)
+ request+="?"+query;
+ request+=" HTTP/1.1\r\n"+
+ "Host: localhost\r\n"+
+ "Connection: close\r\n";
+ if (content!=null)
+ request+="Content-Length: "+content.length()+"\r\n";
+ request+="\r\n" + content;
+
+ Socket socket = new Socket("localhost",_port);
+ socket.getOutputStream().write(request.getBytes("UTF-8"));
+
+ String response = toString(socket.getInputStream());
+ return response;
+ }
+
+
+ protected abstract String toString(InputStream in) throws IOException;
+
+
+ private static class SuspendServlet extends HttpServlet
+ {
+ private Timer _timer=new Timer();
+
+ public SuspendServlet()
+ {}
+
+ /* ------------------------------------------------------------ */
+ protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
+ {
+ final Continuation continuation = ContinuationSupport.getContinuation(request,response);
+
+ response.addHeader("history",continuation.getClass().toString());
+
+ int read_before=0;
+ long sleep_for=-1;
+ long suspend_for=-1;
+ long suspend2_for=-1;
+ long resume_after=-1;
+ long resume2_after=-1;
+ long complete_after=-1;
+ long complete2_after=-1;
+
+ if (request.getParameter("read")!=null)
+ read_before=Integer.parseInt(request.getParameter("read"));
+ if (request.getParameter("sleep")!=null)
+ sleep_for=Integer.parseInt(request.getParameter("sleep"));
+ if (request.getParameter("suspend")!=null)
+ suspend_for=Integer.parseInt(request.getParameter("suspend"));
+ if (request.getParameter("suspend2")!=null)
+ suspend2_for=Integer.parseInt(request.getParameter("suspend2"));
+ if (request.getParameter("resume")!=null)
+ resume_after=Integer.parseInt(request.getParameter("resume"));
+ if (request.getParameter("resume2")!=null)
+ resume2_after=Integer.parseInt(request.getParameter("resume2"));
+ if (request.getParameter("complete")!=null)
+ complete_after=Integer.parseInt(request.getParameter("complete"));
+ if (request.getParameter("complete2")!=null)
+ complete2_after=Integer.parseInt(request.getParameter("complete2"));
+
+ if (continuation.isInitial())
+ {
+ if (read_before>0)
+ {
+ byte[] buf=new byte[read_before];
+ request.getInputStream().read(buf);
+ }
+ else if (read_before<0)
+ {
+ InputStream in = request.getInputStream();
+ int b=in.read();
+ while(b!=-1)
+ b=in.read();
+ }
+
+ if (suspend_for>=0)
+ {
+ if (suspend_for>0)
+ continuation.setTimeout(suspend_for);
+ continuation.addContinuationListener(__listener);
+ ((HttpServletResponse)continuation.getServletResponse()).addHeader("history","suspend");
+ continuation.suspend();
+
+ if (complete_after>0)
+ {
+ TimerTask complete = new TimerTask()
+ {
+ public void run()
+ {
+ try
+ {
+ response.setStatus(200);
+ response.getOutputStream().println("COMPLETED\n");
+ continuation.complete();
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ };
+ synchronized (_timer)
+ {
+ _timer.schedule(complete,complete_after);
+ }
+ }
+ else if (complete_after==0)
+ {
+ response.setStatus(200);
+ response.getOutputStream().println("COMPLETED\n");
+ continuation.complete();
+ }
+ else if (resume_after>0)
+ {
+ TimerTask resume = new TimerTask()
+ {
+ public void run()
+ {
+ ((HttpServletResponse)continuation.getServletResponse()).addHeader("history","resume");
+ continuation.resume();
+ }
+ };
+ synchronized (_timer)
+ {
+ _timer.schedule(resume,resume_after);
+ }
+ }
+ else if (resume_after==0)
+ {
+ ((HttpServletResponse)continuation.getServletResponse()).addHeader("history","resume");
+ continuation.resume();
+ }
+ }
+ else if (sleep_for>=0)
+ {
+ try
+ {
+ Thread.sleep(sleep_for);
+ }
+ catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ }
+ response.setStatus(200);
+ response.getOutputStream().println("SLEPT\n");
+ }
+ else
+ {
+ response.setStatus(200);
+ response.getOutputStream().println("NORMAL\n");
+ }
+ }
+ else if (suspend2_for>=0 && request.getAttribute("2nd")==null)
+ {
+ request.setAttribute("2nd","cycle");
+
+ if (suspend2_for>0)
+ continuation.setTimeout(suspend2_for);
+ // continuation.addContinuationListener(__listener);
+ ((HttpServletResponse)continuation.getServletResponse()).addHeader("history","suspend");
+ continuation.suspend();
+
+ if (complete2_after>0)
+ {
+ TimerTask complete = new TimerTask()
+ {
+ public void run()
+ {
+ try
+ {
+ response.setStatus(200);
+ response.getOutputStream().println("COMPLETED\n");
+ continuation.complete();
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ };
+ synchronized (_timer)
+ {
+ _timer.schedule(complete,complete2_after);
+ }
+ }
+ else if (complete2_after==0)
+ {
+ response.setStatus(200);
+ response.getOutputStream().println("COMPLETED\n");
+ continuation.complete();
+ }
+ else if (resume2_after>0)
+ {
+ TimerTask resume = new TimerTask()
+ {
+ public void run()
+ {
+ ((HttpServletResponse)continuation.getServletResponse()).addHeader("history","resume");
+ continuation.resume();
+ }
+ };
+ synchronized (_timer)
+ {
+ _timer.schedule(resume,resume2_after);
+ }
+ }
+ else if (resume2_after==0)
+ {
+ ((HttpServletResponse)continuation.getServletResponse()).addHeader("history","resume");
+ continuation.resume();
+ }
+ return;
+ }
+ else if (continuation.isExpired())
+ {
+ response.setStatus(200);
+ response.getOutputStream().println("TIMEOUT\n");
+ }
+ else if (continuation.isResumed())
+ {
+ response.setStatus(200);
+ response.getOutputStream().println("RESUMED\n");
+ }
+ else
+ {
+ response.setStatus(200);
+ response.getOutputStream().println("unknown???\n");
+ }
+ }
+ }
+
+
+
+ private static ContinuationListener __listener =
+ new ContinuationListener()
+ {
+ public void onComplete(Continuation continuation)
+ {
+ ((HttpServletResponse)continuation.getServletResponse()).addHeader("history","onComplete");
+ }
+
+ public void onTimeout(Continuation continuation)
+ {
+ ((HttpServletResponse)continuation.getServletResponse()).addHeader("history","onTimeout");
+ continuation.resume();
+ }
+
+ };
+}
diff --git a/test-continuation-jetty6/src/test/java/org/eclipse/jetty/continuation/FauxContinuationTest.java b/test-continuation-jetty6/src/test/java/org/eclipse/jetty/continuation/FauxContinuationTest.java
new file mode 100644
index 0000000..f43e4c7
--- /dev/null
+++ b/test-continuation-jetty6/src/test/java/org/eclipse/jetty/continuation/FauxContinuationTest.java
@@ -0,0 +1,83 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.continuation;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+
+import org.mortbay.jetty.Connector;
+import org.mortbay.jetty.Server;
+import org.mortbay.jetty.nio.SelectChannelConnector;
+import org.mortbay.jetty.servlet.Context;
+import org.mortbay.jetty.servlet.FilterHolder;
+import org.mortbay.jetty.servlet.ServletHandler;
+import org.mortbay.jetty.servlet.ServletHolder;
+import org.mortbay.util.IO;
+
+
+public class FauxContinuationTest extends ContinuationBase
+{
+ protected Server _server = new Server();
+ protected ServletHandler _servletHandler;
+ protected SelectChannelConnector _connector;
+ FilterHolder _filter;
+
+ protected void setUp() throws Exception
+ {
+ _connector = new SelectChannelConnector();
+ _server.setConnectors(new Connector[]{ _connector });
+ Context servletContext = new Context(Context.NO_SECURITY|Context.NO_SESSIONS);
+ _server.setHandler(servletContext);
+ _servletHandler=servletContext.getServletHandler();
+ ServletHolder holder=new ServletHolder(_servlet);
+ _servletHandler.addServletWithMapping(holder,"/");
+ _filter=_servletHandler.addFilterWithMapping(ContinuationFilter.class,"/*",0);
+ }
+
+ protected void tearDown() throws Exception
+ {
+ _server.stop();
+ }
+
+ public void testFaux() throws Exception
+ {
+ _filter.setInitParameter("debug","true");
+ _filter.setInitParameter("faux","true");
+ _server.start();
+ _port=_connector.getLocalPort();
+
+ doit("FauxContinuation");
+ }
+
+
+
+ protected String toString(InputStream in) throws IOException
+ {
+ return IO.toString(in);
+ }
+}
diff --git a/test-continuation/pom.xml b/test-continuation/pom.xml
index cc83550..22eb956 100644
--- a/test-continuation/pom.xml
+++ b/test-continuation/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>test-continuation</artifactId>
diff --git a/test-continuation/src/test/java/org/eclipse/jetty/continuation/ContinuationTest.java b/test-continuation/src/test/java/org/eclipse/jetty/continuation/ContinuationTest.java
index 24bf362..747fd78 100644
--- a/test-continuation/src/test/java/org/eclipse/jetty/continuation/ContinuationTest.java
+++ b/test-continuation/src/test/java/org/eclipse/jetty/continuation/ContinuationTest.java
@@ -20,6 +20,7 @@
import java.io.IOException;
import java.io.InputStream;
+import java.util.EnumSet;
import java.util.ArrayList;
import java.util.List;
@@ -66,6 +67,7 @@
_servletHandler=servletContext.getServletHandler();
ServletHolder holder=new ServletHolder(_servlet);
+ holder.setAsyncSupported(true);
_servletHandler.addServletWithMapping(holder,"/");
_server.start();
diff --git a/test-continuation/src/test/java/org/eclipse/jetty/continuation/FauxContinuationTest.java b/test-continuation/src/test/java/org/eclipse/jetty/continuation/FauxContinuationTest.java
index b8e7ce4..5db81e4 100644
--- a/test-continuation/src/test/java/org/eclipse/jetty/continuation/FauxContinuationTest.java
+++ b/test-continuation/src/test/java/org/eclipse/jetty/continuation/FauxContinuationTest.java
@@ -20,6 +20,9 @@
import java.io.IOException;
import java.io.InputStream;
+import java.util.EnumSet;
+
+import javax.servlet.DispatcherType;
import org.eclipse.jetty.continuation.test.ContinuationBase;
import org.eclipse.jetty.server.Connector;
@@ -49,7 +52,7 @@
ServletHolder holder=new ServletHolder(_servlet);
_servletHandler.addServletWithMapping(holder,"/");
- _filter=_servletHandler.addFilterWithMapping(ContinuationFilter.class,"/*",0);
+ _filter=_servletHandler.addFilterWithMapping(ContinuationFilter.class,"/*",null);
_filter.setInitParameter("debug","true");
_filter.setInitParameter("faux","true");
_server.start();
diff --git a/test-jetty-nested/pom.xml b/test-jetty-nested/pom.xml
index 3fe5400..7cae48a 100644
--- a/test-jetty-nested/pom.xml
+++ b/test-jetty-nested/pom.xml
@@ -4,7 +4,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<artifactId>test-jetty-nested</artifactId>
<name>Jetty :: Nested Test</name>
diff --git a/test-jetty-nested/src/main/java/org/eclipse/jetty/nested/Dump.java b/test-jetty-nested/src/main/java/org/eclipse/jetty/nested/Dump.java
index a71fa17..5f93b4e 100644
--- a/test-jetty-nested/src/main/java/org/eclipse/jetty/nested/Dump.java
+++ b/test-jetty-nested/src/main/java/org/eclipse/jetty/nested/Dump.java
@@ -101,7 +101,7 @@
final boolean flush= request.getParameter("flush")!=null?Boolean.parseBoolean(request.getParameter("flush")):false;
- if(request.getPathInfo()!=null && request.getPathInfo().toLowerCase().indexOf("script")!=-1)
+ if(request.getPathInfo()!=null && request.getPathInfo().toLowerCase(Locale.ENGLISH).indexOf("script")!=-1)
{
response.sendRedirect(response.encodeRedirectURL(getServletContext().getContextPath() + "/dump/info"));
return;
diff --git a/test-jetty-servlet/pom.xml b/test-jetty-servlet/pom.xml
index ce211cb..b6f6487 100644
--- a/test-jetty-servlet/pom.xml
+++ b/test-jetty-servlet/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>test-jetty-servlet</artifactId>
diff --git a/test-jetty-servlet/src/main/java/org/eclipse/jetty/testing/HttpTester.java b/test-jetty-servlet/src/main/java/org/eclipse/jetty/testing/HttpTester.java
index 7e489df..65ce9f1 100644
--- a/test-jetty-servlet/src/main/java/org/eclipse/jetty/testing/HttpTester.java
+++ b/test-jetty-servlet/src/main/java/org/eclipse/jetty/testing/HttpTester.java
@@ -382,7 +382,7 @@
cookie.getMaxAge(),
cookie.getComment(),
cookie.getSecure(),
- false,
+ cookie.isHttpOnly(),
cookie.getVersion());
}
diff --git a/test-jetty-servlet/src/main/java/org/eclipse/jetty/testing/ServletTester.java b/test-jetty-servlet/src/main/java/org/eclipse/jetty/testing/ServletTester.java
index 053e668..4e3de5d 100644
--- a/test-jetty-servlet/src/main/java/org/eclipse/jetty/testing/ServletTester.java
+++ b/test-jetty-servlet/src/main/java/org/eclipse/jetty/testing/ServletTester.java
@@ -19,9 +19,12 @@
package org.eclipse.jetty.testing;
import java.net.InetAddress;
+import java.util.EnumSet;
import java.util.Enumeration;
import java.util.EventListener;
+import javax.servlet.DispatcherType;
+
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.Server;
@@ -245,7 +248,7 @@
* @return the FilterHolder
* @see org.eclipse.jetty.servlet.ServletContextHandler#addFilter(java.lang.Class, java.lang.String, int)
*/
- public FilterHolder addFilter(Class filterClass, String pathSpec, int dispatches)
+ public FilterHolder addFilter(Class filterClass, String pathSpec, EnumSet<DispatcherType> dispatches)
{
return _context.addFilter(filterClass,pathSpec,dispatches);
}
@@ -258,7 +261,7 @@
* @return the FilterHolder
* @see org.eclipse.jetty.servlet.ServletContextHandler#addFilter(java.lang.String, java.lang.String, int)
*/
- public FilterHolder addFilter(String filterClass, String pathSpec, int dispatches)
+ public FilterHolder addFilter(String filterClass, String pathSpec, EnumSet<DispatcherType> dispatches)
{
return _context.addFilter(filterClass,pathSpec,dispatches);
}
diff --git a/test-jetty-webapp/pom.xml b/test-jetty-webapp/pom.xml
index 9d2e009..2a78829 100644
--- a/test-jetty-webapp/pom.xml
+++ b/test-jetty-webapp/pom.xml
@@ -2,7 +2,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>test-jetty-webapp</artifactId>
@@ -110,6 +110,7 @@
<value>222</value>
</systemProperty>
</systemProperties>
+ <useTestScope>true</useTestScope>
<webAppConfig>
<contextPath>/test</contextPath>
<tempDirectory>${project.build.directory}/work</tempDirectory>
diff --git a/test-jetty-webapp/src/main/config/contexts/test.d/override-web.xml b/test-jetty-webapp/src/main/config/contexts/test.d/override-web.xml
index 9e42d6d..512ce2b 100644
--- a/test-jetty-webapp/src/main/config/contexts/test.d/override-web.xml
+++ b/test-jetty-webapp/src/main/config/contexts/test.d/override-web.xml
@@ -15,15 +15,6 @@
<param-value>a context value</param-value>
</context-param>
- <!-- Add or override filter init parameter -->
- <filter>
- <filter-name>TestFilter</filter-name>
- <filter-class>com.acme.TestFilter</filter-class>
- <init-param>
- <param-name>remote</param-name>
- <param-value>false</param-value>
- </init-param>
- </filter>
<!-- Add or override servlet init parameter -->
<servlet>
@@ -46,6 +37,23 @@
<servlet-class>com.acme.SessionDump</servlet-class>
<load-on-startup>5</load-on-startup>
</servlet>
+
+ <!-- Uncomment to override the setup of the test filter -->
+ <!--
+ <filter>
+ <filter-name>TestFilter</filter-name>
+ <filter-class>com.acme.TestFilter</filter-class>
+ <async-support>true</async-support>
+ <init-param>
+ <param-name>remote</param-name>
+ <param-value>false</param-value>
+ </init-param>
+ </filter>
+ <filter-mapping>
+ <filter-name>TestFilter</filter-name>
+ <url-pattern>/*</url-pattern>
+ </filter-mapping>
+ -->
</web-app>
diff --git a/test-jetty-webapp/src/main/java/com/acme/Dump.java b/test-jetty-webapp/src/main/java/com/acme/Dump.java
index 7af0bfe..e99eb99 100644
--- a/test-jetty-webapp/src/main/java/com/acme/Dump.java
+++ b/test-jetty-webapp/src/main/java/com/acme/Dump.java
@@ -119,6 +119,18 @@
@Override
public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
{
+ if (!request.isUserInRole("user"))
+ {
+ try
+ {
+ request.login("user", "password");
+ }
+ catch(ServletException se)
+ {
+ se.printStackTrace();
+ }
+ }
+
// Handle a dump of data
final String data= request.getParameter("data");
final String chars= request.getParameter("chars");
@@ -493,6 +505,10 @@
pout.write("<td>"+request.isSecure()+"</td>");
pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">encodeRedirectURL(/foo?bar): </th>");
+ pout.write("<td>"+response.encodeRedirectURL("/foo?bar")+"</td>");
+
+ pout.write("</tr><tr>\n");
pout.write("<th align=\"right\">isUserInRole(admin): </th>");
pout.write("<td>"+request.isUserInRole("admin")+"</td>");
diff --git a/test-jetty-webapp/src/main/java/com/acme/LoginServlet.java b/test-jetty-webapp/src/main/java/com/acme/LoginServlet.java
new file mode 100644
index 0000000..6d9c70e
--- /dev/null
+++ b/test-jetty-webapp/src/main/java/com/acme/LoginServlet.java
@@ -0,0 +1,92 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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 com.acme;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/* ------------------------------------------------------------ */
+/** Dump Servlet Request.
+ *
+ */
+public class LoginServlet extends HttpServlet
+{
+ private static final Logger LOG = Log.getLogger(SecureModeServlet.class);
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public void init(ServletConfig config) throws ServletException
+ {
+ super.init(config);
+ }
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ doGet(request, response);
+ }
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+
+ response.setContentType("text/html");
+ ServletOutputStream out = response.getOutputStream();
+ out.println("<html>");
+ out.println("<br/>Before getUserPrincipal="+request.getUserPrincipal());
+ out.println("<br/>Before getRemoteUser="+request.getRemoteUser());
+ String param = request.getParameter("action");
+
+ if ("login".equals(param))
+ {
+ request.login("jetty", "jetty");
+ }
+ else if ("logout".equals(param))
+ {
+ request.logout();
+ }
+ else if ("wrong".equals(param))
+ {
+ request.login("jetty", "123");
+ }
+
+ out.println("<br/>After getUserPrincipal="+request.getUserPrincipal());
+ out.println("<br/>After getRemoteUser="+request.getRemoteUser());
+ out.println("</html>");
+ out.flush();
+ }
+}
diff --git a/test-jetty-webapp/src/main/java/com/acme/RegTest.java b/test-jetty-webapp/src/main/java/com/acme/RegTest.java
new file mode 100644
index 0000000..28e47f4
--- /dev/null
+++ b/test-jetty-webapp/src/main/java/com/acme/RegTest.java
@@ -0,0 +1,194 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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 com.acme;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.util.StringUtil;
+
+
+
+
+/* ------------------------------------------------------------ */
+/** Rego Servlet - tests being accessed from servlet 3.0 programmatic
+ * configuration.
+ *
+ */
+public class RegTest extends HttpServlet
+{
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public void init(ServletConfig config) throws ServletException
+ {
+ super.init(config);
+ }
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ doGet(request, response);
+ }
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
+ {
+ request.setCharacterEncoding("UTF-8");
+ PrintWriter pout=null;
+
+ try
+ {
+ pout =response.getWriter();
+ }
+ catch(IllegalStateException e)
+ {
+ pout=new PrintWriter(new OutputStreamWriter(response.getOutputStream(),"UTF-8"));
+ }
+
+ try
+ {
+ pout.write("<html>\n<body>\n");
+ pout.write("<h1>Rego Servlet</h1>\n");
+ pout.write("<table width=\"95%\">");
+ pout.write("<tr>\n");
+ pout.write("<th align=\"right\">getMethod: </th>");
+ pout.write("<td>" + notag(request.getMethod())+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getContentLength: </th>");
+ pout.write("<td>"+Integer.toString(request.getContentLength())+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getContentType: </th>");
+ pout.write("<td>"+notag(request.getContentType())+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getRequestURI: </th>");
+ pout.write("<td>"+notag(request.getRequestURI())+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getRequestURL: </th>");
+ pout.write("<td>"+notag(request.getRequestURL().toString())+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getContextPath: </th>");
+ pout.write("<td>"+request.getContextPath()+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getServletPath: </th>");
+ pout.write("<td>"+notag(request.getServletPath())+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getPathInfo: </th>");
+ pout.write("<td>"+notag(request.getPathInfo())+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getPathTranslated: </th>");
+ pout.write("<td>"+notag(request.getPathTranslated())+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getQueryString: </th>");
+ pout.write("<td>"+notag(request.getQueryString())+"</td>");
+ pout.write("</tr><tr>\n");
+
+ pout.write("<th align=\"right\">getProtocol: </th>");
+ pout.write("<td>"+request.getProtocol()+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getScheme: </th>");
+ pout.write("<td>"+request.getScheme()+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getServerName: </th>");
+ pout.write("<td>"+notag(request.getServerName())+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getServerPort: </th>");
+ pout.write("<td>"+Integer.toString(request.getServerPort())+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getLocalName: </th>");
+ pout.write("<td>"+request.getLocalName()+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getLocalAddr: </th>");
+ pout.write("<td>"+request.getLocalAddr()+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getLocalPort: </th>");
+ pout.write("<td>"+Integer.toString(request.getLocalPort())+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getRemoteUser: </th>");
+ pout.write("<td>"+request.getRemoteUser()+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getUserPrincipal: </th>");
+ pout.write("<td>"+request.getUserPrincipal()+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getRemoteAddr: </th>");
+ pout.write("<td>"+request.getRemoteAddr()+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getRemoteHost: </th>");
+ pout.write("<td>"+request.getRemoteHost()+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getRemotePort: </th>");
+ pout.write("<td>"+request.getRemotePort()+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">getRequestedSessionId: </th>");
+ pout.write("<td>"+request.getRequestedSessionId()+"</td>");
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">isSecure(): </th>");
+ pout.write("<td>"+request.isSecure()+"</td>");
+
+ pout.write("</tr><tr>\n");
+ pout.write("<th align=\"right\">isUserInRole(admin): </th>");
+ pout.write("<td>"+request.isUserInRole("admin")+"</td>");
+
+ pout.write("</tr></table>");
+
+ }
+ catch (Exception e)
+ {
+ getServletContext().log("dump "+e);
+ }
+
+
+ pout.write("</body>\n</html>\n");
+
+ pout.close();
+ }
+
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public String getServletInfo()
+ {
+ return "Rego Servlet";
+ }
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public synchronized void destroy()
+ {
+ }
+
+
+ private String notag(String s)
+ {
+ if (s==null)
+ return "null";
+ s=StringUtil.replace(s,"&","&");
+ s=StringUtil.replace(s,"<","<");
+ s=StringUtil.replace(s,">",">");
+ return s;
+ }
+}
diff --git a/test-jetty-webapp/src/main/java/com/acme/TestListener.java b/test-jetty-webapp/src/main/java/com/acme/TestListener.java
index 084a462..884f283 100644
--- a/test-jetty-webapp/src/main/java/com/acme/TestListener.java
+++ b/test-jetty-webapp/src/main/java/com/acme/TestListener.java
@@ -18,6 +18,7 @@
package com.acme;
+import javax.servlet.DispatcherType;
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import javax.servlet.ServletContextEvent;
@@ -26,6 +27,12 @@
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
+import javax.servlet.ServletRegistration;
+import javax.servlet.FilterRegistration;
+import javax.servlet.ServletSecurityElement;
+import javax.servlet.HttpConstraintElement;
+import javax.servlet.HttpMethodConstraintElement;
+import javax.servlet.annotation.ServletSecurity;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionAttributeListener;
@@ -33,6 +40,9 @@
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
+import java.util.EnumSet;
+import java.util.Set;
+
public class TestListener implements HttpSessionListener, HttpSessionAttributeListener, HttpSessionActivationListener, ServletContextListener, ServletContextAttributeListener, ServletRequestListener, ServletRequestAttributeListener
{
public void attributeAdded(HttpSessionBindingEvent se)
@@ -62,16 +72,33 @@
public void contextInitialized(ServletContextEvent sce)
{
- /* TODO for servlet 3.0
- * FilterRegistration registration=context.addFilter("TestFilter",TestFilter.class.getName());
-
-
- registration.setAsyncSupported(true);
- registration.addMappingForUrlPatterns(
+ //configure programmatic security
+ ServletRegistration.Dynamic rego = sce.getServletContext().addServlet("RegoTest", RegTest.class.getName());
+ rego.addMapping("/rego/*");
+ HttpConstraintElement constraintElement = new HttpConstraintElement(ServletSecurity.EmptyRoleSemantic.PERMIT,
+ ServletSecurity.TransportGuarantee.NONE, new String[]{"admin"});
+ ServletSecurityElement securityElement = new ServletSecurityElement(constraintElement, null);
+ Set<String> unchanged = rego.setServletSecurity(securityElement);
+ //System.err.println("Security constraints registered: "+unchanged.isEmpty());
+
+ //Test that a security constraint from web.xml can't be overridden programmatically
+ ServletRegistration.Dynamic rego2 = sce.getServletContext().addServlet("RegoTest2", RegTest.class.getName());
+ rego2.addMapping("/rego2/*");
+ securityElement = new ServletSecurityElement(constraintElement, null);
+ unchanged = rego2.setServletSecurity(securityElement);
+ //System.err.println("Overridding web.xml constraints not possible:" +!unchanged.isEmpty());
+
+ /* For servlet 3.0 */
+ FilterRegistration.Dynamic registration = sce.getServletContext().addFilter("TestFilter",TestFilter.class.getName());
+ if (registration != null) //otherwise it was configured in web.xml
+ {
+ registration.setInitParameter("remote", "false");
+ registration.setAsyncSupported(true);
+ registration.addMappingForUrlPatterns(
EnumSet.of(DispatcherType.ERROR,DispatcherType.ASYNC,DispatcherType.FORWARD,DispatcherType.INCLUDE,DispatcherType.REQUEST),
true,
- new String[]{"/dump/*","/dispatch/*","*.dump"});
- */
+ new String[]{"/*"});
+ }
}
public void contextDestroyed(ServletContextEvent sce)
diff --git a/test-jetty-webapp/src/main/webapp/META-INF/MANIFEST.MF b/test-jetty-webapp/src/main/webapp/META-INF/MANIFEST.MF
index cc61b1a..cfe644d 100644
--- a/test-jetty-webapp/src/main/webapp/META-INF/MANIFEST.MF
+++ b/test-jetty-webapp/src/main/webapp/META-INF/MANIFEST.MF
@@ -4,8 +4,8 @@
Bundle-SymbolicName: TestIt
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: testit.Activator
-Import-Package: javax.servlet,
- javax.servlet.http,
+Import-Package: javax.servlet;version="2.6",
+ javax.servlet.http;version="2.6",
javax.servlet.jsp,
javax.servlet.jsp.tagext
Require-Bundle: org.eclipse.jetty.client,
diff --git a/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml b/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml
index 3806a33..4eaf5e1 100644
--- a/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml
+++ b/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml
@@ -2,8 +2,9 @@
<web-app
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
- version="2.5">
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+ metadata-complete="false"
+ version="3.0">
<display-name>Test WebApp</display-name>
@@ -17,23 +18,11 @@
<listener-class>com.acme.TestListener</listener-class>
</listener>
- <filter>
- <filter-name>TestFilter</filter-name>
- <filter-class>com.acme.TestFilter</filter-class>
- <init-param>
- <param-name>remote</param-name>
- <param-value>false</param-value>
- </init-param>
- </filter>
- <filter-mapping>
- <filter-name>TestFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
-
<filter>
<filter-name>QoSFilter</filter-name>
<filter-class>org.eclipse.jetty.servlets.QoSFilter</filter-class>
+ <async-support>true</async-support>
<init-param>
<param-name>maxRequests</param-name>
<param-value>10000</param-value>
@@ -52,6 +41,7 @@
<filter>
<filter-name>MultiPart</filter-name>
<filter-class>org.eclipse.jetty.servlets.MultiPartFilter</filter-class>
+ <async-support>true</async-support>
<init-param>
<param-name>deleteFiles</param-name>
<param-value>true</param-value>
@@ -66,6 +56,7 @@
<filter>
<filter-name>GzipFilter</filter-name>
<filter-class>org.eclipse.jetty.servlets.IncludableGzipFilter</filter-class>
+ <async-support>true</async-support>
<init-param>
<param-name>bufferSize</param-name>
<param-value>8192</param-value>
@@ -119,6 +110,16 @@
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
-->
+ <servlet>
+ <servlet-name>Login</servlet-name>
+ <servlet-class>com.acme.LoginServlet</servlet-class>
+ <load-on-startup>1</load-on-startup>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>Login</servlet-name>
+ <url-pattern>/login/*</url-pattern>
+ </servlet-mapping>
<servlet>
@@ -135,6 +136,7 @@
<servlet>
<servlet-name>Dump</servlet-name>
<servlet-class>com.acme.Dump</servlet-class>
+ <async-support>true</async-support>
<load-on-startup>1</load-on-startup>
<run-as><role-name>admin</role-name></run-as>
</servlet>
@@ -170,6 +172,7 @@
<servlet>
<servlet-name>Dispatch</servlet-name>
<servlet-class>com.acme.DispatchServlet</servlet-class>
+ <async-support>true</async-support>
<load-on-startup>1</load-on-startup>
</servlet>
@@ -192,6 +195,7 @@
<servlet>
<servlet-name>Chat</servlet-name>
<servlet-class>com.acme.ChatServlet</servlet-class>
+ <async-support>true</async-support>
<load-on-startup>1</load-on-startup>
</servlet>
@@ -239,11 +243,12 @@
<servlet>
<servlet-name>TransparentProxy</servlet-name>
<servlet-class>org.eclipse.jetty.servlets.ProxyServlet$Transparent</servlet-class>
+ <async-support>true</async-support>
<init-param>
<param-name>Prefix</param-name><param-value>/javadoc-proxy</param-value>
</init-param>
<init-param>
- <param-name>ProxyTo</param-name><param-value>http://download.eclipse.org/jetty/stable-7/apidocs</param-value>
+ <param-name>ProxyTo</param-name><param-value>http://download.eclipse.org/jetty/stable-8/apidocs</param-value>
</init-param>
<init-param>
<param-name>HostHeader</param-name><param-value>download.eclipse.org</param-value>
@@ -271,6 +276,18 @@
<location>/error404.html</location>
</error-page>
+
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>Rego2</web-resource-name>
+ <url-pattern>/rego2/*</url-pattern>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>server-administrator</role-name>
+ </auth-constraint>
+ </security-constraint>
+
<security-constraint>
<web-resource-collection>
<web-resource-name>Auth2</web-resource-name>
diff --git a/test-jetty-webapp/src/main/webapp/auth.html b/test-jetty-webapp/src/main/webapp/auth.html
index 1b1de11..4b67966 100644
--- a/test-jetty-webapp/src/main/webapp/auth.html
+++ b/test-jetty-webapp/src/main/webapp/auth.html
@@ -18,6 +18,12 @@
<li><a href="dump/auth/info">dump/auth/*</a> - Authenticated any user</li>
<li><a href="dump/auth/admin/info">dump/auth/admin/*</a> - Authenticated admin role (<a href="session/?Action=Invalidate">click</a> to invalidate session)</li>
<li><a href="dump/auth/ssl/info">dump/auth/ssl/*</a> - Confidential</li>
+<li><a href="rego/info">rego/info/*</a> - Authenticated admin role from programmatic security (<a href="session/?Action=Invalidate">click</a> to invalidate session)</li>
+<li><a href="rego2/info">rego2/info/*</a> - Authenticated servlet-administrator role from programmatic security (login as admin/admin, <a href="session/?Action=Invalidate">click</a> to invalidate session)</li>
+<li><a href="login?action=login">login</a> - Programmatically login as the user jetty/jetty</li>
+<li><a href="login?action=x">check login status</a> - Check the request's login status</li>
+<li><a href="login?action=logout">logout</a> - Programmatically logout the logged in user</li>
+<li><a href="login?action=wrong">incorrect login</a> - Programmatically login with incorrect credentials</li>
</ul>
<p/>
<p>
diff --git a/test-jetty-webapp/src/main/webapp/index.html b/test-jetty-webapp/src/main/webapp/index.html
index 74ef907..630bc1d 100644
--- a/test-jetty-webapp/src/main/webapp/index.html
+++ b/test-jetty-webapp/src/main/webapp/index.html
@@ -11,9 +11,9 @@
</HEAD>
<BODY>
<A HREF="http://jetty.eclipse.org"><IMG SRC="jetty_banner.gif"></A>
-<h1>Welcome to Jetty 7</h1>
+<h1>Welcome to Jetty 8</h1>
<p>
-This is the Test webapp for the Jetty 7 HTTP Server and Servlet Container.
+This is the Test webapp for the Jetty 8 HTTP Server and Servlet Container.
For more information about Jetty, please visit our
<a href="http://www.eclipse.org/jetty">website</a>
or <a href="http://wiki.eclipse.org/Jetty">wiki</a> or see the bundled <a href="javadoc/">javadoc</a>.<br/>
diff --git a/test-jetty-webapp/src/main/webapp/remote.html b/test-jetty-webapp/src/main/webapp/remote.html
index b759568..3f52269 100644
--- a/test-jetty-webapp/src/main/webapp/remote.html
+++ b/test-jetty-webapp/src/main/webapp/remote.html
@@ -6,9 +6,9 @@
</HEAD>
<BODY>
<A HREF="http://www.eclipse.org/jetty"><IMG SRC="jetty_banner.gif"></A>
-<h1>Welcome to Jetty 7 - REMOTE ACCESS!!</h1>
+<h1>Welcome to Jetty 8 - REMOTE ACCESS!!</h1>
<p>
-This is the Test webapp for the Jetty 7 HTTP Server and Servlet Container.
+This is the Test webapp for the Jetty 8 HTTP Server and Servlet Container.
For more information about Jetty, please visit our
<a href="http://www.eclipse.org/jetty">website</a>
or <a href="http://www.eclipse.org/jetty/documentation/">documentation</a>.
@@ -21,12 +21,11 @@
is displayed because you have accessed this context from a non local IP address.
</p>
<p>
-You can disable the remote address checking by editing contexts/test.d/override-web.xml and changing the
-"remote" init parameter to true for the TestFilter.
+You can disable the remote address checking by editing contexts/test.d/override-web.xml, uncommenting the definition of the TestFilter, and changing the
+"remote" init parameter to "true".
</p>
<p>
-This webapp is deployed in $JETTY_HOME/webapp/test and configured by $JETTY_HOME/contexts/test.xml
-and $JETTY_HOME/contexts/test.d/override-web.xml
+This webapp is deployed in $JETTY_HOME/webapp/test.war and configured by $JETTY_HOME/contexts/test.xml and $JETTY_HOME/contexts/test.d/override-web.xml
</p>
</BODY>
diff --git a/tests/pom.xml b/tests/pom.xml
index 22ec0b4..ecb152d 100644
--- a/tests/pom.xml
+++ b/tests/pom.xml
@@ -21,7 +21,7 @@
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-project</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>tests-parent</artifactId>
diff --git a/tests/test-integration/pom.xml b/tests/test-integration/pom.xml
index bc60242..76320e1 100644
--- a/tests/test-integration/pom.xml
+++ b/tests/test-integration/pom.xml
@@ -20,7 +20,7 @@
<parent>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>tests-parent</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>test-integration</artifactId>
diff --git a/tests/test-loginservice/pom.xml b/tests/test-loginservice/pom.xml
index 771c9f1..af040e0 100644
--- a/tests/test-loginservice/pom.xml
+++ b/tests/test-loginservice/pom.xml
@@ -21,7 +21,7 @@
<parent>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>tests-parent</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<artifactId>test-loginservice</artifactId>
<name>Jetty Tests :: Login Service</name>
diff --git a/tests/test-sessions/pom.xml b/tests/test-sessions/pom.xml
index bfd1159..4a771d4 100644
--- a/tests/test-sessions/pom.xml
+++ b/tests/test-sessions/pom.xml
@@ -21,7 +21,7 @@
<parent>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>tests-parent</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<artifactId>test-sessions-parent</artifactId>
<name>Jetty Tests :: Sessions :: Parent</name>
diff --git a/tests/test-sessions/test-hash-sessions/pom.xml b/tests/test-sessions/test-hash-sessions/pom.xml
index ed726c9..8b0ff30 100644
--- a/tests/test-sessions/test-hash-sessions/pom.xml
+++ b/tests/test-sessions/test-hash-sessions/pom.xml
@@ -21,7 +21,7 @@
<parent>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>test-sessions-parent</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<artifactId>test-hash-sessions</artifactId>
<name>Jetty Tests :: Sessions :: Hash</name>
diff --git a/tests/test-sessions/test-jdbc-sessions/pom.xml b/tests/test-sessions/test-jdbc-sessions/pom.xml
index 9364836..5cdfe5e 100644
--- a/tests/test-sessions/test-jdbc-sessions/pom.xml
+++ b/tests/test-sessions/test-jdbc-sessions/pom.xml
@@ -21,7 +21,7 @@
<parent>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>test-sessions-parent</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<artifactId>test-jdbc-sessions</artifactId>
<name>Jetty Tests :: Sessions :: JDBC</name>
diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java
new file mode 100644
index 0000000..2c83878
--- /dev/null
+++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java
@@ -0,0 +1,192 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.session;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.ContentExchange;
+import org.eclipse.jetty.http.HttpMethods;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.junit.Test;
+import org.junit.Ignore;
+
+/**
+ * SaveIntervalTest
+ *
+ * Checks to see that potentially stale sessions that have not
+ * changed are not always reloaded from the datase.
+ *
+ * This test is Ignored because it takes a little while to run.
+ *
+ */
+public class SaveIntervalTest
+{
+ public static int INACTIVE = 90; //sec
+ public static int SCAVENGE = 100; //sec
+ public static int SAVE = 10; //sec
+
+
+ @Ignore
+ @Test
+ public void testSaveInterval() throws Exception
+ {
+ AbstractTestServer server = new JdbcTestServer(0,INACTIVE,SCAVENGE);
+
+ ServletContextHandler ctxA = server.addContext("/mod");
+ ServletHolder holder = new ServletHolder();
+ TestSaveIntervalServlet servlet = new TestSaveIntervalServlet();
+ holder.setServlet(servlet);
+ ctxA.addServlet(holder, "/test");
+ ((JDBCSessionManager)ctxA.getSessionHandler().getSessionManager()).setSaveInterval(SAVE);
+ server.start();
+ int port=server.getPort();
+ try
+ {
+ HttpClient client = new HttpClient();
+ client.start();
+ try
+ {
+ // Perform a request to create a session
+ ContentExchange exchange1 = new ContentExchange(true);
+ exchange1.setMethod(HttpMethods.GET);
+ exchange1.setURL("http://localhost:" + port + "/mod/test?action=create");
+ client.send(exchange1);
+ exchange1.waitForDone();
+ assertEquals(HttpServletResponse.SC_OK,exchange1.getResponseStatus());
+ String sessionCookie = exchange1.getResponseFields().getStringField("Set-Cookie");
+ assertTrue(sessionCookie != null);
+ // Mangle the cookie, replacing Path with $Path, etc.
+ sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
+ long lastSaved = ((JDBCSessionManager.Session)servlet._session).getLastSaved();
+
+
+ //do another request to change the session attribute
+ ContentExchange exchange2 = new ContentExchange(true);
+ exchange2.setMethod(HttpMethods.GET);
+ exchange2.setURL("http://localhost:" + port + "/mod/test?action=set");
+ exchange2.getRequestFields().add("Cookie", sessionCookie);
+ client.send(exchange2);
+ exchange2.waitForDone();
+ assertEquals(HttpServletResponse.SC_OK,exchange2.getResponseStatus());
+ long tmp = ((JDBCSessionManager.Session)servlet._session).getLastSaved();
+ assertNotEquals(lastSaved, tmp); //set of attribute will cause save to db
+ lastSaved = tmp;
+
+ //do nothing for just a bit longer than the save interval to ensure
+ //session will be checked against database on next request
+ Thread.currentThread().sleep((SAVE+2)*1000);
+
+
+ //do another request to access the session, this will cause session to be initially
+ //checked against db. On exit of request, the access time will need updating, so the
+ //session will be saved to db.
+ ContentExchange exchange3 = new ContentExchange(true);
+ exchange3.setMethod(HttpMethods.GET);
+ exchange3.setURL("http://localhost:" + port + "/mod/test?action=tickle");
+ exchange3.getRequestFields().add("Cookie", sessionCookie);
+ client.send(exchange3);
+ exchange3.waitForDone();
+ assertEquals(HttpServletResponse.SC_OK,exchange3.getResponseStatus());
+ tmp = ((JDBCSessionManager.Session)servlet._session).getLastSaved();
+ assertNotEquals(lastSaved, tmp);
+ lastSaved = tmp;
+
+ //wait a little and do another request to access the session
+ Thread.currentThread().sleep((SAVE/2)*1000);
+
+ //do another request to access the session. This time, the save interval has not
+ //expired, so we should NOT see a debug trace of loading stale session. Nor should
+ //the exit of the request cause a save of the updated access time.
+ ContentExchange exchange4 = new ContentExchange(true);
+ exchange4.setMethod(HttpMethods.GET);
+ exchange4.setURL("http://localhost:" + port + "/mod/test?action=tickle");
+ exchange4.getRequestFields().add("Cookie", sessionCookie);
+ client.send(exchange3);
+ exchange4.waitForDone();
+ assertEquals(HttpServletResponse.SC_OK,exchange4.getResponseStatus());
+ tmp = ((JDBCSessionManager.Session)servlet._session).getLastSaved();
+ assertEquals(lastSaved, tmp); //the save interval did not expire, so update to the access time will not have been persisted
+ }
+ finally
+ {
+ client.stop();
+ }
+ }
+ finally
+ {
+ server.stop();
+ }
+ }
+
+ public static class TestSaveIntervalServlet extends HttpServlet
+ {
+ public HttpSession _session;
+
+
+ @Override
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ String action = request.getParameter("action");
+
+
+ if ("create".equals(action))
+ {
+ HttpSession session = request.getSession(true);
+ System.err.println("CREATE: Session id="+session.getId());
+ _session = session;
+ return;
+ }
+
+ if ("set".equals(action))
+ {
+ HttpSession session = request.getSession(false);
+ if (session == null)
+ throw new ServletException("Session is null for action=change");
+
+ System.err.println("SET: Session id="+session.getId());
+ session.setAttribute("aaa", "12345");
+ assertEquals(_session.getId(), session.getId());
+ return;
+ }
+
+ if ("tickle".equals(action))
+ {
+ HttpSession session = request.getSession(false);
+ if (session == null)
+ throw new ServletException("Session does not exist");
+ System.err.println("TICKLE: Session id="+session.getId());
+ assertEquals(_session.getId(), session.getId());
+ return;
+ }
+ }
+ }
+
+}
diff --git a/tests/test-sessions/test-mongodb-sessions/pom.xml b/tests/test-sessions/test-mongodb-sessions/pom.xml
index 7fa95c6..f98a290 100644
--- a/tests/test-sessions/test-mongodb-sessions/pom.xml
+++ b/tests/test-sessions/test-mongodb-sessions/pom.xml
@@ -21,7 +21,7 @@
<parent>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>test-sessions-parent</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<artifactId>test-mongodb-sessions</artifactId>
<name>Jetty Tests :: Sessions :: Mongo</name>
diff --git a/tests/test-sessions/test-sessions-common/pom.xml b/tests/test-sessions/test-sessions-common/pom.xml
index 7e95511..980af41 100644
--- a/tests/test-sessions/test-sessions-common/pom.xml
+++ b/tests/test-sessions/test-sessions-common/pom.xml
@@ -21,7 +21,7 @@
<parent>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>test-sessions-parent</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<artifactId>test-sessions-common</artifactId>
<name>Jetty Tests :: Sessions :: Common</name>
diff --git a/tests/test-webapps/pom.xml b/tests/test-webapps/pom.xml
index 649dd7a..af4877c 100644
--- a/tests/test-webapps/pom.xml
+++ b/tests/test-webapps/pom.xml
@@ -21,7 +21,7 @@
<parent>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>tests-parent</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<artifactId>test-webapps-parent</artifactId>
<name>Jetty Tests :: WebApps :: Parent</name>
diff --git a/tests/test-webapps/test-webapp-rfc2616/pom.xml b/tests/test-webapps/test-webapp-rfc2616/pom.xml
index 7ba60af..92f4b3c 100644
--- a/tests/test-webapps/test-webapp-rfc2616/pom.xml
+++ b/tests/test-webapps/test-webapp-rfc2616/pom.xml
@@ -21,7 +21,7 @@
<parent>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>test-webapps-parent</artifactId>
- <version>7.6.19-SNAPSHOT</version>
+ <version>8.1.19-SNAPSHOT</version>
</parent>
<artifactId>test-webapp-rfc2616</artifactId>
<name>Jetty Tests :: WebApp :: RFC2616</name>