diff options
270 files changed, 11703 insertions, 5355 deletions
diff --git a/.gitignore b/.gitignore index 12ab1b6945..815d296305 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,4 @@ target/ #maven *.versionsBackup +*.releaseBackup diff --git a/VERSION.txt b/VERSION.txt index 7f2af82283..447dfa37a0 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1,10 +1,10 @@ -jetty-7.6.8-SNAPSHOT +jetty-8.1.8-SNAPSHOT -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 @@ -37,7 +37,7 @@ jetty-7.6.6.v20120903 - 03 September 2012 + 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 @@ -55,15 +55,20 @@ jetty-7.6.5.v20120716 - 16 July 2012 + 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 @@ -72,9 +77,10 @@ jetty-7.6.4.v20120524 - 24 May 2012 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 @@ -95,14 +101,18 @@ jetty-7.6.4.v20120524 - 24 May 2012 + 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 @@ -121,13 +131,19 @@ jetty-7.6.3.v20120416 - 16 April 2012 + 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 @@ -144,11 +160,12 @@ jetty-7.6.2.v20120308 - 08 March 2012 + 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 @@ -162,12 +179,98 @@ jetty-7.6.1.v20120215 - 15 February 2012 + 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 @@ -274,6 +377,22 @@ jetty-7.6.0.RC0 - 29 November 2011 + 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 @@ -289,6 +408,62 @@ jetty-7.5.4.v20111024 - 24 October 2011 + 361655 ExecutorThreadPool.isLowOnThreads() returns wrong value. + JETTY-1444 start threadpool before selector manager +jetty-8.0.3.v20111011 - 11 October 2011 + + 348978 migrate jetty-http-spi + + 358649 StdErrLog system properties for package/class logging LEVEL. + +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 + + 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() + + 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 + + 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-7.5.3.v20111011 - 11 October 2011 + 348978 migrate jetty-http-spi + 358649 StdErrLog system properties for package/class logging LEVEL. @@ -304,6 +479,7 @@ jetty-7.5.2.v20111006 - 06 October 2011 + 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 @@ -343,6 +519,18 @@ jetty-7.5.2.v20111006 - 06 October 2011 + 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 @@ -355,6 +543,12 @@ jetty-7.5.1.v20110908 - 08 September 2011 + 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 @@ -389,6 +583,19 @@ jetty-7.5.0.RC1 - 19 August 2011 + 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 @@ -571,6 +778,26 @@ jetty-7.4.0.RC0 + 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 @@ -596,9 +823,7 @@ jetty-7.3.1.v20110307 - 07 March 2011 + 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) @@ -784,7 +1009,6 @@ jetty-7.2.0.RC0 - 01 October 2010 + 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 @@ -812,6 +1036,15 @@ jetty-7.1.6.v20100715 + 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 @@ -975,6 +1208,9 @@ jetty-7.1.0.RC0 - 27 April 2010 + 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 @@ -1123,6 +1359,11 @@ jetty-7.0.2.RC0 + 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 0000000000..19ebc74c58 --- /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.8-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 0000000000..75ee3f0dfd --- /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-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.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 0000000000..0b16297c15 --- /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-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.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 0000000000..8097ef02ad --- /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-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.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 0000000000..f92f7f661d --- /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 Binary files differnew file mode 100644 index 0000000000..d0fb8420c5 --- /dev/null +++ b/example-async-rest/async-rest-jar/src/main/resources/META-INF/resources/asyncrest/green.png 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 Binary files differnew file mode 100644 index 0000000000..f2a79a07fb --- /dev/null +++ b/example-async-rest/async-rest-jar/src/main/resources/META-INF/resources/asyncrest/red.png 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 0000000000..9876d9983b --- /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 0000000000..44fd96742d --- /dev/null +++ b/example-async-rest/async-rest-webapp/pom.xml @@ -0,0 +1,33 @@ +<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.8-SNAPSHOT</version> + </parent> + <modelVersion>4.0.0</modelVersion> + <groupId>org.eclipse.jetty.example-async-rest</groupId> + <artifactId>example-async-rest-webapp</artifactId> + <packaging>war</packaging> + <name>Example Async Rest :: Webapp</name> + <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 0000000000..5e9495128c --- /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 0000000000..8916bdb6af --- /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 0000000000..f92f7f661d --- /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 0000000000..667c14568d --- /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-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.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 0000000000..55c66e364c --- /dev/null +++ b/example-async-rest/pom.xml @@ -0,0 +1,16 @@ +<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.8-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> + <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 2ea9a89d38..f2530604b9 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.8-SNAPSHOT</version> + <version>8.1.8-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 2848784896..f0c4019c23 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 @@ -148,7 +148,7 @@ public class LikeJettyXml 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 50272a8ac8..6b85c73082 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 @@ public class ManyContexts 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 7fd8f444a6..5f793eaea6 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 @@ public class OneWebApp 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 @@ public class OneWebApp 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 ab289a0a5a..1e2b5e5abd 100644 --- a/jetty-aggregate/jetty-all-server/pom.xml +++ b/jetty-aggregate/jetty-all-server/pom.xml @@ -1,9 +1,8 @@ -<?xml version="1.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.aggregate</groupId> <artifactId>jetty-aggregate-project</artifactId> - <version>7.6.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>jetty-all-server</artifactId> @@ -78,10 +77,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 9235332dfb..956c31c263 100644 --- a/jetty-aggregate/jetty-all/pom.xml +++ b/jetty-aggregate/jetty-all/pom.xml @@ -1,9 +1,8 @@ -<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <parent> <groupId>org.eclipse.jetty.aggregate</groupId> <artifactId>jetty-aggregate-project</artifactId> - <version>7.6.8-SNAPSHOT</version> + <version>8.1.8-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 1be391592e..7a03132240 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.8-SNAPSHOT</version> + <version>8.1.8-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 4d3af29430..39c5b6c9da 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.8-SNAPSHOT</version> + <version>8.1.8-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 4464aff513..56dac6528a 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.8-SNAPSHOT</version> + <version>8.1.8-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 47f24feeed..ce290ac18c 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.8-SNAPSHOT</version> + <version>8.1.8-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 c8d6cb0fa6..3f1f0c9d2a 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.8-SNAPSHOT</version> + <version>8.1.8-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 2cb09de092..8b64c1d502 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.8-SNAPSHOT</version> + <version>8.1.8-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 c52f3100a4..33c7f898da 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.8-SNAPSHOT</version> + <version>8.1.8-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 d7d35e4a08..de7f32bb3e 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>jetty-ajp</artifactId> @@ -21,6 +21,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 c21cf4a9e0..baa1b0b774 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>jetty-annotations</artifactId> @@ -14,6 +14,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> @@ -25,7 +42,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 0000000000..7aa719d7ca --- /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 2367709243..95c5b4145d 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.ArrayList; 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 @@ import org.eclipse.jetty.webapp.WebAppContext; 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 042fe21a12..e927dd0294 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,33 @@ 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 java.util.Set; + +import javax.servlet.ServletContainerInitializer; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +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.Configuration; import org.eclipse.jetty.webapp.AbstractConfiguration; +import org.eclipse.jetty.webapp.Descriptor; +import org.eclipse.jetty.webapp.DiscoveredAnnotation; +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 +53,461 @@ import org.eclipse.jetty.webapp.WebAppContext; */ 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"; + + + protected List<DiscoverableAnnotationHandler> _discoverableAnnotationHandlers = new ArrayList<DiscoverableAnnotationHandler>(); + protected ClassInheritanceHandler _classInheritanceHandler; + protected List<ContainerInitializerAnnotationHandler> _containerInitializerAnnotationHandlers = new ArrayList<ContainerInitializerAnnotationHandler>(); + + + public void preConfigure(final WebAppContext context) throws Exception + { + } + + + + /** + * @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 listener which will call the servletcontainerinitializers when appropriate + ServletContainerInitializerListener listener = new ServletContainerInitializerListener(); + listener.setWebAppContext(context); + context.addEventListener(listener); + } + + + + /** + * 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 70df00e1cf..9a4d63eb28 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 @@ public class AnnotationDecorator implements Decorator _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 bd362f033c..537638778d 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 @@ -54,11 +54,8 @@ public class AnnotationParser { private static final Logger LOG = Log.getLogger(AnnotationParser.class); - protected List<String> _parsedClassNames = new ArrayList<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<String> _parsedClassNames = new ArrayList<String>(); + protected List<Handler> _handlers = new ArrayList<Handler>(); public static String normalize (String name) { @@ -169,37 +166,122 @@ public class AnnotationParser - 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; @@ -308,10 +390,13 @@ public class AnnotationParser 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); + } } } @@ -324,12 +409,13 @@ public class AnnotationParser 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); } } } @@ -355,12 +441,13 @@ public class AnnotationParser { 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); } } } @@ -387,12 +474,13 @@ public class AnnotationParser 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); } } } @@ -408,46 +496,130 @@ public class AnnotationParser * 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 { @@ -469,6 +641,16 @@ public class AnnotationParser } } + + + /** + * 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 { @@ -495,6 +677,15 @@ public class AnnotationParser } } + + + /** + * Parse the given classes + * + * @param classNames + * @param resolver + * @throws Exception + */ public void parse (String[] classNames, ClassNameResolver resolver) throws Exception { @@ -504,6 +695,14 @@ public class AnnotationParser 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 { @@ -522,6 +721,14 @@ public class AnnotationParser } } + + /** + * Parse all classes in a directory + * + * @param dir + * @param resolver + * @throws Exception + */ public void parse (Resource dir, ClassNameResolver resolver) throws Exception { @@ -557,8 +764,9 @@ public class AnnotationParser /** - * 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 @@ -607,7 +815,8 @@ public class AnnotationParser /** - * 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 @@ -649,6 +858,12 @@ public class AnnotationParser scanner.scan(null, uris, true); } + /** + * Parse a particular resource + * @param uri + * @param resolver + * @throws Exception + */ public void parse (URI uri, final ClassNameResolver resolver) throws Exception { @@ -658,10 +873,19 @@ public class AnnotationParser parse(uris, resolver); } - private void scanClass (InputStream is) + + + /** + * Use ASM on a class + * + * @param is + * @throws IOException + */ + protected void scanClass (InputStream is) throws IOException { ClassReader reader = new ClassReader(is); reader.accept(new MyClassVisitor(), ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES); } } + 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 79f111a0ab..8242b08d74 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 @@ public class ClassInheritanceHandler implements ClassHandler 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 0000000000..e84188e640 --- /dev/null +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ContainerInitializerAnnotationHandler.java @@ -0,0 +1,83 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + + +package org.eclipse.jetty.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 0000000000..c3abf5138f --- /dev/null +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/MultiPartConfigAnnotationHandler.java @@ -0,0 +1,95 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.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 ada84129c0..898ac4c86a 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 @@ public class RunAsAnnotationHandler extends AbstractIntrospectableAnnotationHand { 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 0000000000..641859a521 --- /dev/null +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletContainerInitializerListener.java @@ -0,0 +1,142 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.annotations; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +import org.eclipse.jetty.annotations.AnnotationConfiguration; +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.webapp.WebAppContext; + +/** + * ServletContainerInitializerListener + * + * + */ +public class ServletContainerInitializerListener implements ServletContextListener +{ + private static final Logger LOG = Log.getLogger(ServletContainerInitializerListener.class); + protected WebAppContext _context = null; + + + public void setWebAppContext (WebAppContext context) + { + _context = context; + } + + /** + * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent) + */ + public void contextInitialized(ServletContextEvent sce) + { + 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); + } + } + + + /** + * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent) + */ + public void contextDestroyed(ServletContextEvent sce) + { + + } + +} 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 0000000000..bfbef9bf9c --- /dev/null +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletSecurityAnnotationHandler.java @@ -0,0 +1,196 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.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 40ae236c7b..e89ed6d15b 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 @@ public class Util 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 0000000000..f3824a9025 --- /dev/null +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebFilterAnnotation.java @@ -0,0 +1,223 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.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 0000000000..1be7b2c9ec --- /dev/null +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebFilterAnnotationHandler.java @@ -0,0 +1,74 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.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 0000000000..cb48ba4629 --- /dev/null +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotation.java @@ -0,0 +1,96 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.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 0000000000..2dd1c16bd1 --- /dev/null +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebListenerAnnotationHandler.java @@ -0,0 +1,71 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.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 0000000000..62965baeed --- /dev/null +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotation.java @@ -0,0 +1,227 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.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 0000000000..0f74eb906f --- /dev/null +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotationHandler.java @@ -0,0 +1,85 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.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 08397c2861..3268c46c57 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.PostConstruct; 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 23fbf78d17..097e6c40ec 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 @@ package org.eclipse.jetty.annotations; 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 86e6d03ab3..f607b25ac3 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.Resource; 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 0000000000..dd0e2e9cb9 --- /dev/null +++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationConfiguration.java @@ -0,0 +1,58 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.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 b5928edecd..4deb5408f5 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 @@ public class TestAnnotationInheritance { annotatedMethods.add(className+"."+methodName); } + + @Override + public String getAnnotationName() + { + return "org.eclipse.jetty.annotations.Sample"; + } } @After @@ -82,7 +91,7 @@ public class TestAnnotationInheritance 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 @@ public class TestAnnotationInheritance }); 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 ec74005170..ec00c9b905 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 @@ -41,6 +41,9 @@ public class TestAnnotationParser { 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) { @@ -81,9 +84,15 @@ public class TestAnnotationParser 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 () @@ -140,9 +149,17 @@ public class TestAnnotationParser System.err.println(anv.toString()); } } + + @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); } } 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 0000000000..42111880d7 --- /dev/null +++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestSecurityAnnotationConversions.java @@ -0,0 +1,334 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.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 2222cb3dd8..6915448835 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 @@ import org.junit.Test; */ 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 dbbf90f26e..34091b1ac9 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> diff --git a/jetty-continuation/pom.xml b/jetty-continuation/pom.xml index b5ad693666..dcaada141d 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>jetty-continuation</artifactId> @@ -24,7 +24,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 314c6c84b8..fa1a549fc5 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 @@ public class ContinuationSupport __servlet3=servlet3Support; __newServlet3Continuation=s3cc; } - + boolean jetty6Support=false; Constructor<? extends Continuation>j6cc=null; try @@ -80,7 +79,7 @@ public class ContinuationSupport __jetty6=jetty6Support; __newJetty6Continuation=j6cc; } - + Class<?> waiting=null; try { @@ -98,12 +97,12 @@ public class ContinuationSupport /* ------------------------------------------------------------ */ /** * 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 @@ public class ContinuationSupport 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/FauxContinuation.java b/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/FauxContinuation.java index 72fef5fb36..3c8dc73aa8 100644 --- a/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/FauxContinuation.java +++ b/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/FauxContinuation.java @@ -505,4 +505,4 @@ class FauxContinuation implements FilteredContinuation throw new IllegalStateException("!suspended"); } -}
\ No newline at end of file +} 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 76fcb445e1..f30eb24bd7 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 @@ package org.eclipse.jetty.continuation; 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 @@ import javax.servlet.ServletResponseWrapper; /* ------------------------------------------------------------ */ /** * 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 @@ public class Servlet3Continuation implements Continuation // 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 @@ public class Servlet3Continuation implements Continuation listener.onTimeout(Servlet3Continuation.this); } }; - + if (_context!=null) _context.addListener(wrapped); else @@ -188,7 +187,7 @@ public class Servlet3Continuation implements Continuation _expired=false; _context=_request.startAsync(); _context.setTimeout(_timeoutMs); - + for (AsyncListener listener:_listeners) _context.addListener(listener); _listeners.clear(); @@ -201,7 +200,7 @@ public class Servlet3Continuation implements Continuation _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 880e2f2881..ba9f7d88c6 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>jetty-deploy</artifactId> @@ -24,7 +24,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 dd25f7ff34..1b3fd66dcd 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 c8f77aab33..a64f2f1e24 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <artifactId>jetty-distribution</artifactId> <name>Jetty :: Distribution Assemblies</name> @@ -144,7 +144,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 550390edce..16371bea30 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 @@ NO_START=0 ################################################## # 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 @@ if [ "$JETTY_HOME" = "" ] ; then /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 @@ case "$ACTION" in 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 7f6b549463..5442d2fe14 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 # ======================== @@ -136,7 +136,7 @@ shift ################################################## # Read any configuration files ################################################## -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 @@ -192,13 +192,13 @@ if [ -z "$JETTY_HOME" ] ; then "/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 0ada4c0205..dfc2783db5 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 @@ OPTIONS=Server,jsp,jmx,resources,websocket,ext #----------------------------------------------------------- #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 29a49593b6..ca4c37bbae 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.8-SNAPSHOT</version> + <version>8.1.8-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 bf838f1b91..0853013f59 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>jetty-http</artifactId> @@ -41,7 +41,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/HttpFields.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java index a0a117c661..a4f88ff3ea 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.text.SimpleDateFormat; 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 @@ public class HttpFields /* -------------------------------------------------------------- */ /** + * 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 @@ public class HttpFields /** * 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 @@ public class HttpFields 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 @@ public class HttpFields return ("[" + getName() + "=" + _value + (_next == null ? "" : "->") + "]"); } } - } 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 72c5ad298f..3324ddfeee 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 @@ public class PathMap extends HashMap implements Externalizable @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 class PathMap extends HashMap implements Externalizable */ 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) @@ -460,6 +477,9 @@ public class PathMap extends HashMap implements Externalizable */ 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 ff361ac80a..f9f27d186d 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.SimpleBuffers; 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 @@ public class HttpGeneratorClientTest * 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/PathMapTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/PathMapTest.java index 193c25df98..8587389be2 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 @@ public class PathMapTest extends TestCase p.put("*.gz", "7"); p.put("/", "8"); p.put("/XXX:/YYY", "9"); + p.put("", "10"); String[][] tests = { { "/abs/path", "1"}, @@ -73,7 +74,7 @@ public class PathMapTest extends TestCase 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,8 @@ public class PathMapTest extends TestCase 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()); } /** diff --git a/jetty-io/pom.xml b/jetty-io/pom.xml index c08d403fba..3bc51cf05a 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.8-SNAPSHOT</version> + <version>8.1.8-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 24dac02c4b..748016a634 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 @@ package org.eclipse.jetty.io; 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 abstract class AbstractBuffer implements Buffer 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 abstract class AbstractBuffer implements Buffer } /* ------------------------------------------------------------ */ + 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 c6ff99ee7c..ec6d23d77f 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 @@ package org.eclipse.jetty.io; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.charset.Charset; /** @@ -365,7 +366,10 @@ public interface Buffer extends Cloneable /* ------------------------------------------------------------ */ 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 8c03634788..c24723dc90 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 @@ public class BufferUtil { 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-servlet/src/main/java/org/eclipse/jetty/servlet/api/ServletRegistration.java b/jetty-io/src/main/java/org/eclipse/jetty/io/UncheckedIOException.java index c378c7ddf0..0731111ef1 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/api/ServletRegistration.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/UncheckedIOException.java @@ -16,24 +16,33 @@ // ======================================================================== // -package org.eclipse.jetty.servlet.api; -import java.util.Collection; -import java.util.Set; +package org.eclipse.jetty.io; -public interface ServletRegistration +/* ------------------------------------------------------------ */ +/** + * 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 Set<String> addMapping(String... urlPatterns); - - public Collection<String> getMappings(); - - public String getRunAsRole(); + public UncheckedIOException() + { + super(); + } - interface Dynamic extends ServletRegistration, Registration.Dynamic + public UncheckedIOException(String message) { - public void setLoadOnStartup(int loadOnStartup); + super(message); + } - public void setRunAsRole(String roleName); + public UncheckedIOException(Throwable cause) + { + super(cause); } + public UncheckedIOException(String message, Throwable cause) + { + super(message,cause); + } } diff --git a/jetty-jaspi/pom.xml b/jetty-jaspi/pom.xml index c1a3a3cd0a..df5480889d 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>jetty-jaspi</artifactId> @@ -22,6 +22,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 c93bafa770..e28d4a17cc 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.ServerAuthConfig; 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.ServerAuthException; 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 @@ public class JaspiAuthenticator extends LoginAuthenticator } + /** + * @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 @@ public class JaspiAuthenticator extends LoginAuthenticator 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 f25203d741..832a2aab33 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 @@ public class SimpleAuthConfig implements ServerAuthConfig 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 6c6270280b..9bea1c5063 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.util.security.Password; 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 @@ public class FormAuthModule extends BaseAuthModule // 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 @@ public class FormAuthModule extends BaseAuthModule 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 @@ public class FormAuthModule extends BaseAuthModule 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 726fca4c09..71c4fbc3e2 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.8-SNAPSHOT</version> + <version>8.1.8-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 fea782ecc6..fc4276c442 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.8-SNAPSHOT</version> + <version>8.1.8-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 c422892319..c540b6617c 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>jetty-jsp</artifactId> @@ -16,13 +16,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> @@ -40,13 +40,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 1eea5ee00b..0cac1c02fd 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.8-SNAPSHOT</version> + <version>8.1.8-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 a043d69617..e86fc13d03 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <artifactId>jetty-nested</artifactId> <name>Jetty :: Nested</name> @@ -26,7 +26,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 4367d68330..9d32f0e643 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 @@ package org.eclipse.jetty.nested; 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.HttpFields; 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 fb50c9d260..694049d0d0 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>jetty-nosql</artifactId> @@ -28,7 +28,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 95a03e1cad..e2afe90046 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <modelVersion>4.0.0</modelVersion> @@ -93,28 +93,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, @@ -138,13 +137,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 20960b4bd4..0000000000 --- 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 6d10c98886..0000000000 --- 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 deleted file mode 100644 index b2061220f0..0000000000 --- a/jetty-osgi/jetty-osgi-boot-logback/pom.xml +++ /dev/null @@ -1,126 +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.8-SNAPSHOT</version> - <relativePath>../pom.xml</relativePath> - </parent> - <modelVersion>4.0.0</modelVersion> - <artifactId>jetty-osgi-boot-logback</artifactId> - <name>Jetty :: OSGi :: Boot Logback</name> - <description>Jetty OSGi Boot Logback bundle</description> - <properties> - <bundle-symbolic-name>${project.groupId}.boot.logback</bundle-symbolic-name> - </properties> - <dependencies> - <dependency> - <groupId>org.eclipse.jetty.osgi</groupId> - <artifactId>jetty-osgi-boot</artifactId> - <version>${project.version}</version> - <scope>provided</scope> - </dependency> - <dependency> - <groupId>org.eclipse.osgi</groupId> - <artifactId>org.eclipse.osgi</artifactId> - </dependency> - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-webapp</artifactId> - </dependency> - <dependency> - <groupId>org.eclipse.osgi</groupId> - <artifactId>org.eclipse.osgi.services</artifactId> - </dependency> - <dependency> - <groupId>org.slf4j</groupId> - <artifactId>slf4j-api</artifactId> - </dependency> - <dependency> - <groupId>org.slf4j</groupId> - <artifactId>jcl-over-slf4j</artifactId> - </dependency> - <dependency> - <groupId>org.slf4j</groupId> - <artifactId>log4j-over-slf4j</artifactId> - </dependency> - <dependency> - <groupId>ch.qos.logback</groupId> - <artifactId>logback-core</artifactId> - </dependency> - <dependency> - <groupId>ch.qos.logback</groupId> - <artifactId>logback-classic</artifactId> - </dependency> - </dependencies> - - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-jar-plugin</artifactId> - <executions> - <execution> - <id>artifact-jar</id> - <goals> - <goal>jar</goal> - </goals> - </execution> - <execution> - <id>test-jar</id> - <goals> - <goal>test-jar</goal> - </goals> - </execution> - </executions> - <configuration> - <archive> - <manifestFile>target/classes/META-INF/MANIFEST.MF</manifestFile> - </archive> - </configuration> - </plugin> - <plugin> - <groupId>org.apache.felix</groupId> - <artifactId>maven-bundle-plugin</artifactId> - <extensions>true</extensions> - <executions> - <execution> - <id>bundle-manifest</id> - <phase>process-classes</phase> - <goals> - <goal>manifest</goal> - </goals> - </execution> - </executions> - <configuration> - <instructions> - <Bundle-SymbolicName>org.eclipse.jetty.osgi.boot.logback;singleton:=true</Bundle-SymbolicName> - <Bundle-Name>Jetty-OSGi-Logback Integration</Bundle-Name> - <Fragment-Host>org.eclipse.jetty.osgi.boot</Fragment-Host> - <Import-Package> -ch.qos.logback.access.jetty;version="[0.9,1.1)";resolution:=optional, -ch.qos.logback.access.jetty.v7;version="[0.9,1.1)";resolution:=optional, -ch.qos.logback.*;version="[0.9,1.1)", -org.osgi.framework.*, -org.slf4j.*, -*;resolution:=optional - </Import-Package> - <Export-Package> -!org.eclipse.jetty.osgi.boot.logback.internal.*, -org.eclipse.jetty.osgi.boot.logback.*;version="${parsedVersion.osgiVersion}" - </Export-Package> - <_nouses>true</_nouses> - </instructions> - </configuration> - </plugin> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>findbugs-maven-plugin</artifactId> - <configuration> - <onlyAnalyze>org.eclipse.jetty.osgi.boot.logback.*</onlyAnalyze> - </configuration> - </plugin> - </plugins> - </build> - - -</project> diff --git a/jetty-osgi/jetty-osgi-boot-logback/src/main/java/org/eclipse/jetty/osgi/boot/logback/FragmentActivator.java b/jetty-osgi/jetty-osgi-boot-logback/src/main/java/org/eclipse/jetty/osgi/boot/logback/FragmentActivator.java deleted file mode 100644 index 49a57bc58c..0000000000 --- a/jetty-osgi/jetty-osgi-boot-logback/src/main/java/org/eclipse/jetty/osgi/boot/logback/FragmentActivator.java +++ /dev/null @@ -1,92 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== -// - -package org.eclipse.jetty.osgi.boot.logback; - -import java.io.File; -import java.util.Map; - -import org.eclipse.jetty.osgi.boot.internal.webapp.LibExtClassLoaderHelper; -import org.eclipse.jetty.osgi.boot.internal.webapp.OSGiWebappClassLoader; -import org.eclipse.jetty.osgi.boot.internal.webapp.LibExtClassLoaderHelper.IFilesInJettyHomeResourcesProcessor; -import org.eclipse.jetty.osgi.boot.logback.internal.LogbackInitializer; -import org.osgi.framework.BundleActivator; -import org.osgi.framework.BundleContext; - - -/** - * Pseudo fragment activator. - * Called by the main org.eclipse.jetty.osgi.boot bundle. - * Please note: this is not a real BundleActivator. Simply something called back by - * the host bundle. - * The fragment is in charge of placing a hook to configure logback - * when the files inside jettyhome/resources are parsed. - */ -public class FragmentActivator implements BundleActivator, IFilesInJettyHomeResourcesProcessor -{ - /** - * - */ - public void start(BundleContext context) throws Exception - { - LibExtClassLoaderHelper.registeredFilesInJettyHomeResourcesProcessors.add(this); - - //now let's make sure no log4j, no slf4j and no commons.logging - //get inserted as a library that is not an osgi library - OSGiWebappClassLoader.addClassThatIdentifiesAJarThatMustBeRejected("org.apache.commons.logging.Log"); - OSGiWebappClassLoader.addClassThatIdentifiesAJarThatMustBeRejected("org.apache.log4j.Logger"); - OSGiWebappClassLoader.addClassThatIdentifiesAJarThatMustBeRejected("org.slf4j.Logger"); - //OSGiWebappClassLoader.addClassThatIdentifiesAJarThatMustBeRejected(java.util.logging.Logger.class); - - } - - /** - * Called when this bundle is stopped so the Framework can perform the - * bundle-specific activities necessary to stop the bundle. In general, this - * method should undo the work that the <code>BundleActivator.start</code> - * method started. There should be no active threads that were started by - * this bundle when this bundle returns. A stopped bundle must not call any - * Framework objects. - * - * <p> - * This method must complete and return to its caller in a timely manner. - * - * @param context The execution context of the bundle being stopped. - * @throws Exception If this method throws an exception, the - * bundle is still marked as stopped, and the Framework will remove - * the bundle's listeners, unregister all services registered by the - * bundle, and release all services used by the bundle. - */ - public void stop(BundleContext context) throws Exception - { - LibExtClassLoaderHelper.registeredFilesInJettyHomeResourcesProcessors.remove(this); - } - - public void processFilesInResourcesFolder(File jettyHome, Map<String,File> files) - { - try - { - LogbackInitializer.processFilesInResourcesFolder(jettyHome, files); - } - catch (Throwable t) - { - t.printStackTrace(); - } - } - -} diff --git a/jetty-osgi/jetty-osgi-boot-logback/src/main/java/org/eclipse/jetty/osgi/boot/logback/internal/LogbackInitializer.java b/jetty-osgi/jetty-osgi-boot-logback/src/main/java/org/eclipse/jetty/osgi/boot/logback/internal/LogbackInitializer.java deleted file mode 100644 index bbab7c413d..0000000000 --- a/jetty-osgi/jetty-osgi-boot-logback/src/main/java/org/eclipse/jetty/osgi/boot/logback/internal/LogbackInitializer.java +++ /dev/null @@ -1,102 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== -// - -package org.eclipse.jetty.osgi.boot.logback.internal; - -import java.io.File; -import java.util.Map; - -import org.slf4j.LoggerFactory; - -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.joran.JoranConfigurator; -import ch.qos.logback.core.joran.JoranConfiguratorBase; -import ch.qos.logback.core.joran.spi.JoranException; -import ch.qos.logback.core.util.StatusPrinter; - -/** - * Setup logback eventually located in the config file inside jettyhome/resources - * All logback related code is done in this separate class for better debug - * and isolation when it does not load. - */ -public class LogbackInitializer { - - /** - * @return true when we are currently being run by the pde in development mode. - */ - private static boolean isPDEDevelopment() - { - String eclipseCommands = System.getProperty("eclipse.commands"); - // detect if we are being run from the pde: ie during development. - return eclipseCommands != null && eclipseCommands.indexOf("-dev") != -1 - && (eclipseCommands.indexOf("-dev\n") != -1 - || eclipseCommands.indexOf("-dev\r") != -1 - || eclipseCommands.indexOf("-dev ") != -1); - } - - - /** - * Follow the configuration for logback. - * unless the system propery was set in which case it - * was assume it was already setup. - */ - public static void processFilesInResourcesFolder(File jettyHome, Map<String,File> files) - { - String logbackConf = System.getProperty("logback.configurationFile"); - if (logbackConf != null) - { - File confFile = new File(logbackConf); - if (confFile.exists()) - { - //assume logback was configured by this one? - return; - } - } - - File logConf = isPDEDevelopment() ? files.get("logback-dev.xml") : null; - if (logConf == null) - { - logConf = files.get("logback-test.xml"); - } - if (logConf == null) - { - logConf = files.get("logback.xml"); - } - if (logConf == null) - { - return; - } - // assume SLF4J is bound to logback in the current environment - LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); - - try - { - JoranConfiguratorBase configurator = new JoranConfigurator(); - configurator.setContext(lc); - lc.reset(); - configurator.doConfigure(logConf.getAbsoluteFile().getAbsolutePath()); - } - catch (JoranException je) - { - je.printStackTrace(); - } - StatusPrinter.printIfErrorsOccured(lc); - - } - -} diff --git a/jetty-osgi/jetty-osgi-boot-warurl/pom.xml b/jetty-osgi/jetty-osgi-boot-warurl/pom.xml index 8ca64e39f2..92d97eb3aa 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <modelVersion>4.0.0</modelVersion> @@ -79,6 +79,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 b30ae7a464..0000000000 --- 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 849d308bc5..16e586b59a 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <modelVersion>4.0.0</modelVersion> @@ -13,6 +13,10 @@ <bundle-symbolic-name>${project.groupId}.boot</bundle-symbolic-name> </properties> <dependencies> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-annotations</artifactId> + </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-webapp</artifactId> @@ -114,10 +118,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, @@ -129,12 +135,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 0000000000..4005f9a397 --- /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-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.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 0000000000..2142253dc1 --- /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-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.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/internal/jsp/TldLocatableURLClassloader.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/jsp/TldLocatableURLClassloader.java index ad23673df3..7f6c6288ac 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 @@ public class TldLocatableURLClassloader extends URLClassLoader { 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/utils/internal/PackageAdminServiceTracker.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/utils/internal/PackageAdminServiceTracker.java index c386626441..7b5b86cc8d 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 @@ public class PackageAdminServiceTracker implements ServiceListener } } + diff --git a/jetty-osgi/jetty-osgi-equinoxtools/build.properties b/jetty-osgi/jetty-osgi-equinoxtools/build.properties deleted file mode 100644 index 05786b032b..0000000000 --- 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 4deba32c28..0000000000 --- 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 4d5acc96d7..0000000000 --- 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 deleted file mode 100644 index 84e8bf683d..0000000000 --- a/jetty-osgi/jetty-osgi-equinoxtools/pom.xml +++ /dev/null @@ -1,120 +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.8-SNAPSHOT</version> - <relativePath>../pom.xml</relativePath> - </parent> - <modelVersion>4.0.0</modelVersion> - <artifactId>jetty-osgi-equinoxtools</artifactId> - <name>Jetty :: OSGi :: Example Equinox Tools</name> - <description>Jetty OSGi Example Equinox Tools</description> - <properties> - <bundle-symbolic-name>${project.groupId}.equinoxtools</bundle-symbolic-name> - </properties> - <dependencies> - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-webapp</artifactId> - </dependency> - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-continuation</artifactId> - </dependency> - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-websocket</artifactId> - </dependency> - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-servlet</artifactId> - </dependency> - <dependency> - <groupId>org.eclipse.osgi</groupId> - <artifactId>org.eclipse.osgi</artifactId> - </dependency> - <dependency> - <groupId>org.eclipse.osgi</groupId> - <artifactId>org.eclipse.osgi.services</artifactId> - </dependency> - </dependencies> - - <build> - <plugins> - <plugin> - <artifactId>maven-antrun-plugin</artifactId> - <executions> - <execution> - <phase>process-resources</phase> - <configuration> - <tasks> - <copy todir="target/classes/equinoxconsole"> - <fileset dir="equinoxconsole" /> - </copy> - </tasks> - </configuration> - <goals> - <goal>run</goal> - </goals> - </execution> - </executions> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-jar-plugin</artifactId> - <executions> - <execution> - <id>artifact-jar</id> - <goals> - <goal>jar</goal> - </goals> - </execution> - <execution> - <id>test-jar</id> - <goals> - <goal>test-jar</goal> - </goals> - </execution> - </executions> - <configuration> - <archive> - <manifestFile>target/classes/META-INF/MANIFEST.MF</manifestFile> - </archive> - </configuration> - </plugin> - <plugin> - <groupId>org.apache.felix</groupId> - <artifactId>maven-bundle-plugin</artifactId> - <extensions>true</extensions> - <executions> - <execution> - <id>bundle-manifest</id> - <phase>process-classes</phase> - <goals> - <goal>manifest</goal> - </goals> - </execution> - </executions> - <configuration> - <instructions> - <Bundle-SymbolicName>org.eclipse.jetty.osgi.equinoxtools</Bundle-SymbolicName> - <Bundle-Name>Console</Bundle-Name> - <Bundle-Activator>org.eclipse.jetty.osgi.equinoxtools.WebEquinoxToolsActivator</Bundle-Activator> - <Export-Package>org.eclipse.jetty.osgi.equinoxtools;x-internal:=true;version="${parsedVersion.osgiVersion}", - org.eclipse.jetty.osgi.equinoxtools.console;x-internal:=true;version="${parsedVersion.osgiVersion}" - </Export-Package> - <_nouses>true</_nouses> - </instructions> - </configuration> - </plugin> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>findbugs-maven-plugin</artifactId> - <configuration> - <onlyAnalyze>org.eclipse.jetty.osgi.equinoxtools.*</onlyAnalyze> - </configuration> - </plugin> - </plugins> - </build> - -</project> diff --git a/jetty-osgi/jetty-osgi-equinoxtools/src/main/java/org/eclipse/jetty/osgi/equinoxtools/WebEquinoxToolsActivator.java b/jetty-osgi/jetty-osgi-equinoxtools/src/main/java/org/eclipse/jetty/osgi/equinoxtools/WebEquinoxToolsActivator.java deleted file mode 100644 index d4bc6d7013..0000000000 --- a/jetty-osgi/jetty-osgi-equinoxtools/src/main/java/org/eclipse/jetty/osgi/equinoxtools/WebEquinoxToolsActivator.java +++ /dev/null @@ -1,133 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== -// - -package org.eclipse.jetty.osgi.equinoxtools; - -import javax.servlet.ServletException; - -import org.eclipse.jetty.osgi.equinoxtools.console.EquinoxChattingSupport; -import org.eclipse.jetty.osgi.equinoxtools.console.EquinoxConsoleContinuationServlet; -import org.eclipse.jetty.osgi.equinoxtools.console.EquinoxConsoleSyncServlet; -import org.eclipse.jetty.osgi.equinoxtools.console.EquinoxConsoleWebSocketServlet; -import org.eclipse.jetty.osgi.equinoxtools.console.WebConsoleSession; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.log.Logger; -import org.eclipse.osgi.framework.console.ConsoleSession; -import org.osgi.framework.BundleActivator; -import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceReference; -import org.osgi.service.http.HttpService; -import org.osgi.service.http.NamespaceException; -import org.osgi.util.tracker.ServiceTracker; -import org.osgi.util.tracker.ServiceTrackerCustomizer; - -/** - * When started will register on the HttpService 3 servlets for 3 different styles of equinox consoles. - */ -public class WebEquinoxToolsActivator implements BundleActivator -{ - private static final Logger LOG = Log.getLogger(WebEquinoxToolsActivator.class); - - - private static BundleContext context; - public static BundleContext getContext() - { - return context; - } - - private HttpService _httpService; - private ServiceTracker _tracker; - private EquinoxChattingSupport _equinoxChattingSupport; - - - /* - * (non-Javadoc) - * - * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) - */ - public void start(BundleContext bundleContext) throws Exception - { - WebEquinoxToolsActivator.context = bundleContext; - - ServiceTrackerCustomizer httpServiceTrackerCustomizer = new ServiceTrackerCustomizer() - { - public void removedService(ServiceReference reference, Object service) - { - _httpService = null; - } - - public void modifiedService(ServiceReference reference, Object service) - { - _httpService = (HttpService)context.getService(reference); - } - - public Object addingService(ServiceReference reference) - { - _httpService = (HttpService)context.getService(reference); - try - { - //TODO; some effort to use the same console session on the 2 async console servlets? - - //websocket: -// WebConsoleSession wsSession = new WebConsoleSession(); -// WebEquinoxConsoleActivator.context.registerService(ConsoleSession.class.getName(), wsSession, null); -// EquinoxChattingSupport wsEquinoxChattingSupport = new EquinoxChattingSupport(wsSession); - _httpService.registerResources("/equinoxconsole/ws/index.html","/equinoxconsole/ws/index.html",null); - _httpService.registerServlet("/equinoxconsole/ws",new EquinoxConsoleWebSocketServlet(/*wsSession, wsEquinoxChattingSupport*/),null,null); - - //continuations: -// WebConsoleSession contSession = new WebConsoleSession(); -// WebEquinoxConsoleActivator.context.registerService(ConsoleSession.class.getName(), contSession, null); -// EquinoxChattingSupport contEquinoxChattingSupport = new EquinoxChattingSupport(contSession); - _httpService.registerResources("/equinoxconsole/index.html","/equinoxconsole/index.html",null); - _httpService.registerServlet("/equinoxconsole",new EquinoxConsoleContinuationServlet(/*contSession, contEquinoxChattingSupport*/),null,null); - - //legacy synchroneous; keep it in a separate console session. - WebConsoleSession syncSession = new WebConsoleSession(); - WebEquinoxToolsActivator.context.registerService(ConsoleSession.class.getName(), syncSession, null); - _httpService.registerServlet("/equinoxconsole/sync",new EquinoxConsoleSyncServlet(syncSession),null,null); - } - catch (ServletException e) - { - LOG.warn(e); - } - catch (NamespaceException e) - { - LOG.warn(e); - } - return _httpService; - } - }; - - _tracker = new ServiceTracker(context,HttpService.class.getName(),httpServiceTrackerCustomizer); - _tracker.open(); - } - - /* - * (non-Javadoc) - * - * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) - */ - public void stop(BundleContext bundleContext) throws Exception - { - _tracker.close(); - WebEquinoxToolsActivator.context = null; - } - - -} diff --git a/jetty-osgi/jetty-osgi-equinoxtools/src/main/java/org/eclipse/jetty/osgi/equinoxtools/console/EquinoxChattingSupport.java b/jetty-osgi/jetty-osgi-equinoxtools/src/main/java/org/eclipse/jetty/osgi/equinoxtools/console/EquinoxChattingSupport.java deleted file mode 100644 index 4211243555..0000000000 --- a/jetty-osgi/jetty-osgi-equinoxtools/src/main/java/org/eclipse/jetty/osgi/equinoxtools/console/EquinoxChattingSupport.java +++ /dev/null @@ -1,156 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== -// - -package org.eclipse.jetty.osgi.equinoxtools.console; - -import java.util.LinkedList; -import java.util.Queue; - -import org.eclipse.jetty.osgi.equinoxtools.console.WebConsoleWriterOutputStream.OnFlushListener; - -/** - * Processing of the messages to be received and sent to the chat servlets. - * Made to be extended for filtering of the messages and commands. - */ -public class EquinoxChattingSupport -{ - - private WebConsoleSession _consoleSession; - - public EquinoxChattingSupport(WebConsoleSession consoleSession) - { - _consoleSession = consoleSession; - } - - /** - * Split the output into multiple lines. - * Format them for the json messages sent to the chat. - * Empties the console output from what is already displayed in the chat. - * @return The lines to add to the message queue of each client. - */ - protected Queue<String> processConsoleOutput(boolean escape, OnFlushListener onflush) - { - Queue<String> result = new LinkedList<String>(); - String toDisplay = _consoleSession.getOutputAsWriter().getBuffer().toString(); - //the last listener to be called is in charge of clearing the console. - boolean clearConsole = _consoleSession.getOnFlushListeners().indexOf(onflush) == _consoleSession.getOnFlushListeners().size(); - if (clearConsole) - { - _consoleSession.clearOutput(); - } - boolean lastLineIsComplete = toDisplay.endsWith("\n") || toDisplay.endsWith("\r"); - String[] lines = toDisplay.split("\n"); - String lastLine = lastLineIsComplete ? null : lines[lines.length-1]; - if (clearConsole) - { - _consoleSession.getOutputAsWriter().append(lastLine); - } - for (int lnNb = 0; lnNb < (lastLineIsComplete ? lines.length : lines.length-1); lnNb++) - { - String line = lines[lnNb]; - while (line.trim().startsWith("null")) - {//hum.. - line = line.trim().substring("null".length()).trim(); - } - if (line.startsWith("osgi>")) - { - result.add("osgi>"); - result.add(escape ? jsonEscapeString(line.substring("osgi>".length())) : line.substring("osgi>".length())); - } - else - { - result.add(" "); - result.add(escape ? jsonEscapeString(line) : line); - } - } - return result; - } - - /** - * http://www.ietf.org/rfc/rfc4627.txt - * @param str - * @return The same string escaped according to the JSON RFC. - */ - public static String jsonEscapeString(String str) - { - StringBuilder sb = new StringBuilder(); - char[] asChars = str.toCharArray(); - for (char ch : asChars) - { - switch (ch) - { - //the reserved characters - case '"': - sb.append("\\\""); - break; - case '\\': - sb.append("\\\\"); - break; - case '\b': - sb.append("\\b"); - break; - case '\f': - sb.append("\\f"); - break; - case '\n': - sb.append("\\n"); - break; - case '\r': - sb.append("\\r"); - break; - case '\t': - sb.append("\\t"); - break; - case '/': - sb.append("\\/"); - break; - default: - //The non reserved characters - if (ch >= '\u0000' && ch <= '\u001F') - { - //escape as a unicode number when out of range. - String ss = Integer.toHexString(ch); - sb.append("\\u"); - for (int i = 0; i < 4 - ss.length(); i++) - { - //padding - sb.append('0'); - } - sb.append(ss.toUpperCase()); - } - else - { - sb.append(ch); - } - } - } - return sb.toString(); - } - - public void broadcast(OnFlushListener source) - { - for (OnFlushListener onflush : _consoleSession.getOnFlushListeners()) - { - if (onflush != source) - { - onflush.onFlush(); - } - } - } - -} diff --git a/jetty-osgi/jetty-osgi-equinoxtools/src/main/java/org/eclipse/jetty/osgi/equinoxtools/console/EquinoxConsoleContinuationServlet.java b/jetty-osgi/jetty-osgi-equinoxtools/src/main/java/org/eclipse/jetty/osgi/equinoxtools/console/EquinoxConsoleContinuationServlet.java deleted file mode 100644 index ab08af70fc..0000000000 --- a/jetty-osgi/jetty-osgi-equinoxtools/src/main/java/org/eclipse/jetty/osgi/equinoxtools/console/EquinoxConsoleContinuationServlet.java +++ /dev/null @@ -1,253 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== -// - -package org.eclipse.jetty.osgi.equinoxtools.console; - -import java.io.IOException; -import java.io.PrintWriter; -import java.util.HashMap; -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.continuation.Continuation; -import org.eclipse.jetty.continuation.ContinuationSupport; -import org.eclipse.jetty.osgi.equinoxtools.WebEquinoxToolsActivator; -import org.eclipse.jetty.osgi.equinoxtools.console.WebConsoleWriterOutputStream.OnFlushListener; -import org.eclipse.osgi.framework.console.ConsoleSession; - -/** - * Async servlet with jetty continuations to interact with the equinox console. - * Ported from jetty's example 'ChatServlet' - */ -public class EquinoxConsoleContinuationServlet extends HttpServlet implements OnFlushListener -{ - - private static final long serialVersionUID = 1L; - private Map<String,ConsoleUser> _consoleUsers = new HashMap<String, ConsoleUser>(); - private WebConsoleSession _consoleSession; - private EquinoxChattingSupport _support; - - /** - * @param consoleSession - */ - public EquinoxConsoleContinuationServlet() - { - - } - /** - * @param consoleSession - */ - public EquinoxConsoleContinuationServlet(WebConsoleSession consoleSession, EquinoxChattingSupport support) - { - _consoleSession = consoleSession; - _support = support; - } - @Override - public void init() throws ServletException - { - if (_consoleSession == null) - { - _consoleSession = new WebConsoleSession(); - WebEquinoxToolsActivator.getContext().registerService(ConsoleSession.class.getName(), _consoleSession, null); - } - if (_support == null) - { - _support = new EquinoxChattingSupport(_consoleSession); - } - _consoleSession.addOnFlushListener(this); - } - @Override - public void destroy() - { - _consoleSession.removeOnFlushListener(this); - } - - // Serve the HTML with embedded CSS and Javascript. - // This should be static content and should use real JS libraries. - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException - { - if (request.getParameter("action")!=null) - doPost(request,response); - else - response.sendRedirect(request.getContextPath() + request.getServletPath() - + (request.getPathInfo() != null ? request.getPathInfo() : "") + "/index.html"); - } - - // Handle Ajax calls from browser - @Override - protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException - { - // Ajax calls are form encoded - String action = request.getParameter("action"); - String message = request.getParameter("message"); - String username = request.getParameter("user"); - - if (action.equals("join")) - join(request,response,username); - else if (action.equals("poll")) - poll(request,response,username); - else if (action.equals("chat")) - chat(request,response,username,message); - } - - private synchronized void join(HttpServletRequest request,HttpServletResponse response,String username) - throws IOException - { - ConsoleUser member = new ConsoleUser(username); - _consoleUsers.put(username,member); - response.setContentType("text/json;charset=utf-8"); - PrintWriter out=response.getWriter(); - out.print("{action:\"join\"}"); - } - - private synchronized void poll(HttpServletRequest request,HttpServletResponse response,String username) - throws IOException - { - ConsoleUser member = _consoleUsers.get(username); - if (member==null) - { - response.sendError(503); - return; - } - - synchronized(member) - { - if (member.getMessageQueue().size()>0) - { - // Send one chat message - response.setContentType("text/json;charset=utf-8"); - StringBuilder buf=new StringBuilder(); - - buf.append("{\"action\":\"poll\","); - buf.append("\"from\":\""); - buf.append(member.getMessageQueue().poll()); - buf.append("\","); - - String message = member.getMessageQueue().poll(); - int quote=message.indexOf('"'); - while (quote>=0) - { - message=message.substring(0,quote)+'\\'+message.substring(quote); - quote=message.indexOf('"',quote+2); - } - buf.append("\"chat\":\""); - buf.append(message); - buf.append("\"}"); - byte[] bytes = buf.toString().getBytes("utf-8"); - response.setContentLength(bytes.length); - response.getOutputStream().write(bytes); - } - else - { - Continuation continuation = ContinuationSupport.getContinuation(request); - if (continuation.isInitial()) - { - // No chat in queue, so suspend and wait for timeout or chat - continuation.setTimeout(20000); - continuation.suspend(); - member.setContinuation(continuation); - } - else - { - // Timeout so send empty response - response.setContentType("text/json;charset=utf-8"); - PrintWriter out=response.getWriter(); - out.print("{action:\"poll\"}"); - } - } - } - } - - private synchronized void chat(HttpServletRequest request,HttpServletResponse response,String username,String message) - throws IOException - { - if (!message.endsWith("has joined!")) - { - _consoleSession.processCommand(message, false); - } - // Post chat to all members - onFlush(); - - response.setContentType("text/json;charset=utf-8"); - PrintWriter out=response.getWriter(); - out.print("{action:\"chat\"}"); - } - - /** - * Called right after the flush method on the output stream has been executed. - */ - public void onFlush() - { - Queue<String> pendingConsoleOutputMessages = _support.processConsoleOutput(true, this); - for (ConsoleUser m:_consoleUsers.values()) - { - synchronized (m) - { -// m.getMessageQueue().add("osgi>"); // from -// m.getMessageQueue().add("something was printed"); // chat - m.getMessageQueue().addAll(pendingConsoleOutputMessages); - - // wakeup member if polling - if (m.getContinuation()!=null) - { - m.getContinuation().resume(); - m.setContinuation(null); - } - } - } - } - - class ConsoleUser - { - private String _name; - private Continuation _continuation; - private Queue<String> _queue = new LinkedList<String>(); - - public ConsoleUser(String name) - { - _name = name; - } - - public String getName() - { - return _name; - } - - public void setContinuation(Continuation continuation) - { - _continuation = continuation; - } - - public Continuation getContinuation() - { - return _continuation; - } - public Queue<String> getMessageQueue() - { - return _queue; - } - - } -} diff --git a/jetty-osgi/jetty-osgi-equinoxtools/src/main/java/org/eclipse/jetty/osgi/equinoxtools/console/EquinoxConsoleSyncServlet.java b/jetty-osgi/jetty-osgi-equinoxtools/src/main/java/org/eclipse/jetty/osgi/equinoxtools/console/EquinoxConsoleSyncServlet.java deleted file mode 100644 index 33f0b38087..0000000000 --- a/jetty-osgi/jetty-osgi-equinoxtools/src/main/java/org/eclipse/jetty/osgi/equinoxtools/console/EquinoxConsoleSyncServlet.java +++ /dev/null @@ -1,82 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== -// - -package org.eclipse.jetty.osgi.equinoxtools.console; - -import java.io.IOException; -import java.io.PrintWriter; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - - -/** - * Take the example ChatServlet and use it to make an Equinox Console on the web. - */ -public class EquinoxConsoleSyncServlet extends HttpServlet -{ - - private static final long serialVersionUID = 1L; - - private WebConsoleSession _consoleSession; - - public EquinoxConsoleSyncServlet(WebConsoleSession consoleSession) - { - _consoleSession = consoleSession; - } - - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException - { - String cmd = request.getParameter("cmd"); - String Action = request.getParameter("Action"); - if (Action != null && Action.toLowerCase().indexOf("clear") != -1) - { - _consoleSession.clearOutput(); - } - if (cmd != null) - { - _consoleSession.processCommand(cmd, true); - } - - response.setContentType("text/html;charset=utf-8"); - PrintWriter p = response.getWriter(); - p.println("<html><head><title>Equinox Console (Synchroneous)</title></head><body>"); - p.println("<textarea rows=\"30\" cols=\"110\">"); - p.println(_consoleSession.getOutputAsWriter().toString()); - p.println("</textarea>"); - p.println("<form method=\"GET\" action=\""+response.encodeURL(getURI(request))+"\">"); - p.println("osgi> <input type=\"text\" name=\"cmd\" value=\"\"/><br/>\n"); - p.println("<input type=\"submit\" name=\"Action\" value=\"Submit or Refresh\"><br/>"); - p.println("<input type=\"submit\" name=\"Action\" value=\"Clear and Submit\"><br/>"); - p.println("</form>"); - p.println("<br/>"); - } - - - private String getURI(HttpServletRequest request) - { - String uri= (String)request.getAttribute("javax.servlet.forward.request_uri"); - if (uri == null) - uri= request.getRequestURI(); - return uri; - } - -} diff --git a/jetty-osgi/jetty-osgi-equinoxtools/src/main/java/org/eclipse/jetty/osgi/equinoxtools/console/EquinoxConsoleWebSocketServlet.java b/jetty-osgi/jetty-osgi-equinoxtools/src/main/java/org/eclipse/jetty/osgi/equinoxtools/console/EquinoxConsoleWebSocketServlet.java deleted file mode 100644 index 55c8e23ff5..0000000000 --- a/jetty-osgi/jetty-osgi-equinoxtools/src/main/java/org/eclipse/jetty/osgi/equinoxtools/console/EquinoxConsoleWebSocketServlet.java +++ /dev/null @@ -1,182 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== -// - -package org.eclipse.jetty.osgi.equinoxtools.console; - -import java.io.IOException; -import java.util.Queue; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.eclipse.jetty.osgi.equinoxtools.WebEquinoxToolsActivator; -import org.eclipse.jetty.osgi.equinoxtools.console.WebConsoleWriterOutputStream.OnFlushListener; -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; -import org.eclipse.osgi.framework.console.ConsoleSession; - -/** - * Websocket version of the Chat with equinox. - * Ported from jetty's example 'WebSocketChatServlet' - */ -public class EquinoxConsoleWebSocketServlet extends WebSocketServlet implements OnFlushListener -{ - private static final Logger LOG = Log.getLogger(EquinoxConsoleWebSocketServlet.class); - - private final Set<ChatWebSocket> _members = new CopyOnWriteArraySet<ChatWebSocket>(); - private static final long serialVersionUID = 1L; - private WebConsoleSession _consoleSession; - private EquinoxChattingSupport _support; - - public EquinoxConsoleWebSocketServlet() - { - - } - public EquinoxConsoleWebSocketServlet(WebConsoleSession consoleSession, EquinoxChattingSupport support) - { - _consoleSession = consoleSession; - _support = support; - } - @Override - public void init() throws ServletException - { - if (_consoleSession == null) - { - _consoleSession = new WebConsoleSession(); - WebEquinoxToolsActivator.getContext().registerService(ConsoleSession.class.getName(), _consoleSession, null); - } - if (_support == null) - { - _support = new EquinoxChattingSupport(_consoleSession); - } - super.init(); - _consoleSession.addOnFlushListener(this); - } - - @Override - public void destroy() - { - _consoleSession.removeOnFlushListener(this); - } - - - protected void doGet(HttpServletRequest request, HttpServletResponse response) - throws javax.servlet.ServletException ,IOException - { - //getServletContext().getNamedDispatcher("default").forward(request,response); - response.sendRedirect(request.getContextPath() + request.getServletPath() - + (request.getPathInfo() != null ? request.getPathInfo() : "") + "/index.html"); - }; - - public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) - { - return new ChatWebSocket(); - } - - /* ------------------------------------------------------------ */ - /* ------------------------------------------------------------ */ - class ChatWebSocket implements WebSocket.OnTextMessage - { - Connection _connection; - String _username; - - public void onOpen(Connection connection) - { - // LOG.info(this+" onConnect"); - _connection=connection; - _members.add(this); - } - - public void onMessage(byte frame, byte[] data,int offset, int length) - { - // LOG.info(this+" onMessage: "+TypeUtil.toHexString(data,offset,length)); - } - - public void onMessage(String data) - { - LOG.info("onMessage: {}",data); - if (data.indexOf("disconnect")>=0) - _connection.disconnect(); - else - { - if (!data.endsWith(":has joined!")) - { - if (_username != null) - { - if (data.startsWith(_username + ":")) - { - data = data.substring(_username.length()+1); - } - else - { - //we should not be here? - } - } - _consoleSession.processCommand(data, false); - } - else - { - _username = data.substring(0, data.length()-":has joined!".length()); - } - // LOG.info(this+" onMessage: "+data); - onFlush(); - } - } - - public void onClose(int code, String message) - { - // LOG.info(this+" onDisconnect"); - _members.remove(this); - } - - public void onError(String message, Throwable ex) - { - - } - - } - - - /** - * Called right after the flush method on the output stream has been executed. - */ - public void onFlush() - { - Queue<String> pendingConsoleOutputMessages = _support.processConsoleOutput(false, this); - for (ChatWebSocket member : _members) - { - try - { - for (String line : pendingConsoleOutputMessages) - { - member._connection.sendMessage(line); - } - } - catch(IOException e) - { - LOG.warn(e); - } - } - } - -} diff --git a/jetty-osgi/jetty-osgi-equinoxtools/src/main/java/org/eclipse/jetty/osgi/equinoxtools/console/WebConsoleSession.java b/jetty-osgi/jetty-osgi-equinoxtools/src/main/java/org/eclipse/jetty/osgi/equinoxtools/console/WebConsoleSession.java deleted file mode 100644 index b867934247..0000000000 --- a/jetty-osgi/jetty-osgi-equinoxtools/src/main/java/org/eclipse/jetty/osgi/equinoxtools/console/WebConsoleSession.java +++ /dev/null @@ -1,189 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== -// - -package org.eclipse.jetty.osgi.equinoxtools.console; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PipedInputStream; -import java.io.PipedOutputStream; -import java.io.PrintStream; -import java.io.StringWriter; -import java.util.List; - -import org.eclipse.jetty.osgi.equinoxtools.console.WebConsoleWriterOutputStream.OnFlushListener; -import org.eclipse.osgi.framework.console.ConsoleSession; - -/** - * A simple console session for equinox. - * - * @author hmalphettes - */ -public class WebConsoleSession extends ConsoleSession -{ - - private OutputStream _out; - private StringWriter _outWriter; - private PrintStream _source; - private InputStream _in; - - public WebConsoleSession() - { - _outWriter = new StringWriter(); - _out = new WebConsoleWriterOutputStream(_outWriter,"UTF-8"); - try - { - PipedOutputStream source = new PipedOutputStream(); - PipedInputStream sink = new PipedInputStream(source); - _in = sink; - _source = new PrintStream(source); - } - catch (IOException e) - { - //this never happens? - e.printStackTrace(); - } - } - - @Override - protected void doClose() - { - if (_out != null) try { _out.close(); } catch (IOException e) {} - if (_in != null) try { _in.close(); } catch (IOException ioe) {} - } - - @Override - public InputStream getInput() - { - return _in; - } - - @Override - public OutputStream getOutput() - { - return _out; - } - - /** - * For the output we are using a string writer in fact. - * @return - */ - public StringWriter getOutputAsWriter() - { - return _outWriter; - } - - /** - * @return The print stream where commands can be written. - */ - public PrintStream getSource() - { - return _source; - } - - /** - * Issue a command on the console. - * @param cmd - */ - public void issueCommand(String cmd) - { - if (cmd != null) - { - getSource().println(cmd); - } - } - - /** - * @param flushListener Object that is called back when the outputstream is flushed. - */ - public void addOnFlushListener(OnFlushListener flushListener) - { - ((WebConsoleWriterOutputStream)_out).addOnFlushListener(flushListener); - } - /** - * @param flushListener Object that is called back when the outputstream is flushed. - */ - public boolean removeOnFlushListener(OnFlushListener flushListener) - { - return ((WebConsoleWriterOutputStream)_out).removeOnFlushListener(flushListener); - } - - /** - * Process command comming from a web UI - * @param cmd - */ - public void processCommand(String cmd, boolean wait) - { - cmd = cmd.trim(); - while (cmd.startsWith("osgi>")) - { - cmd = cmd.substring("osgi>".length()).trim(); - } - if (cmd.equals("clear")) - { - clearOutput(); - } - else - { - getOutputAsWriter().append(cmd + "\n"); - int originalOutputLength = getOutputAsWriter().getBuffer().length(); - issueCommand(cmd); - - if (wait) - { - //it does not get uglier than this: - //give a little time to equinox to interpret the command so we see the response - //we could do a lot better, but we might as well use the async servlets anyways. - int waitLoopNumber = 0; - int lastWaitOutputLength = -1; - while (waitLoopNumber < 10) - { - waitLoopNumber++; - try - { - Thread.sleep(100); - } - catch (InterruptedException e) - { - break; - } - int newOutputLength = getOutputAsWriter().getBuffer().length(); - if (newOutputLength > originalOutputLength && newOutputLength == lastWaitOutputLength) - { - break; - } - lastWaitOutputLength = newOutputLength; - } - } - } - - } - - public void clearOutput() - { - StringBuffer buf = getOutputAsWriter().getBuffer(); - if (buf.length() > 0) buf.delete(0,buf.length()-1); - } - - public List<OnFlushListener> getOnFlushListeners() - { - return ((WebConsoleWriterOutputStream)_out).getOnFlushListeners(); - } - -} diff --git a/jetty-osgi/jetty-osgi-equinoxtools/src/main/java/org/eclipse/jetty/osgi/equinoxtools/console/WebConsoleWriterOutputStream.java b/jetty-osgi/jetty-osgi-equinoxtools/src/main/java/org/eclipse/jetty/osgi/equinoxtools/console/WebConsoleWriterOutputStream.java deleted file mode 100644 index c60b3e5f49..0000000000 --- a/jetty-osgi/jetty-osgi-equinoxtools/src/main/java/org/eclipse/jetty/osgi/equinoxtools/console/WebConsoleWriterOutputStream.java +++ /dev/null @@ -1,93 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== -// - -package org.eclipse.jetty.osgi.equinoxtools.console; - -import java.io.IOException; -import java.io.Writer; -import java.util.ArrayList; -import java.util.List; - -/** - * Can be set with a listener that is called back right after the flush method is called. - */ -public class WebConsoleWriterOutputStream extends org.eclipse.jetty.io.WriterOutputStream -{ - - /** - * Interface called back after the outputstream is flushed. - */ - public interface OnFlushListener - { - /** - * Called right after the flush method on the output stream has been executed. - */ - public void onFlush(); - - } - - public interface MessageBroadcaster - { - public void broadcast(); - } - - private List<OnFlushListener> _callBacks; - - public WebConsoleWriterOutputStream(Writer writer, String encoding) - { - super(writer, encoding); - } - - @Override - public synchronized void flush() throws IOException - { - super.flush(); - if (_callBacks != null) - { - for (OnFlushListener listener : _callBacks) - { - listener.onFlush(); - } - } - } - - public synchronized void addOnFlushListener(OnFlushListener callback) - { - if (_callBacks == null) - { - _callBacks = new ArrayList<WebConsoleWriterOutputStream.OnFlushListener>(); - } - if (!_callBacks.contains(callback)) - { - _callBacks.add(callback); - } - } - public synchronized boolean removeOnFlushListener(OnFlushListener callback) - { - if (_callBacks != null) - { - return _callBacks.remove(callback); - } - return false; - } - public synchronized List<OnFlushListener> getOnFlushListeners() - { - return _callBacks; - } - -} diff --git a/jetty-osgi/jetty-osgi-httpservice/contexts/httpservice.xml b/jetty-osgi/jetty-osgi-httpservice/contexts/httpservice.xml index 95757f6c80..211a8e8ce2 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 26246ac4dc..4b4e3a9b74 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <modelVersion>4.0.0</modelVersion> @@ -96,8 +96,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> @@ -114,6 +114,4 @@ org.eclipse.equinox.http.servlet, </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 c6efbeb50f..0000000000 --- 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 db10d11a57..0000000000 --- a/jetty-osgi/jetty-osgi-servletbridge/pom.xml +++ /dev/null @@ -1,53 +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> - <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 50236caaff..0000000000 --- a/jetty-osgi/jetty-osgi-servletbridge/src/main/java/org/eclipse/jetty/nested/Dump.java +++ /dev/null @@ -1,1017 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== -// - -package org.eclipse.jetty.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 499b687d8a..0000000000 --- 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-2012 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== -// - -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 e4470e60dd..0000000000 --- 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-2012 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== -// - -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 d6d6e63596..0000000000 --- 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-2012 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== -// - -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 c61dee582d..0000000000 --- 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 63b9bd055c..7c61e8e3e0 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <groupId>org.eclipse.jetty.osgi</groupId> @@ -21,10 +21,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> @@ -76,7 +74,7 @@ <dependencies> <dependency> <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-server</artifactId> + <artifactId>jetty-annotations</artifactId> <version>${project.version}</version> </dependency> <dependency> @@ -144,6 +142,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 5b65bc00f1..48ca540fd9 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <modelVersion>4.0.0</modelVersion> @@ -16,6 +16,7 @@ <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-server</artifactId> + <version>${project.version}</version> </dependency> <dependency> <groupId>org.eclipse.osgi</groupId> @@ -120,7 +121,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 3e4d27d4e2..f08fd1ad7f 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <modelVersion>4.0.0</modelVersion> @@ -117,7 +117,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 5e321f0b9b..e11095f5d1 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <modelVersion>4.0.0</modelVersion> @@ -11,6 +11,7 @@ <description>Jetty OSGi Integration test</description> <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> @@ -223,7 +224,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 ff82304b34..bbaeefb088 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 @@ -128,6 +128,7 @@ public class TestJettyOSGiBootWebAppAsService * plus your testcase, wrapped into a bundle called pax-exam-probe */ @Test + @Ignore public void listBundles() throws Exception { Map<String,Bundle> bundlesIndexedBySymbolicName = new HashMap<String, Bundle>(); 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 24eaa3b270..87adbddf8c 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 @@ -132,14 +132,14 @@ public class TestJettyOSGiBootWithJsp * plus your testcase, wrapped into a bundle called pax-exam-probe */ @Test - + @Ignore 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"); @@ -148,11 +148,11 @@ public class TestJettyOSGiBootWithJsp 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 5a11c02a89..c0b85bdbb6 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>jetty-overlay-deployer</artifactId> diff --git a/jetty-plus/pom.xml b/jetty-plus/pom.xml index d5615a005d..c9abc52f92 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>jetty-plus</artifactId> @@ -28,7 +28,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 0000000000..9ba3dfc1b2 --- /dev/null +++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/annotation/ContainerInitializer.java @@ -0,0 +1,114 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.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 0000000000..9528b79d6f --- /dev/null +++ b/jetty-plus/src/test/java/org/eclipse/jetty/plus/webapp/PlusDescriptorProcessorTest.java @@ -0,0 +1,176 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.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 0000000000..b213172ef0 --- /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 0000000000..e2fff6786f --- /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 0000000000..da1f3d5add --- /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 0000000000..aa40eb3fba --- /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 51b8f4c951..bd3681de84 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.8-SNAPSHOT</version> + <version>8.1.8-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 5de6184e31..dfe1c7811d 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>jetty-rewrite</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-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 c52c6fd2eb..4df73613a7 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 @@ public class HeaderPatternRuleTest extends AbstractRuleTestCase }; 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-security/pom.xml b/jetty-security/pom.xml index e25b2299d7..3af63a46a3 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>jetty-security</artifactId> @@ -24,7 +24,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 16be420e8d..fee3836bbf 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 @@ import org.eclipse.jetty.util.security.Constraint; public class ConstraintMapping { String _method; + String[] _methodOmissions; String _pathSpec; @@ -81,4 +82,19 @@ public class ConstraintMapping { 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 0ca83b1962..5712c1df92 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,16 +19,24 @@ 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 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; @@ -42,17 +50,225 @@ import org.eclipse.jetty.util.security.Constraint; /* ------------------------------------------------------------ */ /** * 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. @@ -136,8 +352,6 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr */ public void setConstraintMappings(List<ConstraintMapping> constraintMappings, Set<String> roles) { - if (isStarted()) - throw new IllegalStateException("Started"); _constraintMappings.clear(); _constraintMappings.addAll(constraintMappings); @@ -156,6 +370,14 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr } } setRoles(roles); + + if (isStarted()) + { + for (ConstraintMapping mapping : _constraintMappings) + { + processConstraintMapping(mapping); + } + } } /* ------------------------------------------------------------ */ @@ -168,9 +390,6 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr */ public void setRoles(Set<String> roles) { - if (isStarted()) - throw new IllegalStateException("Started"); - _roles.clear(); _roles.addAll(roles); } @@ -232,7 +451,9 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr } super.doStart(); } - + + + /* ------------------------------------------------------------ */ @Override protected void doStop() throws Exception { @@ -241,7 +462,15 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr _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()); @@ -253,8 +482,15 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr 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) { @@ -268,10 +504,10 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr 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) { @@ -281,50 +517,120 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr } else { - UserDataConstraint userDataConstraint = UserDataConstraint.get(constraint.getDataConstraint()); - roleInfo.setUserDataConstraint(userDataConstraint); + //combine with any entry that covers all methods + if (httpMethod == null) + { + for (Map.Entry<String, RoleInfo> entry : mappings.entrySet()) + { + if (entry.getKey() != null) + { + RoleInfo specific = entry.getValue(); + specific.combine(roleInfo); + } + } + } + } + } + + /* ------------------------------------------------------------ */ + /** 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(); - boolean checked = constraint.getAuthenticate(); - roleInfo.setChecked(checked); - if (roleInfo.isChecked()) + 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 (constraint.isAnyRole()) + //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) - roleInfo.addRole(role); + ri.addRole(role); } else // * means any role - roleInfo.setAnyRole(true); + ri.setAnyRole(true); } else { - String[] newRoles = constraint.getRoles(); + 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); - roleInfo.addRole(role); - } - } - } - if (httpMethod == null) - { - for (Map.Entry<String, RoleInfo> entry : mappings.entrySet()) - { - if (entry.getKey() != null) - { - RoleInfo specific = entry.getValue(); - specific.combine(roleInfo); + 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); @@ -334,13 +640,46 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr 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) @@ -404,7 +743,11 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr } } - + + /* ------------------------------------------------------------ */ + /** + * @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) @@ -413,7 +756,12 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr } 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 @@ -454,4 +802,5 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr getBeans(), TypeUtil.asList(getHandlers())); } + } 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 ec2fb04b0d..3ae194f3fa 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 @@ public class BasicAuthenticator extends LoginAuthenticator return Constraint.__BASIC_AUTH; } + + /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.security.Authenticator#validateRequest(javax.servlet.ServletRequest, javax.servlet.ServletResponse, boolean) @@ -85,10 +87,9 @@ public class BasicAuthenticator extends LoginAuthenticator 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 a4bef236a3..f34ccf3a0a 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 @@ public class ClientCertAuthenticator extends LoginAuthenticator return Constraint.__CERT_AUTH; } + + /** * @return Authentication for request * @throws ServerAuthException @@ -121,10 +123,9 @@ public class ClientCertAuthenticator extends LoginAuthenticator 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 158058c94f..68f3192dfd 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 @@ package org.eclipse.jetty.security.authentication; 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 @@ public class DeferredAuthentication implements Authentication.Deferred if (identity_service!=null) _previousAssociation=identity_service.associate(((Authentication.User)authentication).getUserIdentity()); + return authentication; } } @@ -78,7 +82,8 @@ public class DeferredAuthentication implements Authentication.Deferred { LOG.debug(e); } - return Authentication.UNAUTHENTICATED; + + return this; } /* ------------------------------------------------------------ */ @@ -101,28 +106,23 @@ public class DeferredAuthentication implements Authentication.Deferred { 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 class DeferredAuthentication implements Authentication.Deferred { } + 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; + } + }; /* ------------------------------------------------------------ */ @@ -309,4 +332,4 @@ public class DeferredAuthentication implements Authentication.Deferred }; -}
\ No newline at end of file +} 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 c42c26c871..1e5a417f29 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 @@ -116,6 +116,8 @@ public class DigestAuthenticator extends LoginAuthenticator { return true; } + + /* ------------------------------------------------------------ */ public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory) throws ServerAuthException @@ -185,10 +187,10 @@ public class DigestAuthenticator extends LoginAuthenticator 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 26a080dbdb..504b59c355 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 @@ public class FormAuthenticator extends LoginAuthenticator _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 @@ public class FormAuthenticator extends LoginAuthenticator 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 @@ public class FormAuthenticator extends LoginAuthenticator 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 17e86574b6..322b17c95e 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 javax.servlet.http.HttpSession; 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 abstract class LoginAuthenticator implements Authenticator { } + + /* ------------------------------------------------------------ */ + 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 260d5ddd3f..7c0a56c1ac 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 @@ public class SessionAuthentication implements Authentication.User, Serializable, { 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 ca5563f4e5..303d2f7e30 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 @@ public class SpnegoAuthenticator extends LoginAuthenticator return _authMethod; } + + public Authentication validateRequest(ServletRequest request, ServletResponse response, boolean mandatory) throws ServerAuthException { HttpServletRequest req = (HttpServletRequest)request; @@ -96,7 +98,7 @@ public class SpnegoAuthenticator extends LoginAuthenticator { 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 a400db3a93..cab25b10e5 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,9 +22,11 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -76,6 +78,8 @@ public class ConstraintTest _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); @@ -189,17 +193,59 @@ public class ConstraintTest @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(); 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); @@ -214,8 +260,8 @@ public class ConstraintTest "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")); @@ -241,7 +287,33 @@ public class ConstraintTest 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"));*/ } + + @Test public void testFormDispatch() throws Exception @@ -847,7 +919,7 @@ public class ConstraintTest 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 0000000000..743effaddd --- /dev/null +++ b/jetty-security/src/test/java/org/eclipse/jetty/security/SpecExampleConstraintTest.java @@ -0,0 +1,311 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.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 169cde2117..35dbdd5959 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>jetty-server</artifactId> @@ -25,7 +25,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 49fe8ca094..a160bfa18d 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 @@ -21,6 +21,7 @@ package org.eclipse.jetty.server; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; +import javax.servlet.DispatcherType; import javax.servlet.ServletInputStream; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; @@ -423,6 +424,7 @@ public abstract class AbstractHttpConnection extends AbstractConnection boolean error = false; String threadName=null; + Throwable async_exception=null; try { if (LOG.isDebugEnabled()) @@ -494,6 +496,7 @@ public abstract class AbstractHttpConnection extends AbstractConnection } catch (EofException e) { + async_exception=e; LOG.debug(e); error=true; _request.setHandled(true); @@ -502,6 +505,7 @@ public abstract class AbstractHttpConnection extends AbstractConnection } catch (RuntimeIOException e) { + async_exception=e; LOG.debug(e); error=true; _request.setHandled(true); @@ -515,6 +519,7 @@ public abstract class AbstractHttpConnection extends AbstractConnection } catch (Throwable e) { + async_exception=e; LOG.warn(String.valueOf(_uri),e); error=true; _request.setHandled(true); @@ -533,7 +538,8 @@ public abstract class AbstractHttpConnection extends AbstractConnection if (_request._async.isUncompleted()) { - _request._async.doComplete(); + + _request._async.doComplete(async_exception); if (_expect100Continue) { 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 90c6503225..0000000000 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContext.java +++ /dev/null @@ -1,48 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== -// - -package org.eclipse.jetty.server; - -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 7bf4999278..8bf27dd29c 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.ServletResponseWrapper; +import javax.servlet.ServletException; + import java.util.ArrayList; import java.util.List; @@ -27,8 +33,8 @@ import javax.servlet.ServletResponse; 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.log.Logger; 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 @@ public class AsyncContinuation implements AsyncContext, Continuation 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 @@ public class AsyncContinuation implements AsyncContext, Continuation 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 class AsyncContinuation implements AsyncContext, Continuation } /* ------------------------------------------------------------ */ + 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) @@ -268,6 +299,7 @@ public class AsyncContinuation implements AsyncContext, Continuation { synchronized (this) { + _continuation=false; _responseWrapped=false; switch(_state) @@ -275,6 +307,15 @@ public class AsyncContinuation implements AsyncContext, Continuation 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 +352,7 @@ public class AsyncContinuation implements AsyncContext, Continuation _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 +360,32 @@ public class AsyncContinuation implements AsyncContext, Continuation _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 +400,6 @@ public class AsyncContinuation implements AsyncContext, Continuation { synchronized (this) { - List<ContinuationListener> listeners=_continuationListeners; - switch(_state) { case __REDISPATCHED: @@ -361,7 +420,7 @@ public class AsyncContinuation implements AsyncContext, Continuation { _state=__UNCOMPLETED; return true; - } + } _initial=false; _state=__REDISPATCHED; return false; @@ -419,27 +478,43 @@ public class AsyncContinuation implements AsyncContext, Continuation /* ------------------------------------------------------------ */ 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 (AsyncListener listener : aListeners) + { + try + { + listener.onTimeout(_event); + } + catch(Exception e) + { + LOG.warn(e); + } + } + } + if (cListeners!=null) { - for (int i=0;i<listeners.size();i++) + for (ContinuationListener listener : cListeners) { - ContinuationListener listener=listeners.get(i); try { listener.onTimeout(this); @@ -450,6 +525,8 @@ public class AsyncContinuation implements AsyncContext, Continuation } } } + + synchronized (this) { @@ -457,7 +534,11 @@ public class AsyncContinuation implements AsyncContext, Continuation { case __ASYNCSTARTED: case __ASYNCWAIT: - dispatch(); + if (_continuation) + dispatch(); + else + // TODO maybe error dispatch? + complete(); } } @@ -501,36 +582,75 @@ public class AsyncContinuation implements AsyncContext, Continuation } } - + /* ------------------------------------------------------------ */ + @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() + protected void doComplete(Throwable ex) { - final List<ContinuationListener> listeners; + 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 (AsyncListener listener : aListeners) + { + try + { + if (ex!=null) + { + _event.getSuppliedRequest().setAttribute(Dispatcher.ERROR_EXCEPTION,ex); + _event.getSuppliedRequest().setAttribute(Dispatcher.ERROR_MESSAGE,ex.getMessage()); + listener.onError(_event); + } + else + listener.onComplete(_event); + } + catch(Exception e) + { + LOG.warn(e); + } + } + } + if (cListeners!=null) { - for(int i=0;i<listeners.size();i++) + for (ContinuationListener listener : cListeners) { try { - listeners.get(i).onComplete(this); + listener.onComplete(this); } catch(Exception e) { @@ -545,7 +665,6 @@ public class AsyncContinuation implements AsyncContext, Continuation { synchronized (this) { -// _history.append("r\n"); switch(_state) { case __DISPATCHED: @@ -617,7 +736,7 @@ public class AsyncContinuation implements AsyncContext, Continuation } else { - ((AsyncEndPoint)endp).scheduleTimeout(_event,_timeoutMs); + ((AsyncEndPoint)endp).scheduleTimeout(_event._timeout,_timeoutMs); } } } @@ -639,7 +758,7 @@ public class AsyncContinuation implements AsyncContext, Continuation final AsyncEventState event=_event; if (event!=null) { - ((AsyncEndPoint)endp).cancelTimeout(event); + ((AsyncEndPoint)endp).cancelTimeout(event._timeout); } } } @@ -736,15 +855,15 @@ public class AsyncContinuation implements AsyncContext, Continuation 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 +888,7 @@ public class AsyncContinuation implements AsyncContext, Continuation { synchronized (this) { - return (_event!=null && _event.getRequest()==_connection._request && _event.getResponse()==_connection._response); + return (_event!=null && _event.getSuppliedRequest()==_connection._request && _event.getSuppliedResponse()==_connection._response); } } @@ -840,6 +959,7 @@ public class AsyncContinuation implements AsyncContext, Continuation */ public void suspend(ServletResponse response) { + _continuation=true; _responseWrapped=!(response instanceof Response); doSuspend(_connection.getRequest().getServletContext(),_connection.getRequest(),response); } @@ -851,6 +971,7 @@ public class AsyncContinuation implements AsyncContext, Continuation public void suspend() { _responseWrapped=false; + _continuation=true; doSuspend(_connection.getRequest().getServletContext(),_connection.getRequest(),_connection.getResponse()); } @@ -860,8 +981,8 @@ public class AsyncContinuation implements AsyncContext, Continuation */ 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 +1031,34 @@ public class AsyncContinuation implements AsyncContext, Continuation /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ - 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(); @@ -969,16 +1103,6 @@ public class AsyncContinuation implements AsyncContext, Continuation { return _dispatchContext==null?_suspendedContext:_dispatchContext; } - - public ServletRequest getRequest() - { - return _request; - } - - public ServletResponse getResponse() - { - return _response; - } /* ------------------------------------------------------------ */ /** @@ -988,16 +1112,5 @@ public class AsyncContinuation implements AsyncContext, Continuation { 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 1c63baf764..ff281f6d03 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 @@ public interface Authentication * @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 7448e297da..e5353cc1d4 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.HashSet; 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 @@ import org.eclipse.jetty.util.UrlEncoded; */ 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 dc5307372d..0000000000 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/DispatcherType.java +++ /dev/null @@ -1,28 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== -// - -package org.eclipse.jetty.server; - -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 4d927bc317..62dc7ab3b1 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,7 @@ package org.eclipse.jetty.server; import java.io.BufferedReader; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -37,16 +38,26 @@ import java.util.Locale; import java.util.Map; import java.util.Map.Entry; +import javax.servlet.AsyncContext; +import javax.servlet.AsyncEvent; +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.HttpVersions; 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; @@ -70,7 +82,9 @@ 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.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 +125,51 @@ import org.eclipse.jetty.util.log.Logger; */ 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 +223,9 @@ public class Request implements HttpServletRequest private Buffer _timeStampBuffer; private HttpURI _uri; - + + private MultiPartInputStream _multiPartInputStream; //if the request is a multi-part mime + /* ------------------------------------------------------------ */ public Request() { @@ -188,7 +243,9 @@ public class Request implements HttpServletRequest 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()); } /* ------------------------------------------------------------ */ @@ -321,7 +378,7 @@ public class Request implements HttpServletRequest { return _async; } - + /* ------------------------------------------------------------ */ /* * @see javax.servlet.ServletRequest#getAttribute(java.lang.String) @@ -377,8 +434,8 @@ public class Request implements HttpServletRequest 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; @@ -1255,6 +1312,7 @@ public class Request implements HttpServletRequest UserIdentity user = ((Authentication.User)_authentication).getUserIdentity(); return user.getUserPrincipal(); } + return null; } @@ -1275,6 +1333,12 @@ public class Request implements HttpServletRequest return _handled; } + public boolean isAsyncStarted() + { + return _async.isAsyncStarted(); + } + + /* ------------------------------------------------------------ */ public boolean isAsyncSupported() { @@ -1410,7 +1474,8 @@ public class Request implements HttpServletRequest if (_savedNewSessions != null) _savedNewSessions.clear(); - _savedNewSessions = null; + _savedNewSessions=null; + _multiPartInputStream = null; } /* ------------------------------------------------------------ */ @@ -1912,6 +1977,118 @@ public class Request implements HttpServletRequest } /* ------------------------------------------------------------ */ + 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 + { + if (getContentType() == null || !getContentType().startsWith("multipart/form-data")) + throw new ServletException("Content-Type != multipart/form-data"); + + 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 && mp.getFile() == 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())); + + String content=new String(mp.getBytes(),charset==null?StringUtil.__UTF8:charset); + getParameter(""); //cause params to be evaluated + getParameters().add(mp.getName(), content); + } + } + } + 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) + { + 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 && mp.getFile() == 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())); + + String content=new String(mp.getBytes(),charset==null?StringUtil.__UTF8:charset); + getParameter(""); //cause params to be evaluated + getParameters().add(mp.getName(), content); + } + } + } + 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 49f6d1551d..b8f9ccb9a7 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,6 +20,7 @@ package org.eclipse.jetty.server; import java.io.IOException; import java.io.PrintWriter; +import java.util.Collection; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; @@ -164,7 +165,7 @@ public class Response implements HttpServletResponse cookie.getMaxAge(), comment, cookie.getSecure(), - http_only,// || cookie.isHttpOnly(), + http_only || cookie.isHttpOnly(), cookie.getVersion()); } @@ -537,6 +538,14 @@ public class Response implements HttpServletResponse } } + + /* ------------------------------------------------------------ */ + public Collection<String> getHeaderNames() + { + final HttpFields fields=_connection.getResponseFields(); + return fields.getFieldNamesCollection(); + } + /* ------------------------------------------------------------ */ /* */ @@ -548,12 +557,13 @@ public class Response implements HttpServletResponse /* ------------------------------------------------------------ */ /* */ - 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; } /* ------------------------------------------------------------ */ @@ -1091,7 +1101,7 @@ public class Response implements HttpServletResponse 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 c891d63066..6699c67448 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 @@ -21,6 +21,8 @@ package org.eclipse.jetty.server; import java.io.IOException; 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; @@ -65,7 +67,7 @@ public class Server extends HandlerWrapper implements Attributes 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 cdcbed1837..463e57cf3c 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 @@ public class ServletRequestHttpWrapper extends ServletRequestWrapper implements 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 + { + + } + }
\ No newline at end of file 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 48514a24f6..4b62f0809d 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 @@ public class ServletResponseHttpWrapper extends ServletResponseWrapper implement { } + /** + * @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 67e0ae2ac0..43c8947d89 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 interface SessionManager extends LifeCycle */ public HttpSession newHttpSession(HttpServletRequest request); - /* ------------------------------------------------------------ */ - /** - * @return true if session cookies should be secure - */ - public boolean getSecureCookies(); /* ------------------------------------------------------------ */ /** @@ -258,19 +256,6 @@ public interface SessionManager extends LifeCycle 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,48 +278,22 @@ public interface SessionManager extends LifeCycle 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) + * @return whether the session management is handled via cookies. */ - public String getSessionDomain(); - + public boolean isUsingCookies(); + /** - * Sets the path to set on the session cookie - * @param path the path to set on the session cookie - * @see #getSessionPath() + * @return whether the session management is handled via URLs. */ - public void setSessionPath(String path); + public boolean isUsingURLs(); - /** - * @return the path to set on the session cookie - * @see #setSessionPath(String) - */ - public String getSessionPath(); + public Set<SessionTrackingMode> getDefaultSessionTrackingModes(); - /** - * 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); + public Set<SessionTrackingMode> getEffectiveSessionTrackingModes(); - /** - * @return the max age to set on the session cookie, in seconds - * @see #setMaxCookieAge(int) - */ - public int getMaxCookieAge(); + public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes); - /** - * @return whether the session management is handled via cookies. - */ - public boolean isUsingCookies(); + public SessionCookieConfig getSessionCookieConfig(); /** * @return True if absolute URLs are check for remoteness before being session encoded. @@ -345,5 +304,4 @@ public interface SessionManager extends LifeCycle * @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 60b1f24ff1..ec4f6af5c1 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.io.InputStream; 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; @@ -35,6 +36,9 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.EnumSet; + +import javax.servlet.DispatcherType; import javax.servlet.RequestDispatcher; import javax.servlet.Servlet; import javax.servlet.ServletContext; @@ -43,9 +47,16 @@ import javax.servlet.ServletContextAttributeListener; 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; @@ -54,7 +65,6 @@ import org.eclipse.jetty.http.MimeTypes; 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; @@ -579,6 +589,19 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. { 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) + { + } /* ------------------------------------------------------------ */ /** @@ -737,14 +760,25 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. 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); } } + } + /* ------------------------------------------------------------ */ + public void callContextInitialized (ServletContextListener l, ServletContextEvent e) + { + l.contextInitialized(e); LOG.info("started {}",this); } /* ------------------------------------------------------------ */ + public void callContextDestroyed (ServletContextListener l, ServletContextEvent e) + { + l.contextDestroyed(e); + } + + /* ------------------------------------------------------------ */ /* * @see org.eclipse.thread.AbstractLifeCycle#doStop() */ @@ -1488,6 +1522,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. _localeEncodingMap.put(locale,encoding); } + /* ------------------------------------------------------------ */ public String getLocaleEncoding(String locale) { if (_localeEncodingMap == null) @@ -1631,6 +1666,10 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. */ 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() { @@ -1647,6 +1686,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. /* * @see javax.servlet.ServletContext#getContext(java.lang.String) */ + @Override public ServletContext getContext(String uripath) { List<ContextHandler> contexts = new ArrayList<ContextHandler>(); @@ -1732,15 +1772,18 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. /* * @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) @@ -1755,15 +1798,17 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. /* * @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; @@ -1773,6 +1818,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. /* * @see javax.servlet.ServletContext#getRequestDispatcher(java.lang.String) */ + @Override public RequestDispatcher getRequestDispatcher(String uriInContext) { if (uriInContext == null) @@ -1809,6 +1855,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. /* * @see javax.servlet.ServletContext#getRealPath(java.lang.String) */ + @Override public String getRealPath(String path) { if (path == null) @@ -1837,6 +1884,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ + @Override public URL getResource(String path) throws MalformedURLException { Resource resource = ContextHandler.this.getResource(path); @@ -1849,6 +1897,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. /* * @see javax.servlet.ServletContext#getResourceAsStream(java.lang.String) */ + @Override public InputStream getResourceAsStream(String path) { try @@ -1870,6 +1919,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. /* * @see javax.servlet.ServletContext#getResourcePaths(java.lang.String) */ + @Override public Set getResourcePaths(String path) { return ContextHandler.this.getResourcePaths(path); @@ -1879,6 +1929,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. /* * @see javax.servlet.ServletContext#getServerInfo() */ + @Override public String getServerInfo() { return "jetty/" + Server.getVersion(); @@ -1888,6 +1939,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. /* * @see javax.servlet.ServletContext#getServlet(java.lang.String) */ + @Override + @Deprecated public Servlet getServlet(String name) throws ServletException { return null; @@ -1898,6 +1951,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. * @see javax.servlet.ServletContext#getServletNames() */ @SuppressWarnings("unchecked") + @Override + @Deprecated public Enumeration getServletNames() { return Collections.enumeration(Collections.EMPTY_LIST); @@ -1908,6 +1963,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. * @see javax.servlet.ServletContext#getServlets() */ @SuppressWarnings("unchecked") + @Override + @Deprecated public Enumeration getServlets() { return Collections.enumeration(Collections.EMPTY_LIST); @@ -1917,6 +1974,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. /* * @see javax.servlet.ServletContext#log(java.lang.Exception, java.lang.String) */ + @Override public void log(Exception exception, String msg) { _logger.warn(msg,exception); @@ -1926,6 +1984,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. /* * @see javax.servlet.ServletContext#log(java.lang.String) */ + @Override public void log(String msg) { _logger.info(msg); @@ -1935,6 +1994,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. /* * @see javax.servlet.ServletContext#log(java.lang.String, java.lang.Throwable) */ + @Override public void log(String message, Throwable throwable) { _logger.warn(message,throwable); @@ -1944,6 +2004,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. /* * @see javax.servlet.ServletContext#getInitParameter(java.lang.String) */ + @Override public String getInitParameter(String name) { return ContextHandler.this.getInitParameter(name); @@ -1954,6 +2015,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. * @see javax.servlet.ServletContext#getInitParameterNames() */ @SuppressWarnings("unchecked") + @Override public Enumeration getInitParameterNames() { return ContextHandler.this.getInitParameterNames(); @@ -1963,6 +2025,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. /* * @see javax.servlet.ServletContext#getAttribute(java.lang.String) */ + @Override public synchronized Object getAttribute(String name) { Object o = ContextHandler.this.getAttribute(name); @@ -1976,6 +2039,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. * @see javax.servlet.ServletContext#getAttributeNames() */ @SuppressWarnings("unchecked") + @Override public synchronized Enumeration getAttributeNames() { HashSet<String> set = new HashSet<String>(); @@ -1996,6 +2060,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. /* * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object) */ + @Override public synchronized void setAttribute(String name, Object value) { checkManagedAttribute(name,value); @@ -2028,6 +2093,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. /* * @see javax.servlet.ServletContext#removeAttribute(java.lang.String) */ + @Override public synchronized void removeAttribute(String name) { checkManagedAttribute(name,null); @@ -2057,6 +2123,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. /* * @see javax.servlet.ServletContext#getServletContextName() */ + @Override public String getServletContextName() { String name = ContextHandler.this.getDisplayName(); @@ -2066,6 +2133,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ + @Override public String getContextPath() { if ((_contextPath != null) && _contextPath.equals(URIUtil.SLASH)) @@ -2082,6 +2150,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. } /* ------------------------------------------------------------ */ + @Override public boolean setInitParameter(String name, String value) { if (ContextHandler.this.getInitParameter(name) != null) @@ -2090,6 +2159,242 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server. 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); + } + + @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/IPAccessHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/IPAccessHandler.java index c6e9e80ac1..b4642ece91 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 @@ public class IPAccessHandler extends HandlerWrapper 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/RequestLogHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/RequestLogHandler.java index bef865cf9d..b5bc248f3a 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,12 +20,12 @@ package org.eclipse.jetty.server.handler; import java.io.IOException; +import javax.servlet.DispatcherType; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; 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/AbstractSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java index 2211d394c4..9b72c1dccb 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 @@ package org.eclipse.jetty.server.session; 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 @@ import org.eclipse.jetty.util.statistic.SampleStatistic; 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 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement 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 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement 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 abstract class AbstractSessionManager extends AbstractLifeCycle implement /* ------------------------------------------------------------ */ public AbstractSessionManager() { + setSessionTrackingModes(__defaultSessionTrackingModes); } /* ------------------------------------------------------------ */ @@ -149,6 +168,16 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement return _context.getContextHandler(); } + public String getSessionPath() + { + return _sessionPath; + } + + public int getMaxCookieAge() + { + return _maxCookieAge; + } + /* ------------------------------------------------------------ */ public HttpCookie access(HttpSession session,boolean secure) { @@ -156,14 +185,14 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement 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 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement return _sessionIdManager; } - /* ------------------------------------------------------------ */ - public int getMaxCookieAge() - { - return _maxCookieAge; - } /* ------------------------------------------------------------ */ /** * @return seconds */ + @Override public int getMaxInactiveInterval() { return _dftMaxIdleSecs; @@ -377,13 +402,38 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement /* ------------------------------------------------------------ */ /** - * @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 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement } /* ------------------------------------------------------------ */ + /** + * 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 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement 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 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement throw new UnsupportedOperationException(); } - /* ------------------------------------------------------------ */ - public String getSessionPath() - { - return _sessionPath; - } + /* ------------------------------------------------------------ */ public int getSessions() @@ -560,15 +648,7 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement _sessionIdManager=metaManager; } - /* ------------------------------------------------------------ */ - public void setMaxCookieAge(int maxCookieAgeInSeconds) - { - _maxCookieAge=maxCookieAgeInSeconds; - if (_maxCookieAge>0 && _refreshCookieAge==0) - _refreshCookieAge=_maxCookieAge/3; - - } /* ------------------------------------------------------------ */ /** @@ -579,15 +659,6 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement _dftMaxIdleSecs=seconds; } - /* ------------------------------------------------------------ */ - /** - * @deprecated use {@link #setSessionIdManager(SessionIdManager)} - */ - @Deprecated - public void setMetaManager(SessionIdManager metaManager) - { - setSessionIdManager(metaManager); - } /* ------------------------------------------------------------ */ public void setRefreshCookieAge(int ageInSeconds) @@ -596,34 +667,13 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement } - /* ------------------------------------------------------------ */ - /** - * 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 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement _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 abstract class AbstractSessionManager extends AbstractLifeCycle implement } /* ------------------------------------------------------------ */ + 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 7f888a99d7..19ee760a51 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 @@ -27,6 +27,7 @@ import java.io.ObjectInputStream; 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/JDBCSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java index e63704793a..d37b4eba0c 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.HashMap; 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; 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 818dc10bf4..1034efed97 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 @@ public class SessionHandler extends ScopedHandler { 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 @@ public class SessionHandler extends ScopedHandler 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 7eaa152400..ea04fc2f44 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.net.Socket; 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 @@ public class AsyncStressTest 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 @@ public class AsyncStressTest } } } + + + private static AsyncListener __asyncListener = new AsyncListener() + { + @Override + public void onComplete(AsyncEvent event) throws IOException + { + } + @Override + public void onTimeout(AsyncEvent event) throws IOException + { + event.getSuppliedRequest().setAttribute("TIMEOUT",Boolean.TRUE); + event.getSuppliedRequest().getAsyncContext().dispatch(); + } - private static ContinuationListener __asyncListener = new ContinuationListener() - { - public void onComplete(Continuation continuation) + @Override + public void onError(AsyncEvent event) throws IOException { + } - public void onTimeout(Continuation continuation) + @Override + public void onStartAsync(AsyncEvent event) throws IOException { - continuation.setAttribute("TIMEOUT",Boolean.TRUE); - continuation.resume(); } }; } 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 1680c9b2fd..01b270bb68 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 org.eclipse.jetty.continuation.Continuation; -import org.eclipse.jetty.continuation.ContinuationListener; +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.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 @@ public class LocalAsyncContextTest _server.setHandler(session); _server.start(); } - + protected Connector initConnector() { return new LocalConnector(); @@ -64,76 +72,123 @@ public class LocalAsyncContextTest @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 @@ public class LocalAsyncContextTest 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 + { + 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 int getRead() + { + 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() { - public void onComplete(Continuation continuation) + + @Override + public void onComplete(AsyncEvent event) throws IOException { + __completed1.incrementAndGet(); } - public void onTimeout(Continuation continuation) + @Override + public void onError(AsyncEvent event) throws IOException { - continuation.setAttribute("TIMEOUT",Boolean.TRUE); - continuation.resume(); } + @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 766031bd72..408feefa62 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.assertFalse; 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.Enumeration; 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.http.MimeTypes; 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 @@ public class RequestTest 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 @@ public class RequestTest 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 @@ public class RequestTest 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 @@ public class RequestTest { ((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,43 @@ public class RequestTest } } + + 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); + + 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 0c205b0bfc..ece7e2e964 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.net.Socket; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; +import java.util.Iterator; import java.util.Locale; import java.util.Map; @@ -104,14 +105,15 @@ public class ResponseTest 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(); 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 e44b9cdaf3..8419806862 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.eclipse.jetty.util.IO; 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 78dff1721e..aafaef02ff 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 @@ package org.eclipse.jetty.server; 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 @@ class SuspendHandler extends HandlerWrapper { _completeAfter = completeAfter; } - + @Override public void handle(String target, final Request baseRequest, final HttpServletRequest request, final HttpServletResponse response) throws IOException, ServletException { @@ -97,7 +100,7 @@ class SuspendHandler extends HandlerWrapper } final AsyncContext asyncContext = baseRequest.startAsync(); - asyncContext.addContinuationListener(LocalAsyncContextTest.__asyncListener); + asyncContext.addListener(this); if (_suspendFor>0) asyncContext.setTimeout(_suspendFor); @@ -110,7 +113,7 @@ class SuspendHandler extends HandlerWrapper try { Thread.sleep(_completeAfter); - response.getOutputStream().print("COMPLETED"); + response.getOutputStream().println("COMPLETED"); response.setStatus(200); baseRequest.setHandled(true); asyncContext.complete(); @@ -124,7 +127,7 @@ class SuspendHandler extends HandlerWrapper } else if (_completeAfter==0) { - response.getOutputStream().print("COMPLETED"); + response.getOutputStream().println("COMPLETED"); response.setStatus(200); baseRequest.setHandled(true); asyncContext.complete(); @@ -167,4 +170,25 @@ class SuspendHandler extends HandlerWrapper } } -}
\ No newline at end of file + @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 0000000000..6792eff573 --- /dev/null +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionCookieTest.java @@ -0,0 +1,199 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.server.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 8ad58a7726..28c57e0e38 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.BufferedReader; 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 @@ public class SessionHandlerTest 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 @@ public class SessionHandlerTest SessionHandler sessionHandler = new SessionHandler(); sessionHandler.setSessionManager(new MockSessionManager() - { + { + @Override public String getSessionIdPathParameterName() { @@ -396,6 +498,114 @@ public class SessionHandlerTest 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 @@ public class SessionHandlerTest { } + /** + * @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 cb80ecefd8..7b6c31b5b1 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>jetty-servlet</artifactId> @@ -25,7 +25,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/FilterHolder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterHolder.java index 4078898e67..e5ef8c2301 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.Collection; 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,24 @@ public class FilterHolder extends Holder<Filter> */ public FilterHolder() { + super (Source.EMBEDDED); } /* ---------------------------------------------------------------- */ /** Constructor */ + public FilterHolder(Holder.Source source) + { + super (source); + } + + /* ---------------------------------------------------------------- */ + /** Constructor + */ public FilterHolder(Class<? extends Filter> filter) { + super (Source.EMBEDDED); setHeldClass(filter); } @@ -68,6 +79,7 @@ public class FilterHolder extends Holder<Filter> */ public FilterHolder(Filter filter) { + super (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 fb98819592..195e5d6b09 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 @@ package org.eclipse.jetty.servlet; 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,8 +172,6 @@ public class FilterMapping implements Dumpable } } - - /* ------------------------------------------------------------ */ /** * @param dispatches The dispatches to set. 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 dc080d5d28..2bef5dfea0 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.HashSet; 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 @@ import org.eclipse.jetty.util.log.Logger; */ 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; @@ -59,10 +61,16 @@ public class Holder<T> extends AbstractLifeCycle implements Dumpable protected ServletHandler _servletHandler; /* ---------------------------------------------------------------- */ - protected Holder() + protected Holder(Source source) { + _source=source; } - + + public Source getSource() + { + return _source; + } + /* ------------------------------------------------------------ */ /** * @return True if this holder was created for a specific instance. @@ -79,7 +87,7 @@ public class Holder<T> extends AbstractLifeCycle implements Dumpable { //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, -1); //try to load class if (_class==null) @@ -341,6 +349,12 @@ public class Holder<T> extends AbstractLifeCycle implements Dumpable 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); @@ -351,20 +365,28 @@ public class Holder<T> extends AbstractLifeCycle implements Dumpable { 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 c8ec32a2b9..055060ae91 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 @@ package org.eclipse.jetty.servlet; 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.List; 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.ErrorHandler; 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 @@ public class ServletContextHandler extends ContextHandler 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 @@ public class ServletContextHandler extends ContextHandler { return getServletHandler().addFilterWithMapping(filterClass,pathSpec,dispatches); } - - /* ------------------------------------------------------------ */ - /** conveniance method to add a filter + /** + * 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 */ - public void addFilter(FilterHolder holder,String pathSpec,int dispatches) - { - getServletHandler().addFilterWithMapping(holder,pathSpec,dispatches); + protected ServletRegistration.Dynamic dynamicHolderAdded(ServletHolder holder) { + return holder.getRegistration(); } - /* ------------------------------------------------------------ */ - /** convenience method to add a filter + /** + * delegate for ServletContext.declareRole method + * @param roleNames role names to add */ - public FilterHolder addFilter(Class<? extends Filter> filterClass,String pathSpec,int dispatches) - { - return getServletHandler().addFilterWithMapping(filterClass,pathSpec,dispatches); + 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); + } } - /* ------------------------------------------------------------ */ - /** convenience method to add a filter + /** + * 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 FilterHolder addFilter(String filterClass,String pathSpec,int dispatches) + public Set<String> setServletSecurity(ServletRegistration.Dynamic registration, ServletSecurityElement servletSecurityElement) { - return getServletHandler().addFilterWithMapping(filterClass,pathSpec,dispatches); + //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(); } - - public void callContextInitialized(ServletContextListener l, ServletContextEvent e) - { - l.contextInitialized(e); + + public void restrictEventListener (EventListener e) + { + if (_restrictListeners && e instanceof ServletContextListener) + _restrictedContextListeners = LazyList.add(_restrictedContextListeners, e); } + public boolean isRestrictListeners() { + return _restrictListeners; + } - public void callContextDestroyed(ServletContextListener l, ServletContextEvent e) + public void setRestrictListeners(boolean restrictListeners) { + this._restrictListeners = restrictListeners; + } + + public void callContextInitialized(ServletContextListener l, ServletContextEvent e) { - l.contextDestroyed(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) + { + super.callContextDestroyed(l, e); + } + /* ------------------------------------------------------------ */ /** @@ -456,6 +517,290 @@ public class ServletContextHandler extends ContextHandler } /* ------------------------------------------------------------ */ + 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 class ServletContextHandler extends ContextHandler 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 @@ public class ServletContextHandler extends ContextHandler /** * @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,160 @@ public class ServletContextHandler extends ContextHandler /** * @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(); + + //TODO handle partial registrations + 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 +1069,7 @@ public class ServletContextHandler extends ContextHandler } /* ------------------------------------------------------------ */ + @Override public <T extends Servlet> T createServlet(Class<T> c) throws ServletException { try @@ -629,16 +1091,39 @@ public class ServletContextHandler extends ContextHandler 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 +1135,22 @@ public class ServletContextHandler extends ContextHandler 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 +1162,71 @@ public class ServletContextHandler extends ContextHandler 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 +1245,36 @@ public class ServletContextHandler extends ContextHandler } } - + + @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 b0812c544b..dfc1d2ae67 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,8 +21,11 @@ package org.eclipse.jetty.servlet; 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.HashSet; +import java.util.Set; import java.util.List; import java.util.Map; import java.util.Queue; @@ -30,14 +33,18 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentMap; +import javax.servlet.AsyncContext; +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; @@ -51,7 +58,7 @@ import org.eclipse.jetty.security.IdentityService; import org.eclipse.jetty.security.SecurityHandler; import org.eclipse.jetty.server.AbstractHttpConnection; import org.eclipse.jetty.server.Dispatcher; -import org.eclipse.jetty.server.DispatcherType; +import org.eclipse.jetty.server.AbstractHttpConnection; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServletRequestHttpWrapper; @@ -279,6 +286,7 @@ public class ServletHandler extends ScopedHandler */ public ServletMapping getServletMapping(String pattern) { + ServletMapping theMapping = null; if (_servletMappings!=null) { for (ServletMapping m:_servletMappings) @@ -289,12 +297,12 @@ public class ServletHandler extends ScopedHandler for (String path:paths) { if (pattern.equals(path)) - return m; + theMapping = m; } } } } - return null; + return theMapping; } /* ------------------------------------------------------------ */ @@ -336,8 +344,8 @@ public class ServletHandler extends ScopedHandler 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); @@ -355,7 +363,7 @@ public class ServletHandler extends ScopedHandler // 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); @@ -761,12 +769,13 @@ public class ServletHandler extends ScopedHandler /** * see also newServletHolder(Class) */ - public ServletHolder newServletHolder() + public ServletHolder newServletHolder(Holder.Source source) { - return new ServletHolder(); + return new ServletHolder(source); } /* ------------------------------------------------------------ */ + /** Convenience method to add a servlet Holder. public ServletHolder newServletHolder(Class<? extends Servlet> servlet) { return new ServletHolder(servlet); @@ -791,7 +800,7 @@ public class ServletHandler extends ScopedHandler */ 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)); addServletWithMapping(holder,pathSpec); @@ -846,23 +855,23 @@ public class ServletHandler extends ScopedHandler { 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) { @@ -879,7 +888,7 @@ public class ServletHandler extends ScopedHandler */ 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); @@ -895,7 +904,7 @@ public class ServletHandler extends ScopedHandler */ public FilterHolder addFilterWithMapping (String className,String pathSpec,EnumSet<DispatcherType> dispatches) { - FilterHolder holder = newFilterHolder(); + FilterHolder holder = newFilterHolder(Holder.Source.EMBEDDED); holder.setName(className+"-"+_filters.length); holder.setClassName(className); @@ -938,7 +947,6 @@ public class ServletHandler extends ScopedHandler } - /* ------------------------------------------------------------ */ /** Convenience method to add a filter. * @param filter class of filter to create @@ -948,7 +956,8 @@ public class ServletHandler extends ScopedHandler */ 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; @@ -1006,14 +1015,13 @@ public class ServletHandler extends ScopedHandler } - /* ------------------------------------------------------------ */ /** 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) { @@ -1152,7 +1160,7 @@ public class ServletHandler extends ScopedHandler 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++) @@ -1185,7 +1193,7 @@ public class ServletHandler extends ScopedHandler try { - if (isStarted()) + if (_contextHandler!=null && _contextHandler.isStarted() || _contextHandler==null && isStarted()) initialize(); } catch (Exception e) @@ -1262,7 +1270,6 @@ public class ServletHandler extends ScopedHandler invalidateChainsCache(); } - /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ private class CachedChain implements FilterChain 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 7f2372bcf1..d3499f62c1 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.Map; 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.security.RunAsToken; 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 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope 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 class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope */ public ServletHolder() { + super (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) { + super (Source.EMBEDDED); setServlet(servlet); } /* ---------------------------------------------------------------- */ /** Constructor for servlet class. */ - public ServletHolder(String name,Class<? extends Servlet> servlet) + public ServletHolder(String name, Class<? extends Servlet> servlet) { + super (Source.EMBEDDED); setName(name); setHeldClass(servlet); } @@ -123,8 +123,19 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope /* ---------------------------------------------------------------- */ /** Constructor for servlet class. */ + public ServletHolder(String name, Servlet servlet) + { + super (Source.EMBEDDED); + setName(name); + setServlet(servlet); + } + + /* ---------------------------------------------------------------- */ + /** Constructor for servlet class. + */ public ServletHolder(Class<? extends Servlet> servlet) { + super (Source.EMBEDDED); setHeldClass(servlet); } @@ -262,20 +273,47 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope _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); + throw ue; + } + + try + { checkServletType(); } catch (UnavailableException ue) { makeUnavailable(ue); + if (!_servletHandler.isStartWithUnavailable()) + throw ue; //servlet is not an instance of javax.servlet.Servlet } + _identityService = _servletHandler.getIdentityService(); if (_identityService!=null && _runAsRole!=null) @@ -472,12 +510,13 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope // 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) { @@ -533,52 +572,26 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope } } - 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); - } - 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); + //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()); } - } - /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.server.UserIdentity.Scope#getContextPath() @@ -645,10 +658,14 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope // 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 +734,33 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope /* -------------------------------------------------------- */ /* -------------------------------------------------------- */ 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 +773,27 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope { 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 +804,35 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope { 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 +952,3 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope } } } - - - - - 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 03b6ead91f..f25000910c 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 @@ public class ServletMapping { private String[] _pathSpecs; private String _servletName; + private boolean _default; + /* ------------------------------------------------------------ */ public ServletMapping() @@ -77,6 +79,25 @@ public class ServletMapping _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 d2c95838b1..0000000000 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/api/FilterRegistration.java +++ /dev/null @@ -1,45 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== -// - -package org.eclipse.jetty.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 dca5eae41c..0000000000 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/api/Registration.java +++ /dev/null @@ -1,43 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== -// - -package org.eclipse.jetty.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/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncContextTest.java index 8da745768a..a3707b39a6 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,6 +27,8 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; +import javax.servlet.AsyncContext; +import javax.servlet.DispatcherType; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; @@ -35,11 +37,8 @@ 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.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; @@ -89,9 +88,9 @@ public class AsyncContextTest { String request = "GET /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()); @@ -294,27 +293,18 @@ public class AsyncContextTest @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 +318,41 @@ public class AsyncContextTest @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 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/DefaultServletTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DefaultServletTest.java index fae6e0bfd1..fc211a0b23 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,7 +23,8 @@ import static org.junit.Assert.assertTrue; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; - +import java.util.EnumSet; +import javax.servlet.DispatcherType; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; @@ -562,7 +563,7 @@ public class DefaultServletTest 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); @@ -571,7 +572,7 @@ public class DefaultServletTest 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 d339511442..c14a9da7f5 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 @@ -24,9 +24,11 @@ import static org.junit.Assert.assertTrue; 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; @@ -298,7 +300,7 @@ public class DispatcherTest _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"); 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 0000000000..a83882ded0 --- /dev/null +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/HolderTest.java @@ -0,0 +1,95 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +/* + * 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-servlets/pom.xml b/jetty-servlets/pom.xml index c8b365cc47..4495cd233d 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>jetty-servlets</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-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java index 7e38d0f21e..1a7b25ed4f 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 @@ -344,17 +344,18 @@ public class DoSFilter implements Filter // Reject this request if (_insertHeaders) ((HttpServletResponse)response).addHeader("DoSFilter","unavailable"); + ((HttpServletResponse)response).sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); return; } case 0: { - // fall through to throttle code + // fall through to throttle code request.setAttribute(__TRACKER,tracker); break; } default: - { + { // insert a delay before throttling the request if (_insertHeaders) ((HttpServletResponse)response).addHeader("DoSFilter","delayed"); @@ -362,12 +363,22 @@ public class DoSFilter implements Filter request.setAttribute(__TRACKER,tracker); if (_delayMs > 0) continuation.setTimeout(_delayMs); + continuation.addContinuationListener(new ContinuationListener() + { + + public void onComplete(Continuation continuation) + { + } + + public void onTimeout(Continuation continuation) + { + } + }); continuation.suspend(); return; } } } - // Throttle the request boolean accepted = false; try @@ -421,6 +432,10 @@ public class DoSFilter implements Filter _context.log("DoS",e); ((HttpServletResponse)response).sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); } + catch (Exception e) + { + e.printStackTrace(); + } finally { if (accepted) 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 bceb6b5efd..d466de3464 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 @@ -20,17 +20,15 @@ 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; @@ -42,16 +40,20 @@ import java.util.Map; 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.B64Code; + +import org.eclipse.jetty.util.IO; 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.StringUtil; import org.eclipse.jetty.util.TypeUtil; @@ -75,15 +77,21 @@ import org.eclipse.jetty.util.TypeUtil; * 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 { 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(); /* ------------------------------------------------------------------------------- */ @@ -97,6 +105,13 @@ public class MultiPartFilter implements Filter 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) @@ -121,15 +136,11 @@ public class MultiPartFilter implements Filter InputStream in = new BufferedInputStream(request.getInputStream()); String content_type=srequest.getContentType(); - // TODO - handle encodings - String boundary="--"+QuotedStringTokenizer.unquote(value(content_type.substring(content_type.indexOf("boundary="))).trim()); - - 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); @@ -137,231 +148,42 @@ public class MultiPartFilter implements Filter params.add(entry.getKey(),value); } + 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 - byte[] bytes=TypeUtil.readLine(in); - String line=bytes==null?null:new String(bytes,"UTF-8"); - if(line==null || !line.equals(boundary)) - { - throw new IOException("Missing initial multi part boundary"); - } - - // Read each part - boolean lastPart=false; - String content_disposition=null; - String content_transfer_encoding=null; - - - outer:while(!lastPart && params.size()<_maxFormKeys) + Collection<Part> parts = mpis.getParts(); + if (parts != null) { - String type_content=null; - - while(true) - { - // read a line - bytes=TypeUtil.readLine(in); - if (bytes==null) - break outer; - - // If blank line, end of part headers - if(bytes.length==0) - break; - line=new String(bytes,"UTF-8"); - - // place part header key and value in map - 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(); - 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"); - } - - QuotedStringTokenizer tok=new QuotedStringTokenizer(content_disposition,";"); - String name=null; - String filename=null; - while(tok.hasMoreTokens()) + Iterator<Part> itor = parts.iterator(); + while (itor.hasNext() && params.size() < _maxFormKeys) { - 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=value(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) + Part p = itor.next(); + MultiPartInputStream.MultiPart mp = (MultiPartInputStream.MultiPart)p; + if (mp.getFile() != null) { - 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(); - } - - - if ("base64".equalsIgnoreCase(content_transfer_encoding)) - { - in = new Base64InputStream(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) - state=in.read(); - 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; + 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()); } } - finally - { - out.close(); - } - - if (file==null) - { - 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); } @@ -370,26 +192,27 @@ public class MultiPartFilter implements Filter 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); } /* ------------------------------------------------------------ */ @@ -530,37 +353,4 @@ public class MultiPartFilter implements Filter _encoding=enc; } } - - private static class Base64InputStream extends InputStream - { - BufferedReader _in; - String _line; - byte[] _buffer; - int _pos; - - public Base64InputStream (InputStream in) - { - _in = new BufferedReader(new InputStreamReader(in)); - } - - @Override - public int read() throws IOException - { - if (_buffer==null || _pos>= _buffer.length) - { - _line = _in.readLine(); - if (_line==null) - return -1; - if (_line.startsWith("--")) - _buffer=(_line+"\r\n").getBytes(); - else if (_line.length()==0) - _buffer="\r\n".getBytes(); - else - _buffer=B64Code.decode(_line); - - _pos=0; - } - return _buffer[_pos++]; - } - } } 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 722cd5331d..483cd59034 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 static org.junit.Assert.assertTrue; 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; @@ -62,7 +63,7 @@ public abstract class AbstractDoSFilterTest _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 +72,7 @@ public abstract class AbstractDoSFilterTest _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"); @@ -209,7 +210,7 @@ public abstract class AbstractDoSFilterTest 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 +247,8 @@ public abstract class AbstractDoSFilterTest 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 56e2cf3527..30ea8865ae 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 @@ public class AsyncProxyServer 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 665eb8e9f8..4203e6a91d 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 93af56813a..faad9b6146 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 @@ public class CrossOriginFilterTest @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 @@ public class CrossOriginFilterTest 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 @@ public class CrossOriginFilterTest 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 @@ public class CrossOriginFilterTest 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 @@ public class CrossOriginFilterTest 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 @@ public class CrossOriginFilterTest 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 @@ public class CrossOriginFilterTest { 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 @@ public class CrossOriginFilterTest // 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 @@ public class CrossOriginFilterTest // 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 @@ public class CrossOriginFilterTest { 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 @@ public class CrossOriginFilterTest 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 @@ public class CrossOriginFilterTest { 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 class CrossOriginFilterTest 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 @@ public class CrossOriginFilterTest { 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 @@ public class CrossOriginFilterTest 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/DoSFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/DoSFilterTest.java index f7aa7e4a9c..9bfb3acadb 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 @@ -45,7 +45,8 @@ public class DoSFilterTest extends AbstractDoSFilterTest @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/GzipWithPipeliningTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipWithPipeliningTest.java index dc627730b2..4ce84495e9 100644 --- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipWithPipeliningTest.java +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipWithPipeliningTest.java @@ -34,6 +34,7 @@ import java.io.InputStream; import java.net.URI; import java.security.DigestOutputStream; import java.security.MessageDigest; +import java.util.EnumSet; import java.util.Arrays; import java.util.Collection; import java.util.regex.Matcher; @@ -42,6 +43,8 @@ import java.util.zip.GZIPInputStream; import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; +import javax.servlet.DispatcherType; + import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.DefaultServlet; @@ -109,7 +112,7 @@ public class GzipWithPipeliningTest holder.setInitParameter("resourceBase",MavenTestingUtils.getTestResourcesDir().getAbsolutePath()); context.addServlet(holder,"/"); - FilterHolder filter = context.addFilter(GzipFilter.class,"/*",0); + FilterHolder filter = context.addFilter(GzipFilter.class,"/*", EnumSet.of(DispatcherType.REQUEST)); filter.setInitParameter("mimeTypes","text/plain"); server.setHandler(context); 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 b319debc32..7a073043b8 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 @@ public class IncludableGzipFilterTest 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 97da7c63f2..38e2c8bbdc 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 @@ -25,7 +25,13 @@ import static org.junit.Assert.assertNotNull; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.io.OutputStream; +import java.net.Socket; +import java.net.URL; +import java.util.EnumSet; +import java.util.Enumeration; +import javax.servlet.DispatcherType; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; @@ -45,7 +51,7 @@ public class MultipartFilterTest private File _dir; private ServletTester tester; - + public static class TestServlet extends DumpServlet { @@ -55,7 +61,6 @@ public class MultipartFilterTest 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); } @@ -76,7 +81,8 @@ public class MultipartFilterTest 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(); } @@ -115,7 +121,7 @@ public class MultipartFilterTest response.parse(tester.getResponses(request.generate())); assertTrue(response.getMethod()==null); - assertEquals(HttpServletResponse.SC_OK,response.getStatus()); + assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,response.getStatus()); } @@ -263,8 +269,7 @@ public class MultipartFilterTest @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - assertEquals("How now brown cow.", req.getParameterMap().get("strupContent-Type: application/octet-stream")); - + assertEquals("How now brown cow.", req.getParameterMap().get("strupContent-Type:")); 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 300430791f..d4a08b069d 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.FileInputStream; 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 @@ public class PutFilterTest 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 826a11a4f0..9997eaeaec 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 static org.junit.Assert.assertTrue; 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 @@ public class QoSFilterTest 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 @@ public class QoSFilterTest 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/gzip/GzipTester.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java index de3aa98fb3..77f47766af 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 @@ -35,11 +35,13 @@ import java.io.InputStream; 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; @@ -460,7 +462,7 @@ public class GzipTester servletTester.setResourceBase(testdir.getDir().getCanonicalPath()); ServletHolder servletHolder = servletTester.addServlet(servletClass,"/"); servletHolder.setInitParameter("baseDir",testdir.getDir().getAbsolutePath()); - FilterHolder holder = servletTester.addFilter(gzipFilterClass,"/*",0); + FilterHolder holder = servletTester.addFilter(gzipFilterClass,"/*",EnumSet.allOf(DispatcherType.class)); return holder; } diff --git a/jetty-spdy/pom.xml b/jetty-spdy/pom.xml index ed6cff25b7..1dd3d9e8a1 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> diff --git a/jetty-spdy/spdy-core/pom.xml b/jetty-spdy/spdy-core/pom.xml index 953eaffb9a..b4b2f4747c 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.8-SNAPSHOT</version> + <version>8.1.8-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 295c777f6c..b45b01f58d 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.8-SNAPSHOT</version> + <version>8.1.8-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 dbbfee3b0b..ddee9a70c7 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.8-SNAPSHOT</version> + <version>8.1.8-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 ca6b17f3b1..4825968f4e 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.8-SNAPSHOT</version> + <version>8.1.8-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 ef22b94371..5d51f61ca1 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.8-SNAPSHOT</version> + <version>8.1.8-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 c75ffd022c..23bb426162 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-io-$(version).jar $(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-server-$(version).jar $(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 @@ $(jetty.home)/lib/jetty-policy-$(version).jar [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 7df9a67d53..74e05d2f33 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>jetty-util</artifactId> @@ -24,7 +24,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> @@ -70,6 +70,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/MultiMap.java b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiMap.java index 85bf6110d0..9220d8151d 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 @@ public class MultiMap<K> implements ConcurrentMap<K,Object>, Serializable * @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 0000000000..4077ed2852 --- /dev/null +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStream.java @@ -0,0 +1,750 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.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.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.servlet.MultipartConfigElement; +import javax.servlet.ServletException; +import javax.servlet.http.Part; + + + +/** + * MultiPartInputStream + * + * Handle a MultiPart Mime input stream, breaking it up on the boundary into files and strings. + */ +public class MultiPartInputStream +{ + 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 + { + _file = File.createTempFile("MultiPart", "", MultiPartInputStream.this._tmpDir); + 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(), 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 BufferedInputStream(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 boundary="--"+QuotedStringTokenizer.unquote(value(_contentType.substring(_contentType.indexOf("boundary=")), true).trim()); + byte[] byteBoundary=(boundary+"--").getBytes(StringUtil.__ISO_8859_1); + + // Get first boundary + byte[] bytes=TypeUtil.readLine(_in); + String line=bytes==null?null:new String(bytes,"UTF-8"); + if(line==null || !line.equals(boundary)) + { + throw new IOException("Missing initial multi part boundary"); + } + + // Read each part + boolean lastPart=false; + String contentDisposition=null; + String contentType=null; + String contentTransferEncoding=null; + outer:while(!lastPart) + { + MultiMap<String> headers = new MultiMap<String>(); + while(true) + { + bytes=TypeUtil.readLine(_in); + if(bytes==null) + break outer; + + // If blank line, end of part headers + if(bytes.length==0) + break; + + total += bytes.length; + if (_config.getMaxRequestSize() > 0 && total > _config.getMaxRequestSize()) + throw new IllegalStateException ("Request exceeds maxRequestSize ("+_config.getMaxRequestSize()+")"); + + line=new String(bytes,"UTF-8"); + + //get content-disposition and content-type + int c=line.indexOf(':',0); + if(c>0) + { + String key=line.substring(0,c).trim().toLowerCase(); + 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,";"); + 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, true); + else if(tl.startsWith("filename=")) + filename=value(t, false); + } + + // 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; + } + + if ("base64".equalsIgnoreCase(contentTransferEncoding)) + { + _in = new Base64InputStream(_in); + } + else if ("quoted-printable".equalsIgnoreCase(contentTransferEncoding)) + { + _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; + } + }; + } + + + + //Have a new Part + MultiPart part = new MultiPart(name, filename); + part.setHeaders(headers); + part.setContentType(contentType); + _parts.add(name, part); + + part.open(); + + 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:_in.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) + state=_in.read(); + break; + } + // look for boundary + if(b>=0&&b<byteBoundary.length&&c==byteBoundary[b]) + b++; + else + { + // this is not a boundary + 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 partial boundary + 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(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; + } + + private static class Base64InputStream extends InputStream + { + BufferedReader _in; + String _line; + byte[] _buffer; + int _pos; + + public Base64InputStream (InputStream in) + { + _in = new BufferedReader(new InputStreamReader(in)); + } + + @Override + public int read() throws IOException + { + if (_buffer==null || _pos>= _buffer.length) + { + _line = _in.readLine(); + if (_line==null) + return -1; + if (_line.startsWith("--")) + _buffer=(_line+"\r\n").getBytes(); + else if (_line.length()==0) + _buffer="\r\n".getBytes(); + else + _buffer=B64Code.decode(_line); + + _pos=0; + } + return _buffer[_pos++]; + } + } +} 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 0000000000..fffda46b32 --- /dev/null +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/MultiPartInputStreamTest.java @@ -0,0 +1,328 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.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.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; + +/** + * 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 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 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()); + } + + 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 28a4d39699..caa24d1732 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 @@ -115,21 +115,21 @@ public class ResourceTest /* ------------------------------------------------------------ */ @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(); __userURL=uri.toURL(); - + __userURL = new URL(__userURL.toString() + "src/test/java/org/eclipse/jetty/util/resource/"); - FilePermission perm = (FilePermission) __userURL.openConnection().getPermission(); - __userDir = new File(perm.getName()).getCanonicalPath() + File.separatorChar; - __relDir = "src/test/java/org/eclipse/jetty/util/resource/".replace('/', File.separatorChar); - + FilePermission perm = (FilePermission) __userURL.openConnection().getPermission(); + __userDir = new File(perm.getName()).getCanonicalPath() + File.separatorChar; + __relDir = "src/test/java/org/eclipse/jetty/util/resource/".replace('/', File.separatorChar); + System.err.println("User Dir="+__userDir); System.err.println("Rel Dir="+__relDir); System.err.println("User URL="+__userURL); 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 9cf9496f31..972f6b3bff 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 @@ import org.junit.Test; 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 @@ public class TimeoutTest @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 a088601b74..85d2c3d484 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>jetty-webapp</artifactId> @@ -52,6 +52,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 8df641817d..988414b85f 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 @@ package org.eclipse.jetty.webapp; 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,15 +37,27 @@ public abstract class DiscoveredAnnotation 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) { + 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() { 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 884301ba13..3f185df449 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 @@ public class FragmentDescriptor extends WebDescriptor 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 4975c54c31..aab89c33ed 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 @@ import org.eclipse.jetty.xml.XmlConfiguration; * * 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 @@ public class JettyWebXmlConfiguration extends AbstractConfiguration 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 @@ public class JettyWebXmlConfiguration extends AbstractConfiguration 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 d6e6982124..cdd84b705d 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.HashMap; 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 @@ public class MetaData _webXmlRoot.parse(); _metaDataComplete=_webXmlRoot.getMetaDataComplete() == MetaDataComplete.True; + + if (_webXmlRoot.isOrdered()) { if (_ordering == null) @@ -197,7 +201,7 @@ public class MetaData _metaDataComplete=true; break; case False: - _metaDataComplete=true; + _metaDataComplete=false; break; case NotSet: break; @@ -267,12 +271,42 @@ public class MetaData */ 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 @@ public class MetaData 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 @@ public class MetaData 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 2190c30050..64f2dbab87 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 @@ public class MetaInfConfiguration extends AbstractConfiguration 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 7b0ada076c..3cdb86cefb 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 @@ public interface Ordering * 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 @@ public interface Ordering return orderedList; } - + @Override public boolean isAbsolute() { return true; @@ -119,7 +120,7 @@ public interface Ordering _order.add(OTHER); } - + @Override public boolean hasOther () { return _hasOther; @@ -146,7 +147,7 @@ public interface Ordering * 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 @@ public interface Ordering 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 1481b3804f..f2f941c478 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}
\ No newline at end of file +public enum Origin {NotSet, WebXml, WebDefaults, WebOverride, WebFragment, Annotation, API}
\ No newline at end of file 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 cc308ed30f..074aade440 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,23 +23,36 @@ import java.io.IOException; 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.ServletContextHandler; +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; @@ -194,7 +207,7 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor */ if (holder == null) { - holder = context.getServletHandler().newServletHolder(); + holder = context.getServletHandler().newServletHolder(Holder.Source.DESCRIPTOR); holder.setName(servlet_name); context.getServletHandler().addServlet(holder); } @@ -458,6 +471,136 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor } } } + + 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; + } + } + } } @@ -484,7 +627,8 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor { //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: @@ -495,14 +639,14 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor //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; } } @@ -522,6 +666,282 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor 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"); + Set<SessionTrackingMode> modes = new HashSet<SessionTrackingMode>(); + modes.addAll(context.getSessionHandler().getSessionManager().getEffectiveSessionTrackingModes()); + 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; + } + } + } + } } @@ -662,7 +1082,7 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor { //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; } } @@ -758,7 +1178,7 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor * @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); @@ -770,9 +1190,11 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor 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; } /** @@ -780,7 +1202,7 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor * @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); @@ -792,6 +1214,7 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor 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()])); @@ -832,6 +1255,18 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor 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); } /** @@ -840,7 +1275,16 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor * @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); @@ -849,19 +1293,51 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor } // 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) @@ -895,6 +1371,8 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor //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"); @@ -943,29 +1421,50 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor { 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) @@ -1165,7 +1664,7 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor 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); } @@ -1311,7 +1810,7 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor { //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: @@ -1321,14 +1820,14 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor //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; } } @@ -1406,7 +1905,7 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor { 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 a1f85963c5..9c218953f6 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 @@ public class WebAppClassLoader extends URLClassLoader { 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 96fd1ac6e0..45ffa3ad59 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.net.URL; 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.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceCollection; +import org.eclipse.jetty.util.security.Constraint; /* ------------------------------------------------------------ */ /** Web Application Context Handler. @@ -90,8 +102,8 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL "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 @@ -1237,6 +1249,79 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL 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 @@ -1287,6 +1372,8 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL } } + + } /* ------------------------------------------------------------ */ 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 03ef85fab0..c0af4a8067 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 @@ public class WebXmlConfiguration extends AbstractConfiguration 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 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 529cae4a75..fe109c48ce 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 @@ public class OrderingTest } @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 bd6ea7feb2..aae72c3e52 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.8-SNAPSHOT</version> + <version>8.1.8-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/test/java/org/eclipse/jetty/websocket/WebSocketParserD06Test.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketParserD06Test.java index 505dde6dba..1d17f9c0d0 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.BufferCache.CachedBuffer; 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 @@ public class WebSocketParserD06Test _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-xml/pom.xml b/jetty-xml/pom.xml index 8f69dae4db..f532f8b8be 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>jetty-xml</artifactId> @@ -6,14 +6,14 @@ <version>20</version> </parent> <artifactId>jetty-project</artifactId> - <version>7.6.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> <name>Jetty :: Project</name> <url>${jetty.url}</url> <packaging>pom</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <jetty.url>http://www.eclipse.org/jetty</jetty.url> - <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> @@ -29,8 +29,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> @@ -183,7 +183,7 @@ </execution> </executions> <configuration> - <targetJdk>1.5</targetJdk> + <targetJdk>1.6</targetJdk> <rulesets> <ruleset>jetty/pmd_logging_ruleset.xml</ruleset> </rulesets> @@ -376,6 +376,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> @@ -386,6 +387,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> @@ -711,8 +713,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 0000000000..9b5ddeffc7 --- /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 02fa6e2917..d853b66d60 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> @@ -28,16 +28,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 0000000000..b61f7326a0 --- /dev/null +++ b/test-continuation-jetty6/src/test/java/org/eclipse/jetty/continuation/ContinuationBase.java @@ -0,0 +1,428 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.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 0000000000..236f0428d1 --- /dev/null +++ b/test-continuation-jetty6/src/test/java/org/eclipse/jetty/continuation/FauxContinuationTest.java @@ -0,0 +1,83 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.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 f07979a561..426bdd5afe 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.8-SNAPSHOT</version> + <version>8.1.8-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 62edf176ed..74bb6acd34 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 @@ package org.eclipse.jetty.continuation; import java.io.IOException; import java.io.InputStream; +import java.util.EnumSet; import org.eclipse.jetty.continuation.test.ContinuationBase; import org.eclipse.jetty.server.Connector; @@ -49,6 +50,7 @@ public class ContinuationTest extends ContinuationBase _server.setHandler(servletContext); _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 7d0dd38e7e..19ba537399 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 @@ package org.eclipse.jetty.continuation; 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 @@ public class FauxContinuationTest extends ContinuationBase 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 adf850d21f..85ae9632db 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <artifactId>test-jetty-nested</artifactId> <name>Jetty :: Nested Test</name> diff --git a/test-jetty-servlet/pom.xml b/test-jetty-servlet/pom.xml index b4b732b719..2d792340eb 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.8-SNAPSHOT</version> + <version>8.1.8-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 0f360c12e6..41cab781df 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 @@ public class HttpTester 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 bd07b9cdb8..4d185c2a78 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; @@ -239,7 +242,7 @@ public class ServletTester * @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); } @@ -252,7 +255,7 @@ public class ServletTester * @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 45cbfe7353..71b4f6a620 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>test-jetty-webapp</artifactId> @@ -94,11 +94,11 @@ </execution> </executions> </plugin> -<!-- +<!-- <plugin> <groupId>org.mortbay.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> - <version>7.5.2-SNAPSHOT</version> + <version>${project.version}</version> <configuration> <stopPort>8087</stopPort> <stopKey>foo</stopKey> @@ -109,6 +109,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 9e42d6d5ef..7b7f9e4312 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> 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 c6ffe0ef7f..0530fa6b7c 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 @@ public class Dump extends HttpServlet @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"); 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 0000000000..e86598d281 --- /dev/null +++ b/test-jetty-webapp/src/main/java/com/acme/LoginServlet.java @@ -0,0 +1,92 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package 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 0000000000..8d99cd11a5 --- /dev/null +++ b/test-jetty-webapp/src/main/java/com/acme/RegTest.java @@ -0,0 +1,194 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package 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 faaa76085b..02674b4882 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.ServletRequestAttributeEvent; 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.HttpSessionBindingEvent; 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,30 @@ public class TestListener implements HttpSessionListener, HttpSessionAttributeL public void contextInitialized(ServletContextEvent sce) { - /* TODO for servlet 3.0 - * FilterRegistration registration=context.addFilter("TestFilter",TestFilter.class.getName()); - - + //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()); + 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 cc61b1a652..cfe644d8a6 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-Name: TestIt 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 f995d0162d..5a5149e53e 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> @@ -114,6 +105,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> @@ -130,6 +131,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> @@ -165,6 +167,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> @@ -187,6 +190,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> @@ -234,11 +238,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> @@ -266,6 +271,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 1b1de1157c..4b67966d26 100644 --- a/test-jetty-webapp/src/main/webapp/auth.html +++ b/test-jetty-webapp/src/main/webapp/auth.html @@ -18,6 +18,12 @@ This page contains several links to test the authentication constraints: <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 74ef907a75..630bc1da19 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/tests/pom.xml b/tests/pom.xml index b4761a4a69..cffe2358a6 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.8-SNAPSHOT</version> + <version>8.1.8-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 949d9b2b20..01a09ec818 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.8-SNAPSHOT</version> + <version>8.1.8-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 9bf59faa9c..19b1b674c8 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.8-SNAPSHOT</version> + <version>8.1.8-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 edbe06b94f..593bc6c2cd 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.8-SNAPSHOT</version> + <version>8.1.8-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 28979d63e0..bfcb15b95d 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.8-SNAPSHOT</version> + <version>8.1.8-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 a2fec154f5..6c28fa045b 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <artifactId>test-jdbc-sessions</artifactId> <name>Jetty Tests :: Sessions :: JDBC</name> diff --git a/tests/test-sessions/test-mongodb-sessions/pom.xml b/tests/test-sessions/test-mongodb-sessions/pom.xml index 4b28896243..32bb781a49 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.8-SNAPSHOT</version> + <version>8.1.8-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 1e6a7941a5..6eab3cf4c2 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.8-SNAPSHOT</version> + <version>8.1.8-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 7ba2b864c0..6b28ad1c5b 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.8-SNAPSHOT</version> + <version>8.1.8-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 3e5b85eca6..441ee23c93 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.8-SNAPSHOT</version> + <version>8.1.8-SNAPSHOT</version> </parent> <artifactId>test-webapp-rfc2616</artifactId> <name>Jetty Tests :: WebApp :: RFC2616</name> |