From 39438d8c62b5883e5a6b91f4a058c39ad80a6562 Mon Sep 17 00:00:00 2001 From: "alessandro.gherardi" Date: Sat, 18 Mar 2017 17:32:35 -0600 Subject: [PATCH] Got rid of unnecessary class HttpClientResponseInputStream. Removed call to entity stream's close() method, since closing the response is sufficient to release all resoures and the method throws an exception on httpclient 4.5.2 --- bom/pom.xml | 5 - bundles/jaxrs-ri/pom.xml | 6 - bundles/pom.xml | 1 - bundles/repackaged/jersey-guava/build.gradle | 132 - bundles/repackaged/jersey-guava/pom.xml | 270 -- .../src/main/resources/META-INF/MANIFEST.MF | 0 bundles/repackaged/pom.xml | 62 - .../apache/connector/ApacheConnector.java | 38 +- .../apache/connector/StreamingTest.java | 17 + .../grizzly/connector/GrizzlyConnector.java | 23 +- .../jetty/connector/JettyConnector.java | 45 +- .../netty/connector/JerseyClientHandler.java | 23 +- .../netty/connector/NettyConnector.java | 12 +- .../httpserver/GrizzlyHttpServerFactory.java | 3 +- .../jersey/jdkhttp/JdkHttpServerFactory.java | 3 +- .../servlet/ServletPropertiesDelegate.java | 16 +- .../jersey/servlet/WebComponent.java | 14 +- .../internal/PersistenceUnitBinder.java | 5 +- .../servlet/internal/ResponseWriter.java | 14 +- .../JerseyServletContainerInitializer.java | 11 +- .../jetty/JettyHttpContainerFactory.java | 3 +- .../client/ClientBackgroundScheduler.java | 71 + .../ClientBackgroundSchedulerLiteral.java | 66 + .../glassfish/jersey/client/ClientBinder.java | 15 +- .../glassfish/jersey/client/ClientConfig.java | 81 +- .../jersey/client/ClientExecutor.java | 115 + .../jersey/client/ClientProperties.java | 28 +- .../jersey/client/ClientRequest.java | 3 +- .../jersey/client/ClientResponse.java | 37 +- .../jersey/client/ClientRuntime.java | 149 +- ...aultClientBackgroundSchedulerProvider.java | 69 + .../jersey/client/InboundJaxrsResponse.java | 9 +- .../glassfish/jersey/client/JerseyClient.java | 6 +- .../jersey/client/JerseyInvocation.java | 61 +- .../jersey/client/JerseyWebTarget.java | 4 +- .../RequestProcessingInitializationStage.java | 30 +- .../jersey/client/filter/EncodingFilter.java | 7 +- .../client/internal/HttpUrlConnector.java | 43 +- .../glassfish/jersey/client/ClientRxTest.java | 4 +- .../JerseyCompletionStageRxInvokerTest.java | 3 +- .../jersey/client/JerseyWebTargetTest.java | 7 +- core-common/pom.xml | 6 - ....java => AbstractHk2InjectionManager.java} | 229 +- .../hk2/DelayedHk2InjectionManager.java | 130 + .../jersey/hk2/Hk2BootstrapBinder.java | 41 +- .../hk2/Hk2InjectionManagerFactory.java | 175 + .../hk2/ImmediateHk2InjectionManager.java | 82 +- .../jersey/hk2/JerseyClassAnalyzer.java | 22 +- .../jersey/internal/BootstrapBag.java | 244 + .../internal/BootstrapConfigurator.java | 77 + .../internal/ContextResolverFactory.java | 74 +- .../internal/ExceptionMapperFactory.java | 101 +- .../jersey/internal/RuntimeDelegateImpl.java | 2 +- .../jersey/internal/guava/AbstractFuture.java | 397 ++ .../guava/AbstractIndexedListIterator.java | 94 + .../internal/guava/AbstractIterator.java | 169 + .../internal/guava/AbstractListMultimap.java | 109 + .../guava/AbstractMapBasedMultimap.java | 1562 +++++++ .../internal/guava/AbstractMapEntry.java | 65 + .../internal/guava/AbstractMultimap.java | 205 + .../guava/AbstractSequentialIterator.java | 75 + .../internal/guava/AbstractSetMultimap.java | 126 + .../AbstractSortedKeySortedSetMultimap.java | 53 + .../guava/AbstractSortedSetMultimap.java | 113 + .../jersey/internal/guava/AbstractTable.java | 165 + .../jersey/internal/guava/AsyncFunction.java | 38 + .../jersey/internal/guava/Cache.java | 58 + .../jersey/internal/guava/CacheBuilder.java | 302 ++ .../jersey/internal/guava/CacheLoader.java | 95 + .../internal/guava/CollectPreconditions.java | 40 + .../jersey/internal/guava/Collections2.java | 144 + .../internal/guava/ComparatorOrdering.java | 61 + .../jersey/internal/guava/Equivalence.java | 166 + .../jersey/internal/guava/ExecutionError.java | 41 + .../jersey/internal/guava/ExecutionList.java | 172 + .../internal/guava/ForwardingCollection.java | 120 + .../internal/guava/ForwardingMapEntry.java | 103 + .../internal/guava/ForwardingObject.java | 77 + .../jersey/internal/guava/ForwardingSet.java | 64 + .../internal/guava/ForwardingSortedSet.java | 91 + .../jersey/internal/guava/Futures.java | 340 ++ .../internal/guava/GenericMapMaker.java | 52 + .../jersey/internal/guava/HashBasedTable.java | 106 + .../jersey/internal/guava/HashMultimap.java | 93 + .../jersey/internal/guava/Hashing.java | 45 + .../jersey/internal/guava/ImmutableEntry.java | 49 + .../jersey/internal/guava/InetAddresses.java | 368 ++ .../glassfish/jersey/internal/guava/Ints.java | 73 + .../jersey/internal/guava/Iterables.java | 63 + .../jersey/internal/guava/Iterators.java | 535 +++ .../jersey/internal/guava/Joiner.java | 208 + .../jersey/internal/guava/ListMultimap.java | 83 + .../internal/guava/ListenableFuture.java | 135 + .../jersey/internal/guava/Lists.java | 307 ++ .../jersey/internal/guava/LoadingCache.java | 80 + .../jersey/internal/guava/LocalCache.java | 4123 +++++++++++++++++ .../jersey/internal/guava/MapMaker.java | 234 + .../internal/guava/MapMakerInternalMap.java | 3329 +++++++++++++ .../glassfish/jersey/internal/guava/Maps.java | 579 +++ .../jersey/internal/guava/MoreExecutors.java | 81 + .../jersey/internal/guava/MoreObjects.java | 201 + .../jersey/internal/guava/Multimap.java | 345 ++ .../jersey/internal/guava/Multimaps.java | 174 + .../internal/guava/NaturalOrdering.java | 56 + .../internal/guava/NullsFirstOrdering.java | 84 + .../internal/guava/NullsLastOrdering.java | 84 + .../jersey/internal/guava/ObjectArrays.java | 50 + .../jersey/internal/guava/Ordering.java | 307 ++ .../internal/guava/PeekingIterator.java | 66 + .../jersey/internal/guava/Platform.java | 57 + .../jersey/internal/guava/Preconditions.java | 436 ++ .../jersey/internal/guava/Predicates.java | 304 ++ .../jersey/internal/guava/Primitives.java | 80 + .../jersey/internal/guava/RemovalCause.java | 69 + .../internal/guava/RemovalNotification.java | 82 + .../guava/ReverseNaturalOrdering.java | 102 + .../internal/guava/ReverseOrdering.java | 109 + .../jersey/internal/guava/Serialization.java | 107 + .../jersey/internal/guava/SetMultimap.java | 103 + .../glassfish/jersey/internal/guava/Sets.java | 272 ++ .../jersey/internal/guava/SettableFuture.java | 69 + .../jersey/internal/guava/SortedIterable.java | 39 + .../internal/guava/SortedIterables.java | 58 + .../jersey/internal/guava/SortedLists.java | 208 + .../internal/guava/SortedSetMultimap.java | 98 + .../jersey/internal/guava/StandardTable.java | 952 ++++ .../jersey/internal/guava/Stopwatch.java | 181 + .../jersey/internal/guava/Table.java | 279 ++ .../jersey/internal/guava/Tables.java | 252 + .../internal/guava/ThreadFactoryBuilder.java | 161 + .../jersey/internal/guava/Ticker.java | 59 + .../internal/guava/TransformedIterator.java | 52 + .../jersey/internal/guava/TreeMultimap.java | 215 + .../guava/UncheckedExecutionException.java | 46 + .../internal/guava/Uninterruptibles.java | 69 + .../internal/guava/UnmodifiableIterator.java | 45 + .../guava/UnmodifiableListIterator.java | 59 + .../jersey/internal/guava/package-info.java | 44 + .../internal/inject/AbstractBinder.java | 51 +- .../internal/inject/InjectionManager.java | 56 +- .../inject/InjectionManagerFactory.java | 73 + .../jersey/internal/inject/Injections.java | 69 +- .../internal/inject/ProviderBinder.java | 13 +- .../jersey/internal/util/JerseyPublisher.java | 324 +- .../internal/util/ReflectionHelper.java | 30 +- .../jersey/internal/util/collection/Refs.java | 16 +- .../internal/util/collection/Views.java | 286 ++ .../internal/CommittingOutputStream.java | 5 +- .../jersey/message/internal/HeaderUtils.java | 36 +- .../internal/InboundMessageContext.java | 5 +- .../jersey/message/internal/MediaTypes.java | 22 +- .../message/internal/MessageBodyFactory.java | 151 +- .../internal/OutboundJaxrsResponse.java | 18 +- .../internal/OutboundMessageContext.java | 53 +- .../message/internal/VariantSelector.java | 15 +- .../internal/WriterInterceptorExecutor.java | 7 +- .../jersey/model/ContractProvider.java | 24 +- .../jersey/model/internal/CommonConfig.java | 15 +- .../jersey/model/internal/ComponentBag.java | 4 +- .../internal/ManagedObjectsFinalizer.java | 13 +- .../jersey/process/internal/RequestScope.java | 38 +- .../jersey/process/internal/Stage.java | 4 +- .../jersey/process/internal/Stages.java | 5 +- .../spi/AbstractThreadPoolProvider.java | 93 +- .../glassfish/jersey/spi/ContentEncoder.java | 7 +- .../org/glassfish/jersey/uri/UriTemplate.java | 5 +- .../jersey/uri/internal/JerseyUriBuilder.java | 9 +- .../jersey/hk2/localization.properties | 3 +- .../jersey/internal/localization.properties | 2 + .../config/ServiceFinderBinderTest.java | 15 +- .../jersey/hk2/BindingTestHelper.java | 5 +- .../jersey/hk2/InjectionManagerTest.java | 25 +- .../internal/ContextResolverFactoryTest.java | 14 +- .../jersey/internal/JaxrsProvidersTest.java | 54 +- .../jersey/internal/ProviderBinderTest.java | 17 +- .../glassfish/jersey/internal/TestBinder.java | 12 - .../internal/TestConfigConfigurator.java | 65 + .../internal/util/JerseyPublisherTest.java | 99 +- .../internal/util/PropertiesHelperTest.java | 25 +- .../jersey/message/DeflateEncodingTest.java | 6 +- .../internal/CommittingOutputStreamTest.java | 17 +- .../message/internal/HeaderUtilsTest.java | 50 +- .../message/internal/MediaTypesTest.java | 20 +- .../model/internal/CommonConfigTest.java | 47 +- .../internal/ExecutorProvidersTest.java | 1 + .../internal/RankedComparatorTest.java | 7 +- .../server/ApplicationConfigurator.java | 150 + .../jersey/server/ApplicationHandler.java | 577 +-- .../server/ComponentProviderConfigurator.java | 90 + ...ontainerMessageBodyWorkersInitializer.java | 6 +- .../jersey/server/ContainerRequest.java | 28 +- .../ExternalRequestScopeConfigurator.java | 122 + .../JerseyResourceContextConfigurator.java | 85 + .../ProcessingProvidersConfigurator.java | 217 + .../glassfish/jersey/server/ResourceBag.java | 10 +- .../server/ResourceBagConfigurator.java | 101 + .../jersey/server/ResourceConfig.java | 94 +- .../glassfish/jersey/server/ServerBinder.java | 41 +- .../jersey/server/ServerBootstrapBag.java | 331 ++ .../jersey/server/ServerRuntime.java | 110 +- .../glassfish/jersey/server/TracingUtils.java | 7 +- .../jersey/server/filter/EncodingFilter.java | 11 +- .../jersey/server/filter/UriConnegFilter.java | 6 +- .../internal/JerseyResourceContext.java | 75 +- .../internal/JsonWithPaddingInterceptor.java | 16 +- .../server/internal/ProcessingProviders.java | 45 +- .../server/internal/RuntimeDelegateImpl.java | 3 +- .../inject/AbstractValueSupplierProvider.java | 6 +- .../BeanParamValueSupplierProvider.java | 2 +- .../CookieParamValueSupplierProvider.java | 2 +- ...legatedInjectionValueSupplierProvider.java | 7 +- .../EntityParamValueSupplierProvider.java | 3 +- .../FormParamValueSupplierProvider.java | 4 +- .../HeaderParamValueSupplierProvider.java | 2 +- .../MatrixParamValueSupplierProvider.java | 2 +- .../inject/ParamConverterConfigurator.java | 65 + .../inject/ParamExtractorConfigurator.java | 79 + .../inject/ParameterInjectionBinder.java | 204 - .../PathParamValueSupplierProvider.java | 2 +- .../QueryParamValueSupplierProvider.java | 2 +- .../ValueSupplierProviderConfigurator.java | 176 + .../WebTargetValueSupplierProvider.java | 36 +- .../monitoring/AggregatingTrimmer.java | 6 +- .../CompositeApplicationEventListener.java | 7 +- .../ExceptionMapperStatisticsImpl.java | 6 +- .../monitoring/ExecutionStatisticsImpl.java | 11 +- .../monitoring/MonitoringEventListener.java | 20 +- .../monitoring/MonitoringStatisticsImpl.java | 35 +- .../monitoring/ResourceStatisticsImpl.java | 7 +- .../monitoring/ResponseStatisticsImpl.java | 6 +- .../monitoring/jmx/ApplicationMXBeanImpl.java | 17 +- .../jmx/ExceptionMapperMXBeanImpl.java | 7 +- .../jmx/ExecutionStatisticsDynamicBean.java | 7 +- .../internal/monitoring/jmx/MBeanExposer.java | 11 +- .../monitoring/jmx/ResourceMxBeanImpl.java | 7 +- .../monitoring/jmx/ResourcesMBeanGroup.java | 7 +- .../process/DefaultCloseableService.java | 8 +- .../process/DefaultRespondingContext.java | 6 +- .../process/ReferencesInitializer.java | 12 +- .../process/RequestProcessingContext.java | 4 +- .../internal/process/RespondingContext.java | 6 +- .../routing/MethodSelectingRouter.java | 21 +- .../server/internal/routing/Routing.java | 88 +- .../routing/RuntimeLocatorModelBuilder.java | 55 +- .../internal/routing/RuntimeModelBuilder.java | 40 +- .../routing/SubResourceLocatorRouter.java | 60 +- .../internal/routing/UriRoutingContext.java | 43 +- .../server/model/ComponentModelValidator.java | 17 +- .../jersey/server/model/Invocable.java | 16 - .../jersey/server/model/Resource.java | 47 +- .../jersey/server/model/ResourceMethod.java | 50 +- .../server/model/ResourceMethodConfig.java | 7 +- .../server/model/ResourceMethodInvoker.java | 212 +- .../server/model/ResourceMethodValidator.java | 18 +- .../jersey/server/model/ResourceModel.java | 24 +- .../jersey/server/model/RuntimeResource.java | 22 +- .../server/model/RuntimeResourceModel.java | 17 +- .../model/RuntimeResourceModelValidator.java | 7 +- .../JavaResourceMethodDispatcherProvider.java | 26 +- .../server/model/internal/ModelErrors.java | 15 +- .../model/internal/ModelProcessorUtil.java | 9 +- .../ResourceMethodDispatcherFactory.java | 12 +- ...esourceMethodInvocationHandlerFactory.java | 12 +- .../ResourceMethodInvokerConfigurator.java | 82 + .../model/internal/ResourceModelBinder.java | 10 - .../internal/VoidVoidDispatcherProvider.java | 11 +- .../monitoring/MonitoringStatistics.java | 3 +- .../spi/internal/ParameterValueHelper.java | 17 +- .../server/wadl/internal/WadlBuilder.java | 7 +- .../processor/OptionsMethodProcessor.java | 7 +- .../wadl/processor/WadlModelProcessor.java | 7 +- .../jersey/server/ApplicationTest.java | 23 +- ...ctory.java => TestConfigConfigurator.java} | 43 +- .../server/TestInjectionManagerFactory.java | 134 + .../server/filter/ApplicationFilterTest.java | 11 +- .../server/filter/UriConnegFilterTest.java | 7 +- .../inject/BeanParamMemoryLeakTest.java | 6 +- .../inject/ParamConverterInternalTest.java | 11 +- .../monitoring/MonitoringStatisticsTest.java | 15 +- .../server/model/ConstrainedToServerTest.java | 6 +- .../server/model/ResourceModelTest.java | 7 +- .../jersey/server/model/ValidatorTest.java | 90 +- .../ResourceMethodDispatcherFactoryTest.java | 16 +- .../annotation/IntrospectionModellerTest.java | 15 +- .../wadl/config/WadlGeneratorConfigTest.java | 21 +- .../WadlGeneratorConfigurationLoaderTest.java | 13 +- .../wadl/config/WadlGeneratorLoaderTest.java | 24 +- .../WadlGeneratorResourceDocSupportTest.java | 9 +- etc/config/copyright-exclude | 3 +- .../bookmark_em/entity/BookmarkEntity.java | 9 +- .../bookmark_em/entity/BookmarkEntityPK.java | 10 +- .../bookmark_em/entity/UserEntity.java | 9 +- .../bookmark/entity/BookmarkEntity.java | 8 +- .../bookmark/entity/BookmarkEntityPK.java | 10 +- .../examples/bookmark/entity/UserEntity.java | 9 +- .../bookstore/webapp/resource/Item.java | 12 +- .../ExtendedWadlWebappOsgiTest.java | 4 +- .../store/ReadWriteLockDataStoreTest.java | 9 +- .../jsonp/service/DocumentStorage.java | 7 +- .../jsonp/JsonProcessingResourceTest.java | 14 +- .../helloworld/test/AbstractWebAppTest.java | 4 +- .../test/AbstractHttpServiceTest.java | 6 +- .../examples/rx/agent/AsyncAgentResource.java | 3 +- .../agent/CompletionStageAgentResource.java | 3 +- .../rx/remote/DestinationResource.java | 7 +- .../managed/ManagedAsyncResourceTest.java | 5 +- .../async/BlockingPostChatResource.java | 5 +- .../async/FireAndForgetChatResource.java | 5 +- .../LongRunningAsyncOperationResource.java | 5 +- .../async/SimpleLongRunningResource.java | 5 +- .../server/async/AsyncResourceTest.java | 5 +- .../internal/ValidateOnExecutionHandler.java | 41 +- ...alidateOnExecutionTraversableResolver.java | 7 +- .../validation/internal/ValidationHelper.java | 28 +- .../internal/InjecteeSkippingAnalyzer.java | 10 +- .../filtering/CommonScopeProvider.java | 16 +- .../filtering/EntityFilteringHelper.java | 11 +- .../filtering/EntityFilteringProcessor.java | 7 +- .../message/filtering/EntityGraphImpl.java | 37 +- .../filtering/EntityInspectorImpl.java | 14 +- .../message/filtering/ObjectGraphImpl.java | 15 +- .../filtering/SecurityAnnotations.java | 5 +- .../message/filtering/SecurityHelper.java | 9 +- .../SecurityServerScopeProvider.java | 5 +- .../filtering/SelectableEntityProcessor.java | 8 +- .../filtering/SelectableScopeResolver.java | 7 +- .../filtering/ServerScopeProvider.java | 5 +- .../filtering/spi/AbstractObjectProvider.java | 9 +- .../filtering/spi/FilteringHelper.java | 7 +- .../SecurityEntityProcessorTest.java | 12 +- .../message/filtering/SecurityHelperTest.java | 16 +- ...FreemarkerDefaultConfigurationFactory.java | 5 +- .../server/mvc/internal/TemplateHelper.java | 20 +- .../mvc/internal/TemplateModelProcessor.java | 9 +- .../internal/ViewableMessageBodyWriter.java | 5 +- .../rx/guava/RxListenableFutureTest.java | 3 +- .../client/rx/rxjava/RxObservableTest.java | 2 +- .../client/rx/rxjava2/RxFlowableTest.java | 3 +- .../spring/SpringComponentProvider.java | 4 +- ...SaxParserFactoryInjectionProviderTest.java | 3 +- .../jersey/moxy/json/MoxyJsonConfig.java | 9 +- .../ConfigurableMoxyJsonProvider.java | 17 +- .../FormDataParamInjectionFeature.java | 5 +- .../FormDataParamValueSupplierProvider.java | 15 +- .../internal/FormDataMultiPartBufferTest.java | 7 +- .../FormDataMultiPartReaderWriterTest.java | 9 +- .../jersey/media/sse/EventSource.java | 5 +- .../jersey/media/sse/SseFeature.java | 11 +- .../jersey/media/sse/internal/JerseySse.java | 26 +- .../sse/internal/JerseySseBroadcaster.java | 17 +- .../sse/internal/JerseySseEventSource.java | 96 +- .../jersey/media/sse/internal/SseBinder.java | 13 +- .../SseEventSinkValueSupplierProvider.java | 4 +- .../jersey/media/sse/localization.properties | 2 + .../media/sse/InboundEventReaderTest.java | 39 +- .../media/sse/SseEventSinkCloseTest.java | 14 +- .../internal/JerseySseBroadcasterTest.java | 2 +- .../jersey/client/oauth1/RequestWrapper.java | 6 +- .../oauth2/OAuth2FlowFacebookBuilder.java | 7 +- .../org/glassfish/jersey/test/JerseyTest.java | 16 +- .../test/inmemory/InMemoryConnector.java | 33 +- .../jersey/test/jetty/JettyContainerTest.java | 4 +- .../test/util/client/LoopBackConnector.java | 25 +- tests/e2e-client/pom.xml | 244 + .../e2e/client/AbortResponseClientTest.java | 2 +- .../tests/e2e/client/BasicClientTest.java | 88 +- .../tests/e2e/client/BufferingTest.java | 2 +- .../e2e/client/CancelFutureClientTest.java | 22 +- ...unkedInputStreamClosedPrematurelyTest.java | 2 +- .../client/ClientBufferingDisabledTest.java | 2 +- .../tests/e2e/client/ClientDestroyTest.java | 2 +- .../client/ClientEntityAnnotationTest.java | 2 +- .../e2e/client/ClientExecutorCloseTest.java | 45 +- .../tests/e2e/client/ClientFilterTest.java | 2 +- .../tests/e2e/client/ClientPathTest.java | 2 +- .../tests/e2e/client/ClientPreInitTest.java | 2 +- .../e2e/client/FollowRedirectHeadTest.java | 2 +- .../tests/e2e/client/FollowRedirectsTest.java | 2 +- .../tests/e2e/client/GenericResponseTest.java | 2 +- .../e2e/client/HttpAuthorizationTest.java | 2 +- .../e2e/client/HttpDigestAuthFilterTest.java | 2 +- .../e2e/client/HttpMethodEntityTest.java | 2 +- .../e2e/client/InjectedClientBodyWorker.java | 2 +- .../client/InjectionManagerProviderTest.java | 0 .../e2e/client/NonSuccessfulResponseTest.java | 2 +- .../client/RequestScopedReadEntityTest.java | 2 +- .../tests/e2e/client/ResponseCloseTest.java | 2 +- .../ResponseReadAndBufferEntityTest.java | 2 +- .../client/ShutdownHookMemoryLeakTest.java | 2 +- .../jersey/tests/e2e/client/TimeoutTest.java | 2 +- .../RequestHeaderModificationsTest.java | 2 +- .../ssl/AbstractConnectorServerTest.java | 2 +- .../ssl/AuthenticationException.java | 2 +- .../ssl/AuthenticationExceptionMapper.java | 2 +- .../client/connector/ssl/RootResource.java | 2 +- .../client/connector/ssl/SecurityFilter.java | 2 +- .../e2e/client/connector/ssl/Server.java | 2 +- .../ssl/SslConnectorConfigurationTest.java | 2 +- .../ssl/SslConnectorHostnameVerifierTest.java | 2 +- .../ssl/SslHttpUrlConnectorTest.java | 2 +- .../grizzlyconnector/NonBlockingTest.java | 2 +- .../client/httpurlconnector/AsyncTest.java | 2 +- .../e2e/client/connector/ssl/keystore-client | Bin .../connector/ssl/keystore-example_com-server | Bin .../connector/ssl/keystore-localhost-server | Bin .../ssl/truststore-example_com-client | Bin .../connector/ssl/truststore-localhost-client | Bin .../client/connector/ssl/truststore-server | Bin tests/e2e-entity/pom.xml | 244 + ...bstractParameterTypeArgumentOrderTest.java | 2 +- .../tests/e2e/entity/AbstractTypeTester.java | 2 +- .../tests/e2e/entity/BeanStreamingTest.java | 2 +- .../jersey/tests/e2e/entity/CharsetTest.java | 2 +- .../entity/ContextResolverMediaTypeTest.java | 2 +- .../tests/e2e/entity/EmptyEntityTest.java | 2 +- .../entity/EmptyRequestToEntityParamTest.java | 2 +- .../e2e/entity/EmptyRequestWithJaxbTest.java | 2 +- .../tests/e2e/entity/EntityTypesTest.java | 2 +- .../e2e/entity/GenericTypeAndEntityTest.java | 21 +- .../e2e/entity/InjectedProviderTest.java | 2 +- .../entity/InterceptedStreamCloseTest.java | 2 +- .../tests/e2e/entity/InvalidEntityTest.java | 2 +- .../tests/e2e/entity/InvalidFormTest.java | 2 +- .../e2e/entity/JAXBContextResolverTest.java | 2 +- .../jersey/tests/e2e/entity/JaxbBean.java | 2 +- .../jersey/tests/e2e/entity/JaxbBeanType.java | 2 +- .../jersey/tests/e2e/entity/JsonMoxyTest.java | 2 +- .../e2e/entity/MediaTypeSelectionTest.java | 2 +- .../MessageBodyProviderAnnotationsTest.java | 2 +- .../tests/e2e/entity/MultipartTest.java | 2 +- .../jersey/tests/e2e/entity/MyArrayList.java | 2 +- .../e2e/entity/NoMessageBodyWorkerTest.java | 2 +- .../ParameterTypeArgumentOrderTest.java | 2 +- ...ArgumentResourceReaderWriterOrderTest.java | 2 +- ...arameterTypeArgumentReversedOrderTest.java | 2 +- .../e2e/entity/RenderedImageTypeTest.java | 2 +- .../tests/e2e/entity/StreamingOutputTest.java | 2 +- .../entity/SubResourceDynamicProxyTest.java | 2 +- .../jersey/tests/e2e/entity/XXETest.java | 2 +- .../entity/XmlJaxBElementProviderTest.java | 2 +- .../jersey/tests/e2e/entity/XmlMoxyTest.java | 2 +- .../filtering/DefaultFilteringScope.java | 0 .../e2e/entity/filtering/EmptyEntityTest.java | 2 +- .../filtering/EntityFilteringClientTest.java | 2 +- .../filtering/EntityFilteringOnClassTest.java | 2 +- .../EntityFilteringOnPropertiesTest.java | 2 +- .../filtering/EntityFilteringScopesTest.java | 2 +- .../filtering/EntityFilteringServerTest.java | 2 +- .../entity/filtering/EntityFilteringTest.java | 13 +- .../FilteringMessageBodyProvider.java | 7 +- .../entity/filtering/PrimaryDetailedView.java | 0 .../filtering/SecondaryDetailedView.java | 0 .../filtering/TertiaryDetailedView.java | 0 .../filtering/domain/ComplexEntity.java | 2 +- .../filtering/domain/ComplexSubEntity.java | 2 +- .../filtering/domain/ComplexSubSubEntity.java | 2 +- .../domain/DefaultFilteringSubEntity.java | 2 +- .../entity/filtering/domain/EmptyEntity.java | 2 +- .../filtering/domain/FilteredClassEntity.java | 2 +- .../domain/ManyFilteringsOnClassEntity.java | 2 +- .../ManyFilteringsOnPropertiesEntity.java | 2 +- .../domain/ManyFilteringsSubEntity.java | 2 +- .../filtering/domain/NonEmptyEntity.java | 2 +- .../domain/OneFilteringOnClassEntity.java | 2 +- .../OneFilteringOnPropertiesEntity.java | 2 +- .../domain/OneFilteringSubEntity.java | 2 +- .../filtering/json/JsonEmptyEntityTest.java | 2 +- .../json/JsonEntityFilteringClientTest.java | 2 +- .../json/JsonEntityFilteringOnClassTest.java | 2 +- .../JsonEntityFilteringOnPropertiesTest.java | 2 +- .../json/JsonEntityFilteringScopesTest.java | 2 +- .../json/JsonEntityFilteringServerTest.java | 2 +- .../json/MoxyEntityFilteringTest.java | 2 +- .../tests/e2e/json/AbstractJsonTest.java | 2 +- .../jersey/tests/e2e/json/GenericTest.java | 2 +- .../tests/e2e/json/InheritanceTest.java | 2 +- .../tests/e2e/json/Jackson1JsonViewTest.java | 2 +- .../tests/e2e/json/JacksonJsonViewTest.java | 2 +- .../jersey/tests/e2e/json/JaxbTest.java | 2 +- .../jersey/tests/e2e/json/Jersey1199Test.java | 2 +- .../jersey/tests/e2e/json/Jersey1835Test.java | 2 +- .../e2e/json/JsonProcessingDisabledTest.java | 2 +- .../tests/e2e/json/JsonProcessingTest.java | 2 +- .../jersey/tests/e2e/json/JsonTestHelper.java | 2 +- .../tests/e2e/json/JsonTestProvider.java | 2 +- .../JsonWithPaddingEncodingFilterTest.java | 4 +- .../tests/e2e/json/JsonWithPaddingTest.java | 2 +- .../jersey/tests/e2e/json/PojoTest.java | 2 +- .../jersey/tests/e2e/json/entity/Animal.java | 2 +- .../tests/e2e/json/entity/AnimalList.java | 2 +- .../tests/e2e/json/entity/AnotherAnimal.java | 2 +- .../e2e/json/entity/AnotherArrayTestBean.java | 2 +- .../tests/e2e/json/entity/AnotherCat.java | 2 +- .../e2e/json/entity/AttrAndCharDataBean.java | 2 +- .../jersey/tests/e2e/json/entity/Cat.java | 2 +- .../jersey/tests/e2e/json/entity/Color.java | 2 +- .../tests/e2e/json/entity/ColorHolder.java | 2 +- .../entity/ComplexBeanWithAttributes.java | 2 +- .../entity/ComplexBeanWithAttributes2.java | 2 +- .../entity/ComplexBeanWithAttributes3.java | 2 +- .../entity/ComplexBeanWithAttributes4.java | 2 +- .../jersey/tests/e2e/json/entity/Dog.java | 2 +- .../e2e/json/entity/EmptyElementBean.java | 2 +- .../entity/EmptyElementContainingBean.java | 2 +- .../e2e/json/entity/EncodedContentBean.java | 2 +- .../tests/e2e/json/entity/FakeArrayBean.java | 2 +- .../tests/e2e/json/entity/IntArray.java | 2 +- .../tests/e2e/json/entity/Jersey1199List.java | 2 +- .../e2e/json/entity/ListAndNonListBean.java | 2 +- .../tests/e2e/json/entity/ListEmptyBean.java | 2 +- .../e2e/json/entity/ListWrapperBean.java | 2 +- .../jersey/tests/e2e/json/entity/MyError.java | 2 +- .../tests/e2e/json/entity/MyMessage.java | 2 +- .../tests/e2e/json/entity/MyResponse.java | 2 +- .../tests/e2e/json/entity/NamespaceBean.java | 2 +- .../entity/NamespaceBeanWithAttribute.java | 2 +- .../tests/e2e/json/entity/NullStringBean.java | 2 +- .../jersey/tests/e2e/json/entity/Person.java | 2 +- .../e2e/json/entity/PureCharDataBean.java | 2 +- .../e2e/json/entity/RegisterMessage.java | 2 +- .../tests/e2e/json/entity/SimpleBean.java | 2 +- .../json/entity/SimpleBeanWithAttributes.java | 2 +- .../SimpleBeanWithJustOneAttribute.java | 2 +- ...impleBeanWithJustOneAttributeAndValue.java | 2 +- .../SimpleBeanWithObjectAttributes.java | 2 +- .../e2e/json/entity/SimpleXmlTypeBean.java | 2 +- .../entity/SingleItemListWrapperBean.java | 2 +- .../tests/e2e/json/entity/TreeModel.java | 2 +- .../e2e/json/entity/TwoListsWrapperBean.java | 2 +- .../jersey/tests/e2e/json/entity/User.java | 2 +- .../tests/e2e/json/entity/UserTable.java | 2 +- .../e2e/json/entity/pojo/PojoAnimal.java | 2 +- .../e2e/json/entity/pojo/PojoAnimalList.java | 2 +- .../tests/e2e/json/entity/pojo/PojoCat.java | 2 +- .../tests/e2e/json/entity/pojo/PojoDog.java | 2 +- .../e2e/sse/BroadcasterExecutorTest.java | 220 + .../jersey/tests/e2e/sse/BroadcasterTest.java | 4 + .../jersey/tests/e2e/sse/EventOutputTest.java | 2 +- .../sse/EventSourceWithNamedEventsTest.java | 2 +- .../sse/SseEventSinkToEventSourceTest.java | 0 .../jersey/tests/e2e/entity/Jersey_yellow.png | Bin .../jersey/tests/e2e/entity/duke_rocket.gif | Bin .../tests/e2e/entity/multipart-testcase.txt | 0 .../glassfish/jersey/tests/e2e/entity/xxe.txt | 0 .../Jackson1JsonTestProvider_AnimalList.json | 0 ...kson1JsonTestProvider_AnimalList_MOXy.json | 0 ...JsonTestProvider_AnotherArrayTestBean.json | 0 ...estProvider_AnotherArrayTestBean_MOXy.json | 0 ...1JsonTestProvider_AttrAndCharDataBean.json | 0 ...TestProvider_AttrAndCharDataBean_MOXy.json | 0 ...estProvider_ComplexBeanWithAttributes.json | 0 ...stProvider_ComplexBeanWithAttributes2.json | 0 ...vider_ComplexBeanWithAttributes2_MOXy.json | 0 ...stProvider_ComplexBeanWithAttributes3.json | 0 ...vider_ComplexBeanWithAttributes3_MOXy.json | 0 ...stProvider_ComplexBeanWithAttributes4.json | 0 ...vider_ComplexBeanWithAttributes4_MOXy.json | 0 ...ovider_ComplexBeanWithAttributes_MOXy.json | 0 ...son1JsonTestProvider_EmptyElementBean.json | 0 ...sonTestProvider_EmptyElementBean_MOXy.json | 0 ...stProvider_EmptyElementContainingBean.json | 0 ...vider_EmptyElementContainingBean_MOXy.json | 0 ...n1JsonTestProvider_EncodedContentBean.json | 0 ...nTestProvider_EncodedContentBean_MOXy.json | 0 ...ackson1JsonTestProvider_FakeArrayBean.json | 0 ...n1JsonTestProvider_FakeArrayBean_MOXy.json | 0 .../Jackson1JsonTestProvider_IntArray.json | 0 ...ackson1JsonTestProvider_IntArray_MOXy.json | 0 ...ckson1JsonTestProvider_Jersey1199List.json | 0 ...1JsonTestProvider_Jersey1199List_MOXy.json | 0 ...n1JsonTestProvider_ListAndNonListBean.json | 0 ...nTestProvider_ListAndNonListBean_MOXy.json | 0 ...ackson1JsonTestProvider_ListEmptyBean.json | 0 ...n1JsonTestProvider_ListEmptyBean_MOXy.json | 0 ...kson1JsonTestProvider_ListWrapperBean.json | 0 ...JsonTestProvider_ListWrapperBean_MOXy.json | 0 .../Jackson1JsonTestProvider_MyResponse.json | 0 ...kson1JsonTestProvider_MyResponse_MOXy.json | 0 ...ackson1JsonTestProvider_NamespaceBean.json | 0 ...stProvider_NamespaceBeanWithAttribute.json | 0 ...vider_NamespaceBeanWithAttribute_MOXy.json | 0 ...n1JsonTestProvider_NamespaceBean_MOXy.json | 0 ...ckson1JsonTestProvider_NullStringBean.json | 0 ...1JsonTestProvider_NullStringBean_MOXy.json | 0 .../Jackson1JsonTestProvider_Person.json | 0 .../Jackson1JsonTestProvider_Person_MOXy.json | 0 ...ckson1JsonTestProvider_PojoAnimalList.json | 0 ...1JsonTestProvider_PojoAnimalList_MOXy.json | 0 ...son1JsonTestProvider_PureCharDataBean.json | 0 ...sonTestProvider_PureCharDataBean_MOXy.json | 0 ...kson1JsonTestProvider_RegisterMessage.json | 0 ...JsonTestProvider_RegisterMessage_MOXy.json | 0 .../Jackson1JsonTestProvider_SimpleBean.json | 0 ...TestProvider_SimpleBeanWithAttributes.json | 0 ...rovider_SimpleBeanWithAttributes_MOXy.json | 0 ...ovider_SimpleBeanWithJustOneAttribute.json | 0 ...impleBeanWithJustOneAttributeAndValue.json | 0 ...BeanWithJustOneAttributeAndValue_MOXy.json | 0 ...r_SimpleBeanWithJustOneAttribute_MOXy.json | 0 ...kson1JsonTestProvider_SimpleBean_MOXy.json | 0 .../Jackson1JsonTestProvider_TreeModel.json | 0 ...ckson1JsonTestProvider_TreeModel_MOXy.json | 0 ...1JsonTestProvider_TwoListsWrapperBean.json | 0 ...TestProvider_TwoListsWrapperBean_MOXy.json | 0 .../entity/Jackson1JsonTestProvider_User.json | 0 .../Jackson1JsonTestProvider_UserTable.json | 0 ...ckson1JsonTestProvider_UserTable_MOXy.json | 0 .../Jackson1JsonTestProvider_User_MOXy.json | 0 .../JacksonJsonTestProvider_AnimalList.json | 0 ...cksonJsonTestProvider_AnimalList_MOXy.json | 0 ...JsonTestProvider_AnotherArrayTestBean.json | 0 ...estProvider_AnotherArrayTestBean_MOXy.json | 0 ...nJsonTestProvider_AttrAndCharDataBean.json | 0 ...TestProvider_AttrAndCharDataBean_MOXy.json | 0 ...estProvider_ComplexBeanWithAttributes.json | 0 ...stProvider_ComplexBeanWithAttributes2.json | 0 ...vider_ComplexBeanWithAttributes2_MOXy.json | 0 ...stProvider_ComplexBeanWithAttributes3.json | 0 ...vider_ComplexBeanWithAttributes3_MOXy.json | 0 ...stProvider_ComplexBeanWithAttributes4.json | 0 ...vider_ComplexBeanWithAttributes4_MOXy.json | 0 ...ovider_ComplexBeanWithAttributes_MOXy.json | 0 ...ksonJsonTestProvider_EmptyElementBean.json | 0 ...sonTestProvider_EmptyElementBean_MOXy.json | 0 ...stProvider_EmptyElementContainingBean.json | 0 ...vider_EmptyElementContainingBean_MOXy.json | 0 ...onJsonTestProvider_EncodedContentBean.json | 0 ...nTestProvider_EncodedContentBean_MOXy.json | 0 ...JacksonJsonTestProvider_FakeArrayBean.json | 0 ...onJsonTestProvider_FakeArrayBean_MOXy.json | 0 .../JacksonJsonTestProvider_IntArray.json | 0 ...JacksonJsonTestProvider_IntArray_MOXy.json | 0 ...acksonJsonTestProvider_Jersey1199List.json | 0 ...nJsonTestProvider_Jersey1199List_MOXy.json | 0 ...onJsonTestProvider_ListAndNonListBean.json | 0 ...nTestProvider_ListAndNonListBean_MOXy.json | 0 ...JacksonJsonTestProvider_ListEmptyBean.json | 0 ...onJsonTestProvider_ListEmptyBean_MOXy.json | 0 ...cksonJsonTestProvider_ListWrapperBean.json | 0 ...JsonTestProvider_ListWrapperBean_MOXy.json | 0 .../JacksonJsonTestProvider_MyResponse.json | 0 ...cksonJsonTestProvider_MyResponse_MOXy.json | 0 ...JacksonJsonTestProvider_NamespaceBean.json | 0 ...stProvider_NamespaceBeanWithAttribute.json | 0 ...vider_NamespaceBeanWithAttribute_MOXy.json | 0 ...onJsonTestProvider_NamespaceBean_MOXy.json | 0 ...acksonJsonTestProvider_NullStringBean.json | 0 ...nJsonTestProvider_NullStringBean_MOXy.json | 0 .../JacksonJsonTestProvider_Person.json | 0 .../JacksonJsonTestProvider_Person_MOXy.json | 0 ...acksonJsonTestProvider_PojoAnimalList.json | 0 ...nJsonTestProvider_PojoAnimalList_MOXy.json | 0 ...ksonJsonTestProvider_PureCharDataBean.json | 0 ...sonTestProvider_PureCharDataBean_MOXy.json | 0 ...cksonJsonTestProvider_RegisterMessage.json | 0 ...JsonTestProvider_RegisterMessage_MOXy.json | 0 .../JacksonJsonTestProvider_SimpleBean.json | 0 ...TestProvider_SimpleBeanWithAttributes.json | 0 ...rovider_SimpleBeanWithAttributes_MOXy.json | 0 ...ovider_SimpleBeanWithJustOneAttribute.json | 0 ...impleBeanWithJustOneAttributeAndValue.json | 0 ...BeanWithJustOneAttributeAndValue_MOXy.json | 0 ...r_SimpleBeanWithJustOneAttribute_MOXy.json | 0 ...cksonJsonTestProvider_SimpleBean_MOXy.json | 0 .../JacksonJsonTestProvider_TreeModel.json | 0 ...acksonJsonTestProvider_TreeModel_MOXy.json | 0 ...nJsonTestProvider_TwoListsWrapperBean.json | 0 ...TestProvider_TwoListsWrapperBean_MOXy.json | 0 .../entity/JacksonJsonTestProvider_User.json | 0 .../JacksonJsonTestProvider_UserTable.json | 0 ...acksonJsonTestProvider_UserTable_MOXy.json | 0 .../JacksonJsonTestProvider_User_MOXy.json | 0 ...BadgerfishJsonTestProvider_AnimalList.json | 0 ...rfishJsonTestProvider_AnimalList_MOXy.json | 0 ...JsonTestProvider_AnotherArrayTestBean.json | 0 ...estProvider_AnotherArrayTestBean_MOXy.json | 0 ...hJsonTestProvider_AttrAndCharDataBean.json | 0 ...TestProvider_AttrAndCharDataBean_MOXy.json | 0 ...estProvider_ComplexBeanWithAttributes.json | 0 ...stProvider_ComplexBeanWithAttributes2.json | 0 ...vider_ComplexBeanWithAttributes2_MOXy.json | 0 ...stProvider_ComplexBeanWithAttributes3.json | 0 ...vider_ComplexBeanWithAttributes3_MOXy.json | 0 ...stProvider_ComplexBeanWithAttributes4.json | 0 ...vider_ComplexBeanWithAttributes4_MOXy.json | 0 ...ovider_ComplexBeanWithAttributes_MOXy.json | 0 ...fishJsonTestProvider_EmptyElementBean.json | 0 ...sonTestProvider_EmptyElementBean_MOXy.json | 0 ...stProvider_EmptyElementContainingBean.json | 0 ...vider_EmptyElementContainingBean_MOXy.json | 0 ...shJsonTestProvider_EncodedContentBean.json | 0 ...nTestProvider_EncodedContentBean_MOXy.json | 0 ...gerfishJsonTestProvider_FakeArrayBean.json | 0 ...shJsonTestProvider_FakeArrayBean_MOXy.json | 0 ...onBadgerfishJsonTestProvider_IntArray.json | 0 ...gerfishJsonTestProvider_IntArray_MOXy.json | 0 ...erfishJsonTestProvider_Jersey1199List.json | 0 ...hJsonTestProvider_Jersey1199List_MOXy.json | 0 ...shJsonTestProvider_ListAndNonListBean.json | 0 ...nTestProvider_ListAndNonListBean_MOXy.json | 0 ...gerfishJsonTestProvider_ListEmptyBean.json | 0 ...shJsonTestProvider_ListEmptyBean_MOXy.json | 0 ...rfishJsonTestProvider_ListWrapperBean.json | 0 ...JsonTestProvider_ListWrapperBean_MOXy.json | 0 ...BadgerfishJsonTestProvider_MyResponse.json | 0 ...rfishJsonTestProvider_MyResponse_MOXy.json | 0 ...gerfishJsonTestProvider_NamespaceBean.json | 0 ...stProvider_NamespaceBeanWithAttribute.json | 0 ...vider_NamespaceBeanWithAttribute_MOXy.json | 0 ...shJsonTestProvider_NamespaceBean_MOXy.json | 0 ...erfishJsonTestProvider_NullStringBean.json | 0 ...hJsonTestProvider_NullStringBean_MOXy.json | 0 ...isonBadgerfishJsonTestProvider_Person.json | 0 ...adgerfishJsonTestProvider_Person_MOXy.json | 0 ...fishJsonTestProvider_PureCharDataBean.json | 0 ...sonTestProvider_PureCharDataBean_MOXy.json | 0 ...rfishJsonTestProvider_RegisterMessage.json | 0 ...JsonTestProvider_RegisterMessage_MOXy.json | 0 ...BadgerfishJsonTestProvider_SimpleBean.json | 0 ...TestProvider_SimpleBeanWithAttributes.json | 0 ...rovider_SimpleBeanWithAttributes_MOXy.json | 0 ...ovider_SimpleBeanWithJustOneAttribute.json | 0 ...impleBeanWithJustOneAttributeAndValue.json | 0 ...BeanWithJustOneAttributeAndValue_MOXy.json | 0 ...r_SimpleBeanWithJustOneAttribute_MOXy.json | 0 ...rfishJsonTestProvider_SimpleBean_MOXy.json | 0 ...nBadgerfishJsonTestProvider_TreeModel.json | 0 ...erfishJsonTestProvider_TreeModel_MOXy.json | 0 ...hJsonTestProvider_TwoListsWrapperBean.json | 0 ...TestProvider_TwoListsWrapperBean_MOXy.json | 0 ...ttisonBadgerfishJsonTestProvider_User.json | 0 ...nBadgerfishJsonTestProvider_UserTable.json | 0 ...erfishJsonTestProvider_UserTable_MOXy.json | 0 ...nBadgerfishJsonTestProvider_User_MOXy.json | 0 ...isonMappedJsonTestProvider_AnimalList.json | 0 ...appedJsonTestProvider_AnimalList_MOXy.json | 0 ...JsonTestProvider_AnotherArrayTestBean.json | 0 ...estProvider_AnotherArrayTestBean_MOXy.json | 0 ...dJsonTestProvider_AttrAndCharDataBean.json | 0 ...TestProvider_AttrAndCharDataBean_MOXy.json | 0 ...estProvider_ComplexBeanWithAttributes.json | 0 ...stProvider_ComplexBeanWithAttributes2.json | 0 ...vider_ComplexBeanWithAttributes2_MOXy.json | 0 ...stProvider_ComplexBeanWithAttributes3.json | 0 ...vider_ComplexBeanWithAttributes3_MOXy.json | 0 ...stProvider_ComplexBeanWithAttributes4.json | 0 ...vider_ComplexBeanWithAttributes4_MOXy.json | 0 ...ovider_ComplexBeanWithAttributes_MOXy.json | 0 ...ppedJsonTestProvider_EmptyElementBean.json | 0 ...sonTestProvider_EmptyElementBean_MOXy.json | 0 ...stProvider_EmptyElementContainingBean.json | 0 ...vider_EmptyElementContainingBean_MOXy.json | 0 ...edJsonTestProvider_EncodedContentBean.json | 0 ...nTestProvider_EncodedContentBean_MOXy.json | 0 ...nMappedJsonTestProvider_FakeArrayBean.json | 0 ...edJsonTestProvider_FakeArrayBean_MOXy.json | 0 ...ttisonMappedJsonTestProvider_IntArray.json | 0 ...nMappedJsonTestProvider_IntArray_MOXy.json | 0 ...MappedJsonTestProvider_Jersey1199List.json | 0 ...dJsonTestProvider_Jersey1199List_MOXy.json | 0 ...edJsonTestProvider_ListAndNonListBean.json | 0 ...nTestProvider_ListAndNonListBean_MOXy.json | 0 ...nMappedJsonTestProvider_ListEmptyBean.json | 0 ...edJsonTestProvider_ListEmptyBean_MOXy.json | 0 ...appedJsonTestProvider_ListWrapperBean.json | 0 ...JsonTestProvider_ListWrapperBean_MOXy.json | 0 ...isonMappedJsonTestProvider_MyResponse.json | 0 ...appedJsonTestProvider_MyResponse_MOXy.json | 0 ...nMappedJsonTestProvider_NamespaceBean.json | 0 ...stProvider_NamespaceBeanWithAttribute.json | 0 ...vider_NamespaceBeanWithAttribute_MOXy.json | 0 ...edJsonTestProvider_NamespaceBean_MOXy.json | 0 ...MappedJsonTestProvider_NullStringBean.json | 0 ...dJsonTestProvider_NullStringBean_MOXy.json | 0 ...JettisonMappedJsonTestProvider_Person.json | 0 ...sonMappedJsonTestProvider_Person_MOXy.json | 0 ...ppedJsonTestProvider_PureCharDataBean.json | 0 ...sonTestProvider_PureCharDataBean_MOXy.json | 0 ...appedJsonTestProvider_RegisterMessage.json | 0 ...JsonTestProvider_RegisterMessage_MOXy.json | 0 ...isonMappedJsonTestProvider_SimpleBean.json | 0 ...TestProvider_SimpleBeanWithAttributes.json | 0 ...rovider_SimpleBeanWithAttributes_MOXy.json | 0 ...ovider_SimpleBeanWithJustOneAttribute.json | 0 ...impleBeanWithJustOneAttributeAndValue.json | 0 ...BeanWithJustOneAttributeAndValue_MOXy.json | 0 ...r_SimpleBeanWithJustOneAttribute_MOXy.json | 0 ...appedJsonTestProvider_SimpleBean_MOXy.json | 0 ...estProvider_SingleItemListWrapperBean.json | 0 ...ovider_SingleItemListWrapperBean_MOXy.json | 0 ...tisonMappedJsonTestProvider_TreeModel.json | 0 ...MappedJsonTestProvider_TreeModel_MOXy.json | 0 ...dJsonTestProvider_TwoListsWrapperBean.json | 0 ...TestProvider_TwoListsWrapperBean_MOXy.json | 0 .../JettisonMappedJsonTestProvider_User.json | 0 ...tisonMappedJsonTestProvider_UserTable.json | 0 ...MappedJsonTestProvider_UserTable_MOXy.json | 0 ...tisonMappedJsonTestProvider_User_MOXy.json | 0 .../MoxyJsonTestProvider_AnimalList.json | 0 .../MoxyJsonTestProvider_AnimalList_MOXy.json | 0 ...JsonTestProvider_AnotherArrayTestBean.json | 0 ...estProvider_AnotherArrayTestBean_MOXy.json | 0 ...yJsonTestProvider_AttrAndCharDataBean.json | 0 ...TestProvider_AttrAndCharDataBean_MOXy.json | 0 ...estProvider_ComplexBeanWithAttributes.json | 0 ...stProvider_ComplexBeanWithAttributes2.json | 0 ...vider_ComplexBeanWithAttributes2_MOXy.json | 0 ...stProvider_ComplexBeanWithAttributes3.json | 0 ...vider_ComplexBeanWithAttributes3_MOXy.json | 0 ...stProvider_ComplexBeanWithAttributes4.json | 0 ...vider_ComplexBeanWithAttributes4_MOXy.json | 0 ...ovider_ComplexBeanWithAttributes_MOXy.json | 0 ...MoxyJsonTestProvider_EmptyElementBean.json | 0 ...sonTestProvider_EmptyElementBean_MOXy.json | 0 ...stProvider_EmptyElementContainingBean.json | 0 ...vider_EmptyElementContainingBean_MOXy.json | 0 ...xyJsonTestProvider_EncodedContentBean.json | 0 ...nTestProvider_EncodedContentBean_MOXy.json | 0 .../MoxyJsonTestProvider_FakeArrayBean.json | 0 ...xyJsonTestProvider_FakeArrayBean_MOXy.json | 0 .../entity/MoxyJsonTestProvider_IntArray.json | 0 .../MoxyJsonTestProvider_IntArray_MOXy.json | 0 .../MoxyJsonTestProvider_Jersey1199List.json | 0 ...yJsonTestProvider_Jersey1199List_MOXy.json | 0 ...xyJsonTestProvider_ListAndNonListBean.json | 0 ...nTestProvider_ListAndNonListBean_MOXy.json | 0 .../MoxyJsonTestProvider_ListEmptyBean.json | 0 ...xyJsonTestProvider_ListEmptyBean_MOXy.json | 0 .../MoxyJsonTestProvider_ListWrapperBean.json | 0 ...JsonTestProvider_ListWrapperBean_MOXy.json | 0 .../MoxyJsonTestProvider_MyResponse.json | 0 .../MoxyJsonTestProvider_MyResponse_MOXy.json | 0 .../MoxyJsonTestProvider_NamespaceBean.json | 0 ...stProvider_NamespaceBeanWithAttribute.json | 0 ...vider_NamespaceBeanWithAttribute_MOXy.json | 0 ...xyJsonTestProvider_NamespaceBean_MOXy.json | 0 .../MoxyJsonTestProvider_NullStringBean.json | 0 ...yJsonTestProvider_NullStringBean_MOXy.json | 0 .../entity/MoxyJsonTestProvider_Person.json | 0 .../MoxyJsonTestProvider_Person_MOXy.json | 0 ...MoxyJsonTestProvider_PureCharDataBean.json | 0 ...sonTestProvider_PureCharDataBean_MOXy.json | 0 .../MoxyJsonTestProvider_RegisterMessage.json | 0 ...JsonTestProvider_RegisterMessage_MOXy.json | 0 .../MoxyJsonTestProvider_SimpleBean.json | 0 ...TestProvider_SimpleBeanWithAttributes.json | 0 ...rovider_SimpleBeanWithAttributes_MOXy.json | 0 ...ovider_SimpleBeanWithJustOneAttribute.json | 0 ...impleBeanWithJustOneAttributeAndValue.json | 0 ...BeanWithJustOneAttributeAndValue_MOXy.json | 0 ...r_SimpleBeanWithJustOneAttribute_MOXy.json | 0 .../MoxyJsonTestProvider_SimpleBean_MOXy.json | 0 .../MoxyJsonTestProvider_TreeModel.json | 0 .../MoxyJsonTestProvider_TreeModel_MOXy.json | 0 ...yJsonTestProvider_TwoListsWrapperBean.json | 0 ...TestProvider_TwoListsWrapperBean_MOXy.json | 0 .../entity/MoxyJsonTestProvider_User.json | 0 .../MoxyJsonTestProvider_UserTable.json | 0 .../MoxyJsonTestProvider_UserTable_MOXy.json | 0 .../MoxyJsonTestProvider_User_MOXy.json | 0 tests/e2e-server/pom.xml | 244 + ...tractDisableMetainfServicesLookupTest.java | 0 .../tests/e2e/server/AllInjectablesTest.java | 2 +- .../server/AmbigousResourceMethodTest.java | 2 +- .../tests/e2e/server/AppNameBindingTest.java | 2 +- .../tests/e2e/server/AppNameBindingTest2.java | 2 +- .../tests/e2e/server/AsyncCallbackTest.java | 2 +- .../tests/e2e/server/AsyncResponseTest.java | 2 +- .../e2e/server/BeanParamExceptionTest.java | 2 +- .../tests/e2e/server/BeanParamTest.java | 2 +- .../tests/e2e/server/BroadcasterTest.java | 9 +- .../e2e/server/ChunkedInputOutputTest.java | 2 +- .../server/ClientResponseOnServerTest.java | 2 +- .../tests/e2e/server/CloseableTest.java | 4 +- ...erListenerRegistrationAsProvidersTest.java | 2 +- .../e2e/server/ContentNegotiationTest.java | 2 +- .../CustomInjectablesApplicationTest.java | 6 +- .../CustomInjectablesResourceConfigTest.java | 0 .../CustomMultivaluedMapProviderTest.java | 2 +- .../e2e/server/EncodedFormParamTest.java | 2 +- .../server/EncodedSlashInPathSegmentTest.java | 2 +- .../tests/e2e/server/EntityExpansionTest.java | 2 +- .../e2e/server/ExceptionLoggingTest.java | 18 +- .../ExceptionMapperPropagationTest.java | 2 +- .../tests/e2e/server/ExceptionMapperTest.java | 2 +- .../server/ExtendedExceptionMapperTest.java | 2 +- .../tests/e2e/server/ExtendedUriInfoTest.java | 2 +- .../FormParamMultivaluedInjectionTest.java | 2 +- .../GlobalNameBoundInterceptorTest.java | 2 +- .../GloballyNameBoundResourceFilterTest.java | 2 +- .../e2e/server/GrizzlyInjectionTest.java | 2 +- .../e2e/server/InitializationLoggingTest.java | 2 +- .../InjectionManagerServerProviderTest.java | 0 .../tests/e2e/server/InjectionTest.java | 2 +- .../e2e/server/InputStreamResponseTest.java | 2 +- .../InterceptorHttpHeadersInjectionTest.java | 2 +- .../InterceptorNameAndDynamicBindingTest.java | 2 +- .../MessageBodyProvidersExceptionsTest.java | 2 +- .../MetainfServicesLookupDisabledTest.java | 2 +- .../MetainfServicesLookupEnabledTest.java | 2 +- .../tests/e2e/server/ModelProcessorTest.java | 14 +- .../tests/e2e/server/NameBindingTest.java | 11 +- .../tests/e2e/server/ParamConverterTest.java | 2 +- .../tests/e2e/server/PathEncodingTest.java | 2 +- .../e2e/server/PerRequestLifecycleTest.java | 2 +- .../tests/e2e/server/PostConstructTest.java | 11 +- .../tests/e2e/server/PrimitiveTypesTest.java | 2 +- .../jersey/tests/e2e/server/ReloadTest.java | 2 +- .../e2e/server/RequestScopedAndAsyncTest.java | 0 .../tests/e2e/server/ResourceConfigTest.java | 2 +- .../tests/e2e/server/ResourceFilterTest.java | 2 +- .../tests/e2e/server/ResourceRoutingTest.java | 2 +- .../e2e/server/ResponseStatusTypeTest.java | 2 +- .../tests/e2e/server/RuntimeConfigTest.java | 2 +- .../e2e/server/SecurityContextFilterTest.java | 2 +- .../tests/e2e/server/ServerDestroyTest.java | 11 +- .../e2e/server/SingletonProviderTest.java | 2 +- .../SingletonProvidersResourcesTest.java | 0 .../e2e/server/SingletonResourceTest.java | 0 .../e2e/server/StreamMethodCallTest.java | 2 +- .../tests/e2e/server/SubResourceTest.java | 2 +- .../server/SubjectSecurityContextTest.java | 2 +- .../e2e/server/UriBuilderTemplateTest.java | 2 +- .../WebApplicationExceptionLoggingTest.java | 2 +- .../server/filter/PostToPutDeleteTest.java | 2 +- .../e2e/server/filter/RolesAllowedTest.java | 2 +- ...riConnegLanguageMediaTypeNegativeTest.java | 2 +- .../UriConnegLanguageMediaTypeTest.java | 10 +- .../server/filter/UriConnegLanguageTest.java | 8 +- .../filter/UriConnegMappingFromProperty.java | 15 +- .../UriConnegMappingFromStringTest.java | 2 +- .../server/filter/UriConnegMediaTypeTest.java | 12 +- .../monitoring/ApplicationInfoTest.java | 2 +- .../server/monitoring/EventListenerTest.java | 2 +- .../e2e/server/monitoring/MBeansTest.java | 2 +- .../MonitoringStatisticsLocatorTest.java | 2 +- .../ReloadApplicationEventTest.java | 2 +- .../monitoring/StatisticsDestroyTest.java | 2 +- .../mvc/BeanValidationErrorTemplateTest.java | 2 +- .../server/mvc/CustomViewableContextTest.java | 2 +- .../e2e/server/mvc/ErrorTemplateTest.java | 2 +- .../mvc/ExceptionViewProcessorTest.java | 2 +- .../mvc/ExplicitProduceTemplateTest.java | 2 +- .../e2e/server/mvc/ExplicitTemplateTest.java | 2 +- .../mvc/FlatInheritedViewProcessorTest.java | 2 +- .../e2e/server/mvc/FlatViewProcessorTest.java | 2 +- .../ImplicitProducesViewProcessorTest.java | 2 +- .../mvc/ImplicitTemplateProgrammaticTest.java | 2 +- .../e2e/server/mvc/ImplicitTemplateTest.java | 2 +- .../ImplicitViewWithResourceFilterTest.java | 2 +- .../mvc/InheritedViewProcessorTest.java | 2 +- .../tests/e2e/server/mvc/MvcEncodingTest.java | 2 +- .../mvc/TemplateMethodSelectionTest.java | 2 +- .../e2e/server/mvc/ViewProcessorTest.java | 2 +- .../server/mvc/provider/AbcViewProcessor.java | 2 +- .../mvc/provider/CustomViewableContext.java | 2 +- .../server/mvc/provider/DefViewProcessor.java | 2 +- .../mvc/provider/TestViewProcessor.java | 2 +- .../spi/AbstractTemplateProcessorTest.java | 0 .../jersey/tests/e2e/server/package-info.java | 2 +- .../e2e/server/routing/InheritanceTest.java | 2 +- .../ResponseMediaTypeFromProvidersTest.java | 9 +- .../e2e/server/scanning/CustomFeature.java | 2 +- .../scanning/RankedProviderScanningTest.java | 2 +- .../tests/e2e/server/scanning/Resource.java | 2 +- .../scanning/ext/Ext1WriterInterceptor.java | 2 +- .../scanning/ext/Ext2WriterInterceptor.java | 2 +- .../scanning/ext/Ext3WriterInterceptor.java | 2 +- .../scanning/ext/Ext4WriterInterceptor.java | 2 +- .../validation/BasicBadSubResource.java | 2 +- .../e2e/server/validation/BasicResource.java | 2 +- .../server/validation/BasicSubResource.java | 2 +- .../validation/BasicValidationTest.java | 2 +- .../e2e/server/validation/ContactBean.java | 14 +- .../e2e/server/validation/CustomBean.java | 2 +- .../CustomConfigValidationTest.java | 2 +- .../server/validation/CustomValidation.java | 2 +- .../EntityInheritanceValidationTest.java | 2 +- .../tests/e2e/server/validation/Extended.java | 2 +- .../validation/FieldPropertyValidation.java | 2 +- .../FieldPropertyValidationResource.java | 2 +- .../FieldPropertyValidationTest.java | 2 +- .../validation/InheritanceValidationTest.java | 2 +- .../validation/MultipleParamConstraint.java | 2 +- .../e2e/server/validation/NonEmptyNames.java | 2 +- .../e2e/server/validation/OneContact.java | 2 +- .../server/validation/ParamConstraint.java | 2 +- .../ProgrammaticValidationTest.java | 7 +- .../validation/PropertyValidationTest.java | 2 +- .../validation/ValidationInflector.java | 2 +- .../AnotherContactBean.java | 14 +- .../ValidateOnExecutionAbstractTest.java | 2 +- .../ValidateOnExecutionBasicTest.java | 2 +- ...ateOnExecutionInheritanceGenericsTest.java | 2 +- .../ValidateOnExecutionInheritanceTest.java | 2 +- .../ValidateOnExecutionOverrideTest.java | 2 +- .../server/wadl/OverrideWadlResourceTest.java | 2 +- .../server/wadl/ResourceExtendedFlagTest.java | 11 +- .../e2e/server/wadl/WadlBeanParamTest.java | 2 +- .../server/wadl/WadlEmptyMediaTypeTest.java | 78 +- .../e2e/server/wadl/WadlResourceTest.java | 28 +- .../CustomViewableContext/index.testp | 0 .../javax.ws.rs.ext.MessageBodyReader | 0 .../javax.ws.rs.ext.MessageBodyWriter | 0 ...jersey.internal.spi.ForcedAutoDiscoverable | 1 + .../resources/ValidationMessages.properties | 4 +- .../ErrorTemplateResource/index.testp | 0 .../CustomResolvingClass/index.abc | 0 .../CustomResolvingClass/index.testp | 0 .../CustomResolvingClass/relative.abc | 0 .../CustomResolvingClass/relative.testp | 0 .../ErrorTemplateResource/absolute.abc | 0 .../ErrorTemplateResource/absolute.testp | 0 .../ErrorTemplateResource/index.testp | 0 .../ErrorTemplateResource/relative.testp | 0 .../ErrorTemplateSubResource/absolute.testp | 0 .../ErrorTemplateSubResource/index.testp | 0 .../ErrorTemplateSubResource/relative.testp | 0 .../mvc/ExceptionViewProcessorTest/404.testp | 0 .../WebAppExceptionMapper/406.testp | 0 .../ExplicitNoProducesTemplate/index.abc | 0 .../ExplicitNoProducesTemplate/index.def | 0 .../absolute.abc | 0 .../ExplicitTemplateProducesClass/index.abc | 0 .../relative.abc | 0 .../ExplicitTwoGetProducesTemplate/index.abc | 0 .../ExplicitTwoGetProducesTemplate/index.def | 0 .../index.abc | 0 .../index.def | 0 .../CustomResolvingClass/index.abc | 0 .../CustomResolvingClass/index.testp | 0 .../CustomResolvingClass/relative.abc | 0 .../CustomResolvingClass/relative.testp | 0 .../ExplicitTemplate/absolute.abc | 0 .../ExplicitTemplate/absolute.testp | 0 .../ExplicitTemplate/index.testp | 0 .../ExplicitTemplate/relative.testp | 0 .../absolute.testp | 0 .../ExplicitTemplateSubResource/index.testp | 0 .../relative.testp | 0 ...cessorTest.ExplicitTemplate.override.testp | 0 ...sorTest.ExplicitTemplateBase.inherit.testp | 0 ...cessorTest.ExplicitTemplateBase.show.testp | 0 ...cessorTest.ImplicitTemplate.override.testp | 0 ...essorTest.ImplicitTemplateBase.index.testp | 0 ...sorTest.ImplicitTemplateBase.inherit.testp | 0 ...wProcessorTest.ExplicitTemplate.show.testp | 0 ...rTest.ImplicitExplicitTemplate.index.testp | 0 ...orTest.ImplicitExplicitTemplate.show.testp | 0 ...ProcessorTest.ImplicitTemplate.index.testp | 0 ...orTest.ImplicitWithGetTemplate.index.testp | 0 ...plicitWithSubResourceGetTemplate.sub.testp | 0 .../ExplicitTemplate/show.testp | 0 .../ImplicitExplicitTemplate/index.testp | 0 .../ImplicitExplicitTemplate/show.testp | 0 .../ImplicitTemplate/index.testp | 0 .../ImplicitWithGetTemplate/index.testp | 0 .../sub.testp | 0 .../Handler/index.testp | 0 .../AnotherImplicitGetResource/get.testp | 0 .../AnotherImplicitGetResource/index.testp | 0 .../ImplicitResource/get.testp | 0 .../ImplicitResource/index.testp | 0 .../ImplicitSingletonResource/index.testp | 0 .../ImplicitSubSubResource/index.testp | 0 .../ImplicitTemplate/index.testp | 0 .../ExplicitTemplate/override.testp | 0 .../ExplicitTemplateBase/inherit.testp | 0 .../ExplicitTemplateBase/override.testp | 0 .../ExplicitTemplateBase/show.testp | 0 .../ImplicitTemplate/override.testp | 0 .../ImplicitTemplateBase/index.testp | 0 .../ImplicitTemplateBase/inherit.testp | 0 .../ImplicitTemplateBase/override.testp | 0 .../MvcEncodingTest/FreemarkerResource.ftl | 0 .../MvcEncodingTest/MustacheResource.mustache | 0 .../ResolvingClass/show.testp | 0 .../AsViewableResource/getAsHTML | 0 .../AsViewableResource/index.testp | 0 .../TemplateAnnotatedResource/getAsHTML | 0 .../TemplateAnnotatedResource/index.testp | 0 .../TemplateAnnotatedResourceMethod/getAsHTML | 0 .../index.testp | 0 .../ExplicitTemplate/absolute/show.testp | 0 .../ExplicitTemplate/show.testp | 0 .../ImplicitExplicitTemplate/index.testp | 0 .../ImplicitExplicitTemplate/show.testp | 0 .../ImplicitTemplate/index.testp | 0 .../ImplicitWithGetTemplate/index.testp | 0 .../sub.testp | 0 .../ExplicitTemplate/absolute/show.testp | 0 .../ExplicitTemplate/show.testp | 0 .../ImplicitExplicitTemplate/index.testp | 0 .../ImplicitExplicitTemplate/show.testp | 0 .../ImplicitTemplate/index.testp | 0 .../ImplicitWithGetTemplate/index.testp | 0 .../sub.testp | 0 .../Resource/index.fct | 0 .../e2e/ExecutorServiceProviderTest.java | 10 +- .../e2e/common/ProvidersOrderingTest.java | 7 +- .../jersey/tests/e2e/container/HeadTest.java | 14 +- .../e2e/container/JerseyContainerTest.java | 15 +- .../e2e/oauth/OAuthClientServerTest.java | 14 +- ...jersey.internal.spi.ForcedAutoDiscoverable | 1 - .../jersey2812/Jersey2812ITCase.java | 5 +- .../integration/jersey1883/MyApplication.java | 8 +- .../integration/jersey1960/Jersey1960App.java | 8 +- .../jersey2167/MyValueSupplierProvider.java | 3 +- .../jersey2794/Jersey2794ITCase.java | 17 +- .../jersey2846/Jersey2846ITCase.java | 19 +- .../servlet_25_init_1/Servlet25init1.java | 9 +- .../servlet_25_init_5/Servlet25init5.java | 7 +- .../servlet_25_mvc_1/resource/Item.java | 12 +- .../servlet_25_mvc_2/resource/Item.java | 12 +- .../servlet_25_mvc_3/resource/Item.java | 12 +- .../AsyncServletResourceITCase.java | 5 +- .../AsyncServletResourceTest.java | 5 +- .../RequestResponseWrapperProvider.java | 6 +- .../jersey/osgi/test/util/Helper.java | 5 +- tests/pom.xml | 3 + ...MultiThreadingAggregatedReservoirTest.java | 56 +- 1121 files changed, 29099 insertions(+), 4510 deletions(-) delete mode 100644 bundles/repackaged/jersey-guava/build.gradle delete mode 100644 bundles/repackaged/jersey-guava/pom.xml delete mode 100644 bundles/repackaged/jersey-guava/src/main/resources/META-INF/MANIFEST.MF delete mode 100644 bundles/repackaged/pom.xml create mode 100644 core-client/src/main/java/org/glassfish/jersey/client/ClientBackgroundScheduler.java create mode 100644 core-client/src/main/java/org/glassfish/jersey/client/ClientBackgroundSchedulerLiteral.java create mode 100644 core-client/src/main/java/org/glassfish/jersey/client/ClientExecutor.java create mode 100644 core-client/src/main/java/org/glassfish/jersey/client/DefaultClientBackgroundSchedulerProvider.java rename core-common/src/main/java/org/glassfish/jersey/hk2/{HK2InjectionManager.java => AbstractHk2InjectionManager.java} (65%) create mode 100644 core-common/src/main/java/org/glassfish/jersey/hk2/DelayedHk2InjectionManager.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/hk2/Hk2InjectionManagerFactory.java rename media/sse/src/main/java/org/glassfish/jersey/media/sse/internal/SseValueSupplierProvider.java => core-common/src/main/java/org/glassfish/jersey/hk2/ImmediateHk2InjectionManager.java (51%) create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/BootstrapBag.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/BootstrapConfigurator.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractFuture.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractIndexedListIterator.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractIterator.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractListMultimap.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractMapBasedMultimap.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractMapEntry.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractMultimap.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractSequentialIterator.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractSetMultimap.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractSortedKeySortedSetMultimap.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractSortedSetMultimap.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractTable.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/AsyncFunction.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/Cache.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/CacheBuilder.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/CacheLoader.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/CollectPreconditions.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/Collections2.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/ComparatorOrdering.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/Equivalence.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/ExecutionError.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/ExecutionList.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/ForwardingCollection.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/ForwardingMapEntry.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/ForwardingObject.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/ForwardingSet.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/ForwardingSortedSet.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/Futures.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/GenericMapMaker.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/HashBasedTable.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/HashMultimap.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/Hashing.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/ImmutableEntry.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/InetAddresses.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/Ints.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/Iterables.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/Iterators.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/Joiner.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/ListMultimap.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/ListenableFuture.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/Lists.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/LoadingCache.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/LocalCache.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/MapMaker.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/MapMakerInternalMap.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/Maps.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/MoreExecutors.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/MoreObjects.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/Multimap.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/Multimaps.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/NaturalOrdering.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/NullsFirstOrdering.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/NullsLastOrdering.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/ObjectArrays.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/Ordering.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/PeekingIterator.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/Platform.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/Preconditions.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/Predicates.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/Primitives.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/RemovalCause.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/RemovalNotification.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/ReverseNaturalOrdering.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/ReverseOrdering.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/Serialization.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/SetMultimap.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/Sets.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/SettableFuture.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/SortedIterable.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/SortedIterables.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/SortedLists.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/SortedSetMultimap.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/StandardTable.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/Stopwatch.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/Table.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/Tables.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/ThreadFactoryBuilder.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/Ticker.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/TransformedIterator.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/TreeMultimap.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/UncheckedExecutionException.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/Uninterruptibles.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/UnmodifiableIterator.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/UnmodifiableListIterator.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/guava/package-info.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/inject/InjectionManagerFactory.java create mode 100644 core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Views.java create mode 100644 core-common/src/test/java/org/glassfish/jersey/internal/TestConfigConfigurator.java create mode 100644 core-server/src/main/java/org/glassfish/jersey/server/ApplicationConfigurator.java create mode 100644 core-server/src/main/java/org/glassfish/jersey/server/ComponentProviderConfigurator.java create mode 100644 core-server/src/main/java/org/glassfish/jersey/server/ExternalRequestScopeConfigurator.java create mode 100644 core-server/src/main/java/org/glassfish/jersey/server/JerseyResourceContextConfigurator.java create mode 100644 core-server/src/main/java/org/glassfish/jersey/server/ProcessingProvidersConfigurator.java create mode 100644 core-server/src/main/java/org/glassfish/jersey/server/ResourceBagConfigurator.java create mode 100644 core-server/src/main/java/org/glassfish/jersey/server/ServerBootstrapBag.java create mode 100644 core-server/src/main/java/org/glassfish/jersey/server/internal/inject/ParamConverterConfigurator.java create mode 100644 core-server/src/main/java/org/glassfish/jersey/server/internal/inject/ParamExtractorConfigurator.java delete mode 100644 core-server/src/main/java/org/glassfish/jersey/server/internal/inject/ParameterInjectionBinder.java create mode 100644 core-server/src/main/java/org/glassfish/jersey/server/internal/inject/ValueSupplierProviderConfigurator.java create mode 100644 core-server/src/main/java/org/glassfish/jersey/server/model/internal/ResourceMethodInvokerConfigurator.java rename core-server/src/test/java/org/glassfish/jersey/server/{InjectionManagerFactory.java => TestConfigConfigurator.java} (60%) create mode 100644 core-server/src/test/java/org/glassfish/jersey/server/TestInjectionManagerFactory.java create mode 100644 tests/e2e-client/pom.xml rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/AbortResponseClientTest.java (98%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/BasicClientTest.java (88%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/BufferingTest.java (99%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/CancelFutureClientTest.java (95%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/ChunkedInputStreamClosedPrematurelyTest.java (99%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientBufferingDisabledTest.java (99%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientDestroyTest.java (99%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientEntityAnnotationTest.java (98%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientExecutorCloseTest.java (61%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientFilterTest.java (98%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientPathTest.java (98%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/ClientPreInitTest.java (99%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/FollowRedirectHeadTest.java (98%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/FollowRedirectsTest.java (98%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/GenericResponseTest.java (98%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/HttpAuthorizationTest.java (99%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/HttpDigestAuthFilterTest.java (99%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/HttpMethodEntityTest.java (99%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/InjectedClientBodyWorker.java (99%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/InjectionManagerProviderTest.java (100%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/NonSuccessfulResponseTest.java (98%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/RequestScopedReadEntityTest.java (98%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/ResponseCloseTest.java (98%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/ResponseReadAndBufferEntityTest.java (99%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/ShutdownHookMemoryLeakTest.java (99%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/TimeoutTest.java (98%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/RequestHeaderModificationsTest.java (99%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/AbstractConnectorServerTest.java (98%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/AuthenticationException.java (97%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/AuthenticationExceptionMapper.java (97%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/RootResource.java (98%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/SecurityFilter.java (98%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/Server.java (98%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/SslConnectorConfigurationTest.java (98%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/SslConnectorHostnameVerifierTest.java (98%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/SslHttpUrlConnectorTest.java (98%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/grizzlyconnector/NonBlockingTest.java (98%) rename tests/{e2e => e2e-client}/src/test/java/org/glassfish/jersey/tests/e2e/client/httpurlconnector/AsyncTest.java (99%) rename tests/{e2e => e2e-client}/src/test/resources/org/glassfish/jersey/tests/e2e/client/connector/ssl/keystore-client (100%) rename tests/{e2e => e2e-client}/src/test/resources/org/glassfish/jersey/tests/e2e/client/connector/ssl/keystore-example_com-server (100%) rename tests/{e2e => e2e-client}/src/test/resources/org/glassfish/jersey/tests/e2e/client/connector/ssl/keystore-localhost-server (100%) rename tests/{e2e => e2e-client}/src/test/resources/org/glassfish/jersey/tests/e2e/client/connector/ssl/truststore-example_com-client (100%) rename tests/{e2e => e2e-client}/src/test/resources/org/glassfish/jersey/tests/e2e/client/connector/ssl/truststore-localhost-client (100%) rename tests/{e2e => e2e-client}/src/test/resources/org/glassfish/jersey/tests/e2e/client/connector/ssl/truststore-server (100%) create mode 100644 tests/e2e-entity/pom.xml rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/AbstractParameterTypeArgumentOrderTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/AbstractTypeTester.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/BeanStreamingTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/CharsetTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/ContextResolverMediaTypeTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/EmptyEntityTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/EmptyRequestToEntityParamTest.java (97%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/EmptyRequestWithJaxbTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/EntityTypesTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/GenericTypeAndEntityTest.java (96%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/InjectedProviderTest.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/InterceptedStreamCloseTest.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/InvalidEntityTest.java (97%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/InvalidFormTest.java (97%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/JAXBContextResolverTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/JaxbBean.java (97%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/JaxbBeanType.java (97%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/JsonMoxyTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/MediaTypeSelectionTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/MessageBodyProviderAnnotationsTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/MultipartTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/MyArrayList.java (97%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/NoMessageBodyWorkerTest.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/ParameterTypeArgumentOrderTest.java (97%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/ParameterTypeArgumentResourceReaderWriterOrderTest.java (97%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/ParameterTypeArgumentReversedOrderTest.java (97%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/RenderedImageTypeTest.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/StreamingOutputTest.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/SubResourceDynamicProxyTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/XXETest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/XmlJaxBElementProviderTest.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/XmlMoxyTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/DefaultFilteringScope.java (100%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EmptyEntityTest.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EntityFilteringClientTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EntityFilteringOnClassTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EntityFilteringOnPropertiesTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EntityFilteringScopesTest.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EntityFilteringServerTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/EntityFilteringTest.java (88%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/FilteringMessageBodyProvider.java (97%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/PrimaryDetailedView.java (100%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/SecondaryDetailedView.java (100%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/TertiaryDetailedView.java (100%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/ComplexEntity.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/ComplexSubEntity.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/ComplexSubSubEntity.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/DefaultFilteringSubEntity.java (97%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/EmptyEntity.java (96%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/FilteredClassEntity.java (97%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/ManyFilteringsOnClassEntity.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/ManyFilteringsOnPropertiesEntity.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/ManyFilteringsSubEntity.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/NonEmptyEntity.java (96%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/OneFilteringOnClassEntity.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/OneFilteringOnPropertiesEntity.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/domain/OneFilteringSubEntity.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/JsonEmptyEntityTest.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/JsonEntityFilteringClientTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/JsonEntityFilteringOnClassTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/JsonEntityFilteringOnPropertiesTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/JsonEntityFilteringScopesTest.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/JsonEntityFilteringServerTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/entity/filtering/json/MoxyEntityFilteringTest.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/AbstractJsonTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/GenericTest.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/InheritanceTest.java (97%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/Jackson1JsonViewTest.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/JacksonJsonViewTest.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/JaxbTest.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/Jersey1199Test.java (97%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/Jersey1835Test.java (97%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/JsonProcessingDisabledTest.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/JsonProcessingTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/JsonTestHelper.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/JsonTestProvider.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/JsonWithPaddingEncodingFilterTest.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/JsonWithPaddingTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/PojoTest.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/Animal.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/AnimalList.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/AnotherAnimal.java (97%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/AnotherArrayTestBean.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/AnotherCat.java (97%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/AttrAndCharDataBean.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/Cat.java (97%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/Color.java (96%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ColorHolder.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ComplexBeanWithAttributes.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ComplexBeanWithAttributes2.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ComplexBeanWithAttributes3.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ComplexBeanWithAttributes4.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/Dog.java (97%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/EmptyElementBean.java (97%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/EmptyElementContainingBean.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/EncodedContentBean.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/FakeArrayBean.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/IntArray.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/Jersey1199List.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ListAndNonListBean.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ListEmptyBean.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/ListWrapperBean.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/MyError.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/MyMessage.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/MyResponse.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/NamespaceBean.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/NamespaceBeanWithAttribute.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/NullStringBean.java (97%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/Person.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/PureCharDataBean.java (97%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/RegisterMessage.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SimpleBean.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SimpleBeanWithAttributes.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SimpleBeanWithJustOneAttribute.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SimpleBeanWithJustOneAttributeAndValue.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SimpleBeanWithObjectAttributes.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SimpleXmlTypeBean.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/SingleItemListWrapperBean.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/TreeModel.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/TwoListsWrapperBean.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/User.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/UserTable.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/pojo/PojoAnimal.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/pojo/PojoAnimalList.java (98%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/pojo/PojoCat.java (97%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/json/entity/pojo/PojoDog.java (97%) create mode 100644 tests/e2e-entity/src/test/java/org/glassfish/jersey/tests/e2e/sse/BroadcasterExecutorTest.java rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/sse/BroadcasterTest.java (97%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/sse/EventOutputTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/sse/EventSourceWithNamedEventsTest.java (99%) rename tests/{e2e => e2e-entity}/src/test/java/org/glassfish/jersey/tests/e2e/sse/SseEventSinkToEventSourceTest.java (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/entity/Jersey_yellow.png (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/entity/duke_rocket.gif (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/entity/multipart-testcase.txt (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/entity/xxe.txt (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_AnimalList.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_AnimalList_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_AnotherArrayTestBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_AnotherArrayTestBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_AttrAndCharDataBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_AttrAndCharDataBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes2.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes2_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes3.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes3_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes4.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes4_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ComplexBeanWithAttributes_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_EmptyElementBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_EmptyElementBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_EmptyElementContainingBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_EmptyElementContainingBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_EncodedContentBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_EncodedContentBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_FakeArrayBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_FakeArrayBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_IntArray.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_IntArray_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_Jersey1199List.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_Jersey1199List_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ListAndNonListBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ListAndNonListBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ListEmptyBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ListEmptyBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ListWrapperBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_ListWrapperBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_MyResponse.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_MyResponse_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_NamespaceBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_NamespaceBeanWithAttribute.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_NamespaceBeanWithAttribute_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_NamespaceBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_NullStringBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_NullStringBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_Person.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_Person_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_PojoAnimalList.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_PojoAnimalList_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_PureCharDataBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_PureCharDataBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_RegisterMessage.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_RegisterMessage_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBeanWithAttributes.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBeanWithAttributes_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBeanWithJustOneAttribute.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBeanWithJustOneAttributeAndValue.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBeanWithJustOneAttributeAndValue_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBeanWithJustOneAttribute_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_SimpleBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_TreeModel.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_TreeModel_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_TwoListsWrapperBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_TwoListsWrapperBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_User.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_UserTable.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_UserTable_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/Jackson1JsonTestProvider_User_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_AnimalList.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_AnimalList_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_AnotherArrayTestBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_AnotherArrayTestBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_AttrAndCharDataBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_AttrAndCharDataBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes2.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes2_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes3.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes3_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes4.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes4_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ComplexBeanWithAttributes_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_EmptyElementBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_EmptyElementBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_EmptyElementContainingBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_EmptyElementContainingBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_EncodedContentBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_EncodedContentBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_FakeArrayBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_FakeArrayBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_IntArray.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_IntArray_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_Jersey1199List.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_Jersey1199List_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ListAndNonListBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ListAndNonListBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ListEmptyBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ListEmptyBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ListWrapperBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_ListWrapperBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_MyResponse.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_MyResponse_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_NamespaceBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_NamespaceBeanWithAttribute.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_NamespaceBeanWithAttribute_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_NamespaceBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_NullStringBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_NullStringBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_Person.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_Person_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_PojoAnimalList.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_PojoAnimalList_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_PureCharDataBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_PureCharDataBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_RegisterMessage.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_RegisterMessage_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBeanWithAttributes.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBeanWithAttributes_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBeanWithJustOneAttribute.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBeanWithJustOneAttribute_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_SimpleBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_TreeModel.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_TreeModel_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_TwoListsWrapperBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_TwoListsWrapperBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_User.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_UserTable.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_UserTable_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JacksonJsonTestProvider_User_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_AnimalList.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_AnimalList_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_AnotherArrayTestBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_AnotherArrayTestBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_AttrAndCharDataBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_AttrAndCharDataBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes2.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes2_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes3.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes3_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes4.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes4_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ComplexBeanWithAttributes_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_EmptyElementBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_EmptyElementBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_EmptyElementContainingBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_EmptyElementContainingBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_EncodedContentBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_EncodedContentBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_FakeArrayBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_FakeArrayBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_IntArray.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_IntArray_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_Jersey1199List.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_Jersey1199List_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ListAndNonListBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ListAndNonListBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ListEmptyBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ListEmptyBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ListWrapperBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_ListWrapperBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_MyResponse.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_MyResponse_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_NamespaceBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_NamespaceBeanWithAttribute.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_NamespaceBeanWithAttribute_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_NamespaceBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_NullStringBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_NullStringBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_Person.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_Person_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_PureCharDataBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_PureCharDataBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_RegisterMessage.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_RegisterMessage_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBeanWithAttributes.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBeanWithAttributes_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBeanWithJustOneAttribute.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBeanWithJustOneAttribute_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_SimpleBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_TreeModel.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_TreeModel_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_TwoListsWrapperBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_TwoListsWrapperBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_User.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_UserTable.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_UserTable_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonBadgerfishJsonTestProvider_User_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_AnimalList.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_AnimalList_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_AnotherArrayTestBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_AnotherArrayTestBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_AttrAndCharDataBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_AttrAndCharDataBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes2.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes2_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes3.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes3_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes4.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes4_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ComplexBeanWithAttributes_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_EmptyElementBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_EmptyElementBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_EmptyElementContainingBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_EmptyElementContainingBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_EncodedContentBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_EncodedContentBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_FakeArrayBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_FakeArrayBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_IntArray.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_IntArray_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_Jersey1199List.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_Jersey1199List_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ListAndNonListBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ListAndNonListBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ListEmptyBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ListEmptyBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ListWrapperBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_ListWrapperBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_MyResponse.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_MyResponse_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_NamespaceBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_NamespaceBeanWithAttribute.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_NamespaceBeanWithAttribute_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_NamespaceBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_NullStringBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_NullStringBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_Person.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_Person_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_PureCharDataBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_PureCharDataBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_RegisterMessage.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_RegisterMessage_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBeanWithAttributes.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBeanWithAttributes_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBeanWithJustOneAttribute.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBeanWithJustOneAttribute_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SimpleBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SingleItemListWrapperBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_SingleItemListWrapperBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_TreeModel.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_TreeModel_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_TwoListsWrapperBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_TwoListsWrapperBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_User.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_UserTable.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_UserTable_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/JettisonMappedJsonTestProvider_User_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_AnimalList.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_AnimalList_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_AnotherArrayTestBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_AnotherArrayTestBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_AttrAndCharDataBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_AttrAndCharDataBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes2.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes2_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes3.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes3_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes4.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes4_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ComplexBeanWithAttributes_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_EmptyElementBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_EmptyElementBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_EmptyElementContainingBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_EmptyElementContainingBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_EncodedContentBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_EncodedContentBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_FakeArrayBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_FakeArrayBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_IntArray.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_IntArray_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_Jersey1199List.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_Jersey1199List_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ListAndNonListBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ListAndNonListBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ListEmptyBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ListEmptyBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ListWrapperBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_ListWrapperBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_MyResponse.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_MyResponse_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_NamespaceBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_NamespaceBeanWithAttribute.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_NamespaceBeanWithAttribute_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_NamespaceBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_NullStringBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_NullStringBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_Person.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_Person_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_PureCharDataBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_PureCharDataBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_RegisterMessage.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_RegisterMessage_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBeanWithAttributes.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBeanWithAttributes_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBeanWithJustOneAttribute.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBeanWithJustOneAttributeAndValue_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBeanWithJustOneAttribute_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_SimpleBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_TreeModel.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_TreeModel_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_TwoListsWrapperBean.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_TwoListsWrapperBean_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_User.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_UserTable.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_UserTable_MOXy.json (100%) rename tests/{e2e => e2e-entity}/src/test/resources/org/glassfish/jersey/tests/e2e/json/entity/MoxyJsonTestProvider_User_MOXy.json (100%) create mode 100644 tests/e2e-server/pom.xml rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/AbstractDisableMetainfServicesLookupTest.java (100%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/AllInjectablesTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/AmbigousResourceMethodTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/AppNameBindingTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/AppNameBindingTest2.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/AsyncCallbackTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/AsyncResponseTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/BeanParamExceptionTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/BeanParamTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/BroadcasterTest.java (95%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/ChunkedInputOutputTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/ClientResponseOnServerTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/CloseableTest.java (97%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/ContainerListenerRegistrationAsProvidersTest.java (97%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/ContentNegotiationTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/CustomInjectablesApplicationTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/CustomInjectablesResourceConfigTest.java (100%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/CustomMultivaluedMapProviderTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/EncodedFormParamTest.java (97%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/EncodedSlashInPathSegmentTest.java (97%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/EntityExpansionTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionLoggingTest.java (88%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionMapperPropagationTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/ExceptionMapperTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/ExtendedExceptionMapperTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/ExtendedUriInfoTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/FormParamMultivaluedInjectionTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/GlobalNameBoundInterceptorTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/GloballyNameBoundResourceFilterTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/GrizzlyInjectionTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/InitializationLoggingTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/InjectionManagerServerProviderTest.java (100%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/InjectionTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/InputStreamResponseTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/InterceptorHttpHeadersInjectionTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/InterceptorNameAndDynamicBindingTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/MessageBodyProvidersExceptionsTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/MetainfServicesLookupDisabledTest.java (97%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/MetainfServicesLookupEnabledTest.java (96%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/ModelProcessorTest.java (96%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/NameBindingTest.java (97%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/ParamConverterTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/PathEncodingTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/PerRequestLifecycleTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/PostConstructTest.java (92%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/PrimitiveTypesTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/ReloadTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/RequestScopedAndAsyncTest.java (100%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/ResourceConfigTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/ResourceFilterTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/ResourceRoutingTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/ResponseStatusTypeTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/RuntimeConfigTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/SecurityContextFilterTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/ServerDestroyTest.java (96%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/SingletonProviderTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/SingletonProvidersResourcesTest.java (100%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/SingletonResourceTest.java (100%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/StreamMethodCallTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/SubResourceTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/SubjectSecurityContextTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/UriBuilderTemplateTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/WebApplicationExceptionLoggingTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/PostToPutDeleteTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/RolesAllowedTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/UriConnegLanguageMediaTypeNegativeTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/UriConnegLanguageMediaTypeTest.java (95%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/UriConnegLanguageTest.java (96%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/UriConnegMappingFromProperty.java (83%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/UriConnegMappingFromStringTest.java (97%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/filter/UriConnegMediaTypeTest.java (95%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/monitoring/ApplicationInfoTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/monitoring/EventListenerTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/monitoring/MBeansTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/monitoring/MonitoringStatisticsLocatorTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/monitoring/ReloadApplicationEventTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/monitoring/StatisticsDestroyTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/BeanValidationErrorTemplateTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/CustomViewableContextTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ExceptionViewProcessorTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateProgrammaticTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitViewWithResourceFilterTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/MvcEncodingTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/provider/AbcViewProcessor.java (97%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/provider/CustomViewableContext.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/provider/DefViewProcessor.java (97%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/provider/TestViewProcessor.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/mvc/spi/AbstractTemplateProcessorTest.java (100%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/package-info.java (96%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/routing/InheritanceTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/routing/ResponseMediaTypeFromProvidersTest.java (97%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/CustomFeature.java (97%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/RankedProviderScanningTest.java (97%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/Resource.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/ext/Ext1WriterInterceptor.java (97%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/ext/Ext2WriterInterceptor.java (97%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/ext/Ext3WriterInterceptor.java (97%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/scanning/ext/Ext4WriterInterceptor.java (97%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/BasicBadSubResource.java (97%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/BasicResource.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/BasicSubResource.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/BasicValidationTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/ContactBean.java (92%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/CustomBean.java (97%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/CustomConfigValidationTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/CustomValidation.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/EntityInheritanceValidationTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/Extended.java (96%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/FieldPropertyValidation.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/FieldPropertyValidationResource.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/FieldPropertyValidationTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/InheritanceValidationTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/MultipleParamConstraint.java (97%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/NonEmptyNames.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/OneContact.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/ParamConstraint.java (97%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/ProgrammaticValidationTest.java (97%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/PropertyValidationTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/ValidationInflector.java (97%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/AnotherContactBean.java (93%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/ValidateOnExecutionAbstractTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/ValidateOnExecutionBasicTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/ValidateOnExecutionInheritanceGenericsTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/ValidateOnExecutionInheritanceTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/validateonexecution/ValidateOnExecutionOverrideTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/OverrideWadlResourceTest.java (97%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/ResourceExtendedFlagTest.java (98%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlBeanParamTest.java (99%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlEmptyMediaTypeTest.java (52%) rename tests/{e2e => e2e-server}/src/test/java/org/glassfish/jersey/tests/e2e/server/wadl/WadlResourceTest.java (98%) rename tests/{e2e => e2e-server}/src/test/resources/CustomViewableContext/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/META-INF/services/javax.ws.rs.ext.MessageBodyReader (100%) rename tests/{e2e => e2e-server}/src/test/resources/META-INF/services/javax.ws.rs.ext.MessageBodyWriter (100%) create mode 100644 tests/e2e-server/src/test/resources/META-INF/services/org.glassfish.jersey.internal.spi.ForcedAutoDiscoverable rename tests/{e2e => e2e-server}/src/test/resources/ValidationMessages.properties (94%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/BeanValidationErrorTemplateTest/ErrorTemplateResource/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/CustomResolvingClass/index.abc (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/CustomResolvingClass/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/CustomResolvingClass/relative.abc (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/CustomResolvingClass/relative.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateResource/absolute.abc (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateResource/absolute.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateResource/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateResource/relative.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateSubResource/absolute.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateSubResource/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ErrorTemplateTest/ErrorTemplateSubResource/relative.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExceptionViewProcessorTest/404.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExceptionViewProcessorTest/WebAppExceptionMapper/406.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitNoProducesTemplate/index.abc (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitNoProducesTemplate/index.def (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitTemplateProducesClass/absolute.abc (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitTemplateProducesClass/index.abc (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitTemplateProducesClass/relative.abc (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitTwoGetProducesTemplate/index.abc (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitTwoGetProducesTemplate/index.def (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitWildcardProducesTemplate/index.abc (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitProduceTemplateTest/ExplicitWildcardProducesTemplate/index.def (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/CustomResolvingClass/index.abc (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/CustomResolvingClass/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/CustomResolvingClass/relative.abc (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/CustomResolvingClass/relative.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplate/absolute.abc (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplate/absolute.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplate/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplate/relative.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplateSubResource/absolute.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplateSubResource/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ExplicitTemplateTest/ExplicitTemplateSubResource/relative.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ExplicitTemplate.override.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ExplicitTemplateBase.inherit.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ExplicitTemplateBase.show.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ImplicitTemplate.override.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ImplicitTemplateBase.index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatInheritedViewProcessorTest.ImplicitTemplateBase.inherit.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ExplicitTemplate.show.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ImplicitExplicitTemplate.index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ImplicitExplicitTemplate.show.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ImplicitTemplate.index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ImplicitWithGetTemplate.index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/FlatViewProcessorTest.ImplicitWithSubResourceGetTemplate.sub.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ExplicitTemplate/show.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitExplicitTemplate/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitExplicitTemplate/show.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitTemplate/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitWithGetTemplate/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitProducesViewProcessorTest/ImplicitWithSubResourceGetTemplate/sub.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateProgrammaticTest/Handler/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/AnotherImplicitGetResource/get.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/AnotherImplicitGetResource/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/ImplicitResource/get.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/ImplicitResource/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/ImplicitSingletonResource/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitTemplateTest/ImplicitSubSubResource/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ImplicitViewWithResourceFilterTest/ImplicitTemplate/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ExplicitTemplate/override.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ExplicitTemplateBase/inherit.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ExplicitTemplateBase/override.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ExplicitTemplateBase/show.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ImplicitTemplate/override.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ImplicitTemplateBase/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ImplicitTemplateBase/inherit.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/InheritedViewProcessorTest/ImplicitTemplateBase/override.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/MvcEncodingTest/FreemarkerResource.ftl (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/MvcEncodingTest/MustacheResource.mustache (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ResolvingClassViewProcessorTest/ResolvingClass/show.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest/AsViewableResource/getAsHTML (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest/AsViewableResource/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest/TemplateAnnotatedResource/getAsHTML (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest/TemplateAnnotatedResource/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest/TemplateAnnotatedResourceMethod/getAsHTML (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateMethodSelectionTest/TemplateAnnotatedResourceMethod/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ExplicitTemplate/absolute/show.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ExplicitTemplate/show.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ImplicitExplicitTemplate/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ImplicitExplicitTemplate/show.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ImplicitTemplate/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ImplicitWithGetTemplate/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/TemplateProcessorTest/ImplicitWithSubResourceGetTemplate/sub.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ExplicitTemplate/absolute/show.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ExplicitTemplate/show.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ImplicitExplicitTemplate/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ImplicitExplicitTemplate/show.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ImplicitTemplate/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ImplicitWithGetTemplate/index.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/ViewProcessorTest/ImplicitWithSubResourceGetTemplate/sub.testp (100%) rename tests/{e2e => e2e-server}/src/test/resources/org/glassfish/jersey/tests/e2e/server/mvc/spi/AbstractTemplateProcessorTest/Resource/index.fct (100%) diff --git a/bom/pom.xml b/bom/pom.xml index 97da21255e..afafa6ab50 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -81,11 +81,6 @@ jaxrs-ri ${project.version} - - org.glassfish.jersey.bundles.repackaged - jersey-guava - ${project.version} - org.glassfish.jersey.bundles.repackaged jersey-jsr166e diff --git a/bundles/jaxrs-ri/pom.xml b/bundles/jaxrs-ri/pom.xml index 345c4e7d07..d3f7a0941d 100644 --- a/bundles/jaxrs-ri/pom.xml +++ b/bundles/jaxrs-ri/pom.xml @@ -166,12 +166,6 @@ hk2-locator provided - - org.glassfish.jersey.bundles.repackaged - jersey-guava - ${project.version} - provided - org.osgi org.osgi.core diff --git a/bundles/pom.xml b/bundles/pom.xml index 48ac1eddfe..5550f7ccf1 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -58,7 +58,6 @@ Jersey bundles providers umbrella project module. - repackaged jaxrs-ri diff --git a/bundles/repackaged/jersey-guava/build.gradle b/bundles/repackaged/jersey-guava/build.gradle deleted file mode 100644 index f16a069ab5..0000000000 --- a/bundles/repackaged/jersey-guava/build.gradle +++ /dev/null @@ -1,132 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2014-2015 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * http://glassfish.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -/** - * This task deletes all (unpacked) repackaged classes from target/classes directory. - * Needed to be able to run `mvn install` repeatedly without cleaning the module. - */ -task deleteClasses(type: Delete) << { - def dir = file ("" + buildDir - + "/classes/" - + shadedPackage.replaceAll('\\.', '/')) - - if (dir.isDirectory()) { - dir.deleteDir() - } -} - -task generateProGuardConfig << { - // import regexp pattern (strip method name from static imports) (shadedPackage is script parameter) - def pattern = ~/.*(${shadedPackage}(\.[a-z0-9]+)*\.[A-Z][A-Za-z0-9]*)(\.[A-Za-z0-9]+)*.*/ - def classes = [] as Set - - // create module list from core ... - def modules = [] - - ['/core-common', '/core-client', '/core-server'].each { - modules << absolutePath + it - } - - // ... and all projects in connectors, containers, ext and media - ['/connectors', '/containers', '/ext', '/media'].each { - new File(absolutePath + it).listFiles().each { - if (it.isDirectory()) { - modules << it.absolutePath - } - } - } - - // for each module ... - modules.each { - // ... get files in the (sub-)tree ... (absolutePath is script parameter) - fileTree(it + '/src/main/java') - // ... that includes only *.java files ... - .include('**/*.java') - .each { File file -> - - // ... store class from import that match pattern into a set ... - file.eachLine { - def matcher = pattern.matcher(it) - - if(matcher.matches()) { - classes << matcher.group(1) - } - } - } - } - - // ... and create ProGuard configuration file ... (confFile is script parameter) - def conf = new File(confFile) - conf.delete() - conf.createNewFile() - - def ln = System.getProperty('line.separator') - - classes.each { - conf << ("-keep public class ${it}${ln}") - conf << ("-keepclassmembers public class ${it} { *; }${ln}") - } -} - -task copyShadedJar(type: Copy) { - def version = project.hasProperty('jerseyVersion') ? jerseyVersion : '' - from ('target') { - include 'jersey-guava-' + version + '-shaded-sources.jar' - } - into 'target' - - rename { - String fileName -> fileName.replace('shaded-sources', 'sources') - } -} - -task deleteShadedJar(type: Delete) { - def version = project.hasProperty('jerseyVersion') ? jerseyVersion : '' - delete ('target/jersey-guava-' + version + '-shaded-sources.jar') -} - -task renameShadedJar(dependsOn: ['copyShadedJar', 'deleteShadedJar']) -deleteShadedJar.mustRunAfter copyShadedJar - -copyShadedJar.onlyIf {project.hasProperty('jerseyVersion')} -deleteShadedJar.onlyIf {project.hasProperty('jerseyVersion')} -renameShadedJar.onlyIf {project.hasProperty('jerseyVersion')} -generateProGuardConfig.onlyIf {!project.hasProperty('jerseyVersion')} - diff --git a/bundles/repackaged/jersey-guava/pom.xml b/bundles/repackaged/jersey-guava/pom.xml deleted file mode 100644 index d13b82a1ff..0000000000 --- a/bundles/repackaged/jersey-guava/pom.xml +++ /dev/null @@ -1,270 +0,0 @@ - - - - - 4.0.0 - - - org.glassfish.jersey.bundles.repackaged - project - 2.26-SNAPSHOT - - - jersey-guava - bundle - jersey-repackaged-guava - - Jersey Guava Repackaged - - - - - org.apache.maven.plugins - maven-shade-plugin - true - - false - true - true - - - com.google.guava:guava:* - - - - - com.google.common - ${shade.package} - - - com.google.thirdparty - ${shade.package.thirdparty} - - - - - - org.apache.felix - maven-bundle-plugin - - 2.3.7 - true - true - - - osgi-bundle - - manifest - - - - - - - !${jersey.repackaged.prefix}.com.google.common.*, - !${jersey.repackaged.prefix}.com.google.thirdparty.*, - javax.annotation;resolution:=optional;version="[1.2,2)", - javax.inject;resolution:=optional;version="[1.0,2)", - sun.misc.*;resolution:=optional, - * - - - !${jersey.repackaged.prefix}.com.google.common.base.internal, - ${jersey.repackaged.prefix}.com.google.common.*, - ${jersey.repackaged.prefix}.com.google.thirdparty.* - - <_include>src/main/resources/META-INF/MANIFEST.MF - - true - - - - - - - - release - - false - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - package - - copy - - - - - com.google.code.findbugs - jsr305 - 2.0.1 - jsr305.jar - - - org.glassfish.hk2.external - javax.inject - javax.inject.jar - - - ${project.build.directory}/dependencies - false - true - - - - - - org.fortasoft - gradle-maven-plugin - - ${gradleHome} - - - - proguard - package - - invoke - - - - deleteClasses - generateProGuardConfig - - - -q - -PabsolutePath=${project.parent.parent.parent.basedir} - -PbuildDir=${project.build.directory} - -PconfFile=${project.build.directory}/proguard.conf - -PshadedPackage=${shade.package} - - - - - renameShaddedJar - integration-test - - invoke - - - - deleteClasses - renameShadedJar - - - -q - -PbuildDir=${project.build.directory} - -PjerseyVersion=${jersey.version} - -PshadedPackage=${shade.package} - - - - - - - org.apache.maven.plugins - maven-shade-plugin - true - - ${project.build.directory}/${project.build.finalName}-shade.jar - - - - com.github.wvengen - proguard-maven-plugin - - - proguard - package - - proguard - - - ${project.build.finalName}-shade.jar - ${project.build.finalName}.jar - - ${java.home}/lib/rt.jar - ${java.home}/lib/jsse.jar - ${project.build.directory}/dependencies/jsr305.jar - ${project.build.directory}/dependencies/javax.inject.jar - - - - - - - - false - ${project.build.directory}/proguard.conf - - - - - - org.apache.felix - maven-bundle-plugin - - - - - - - - - com.google.guava - guava - true - - - - - ${jersey.repackaged.prefix}.com.google.common - ${jersey.repackaged.prefix}.com.google.thirdparty - - diff --git a/bundles/repackaged/jersey-guava/src/main/resources/META-INF/MANIFEST.MF b/bundles/repackaged/jersey-guava/src/main/resources/META-INF/MANIFEST.MF deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/bundles/repackaged/pom.xml b/bundles/repackaged/pom.xml deleted file mode 100644 index 47bd01e4cf..0000000000 --- a/bundles/repackaged/pom.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - - 4.0.0 - - - org.glassfish.jersey.bundles - project - 2.26-SNAPSHOT - - - org.glassfish.jersey.bundles.repackaged - project - pom - jersey-repackaged - - Jersey Repackaged Dependencies. - - - jersey-guava - - diff --git a/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnector.java b/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnector.java index 8972f2dde8..53693ee604 100644 --- a/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnector.java +++ b/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnector.java @@ -51,6 +51,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicLong; import java.util.logging.Level; @@ -123,8 +124,6 @@ import org.apache.http.util.TextUtils; import org.apache.http.util.VersionInfo; -import jersey.repackaged.com.google.common.util.concurrent.MoreExecutors; - /** * A {@link Connector} that utilizes the Apache HTTP Client to send and receive * HTTP request and responses. @@ -478,7 +477,7 @@ public ClientResponse apply(final ClientRequest clientRequest) throws Processing } try { - responseContext.setEntityStream(new HttpClientResponseInputStream(getInputStream(response))); + responseContext.setEntityStream(getInputStream(response)); } catch (final IOException e) { LOGGER.log(Level.SEVERE, null, e); } @@ -491,16 +490,16 @@ public ClientResponse apply(final ClientRequest clientRequest) throws Processing @Override public Future apply(final ClientRequest request, final AsyncConnectorCallback callback) { - return MoreExecutors.sameThreadExecutor().submit(new Runnable() { - @Override - public void run() { - try { - callback.response(apply(request)); - } catch (final Throwable t) { - callback.failure(t); - } - } - }); + try { + ClientResponse response = apply(request); + callback.response(response); + return CompletableFuture.completedFuture(response); + } catch (Throwable t) { + callback.failure(t); + CompletableFuture future = new CompletableFuture<>(); + future.completeExceptionally(t); + return future; + } } @Override @@ -617,18 +616,6 @@ private static Map writeOutBoundHeaders(final MultivaluedMap get() { return output; } + + @GET + @Path("get") + @Produces(MediaType.TEXT_PLAIN) + public String getString() { + return "OK"; + } } } diff --git a/connectors/grizzly-connector/src/main/java/org/glassfish/jersey/grizzly/connector/GrizzlyConnector.java b/connectors/grizzly-connector/src/main/java/org/glassfish/jersey/grizzly/connector/GrizzlyConnector.java index 7a8557db8d..4817fc9de1 100644 --- a/connectors/grizzly-connector/src/main/java/org/glassfish/jersey/grizzly/connector/GrizzlyConnector.java +++ b/connectors/grizzly-connector/src/main/java/org/glassfish/jersey/grizzly/connector/GrizzlyConnector.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2010-2016 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -46,6 +46,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -86,8 +87,6 @@ import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; import com.ning.http.util.ProxyUtils; -import jersey.repackaged.com.google.common.util.concurrent.SettableFuture; - /** * The transport using the AsyncHttpClient. * @@ -204,7 +203,7 @@ public ClientResponse apply(final ClientRequest request) { final Request connectorRequest = translate(request); final Map clientHeadersSnapshot = writeOutBoundHeaders(request.getHeaders(), connectorRequest); - final SettableFuture responseFuture = SettableFuture.create(); + final CompletableFuture responseFuture = new CompletableFuture<>(); final ByteBufferInputStream entityStream = new ByteBufferInputStream(); final AtomicBoolean futureSet = new AtomicBoolean(false); @@ -225,9 +224,9 @@ public STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception { } HeaderUtils.checkHeaderChanges(clientHeadersSnapshot, request.getHeaders(), - GrizzlyConnector.this.getClass().getName()); + GrizzlyConnector.this.getClass().getName()); - responseFuture.set(translate(request, this.status, headers, entityStream)); + responseFuture.complete(translate(request, this.status, headers, entityStream)); return STATE.CONTINUE; } @@ -249,7 +248,7 @@ public void onThrowable(Throwable t) { if (futureSet.compareAndSet(false, true)) { t = t instanceof IOException ? new ProcessingException(t.getMessage(), t) : t; - responseFuture.setException(t); + responseFuture.completeExceptionally(t); } } }); @@ -282,13 +281,13 @@ public STATE onStatusReceived(final HttpResponseStatus responseStatus) throws Ex } @Override - public STATE onHeadersReceived(final HttpResponseHeaders headers) throws Exception { + public STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception { if (!callbackInvoked.compareAndSet(false, true)) { return STATE.ABORT; } HeaderUtils.checkHeaderChanges(clientHeadersSnapshot, request.getHeaders(), - GrizzlyConnector.this.getClass().getName()); + GrizzlyConnector.this.getClass().getName()); // hand-off to grizzly's application thread pool for response processing processResponse(new Runnable() { @Override @@ -328,9 +327,9 @@ public void onThrowable(Throwable t) { if (callbackInvoked.compareAndSet(false, true)) { callback.failure(failure); } - final SettableFuture errorFuture = SettableFuture.create(); - errorFuture.setException(failure); - return errorFuture; + CompletableFuture future = new CompletableFuture<>(); + future.completeExceptionally(failure); + return future; } @Override diff --git a/connectors/jetty-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyConnector.java b/connectors/jetty-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyConnector.java index 9c8aafc19f..9eafe0960d 100644 --- a/connectors/jetty-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyConnector.java +++ b/connectors/jetty-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyConnector.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -52,6 +52,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -97,10 +98,6 @@ import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; -import jersey.repackaged.com.google.common.util.concurrent.FutureCallback; -import jersey.repackaged.com.google.common.util.concurrent.Futures; -import jersey.repackaged.com.google.common.util.concurrent.SettableFuture; - /** * A {@link Connector} that utilizes the Jetty HTTP Client to send and receive * HTTP request and responses. @@ -167,7 +164,7 @@ class JettyConnector implements Connector { sslContextFactory.setSslContext(sslContext); Boolean enableHostnameVerification = (Boolean) config.getProperties() - .get(JettyClientProperties.ENABLE_SSL_HOSTNAME_VERIFICATION); + .get(JettyClientProperties.ENABLE_SSL_HOSTNAME_VERIFICATION); if (enableHostnameVerification != null && enableHostnameVerification) { sslContextFactory.setEndpointIdentificationAlgorithm("https"); } @@ -256,7 +253,7 @@ public ClientResponse apply(final ClientRequest jerseyRequest) throws Processing try { final ContentResponse jettyResponse = jettyRequest.send(); HeaderUtils.checkHeaderChanges(clientHeadersSnapshot, jerseyRequest.getHeaders(), - JettyConnector.this.getClass().getName()); + JettyConnector.this.getClass().getName()); final javax.ws.rs.core.Response.StatusType status = jettyResponse.getReason() == null ? Statuses.from(jettyResponse.getStatus()) @@ -388,20 +385,16 @@ public Future apply(final ClientRequest jerseyRequest, final AsyncConnectorCa final AtomicBoolean callbackInvoked = new AtomicBoolean(false); final Throwable failure; try { - final SettableFuture responseFuture = SettableFuture.create(); - Futures.addCallback(responseFuture, new FutureCallback() { - @Override - public void onSuccess(final ClientResponse result) { - } + final CompletableFuture responseFuture = + new CompletableFuture().whenComplete( + (clientResponse, throwable) -> { + if (throwable != null && throwable instanceof CancellationException) { + // take care of future cancellation + jettyRequest.abort(throwable); + + } + }); - @Override - public void onFailure(final Throwable t) { - if (t instanceof CancellationException) { - // take care of future cancellation - jettyRequest.abort(t); - } - } - }); final AtomicReference jerseyResponse = new AtomicReference<>(); final ByteBufferInputStream entityStream = new ByteBufferInputStream(); jettyRequest.send(new Response.Listener.Adapter() { @@ -409,7 +402,7 @@ public void onFailure(final Throwable t) { @Override public void onHeaders(final Response jettyResponse) { HeaderUtils.checkHeaderChanges(clientHeadersSnapshot, jerseyRequest.getHeaders(), - JettyConnector.this.getClass().getName()); + JettyConnector.this.getClass().getName()); if (responseFuture.isDone()) { if (!callbackInvoked.compareAndSet(false, true)) { @@ -429,7 +422,7 @@ public void onContent(final Response jettyResponse, final ByteBuffer content) { final ProcessingException pe = new ProcessingException(ex); entityStream.closeQueue(pe); // try to complete the future with an exception - responseFuture.setException(pe); + responseFuture.completeExceptionally(pe); Thread.currentThread().interrupt(); } } @@ -438,14 +431,14 @@ public void onContent(final Response jettyResponse, final ByteBuffer content) { public void onComplete(final Result result) { entityStream.closeQueue(); // try to complete the future with the response only once truly done - responseFuture.set(jerseyResponse.get()); + responseFuture.complete(jerseyResponse.get()); } @Override public void onFailure(final Response response, final Throwable t) { entityStream.closeQueue(t); // try to complete the future with an exception - responseFuture.setException(t); + responseFuture.completeExceptionally(t); if (callbackInvoked.compareAndSet(false, true)) { callback.failure(t); } @@ -460,7 +453,9 @@ public void onFailure(final Response response, final Throwable t) { if (callbackInvoked.compareAndSet(false, true)) { callback.failure(failure); } - return Futures.immediateFailedFuture(failure); + CompletableFuture future = new CompletableFuture<>(); + future.completeExceptionally(failure); + return future; } private static ClientResponse translateResponse(final ClientRequest jerseyRequest, diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java index 8e3e9813c8..a91d9a7c99 100644 --- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java +++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -44,10 +44,16 @@ import java.io.IOException; import java.io.InputStream; import java.util.Map; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.LinkedBlockingDeque; import javax.ws.rs.core.Response; +import org.glassfish.jersey.client.ClientRequest; +import org.glassfish.jersey.client.ClientResponse; +import org.glassfish.jersey.client.spi.AsyncConnectorCallback; +import org.glassfish.jersey.netty.connector.internal.NettyInputStream; + import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; @@ -59,11 +65,6 @@ import io.netty.handler.codec.http.LastHttpContent; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; -import jersey.repackaged.com.google.common.util.concurrent.SettableFuture; -import org.glassfish.jersey.client.ClientRequest; -import org.glassfish.jersey.client.ClientResponse; -import org.glassfish.jersey.client.spi.AsyncConnectorCallback; -import org.glassfish.jersey.netty.connector.internal.NettyInputStream; /** * Jersey implementation of Netty channel handler. @@ -77,10 +78,10 @@ class JerseyClientHandler extends SimpleChannelInboundHandler { private final AsyncConnectorCallback asyncConnectorCallback; private final ClientRequest jerseyRequest; - private final SettableFuture future; + private final CompletableFuture future; JerseyClientHandler(NettyConnector nettyConnector, ClientRequest request, - AsyncConnectorCallback callback, SettableFuture future) { + AsyncConnectorCallback callback, CompletableFuture future) { this.connector = nettyConnector; this.asyncConnectorCallback = callback; this.jerseyRequest = request; @@ -115,7 +116,7 @@ public String getReasonPhrase() { // request entity handling. if ((response.headers().contains(HttpHeaderNames.CONTENT_LENGTH) && HttpUtil.getContentLength(response) > 0) - || HttpUtil.isTransferEncodingChunked(response)) { + || HttpUtil.isTransferEncodingChunked(response)) { ctx.channel().closeFuture().addListener(new GenericFutureListener>() { @Override @@ -139,7 +140,7 @@ public int read() throws IOException { @Override public void run() { asyncConnectorCallback.response(jerseyResponse); - future.set(jerseyResponse); + future.complete(jerseyResponse); } }); } @@ -175,7 +176,7 @@ public void run() { } }); } - future.setException(cause); + future.completeExceptionally(cause); isList.add(NettyInputStream.END_OF_INPUT_ERROR); } } diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java index faa57d5e34..e41c6c5e6a 100644 --- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java +++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -46,6 +46,7 @@ import java.net.URI; import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -81,7 +82,6 @@ import io.netty.handler.ssl.JdkSslContext; import io.netty.handler.stream.ChunkedWriteHandler; import io.netty.util.concurrent.GenericFutureListener; -import jersey.repackaged.com.google.common.util.concurrent.SettableFuture; import org.glassfish.jersey.client.ClientProperties; import org.glassfish.jersey.client.ClientRequest; import org.glassfish.jersey.client.ClientResponse; @@ -160,7 +160,7 @@ public void failure(Throwable failure) { @Override public Future apply(final ClientRequest jerseyRequest, final AsyncConnectorCallback jerseyCallback) { - final SettableFuture settableFuture = SettableFuture.create(); + final CompletableFuture settableFuture = new CompletableFuture<>(); final URI requestUri = jerseyRequest.getUri(); String host = requestUri.getHost(); @@ -221,7 +221,7 @@ protected void initChannel(SocketChannel ch) throws Exception { @Override public void operationComplete(io.netty.util.concurrent.Future future) throws Exception { if (!settableFuture.isDone()) { - settableFuture.setException(new IOException("Channel closed.")); + settableFuture.completeExceptionally(new IOException("Channel closed.")); } } }; @@ -284,7 +284,7 @@ public void run() { jerseyRequest.writeEntity(); } catch (IOException e) { jerseyCallback.failure(e); - settableFuture.setException(e); + settableFuture.completeExceptionally(e); } } }); @@ -299,7 +299,7 @@ public void run() { } } catch (InterruptedException e) { - settableFuture.setException(e); + settableFuture.completeExceptionally(e); return settableFuture; } diff --git a/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerFactory.java b/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerFactory.java index 3bc8860523..65461a6eff 100644 --- a/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerFactory.java +++ b/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerFactory.java @@ -45,6 +45,7 @@ import javax.ws.rs.ProcessingException; import org.glassfish.jersey.grizzly2.httpserver.internal.LocalizationMessages; +import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder; import org.glassfish.jersey.process.JerseyProcessingUncaughtExceptionHandler; import org.glassfish.jersey.server.ApplicationHandler; import org.glassfish.jersey.server.ResourceConfig; @@ -58,8 +59,6 @@ import org.glassfish.grizzly.ssl.SSLEngineConfigurator; import org.glassfish.grizzly.utils.Charsets; -import jersey.repackaged.com.google.common.util.concurrent.ThreadFactoryBuilder; - /** * Factory for creating Grizzly Http Server. *

diff --git a/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServerFactory.java b/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServerFactory.java index 9271b4fba1..2d7a809df4 100644 --- a/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServerFactory.java +++ b/containers/jdk-http/src/main/java/org/glassfish/jersey/jdkhttp/JdkHttpServerFactory.java @@ -51,6 +51,7 @@ import javax.net.ssl.SSLContext; +import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder; import org.glassfish.jersey.jdkhttp.internal.LocalizationMessages; import org.glassfish.jersey.process.JerseyProcessingUncaughtExceptionHandler; import org.glassfish.jersey.server.ResourceConfig; @@ -62,8 +63,6 @@ import com.sun.net.httpserver.HttpsConfigurator; import com.sun.net.httpserver.HttpsServer; -import jersey.repackaged.com.google.common.util.concurrent.ThreadFactoryBuilder; - /** * Factory for creating {@link HttpServer JDK HttpServer} instances to run Jersey applications. * diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/ServletPropertiesDelegate.java b/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/ServletPropertiesDelegate.java index fd64fd9fa2..3ce4223ca9 100644 --- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/ServletPropertiesDelegate.java +++ b/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/ServletPropertiesDelegate.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -41,15 +41,12 @@ package org.glassfish.jersey.servlet; import java.util.Collection; -import java.util.Iterator; +import java.util.Collections; import javax.servlet.http.HttpServletRequest; import org.glassfish.jersey.internal.PropertiesDelegate; -import jersey.repackaged.com.google.common.collect.Iterators; -import jersey.repackaged.com.google.common.collect.Lists; - /** * @author Martin Matula */ @@ -67,13 +64,8 @@ public Object getProperty(String name) { @Override public Collection getPropertyNames() { - return Lists.newLinkedList(new Iterable() { - @Override - @SuppressWarnings("unchecked") - public Iterator iterator() { - return Iterators.forEnumeration(request.getAttributeNames()); - } - }); + //noinspection unchecked + return Collections.list(request.getAttributeNames()); } @Override diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebComponent.java b/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebComponent.java index f56729b12e..d272b397ea 100644 --- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebComponent.java +++ b/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebComponent.java @@ -58,6 +58,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; import javax.ws.rs.RuntimeType; import javax.ws.rs.core.Form; @@ -110,9 +111,6 @@ import org.glassfish.jersey.servlet.spi.FilterUrlMappingsProvider; import org.glassfish.jersey.uri.UriComponent; -import jersey.repackaged.com.google.common.base.Predicate; -import jersey.repackaged.com.google.common.collect.Collections2; - /** * An common Jersey web component that may be extended by a Servlet and/or * Filter implementation, or encapsulated by a Servlet or Filter implementation. @@ -658,13 +656,9 @@ private List getDecodedQueryParamList(final String queryString) { * @return list of form param values for given name without values of query param of the same name. */ private List filterQueryParams(final String name, final List values, final Collection params) { - return new ArrayList<>(Collections2.filter(values, new Predicate() { - @Override - public boolean apply(final String input) { - return !params.remove(name + "=" + input) - && !params.remove(name + "[]=" + input); - } - })); + return values.stream() + .filter(s -> !params.remove(name + "=" + s) && !params.remove(name + "[]=" + s)) + .collect(Collectors.toList()); } /** diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/PersistenceUnitBinder.java b/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/PersistenceUnitBinder.java index e56032f8c9..e44790f8e3 100644 --- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/PersistenceUnitBinder.java +++ b/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/PersistenceUnitBinder.java @@ -42,6 +42,7 @@ import java.lang.reflect.Proxy; import java.util.Enumeration; +import java.util.HashMap; import java.util.Map; import javax.ws.rs.core.GenericType; @@ -56,8 +57,6 @@ import org.glassfish.jersey.internal.inject.InjectionResolver; import org.glassfish.jersey.server.ContainerException; -import jersey.repackaged.com.google.common.collect.Maps; - /** * {@link PersistenceUnit Persistence unit} injection binder. * @@ -84,7 +83,7 @@ public PersistenceUnitBinder(ServletConfig servletConfig) { @Singleton private static class PersistenceUnitInjectionResolver implements InjectionResolver { - private final Map persistenceUnits = Maps.newHashMap(); + private final Map persistenceUnits = new HashMap<>(); private PersistenceUnitInjectionResolver(ServletConfig servletConfig) { for (final Enumeration parameterNames = servletConfig.getInitParameterNames(); parameterNames.hasMoreElements(); ) { diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ResponseWriter.java b/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ResponseWriter.java index 3476d658d9..fed6d3bb13 100644 --- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ResponseWriter.java +++ b/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ResponseWriter.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -37,6 +37,7 @@ * only if the new code is made subject to such option by the copyright * holder. */ + package org.glassfish.jersey.servlet.internal; import java.io.IOException; @@ -44,6 +45,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -61,8 +63,6 @@ import org.glassfish.jersey.server.spi.ContainerResponseWriter; import org.glassfish.jersey.servlet.spi.AsyncContextDelegate; -import jersey.repackaged.com.google.common.util.concurrent.SettableFuture; - /** * An internal implementation of {@link ContainerResponseWriter} for Servlet containers. * The writer depends on provided {@link AsyncContextDelegate} to support async functionality. @@ -88,7 +88,7 @@ public class ResponseWriter implements ContainerResponseWriter { * If {@code true} method {@link HttpServletResponse#setStatus} is used over {@link HttpServletResponse#sendError}. */ private final boolean configSetStatusOverSendError; - private final SettableFuture responseContext; + private final CompletableFuture responseContext; private final AsyncContextDelegate asyncExt; private final JerseyRequestTimeoutHandler requestTimeoutHandler; @@ -113,7 +113,7 @@ public ResponseWriter(final boolean useSetStatusOn404, this.configSetStatusOverSendError = configSetStatusOverSendError; this.response = response; this.asyncExt = asyncExt; - this.responseContext = SettableFuture.create(); + this.responseContext = new CompletableFuture<>(); this.requestTimeoutHandler = new JerseyRequestTimeoutHandler(this, timeoutTaskExecutor); } @@ -139,7 +139,7 @@ public void setSuspendTimeout(final long timeOut, final TimeUnit timeUnit) throw @Override public OutputStream writeResponseStatusAndHeaders(final long contentLength, final ContainerResponse responseContext) throws ContainerException { - this.responseContext.set(responseContext); + this.responseContext.complete(responseContext); // first set the content length, so that if headers have an explicit value, it takes precedence over this one if (responseContext.hasEntity() && contentLength != -1 && contentLength < Integer.MAX_VALUE) { @@ -216,7 +216,7 @@ private void callSendError() { final boolean hasEntity = responseContext.hasEntity(); final Response.StatusType status = responseContext.getStatusInfo(); if (!hasEntity && status != null && status.getStatusCode() >= 400 - && !(useSetStatusOn404 && status == Response.Status.NOT_FOUND)) { + && !(useSetStatusOn404 && status == Response.Status.NOT_FOUND)) { final String reason = status.getReasonPhrase(); try { if (reason == null || reason.isEmpty()) { diff --git a/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/init/JerseyServletContainerInitializer.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/init/JerseyServletContainerInitializer.java index 5ba6b7f9ba..3ef0b982d5 100644 --- a/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/init/JerseyServletContainerInitializer.java +++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/init/JerseyServletContainerInitializer.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -40,7 +40,9 @@ package org.glassfish.jersey.servlet.init; +import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; @@ -69,9 +71,6 @@ import org.glassfish.jersey.servlet.internal.Utils; import org.glassfish.jersey.servlet.internal.spi.ServletContainerProvider; -import jersey.repackaged.com.google.common.collect.Lists; -import jersey.repackaged.com.google.common.collect.Maps; - /* It is RECOMMENDED that implementations support the Servlet 3 framework pluggability mechanism to enable portability between containers and to avail @@ -214,7 +213,7 @@ private static boolean isJerseyServlet(final String className) { private static List getInitParamDeclaredRegistrations(final ServletContext context, final Class clazz) { - final List registrations = Lists.newArrayList(); + final List registrations = new ArrayList<>(); collectJaxRsRegistrations(context.getServletRegistrations(), registrations, clazz); collectJaxRsRegistrations(context.getFilterRegistrations(), registrations, clazz); return registrations; @@ -339,7 +338,7 @@ private static void addServletWithExistingRegistration(final ServletContext cont } private static Map getInitParams(final ServletRegistration sr) { - final Map initParams = Maps.newHashMap(); + final Map initParams = new HashMap<>(); for (final Map.Entry entry : sr.getInitParameters().entrySet()) { initParams.put(entry.getKey(), entry.getValue()); } diff --git a/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java index 0b47fb7001..c3db59de2a 100644 --- a/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java +++ b/containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainerFactory.java @@ -44,6 +44,7 @@ import javax.ws.rs.ProcessingException; +import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder; import org.glassfish.jersey.jetty.internal.LocalizationMessages; import org.glassfish.jersey.process.JerseyProcessingUncaughtExceptionHandler; import org.glassfish.jersey.server.ContainerFactory; @@ -60,8 +61,6 @@ import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; -import jersey.repackaged.com.google.common.util.concurrent.ThreadFactoryBuilder; - /** * Factory for creating and starting Jetty server handlers. This returns * a handle to the started server as {@link Server} instances, which allows diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientBackgroundScheduler.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientBackgroundScheduler.java new file mode 100644 index 0000000000..add2b81d44 --- /dev/null +++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientBackgroundScheduler.java @@ -0,0 +1,71 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package org.glassfish.jersey.client; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.inject.Qualifier; + +/** + * Injection qualifier that can be used to inject an {@link java.util.concurrent.ScheduledExecutorService} + * instance used by Jersey client runtime to schedule background tasks. + *

+ * The scheduled executor service instance injected using this injection qualifier can be customized + * by registering a custom {@link org.glassfish.jersey.spi.ScheduledExecutorServiceProvider} implementation that is itself + * annotated with the {@code @ClientAsyncExecutor} annotation. + *

+ * + * @author Adam Lindenthal (adam.lindenthal at oracle.com) + * @see ClientBackgroundSchedulerLiteral + * @since 2.26 + */ +@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@Qualifier +public @interface ClientBackgroundScheduler { + +} diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientBackgroundSchedulerLiteral.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientBackgroundSchedulerLiteral.java new file mode 100644 index 0000000000..cce08ea7ab --- /dev/null +++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientBackgroundSchedulerLiteral.java @@ -0,0 +1,66 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package org.glassfish.jersey.client; + +import org.glassfish.jersey.internal.inject.AnnotationLiteral; + +/** + * {@link ClientBackgroundScheduler} annotation literal. + *

+ * This class provides a {@link #INSTANCE constant instance} of the {@code @ClientBackgroundScheduler} annotation to be used + * in method calls that require use of annotation instances. + *

+ * + * @author Adam Lindenthal (adam.lindenthal at oracle.com) + * @since 2.26 + */ +@SuppressWarnings("ClassExplicitlyAnnotation") +public final class ClientBackgroundSchedulerLiteral extends AnnotationLiteral + implements ClientBackgroundScheduler { + + /** + * An {@link ClientBackgroundScheduler} annotation instance. + */ + public static final ClientBackgroundScheduler INSTANCE = new ClientBackgroundSchedulerLiteral(); + + private ClientBackgroundSchedulerLiteral() { + // prevents instantiation from the outside. + } +} diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientBinder.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientBinder.java index 0440315597..bf44555167 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/ClientBinder.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientBinder.java @@ -52,7 +52,6 @@ import javax.inject.Provider; import javax.inject.Singleton; -import org.glassfish.jersey.internal.ContextResolverFactory; import org.glassfish.jersey.internal.JaxrsProviders; import org.glassfish.jersey.internal.PropertiesDelegate; import org.glassfish.jersey.internal.ServiceFinderBinder; @@ -60,11 +59,10 @@ import org.glassfish.jersey.internal.inject.ReferencingFactory; import org.glassfish.jersey.internal.spi.AutoDiscoverable; import org.glassfish.jersey.internal.util.collection.Ref; -import org.glassfish.jersey.message.internal.MessageBodyFactory; import org.glassfish.jersey.message.internal.MessagingBinders; -import org.glassfish.jersey.process.internal.RequestScope; import org.glassfish.jersey.process.internal.RequestScoped; import org.glassfish.jersey.spi.ExecutorServiceProvider; +import org.glassfish.jersey.spi.ScheduledExecutorServiceProvider; /** * Registers all binders necessary for {@link Client} runtime. @@ -111,22 +109,19 @@ public PropertiesDelegate get() { @Override protected void configure() { - install(new RequestScope.Binder(), // must go first as it registers the request scope instance. - new MessagingBinders.MessageBodyProviders(clientRuntimeProperties, RuntimeType.CLIENT), + install(new MessagingBinders.MessageBodyProviders(clientRuntimeProperties, RuntimeType.CLIENT), new MessagingBinders.HeaderDelegateProviders(), - new MessageBodyFactory.Binder(), - new ContextResolverFactory.Binder(), new JaxrsProviders.Binder(), new ServiceFinderBinder<>(AutoDiscoverable.class, clientRuntimeProperties, RuntimeType.CLIENT)); - bindFactory(ReferencingFactory.referenceFactory()).to(new GenericType>() { + bindFactory(ReferencingFactory.referenceFactory()).to(new GenericType>() { }).in(RequestScoped.class); bindFactory(RequestContextInjectionFactory.class) .to(ClientRequest.class) .in(RequestScoped.class); - bindFactory(ReferencingFactory.referenceFactory()).to(new GenericType>() { + bindFactory(ReferencingFactory.referenceFactory()).to(new GenericType>() { }).in(RequestScoped.class); bindFactory(PropertiesDelegateFactory.class, Singleton.class).to(PropertiesDelegate.class).in(RequestScoped.class); @@ -141,5 +136,7 @@ protected void configure() { bind(asyncThreadPoolSize).named("ClientAsyncThreadPoolSize"); // DefaultClientAsyncExecutorProvider must be singleton scoped, so that @PreDestroy, which closes the executor, is called bind(DefaultClientAsyncExecutorProvider.class).to(ExecutorServiceProvider.class).in(Singleton.class); + + bind(DefaultClientBackgroundSchedulerProvider.class).to(ScheduledExecutorServiceProvider.class).in(Singleton.class); } } diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientConfig.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientConfig.java index b52bdd8ab4..0aafccae61 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/ClientConfig.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientConfig.java @@ -40,8 +40,10 @@ package org.glassfish.jersey.client; +import java.util.Arrays; import java.util.Collection; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Set; @@ -55,17 +57,23 @@ import org.glassfish.jersey.client.internal.LocalizationMessages; import org.glassfish.jersey.client.spi.Connector; import org.glassfish.jersey.client.spi.ConnectorProvider; +import org.glassfish.jersey.internal.BootstrapBag; +import org.glassfish.jersey.internal.BootstrapConfigurator; +import org.glassfish.jersey.internal.ContextResolverFactory; +import org.glassfish.jersey.internal.ExceptionMapperFactory; import org.glassfish.jersey.internal.ServiceFinder; -import org.glassfish.jersey.internal.inject.AbstractBinder; +import org.glassfish.jersey.internal.inject.Bindings; import org.glassfish.jersey.internal.inject.InjectionManager; import org.glassfish.jersey.internal.inject.Injections; import org.glassfish.jersey.internal.inject.ProviderBinder; import org.glassfish.jersey.internal.util.collection.LazyValue; import org.glassfish.jersey.internal.util.collection.Value; import org.glassfish.jersey.internal.util.collection.Values; +import org.glassfish.jersey.message.internal.MessageBodyFactory; import org.glassfish.jersey.model.internal.CommonConfig; import org.glassfish.jersey.model.internal.ComponentBag; import org.glassfish.jersey.process.internal.ExecutorProviders; +import org.glassfish.jersey.process.internal.RequestScope; /** * Jersey externalized implementation of client-side JAX-RS {@link javax.ws.rs.core.Configurable @@ -81,6 +89,21 @@ public class ClientConfig implements Configurable, ExtendedConfig */ private State state; + private static class RuntimeConfigConfigurator implements BootstrapConfigurator { + + private final State runtimeConfig; + + private RuntimeConfigConfigurator(State runtimeConfig) { + this.runtimeConfig = runtimeConfig; + } + + @Override + public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) { + bootstrapBag.setConfiguration(runtimeConfig); + injectionManager.register(Bindings.service(runtimeConfig).to(Configuration.class)); + } + } + /** * Default encapsulation of the internal configuration state. */ @@ -89,36 +112,23 @@ private static class State implements Configurable, ExtendedConfig { /** * Strategy that returns the same state instance. */ - private static final StateChangeStrategy IDENTITY = new StateChangeStrategy() { - - @Override - public State onChange(final State state) { - return state; - } - }; + private static final StateChangeStrategy IDENTITY = state -> state; /** * Strategy that returns a copy of the state instance. */ - private static final StateChangeStrategy COPY_ON_CHANGE = new StateChangeStrategy() { - - @Override - public State onChange(final State state) { - return state.copy(); - } - }; + private static final StateChangeStrategy COPY_ON_CHANGE = State::copy; private volatile StateChangeStrategy strategy; private final CommonConfig commonConfig; private final JerseyClient client; private volatile ConnectorProvider connectorProvider; - private final LazyValue runtime = Values.lazy((Value) this::initRuntime); /** * Configuration state change strategy. */ - private static interface StateChangeStrategy { + private interface StateChangeStrategy { /** * Invoked whenever a mutator method is called in the given configuration @@ -128,7 +138,7 @@ private static interface StateChangeStrategy { * @return state instance that will be mutated and returned from the * invoked configuration state mutator method. */ - public State onChange(final State state); + State onChange(final State state); } /** @@ -373,7 +383,7 @@ public ComponentBag getComponentBag() { */ @SuppressWarnings("MethodOnlyUsedFromInnerClass") private ClientRuntime initRuntime() { - /** + /* * Ensure that any attempt to add a new provider, feature, binder or modify the connector * will cause a copy of the current state. */ @@ -382,8 +392,17 @@ private ClientRuntime initRuntime() { final State runtimeCfgState = this.copy(); runtimeCfgState.markAsShared(); - final InjectionManager injectionManager = - Injections.createInjectionManager(new ClientBinder(runtimeCfgState.getProperties())); + InjectionManager injectionManager = Injections.createInjectionManager(); + injectionManager.register(new ClientBinder(runtimeCfgState.getProperties())); + + BootstrapBag bootstrapBag = new BootstrapBag(); + List bootstrapConfigurators = Arrays.asList( + new RequestScope.RequestScopeConfigurator(), + new RuntimeConfigConfigurator(runtimeCfgState), + new ContextResolverFactory.ContextResolversConfigurator(), + new MessageBodyFactory.MessageBodyWorkersConfigurator(), + new ExceptionMapperFactory.ExceptionMappersConfigurator()); + bootstrapConfigurators.forEach(configurator -> configurator.init(injectionManager, bootstrapBag)); // AutoDiscoverable. if (!CommonProperties.getValue(runtimeCfgState.getProperties(), RuntimeType.CLIENT, @@ -396,27 +415,21 @@ private ClientRuntime initRuntime() { // Configure binders and features. runtimeCfgState.configureMetaProviders(injectionManager); - // Bind configuration. - final AbstractBinder configBinder = new AbstractBinder() { - @Override - protected void configure() { - bind(runtimeCfgState).to(Configuration.class); - } - }; - injectionManager.register(configBinder); - // Bind providers. ProviderBinder.bindProviders(runtimeCfgState.getComponentBag(), RuntimeType.CLIENT, null, injectionManager); + injectionManager.completeRegistration(); + + bootstrapConfigurators.forEach(configurator -> configurator.postInit(injectionManager, bootstrapBag)); + // Bind executors. ExecutorProviders.createInjectionBindings(injectionManager); final ClientConfig configuration = new ClientConfig(runtimeCfgState); final Connector connector = connectorProvider.getConnector(client, configuration); - final ClientRuntime crt = new ClientRuntime(configuration, connector, injectionManager); + final ClientRuntime crt = new ClientRuntime(configuration, connector, injectionManager, bootstrapBag); client.registerShutdownHook(crt); - return crt; } @@ -723,6 +736,10 @@ ClientRuntime getRuntime() { return state.runtime.get(); } + public ClientExecutor getClientExecutor() { + return state.runtime.get(); + } + /** * Get the parent Jersey client this configuration is bound to. * diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientExecutor.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientExecutor.java new file mode 100644 index 0000000000..93b6ec7549 --- /dev/null +++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientExecutor.java @@ -0,0 +1,115 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package org.glassfish.jersey.client; + +import java.util.concurrent.Callable; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +/** + * Executor for client async processing and background task scheduling. + * + * @author Adam Lindenthal (adam.lindenthal at oracle.com) + * @since 2.26 + */ +public interface ClientExecutor { + /** + * Submits a value-returning task for execution and returns a {@link Future} representing the pending results of the task. + * The Future's {@code get()} method will return the task's result upon successful completion. + * + * @param task task to submit + * @param task's return type + * @return a {@code Future} representing pending completion of the task + * @throws {@link java.util.concurrent.RejectedExecutionException} if the task cannot be scheduled for execution + * @throws {@link NullPointerException} if the task is null + */ + Future submit(Callable task); + + /** + * Submits a {@link Runnable} task for execution and returns a {@link Future} representing that task. The Future's {@code + * get()} method will return the given result upon successful completion. + * + * @param task the task to submit + * @return a {@code Future} representing pending completion of the task + * @throws {@link java.util.concurrent.RejectedExecutionException} if the task cannot be scheduled for execution + * @throws {@link NullPointerException} if the task is null + */ + Future submit(Runnable task); + + /** + * Submits a {@link Runnable} task for execution and returns a {@link Future} representing that task. The Future's {@code + * get()} method will return the given result upon successful completion. + * + * @param task the task to submit + * @param result the result to return + * @param result type + * @return a {@code Future} representing pending completion of the task + * @throws {@link java.util.concurrent.RejectedExecutionException} if the task cannot be scheduled for execution + * @throws {@link NullPointerException} if the task is null + */ + Future submit(Runnable task, T result); + + /** + * Creates and executes a {@link ScheduledFuture} that becomes enabled after the given delay. + * + * @param callable the function to execute + * @param delay the time from now to delay execution + * @param unit the time unit of the delay parameter + * @param return type of the function + * @return a {@code ScheduledFuture} that can be used to extract result or cancel + * @throws {@link java.util.concurrent.RejectedExecutionException} if the task cannot be scheduled for execution + * @throws {@link NullPointerException} if callable is null + */ + ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit); + + /** + * Creates and executes a one-shot action that becomes enabled after the given delay. + * + * @param command the task to execute + * @param delay the time from now to delay execution + * @param unit the time unit of the daly parameter + * @return a scheduledFuture representing pending completion of the task and whose {@code get()} method will return {@code + * null} upon completion + */ + ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit); + + +} diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java index 14885d18a5..c9c311b21e 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -148,6 +148,32 @@ public final class ClientProperties { */ public static final String ASYNC_THREADPOOL_SIZE = "jersey.config.client.async.threadPoolSize"; + /** + * Scheduler thread pool size. + *

+ * The value MUST be an instance of {@link java.lang.Integer}. + *

+ *

+ * If the property is absent then thread pool used for background task scheduling will + * be initialized as default scheduled thread pool executor, which creates new thread + * for every new request, see {@link java.util.concurrent.Executors}. When a + * value > 0 is provided, the created scheduled thread pool executor limited to that + * number of threads will be utilized. Zero or negative values will be ignored. + *

+ *

+ * Note that the property may be ignored if a custom {@link org.glassfish.jersey.spi.ExecutorServiceProvider} + * is configured to execute background tasks scheduling in the client runtime (see + * {@link org.glassfish.jersey.client.ClientBackgroundScheduler}). + *

+ *

+ * A default value is not set. + *

+ *

+ * The name of the configuration property is {@value}. + *

+ */ + public static final String BACKGROUND_SCHEDULER_THREADPOOL_SIZE = "jersey.config.client.backgroundScheduler.threadPoolSize"; + /** * If {@link org.glassfish.jersey.client.filter.EncodingFilter} is * registered, this property indicates the value of Content-Encoding diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientRequest.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientRequest.java index c8b1883909..d38e5ed6da 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/ClientRequest.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientRequest.java @@ -68,6 +68,7 @@ import org.glassfish.jersey.client.internal.LocalizationMessages; import org.glassfish.jersey.internal.MapPropertiesDelegate; import org.glassfish.jersey.internal.PropertiesDelegate; +import org.glassfish.jersey.internal.guava.Preconditions; import org.glassfish.jersey.internal.inject.InjectionManager; import org.glassfish.jersey.internal.inject.InjectionManagerSupplier; import org.glassfish.jersey.internal.util.ExceptionUtils; @@ -75,8 +76,6 @@ import org.glassfish.jersey.message.MessageBodyWorkers; import org.glassfish.jersey.message.internal.OutboundMessageContext; -import jersey.repackaged.com.google.common.base.Preconditions; - /** * Jersey client request context. * diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientResponse.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientResponse.java index d21eb8389a..7e57fc5a5f 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/ClientResponse.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientResponse.java @@ -50,6 +50,7 @@ import java.util.Collections; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import javax.ws.rs.ProcessingException; import javax.ws.rs.client.ClientResponseContext; @@ -67,11 +68,6 @@ import org.glassfish.jersey.message.internal.OutboundJaxrsResponse; import org.glassfish.jersey.message.internal.Statuses; -import jersey.repackaged.com.google.common.base.Function; -import jersey.repackaged.com.google.common.base.MoreObjects; -import jersey.repackaged.com.google.common.collect.Collections2; -import jersey.repackaged.com.google.common.collect.Sets; - /** * Jersey client response context. * @@ -241,27 +237,26 @@ public Map getCookies() { @Override public Set getLinks() { - return Sets.newHashSet(Collections2.transform(super.getLinks(), new Function() { - @Override - public Link apply(Link link) { - if (link.getUri().isAbsolute()) { - return link; - } + return super.getLinks() + .stream() + .map(link -> { + if (link.getUri().isAbsolute()) { + return link; + } - return Link.fromLink(link).baseUri(getResolvedRequestUri()).build(); - } - })); + return Link.fromLink(link).baseUri(getResolvedRequestUri()).build(); + }) + .collect(Collectors.toSet()); } @Override public String toString() { - return MoreObjects - .toStringHelper(this) - .add("method", requestContext.getMethod()) - .add("uri", requestContext.getUri()) - .add("status", status.getStatusCode()) - .add("reason", status.getReasonPhrase()) - .toString(); + return "ClientResponse{" + + "method=" + requestContext.getMethod() + + ", uri=" + requestContext.getUri() + + ", status=" + status.getStatusCode() + + ", reason=" + status.getReasonPhrase() + + "}"; } /** diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientRuntime.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientRuntime.java index 7567381a2a..548bb3887c 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/ClientRuntime.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientRuntime.java @@ -40,24 +40,33 @@ package org.glassfish.jersey.client; -import java.util.Arrays; +import java.util.Collections; +import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; import javax.ws.rs.ProcessingException; +import javax.ws.rs.core.GenericType; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MultivaluedMap; +import javax.inject.Provider; + import org.glassfish.jersey.client.internal.LocalizationMessages; import org.glassfish.jersey.client.spi.AsyncConnectorCallback; import org.glassfish.jersey.client.spi.Connector; +import org.glassfish.jersey.internal.BootstrapBag; import org.glassfish.jersey.internal.Version; import org.glassfish.jersey.internal.inject.InjectionManager; import org.glassfish.jersey.internal.inject.Providers; import org.glassfish.jersey.internal.util.collection.LazyValue; +import org.glassfish.jersey.internal.util.collection.Ref; import org.glassfish.jersey.internal.util.collection.Value; import org.glassfish.jersey.internal.util.collection.Values; import org.glassfish.jersey.message.MessageBodyWorkers; @@ -71,7 +80,7 @@ * * @author Marek Potociar (marek.potociar at oracle.com) */ -class ClientRuntime implements JerseyClient.ShutdownHook { +class ClientRuntime implements JerseyClient.ShutdownHook, ClientExecutor { private static final Logger LOG = Logger.getLogger(ClientRuntime.class.getName()); @@ -83,6 +92,7 @@ class ClientRuntime implements JerseyClient.ShutdownHook { private final RequestScope requestScope; private final LazyValue asyncRequestExecutor; + private final LazyValue backgroundScheduler; private final Iterable lifecycleListeners; @@ -93,13 +103,19 @@ class ClientRuntime implements JerseyClient.ShutdownHook { /** * Create new client request processing runtime. * - * @param config client runtime configuration. - * @param connector client transport connector. - * @param injectionManager injection manager. + * @param config client runtime configuration. + * @param connector client transport connector. + * @param injectionManager injection manager. */ - public ClientRuntime(final ClientConfig config, final Connector connector, final InjectionManager injectionManager) { - Stage.Builder requestingChainBuilder = Stages - .chain(injectionManager.createAndInitialize(RequestProcessingInitializationStage.class)); + public ClientRuntime(final ClientConfig config, final Connector connector, final InjectionManager injectionManager, + final BootstrapBag bootstrapBag) { + Provider> clientRequest = + injectionManager.getInstance(new GenericType>>() {}.getType()); + + RequestProcessingInitializationStage requestProcessingInitializationStage = + new RequestProcessingInitializationStage(clientRequest, bootstrapBag.getMessageBodyWorkers(), injectionManager); + + Stage.Builder requestingChainBuilder = Stages.chain(requestProcessingInitializationStage); ChainableStage requestFilteringStage = ClientFilteringStages.createRequestFilteringStage(injectionManager); this.requestProcessingRoot = requestFilteringStage != null @@ -111,15 +127,11 @@ public ClientRuntime(final ClientConfig config, final Connector connector, final this.config = config; this.connector = connector; - - this.requestScope = injectionManager.getInstance(RequestScope.class); - - this.asyncRequestExecutor = Values.lazy(new Value() { - @Override - public ExecutorService get() { - return injectionManager.getInstance(ExecutorService.class, ClientAsyncExecutorLiteral.INSTANCE); - } - }); + this.requestScope = bootstrapBag.getRequestScope(); + this.asyncRequestExecutor = Values.lazy((Value) + () -> injectionManager.getInstance(ExecutorService.class, ClientAsyncExecutorLiteral.INSTANCE)); + this.backgroundScheduler = Values.lazy((Value) () + -> injectionManager.getInstance(ScheduledExecutorService.class, ClientBackgroundSchedulerLiteral.INSTANCE)); this.injectionManager = injectionManager; this.lifecycleListeners = Providers.getAllProviders(injectionManager, ClientLifecycleListener.class); @@ -134,58 +146,70 @@ public ExecutorService get() { } /** - * Submit a {@link ClientRequest client request} for asynchronous processing. + * Prepare a {@code Runnable} to be used to submit a {@link ClientRequest client request} for asynchronous processing. *

- * Both, the request processing as well as response callback invocation will be executed - * in a context of an active {@link RequestScope.Instance request scope instance}. - *

* * @param request client request to be sent. * @param callback asynchronous response callback. + * @return {@code Runnable} to be submitted for async processing using {@link #submit(Runnable)}. */ - public void submit(final ClientRequest request, final ResponseCallback callback) { - submit(asyncRequestExecutor.get(), new Runnable() { - - @Override - public void run() { + Runnable createRunnableForAsyncProcessing(ClientRequest request, final ResponseCallback callback) { + return () -> requestScope.runInScope(() -> { + try { + ClientRequest processedRequest; try { - ClientRequest processedRequest; - try { - processedRequest = Stages.process(request, requestProcessingRoot); - processedRequest = addUserAgent(processedRequest, connector.getName()); - } catch (final AbortException aborted) { - processResponse(aborted.getAbortResponse(), callback); - return; + processedRequest = Stages.process(request, requestProcessingRoot); + processedRequest = addUserAgent(processedRequest, connector.getName()); + } catch (final AbortException aborted) { + processResponse(aborted.getAbortResponse(), callback); + return; + } + + final AsyncConnectorCallback connectorCallback = new AsyncConnectorCallback() { + + @Override + public void response(final ClientResponse response) { + requestScope.runInScope(() -> processResponse(response, callback)); } - final AsyncConnectorCallback connectorCallback = new AsyncConnectorCallback() { - - @Override - public void response(final ClientResponse response) { - requestScope.runInScope(new Runnable() { - public void run() { - processResponse(response, callback); - } - }); - } - - @Override - public void failure(final Throwable failure) { - requestScope.runInScope(new Runnable() { - public void run() { - processFailure(failure, callback); - } - }); - } - }; - connector.apply(processedRequest, connectorCallback); - } catch (final Throwable throwable) { - processFailure(throwable, callback); - } + @Override + public void failure(final Throwable failure) { + requestScope.runInScope(() -> processFailure(failure, callback)); + } + }; + + connector.apply(processedRequest, connectorCallback); + } catch (final Throwable throwable) { + processFailure(throwable, callback); } }); } + @Override + public Future submit(Callable task) { + return asyncRequestExecutor.get().submit(task); + } + + @Override + public Future submit(Runnable task) { + return asyncRequestExecutor.get().submit(task); + } + + @Override + public Future submit(Runnable task, T result) { + return asyncRequestExecutor.get().submit(task, result); + } + + @Override + public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) { + return backgroundScheduler.get().schedule(callable, delay, unit); + } + + @Override + public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { + return backgroundScheduler.get().schedule(command, delay, unit); + } + private void processResponse(final ClientResponse response, final ResponseCallback callback) { final ClientResponse processedResponse; try { @@ -203,12 +227,7 @@ private void processFailure(final Throwable failure, final ResponseCallback call } private Future submit(final ExecutorService executor, final Runnable task) { - return executor.submit(new Runnable() { - @Override - public void run() { - requestScope.runInScope(task); - } - }); + return executor.submit(() -> requestScope.runInScope(task)); } private ClientRequest addUserAgent(final ClientRequest clientRequest, final String connectorName) { @@ -222,10 +241,10 @@ private ClientRequest addUserAgent(final ClientRequest clientRequest, final Stri } else if (!clientRequest.ignoreUserAgent()) { if (connectorName != null && !connectorName.isEmpty()) { headers.put(HttpHeaders.USER_AGENT, - Arrays.asList(String.format("Jersey/%s (%s)", Version.getVersion(), connectorName))); + Collections.singletonList(String.format("Jersey/%s (%s)", Version.getVersion(), connectorName))); } else { headers.put(HttpHeaders.USER_AGENT, - Arrays.asList(String.format("Jersey/%s", Version.getVersion()))); + Collections.singletonList(String.format("Jersey/%s", Version.getVersion()))); } } diff --git a/core-client/src/main/java/org/glassfish/jersey/client/DefaultClientBackgroundSchedulerProvider.java b/core-client/src/main/java/org/glassfish/jersey/client/DefaultClientBackgroundSchedulerProvider.java new file mode 100644 index 0000000000..0b0fbb2a8e --- /dev/null +++ b/core-client/src/main/java/org/glassfish/jersey/client/DefaultClientBackgroundSchedulerProvider.java @@ -0,0 +1,69 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package org.glassfish.jersey.client; + +import javax.inject.Inject; + +import org.glassfish.jersey.spi.ScheduledThreadPoolExecutorProvider; + +/** + * Default {@link org.glassfish.jersey.spi.ScheduledExecutorServiceProvider} used on the client side for providing the scheduled + * executor service that runs background tasks. + * + * @author Adam Lindenthal (adam.lindenthal at oracle.com) + * @since 2.26 + */ +@ClientBackgroundScheduler +class DefaultClientBackgroundSchedulerProvider extends ScheduledThreadPoolExecutorProvider { + + /** + * Creates a new instance. + */ + @Inject + public DefaultClientBackgroundSchedulerProvider() { + super("jersey-client-background-scheduler"); + } + + @Override + protected int getCorePoolSize() { + return 1; + } +} diff --git a/core-client/src/main/java/org/glassfish/jersey/client/InboundJaxrsResponse.java b/core-client/src/main/java/org/glassfish/jersey/client/InboundJaxrsResponse.java index d5d003d0ca..11dcca1155 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/InboundJaxrsResponse.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/InboundJaxrsResponse.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -58,8 +58,6 @@ import org.glassfish.jersey.internal.util.Producer; import org.glassfish.jersey.process.internal.RequestScope; -import jersey.repackaged.com.google.common.base.MoreObjects; - /** * Implementation of an inbound client-side JAX-RS {@link Response} message. *

@@ -256,10 +254,7 @@ public MultivaluedMap getMetadata() { @Override public String toString() { - return MoreObjects - .toStringHelper(this) - .add("context", context) - .toString(); + return "InboundJaxrsResponse{" + "context=" + context + "}"; } private T runInScopeIfPossible(Producer producer) { diff --git a/core-client/src/main/java/org/glassfish/jersey/client/JerseyClient.java b/core-client/src/main/java/org/glassfish/jersey/client/JerseyClient.java index 2404c075db..260444d385 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/JerseyClient.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/JerseyClient.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2011-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -64,8 +64,8 @@ import org.glassfish.jersey.internal.util.collection.UnsafeValue; import org.glassfish.jersey.internal.util.collection.Values; -import static jersey.repackaged.com.google.common.base.Preconditions.checkNotNull; -import static jersey.repackaged.com.google.common.base.Preconditions.checkState; +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; +import static org.glassfish.jersey.internal.guava.Preconditions.checkState; /** * Jersey implementation of {@link javax.ws.rs.client.Client JAX-RS Client} diff --git a/core-client/src/main/java/org/glassfish/jersey/client/JerseyInvocation.java b/core-client/src/main/java/org/glassfish/jersey/client/JerseyInvocation.java index cdc035bb09..cc06c28ba6 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/JerseyInvocation.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/JerseyInvocation.java @@ -45,6 +45,7 @@ import java.util.Locale; import java.util.Map; import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.logging.Logger; @@ -87,8 +88,6 @@ import org.glassfish.jersey.internal.util.ReflectionHelper; import org.glassfish.jersey.process.internal.RequestScope; -import jersey.repackaged.com.google.common.util.concurrent.SettableFuture; - /** * Jersey implementation of {@link javax.ws.rs.client.Invocation JAX-RS client-side * request invocation} contract. @@ -499,7 +498,7 @@ public T rx(Class clazz, ExecutorService executorServic * Create {@link RxInvoker} from provided {@code RxInvoker} subclass. *

* The method does a lookup for {@link RxInvokerProvider}, which provides given {@code RxInvoker} subclass - * and if found, calls {@link RxInvokerProvider#getRxInvoker(Invocation.Builder, ExecutorService)}. + * and if found, calls {@link RxInvokerProvider#getRxInvoker(javax.ws.rs.client.SyncInvoker, ExecutorService)}. * * @param clazz {@code RxInvoker} subclass to be created. * @param executorService to be passed to the factory method invocation. @@ -809,13 +808,14 @@ public T call() throws ProcessingException { @Override public Future submit() { - final SettableFuture responseFuture = SettableFuture.create(); - request().getClientRuntime().submit(requestForCall(requestContext), new ResponseCallback() { + final CompletableFuture responseFuture = new CompletableFuture<>(); + final ClientRuntime runtime = request().getClientRuntime(); + runtime.submit(runtime.createRunnableForAsyncProcessing(requestForCall(requestContext), new ResponseCallback() { @Override public void completed(final ClientResponse response, final RequestScope scope) { if (!responseFuture.isCancelled()) { - responseFuture.set(new InboundJaxrsResponse(response, scope)); + responseFuture.complete(new InboundJaxrsResponse(response, scope)); } else { response.close(); } @@ -824,10 +824,10 @@ public void completed(final ClientResponse response, final RequestScope scope) { @Override public void failed(final ProcessingException error) { if (!responseFuture.isCancelled()) { - responseFuture.setException(error); + responseFuture.completeExceptionally(error); } } - }); + })); return responseFuture; } @@ -837,9 +837,10 @@ public Future submit(final Class responseType) { if (responseType == null) { throw new IllegalArgumentException(LocalizationMessages.RESPONSE_TYPE_IS_NULL()); } - final SettableFuture responseFuture = SettableFuture.create(); + final CompletableFuture responseFuture = new CompletableFuture<>(); //noinspection Duplicates - request().getClientRuntime().submit(requestForCall(requestContext), new ResponseCallback() { + final ClientRuntime runtime = request().getClientRuntime(); + runtime.submit(runtime.createRunnableForAsyncProcessing(requestForCall(requestContext), new ResponseCallback() { @Override public void completed(final ClientResponse response, final RequestScope scope) { @@ -848,7 +849,7 @@ public void completed(final ClientResponse response, final RequestScope scope) { return; } try { - responseFuture.set(translate(response, scope, responseType)); + responseFuture.complete(translate(response, scope, responseType)); } catch (final ProcessingException ex) { failed(ex); } @@ -860,12 +861,12 @@ public void failed(final ProcessingException error) { return; } if (error.getCause() instanceof WebApplicationException) { - responseFuture.setException(error.getCause()); + responseFuture.completeExceptionally(error.getCause()); } else { - responseFuture.setException(error); + responseFuture.completeExceptionally(error); } } - }); + })); return responseFuture; } @@ -900,9 +901,10 @@ public Future submit(final GenericType responseType) { if (responseType == null) { throw new IllegalArgumentException(LocalizationMessages.RESPONSE_TYPE_IS_NULL()); } - final SettableFuture responseFuture = SettableFuture.create(); + final CompletableFuture responseFuture = new CompletableFuture<>(); //noinspection Duplicates - request().getClientRuntime().submit(requestForCall(requestContext), new ResponseCallback() { + final ClientRuntime runtime = request().getClientRuntime(); + runtime.submit(runtime.createRunnableForAsyncProcessing(requestForCall(requestContext), new ResponseCallback() { @Override public void completed(final ClientResponse response, final RequestScope scope) { @@ -912,7 +914,7 @@ public void completed(final ClientResponse response, final RequestScope scope) { } try { - responseFuture.set(translate(response, scope, responseType)); + responseFuture.complete(translate(response, scope, responseType)); } catch (final ProcessingException ex) { failed(ex); } @@ -924,12 +926,12 @@ public void failed(final ProcessingException error) { return; } if (error.getCause() instanceof WebApplicationException) { - responseFuture.setException(error.getCause()); + responseFuture.completeExceptionally(error.getCause()); } else { - responseFuture.setException(error); + responseFuture.completeExceptionally(error); } } - }); + })); return responseFuture; } @@ -980,7 +982,7 @@ public Future submit(final InvocationCallback callback) { * request invocation. */ public Future submit(final GenericType responseType, final InvocationCallback callback) { - final SettableFuture responseFuture = SettableFuture.create(); + final CompletableFuture responseFuture = new CompletableFuture<>(); try { final ReflectionHelper.DeclaringClassInterfacePair pair = @@ -1017,11 +1019,11 @@ public void completed(final ClientResponse response, final RequestScope scope) { final T result; if (callbackParamClass == Response.class) { result = callbackParamClass.cast(new InboundJaxrsResponse(response, scope)); - responseFuture.set(result); + responseFuture.complete(result); callback.completed(result); } else if (response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL) { result = response.readEntity(new GenericType(callbackParamType)); - responseFuture.set(result); + responseFuture.complete(result); callback.completed(result); } else { failed(convertToException(new InboundJaxrsResponse(response, scope))); @@ -1032,28 +1034,29 @@ public void completed(final ClientResponse response, final RequestScope scope) { public void failed(final ProcessingException error) { try { if (error.getCause() instanceof WebApplicationException) { - responseFuture.setException(error.getCause()); + responseFuture.completeExceptionally(error.getCause()); } else if (!responseFuture.isCancelled()) { - responseFuture.setException(error); + responseFuture.completeExceptionally(error); } } finally { callback.failed(error.getCause() instanceof CancellationException ? error.getCause() : error); } } }; - request().getClientRuntime().submit(requestForCall(requestContext), responseCallback); + final ClientRuntime runtime = request().getClientRuntime(); + runtime.submit(runtime.createRunnableForAsyncProcessing(requestForCall(requestContext), responseCallback)); } catch (final Throwable error) { final ProcessingException ce; //noinspection ChainOfInstanceofChecks if (error instanceof ProcessingException) { ce = (ProcessingException) error; - responseFuture.setException(ce); + responseFuture.completeExceptionally(ce); } else if (error instanceof WebApplicationException) { ce = new ProcessingException(error); - responseFuture.setException(error); + responseFuture.completeExceptionally(error); } else { ce = new ProcessingException(error); - responseFuture.setException(ce); + responseFuture.completeExceptionally(ce); } callback.failed(ce); } diff --git a/core-client/src/main/java/org/glassfish/jersey/client/JerseyWebTarget.java b/core-client/src/main/java/org/glassfish/jersey/client/JerseyWebTarget.java index 4a826607c8..8fffa8b107 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/JerseyWebTarget.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/JerseyWebTarget.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -48,7 +48,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.UriBuilder; -import jersey.repackaged.com.google.common.base.Preconditions; +import org.glassfish.jersey.internal.guava.Preconditions; /** * Jersey implementation of {@link javax.ws.rs.client.WebTarget JAX-RS client target} diff --git a/core-client/src/main/java/org/glassfish/jersey/client/RequestProcessingInitializationStage.java b/core-client/src/main/java/org/glassfish/jersey/client/RequestProcessingInitializationStage.java index 6f9bb6beff..994d1789f9 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/RequestProcessingInitializationStage.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/RequestProcessingInitializationStage.java @@ -41,11 +41,13 @@ package org.glassfish.jersey.client; import java.util.Collections; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import javax.ws.rs.ext.ReaderInterceptor; import javax.ws.rs.ext.WriterInterceptor; -import javax.inject.Inject; import javax.inject.Provider; import org.glassfish.jersey.internal.inject.InjectionManager; @@ -54,9 +56,6 @@ import org.glassfish.jersey.message.MessageBodyWorkers; import org.glassfish.jersey.model.internal.RankedComparator; -import jersey.repackaged.com.google.common.base.Function; -import jersey.repackaged.com.google.common.collect.Lists; - /** * Function that can be put to an acceptor chain to properly initialize * the client-side request-scoped processing injection for the current @@ -66,7 +65,7 @@ */ public class RequestProcessingInitializationStage implements Function { private final Provider> requestRefProvider; - private final Provider workersProvider; + private final MessageBodyWorkers workersProvider; private final Iterable writerInterceptors; private final Iterable readerInterceptors; @@ -78,23 +77,30 @@ public class RequestProcessingInitializationStage implements Function> requestRefProvider, - Provider workersProvider, + MessageBodyWorkers workersProvider, InjectionManager injectionManager) { this.requestRefProvider = requestRefProvider; this.workersProvider = workersProvider; - writerInterceptors = Collections.unmodifiableList(Lists.newArrayList(Providers.getAllProviders(injectionManager, - WriterInterceptor.class, new RankedComparator()))); - readerInterceptors = Collections.unmodifiableList(Lists.newArrayList(Providers.getAllProviders(injectionManager, - ReaderInterceptor.class, new RankedComparator()))); + writerInterceptors = Collections.unmodifiableList( + StreamSupport.stream( + Providers.getAllProviders(injectionManager, WriterInterceptor.class, + new RankedComparator<>()).spliterator(), false) + .collect(Collectors.toList()) + ); + readerInterceptors = Collections.unmodifiableList( + StreamSupport.stream( + Providers.getAllProviders(injectionManager, ReaderInterceptor.class, + new RankedComparator<>()).spliterator(), false) + .collect(Collectors.toList()) + ); } @Override public ClientRequest apply(ClientRequest requestContext) { requestRefProvider.get().set(requestContext); - requestContext.setWorkers(workersProvider.get()); + requestContext.setWorkers(workersProvider); requestContext.setWriterInterceptors(writerInterceptors); requestContext.setReaderInterceptors(readerInterceptors); diff --git a/core-client/src/main/java/org/glassfish/jersey/client/filter/EncodingFilter.java b/core-client/src/main/java/org/glassfish/jersey/client/filter/EncodingFilter.java index 7e91f8b2f8..d6d9dbb99a 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/filter/EncodingFilter.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/filter/EncodingFilter.java @@ -44,6 +44,7 @@ import java.util.ArrayList; import java.util.List; import java.util.SortedSet; +import java.util.TreeSet; import java.util.logging.Logger; import javax.ws.rs.client.ClientRequestContext; @@ -58,8 +59,6 @@ import org.glassfish.jersey.internal.inject.InjectionManager; import org.glassfish.jersey.spi.ContentEncoder; -import jersey.repackaged.com.google.common.collect.Sets; - /** * Client filter adding support for {@link org.glassfish.jersey.spi.ContentEncoder content encoding}. The filter adds * list of supported encodings to the Accept-Header values. @@ -105,12 +104,12 @@ List getSupportedEncodings() { // no need for synchronization - in case of a race condition, the property // may be set twice, but it does not break anything if (supportedEncodings == null) { - SortedSet se = Sets.newTreeSet(); + SortedSet se = new TreeSet<>(); List encoders = injectionManager.getAllInstances(ContentEncoder.class); for (ContentEncoder encoder : encoders) { se.addAll(encoder.getSupportedEncodings()); } - supportedEncodings = new ArrayList(se); + supportedEncodings = new ArrayList<>(se); } return supportedEncodings; } diff --git a/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java b/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java index cc800c7665..d07a836849 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2011-2016 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -55,9 +55,11 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; import javax.ws.rs.ProcessingException; import javax.ws.rs.client.Client; @@ -84,10 +86,6 @@ import org.glassfish.jersey.message.internal.OutboundMessageContext; import org.glassfish.jersey.message.internal.Statuses; -import jersey.repackaged.com.google.common.base.Predicates; -import jersey.repackaged.com.google.common.collect.Maps; -import jersey.repackaged.com.google.common.util.concurrent.MoreExecutors; - /** * Default client transport connector using {@link HttpURLConnection}. * @@ -290,18 +288,16 @@ public ClientResponse apply(ClientRequest request) { @Override public Future apply(final ClientRequest request, final AsyncConnectorCallback callback) { - return MoreExecutors.sameThreadExecutor().submit(new Runnable() { - @Override - public void run() { - try { - callback.response(_apply(request)); - } catch (IOException ex) { - callback.failure(new ProcessingException(ex)); - } catch (Throwable t) { - callback.failure(t); - } - } - }); + + try { + callback.response(_apply(request)); + } catch (IOException ex) { + callback.failure(new ProcessingException(ex)); + } catch (Throwable t) { + callback.failure(t); + } + + return CompletableFuture.completedFuture(null); } @Override @@ -408,7 +404,14 @@ public OutputStream getOutputStream(int contentLength) throws IOException { } ClientResponse responseContext = new ClientResponse(status, request, resolvedRequestUri); - responseContext.headers(Maps.filterKeys(uc.getHeaderFields(), Predicates.notNull())); + responseContext.headers( + uc.getHeaderFields() + .entrySet() + .stream() + .filter(stringListEntry -> stringListEntry.getKey() != null) + .collect(Collectors.toMap(Map.Entry::getKey, + Map.Entry::getValue)) + ); responseContext.setEntityStream(getInputStream(uc)); return responseContext; @@ -491,9 +494,7 @@ public Object run() throws NoSuchFieldException, setRequestMethodViaJreBugWorkaround(delegateConnection, method); } catch (NoSuchFieldException e) { // Ignore for now, keep going - } catch (IllegalArgumentException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { + } catch (IllegalArgumentException | IllegalAccessException e) { throw new RuntimeException(e); } try { diff --git a/core-client/src/test/java/org/glassfish/jersey/client/ClientRxTest.java b/core-client/src/test/java/org/glassfish/jersey/client/ClientRxTest.java index d61eaa732d..443991887b 100644 --- a/core-client/src/test/java/org/glassfish/jersey/client/ClientRxTest.java +++ b/core-client/src/test/java/org/glassfish/jersey/client/ClientRxTest.java @@ -54,6 +54,8 @@ import javax.ws.rs.core.GenericType; import javax.ws.rs.ext.Provider; +import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder; + import org.hamcrest.core.AllOf; import org.hamcrest.core.StringContains; import org.junit.After; @@ -62,8 +64,6 @@ import org.junit.rules.ExpectedException; import static org.junit.Assert.assertTrue; -import jersey.repackaged.com.google.common.util.concurrent.ThreadFactoryBuilder; - /** * Sanity test for {@link Invocation.Builder#rx()} methods. * diff --git a/core-client/src/test/java/org/glassfish/jersey/client/JerseyCompletionStageRxInvokerTest.java b/core-client/src/test/java/org/glassfish/jersey/client/JerseyCompletionStageRxInvokerTest.java index d2996082de..287228b5ce 100644 --- a/core-client/src/test/java/org/glassfish/jersey/client/JerseyCompletionStageRxInvokerTest.java +++ b/core-client/src/test/java/org/glassfish/jersey/client/JerseyCompletionStageRxInvokerTest.java @@ -51,6 +51,7 @@ import javax.ws.rs.core.GenericType; import javax.ws.rs.core.Response; +import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder; import org.glassfish.jersey.process.JerseyProcessingUncaughtExceptionHandler; import org.hamcrest.Matcher; @@ -62,8 +63,6 @@ import static org.hamcrest.Matchers.not; import static org.hamcrest.core.Is.is; -import jersey.repackaged.com.google.common.util.concurrent.ThreadFactoryBuilder; - /** * @author Pavel Bucek (pavel.bucek at oracle.com) */ diff --git a/core-client/src/test/java/org/glassfish/jersey/client/JerseyWebTargetTest.java b/core-client/src/test/java/org/glassfish/jersey/client/JerseyWebTargetTest.java index 8d91a91f96..521f8f82b5 100644 --- a/core-client/src/test/java/org/glassfish/jersey/client/JerseyWebTargetTest.java +++ b/core-client/src/test/java/org/glassfish/jersey/client/JerseyWebTargetTest.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2014 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -41,6 +41,7 @@ import java.io.IOException; import java.net.URI; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -61,8 +62,6 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.fail; -import jersey.repackaged.com.google.common.collect.Lists; - /** * {@code JerseyWebTarget} implementation unit tests. * @@ -208,7 +207,7 @@ public void testResolveTemplatesFromEncoded() { assertEquals("/path/a%2520%253F/*///b/", target.path("path/{a}/{b}").resolveTemplates(map, false).getUri().toString()); - List> corruptedTemplateValuesList = Lists.>newArrayList( + List> corruptedTemplateValuesList = Arrays.asList( null, new HashMap() {{ put(null, "value"); diff --git a/core-common/pom.xml b/core-common/pom.xml index 1d2f0639b0..3ab6279ccf 100644 --- a/core-common/pom.xml +++ b/core-common/pom.xml @@ -176,12 +176,6 @@ provided - - org.glassfish.jersey.bundles.repackaged - jersey-guava - ${project.version} - - org.glassfish.hk2 hk2-api diff --git a/core-common/src/main/java/org/glassfish/jersey/hk2/HK2InjectionManager.java b/core-common/src/main/java/org/glassfish/jersey/hk2/AbstractHk2InjectionManager.java similarity index 65% rename from core-common/src/main/java/org/glassfish/jersey/hk2/HK2InjectionManager.java rename to core-common/src/main/java/org/glassfish/jersey/hk2/AbstractHk2InjectionManager.java index 1666d21e48..887fbcc205 100644 --- a/core-common/src/main/java/org/glassfish/jersey/hk2/HK2InjectionManager.java +++ b/core-common/src/main/java/org/glassfish/jersey/hk2/AbstractHk2InjectionManager.java @@ -66,78 +66,55 @@ import org.jvnet.hk2.external.runtime.ServiceLocatorRuntimeBean; /** - * Implementation of {@link InjectionManager} for HK Dependency Injection Framework. + * Abstract class dedicated to implementations of {@link InjectionManager} providing several convenient methods. + * + * @author Petr Bouda (petr.bouda at oracle.com) */ -public class HK2InjectionManager implements InjectionManager { +abstract class AbstractHk2InjectionManager implements InjectionManager { - private static final Logger LOGGER = Logger.getLogger(HK2InjectionManager.class.getName()); + private static final Logger LOGGER = Logger.getLogger(AbstractHk2InjectionManager.class.getName()); private static final ServiceLocatorFactory factory = ServiceLocatorFactory.getInstance(); private ServiceLocator locator; /** - * Default constructor to be able to find a {@link HK2InjectionManager} using {@link java.util.ServiceLoader}. + * Constructor without parent and initial binder. */ - public HK2InjectionManager() { + AbstractHk2InjectionManager() { + this(null, null); } /** - * Creates a new instance using the passed {@link ServiceLocator}. More information about how to use this constructor can - * be found here {@link #createInjectionManager(ServiceLocator)}. + * Constructor with parent. * - * @param locator service locator used in this injection manager. + * @param parent parent of type {@link org.glassfish.jersey.internal.inject.InjectionManager} or {@link ServiceLocator}. */ - private HK2InjectionManager(ServiceLocator locator) { - this.locator = locator; + AbstractHk2InjectionManager(Object parent) { + this(parent, null); } /** - * Creates a new {@link ServiceLocator} instance from static {@link ServiceLocatorFactory} and adds the provided parent - * locator if the instance is not null. + * Constructor with initial binder. * - * @param name name of the injection manager. - * @param parentLocator parent locator, can be {@code null}. - * @return new instance of injection manager. + * @param binder initial binder which will be immediately registered. */ - private static ServiceLocator createLocator(String name, ServiceLocator parentLocator) { - ServiceLocator result = factory.create(name, parentLocator, null, ServiceLocatorFactory.CreatePolicy.DESTROY); - result.setNeutralContextClassLoader(false); - ServiceLocatorUtilities.enablePerThreadScope(result); - return result; + AbstractHk2InjectionManager(Binder binder) { + this(null, binder); } /** - * Creates a new instance of {@link HK2InjectionManager} and automatically add the provided instance of {@code ServiceLocator} - * as a underlying injection manager. - *

- * Commonly used in this scenarios: - * - create {@code InjectionManager} with externally provided {@code ServiceLocator}. - * - create {@code InjectionManager} which is immediately put into the different {@code InjectionManager} as a parent. + * Private constructor. * - * @param locator HK2 ServiceLocator - * @return new instance of {@link HK2InjectionManager} with underlying {@link ServiceLocator}. + * @param parent parent of type {@link org.glassfish.jersey.internal.inject.InjectionManager} or {@link ServiceLocator}. + * @param binder initial binder which will be immediately registered. */ - public static InjectionManager createInjectionManager(ServiceLocator locator) { - return new HK2InjectionManager(locator); - } - - /** - * Returns {@link ServiceLocator} dedicated to this instance of {@link HK2InjectionManager}. - * - * @return underlying instance of {@code ServiceLocator}. - */ - public ServiceLocator getServiceLocator() { - return locator; - } - - @Override - public void initialize(String name, Object parent, Binder... binders) { + private AbstractHk2InjectionManager(Object parent, Binder binder) { ServiceLocator parentLocator = resolveServiceLocatorParent(parent); - this.locator = createLocator(name, parentLocator); + this.locator = createLocator(parentLocator); // Register all components needed for proper HK2 locator bootstrap - Hk2Helper.bind(locator, new Hk2BootstrapBinder(this, CompositeBinder.wrap(binders))); + Hk2Helper.bind(locator, new Hk2BootstrapBinder(locator, CompositeBinder.wrap(binder))); this.locator.setDefaultClassAnalyzerName(JerseyClassAnalyzer.NAME); @@ -145,8 +122,7 @@ public void initialize(String name, Object parent, Binder... binders) { ServiceLocatorRuntimeBean serviceLocatorRuntimeBean = locator.getService(ServiceLocatorRuntimeBean.class); if (serviceLocatorRuntimeBean != null) { if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine(LocalizationMessages.HK_2_CLEARING_CACHE( - serviceLocatorRuntimeBean.getServiceCacheSize(), + LOGGER.fine(LocalizationMessages.HK_2_CLEARING_CACHE(serviceLocatorRuntimeBean.getServiceCacheSize(), serviceLocatorRuntimeBean.getReflectionCacheSize())); } serviceLocatorRuntimeBean.clearReflectionCache(); @@ -154,49 +130,49 @@ public void initialize(String name, Object parent, Binder... binders) { } } + /** - * Checks if the parent is null then must be an instance of {@link ServiceLocator} or {@link HK2InjectionManager}. + * Creates a new {@link ServiceLocator} instance from static {@link ServiceLocatorFactory} and adds the provided parent + * locator if the instance is not null. * - * @param parent object represented by {@code ServiceLocator} or {@code HK2InjectionManager}. + * @param parentLocator parent locator, can be {@code null}. + * @return new instance of injection manager. */ - private static void assertParentLocatorType(Object parent) { - if (parent != null && !(parent instanceof ServiceLocator || parent instanceof HK2InjectionManager)) { - throw new IllegalArgumentException(LocalizationMessages.HK_2_UNKNOWN_PARENT_INJECTION_MANAGER( - parent.getClass().getSimpleName())); - } - } - - @Override - public void shutdown() { - if (factory.find(locator.getName()) != null) { - factory.destroy(locator.getName()); - } else { - locator.shutdown(); - } + private static ServiceLocator createLocator(ServiceLocator parentLocator) { + ServiceLocator result = factory.create(null, parentLocator, null, ServiceLocatorFactory.CreatePolicy.DESTROY); + result.setNeutralContextClassLoader(false); + ServiceLocatorUtilities.enablePerThreadScope(result); + return result; } - @Override - public void register(Binding binding) { - Hk2Helper.bind(locator, binding); - } + private static ServiceLocator resolveServiceLocatorParent(Object parent) { + assertParentLocatorType(parent); - @Override - public void register(Iterable descriptors) { - Hk2Helper.bind(locator, descriptors); + ServiceLocator parentLocator = null; + if (parent != null) { + if (parent instanceof ServiceLocator) { + parentLocator = (ServiceLocator) parent; + } else if (parent instanceof AbstractHk2InjectionManager) { + parentLocator = ((AbstractHk2InjectionManager) parent).getServiceLocator(); + } + } + return parentLocator; } - @Override - public void register(Binder binder) { - Hk2Helper.bind(locator, binder); + /** + * Checks if the parent is null then must be an instance of {@link ServiceLocator} or {@link InjectionManager}. + * + * @param parent object represented by {@code ServiceLocator} or {@code HK2InjectionManager}. + */ + private static void assertParentLocatorType(Object parent) { + if (parent != null && !(parent instanceof ServiceLocator || parent instanceof AbstractHk2InjectionManager)) { + throw new IllegalArgumentException( + LocalizationMessages.HK_2_UNKNOWN_PARENT_INJECTION_MANAGER(parent.getClass().getSimpleName())); + } } - @Override - public void register(Object provider) { - if (isRegistrable(provider.getClass())) { - ServiceLocatorUtilities.bind(locator, (org.glassfish.hk2.utilities.Binder) provider); - } else { - throw new IllegalArgumentException(); - } + public ServiceLocator getServiceLocator() { + return locator; } @Override @@ -204,15 +180,10 @@ public boolean isRegistrable(Class clazz) { return org.glassfish.hk2.utilities.Binder.class.isAssignableFrom(clazz); } - @Override - public U createAndInitialize(Class clazz) { - return locator.createAndInitialize(clazz); - } - @Override @SuppressWarnings("unchecked") public List> getAllServiceHolders(Class contract, Annotation... qualifiers) { - return locator.getAllServiceHandles(contract, qualifiers).stream() + return getServiceLocator().getAllServiceHandles(contract, qualifiers).stream() .map(sh -> new ServiceHolderImpl<>( sh.getService(), (Class) sh.getActiveDescriptor().getImplementationClass(), @@ -223,83 +194,83 @@ public List> getAllServiceHolders(Class contract, Annota @Override public T getInstance(Class clazz, Annotation... annotations) { - return locator.getService(clazz, annotations); + return getServiceLocator().getService(clazz, annotations); } @Override public T getInstance(Type clazz) { - return locator.getService(clazz); + return getServiceLocator().getService(clazz); } @Override public Object getInstance(ForeignDescriptor foreignDescriptor) { - return locator.getServiceHandle((ActiveDescriptor) foreignDescriptor.get()).getService(); + return getServiceLocator().getServiceHandle((ActiveDescriptor) foreignDescriptor.get()).getService(); } @Override - public ForeignDescriptor createForeignDescriptor(Binding binding) { - ForeignDescriptor foreignDescriptor = createAndTranslateForeignDescriptor(binding); - ActiveDescriptor activeDescriptor = ServiceLocatorUtilities - .addOneDescriptor(locator, (Descriptor) foreignDescriptor.get(), false); - return ForeignDescriptor.wrap(activeDescriptor, activeDescriptor::dispose); + public T getInstance(Class clazz) { + return getServiceLocator().getService(clazz); } - @SuppressWarnings("unchecked") - private ForeignDescriptor createAndTranslateForeignDescriptor(Binding binding) { - ActiveDescriptor activeDescriptor; - if (ClassBinding.class.isAssignableFrom(binding.getClass())) { - activeDescriptor = Hk2Helper.translateToActiveDescriptor((ClassBinding) binding); - } else if (InstanceBinding.class.isAssignableFrom(binding.getClass())) { - activeDescriptor = Hk2Helper.translateToActiveDescriptor((InstanceBinding) binding); - } else { - throw new RuntimeException(org.glassfish.jersey.internal.LocalizationMessages.UNKNOWN_DESCRIPTOR_TYPE( - binding.getClass().getSimpleName())); - } - - return ForeignDescriptor.wrap(activeDescriptor, activeDescriptor::dispose); + @Override + public T getInstance(Class clazz, String classAnalyzer) { + return getServiceLocator().getService(clazz, classAnalyzer); } - private static ServiceLocator resolveServiceLocatorParent(Object parent) { - assertParentLocatorType(parent); + @Override + public List getAllInstances(Type clazz) { + return getServiceLocator().getAllServices(clazz); + } - ServiceLocator parentLocator = null; - if (parent != null) { - if (parent instanceof ServiceLocator) { - parentLocator = (ServiceLocator) parent; - } else if (parent instanceof HK2InjectionManager) { - parentLocator = ((HK2InjectionManager) parent).getServiceLocator(); - } - } - return parentLocator; + @Override + public void preDestroy(Object preDestroyMe) { + getServiceLocator().preDestroy(preDestroyMe); } @Override - public T getInstance(Class clazz) { - return locator.getService(clazz); + public void shutdown() { + if (factory.find(getServiceLocator().getName()) != null) { + factory.destroy(getServiceLocator().getName()); + } else { + getServiceLocator().shutdown(); + } } @Override - public T getInstance(Class clazz, String classAnalyzer) { - return locator.getService(clazz, classAnalyzer); + public U createAndInitialize(Class clazz) { + return getServiceLocator().createAndInitialize(clazz); } @Override - public List getAllInstances(Type clazz) { - return locator.getAllServices(clazz); + public ForeignDescriptor createForeignDescriptor(Binding binding) { + ForeignDescriptor foreignDescriptor = createAndTranslateForeignDescriptor(binding); + ActiveDescriptor activeDescriptor = ServiceLocatorUtilities + .addOneDescriptor(getServiceLocator(), (Descriptor) foreignDescriptor.get(), false); + return ForeignDescriptor.wrap(activeDescriptor, activeDescriptor::dispose); } @Override public void inject(Object injectMe) { - locator.inject(injectMe); + getServiceLocator().inject(injectMe); } @Override public void inject(Object injectMe, String classAnalyzer) { - locator.inject(injectMe, classAnalyzer); + getServiceLocator().inject(injectMe, classAnalyzer); } - @Override - public void preDestroy(Object preDestroyMe) { - locator.preDestroy(preDestroyMe); + @SuppressWarnings("unchecked") + private ForeignDescriptor createAndTranslateForeignDescriptor(Binding binding) { + ActiveDescriptor activeDescriptor; + if (ClassBinding.class.isAssignableFrom(binding.getClass())) { + activeDescriptor = Hk2Helper.translateToActiveDescriptor((ClassBinding) binding); + } else if (InstanceBinding.class.isAssignableFrom(binding.getClass())) { + activeDescriptor = Hk2Helper.translateToActiveDescriptor((InstanceBinding) binding); + } else { + throw new RuntimeException(org.glassfish.jersey.internal.LocalizationMessages + .UNKNOWN_DESCRIPTOR_TYPE(binding.getClass().getSimpleName())); + } + + return ForeignDescriptor.wrap(activeDescriptor, activeDescriptor::dispose); } } diff --git a/core-common/src/main/java/org/glassfish/jersey/hk2/DelayedHk2InjectionManager.java b/core-common/src/main/java/org/glassfish/jersey/hk2/DelayedHk2InjectionManager.java new file mode 100644 index 0000000000..17c8e6f418 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/hk2/DelayedHk2InjectionManager.java @@ -0,0 +1,130 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package org.glassfish.jersey.hk2; + +import java.util.ArrayList; +import java.util.List; + +import org.glassfish.jersey.internal.inject.AbstractBinder; +import org.glassfish.jersey.internal.inject.Binder; +import org.glassfish.jersey.internal.inject.Binding; + +import org.glassfish.hk2.api.ServiceLocator; +import org.glassfish.hk2.utilities.ServiceLocatorUtilities; + +/** + * Implementation of {@link org.glassfish.jersey.internal.inject.InjectionManager} that is able to delay service's registration + * and injection to {@link #completeRegistration()} phase. During the Jersey bootstrap just keep the bindings and other + * operation for a later use. + * + * @author Petr Bouda (petr.bouda at oracle.com) + */ +public class DelayedHk2InjectionManager extends AbstractHk2InjectionManager { + + // Keeps all binders and bindings added to the InjectionManager during the bootstrap. + private final AbstractBinder bindings = new AbstractBinder() { + @Override + protected void configure() { + } + }; + + // Keeps DI provider specific object for registration. + private final List providers = new ArrayList<>(); + + /** + * Constructor without parent and initial binder. + */ + DelayedHk2InjectionManager() { + super(); + } + + /** + * Constructor with parent. + * + * @param parent parent of type {@link org.glassfish.jersey.internal.inject.InjectionManager} or {@link ServiceLocator}. + */ + DelayedHk2InjectionManager(Object parent) { + super(parent); + } + + /** + * Constructor with initial binder. + * + * @param binder initial binder which will be immediately registered. + */ + DelayedHk2InjectionManager(Binder binder) { + super(binder); + } + + @Override + public void register(Binding binding) { + bindings.bind(binding); + } + + @Override + public void register(Iterable bindings) { + for (Binding binding : bindings) { + this.bindings.bind(binding); + } + } + + @Override + public void register(Binder binder) { + for (Binding binding : binder.getBindings()) { + bindings.bind(binding); + } + } + + @Override + public void register(Object provider) throws IllegalArgumentException { + if (isRegistrable(provider.getClass())) { + providers.add((org.glassfish.hk2.utilities.Binder) provider); + } else { + throw new IllegalArgumentException(LocalizationMessages.HK_2_PROVIDER_NOT_REGISTRABLE(provider.getClass())); + } + } + + @Override + public void completeRegistration() throws IllegalStateException { + Hk2Helper.bind(getServiceLocator(), bindings); + ServiceLocatorUtilities.bind(getServiceLocator(), providers.toArray(new org.glassfish.hk2.utilities.Binder[]{})); + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/hk2/Hk2BootstrapBinder.java b/core-common/src/main/java/org/glassfish/jersey/hk2/Hk2BootstrapBinder.java index 4ccc58e078..06ec455f0f 100644 --- a/core-common/src/main/java/org/glassfish/jersey/hk2/Hk2BootstrapBinder.java +++ b/core-common/src/main/java/org/glassfish/jersey/hk2/Hk2BootstrapBinder.java @@ -41,7 +41,8 @@ package org.glassfish.jersey.hk2; import org.glassfish.jersey.internal.inject.AbstractBinder; -import org.glassfish.jersey.internal.inject.InjectionManager; + +import org.glassfish.hk2.api.ServiceLocator; /** * {@link AbstractBinder} that registers all components needed for a proper bootstrap of Jersey based on HK2 framework. @@ -52,26 +53,24 @@ public class Hk2BootstrapBinder extends AbstractBinder { private final AbstractBinder externalBinder; - private final InjectionManager injectionManager; + private final ServiceLocator serviceLocator; /** * Create a bootstrap which is specific for HK2 module and automatically install {@code externalBinder}. * - * @param injectionManager HK2 service locator. - * @param externalBinder externally provided binder (particularly Jersey specific services). + * @param serviceLocator HK2 service locator. + * @param externalBinder externally provided binder (particularly Jersey specific services). */ - public Hk2BootstrapBinder(InjectionManager injectionManager, AbstractBinder externalBinder) { - this.injectionManager = injectionManager; + Hk2BootstrapBinder(ServiceLocator serviceLocator, AbstractBinder externalBinder) { + this.serviceLocator = serviceLocator; this.externalBinder = externalBinder; } @Override protected void configure() { install( - // First service the current injection manager to be able to inject itself into other services. - new HK2InjectionManagerBinder(injectionManager), - // Jersey-like class analyzer that is able to choose the right services' construtor. - new JerseyClassAnalyzer.Binder(injectionManager), + // Jersey-like class analyzer that is able to choose the right services' constructor. + new JerseyClassAnalyzer.Binder(serviceLocator), // Activate possibility to start Request Scope. new RequestContext.Binder(), // Add support for Context annotation. @@ -81,26 +80,4 @@ protected void configure() { // Improved HK2 Error reporting. new JerseyErrorService.Binder()); } - - /** - * Binder that registers a provided injection manager. - */ - private static class HK2InjectionManagerBinder extends AbstractBinder { - - private final InjectionManager injectionManager; - - /** - * Constructor for a creation injection manager binder. - * - * @param injectionManager current injection manager. - */ - private HK2InjectionManagerBinder(InjectionManager injectionManager) { - this.injectionManager = injectionManager; - } - - @Override - protected void configure() { - bind(injectionManager).to(InjectionManager.class); - } - } } diff --git a/core-common/src/main/java/org/glassfish/jersey/hk2/Hk2InjectionManagerFactory.java b/core-common/src/main/java/org/glassfish/jersey/hk2/Hk2InjectionManagerFactory.java new file mode 100644 index 0000000000..40b4d1a762 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/hk2/Hk2InjectionManagerFactory.java @@ -0,0 +1,175 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package org.glassfish.jersey.hk2; + +import java.security.AccessController; + +import org.glassfish.jersey.internal.inject.AbstractBinder; +import org.glassfish.jersey.internal.inject.Binder; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.internal.inject.InjectionManagerFactory; +import org.glassfish.jersey.internal.util.PropertiesHelper; + +/** + * Factory which is able to create {@link InjectionManager} instance using service loading and automatically initialize injection + * manager using {@code parent} or immediately registers binder. + */ +public class Hk2InjectionManagerFactory implements InjectionManagerFactory { + + /** + * Hk2 Injection manager strategy. + *

+ * Value can be supplied only via java properties, which would typically be done using '-D' parameter, + * for example: {@code java -Dorg.glassfish.jersey.hk2.injection.manager.strategy=delayed ...} + *

+ * Valid values are "immediate" and "delayed" and values are case-insensitive. + *

+ * Default value is "immediate". + */ + public static final String HK2_INJECTION_MANAGER_STRATEGY = "org.glassfish.jersey.hk2.injection.manager.strategy"; + + private enum Hk2InjectionManagerStrategy { + + /** + * @see ImmediateHk2InjectionManager + */ + IMMEDIATE { + @Override + InjectionManager createInjectionManager() { + return new ImmediateHk2InjectionManager(); + } + + @Override + InjectionManager createInjectionManager(final Object parent) { + return new ImmediateHk2InjectionManager(parent); + } + + @Override + InjectionManager createInjectionManager(final Binder binder) { + return new ImmediateHk2InjectionManager(binder); + } + }, + /** + * @see DelayedHk2InjectionManager + */ + DELAYED { + @Override + InjectionManager createInjectionManager() { + return new DelayedHk2InjectionManager(); + } + + @Override + InjectionManager createInjectionManager(final Object parent) { + return new DelayedHk2InjectionManager(parent); + } + + @Override + InjectionManager createInjectionManager(final Binder binder) { + return new DelayedHk2InjectionManager(binder); + } + }; + + abstract InjectionManager createInjectionManager(); + + abstract InjectionManager createInjectionManager(Object parent); + + abstract InjectionManager createInjectionManager(Binder binder); + } + + @Override + public InjectionManager create() { + return initInjectionManager(getStrategy().createInjectionManager()); + } + + @Override + public InjectionManager create(Object parent) { + return initInjectionManager(getStrategy().createInjectionManager(parent)); + } + + // TODO: CANDIDATE TO DELETE: is used in RuntimeDelegateImpl super(...). + @Override + public InjectionManager create(Binder binder) { + return initInjectionManager(getStrategy().createInjectionManager(binder)); + } + + private Hk2InjectionManagerStrategy getStrategy() { + String value = AccessController.doPrivileged(PropertiesHelper.getSystemProperty(HK2_INJECTION_MANAGER_STRATEGY)); + if (value == null || value.isEmpty()) { + return Hk2InjectionManagerStrategy.IMMEDIATE; + } + + if ("immediate".equalsIgnoreCase(value)) { + return Hk2InjectionManagerStrategy.IMMEDIATE; + } else if ("delayed".equalsIgnoreCase(value)) { + return Hk2InjectionManagerStrategy.DELAYED; + } else { + throw new IllegalStateException("Illegal value of " + HK2_INJECTION_MANAGER_STRATEGY + + ". Expected \"immediate\" or \"delayed\", the actual value is: " + value); + } + } + + private InjectionManager initInjectionManager(InjectionManager injectionManager) { + injectionManager.register(new Hk2InjectionManagerBinder(injectionManager)); + return injectionManager; + } + + /** + * Binder that registers a provided injection manager. + */ + private static class Hk2InjectionManagerBinder extends AbstractBinder { + + private final InjectionManager injectionManager; + + /** + * Constructor for a creation injection manager binder. + * + * @param injectionManager current injection manager. + */ + private Hk2InjectionManagerBinder(InjectionManager injectionManager) { + this.injectionManager = injectionManager; + } + + @Override + protected void configure() { + bind(injectionManager).to(InjectionManager.class); + } + } +} diff --git a/media/sse/src/main/java/org/glassfish/jersey/media/sse/internal/SseValueSupplierProvider.java b/core-common/src/main/java/org/glassfish/jersey/hk2/ImmediateHk2InjectionManager.java similarity index 51% rename from media/sse/src/main/java/org/glassfish/jersey/media/sse/internal/SseValueSupplierProvider.java rename to core-common/src/main/java/org/glassfish/jersey/hk2/ImmediateHk2InjectionManager.java index c4a67fb0a2..3de3c0d00c 100644 --- a/media/sse/src/main/java/org/glassfish/jersey/media/sse/internal/SseValueSupplierProvider.java +++ b/core-common/src/main/java/org/glassfish/jersey/hk2/ImmediateHk2InjectionManager.java @@ -37,59 +37,73 @@ * only if the new code is made subject to such option by the copyright * holder. */ -package org.glassfish.jersey.media.sse.internal; -import javax.ws.rs.sse.Sse; +package org.glassfish.jersey.hk2; -import javax.inject.Provider; +import org.glassfish.jersey.internal.inject.Binder; +import org.glassfish.jersey.internal.inject.Binding; -import org.glassfish.jersey.server.ContainerRequest; -import org.glassfish.jersey.server.internal.inject.AbstractRequestDerivedValueSupplier; -import org.glassfish.jersey.server.internal.inject.AbstractValueSupplierProvider; -import org.glassfish.jersey.server.internal.inject.MultivaluedParameterExtractorProvider; -import org.glassfish.jersey.server.model.Parameter; +import org.glassfish.hk2.api.ServiceLocator; +import org.glassfish.hk2.utilities.ServiceLocatorUtilities; /** - * {@link org.glassfish.jersey.server.spi.internal.ValueSupplierProvider} for binding {@link Sse} to its implementation. + * Implementation of {@link org.glassfish.jersey.internal.inject.InjectionManager} that is able to register and inject services. * - * @author Adam Lindenthal (adam.lindenthal at oracle.com) + * @author Petr Bouda (petr.bouda at oracle.com) */ -public class SseValueSupplierProvider extends AbstractValueSupplierProvider { +public class ImmediateHk2InjectionManager extends AbstractHk2InjectionManager { /** - * Constructor. + * Constructor without parent and initial binder. + */ + ImmediateHk2InjectionManager() { + super(); + } + + /** + * Constructor with parent. + * + * @param parent parent of type {@link org.glassfish.jersey.internal.inject.InjectionManager} or {@link ServiceLocator}. + */ + ImmediateHk2InjectionManager(Object parent) { + super(parent); + } + + /** + * Constructor with initial binder. * - * @param mpep multivalued map parameter extractor provider. - * @param requestProvider request provider. + * @param binder initial binder which will be immediately registered. */ - public SseValueSupplierProvider(final MultivaluedParameterExtractorProvider mpep, - final Provider requestProvider) { - super(mpep, requestProvider, Parameter.Source.CONTEXT); + ImmediateHk2InjectionManager(Binder binder) { + super(binder); } @Override - protected AbstractRequestDerivedValueSupplier createValueSupplier(final Parameter parameter, - final Provider requestProvider) { - if (parameter == null) { - return null; - } + public void completeRegistration() throws IllegalStateException { + // No-op method. + } - final Class rawParameterType = parameter.getRawType(); - if (rawParameterType == Sse.class) { - return new SseValueSupplier(requestProvider); - } - return null; + @Override + public void register(Binding binding) { + Hk2Helper.bind(getServiceLocator(), binding); } - private static final class SseValueSupplier extends AbstractRequestDerivedValueSupplier { + @Override + public void register(Iterable descriptors) { + Hk2Helper.bind(getServiceLocator(), descriptors); + } - SseValueSupplier(final Provider requestProvider) { - super(requestProvider); - } + @Override + public void register(Binder binder) { + Hk2Helper.bind(getServiceLocator(), binder); + } - @Override - public Sse get() { - return JerseySse.getInstance(); + @Override + public void register(Object provider) { + if (isRegistrable(provider.getClass())) { + ServiceLocatorUtilities.bind(getServiceLocator(), (org.glassfish.hk2.utilities.Binder) provider); + } else { + throw new IllegalArgumentException(LocalizationMessages.HK_2_PROVIDER_NOT_REGISTRABLE(provider.getClass())); } } } diff --git a/core-common/src/main/java/org/glassfish/jersey/hk2/JerseyClassAnalyzer.java b/core-common/src/main/java/org/glassfish/jersey/hk2/JerseyClassAnalyzer.java index 5b0052606e..51d4c5e89d 100644 --- a/core-common/src/main/java/org/glassfish/jersey/hk2/JerseyClassAnalyzer.java +++ b/core-common/src/main/java/org/glassfish/jersey/hk2/JerseyClassAnalyzer.java @@ -58,7 +58,6 @@ import org.glassfish.jersey.internal.Errors; import org.glassfish.jersey.internal.LocalizationMessages; import org.glassfish.jersey.internal.inject.AbstractBinder; -import org.glassfish.jersey.internal.inject.InjectionManager; import org.glassfish.jersey.internal.inject.InjectionResolver; import org.glassfish.jersey.internal.util.collection.ImmutableCollectors; import org.glassfish.jersey.internal.util.collection.LazyValue; @@ -67,6 +66,7 @@ import org.glassfish.hk2.api.ClassAnalyzer; import org.glassfish.hk2.api.MultiException; +import org.glassfish.hk2.api.ServiceLocator; /** * Implementation of the {@link ClassAnalyzer} that supports selection @@ -90,23 +90,23 @@ public final class JerseyClassAnalyzer implements ClassAnalyzer { */ public static final class Binder extends AbstractBinder { - private final InjectionManager injectionManager; + private final ServiceLocator serviceLocator; /** * Constructor for {@code JerseyClassAnalyzer}. * - * @param injectionManager current injection manager. + * @param serviceLocator current injection manager. */ - public Binder(InjectionManager injectionManager) { - this.injectionManager = injectionManager; + public Binder(ServiceLocator serviceLocator) { + this.serviceLocator = serviceLocator; } @Override protected void configure() { ClassAnalyzer defaultAnalyzer = - injectionManager.getInstance(ClassAnalyzer.class, ClassAnalyzer.DEFAULT_IMPLEMENTATION_NAME); + serviceLocator.getService(ClassAnalyzer.class, ClassAnalyzer.DEFAULT_IMPLEMENTATION_NAME); - Supplier> resolvers = () -> injectionManager.getAllInstances(InjectionResolver.class); + Supplier> resolvers = () -> serviceLocator.getAllServices(InjectionResolver.class); bind(new JerseyClassAnalyzer(defaultAnalyzer, resolvers)) .analyzeWith(ClassAnalyzer.DEFAULT_IMPLEMENTATION_NAME) @@ -169,12 +169,8 @@ public Constructor getConstructor(final Class clazz) throws MultiExcep } // At this point, we simply need to find the constructor with the largest number of parameters - final Constructor[] constructors = AccessController.doPrivileged(new PrivilegedAction[]>() { - @Override - public Constructor[] run() { - return clazz.getDeclaredConstructors(); - } - }); + final Constructor[] constructors = AccessController.doPrivileged( + (PrivilegedAction[]>) clazz::getDeclaredConstructors); Constructor selected = null; int selectedSize = 0; int maxParams = -1; diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/BootstrapBag.java b/core-common/src/main/java/org/glassfish/jersey/internal/BootstrapBag.java new file mode 100644 index 0000000000..669427c254 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/BootstrapBag.java @@ -0,0 +1,244 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package org.glassfish.jersey.internal; + +import java.lang.reflect.Type; +import java.util.Objects; + +import javax.ws.rs.core.Configuration; + +import org.glassfish.jersey.message.MessageBodyWorkers; +import org.glassfish.jersey.process.internal.RequestScope; +import org.glassfish.jersey.spi.ContextResolvers; +import org.glassfish.jersey.spi.ExceptionMappers; + +/** + * A holder that is used only during Jersey bootstrap to keep the instances of the given types and then use them during the + * bootstrap. This works as a replacement of an injection framework during a bootstrap and intentionally keeps all needed types in + * separate fields to make strong type nature and to preserve a clear view which types are needed to inject to other services. + * + * @author Petr Bouda (petr.bouda at oracle.com) + */ +public class BootstrapBag { + + private Configuration configuration; + private RequestScope requestScope; + private MessageBodyWorkers messageBodyWorkers; + private ExceptionMappers exceptionMappers; + private ContextResolvers contextResolvers; + + /** + * Gets an instance of {@link RequestScope}. + * + * @return {@code RequestScope} instance. + */ + public RequestScope getRequestScope() { + requireNonNull(requestScope, RequestScope.class); + return requestScope; + } + + /** + * Sets an instance of {@link RequestScope}. + * + * @param requestScope {@code RequestScope} instance. + */ + public void setRequestScope(RequestScope requestScope) { + this.requestScope = requestScope; + } + + /** + * Gets an instance of {@link MessageBodyWorkers}. + * + * @return {@code MessageBodyWorkers} instance. + */ + public MessageBodyWorkers getMessageBodyWorkers() { + requireNonNull(messageBodyWorkers, MessageBodyWorkers.class); + return messageBodyWorkers; + } + + /** + * Sets an instance of {@link MessageBodyWorkers}. + * + * @param messageBodyWorkers {@code MessageBodyWorkers} instance. + */ + public void setMessageBodyWorkers(MessageBodyWorkers messageBodyWorkers) { + this.messageBodyWorkers = messageBodyWorkers; + } + + /** + * Gets an instance of {@link Configuration}. + * + * @return {@code Configuration} instance. + */ + public Configuration getConfiguration() { + requireNonNull(configuration, Configuration.class); + return configuration; + } + + /** + * Sets an instance of {@link Configuration}. + * + * @param configuration {@code Configuration} instance. + */ + public void setConfiguration(Configuration configuration) { + this.configuration = configuration; + } + + /** + * Gets an instance of {@link ExceptionMappers}. + * + * @return {@code ExceptionMappers} instance. + */ + public ExceptionMappers getExceptionMappers() { + requireNonNull(exceptionMappers, ExceptionMappers.class); + return exceptionMappers; + } + + /** + * Sets an instance of {@link ExceptionMappers}. + * + * @param exceptionMappers {@code ExceptionMappers} instance. + */ + public void setExceptionMappers(ExceptionMappers exceptionMappers) { + this.exceptionMappers = exceptionMappers; + } + + /** + * Gets an instance of {@link ContextResolvers}. + * + * @return {@code ContextResolvers} instance. + */ + public ContextResolvers getContextResolvers() { + requireNonNull(contextResolvers, ContextResolvers.class); + return contextResolvers; + } + + /** + * Sets an instance of {@link ContextResolvers}. + * + * @param contextResolvers {@code ContextResolvers} instance. + */ + public void setContextResolvers(ContextResolvers contextResolvers) { + this.contextResolvers = contextResolvers; + } + + /** + * Creates an immutable version of bootstrap bag. + * + * @return immutable bootstrap bag. + */ + public BootstrapBag toImmutable() { + return new ImmutableBootstrapBag(this); + } + + /** + * Check whether the value is not {@code null} that means that the proper {@link BootstrapConfigurator} has not been configured + * or in a wrong order. + * + * @param object tested object. + * @param type type of the tested object. + */ + protected static void requireNonNull(Object object, Type type) { + Objects.requireNonNull(object, type + " has not been added into BootstrapBag yet"); + } + + /** + * Immutable version of {@link BootstrapBag}. + */ + public static class ImmutableBootstrapBag extends BootstrapBag { + + private final BootstrapBag delegate; + + protected ImmutableBootstrapBag(BootstrapBag delegate) { + this.delegate = delegate; + } + + @Override + public RequestScope getRequestScope() { + return delegate.getRequestScope(); + } + + @Override + public void setRequestScope(RequestScope requestScope) { + throw new UnsupportedOperationException(); + } + + @Override + public MessageBodyWorkers getMessageBodyWorkers() { + return delegate.getMessageBodyWorkers(); + } + + @Override + public void setMessageBodyWorkers(MessageBodyWorkers messageBodyWorkers) { + throw new UnsupportedOperationException(); + } + + @Override + public Configuration getConfiguration() { + return delegate.getConfiguration(); + } + + @Override + public void setConfiguration(Configuration configuration) { + throw new UnsupportedOperationException(); + } + + @Override + public ExceptionMappers getExceptionMappers() { + return delegate.getExceptionMappers(); + } + + @Override + public void setExceptionMappers(ExceptionMappers exceptionMappers) { + throw new UnsupportedOperationException(); + } + + @Override + public ContextResolvers getContextResolvers() { + return delegate.getContextResolvers(); + } + + @Override + public void setContextResolvers(ContextResolvers contextResolvers) { + throw new UnsupportedOperationException(); + } + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/BootstrapConfigurator.java b/core-common/src/main/java/org/glassfish/jersey/internal/BootstrapConfigurator.java new file mode 100644 index 0000000000..34cb4da78a --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/BootstrapConfigurator.java @@ -0,0 +1,77 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package org.glassfish.jersey.internal; + +import org.glassfish.jersey.internal.inject.InjectionManager; + +/** + * Configurator which contains two methods, {@link #init(InjectionManager, BootstrapBag)} contains {@link InjectionManager} + * into which only registering services make sense because injection manager has not been completed yet and + * {@link #postInit(InjectionManager, BootstrapBag)} in which {@link InjectionManager} has been already completed and is able to + * create and provide services. + *

+ * The configurators should register instances into {@link InjectionManager} only if the instance must be really injectable if + * the instance can be used internally without the injection, then extend {@link BootstrapBag} and propagate the instance to + * correct services using constructors or methods in a phase of Jersey initialization. + * + * @author Petr Bouda (petr.bouda at oracle.com) + */ +public interface BootstrapConfigurator { + + /** + * Pre-initialization method should only register services into {@link InjectionManager} and populate {@link BootstrapBag}. + * + * @param injectionManager not completed injection manager. + * @param bootstrapBag bootstrap bag with services used in following processing. + */ + void init(InjectionManager injectionManager, BootstrapBag bootstrapBag); + + /** + * Post-initialization method can get services from {@link InjectionManager} and is not able to register the new one because + * injection manager is already completed. + * + * @param injectionManager already completed injection manager. + * @param bootstrapBag bootstrap bag with services used in following processing. + */ + default void postInit(InjectionManager injectionManager, BootstrapBag bootstrapBag) { + } + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/ContextResolverFactory.java b/core-common/src/main/java/org/glassfish/jersey/internal/ContextResolverFactory.java index 956f4c6029..38a565c0d4 100644 --- a/core-common/src/main/java/org/glassfish/jersey/internal/ContextResolverFactory.java +++ b/core-common/src/main/java/org/glassfish/jersey/internal/ContextResolverFactory.java @@ -51,12 +51,9 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.ext.ContextResolver; -import javax.inject.Inject; -import javax.inject.Singleton; - -import org.glassfish.jersey.internal.inject.AbstractBinder; +import org.glassfish.jersey.internal.inject.Bindings; import org.glassfish.jersey.internal.inject.InjectionManager; -import org.glassfish.jersey.internal.inject.Providers; +import org.glassfish.jersey.internal.inject.InstanceBinding; import org.glassfish.jersey.internal.util.ReflectionHelper; import org.glassfish.jersey.internal.util.ReflectionHelper.DeclaringClassInterfacePair; import org.glassfish.jersey.internal.util.collection.KeyComparatorHashMap; @@ -73,47 +70,56 @@ public class ContextResolverFactory implements ContextResolvers { /** - * Injection binder defining {@link ContextResolverFactory} and - * {@link ContextResolvers} bindings. + * Configurator which initializes and register {@link ContextResolvers} instance into {@link InjectionManager} and + * {@link BootstrapBag}. + * + * @author Petr Bouda (petr.bouda at oracle.com) */ - public static class Binder extends AbstractBinder { + public static class ContextResolversConfigurator implements BootstrapConfigurator { + + private ContextResolverFactory contextResolverFactory; @Override - protected void configure() { - bindAsContract(ContextResolverFactory.class).to(ContextResolvers.class).in(Singleton.class); + public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) { + contextResolverFactory = new ContextResolverFactory(); + InstanceBinding binding = + Bindings.service(contextResolverFactory) + .to(ContextResolvers.class); + injectionManager.register(binding); + } + + @Override + public void postInit(InjectionManager injectionManager, BootstrapBag bootstrapBag) { + contextResolverFactory.initialize(injectionManager.getAllInstances(ContextResolver.class)); + bootstrapBag.setContextResolvers(contextResolverFactory); } } - private final Map> resolver = - new HashMap>(3); - private final Map> cache = - new HashMap>(3); + private final Map> resolver = new HashMap<>(3); + private final Map> cache = new HashMap<>(3); /** - * Create new context resolver factory backed by the supplied {@link InjectionManager injection manager}. - * - * @param injectionManager injection manager. + * Private constructor to allow to create {@link ContextResolverFactory} only in {@link ContextResolversConfigurator}. */ - @Inject - public ContextResolverFactory(final InjectionManager injectionManager) { - final Map>> rs = - new HashMap>>(); + private ContextResolverFactory(){ + } - final Iterable providers = Providers.getAllProviders(injectionManager, ContextResolver.class); - for (final ContextResolver provider : providers) { - final List ms = MediaTypes.createFrom(provider.getClass().getAnnotation(Produces.class)); + private void initialize(List contextResolvers) { + Map>> rs = new HashMap<>(); - final Type type = getParameterizedType(provider.getClass()); + for (ContextResolver provider : contextResolvers) { + List ms = MediaTypes.createFrom(provider.getClass().getAnnotation(Produces.class)); + Type type = getParameterizedType(provider.getClass()); Map> mr = rs.get(type); if (mr == null) { - mr = new HashMap>(); + mr = new HashMap<>(); rs.put(type, mr); } - for (final MediaType m : ms) { + for (MediaType m : ms) { List crl = mr.get(m); if (crl == null) { - crl = new ArrayList(); + crl = new ArrayList<>(); mr.put(m, crl); } crl.add(provider); @@ -123,14 +129,12 @@ public ContextResolverFactory(final InjectionManager injectionManager) { // Reduce set of two or more context resolvers for same type and // media type - for (final Map.Entry>> e : rs.entrySet()) { - final Map mr = new KeyComparatorHashMap( - 4, MessageBodyFactory.MEDIA_TYPE_KEY_COMPARATOR); + for (Map.Entry>> e : rs.entrySet()) { + Map mr = new KeyComparatorHashMap<>(4, MessageBodyFactory.MEDIA_TYPE_KEY_COMPARATOR); resolver.put(e.getKey(), mr); + cache.put(e.getKey(), new ConcurrentHashMap<>(4)); - cache.put(e.getKey(), new ConcurrentHashMap(4)); - - for (final Map.Entry> f : e.getValue().entrySet()) { + for (Map.Entry> f : e.getValue().entrySet()) { mr.put(f.getKey(), reduce(f.getValue())); } } @@ -191,7 +195,7 @@ ContextResolver reduce() { } private static List removeNull(final ContextResolver... cra) { - final List crl = new ArrayList(cra.length); + final List crl = new ArrayList<>(cra.length); for (final ContextResolver cr : cra) { if (cr != null) { crl.add(cr); diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/ExceptionMapperFactory.java b/core-common/src/main/java/org/glassfish/jersey/internal/ExceptionMapperFactory.java index 9489a55c43..efc39a1aa4 100644 --- a/core-common/src/main/java/org/glassfish/jersey/internal/ExceptionMapperFactory.java +++ b/core-common/src/main/java/org/glassfish/jersey/internal/ExceptionMapperFactory.java @@ -55,17 +55,18 @@ import javax.ws.rs.ProcessingException; import javax.ws.rs.ext.ExceptionMapper; -import javax.inject.Inject; -import javax.inject.Singleton; - -import org.glassfish.jersey.internal.inject.AbstractBinder; +import org.glassfish.jersey.internal.inject.Bindings; import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.internal.inject.InstanceBinding; import org.glassfish.jersey.internal.inject.Providers; +import org.glassfish.jersey.internal.inject.ServiceHolder; import org.glassfish.jersey.internal.util.ReflectionHelper; import org.glassfish.jersey.internal.util.collection.ClassTypePair; +import org.glassfish.jersey.internal.util.collection.LazyValue; +import org.glassfish.jersey.internal.util.collection.Value; +import org.glassfish.jersey.internal.util.collection.Values; import org.glassfish.jersey.spi.ExceptionMappers; import org.glassfish.jersey.spi.ExtendedExceptionMapper; -import org.glassfish.jersey.internal.inject.ServiceHolder; /** * {@link ExceptionMappers Exception mappers} implementation that aggregates @@ -82,13 +83,21 @@ public class ExceptionMapperFactory implements ExceptionMappers { private static final Logger LOGGER = Logger.getLogger(ExceptionMapperFactory.class.getName()); /** - * Exception mapper factory injection binder. + * Configurator which initializes and register {@link ExceptionMappers} instance into {@link InjectionManager} and + * {@link BootstrapBag}. + * + * @author Petr Bouda (petr.bouda at oracle.com) */ - public static class Binder extends AbstractBinder { + public static class ExceptionMappersConfigurator implements BootstrapConfigurator { @Override - protected void configure() { - bindAsContract(ExceptionMapperFactory.class).to(ExceptionMappers.class).in(Singleton.class); + public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) { + ExceptionMapperFactory exceptionMapperFactory = new ExceptionMapperFactory(injectionManager); + InstanceBinding binding = + Bindings.service(exceptionMapperFactory) + .to(ExceptionMappers.class); + injectionManager.register(binding); + bootstrapBag.setExceptionMappers(exceptionMapperFactory); } } @@ -103,7 +112,7 @@ public ExceptionMapperType(final ServiceHolder mapper, final Cl } } - private final Set exceptionMapperTypes = new LinkedHashSet(); + private final Value> exceptionMapperTypes; @Override @SuppressWarnings("unchecked") @@ -121,7 +130,7 @@ private ExceptionMapper find(final Class type, final ExceptionMapper mapper = null; int minDistance = Integer.MAX_VALUE; - for (final ExceptionMapperType mapperType : exceptionMapperTypes) { + for (final ExceptionMapperType mapperType : exceptionMapperTypes.get()) { final int d = distance(type, mapperType.exceptionType); if (d >= 0 && d <= minDistance) { final ExceptionMapper candidate = mapperType.mapper.getInstance(); @@ -168,40 +177,56 @@ private boolean isPreferredCandidate(final T exceptionInst * * @param injectionManager injection manager. */ - @Inject - public ExceptionMapperFactory(InjectionManager injectionManager) { - Collection> mapperHandles = - Providers.getAllServiceHolders(injectionManager, ExceptionMapper.class); - - for (ServiceHolder mapperHandle: mapperHandles) { - ExceptionMapper mapper = mapperHandle.getInstance(); - - if (Proxy.isProxyClass(mapper.getClass())) { - SortedSet> mapperTypes = - new TreeSet<>((o1, o2) -> o1.isAssignableFrom(o2) ? -1 : 1); - - Set contracts = mapperHandle.getContractTypes(); - for (final Type contract : contracts) { - if (contract instanceof Class - && ExceptionMapper.class.isAssignableFrom((Class) contract) && contract != ExceptionMapper.class) { - //noinspection unchecked - mapperTypes.add((Class) contract); + ExceptionMapperFactory(InjectionManager injectionManager) { + exceptionMapperTypes = createLazyExceptionMappers(injectionManager); + } + + /** + * Returns {@link LazyValue} of exception mappers that delays their creation to the first use. The exception mappers won't be + * created during bootstrap but at the time of the first call. + * + * @param injectionManager injection manager that may not be fully populated at the time of a function call therefore the + * result is wrapped to lazy value. + * @return lazy value of exception mappers. + */ + private LazyValue> createLazyExceptionMappers(InjectionManager injectionManager) { + return Values.lazy((Value>) () -> { + Collection> mapperHandles = + Providers.getAllServiceHolders(injectionManager, ExceptionMapper.class); + + Set exceptionMapperTypes = new LinkedHashSet<>(); + for (ServiceHolder mapperHandle: mapperHandles) { + ExceptionMapper mapper = mapperHandle.getInstance(); + + if (Proxy.isProxyClass(mapper.getClass())) { + SortedSet> mapperTypes = + new TreeSet<>((o1, o2) -> o1.isAssignableFrom(o2) ? -1 : 1); + + Set contracts = mapperHandle.getContractTypes(); + for (final Type contract : contracts) { + if (contract instanceof Class + && ExceptionMapper.class.isAssignableFrom((Class) contract) + && contract != ExceptionMapper.class) { + //noinspection unchecked + mapperTypes.add((Class) contract); + } } - } - if (!mapperTypes.isEmpty()) { - final Class c = getExceptionType(mapperTypes.first()); + if (!mapperTypes.isEmpty()) { + final Class c = getExceptionType(mapperTypes.first()); + if (c != null) { + exceptionMapperTypes.add(new ExceptionMapperType(mapperHandle, c)); + } + } + } else { + final Class c = getExceptionType(mapper.getClass()); if (c != null) { exceptionMapperTypes.add(new ExceptionMapperType(mapperHandle, c)); } } - } else { - final Class c = getExceptionType(mapper.getClass()); - if (c != null) { - exceptionMapperTypes.add(new ExceptionMapperType(mapperHandle, c)); - } } - } + return exceptionMapperTypes; + }); } private int distance(Class c, final Class emtc) { diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/RuntimeDelegateImpl.java b/core-common/src/main/java/org/glassfish/jersey/internal/RuntimeDelegateImpl.java index 124c2c9917..d697195782 100644 --- a/core-common/src/main/java/org/glassfish/jersey/internal/RuntimeDelegateImpl.java +++ b/core-common/src/main/java/org/glassfish/jersey/internal/RuntimeDelegateImpl.java @@ -60,7 +60,7 @@ public class RuntimeDelegateImpl extends AbstractRuntimeDelegate { public RuntimeDelegateImpl() { - super(Injections.createInjectionManager("jersey-common-rd-locator", new MessagingBinders.HeaderDelegateProviders())); + super(Injections.createInjectionManager(new MessagingBinders.HeaderDelegateProviders())); } @Override diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractFuture.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractFuture.java new file mode 100644 index 0000000000..9da9704c2e --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractFuture.java @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.AbstractQueuedSynchronizer; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; + +/** + * An abstract implementation of the {@link ListenableFuture} interface. This + * class is preferable to {@link java.util.concurrent.FutureTask} for two + * reasons: It implements {@code ListenableFuture}, and it does not implement + * {@code Runnable}. (If you want a {@code Runnable} implementation of {@code + * ListenableFuture}, create a {@link ListenableFutureTask}, or submit your + * tasks to a {@link ListeningExecutorService}.) + *

+ *

This class implements all methods in {@code ListenableFuture}. + * Subclasses should provide a way to set the result of the computation through + * the protected methods {@link #set(Object)} and + * {@link #setException(Throwable)}. Subclasses may also override {@link + * #interruptTask()}, which will be invoked automatically if a call to {@link + * #cancel(boolean) cancel(true)} succeeds in canceling the future. + *

+ *

{@code AbstractFuture} uses an {@link AbstractQueuedSynchronizer} to deal + * with concurrency issues and guarantee thread safety. + *

+ *

The state changing methods all return a boolean indicating success or + * failure in changing the future's state. Valid states are running, + * completed, failed, or cancelled. + *

+ *

This class uses an {@link ExecutionList} to guarantee that all registered + * listeners will be executed, either when the future finishes or, for listeners + * that are added after the future completes, immediately. + * {@code Runnable}-{@code Executor} pairs are stored in the execution list but + * are not necessarily executed in the order in which they were added. (If a + * listener is added after the Future is complete, it will be executed + * immediately, even if earlier listeners have not been executed. Additionally, + * executors need not guarantee FIFO execution, or different listeners may run + * in different executors.) + * + * @author Sven Mawson + * @since 1.0 + */ +public abstract class AbstractFuture implements ListenableFuture { + + /** + * Synchronization control for AbstractFutures. + */ + private final Sync sync = new Sync(); + + // The execution list to hold our executors. + private final ExecutionList executionList = new ExecutionList(); + + /** + * Constructor for use by subclasses. + */ + AbstractFuture() { + } + + /* + * Improve the documentation of when InterruptedException is thrown. Our + * behavior matches the JDK's, but the JDK's documentation is misleading. + */ + + private static CancellationException cancellationExceptionWithCause( + Throwable cause) { + CancellationException exception = new CancellationException("Task was cancelled."); + exception.initCause(cause); + return exception; + } + + /* + * Improve the documentation of when InterruptedException is thrown. Our + * behavior matches the JDK's, but the JDK's documentation is misleading. + */ + + /** + * {@inheritDoc} + *

+ *

The default {@link AbstractFuture} implementation throws {@code + * InterruptedException} if the current thread is interrupted before or during + * the call, even if the value is already available. + * + * @throws InterruptedException if the current thread was interrupted before + * or during the call (optional but recommended). + * @throws CancellationException {@inheritDoc} + */ + @Override + public V get(long timeout, TimeUnit unit) throws InterruptedException, + TimeoutException, ExecutionException { + return sync.get(unit.toNanos(timeout)); + } + + /** + * {@inheritDoc} + *

+ *

The default {@link AbstractFuture} implementation throws {@code + * InterruptedException} if the current thread is interrupted before or during + * the call, even if the value is already available. + * + * @throws InterruptedException if the current thread was interrupted before + * or during the call (optional but recommended). + * @throws CancellationException {@inheritDoc} + */ + @Override + public V get() throws InterruptedException, ExecutionException { + return sync.get(); + } + + @Override + public boolean isDone() { + return sync.isDone(); + } + + @Override + public boolean isCancelled() { + return sync.isCancelled(); + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + if (!sync.cancel(mayInterruptIfRunning)) { + return false; + } + executionList.execute(); + if (mayInterruptIfRunning) { + interruptTask(); + } + return true; + } + + /** + * Subclasses can override this method to implement interruption of the + * future's computation. The method is invoked automatically by a successful + * call to {@link #cancel(boolean) cancel(true)}. + *

+ *

The default implementation does nothing. + * + * @since 10.0 + */ + private void interruptTask() { + } + + /** + * Returns true if this future was cancelled with {@code + * mayInterruptIfRunning} set to {@code true}. + * + * @since 14.0 + */ + final boolean wasInterrupted() { + return sync.wasInterrupted(); + } + + /** + * {@inheritDoc} + * + * @since 10.0 + */ + @Override + public void addListener(Runnable listener, Executor exec) { + executionList.add(listener, exec); + } + + /** + * Subclasses should invoke this method to set the result of the computation + * to {@code value}. This will set the state of the future to + * {@link AbstractFuture.Sync#COMPLETED} and invoke the listeners if the + * state was successfully changed. + * + * @param value the value that was the result of the task. + * @return true if the state was successfully changed. + */ + boolean set(V value) { + boolean result = sync.set(value); + if (result) { + executionList.execute(); + } + return result; + } + + /** + * Subclasses should invoke this method to set the result of the computation + * to an error, {@code throwable}. This will set the state of the future to + * {@link AbstractFuture.Sync#COMPLETED} and invoke the listeners if the + * state was successfully changed. + * + * @param throwable the exception that the task failed with. + * @return true if the state was successfully changed. + */ + boolean setException(Throwable throwable) { + boolean result = sync.setException(checkNotNull(throwable)); + if (result) { + executionList.execute(); + } + return result; + } + + /** + *

Following the contract of {@link AbstractQueuedSynchronizer} we create a + * private subclass to hold the synchronizer. This synchronizer is used to + * implement the blocking and waiting calls as well as to handle state changes + * in a thread-safe manner. The current state of the future is held in the + * Sync state, and the lock is released whenever the state changes to + * {@link #COMPLETED}, {@link #CANCELLED}, or {@link #INTERRUPTED} + *

+ *

To avoid races between threads doing release and acquire, we transition + * to the final state in two steps. One thread will successfully CAS from + * RUNNING to COMPLETING, that thread will then set the result of the + * computation, and only then transition to COMPLETED, CANCELLED, or + * INTERRUPTED. + *

+ *

We don't use the integer argument passed between acquire methods so we + * pass around a -1 everywhere. + */ + static final class Sync extends AbstractQueuedSynchronizer { + + /* Valid states. */ + static final int RUNNING = 0; + static final int COMPLETING = 1; + static final int COMPLETED = 2; + static final int CANCELLED = 4; + static final int INTERRUPTED = 8; + private static final long serialVersionUID = 0L; + private V value; + private Throwable exception; + + /* + * Acquisition succeeds if the future is done, otherwise it fails. + */ + @Override + protected int tryAcquireShared(int ignored) { + if (isDone()) { + return 1; + } + return -1; + } + + /* + * We always allow a release to go through, this means the state has been + * successfully changed and the result is available. + */ + @Override + protected boolean tryReleaseShared(int finalState) { + setState(finalState); + return true; + } + + /** + * Blocks until the task is complete or the timeout expires. Throws a + * {@link TimeoutException} if the timer expires, otherwise behaves like + * {@link #get()}. + */ + V get(long nanos) throws TimeoutException, CancellationException, + ExecutionException, InterruptedException { + + // Attempt to acquire the shared lock with a timeout. + if (!tryAcquireSharedNanos(-1, nanos)) { + throw new TimeoutException("Timeout waiting for task."); + } + + return getValue(); + } + + /** + * Blocks until {@link #complete(Object, Throwable, int)} has been + * successfully called. Throws a {@link CancellationException} if the task + * was cancelled, or a {@link ExecutionException} if the task completed with + * an error. + */ + V get() throws CancellationException, ExecutionException, + InterruptedException { + + // Acquire the shared lock allowing interruption. + acquireSharedInterruptibly(-1); + return getValue(); + } + + /** + * Implementation of the actual value retrieval. Will return the value + * on success, an exception on failure, a cancellation on cancellation, or + * an illegal state if the synchronizer is in an invalid state. + */ + private V getValue() throws CancellationException, ExecutionException { + int state = getState(); + switch (state) { + case COMPLETED: + if (exception != null) { + throw new ExecutionException(exception); + } else { + return value; + } + + case CANCELLED: + case INTERRUPTED: + throw cancellationExceptionWithCause( + exception); + + default: + throw new IllegalStateException( + "Error, synchronizer in invalid state: " + state); + } + } + + /** + * Checks if the state is {@link #COMPLETED}, {@link #CANCELLED}, or {@link + * INTERRUPTED}. + */ + boolean isDone() { + return (getState() & (COMPLETED | CANCELLED | INTERRUPTED)) != 0; + } + + /** + * Checks if the state is {@link #CANCELLED} or {@link #INTERRUPTED}. + */ + boolean isCancelled() { + return (getState() & (CANCELLED | INTERRUPTED)) != 0; + } + + /** + * Checks if the state is {@link #INTERRUPTED}. + */ + boolean wasInterrupted() { + return getState() == INTERRUPTED; + } + + /** + * Transition to the COMPLETED state and set the value. + */ + boolean set(V v) { + return complete(v, null, COMPLETED); + } + + /** + * Transition to the COMPLETED state and set the exception. + */ + boolean setException(Throwable t) { + return complete(null, t, COMPLETED); + } + + /** + * Transition to the CANCELLED or INTERRUPTED state. + */ + boolean cancel(boolean interrupt) { + return complete(null, null, interrupt ? INTERRUPTED : CANCELLED); + } + + /** + * Implementation of completing a task. Either {@code v} or {@code t} will + * be set but not both. The {@code finalState} is the state to change to + * from {@link #RUNNING}. If the state is not in the RUNNING state we + * return {@code false} after waiting for the state to be set to a valid + * final state ({@link #COMPLETED}, {@link #CANCELLED}, or {@link + * #INTERRUPTED}). + * + * @param v the value to set as the result of the computation. + * @param t the exception to set as the result of the computation. + * @param finalState the state to transition to. + */ + private boolean complete(V v, Throwable t, + int finalState) { + boolean doCompletion = compareAndSetState(RUNNING, COMPLETING); + if (doCompletion) { + // If this thread successfully transitioned to COMPLETING, set the value + // and exception and then release to the final state. + this.value = v; + // Don't actually construct a CancellationException until necessary. + this.exception = ((finalState & (CANCELLED | INTERRUPTED)) != 0) + ? new CancellationException("Future.cancel() was called.") : t; + releaseShared(finalState); + } else if (getState() == COMPLETING) { + // If some other thread is currently completing the future, block until + // they are done so we can guarantee completion. + acquireShared(-1); + } + return doCompletion; + } + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractIndexedListIterator.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractIndexedListIterator.java new file mode 100644 index 0000000000..b9302af1a8 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractIndexedListIterator.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.ListIterator; +import java.util.NoSuchElementException; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkPositionIndex; + +/** + * This class provides a skeletal implementation of the {@link ListIterator} + * interface across a fixed number of elements that may be retrieved by + * position. It does not support {@link #remove}, {@link #set}, or {@link #add}. + * + * @author Jared Levy + */ +abstract class AbstractIndexedListIterator + extends UnmodifiableListIterator { + private final int size; + private int position; + + /** + * Constructs an iterator across a sequence of the given size with the given + * initial position. That is, the first call to {@link #nextIndex()} will + * return {@code position}, and the first call to {@link #next()} will return + * the element at that index, if available. Calls to {@link #previous()} can + * retrieve the preceding {@code position} elements. + * + * @throws IndexOutOfBoundsException if {@code position} is negative or is + * greater than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + AbstractIndexedListIterator(int size, int position) { + checkPositionIndex(position, size); + this.size = size; + this.position = position; + } + + /** + * Returns the element with the specified index. This method is called by + * {@link #next()}. + */ + protected abstract E get(int index); + + @Override + public final boolean hasNext() { + return position < size; + } + + @Override + public final E next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + return get(position++); + } + + @Override + public final int nextIndex() { + return position; + } + + @Override + public final boolean hasPrevious() { + return position > 0; + } + + @Override + public final E previous() { + if (!hasPrevious()) { + throw new NoSuchElementException(); + } + return get(--position); + } + + @Override + public final int previousIndex() { + return position - 1; + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractIterator.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractIterator.java new file mode 100644 index 0000000000..c31238592f --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractIterator.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.NoSuchElementException; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkState; + +/** + * This class provides a skeletal implementation of the {@code Iterator} + * interface, to make this interface easier to implement for certain types of + * data sources. + *

+ *

{@code Iterator} requires its implementations to support querying the + * end-of-data status without changing the iterator's state, using the {@link + * #hasNext} method. But many data sources, such as {@link + * java.io.Reader#read()}, do not expose this information; the only way to + * discover whether there is any data left is by trying to retrieve it. These + * types of data sources are ordinarily difficult to write iterators for. But + * using this class, one must implement only the {@link #computeNext} method, + * and invoke the {@link #endOfData} method when appropriate. + *

+ *

Another example is an iterator that skips over null elements in a backing + * iterator. This could be implemented as:

   {@code
+ * 

+ * public static Iterator skipNulls(final Iterator in) { + * return new AbstractIterator() { + * protected String computeNext() { + * while (in.hasNext()) { + * String s = in.next(); + * if (s != null) { + * return s; + * } + * } + * return endOfData(); + * } + * }; + * }}

+ *

+ *

This class supports iterators that include null elements. + * + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ +// When making changes to this class, please also update the copy at +// org.glassfish.jersey.internal.guava.common.base.AbstractIterator +public abstract class AbstractIterator extends UnmodifiableIterator { + private State state = State.NOT_READY; + private T next; + + /** + * Constructor for use by subclasses. + */ + AbstractIterator() { + } + + /** + * Returns the next element. Note: the implementation must call {@link + * #endOfData()} when there are no elements left in the iteration. Failure to + * do so could result in an infinite loop. + *

+ *

The initial invocation of {@link #hasNext()} or {@link #next()} calls + * this method, as does the first invocation of {@code hasNext} or {@code + * next} following each successful call to {@code next}. Once the + * implementation either invokes {@code endOfData} or throws an exception, + * {@code computeNext} is guaranteed to never be called again. + *

+ *

If this method throws an exception, it will propagate outward to the + * {@code hasNext} or {@code next} invocation that invoked this method. Any + * further attempts to use the iterator will result in an {@link + * IllegalStateException}. + *

+ *

The implementation of this method may not invoke the {@code hasNext}, + * {@code next}, or {@link #peek()} methods on this instance; if it does, an + * {@code IllegalStateException} will result. + * + * @return the next element if there was one. If {@code endOfData} was called + * during execution, the return value will be ignored. + * @throws RuntimeException if any unrecoverable error happens. This exception + * will propagate outward to the {@code hasNext()}, {@code next()}, or + * {@code peek()} invocation that invoked this method. Any further + * attempts to use the iterator will result in an + * {@link IllegalStateException}. + */ + protected abstract T computeNext(); + + /** + * Implementations of {@link #computeNext} must invoke this method when + * there are no elements left in the iteration. + * + * @return {@code null}; a convenience so your {@code computeNext} + * implementation can use the simple statement {@code return endOfData();} + */ + final T endOfData() { + state = State.DONE; + return null; + } + + @Override + public final boolean hasNext() { + checkState(state != State.FAILED); + switch (state) { + case DONE: + return false; + case READY: + return true; + default: + } + return tryToComputeNext(); + } + + private boolean tryToComputeNext() { + state = State.FAILED; // temporary pessimism + next = computeNext(); + if (state != State.DONE) { + state = State.READY; + return true; + } + return false; + } + + @Override + public final T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + state = State.NOT_READY; + T result = next; + next = null; + return result; + } + + private enum State { + /** + * We have computed the next element and haven't returned it yet. + */ + READY, + + /** + * We haven't yet computed or have already returned the element. + */ + NOT_READY, + + /** + * We have reached the end of the data and are finished. + */ + DONE, + + /** + * We've suffered an exception and are kaput. + */ + FAILED, + } + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractListMultimap.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractListMultimap.java new file mode 100644 index 0000000000..2a03be7f9a --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractListMultimap.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * Basic implementation of the {@link ListMultimap} interface. It's a wrapper + * around {@link AbstractMapBasedMultimap} that converts the returned collections into + * {@code Lists}. The {@link #createCollection} method must return a {@code + * List}. + * + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +abstract class AbstractListMultimap + extends AbstractMapBasedMultimap implements ListMultimap { + private static final long serialVersionUID = 6588350623831699109L; + + /** + * Creates a new multimap that uses the provided map. + * + * @param map place to store the mapping from each key to its corresponding + * values + */ + AbstractListMultimap(Map> map) { + super(map); + } + + @Override + abstract List createCollection(); + + // Following Javadoc copied from ListMultimap. + + /** + * {@inheritDoc} + *

+ *

Because the values for a given key may have duplicates and follow the + * insertion ordering, this method returns a {@link List}, instead of the + * {@link Collection} specified in the {@link Multimap} interface. + */ + @Override + public List get(K key) { + return (List) super.get(key); + } + + /** + * {@inheritDoc} + *

+ *

Because the values for a given key may have duplicates and follow the + * insertion ordering, this method returns a {@link List}, instead of the + * {@link Collection} specified in the {@link Multimap} interface. + */ + @Override + public List removeAll(Object key) { + return (List) super.removeAll(key); + } + + /** + * Stores a key-value pair in the multimap. + * + * @param key key to store in the multimap + * @param value value to store in the multimap + * @return {@code true} always + */ + @Override + public boolean put(K key, V value) { + return super.put(key, value); + } + + /** + * {@inheritDoc} + *

+ *

Though the method signature doesn't say so explicitly, the returned map + * has {@link List} values. + */ + @Override + public Map> asMap() { + return super.asMap(); + } + + /** + * Compares the specified object to this multimap for equality. + *

+ *

Two {@code ListMultimap} instances are equal if, for each key, they + * contain the same values in the same order. If the value orderings disagree, + * the multimaps will not be considered equal. + */ + @Override + public boolean equals(Object object) { + return super.equals(object); + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractMapBasedMultimap.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractMapBasedMultimap.java new file mode 100644 index 0000000000..c7a4c8e575 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractMapBasedMultimap.java @@ -0,0 +1,1562 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.io.Serializable; +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.RandomAccess; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkArgument; +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; + +/** + * Basic implementation of the {@link Multimap} interface. This class represents + * a multimap as a map that associates each key with a collection of values. All + * methods of {@link Multimap} are supported, including those specified as + * optional in the interface. + *

+ *

To implement a multimap, a subclass must define the method {@link + * #createCollection()}, which creates an empty collection of values for a key. + *

+ *

The multimap constructor takes a map that has a single entry for each + * distinct key. When you insert a key-value pair with a key that isn't already + * in the multimap, {@code AbstractMapBasedMultimap} calls {@link #createCollection()} + * to create the collection of values for that key. The subclass should not call + * {@link #createCollection()} directly, and a new instance should be created + * every time the method is called. + *

+ *

For example, the subclass could pass a {@link java.util.TreeMap} during + * construction, and {@link #createCollection()} could return a {@link + * java.util.TreeSet}, in which case the multimap's iterators would propagate + * through the keys and values in sorted order. + *

+ *

Keys and values may be null, as long as the underlying collection classes + * support null elements. + *

+ *

The collections created by {@link #createCollection()} may or may not + * allow duplicates. If the collection, such as a {@link Set}, does not support + * duplicates, an added key-value pair will replace an existing pair with the + * same key and value, if such a pair is present. With collections like {@link + * List} that allow duplicates, the collection will keep the existing key-value + * pairs while adding a new pair. + *

+ *

This class is not threadsafe when any concurrent operations update the + * multimap, even if the underlying map and {@link #createCollection()} method + * return threadsafe classes. Concurrent read operations will work correctly. To + * allow concurrent update operations, wrap your multimap with a call to {@link + * Multimaps#synchronizedMultimap}. + *

+ *

For serialization to work, the subclass must specify explicit + * {@code readObject} and {@code writeObject} methods. + * + * @author Jared Levy + * @author Louis Wasserman + */ +abstract class AbstractMapBasedMultimap extends AbstractMultimap + implements Serializable { + /* + * Here's an outline of the overall design. + * + * The map variable contains the collection of values associated with each + * key. When a key-value pair is added to a multimap that didn't previously + * contain any values for that key, a new collection generated by + * createCollection is added to the map. That same collection instance + * remains in the map as long as the multimap has any values for the key. If + * all values for the key are removed, the key and collection are removed + * from the map. + * + * The get method returns a WrappedCollection, which decorates the collection + * in the map (if the key is present) or an empty collection (if the key is + * not present). When the collection delegate in the WrappedCollection is + * empty, the multimap may contain subsequently added values for that key. To + * handle that situation, the WrappedCollection checks whether map contains + * an entry for the provided key, and if so replaces the delegate. + */ + + private static final long serialVersionUID = 2447537837011683357L; + private transient Map> map; + private transient int totalSize; + + /** + * Creates a new multimap that uses the provided map. + * + * @param map place to store the mapping from each key to its corresponding + * values + * @throws IllegalArgumentException if {@code map} is not empty + */ + AbstractMapBasedMultimap(Map> map) { + checkArgument(map.isEmpty()); + this.map = map; + } + + /** + * Used during deserialization only. + */ + final void setMap(Map> map) { + this.map = map; + totalSize = 0; + for (Collection values : map.values()) { + checkArgument(!values.isEmpty()); + totalSize += values.size(); + } + } + + /** + * Creates an unmodifiable, empty collection of values. + *

+ *

This is used in {@link #removeAll} on an empty key. + */ + Collection createUnmodifiableEmptyCollection() { + return unmodifiableCollectionSubclass(createCollection()); + } + + /** + * Creates the collection of values for a single key. + *

+ *

Collections with weak, soft, or phantom references are not supported. + * Each call to {@code createCollection} should create a new instance. + *

+ *

The returned collection class determines whether duplicate key-value + * pairs are allowed. + * + * @return an empty collection of values + */ + abstract Collection createCollection(); + + /** + * Creates the collection of values for an explicitly provided key. By + * default, it simply calls {@link #createCollection()}, which is the correct + * behavior for most implementations. The {@link LinkedHashMultimap} class + * overrides it. + * + * @param key key to associate with values in the collection + * @return an empty collection of values + */ + Collection createCollection(K key) { + return createCollection(); + } + + // Query Operations + + Map> backingMap() { + return map; + } + + @Override + public int size() { + return totalSize; + } + + // Modification Operations + + @Override + public boolean containsKey(Object key) { + return map.containsKey(key); + } + + @Override + public boolean put(K key, V value) { + Collection collection = map.get(key); + if (collection == null) { + collection = createCollection(key); + if (collection.add(value)) { + totalSize++; + map.put(key, collection); + return true; + } else { + throw new AssertionError("New Collection violated the Collection spec"); + } + } else if (collection.add(value)) { + totalSize++; + return true; + } else { + return false; + } + } + + // Bulk Operations + + /** + * {@inheritDoc} + *

+ *

The returned collection is immutable. + */ + @Override + public Collection removeAll(Object key) { + Collection collection = map.remove(key); + + if (collection == null) { + return createUnmodifiableEmptyCollection(); + } + + Collection output = createCollection(); + output.addAll(collection); + totalSize -= collection.size(); + collection.clear(); + + return unmodifiableCollectionSubclass(output); + } + + Collection unmodifiableCollectionSubclass(Collection collection) { + // We don't deal with NavigableSet here yet for GWT reasons -- instead, + // non-GWT TreeMultimap explicitly overrides this and uses NavigableSet. + if (collection instanceof SortedSet) { + return Collections.unmodifiableSortedSet((SortedSet) collection); + } else if (collection instanceof Set) { + return Collections.unmodifiableSet((Set) collection); + } else if (collection instanceof List) { + return Collections.unmodifiableList((List) collection); + } else { + return Collections.unmodifiableCollection(collection); + } + } + + // Views + + @Override + public void clear() { + // Clear each collection, to make previously returned collections empty. + for (Collection collection : map.values()) { + collection.clear(); + } + map.clear(); + totalSize = 0; + } + + /** + * {@inheritDoc} + *

+ *

The returned collection is not serializable. + */ + @Override + public Collection get(K key) { + Collection collection = map.get(key); + if (collection == null) { + collection = createCollection(key); + } + return wrapCollection(key, collection); + } + + /** + * Generates a decorated collection that remains consistent with the values in + * the multimap for the provided key. Changes to the multimap may alter the + * returned collection, and vice versa. + */ + Collection wrapCollection(K key, Collection collection) { + // We don't deal with NavigableSet here yet for GWT reasons -- instead, + // non-GWT TreeMultimap explicitly overrides this and uses NavigableSet. + if (collection instanceof SortedSet) { + return new WrappedSortedSet(key, (SortedSet) collection, null); + } else if (collection instanceof Set) { + return new WrappedSet(key, (Set) collection); + } else if (collection instanceof List) { + return wrapList(key, (List) collection, null); + } else { + return new WrappedCollection(key, collection, null); + } + } + + private List wrapList( + K key, List list, WrappedCollection ancestor) { + return (list instanceof RandomAccess) + ? new RandomAccessWrappedList(key, list, ancestor) + : new WrappedList(key, list, ancestor); + } + + private Iterator iteratorOrListIterator(Collection collection) { + return (collection instanceof List) + ? ((List) collection).listIterator() + : collection.iterator(); + } + + @Override + Set createKeySet() { + // TreeMultimap uses NavigableKeySet explicitly, but we don't handle that here for GWT + // compatibility reasons + return (map instanceof SortedMap) + ? new SortedKeySet((SortedMap>) map) : new KeySet(map); + } + + /** + * Removes all values for the provided key. Unlike {@link #removeAll}, it + * returns the number of removed mappings. + */ + private int removeValuesForKey(Object key) { + Collection collection = Maps.safeRemove(map, key); + + int count = 0; + if (collection != null) { + count = collection.size(); + collection.clear(); + totalSize -= count; + } + return count; + } + + /** + * {@inheritDoc} + *

+ *

The iterator generated by the returned collection traverses the values + * for one key, followed by the values of a second key, and so on. + */ + @Override + public Collection values() { + return super.values(); + } + + @Override + Iterator valueIterator() { + return new Itr() { + @Override + V output(K key, V value) { + return value; + } + }; + } + + /** + * {@inheritDoc} + *

+ *

The iterator generated by the returned collection traverses the values + * for one key, followed by the values of a second key, and so on. + *

+ *

Each entry is an immutable snapshot of a key-value mapping in the + * multimap, taken at the time the entry is returned by a method call to the + * collection or its iterator. + */ + @Override + public Collection> entries() { + return super.entries(); + } + + /** + * Returns an iterator across all key-value map entries, used by {@code + * entries().iterator()} and {@code values().iterator()}. The default + * behavior, which traverses the values for one key, the values for a second + * key, and so on, suffices for most {@code AbstractMapBasedMultimap} implementations. + * + * @return an iterator across map entries + */ + @Override + Iterator> entryIterator() { + return new Itr>() { + @Override + Entry output(K key, V value) { + return Maps.immutableEntry(key, value); + } + }; + } + + @Override + Map> createAsMap() { + // TreeMultimap uses NavigableAsMap explicitly, but we don't handle that here for GWT + // compatibility reasons + return (map instanceof SortedMap) + ? new SortedAsMap((SortedMap>) map) : new AsMap(map); + } + + /** + * Collection decorator that stays in sync with the multimap values for a key. + * There are two kinds of wrapped collections: full and subcollections. Both + * have a delegate pointing to the underlying collection class. + *

+ *

Full collections, identified by a null ancestor field, contain all + * multimap values for a given key. Its delegate is a value in {@link + * AbstractMapBasedMultimap#map} whenever the delegate is non-empty. The {@code + * refreshIfEmpty}, {@code removeIfEmpty}, and {@code addToMap} methods ensure + * that the {@code WrappedCollection} and map remain consistent. + *

+ *

A subcollection, such as a sublist, contains some of the values for a + * given key. Its ancestor field points to the full wrapped collection with + * all values for the key. The subcollection {@code refreshIfEmpty}, {@code + * removeIfEmpty}, and {@code addToMap} methods call the corresponding methods + * of the full wrapped collection. + */ + private class WrappedCollection extends AbstractCollection { + final K key; + final WrappedCollection ancestor; + final Collection ancestorDelegate; + Collection delegate; + + WrappedCollection(K key, Collection delegate, + WrappedCollection ancestor) { + this.key = key; + this.delegate = delegate; + this.ancestor = ancestor; + this.ancestorDelegate + = (ancestor == null) ? null : ancestor.getDelegate(); + } + + /** + * If the delegate collection is empty, but the multimap has values for the + * key, replace the delegate with the new collection for the key. + *

+ *

For a subcollection, refresh its ancestor and validate that the + * ancestor delegate hasn't changed. + */ + void refreshIfEmpty() { + if (ancestor != null) { + ancestor.refreshIfEmpty(); + if (ancestor.getDelegate() != ancestorDelegate) { + throw new ConcurrentModificationException(); + } + } else if (delegate.isEmpty()) { + Collection newDelegate = map.get(key); + if (newDelegate != null) { + delegate = newDelegate; + } + } + } + + /** + * If collection is empty, remove it from {@code AbstractMapBasedMultimap.this.map}. + * For subcollections, check whether the ancestor collection is empty. + */ + void removeIfEmpty() { + if (ancestor != null) { + ancestor.removeIfEmpty(); + } else if (delegate.isEmpty()) { + map.remove(key); + } + } + + K getKey() { + return key; + } + + /** + * Add the delegate to the map. Other {@code WrappedCollection} methods + * should call this method after adding elements to a previously empty + * collection. + *

+ *

Subcollection add the ancestor's delegate instead. + */ + void addToMap() { + if (ancestor != null) { + ancestor.addToMap(); + } else { + map.put(key, delegate); + } + } + + @Override + public int size() { + refreshIfEmpty(); + return delegate.size(); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + refreshIfEmpty(); + return delegate.equals(object); + } + + @Override + public int hashCode() { + refreshIfEmpty(); + return delegate.hashCode(); + } + + @Override + public String toString() { + refreshIfEmpty(); + return delegate.toString(); + } + + Collection getDelegate() { + return delegate; + } + + @Override + public Iterator iterator() { + refreshIfEmpty(); + return new WrappedIterator(); + } + + @Override + public boolean add(V value) { + refreshIfEmpty(); + boolean wasEmpty = delegate.isEmpty(); + boolean changed = delegate.add(value); + if (changed) { + totalSize++; + if (wasEmpty) { + addToMap(); + } + } + return changed; + } + + WrappedCollection getAncestor() { + return ancestor; + } + + @Override + public boolean addAll(Collection collection) { + if (collection.isEmpty()) { + return false; + } + int oldSize = size(); // calls refreshIfEmpty + boolean changed = delegate.addAll(collection); + if (changed) { + int newSize = delegate.size(); + totalSize += (newSize - oldSize); + if (oldSize == 0) { + addToMap(); + } + } + return changed; + } + + // The following methods are provided for better performance. + + @Override + public boolean contains(Object o) { + refreshIfEmpty(); + return delegate.contains(o); + } + + @Override + public boolean containsAll(Collection c) { + refreshIfEmpty(); + return delegate.containsAll(c); + } + + @Override + public void clear() { + int oldSize = size(); // calls refreshIfEmpty + if (oldSize == 0) { + return; + } + delegate.clear(); + totalSize -= oldSize; + removeIfEmpty(); // maybe shouldn't be removed if this is a sublist + } + + @Override + public boolean remove(Object o) { + refreshIfEmpty(); + boolean changed = delegate.remove(o); + if (changed) { + totalSize--; + removeIfEmpty(); + } + return changed; + } + + @Override + public boolean removeAll(Collection c) { + if (c.isEmpty()) { + return false; + } + int oldSize = size(); // calls refreshIfEmpty + boolean changed = delegate.removeAll(c); + if (changed) { + int newSize = delegate.size(); + totalSize += (newSize - oldSize); + removeIfEmpty(); + } + return changed; + } + + @Override + public boolean retainAll(Collection c) { + checkNotNull(c); + int oldSize = size(); // calls refreshIfEmpty + boolean changed = delegate.retainAll(c); + if (changed) { + int newSize = delegate.size(); + totalSize += (newSize - oldSize); + removeIfEmpty(); + } + return changed; + } + + /** + * Collection iterator for {@code WrappedCollection}. + */ + class WrappedIterator implements Iterator { + final Iterator delegateIterator; + final Collection originalDelegate = delegate; + + WrappedIterator() { + delegateIterator = iteratorOrListIterator(delegate); + } + + WrappedIterator(Iterator delegateIterator) { + this.delegateIterator = delegateIterator; + } + + /** + * If the delegate changed since the iterator was created, the iterator is + * no longer valid. + */ + void validateIterator() { + refreshIfEmpty(); + if (delegate != originalDelegate) { + throw new ConcurrentModificationException(); + } + } + + @Override + public boolean hasNext() { + validateIterator(); + return delegateIterator.hasNext(); + } + + @Override + public V next() { + validateIterator(); + return delegateIterator.next(); + } + + @Override + public void remove() { + delegateIterator.remove(); + totalSize--; + removeIfEmpty(); + } + + Iterator getDelegateIterator() { + validateIterator(); + return delegateIterator; + } + } + } + + /** + * Set decorator that stays in sync with the multimap values for a key. + */ + private class WrappedSet extends WrappedCollection implements Set { + WrappedSet(K key, Set delegate) { + super(key, delegate, null); + } + + @Override + public boolean removeAll(Collection c) { + if (c.isEmpty()) { + return false; + } + int oldSize = size(); // calls refreshIfEmpty + + // Guava issue 1013: AbstractSet and most JDK set implementations are + // susceptible to quadratic removeAll performance on lists; + // use a slightly smarter implementation here + boolean changed = Sets.removeAllImpl((Set) delegate, c); + if (changed) { + int newSize = delegate.size(); + totalSize += (newSize - oldSize); + removeIfEmpty(); + } + return changed; + } + } + + /** + * SortedSet decorator that stays in sync with the multimap values for a key. + */ + private class WrappedSortedSet extends WrappedCollection + implements SortedSet { + WrappedSortedSet(K key, SortedSet delegate, + WrappedCollection ancestor) { + super(key, delegate, ancestor); + } + + SortedSet getSortedSetDelegate() { + return (SortedSet) getDelegate(); + } + + @Override + public Comparator comparator() { + return getSortedSetDelegate().comparator(); + } + + @Override + public V first() { + refreshIfEmpty(); + return getSortedSetDelegate().first(); + } + + @Override + public V last() { + refreshIfEmpty(); + return getSortedSetDelegate().last(); + } + + @Override + public SortedSet headSet(V toElement) { + refreshIfEmpty(); + return new WrappedSortedSet( + getKey(), getSortedSetDelegate().headSet(toElement), + (getAncestor() == null) ? this : getAncestor()); + } + + @Override + public SortedSet subSet(V fromElement, V toElement) { + refreshIfEmpty(); + return new WrappedSortedSet( + getKey(), getSortedSetDelegate().subSet(fromElement, toElement), + (getAncestor() == null) ? this : getAncestor()); + } + + @Override + public SortedSet tailSet(V fromElement) { + refreshIfEmpty(); + return new WrappedSortedSet( + getKey(), getSortedSetDelegate().tailSet(fromElement), + (getAncestor() == null) ? this : getAncestor()); + } + } + + class WrappedNavigableSet extends WrappedSortedSet implements NavigableSet { + WrappedNavigableSet( + K key, NavigableSet delegate, WrappedCollection ancestor) { + super(key, delegate, ancestor); + } + + @Override + NavigableSet getSortedSetDelegate() { + return (NavigableSet) super.getSortedSetDelegate(); + } + + @Override + public V lower(V v) { + return getSortedSetDelegate().lower(v); + } + + @Override + public V floor(V v) { + return getSortedSetDelegate().floor(v); + } + + @Override + public V ceiling(V v) { + return getSortedSetDelegate().ceiling(v); + } + + @Override + public V higher(V v) { + return getSortedSetDelegate().higher(v); + } + + @Override + public V pollFirst() { + return Iterators.pollNext(iterator()); + } + + @Override + public V pollLast() { + return Iterators.pollNext(descendingIterator()); + } + + private NavigableSet wrap(NavigableSet wrapped) { + return new WrappedNavigableSet(key, wrapped, + (getAncestor() == null) ? this : getAncestor()); + } + + @Override + public NavigableSet descendingSet() { + return wrap(getSortedSetDelegate().descendingSet()); + } + + @Override + public Iterator descendingIterator() { + return new AbstractMapBasedMultimap.WrappedCollection.WrappedIterator(getSortedSetDelegate() + .descendingIterator()); + } + + @Override + public NavigableSet subSet( + V fromElement, boolean fromInclusive, V toElement, boolean toInclusive) { + return wrap( + getSortedSetDelegate().subSet(fromElement, fromInclusive, toElement, toInclusive)); + } + + @Override + public NavigableSet headSet(V toElement, boolean inclusive) { + return wrap(getSortedSetDelegate().headSet(toElement, inclusive)); + } + + @Override + public NavigableSet tailSet(V fromElement, boolean inclusive) { + return wrap(getSortedSetDelegate().tailSet(fromElement, inclusive)); + } + } + + /** + * List decorator that stays in sync with the multimap values for a key. + */ + private class WrappedList extends WrappedCollection implements List { + WrappedList(K key, List delegate, + WrappedCollection ancestor) { + super(key, delegate, ancestor); + } + + List getListDelegate() { + return (List) getDelegate(); + } + + @Override + public boolean addAll(int index, Collection c) { + if (c.isEmpty()) { + return false; + } + int oldSize = size(); // calls refreshIfEmpty + boolean changed = getListDelegate().addAll(index, c); + if (changed) { + int newSize = getDelegate().size(); + totalSize += (newSize - oldSize); + if (oldSize == 0) { + addToMap(); + } + } + return changed; + } + + @Override + public V get(int index) { + refreshIfEmpty(); + return getListDelegate().get(index); + } + + @Override + public V set(int index, V element) { + refreshIfEmpty(); + return getListDelegate().set(index, element); + } + + @Override + public void add(int index, V element) { + refreshIfEmpty(); + boolean wasEmpty = getDelegate().isEmpty(); + getListDelegate().add(index, element); + totalSize++; + if (wasEmpty) { + addToMap(); + } + } + + @Override + public V remove(int index) { + refreshIfEmpty(); + V value = getListDelegate().remove(index); + totalSize--; + removeIfEmpty(); + return value; + } + + @Override + public int indexOf(Object o) { + refreshIfEmpty(); + return getListDelegate().indexOf(o); + } + + @Override + public int lastIndexOf(Object o) { + refreshIfEmpty(); + return getListDelegate().lastIndexOf(o); + } + + @Override + public ListIterator listIterator() { + refreshIfEmpty(); + return new WrappedListIterator(); + } + + @Override + public ListIterator listIterator(int index) { + refreshIfEmpty(); + return new WrappedListIterator(index); + } + + @Override + public List subList(int fromIndex, int toIndex) { + refreshIfEmpty(); + return wrapList( + getKey(), + getListDelegate().subList(fromIndex, toIndex), + (getAncestor() == null) ? this : getAncestor()); + } + + /** + * ListIterator decorator. + */ + private class WrappedListIterator extends WrappedIterator + implements ListIterator { + WrappedListIterator() { + } + + public WrappedListIterator(int index) { + super(getListDelegate().listIterator(index)); + } + + private ListIterator getDelegateListIterator() { + return (ListIterator) getDelegateIterator(); + } + + @Override + public boolean hasPrevious() { + return getDelegateListIterator().hasPrevious(); + } + + @Override + public V previous() { + return getDelegateListIterator().previous(); + } + + @Override + public int nextIndex() { + return getDelegateListIterator().nextIndex(); + } + + @Override + public int previousIndex() { + return getDelegateListIterator().previousIndex(); + } + + @Override + public void set(V value) { + getDelegateListIterator().set(value); + } + + @Override + public void add(V value) { + boolean wasEmpty = isEmpty(); + getDelegateListIterator().add(value); + totalSize++; + if (wasEmpty) { + addToMap(); + } + } + } + } + + /** + * List decorator that stays in sync with the multimap values for a key and + * supports rapid random access. + */ + private class RandomAccessWrappedList extends WrappedList + implements RandomAccess { + RandomAccessWrappedList(K key, List delegate, + WrappedCollection ancestor) { + super(key, delegate, ancestor); + } + } + + /* + * TODO(kevinb): should we copy this javadoc to each concrete class, so that + * classes like LinkedHashMultimap that need to say something different are + * still able to {@inheritDoc} all the way from Multimap? + */ + + private class KeySet extends Maps.KeySet> { + KeySet(final Map> subMap) { + super(subMap); + } + + @Override + public Iterator iterator() { + final Iterator>> entryIterator + = map().entrySet().iterator(); + return new Iterator() { + Entry> entry; + + @Override + public boolean hasNext() { + return entryIterator.hasNext(); + } + + @Override + public K next() { + entry = entryIterator.next(); + return entry.getKey(); + } + + @Override + public void remove() { + CollectPreconditions.checkRemove(entry != null); + Collection collection = entry.getValue(); + entryIterator.remove(); + totalSize -= collection.size(); + collection.clear(); + } + }; + } + + // The following methods are included for better performance. + + @Override + public boolean remove(Object key) { + int count = 0; + Collection collection = map().remove(key); + if (collection != null) { + count = collection.size(); + collection.clear(); + totalSize -= count; + } + return count > 0; + } + + @Override + public void clear() { + Iterators.clear(iterator()); + } + + @Override + public boolean containsAll(Collection c) { + return map().keySet().containsAll(c); + } + + @Override + public boolean equals(Object object) { + return this == object || this.map().keySet().equals(object); + } + + @Override + public int hashCode() { + return map().keySet().hashCode(); + } + } + + private class SortedKeySet extends KeySet implements SortedSet { + + SortedKeySet(SortedMap> subMap) { + super(subMap); + } + + SortedMap> sortedMap() { + return (SortedMap>) super.map(); + } + + @Override + public Comparator comparator() { + return sortedMap().comparator(); + } + + @Override + public K first() { + return sortedMap().firstKey(); + } + + @Override + public SortedSet headSet(K toElement) { + return new SortedKeySet(sortedMap().headMap(toElement)); + } + + @Override + public K last() { + return sortedMap().lastKey(); + } + + @Override + public SortedSet subSet(K fromElement, K toElement) { + return new SortedKeySet(sortedMap().subMap(fromElement, toElement)); + } + + @Override + public SortedSet tailSet(K fromElement) { + return new SortedKeySet(sortedMap().tailMap(fromElement)); + } + } + + class NavigableKeySet extends SortedKeySet implements NavigableSet { + NavigableKeySet(NavigableMap> subMap) { + super(subMap); + } + + @Override + NavigableMap> sortedMap() { + return (NavigableMap>) super.sortedMap(); + } + + @Override + public K lower(K k) { + return sortedMap().lowerKey(k); + } + + @Override + public K floor(K k) { + return sortedMap().floorKey(k); + } + + @Override + public K ceiling(K k) { + return sortedMap().ceilingKey(k); + } + + @Override + public K higher(K k) { + return sortedMap().higherKey(k); + } + + @Override + public K pollFirst() { + return Iterators.pollNext(iterator()); + } + + @Override + public K pollLast() { + return Iterators.pollNext(descendingIterator()); + } + + @Override + public NavigableSet descendingSet() { + return new NavigableKeySet(sortedMap().descendingMap()); + } + + @Override + public Iterator descendingIterator() { + return descendingSet().iterator(); + } + + @Override + public NavigableSet headSet(K toElement) { + return headSet(toElement, false); + } + + @Override + public NavigableSet headSet(K toElement, boolean inclusive) { + return new NavigableKeySet(sortedMap().headMap(toElement, inclusive)); + } + + @Override + public NavigableSet subSet(K fromElement, K toElement) { + return subSet(fromElement, true, toElement, false); + } + + @Override + public NavigableSet subSet( + K fromElement, boolean fromInclusive, K toElement, boolean toInclusive) { + return new NavigableKeySet( + sortedMap().subMap(fromElement, fromInclusive, toElement, toInclusive)); + } + + @Override + public NavigableSet tailSet(K fromElement) { + return tailSet(fromElement, true); + } + + @Override + public NavigableSet tailSet(K fromElement, boolean inclusive) { + return new NavigableKeySet(sortedMap().tailMap(fromElement, inclusive)); + } + } + + private abstract class Itr implements Iterator { + final Iterator>> keyIterator; + K key; + Collection collection; + Iterator valueIterator; + + Itr() { + keyIterator = map.entrySet().iterator(); + key = null; + collection = null; + valueIterator = Iterators.emptyModifiableIterator(); + } + + abstract T output(K key, V value); + + @Override + public boolean hasNext() { + return keyIterator.hasNext() || valueIterator.hasNext(); + } + + @Override + public T next() { + if (!valueIterator.hasNext()) { + Entry> mapEntry = keyIterator.next(); + key = mapEntry.getKey(); + collection = mapEntry.getValue(); + valueIterator = collection.iterator(); + } + return output(key, valueIterator.next()); + } + + @Override + public void remove() { + valueIterator.remove(); + if (collection.isEmpty()) { + keyIterator.remove(); + } + totalSize--; + } + } + + private class AsMap extends Maps.ImprovedAbstractMap> { + /** + * Usually the same as map, but smaller for the headMap(), tailMap(), or + * subMap() of a SortedAsMap. + */ + final transient Map> submap; + + AsMap(Map> submap) { + this.submap = submap; + } + + @Override + protected Set>> createEntrySet() { + return new AsMapEntries(); + } + + // The following methods are included for performance. + + @Override + public boolean containsKey(Object key) { + return Maps.safeContainsKey(submap, key); + } + + @Override + public Collection get(Object key) { + Collection collection = Maps.safeGet(submap, key); + if (collection == null) { + return null; + } + @SuppressWarnings("unchecked") + K k = (K) key; + return wrapCollection(k, collection); + } + + @Override + public Set keySet() { + return AbstractMapBasedMultimap.this.keySet(); + } + + @Override + public int size() { + return submap.size(); + } + + @Override + public Collection remove(Object key) { + Collection collection = submap.remove(key); + if (collection == null) { + return null; + } + + Collection output = createCollection(); + output.addAll(collection); + totalSize -= collection.size(); + collection.clear(); + return output; + } + + @Override + public boolean equals(Object object) { + return this == object || submap.equals(object); + } + + @Override + public int hashCode() { + return submap.hashCode(); + } + + @Override + public String toString() { + return submap.toString(); + } + + @Override + public void clear() { + if (submap == map) { + AbstractMapBasedMultimap.this.clear(); + } else { + Iterators.clear(new AsMapIterator()); + } + } + + Entry> wrapEntry(Entry> entry) { + K key = entry.getKey(); + return Maps.immutableEntry(key, wrapCollection(key, entry.getValue())); + } + + class AsMapEntries extends Maps.EntrySet> { + @Override + Map> map() { + return AbstractMapBasedMultimap.AsMap.this; + } + + @Override + public Iterator>> iterator() { + return new AsMapIterator(); + } + + // The following methods are included for performance. + + @Override + public boolean contains(Object o) { + return Collections2.safeContains(submap.entrySet(), o); + } + + @Override + public boolean remove(Object o) { + if (!contains(o)) { + return false; + } + Entry entry = (Entry) o; + removeValuesForKey(entry.getKey()); + return true; + } + } + + /** + * Iterator across all keys and value collections. + */ + class AsMapIterator implements Iterator>> { + final Iterator>> delegateIterator + = submap.entrySet().iterator(); + Collection collection; + + @Override + public boolean hasNext() { + return delegateIterator.hasNext(); + } + + @Override + public Entry> next() { + Entry> entry = delegateIterator.next(); + collection = entry.getValue(); + return wrapEntry(entry); + } + + @Override + public void remove() { + delegateIterator.remove(); + totalSize -= collection.size(); + collection.clear(); + } + } + } + + private class SortedAsMap extends AsMap + implements SortedMap> { + SortedSet sortedKeySet; + + SortedAsMap(SortedMap> submap) { + super(submap); + } + + SortedMap> sortedMap() { + return (SortedMap>) submap; + } + + @Override + public Comparator comparator() { + return sortedMap().comparator(); + } + + @Override + public K firstKey() { + return sortedMap().firstKey(); + } + + @Override + public K lastKey() { + return sortedMap().lastKey(); + } + + @Override + public SortedMap> headMap(K toKey) { + return new SortedAsMap(sortedMap().headMap(toKey)); + } + + @Override + public SortedMap> subMap(K fromKey, K toKey) { + return new SortedAsMap(sortedMap().subMap(fromKey, toKey)); + } + + @Override + public SortedMap> tailMap(K fromKey) { + return new SortedAsMap(sortedMap().tailMap(fromKey)); + } + + // returns a SortedSet, even though returning a Set would be sufficient to + // satisfy the SortedMap.keySet() interface + @Override + public SortedSet keySet() { + SortedSet result = sortedKeySet; + return (result == null) ? sortedKeySet = createKeySet() : result; + } + + @Override + SortedSet createKeySet() { + return new SortedKeySet(sortedMap()); + } + } + + class NavigableAsMap extends SortedAsMap implements NavigableMap> { + + NavigableAsMap(NavigableMap> submap) { + super(submap); + } + + @Override + NavigableMap> sortedMap() { + return (NavigableMap>) super.sortedMap(); + } + + @Override + public Entry> lowerEntry(K key) { + Entry> entry = sortedMap().lowerEntry(key); + return (entry == null) ? null : wrapEntry(entry); + } + + @Override + public K lowerKey(K key) { + return sortedMap().lowerKey(key); + } + + @Override + public Entry> floorEntry(K key) { + Entry> entry = sortedMap().floorEntry(key); + return (entry == null) ? null : wrapEntry(entry); + } + + @Override + public K floorKey(K key) { + return sortedMap().floorKey(key); + } + + @Override + public Entry> ceilingEntry(K key) { + Entry> entry = sortedMap().ceilingEntry(key); + return (entry == null) ? null : wrapEntry(entry); + } + + @Override + public K ceilingKey(K key) { + return sortedMap().ceilingKey(key); + } + + @Override + public Entry> higherEntry(K key) { + Entry> entry = sortedMap().higherEntry(key); + return (entry == null) ? null : wrapEntry(entry); + } + + @Override + public K higherKey(K key) { + return sortedMap().higherKey(key); + } + + @Override + public Entry> firstEntry() { + Entry> entry = sortedMap().firstEntry(); + return (entry == null) ? null : wrapEntry(entry); + } + + @Override + public Entry> lastEntry() { + Entry> entry = sortedMap().lastEntry(); + return (entry == null) ? null : wrapEntry(entry); + } + + @Override + public Entry> pollFirstEntry() { + return pollAsMapEntry(entrySet().iterator()); + } + + @Override + public Entry> pollLastEntry() { + return pollAsMapEntry(descendingMap().entrySet().iterator()); + } + + Entry> pollAsMapEntry(Iterator>> entryIterator) { + if (!entryIterator.hasNext()) { + return null; + } + Entry> entry = entryIterator.next(); + Collection output = createCollection(); + output.addAll(entry.getValue()); + entryIterator.remove(); + return Maps.immutableEntry(entry.getKey(), unmodifiableCollectionSubclass(output)); + } + + @Override + public NavigableMap> descendingMap() { + return new NavigableAsMap(sortedMap().descendingMap()); + } + + @Override + public NavigableSet keySet() { + return (NavigableSet) super.keySet(); + } + + @Override + NavigableSet createKeySet() { + return new NavigableKeySet(sortedMap()); + } + + @Override + public NavigableSet navigableKeySet() { + return keySet(); + } + + @Override + public NavigableSet descendingKeySet() { + return descendingMap().navigableKeySet(); + } + + @Override + public NavigableMap> subMap(K fromKey, K toKey) { + return subMap(fromKey, true, toKey, false); + } + + @Override + public NavigableMap> subMap( + K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + return new NavigableAsMap(sortedMap().subMap(fromKey, fromInclusive, toKey, toInclusive)); + } + + @Override + public NavigableMap> headMap(K toKey) { + return headMap(toKey, false); + } + + @Override + public NavigableMap> headMap(K toKey, boolean inclusive) { + return new NavigableAsMap(sortedMap().headMap(toKey, inclusive)); + } + + @Override + public NavigableMap> tailMap(K fromKey) { + return tailMap(fromKey, true); + } + + @Override + public NavigableMap> tailMap(K fromKey, boolean inclusive) { + return new NavigableAsMap(sortedMap().tailMap(fromKey, inclusive)); + } + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractMapEntry.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractMapEntry.java new file mode 100644 index 0000000000..581a4d21a8 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractMapEntry.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Map.Entry; +import java.util.Objects; + +/** + * Implementation of the {@code equals}, {@code hashCode}, and {@code toString} + * methods of {@code Entry}. + * + * @author Jared Levy + */ +abstract class AbstractMapEntry implements Entry { + + @Override + public abstract K getKey(); + + @Override + public abstract V getValue(); + + @Override + public V setValue(V value) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean equals(Object object) { + if (object instanceof Entry) { + Entry that = (Entry) object; + return Objects.equals(this.getKey(), that.getKey()) + && Objects.equals(this.getValue(), that.getValue()); + } + return false; + } + + @Override + public int hashCode() { + K k = getKey(); + V v = getValue(); + return ((k == null) ? 0 : k.hashCode()) ^ ((v == null) ? 0 : v.hashCode()); + } + + /** + * Returns a string representation of the form {@code {key}={value}}. + */ + @Override + public String toString() { + return getKey() + "=" + getValue(); + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractMultimap.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractMultimap.java new file mode 100644 index 0000000000..17244fd04f --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractMultimap.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; + +/** + * A skeleton {@code Multimap} implementation, not necessarily in terms of a {@code Map}. + * + * @author Louis Wasserman + */ +abstract class AbstractMultimap implements Multimap { + private transient Collection> entries; + private transient Set keySet; + private transient Collection values; + private transient Map> asMap; + + @Override + public boolean containsValue(Object value) { + for (Collection collection : asMap().values()) { + if (collection.contains(value)) { + return true; + } + } + + return false; + } + + @Override + public boolean containsEntry(Object key, Object value) { + Collection collection = asMap().get(key); + return collection != null && collection.contains(value); + } + + @Override + public boolean remove(Object key, Object value) { + Collection collection = asMap().get(key); + return collection != null && collection.remove(value); + } + + @Override + public boolean put(K key, V value) { + return get(key).add(value); + } + + @Override + public boolean putAll(K key, Iterable values) { + checkNotNull(values); + // make sure we only call values.iterator() once + // and we only call get(key) if values is nonempty + if (values instanceof Collection) { + Collection valueCollection = (Collection) values; + return !valueCollection.isEmpty() && get(key).addAll(valueCollection); + } else { + Iterator valueItr = values.iterator(); + return valueItr.hasNext() && Iterators.addAll(get(key), valueItr); + } + } + + @Override + public Collection> entries() { + Collection> result = entries; + return (result == null) ? entries = createEntries() : result; + } + + private Collection> createEntries() { + if (this instanceof SetMultimap) { + return new EntrySet(); + } else { + return new Entries(); + } + } + + abstract Iterator> entryIterator(); + + @Override + public Set keySet() { + Set result = keySet; + return (result == null) ? keySet = createKeySet() : result; + } + + Set createKeySet() { + return new Maps.KeySet>(asMap()); + } + + @Override + public Collection values() { + Collection result = values; + return (result == null) ? values = createValues() : result; + } + + private Collection createValues() { + return new Values(); + } + + Iterator valueIterator() { + return Maps.valueIterator(entries().iterator()); + } + + @Override + public Map> asMap() { + Map> result = asMap; + return (result == null) ? asMap = createAsMap() : result; + } + + abstract Map> createAsMap(); + + @Override + public boolean equals(Object object) { + return Multimaps.equalsImpl(this, object); + } + + /** + * Returns the hash code for this multimap. + *

+ *

The hash code of a multimap is defined as the hash code of the map view, + * as returned by {@link Multimap#asMap}. + * + * @see Map#hashCode + */ + @Override + public int hashCode() { + return asMap().hashCode(); + } + + /** + * Returns a string representation of the multimap, generated by calling + * {@code toString} on the map returned by {@link Multimap#asMap}. + * + * @return a string representation of the multimap + */ + @Override + public String toString() { + return asMap().toString(); + } + + // Comparison and hashing + + private class Entries extends Multimaps.Entries { + @Override + Multimap multimap() { + return AbstractMultimap.this; + } + + @Override + public Iterator> iterator() { + return entryIterator(); + } + } + + private class EntrySet extends Entries implements Set> { + @Override + public int hashCode() { + return Sets.hashCodeImpl(this); + } + + @Override + public boolean equals(Object obj) { + return Sets.equalsImpl(this, obj); + } + } + + private class Values extends AbstractCollection { + @Override + public Iterator iterator() { + return valueIterator(); + } + + @Override + public int size() { + return AbstractMultimap.this.size(); + } + + @Override + public boolean contains(Object o) { + return AbstractMultimap.this.containsValue(o); + } + + @Override + public void clear() { + AbstractMultimap.this.clear(); + } + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractSequentialIterator.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractSequentialIterator.java new file mode 100644 index 0000000000..dde2b1401d --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractSequentialIterator.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2010 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.NoSuchElementException; + +/** + * This class provides a skeletal implementation of the {@code Iterator} + * interface for sequences whose next element can always be derived from the + * previous element. Null elements are not supported, nor is the + * {@link #remove()} method. + *

+ *

Example:

   {@code
+ * 

+ * Iterator powersOfTwo = + * new AbstractSequentialIterator(1) { + * protected Integer computeNext(Integer previous) { + * return (previous == 1 << 30) ? null : previous * 2; + * } + * };}

+ * + * @author Chris Povirk + * @since 12.0 (in Guava as {@code AbstractLinkedIterator} since 8.0) + */ +abstract class AbstractSequentialIterator + extends UnmodifiableIterator { + private T nextOrNull; + + /** + * Creates a new iterator with the given first element, or, if {@code + * firstOrNull} is null, creates a new empty iterator. + */ + AbstractSequentialIterator(T firstOrNull) { + this.nextOrNull = firstOrNull; + } + + /** + * Returns the element that follows {@code previous}, or returns {@code null} + * if no elements remain. This method is invoked during each call to + * {@link #next()} in order to compute the result of a future call to + * {@code next()}. + */ + protected abstract T computeNext(T previous); + + @Override + public final boolean hasNext() { + return nextOrNull != null; + } + + @Override + public final T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + try { + return nextOrNull; + } finally { + nextOrNull = computeNext(nextOrNull); + } + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractSetMultimap.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractSetMultimap.java new file mode 100644 index 0000000000..6247624870 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractSetMultimap.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +/** + * Basic implementation of the {@link SetMultimap} interface. It's a wrapper + * around {@link AbstractMapBasedMultimap} that converts the returned collections into + * {@code Sets}. The {@link #createCollection} method must return a {@code Set}. + * + * @author Jared Levy + */ +abstract class AbstractSetMultimap + extends AbstractMapBasedMultimap implements SetMultimap { + private static final long serialVersionUID = 7431625294878419160L; + + /** + * Creates a new multimap that uses the provided map. + * + * @param map place to store the mapping from each key to its corresponding + * values + */ + AbstractSetMultimap(Map> map) { + super(map); + } + + @Override + abstract Set createCollection(); + + // Following Javadoc copied from SetMultimap. + + @Override + Set createUnmodifiableEmptyCollection() { + return Collections.emptySet(); + } + + /** + * {@inheritDoc} + *

+ *

Because a {@code SetMultimap} has unique values for a given key, this + * method returns a {@link Set}, instead of the {@link Collection} specified + * in the {@link Multimap} interface. + */ + @Override + public Set get(K key) { + return (Set) super.get(key); + } + + /** + * {@inheritDoc} + *

+ *

Because a {@code SetMultimap} has unique values for a given key, this + * method returns a {@link Set}, instead of the {@link Collection} specified + * in the {@link Multimap} interface. + */ + @Override + public Set> entries() { + return (Set>) super.entries(); + } + + /** + * {@inheritDoc} + *

+ *

Because a {@code SetMultimap} has unique values for a given key, this + * method returns a {@link Set}, instead of the {@link Collection} specified + * in the {@link Multimap} interface. + */ + @Override + public Set removeAll(Object key) { + return (Set) super.removeAll(key); + } + + /** + * {@inheritDoc} + *

+ *

Though the method signature doesn't say so explicitly, the returned map + * has {@link Set} values. + */ + @Override + public Map> asMap() { + return super.asMap(); + } + + /** + * Stores a key-value pair in the multimap. + * + * @param key key to store in the multimap + * @param value value to store in the multimap + * @return {@code true} if the method increased the size of the multimap, or + * {@code false} if the multimap already contained the key-value pair + */ + @Override + public boolean put(K key, V value) { + return super.put(key, value); + } + + /** + * Compares the specified object to this multimap for equality. + *

+ *

Two {@code SetMultimap} instances are equal if, for each key, they + * contain the same values. Equality does not depend on the ordering of keys + * or values. + */ + @Override + public boolean equals(Object object) { + return super.equals(object); + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractSortedKeySortedSetMultimap.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractSortedKeySortedSetMultimap.java new file mode 100644 index 0000000000..813ed07a38 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractSortedKeySortedSetMultimap.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Collection; +import java.util.SortedMap; +import java.util.SortedSet; + +/** + * Basic implementation of a {@link SortedSetMultimap} with a sorted key set. + *

+ * This superclass allows {@code TreeMultimap} to override methods to return + * navigable set and map types in non-GWT only, while GWT code will inherit the + * SortedMap/SortedSet overrides. + * + * @author Louis Wasserman + */ +abstract class AbstractSortedKeySortedSetMultimap extends AbstractSortedSetMultimap { + + AbstractSortedKeySortedSetMultimap(SortedMap> map) { + super(map); + } + + @Override + public SortedMap> asMap() { + return (SortedMap>) super.asMap(); + } + + @Override + SortedMap> backingMap() { + return (SortedMap>) super.backingMap(); + } + + @Override + public SortedSet keySet() { + return (SortedSet) super.keySet(); + } + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractSortedSetMultimap.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractSortedSetMultimap.java new file mode 100644 index 0000000000..fa547e5c57 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractSortedSetMultimap.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.SortedSet; + +/** + * Basic implementation of the {@link SortedSetMultimap} interface. It's a + * wrapper around {@link AbstractMapBasedMultimap} that converts the returned + * collections into sorted sets. The {@link #createCollection} method + * must return a {@code SortedSet}. + * + * @author Jared Levy + */ +abstract class AbstractSortedSetMultimap + extends AbstractSetMultimap implements SortedSetMultimap { + private static final long serialVersionUID = 430848587173315748L; + + /** + * Creates a new multimap that uses the provided map. + * + * @param map place to store the mapping from each key to its corresponding + * values + */ + AbstractSortedSetMultimap(Map> map) { + super(map); + } + + @Override + abstract SortedSet createCollection(); + + @Override + SortedSet createUnmodifiableEmptyCollection() { + return Collections.unmodifiableSortedSet(createCollection()); + } + + /** + * Returns a collection view of all values associated with a key. If no + * mappings in the multimap have the provided key, an empty collection is + * returned. + *

+ *

Changes to the returned collection will update the underlying multimap, + * and vice versa. + *

+ *

Because a {@code SortedSetMultimap} has unique sorted values for a given + * key, this method returns a {@link SortedSet}, instead of the + * {@link Collection} specified in the {@link Multimap} interface. + */ + @Override + public SortedSet get(K key) { + return (SortedSet) super.get(key); + } + + /** + * Removes all values associated with a given key. The returned collection is + * immutable. + *

+ *

Because a {@code SortedSetMultimap} has unique sorted values for a given + * key, this method returns a {@link SortedSet}, instead of the + * {@link Collection} specified in the {@link Multimap} interface. + */ + @Override + public SortedSet removeAll(Object key) { + return (SortedSet) super.removeAll(key); + } + + /** + * Returns a map view that associates each key with the corresponding values + * in the multimap. Changes to the returned map, such as element removal, will + * update the underlying multimap. The map does not support {@code setValue} + * on its entries, {@code put}, or {@code putAll}. + *

+ *

When passed a key that is present in the map, {@code + * asMap().get(Object)} has the same behavior as {@link #get}, returning a + * live collection. When passed a key that is not present, however, {@code + * asMap().get(Object)} returns {@code null} instead of an empty collection. + *

+ *

Though the method signature doesn't say so explicitly, the returned map + * has {@link SortedSet} values. + */ + @Override + public Map> asMap() { + return super.asMap(); + } + + /** + * {@inheritDoc} + *

+ * Consequently, the values do not follow their natural ordering or the + * ordering of the value comparator. + */ + @Override + public Collection values() { + return super.values(); + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractTable.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractTable.java new file mode 100644 index 0000000000..1a3e5ac94f --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/AbstractTable.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2013 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.AbstractSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * Skeletal, implementation-agnostic implementation of the {@link Table} interface. + * + * @author Louis Wasserman + */ +abstract class AbstractTable implements Table { + + private transient Set> cellSet; + + @Override + public boolean containsRow(Object rowKey) { + return Maps.safeContainsKey(rowMap(), rowKey); + } + + @Override + public boolean containsColumn(Object columnKey) { + return Maps.safeContainsKey(columnMap(), columnKey); + } + + @Override + public Set rowKeySet() { + return rowMap().keySet(); + } + + @Override + public Set columnKeySet() { + return columnMap().keySet(); + } + + @Override + public boolean containsValue(Object value) { + for (Map row : rowMap().values()) { + if (row.containsValue(value)) { + return true; + } + } + return false; + } + + @Override + public boolean contains(Object rowKey, Object columnKey) { + Map row = Maps.safeGet(rowMap(), rowKey); + return row != null && Maps.safeContainsKey(row, columnKey); + } + + @Override + public V get(Object rowKey, Object columnKey) { + Map row = Maps.safeGet(rowMap(), rowKey); + return (row == null) ? null : Maps.safeGet(row, columnKey); + } + + @Override + public void clear() { + Iterators.clear(cellSet().iterator()); + } + + @Override + public V remove(Object rowKey, Object columnKey) { + Map row = Maps.safeGet(rowMap(), rowKey); + return (row == null) ? null : Maps.safeRemove(row, columnKey); + } + + @Override + public V put(R rowKey, C columnKey, V value) { + return row(rowKey).put(columnKey, value); + } + + @Override + public void putAll(Table table) { + for (Cell cell : table.cellSet()) { + put(cell.getRowKey(), cell.getColumnKey(), cell.getValue()); + } + } + + @Override + public Set> cellSet() { + Set> result = cellSet; + return (result == null) ? cellSet = createCellSet() : result; + } + + private Set> createCellSet() { + return new CellSet(); + } + + abstract Iterator> cellIterator(); + + @Override + public boolean equals(Object obj) { + return Tables.equalsImpl(this, obj); + } + + @Override + public int hashCode() { + return cellSet().hashCode(); + } + + /** + * Returns the string representation {@code rowMap().toString()}. + */ + @Override + public String toString() { + return rowMap().toString(); + } + + private class CellSet extends AbstractSet> { + @Override + public boolean contains(Object o) { + if (o instanceof Cell) { + Cell cell = (Cell) o; + Map row = Maps.safeGet(rowMap(), cell.getRowKey()); + return row != null && Collections2.safeContains( + row.entrySet(), Maps.immutableEntry(cell.getColumnKey(), cell.getValue())); + } + return false; + } + + @Override + public boolean remove(Object o) { + if (o instanceof Cell) { + Cell cell = (Cell) o; + Map row = Maps.safeGet(rowMap(), cell.getRowKey()); + return row != null && Collections2.safeRemove( + row.entrySet(), Maps.immutableEntry(cell.getColumnKey(), cell.getValue())); + } + return false; + } + + @Override + public void clear() { + AbstractTable.this.clear(); + } + + @Override + public Iterator> iterator() { + return cellIterator(); + } + + @Override + public int size() { + return AbstractTable.this.size(); + } + } + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/AsyncFunction.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/AsyncFunction.java new file mode 100644 index 0000000000..206ceac0a0 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/AsyncFunction.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.concurrent.Future; + +/** + * Transforms a value, possibly asynchronously. For an example usage and more + * information, see {@link Futures#transform(ListenableFuture, AsyncFunction)}. + * + * @author Chris Povirk + * @since 11.0 + */ +interface AsyncFunction { + /** + * Returns an output {@code Future} to use in place of the given {@code + * input}. The output {@code Future} need not be {@linkplain Future#isDone + * done}, making {@code AsyncFunction} suitable for asynchronous derivations. + *

+ *

Throwing an exception from this method is equivalent to returning a + * failing {@code Future}. + */ + ListenableFuture apply(I input); +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/Cache.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Cache.java new file mode 100644 index 0000000000..b360b0a59e --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Cache.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.concurrent.Callable; + +/** + * A semi-persistent mapping from keys to values. Cache entries are manually added using + * {@link #get(Object, Callable)} or {@link #put(Object, Object)}, and are stored in the cache until + * either evicted or manually invalidated. + *

+ *

Implementations of this interface are expected to be thread-safe, and can be safely accessed + * by multiple concurrent threads. + *

+ *

Note that while this class is still annotated as {@link Beta}, the API is frozen from a + * consumer's standpoint. In other words existing methods are all considered {@code non-Beta} and + * won't be changed without going through an 18 month deprecation cycle; however new methods may be + * added at any time. + * + * @author Charles Fry + * @since 10.0 + */ +public interface Cache { + + /** + * Returns the value associated with {@code key} in this cache, or {@code null} if there is no + * cached value for {@code key}. + * + * @since 11.0 + */ + V getIfPresent(Object key); + + /** + * Associates {@code value} with {@code key} in this cache. If the cache previously contained a + * value associated with {@code key}, the old value is replaced by {@code value}. + *

+ *

Prefer {@link #get(Object, Callable)} when using the conventional "if cached, return; + * otherwise create, cache and return" pattern. + * + * @since 11.0 + */ + void put(K key, V value); + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/CacheBuilder.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/CacheBuilder.java new file mode 100644 index 0000000000..53b567e557 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/CacheBuilder.java @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; +import java.util.ConcurrentModificationException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkArgument; +import static org.glassfish.jersey.internal.guava.Preconditions.checkState; + +/** + *

A builder of {@link LoadingCache} and {@link Cache} instances having any combination of the + * following features: + *

+ *

    + *
  • automatic loading of entries into the cache + *
  • least-recently-used eviction when a maximum size is exceeded + *
  • time-based expiration of entries, measured since last access or last write + *
  • keys automatically wrapped in {@linkplain WeakReference weak} references + *
  • values automatically wrapped in {@linkplain WeakReference weak} or + * {@linkplain SoftReference soft} references + *
  • notification of evicted (or otherwise removed) entries + *
  • accumulation of cache access statistics + *
+ *

+ *

These features are all optional; caches can be created using all or none of them. By default + * cache instances created by {@code CacheBuilder} will not perform any type of eviction. + *

+ *

Usage example:

   {@code
+ * 

+ * LoadingCache graphs = CacheBuilder.newBuilder() + * .maximumSize(10000) + * .expireAfterWrite(10, TimeUnit.MINUTES) + * .removalListener(MY_LISTENER) + * .build( + * new CacheLoader() { + * public Graph load(Key key) throws AnyException { + * return createExpensiveGraph(key); + * } + * });}

+ *

+ *

Or equivalently,

   {@code
+ * 

+ * // In real life this would come from a command-line flag or config file + * String spec = "maximumSize=10000,expireAfterWrite=10m"; + *

+ * LoadingCache graphs = CacheBuilder.from(spec) + * .removalListener(MY_LISTENER) + * .build( + * new CacheLoader() { + * public Graph load(Key key) throws AnyException { + * return createExpensiveGraph(key); + * } + * });}

+ *

+ *

The returned cache is implemented as a hash table with similar performance characteristics to + * {@link ConcurrentHashMap}. It implements all optional operations of the {@link LoadingCache} and + * {@link Cache} interfaces. The {@code asMap} view (and its collection views) have weakly + * consistent iterators. This means that they are safe for concurrent use, but if other threads + * modify the cache after the iterator is created, it is undefined which of these changes, if any, + * are reflected in that iterator. These iterators never throw {@link + * ConcurrentModificationException}. + *

+ *

Note: by default, the returned cache uses equality comparisons (the + * {@link Object#equals equals} method) to determine equality for keys or values. However, if + * {@link #weakKeys} was specified, the cache uses identity ({@code ==}) + * comparisons instead for keys. Likewise, if {@link #weakValues} or {@link #softValues} was + * specified, the cache uses identity comparisons for values. + *

+ *

Entries are automatically evicted from the cache when any of + * {@linkplain #maximumSize(long) maximumSize}, {@linkplain #maximumWeight(long) maximumWeight}, + * {@linkplain #expireAfterWrite expireAfterWrite}, + * {@linkplain #expireAfterAccess expireAfterAccess}, {@linkplain #weakKeys weakKeys}, + * {@linkplain #weakValues weakValues}, or {@linkplain #softValues softValues} are requested. + *

+ *

If {@linkplain #maximumSize(long) maximumSize} or + * {@linkplain #maximumWeight(long) maximumWeight} is requested entries may be evicted on each cache + * modification. + *

+ *

If {@linkplain #expireAfterWrite expireAfterWrite} or + * {@linkplain #expireAfterAccess expireAfterAccess} is requested entries may be evicted on each + * cache modification, on occasional cache accesses, or on calls to {@link Cache#cleanUp}. Expired + * entries may be counted by {@link Cache#size}, but will never be visible to read or write + * operations. + *

+ *

If {@linkplain #weakKeys weakKeys}, {@linkplain #weakValues weakValues}, or + * {@linkplain #softValues softValues} are requested, it is possible for a key or value present in + * the cache to be reclaimed by the garbage collector. Entries with reclaimed keys or values may be + * removed from the cache on each cache modification, on occasional cache accesses, or on calls to + * {@link Cache#cleanUp}; such entries may be counted in {@link Cache#size}, but will never be + * visible to read or write operations. + *

+ *

Certain cache configurations will result in the accrual of periodic maintenance tasks which + * will be performed during write operations, or during occasional read operations in the absence of + * writes. The {@link Cache#cleanUp} method of the returned cache will also perform maintenance, but + * calling it should not be necessary with a high throughput cache. Only caches built with + * {@linkplain #removalListener removalListener}, {@linkplain #expireAfterWrite expireAfterWrite}, + * {@linkplain #expireAfterAccess expireAfterAccess}, {@linkplain #weakKeys weakKeys}, + * {@linkplain #weakValues weakValues}, or {@linkplain #softValues softValues} perform periodic + * maintenance. + *

+ *

The caches produced by {@code CacheBuilder} are serializable, and the deserialized caches + * retain all the configuration properties of the original cache. Note that the serialized form does + * not include cache contents, but only configuration. + *

+ *

See the Guava User Guide article on caching for a higher-level + * explanation. + * + * @param the base key type for all caches created by this builder + * @param the base value type for all caches created by this builder + * @author Charles Fry + * @author Kevin Bourrillion + * @since 10.0 + */ +public final class CacheBuilder { + + public static final Ticker NULL_TICKER = new Ticker() { + @Override + public long read() { + return 0; + } + }; + static final int UNSET_INT = -1; + static final int DEFAULT_INITIAL_CAPACITY = 16; + private static final int DEFAULT_CONCURRENCY_LEVEL = 4; + static final int DEFAULT_EXPIRATION_NANOS = 0; + static final int DEFAULT_REFRESH_NANOS = 0; + private final int initialCapacity = UNSET_INT; + private final int concurrencyLevel = UNSET_INT; + private long maximumSize = UNSET_INT; + private final long maximumWeight = UNSET_INT; + private final long expireAfterWriteNanos = UNSET_INT; + private long expireAfterAccessNanos = UNSET_INT; + private final long refreshNanos = UNSET_INT; + + // TODO(fry): make constructor private and update tests to use newBuilder + private CacheBuilder() { + } + + /** + * Constructs a new {@code CacheBuilder} instance with default settings, including strong keys, + * strong values, and no automatic eviction of any kind. + */ + public static CacheBuilder newBuilder() { + return new CacheBuilder(); + } + + int getConcurrencyLevel() { + return (concurrencyLevel == UNSET_INT) ? DEFAULT_CONCURRENCY_LEVEL : concurrencyLevel; + } + + /** + * Specifies the maximum number of entries the cache may contain. Note that the cache may evict + * an entry before this limit is exceeded. As the cache size grows close to the maximum, the + * cache evicts entries that are less likely to be used again. For example, the cache may evict an + * entry because it hasn't been used recently or very often. + *

+ *

When {@code size} is zero, elements will be evicted immediately after being loaded into the + * cache. This can be useful in testing, or to disable caching temporarily without a code change. + *

+ *

This feature cannot be used in conjunction with {@link #maximumWeight}. + * + * @param size the maximum size of the cache + * @throws IllegalArgumentException if {@code size} is negative + * @throws IllegalStateException if a maximum size or weight was already set + */ + public CacheBuilder maximumSize(long size) { + checkState(this.maximumSize == UNSET_INT, "maximum size was already set to %s", + this.maximumSize); + checkState(this.maximumWeight == UNSET_INT, "maximum weight was already set to %s", + this.maximumWeight); + checkArgument(size >= 0, "maximum size must not be negative"); + this.maximumSize = size; + return this; + } + + /** + * Specifies that each entry should be automatically removed from the cache once a fixed duration + * has elapsed after the entry's creation, the most recent replacement of its value, or its last + * access. Access time is reset by all cache read and write operations (including + * {@code Cache.asMap().get(Object)} and {@code Cache.asMap().put(K, V)}), but not by operations + * on the collection-views of {@link Cache#asMap}. + *

+ *

When {@code duration} is zero, this method hands off to + * {@link #maximumSize(long) maximumSize}{@code (0)}, ignoring any otherwise-specificed maximum + * size or weight. This can be useful in testing, or to disable caching temporarily without a code + * change. + *

+ *

Expired entries may be counted in {@link Cache#size}, but will never be visible to read or + * write operations. Expired entries are cleaned up as part of the routine maintenance described + * in the class javadoc. + * + * @param duration the length of time after an entry is last accessed that it should be + * automatically removed + * @param unit the unit that {@code duration} is expressed in + * @throws IllegalArgumentException if {@code duration} is negative + * @throws IllegalStateException if the time to idle or time to live was already set + */ + public CacheBuilder expireAfterAccess(long duration, TimeUnit unit) { + checkState(expireAfterAccessNanos == UNSET_INT, "expireAfterAccess was already set to %s ns", + expireAfterAccessNanos); + checkArgument(duration >= 0, "duration cannot be negative: %s %s", duration, unit); + this.expireAfterAccessNanos = unit.toNanos(duration); + return this; + } + + long getExpireAfterAccessNanos() { + return (expireAfterAccessNanos == UNSET_INT) + ? DEFAULT_EXPIRATION_NANOS : expireAfterAccessNanos; + } + + /** + * Builds a cache, which either returns an already-loaded value for a given key or atomically + * computes or retrieves it using the supplied {@code CacheLoader}. If another thread is currently + * loading the value for this key, simply waits for that thread to finish and returns its + * loaded value. Note that multiple threads can concurrently load values for distinct keys. + *

+ *

This method does not alter the state of this {@code CacheBuilder} instance, so it can be + * invoked again to create multiple independent caches. + * + * @param loader the cache loader used to obtain new values + * @return a cache having the requested features + */ + public LoadingCache build( + CacheLoader loader) { + checkWeightWithWeigher(); + return new LocalCache.LocalLoadingCache(this, loader); + } + + /** + * Builds a cache which does not automatically load values when keys are requested. + *

+ *

Consider {@link #build(CacheLoader)} instead, if it is feasible to implement a + * {@code CacheLoader}. + *

+ *

This method does not alter the state of this {@code CacheBuilder} instance, so it can be + * invoked again to create multiple independent caches. + * + * @return a cache having the requested features + * @since 11.0 + */ + public Cache build() { + checkWeightWithWeigher(); + checkNonLoadingCache(); + return new LocalCache.LocalManualCache(this); + } + + private void checkNonLoadingCache() { + checkState(refreshNanos == UNSET_INT, "refreshAfterWrite requires a LoadingCache"); + } + + private void checkWeightWithWeigher() { + checkState(maximumWeight == UNSET_INT, "maximumWeight requires weigher"); + } + + /** + * Returns a string representation for this CacheBuilder instance. The exact form of the returned + * string is not specified. + */ + @Override + public String toString() { + MoreObjects.ToStringHelper s = MoreObjects.toStringHelper(this); + if (initialCapacity != UNSET_INT) { + s.add("initialCapacity", initialCapacity); + } + if (concurrencyLevel != UNSET_INT) { + s.add("concurrencyLevel", concurrencyLevel); + } + if (maximumSize != UNSET_INT) { + s.add("maximumSize", maximumSize); + } + if (maximumWeight != UNSET_INT) { + s.add("maximumWeight", maximumWeight); + } + if (expireAfterWriteNanos != UNSET_INT) { + s.add("expireAfterWrite", expireAfterWriteNanos + "ns"); + } + if (expireAfterAccessNanos != UNSET_INT) { + s.add("expireAfterAccess", expireAfterAccessNanos + "ns"); + } + return s.toString(); + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/CacheLoader.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/CacheLoader.java new file mode 100644 index 0000000000..cd8859b82a --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/CacheLoader.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; + +/** + * Computes or retrieves values, based on a key, for use in populating a {@link LoadingCache}. + *

+ *

Most implementations will only need to implement {@link #load}. Other methods may be + * overridden as desired. + *

+ *

Usage example:

   {@code
+ * 

+ * CacheLoader loader = new CacheLoader() { + * public Graph load(Key key) throws AnyException { + * return createExpensiveGraph(key); + * } + * }; + * LoadingCache cache = CacheBuilder.newBuilder().build(loader);}

+ * + * @author Charles Fry + * @since 10.0 + */ +public abstract class CacheLoader { + /** + * Constructor for use by subclasses. + */ + protected CacheLoader() { + } + + /** + * Computes or retrieves the value corresponding to {@code key}. + * + * @param key the non-null key whose value should be loaded + * @return the value associated with {@code key}; must not be null + * @throws Exception if unable to load the result + * @throws InterruptedException if this method is interrupted. {@code InterruptedException} is + * treated like any other {@code Exception} in all respects except that, when it is caught, + * the thread's interrupt status is set + */ + public abstract V load(K key) throws Exception; + + /** + * Computes or retrieves a replacement value corresponding to an already-cached {@code key}. This + * method is called when an existing cache entry is refreshed by + * {@link CacheBuilder#refreshAfterWrite}, or through a call to {@link LoadingCache#refresh}. + *

+ *

This implementation synchronously delegates to {@link #load}. It is recommended that it be + * overridden with an asynchronous implementation when using + * {@link CacheBuilder#refreshAfterWrite}. + *

+ *

Note: all exceptions thrown by this method will be logged and then swallowed. + * + * @param key the non-null key whose value should be loaded + * @param oldValue the non-null old value corresponding to {@code key} + * @return the future new value associated with {@code key}; + * must not be null, must not return null + * @throws Exception if unable to reload the result + * @throws InterruptedException if this method is interrupted. {@code InterruptedException} is + * treated like any other {@code Exception} in all respects except that, when it is caught, + * the thread's interrupt status is set + * @since 11.0 + */ + public ListenableFuture reload(K key, V oldValue) throws Exception { + checkNotNull(key); + checkNotNull(oldValue); + return Futures.immediateFuture(load(key)); + } + + /** + * Thrown to indicate that an invalid response was returned from a call to {@link CacheLoader}. + * + * @since 11.0 + */ + public static final class InvalidCacheLoadException extends RuntimeException { + public InvalidCacheLoadException(String message) { + super(message); + } + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/CollectPreconditions.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/CollectPreconditions.java new file mode 100644 index 0000000000..ad07373067 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/CollectPreconditions.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkState; + +/** + * Precondition checks useful in collection implementations. + */ +final class CollectPreconditions { + + static int checkNonnegative(int value, String name) { + if (value < 0) { + throw new IllegalArgumentException(name + " cannot be negative but was: " + value); + } + return value; + } + + /** + * Precondition tester for {@code Iterator.remove()} that throws an exception with a consistent + * error message. + */ + static void checkRemove(boolean canRemove) { + checkState(canRemove, "no calls to next() since the last call to remove()"); + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/Collections2.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Collections2.java new file mode 100644 index 0000000000..d081b0651f --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Collections2.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Iterator; +import java.util.function.Function; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkArgument; +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; + +/** + * Provides static methods for working with {@code Collection} instances. + * + * @author Chris Povirk + * @author Mike Bostock + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +final class Collections2 { + static final Joiner STANDARD_JOINER = Joiner.on(); + + private Collections2() { + } + + /** + * Delegates to {@link Collection#contains}. Returns {@code false} if the + * {@code contains} method throws a {@code ClassCastException} or + * {@code NullPointerException}. + */ + static boolean safeContains( + Collection collection, Object object) { + checkNotNull(collection); + try { + return collection.contains(object); + } catch (ClassCastException e) { + return false; + } catch (NullPointerException e) { + return false; + } + } + + /** + * Delegates to {@link Collection#remove}. Returns {@code false} if the + * {@code remove} method throws a {@code ClassCastException} or + * {@code NullPointerException}. + */ + static boolean safeRemove(Collection collection, Object object) { + checkNotNull(collection); + try { + return collection.remove(object); + } catch (ClassCastException e) { + return false; + } catch (NullPointerException e) { + return false; + } + } + + /** + * Returns a collection that applies {@code function} to each element of + * {@code fromCollection}. The returned collection is a live view of {@code + * fromCollection}; changes to one affect the other. + *

+ *

The returned collection's {@code add()} and {@code addAll()} methods + * throw an {@link UnsupportedOperationException}. All other collection + * methods are supported, as long as {@code fromCollection} supports them. + *

+ *

The returned collection isn't threadsafe or serializable, even if + * {@code fromCollection} is. + *

+ *

When a live view is not needed, it may be faster to copy the + * transformed collection and use the copy. + *

+ *

If the input {@code Collection} is known to be a {@code List}, consider + * {@link Lists#transform}. If only an {@code Iterable} is available, use + * {@link Iterables#transform}. + */ + public static Collection transform(Collection fromCollection, + Function function) { + return new TransformedCollection(fromCollection, function); + } + + /** + * Returns best-effort-sized StringBuilder based on the given collection size. + */ + static StringBuilder newStringBuilderForCollection(int size) { + CollectPreconditions.checkNonnegative(size, "size"); + return new StringBuilder((int) Math.min(size * 8L, Ints.MAX_POWER_OF_TWO)); + } + + /** + * Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557 + */ + static Collection cast(Iterable iterable) { + return (Collection) iterable; + } + + static class TransformedCollection extends AbstractCollection { + final Collection fromCollection; + final Function function; + + TransformedCollection(Collection fromCollection, + Function function) { + this.fromCollection = checkNotNull(fromCollection); + this.function = checkNotNull(function); + } + + @Override + public void clear() { + fromCollection.clear(); + } + + @Override + public boolean isEmpty() { + return fromCollection.isEmpty(); + } + + @Override + public Iterator iterator() { + return Iterators.transform(fromCollection.iterator(), function); + } + + @Override + public int size() { + return fromCollection.size(); + } + } + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/ComparatorOrdering.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ComparatorOrdering.java new file mode 100644 index 0000000000..1cba96a146 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ComparatorOrdering.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.io.Serializable; +import java.util.Comparator; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; + +/** + * An ordering for a pre-existing comparator. + */ +final class ComparatorOrdering extends Ordering implements Serializable { + private static final long serialVersionUID = 0; + private final Comparator comparator; + + ComparatorOrdering(Comparator comparator) { + this.comparator = checkNotNull(comparator); + } + + @Override + public int compare(T a, T b) { + return comparator.compare(a, b); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof ComparatorOrdering) { + ComparatorOrdering that = (ComparatorOrdering) object; + return this.comparator.equals(that.comparator); + } + return false; + } + + @Override + public int hashCode() { + return comparator.hashCode(); + } + + @Override + public String toString() { + return comparator.toString(); + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/Equivalence.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Equivalence.java new file mode 100644 index 0000000000..6a1b4e0e4a --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Equivalence.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2010 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.io.Serializable; + +/** + * A strategy for determining whether two instances are considered equivalent. Examples of + * equivalences are the {@linkplain #identity() identity equivalence} and {@linkplain #equals equals + * equivalence}. + * + * @author Bob Lee + * @author Ben Yu + * @author Gregory Kick + * @since 10.0 (mostly source-compatible since 4.0) + */ +public abstract class Equivalence { + + /** + * Returns an equivalence that delegates to {@link Object#equals} and {@link Object#hashCode}. + * {@link Equivalence#equivalent} returns {@code true} if both values are null, or if neither + * value is null and {@link Object#equals} returns {@code true}. {@link Equivalence#hash} returns + * {@code 0} if passed a null value. + * + * @since 4.0 (in Equivalences) + */ + public static Equivalence equals() { + return Equals.INSTANCE; + } + + /** + * Returns an equivalence that uses {@code ==} to compare values and {@link + * System#identityHashCode(Object)} to compute the hash code. {@link Equivalence#equivalent} + * returns {@code true} if {@code a == b}, including in the case that a and b are both null. + * + * @since 4.0 (in Equivalences) + */ + public static Equivalence identity() { + return Identity.INSTANCE; + } + + /** + * Returns {@code true} if the given objects are considered equivalent. + *

+ *

The {@code equivalent} method implements an equivalence relation on object references: + *

+ *

    + *
  • It is reflexive: for any reference {@code x}, including null, {@code + * equivalent(x, x)} returns {@code true}. + *
  • It is symmetric: for any references {@code x} and {@code y}, {@code + * equivalent(x, y) == equivalent(y, x)}. + *
  • It is transitive: for any references {@code x}, {@code y}, and {@code z}, if + * {@code equivalent(x, y)} returns {@code true} and {@code equivalent(y, z)} returns {@code + * true}, then {@code equivalent(x, z)} returns {@code true}. + *
  • It is consistent: for any references {@code x} and {@code y}, multiple invocations + * of {@code equivalent(x, y)} consistently return {@code true} or consistently return {@code + * false} (provided that neither {@code x} nor {@code y} is modified). + *
+ */ + public final boolean equivalent(T a, T b) { + if (a == b) { + return true; + } + if (a == null || b == null) { + return false; + } + return doEquivalent(a, b); + } + + /** + * Returns {@code true} if {@code a} and {@code b} are considered equivalent. + *

+ *

Called by {@link #equivalent}. {@code a} and {@code b} are not the same + * object and are not nulls. + * + * @since 10.0 (previously, subclasses would override equivalent()) + */ + protected abstract boolean doEquivalent(T a, T b); + + /** + * Returns a hash code for {@code t}. + *

+ *

The {@code hash} has the following properties: + *

    + *
  • It is consistent: for any reference {@code x}, multiple invocations of + * {@code hash(x}} consistently return the same value provided {@code x} remains unchanged + * according to the definition of the equivalence. The hash need not remain consistent from + * one execution of an application to another execution of the same application. + *
  • It is distributable across equivalence: for any references {@code x} and {@code y}, + * if {@code equivalent(x, y)}, then {@code hash(x) == hash(y)}. It is not necessary + * that the hash be distributable across inequivalence. If {@code equivalence(x, y)} + * is false, {@code hash(x) == hash(y)} may still be true. + *
  • {@code hash(null)} is {@code 0}. + *
+ */ + public final int hash(T t) { + if (t == null) { + return 0; + } + return doHash(t); + } + + /** + * Returns a hash code for non-null object {@code t}. + *

+ *

Called by {@link #hash}. + * + * @since 10.0 (previously, subclasses would override hash()) + */ + protected abstract int doHash(T t); + + static final class Equals extends Equivalence implements Serializable { + + static final Equivalence.Equals INSTANCE = new Equivalence.Equals(); + private static final long serialVersionUID = 1; + + @Override + protected boolean doEquivalent(Object a, Object b) { + return a.equals(b); + } + + @Override + protected int doHash(Object o) { + return o.hashCode(); + } + + private Object readResolve() { + return INSTANCE; + } + } + + static final class Identity extends Equivalence implements Serializable { + + static final Equivalence.Identity INSTANCE = new Equivalence.Identity(); + private static final long serialVersionUID = 1; + + @Override + protected boolean doEquivalent(Object a, Object b) { + return false; + } + + @Override + protected int doHash(Object o) { + return System.identityHashCode(o); + } + + private Object readResolve() { + return INSTANCE; + } + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/ExecutionError.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ExecutionError.java new file mode 100644 index 0000000000..b0c0be8a62 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ExecutionError.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +/** + * {@link Error} variant of {@link java.util.concurrent.ExecutionException}. As + * with {@code ExecutionException}, the error's {@linkplain #getCause() cause} + * comes from a failed task, possibly run in another thread. That cause should + * itself be an {@code Error}; if not, use {@code ExecutionException} or {@link + * UncheckedExecutionException}. This allows the client code to continue to + * distinguish between exceptions and errors, even when they come from other + * threads. + * + * @author Chris Povirk + * @since 10.0 + */ +public class ExecutionError extends Error { + + private static final long serialVersionUID = 0; + + /** + * Creates a new instance with the given cause. + */ + public ExecutionError(Error cause) { + super(cause); + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/ExecutionList.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ExecutionList.java new file mode 100644 index 0000000000..1ba2b739d1 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ExecutionList.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.concurrent.Executor; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + *

A list of listeners, each with an associated {@code Executor}, that + * guarantees that every {@code Runnable} that is {@linkplain #add added} will + * be executed after {@link #execute()} is called. Any {@code Runnable} added + * after the call to {@code execute} is still guaranteed to execute. There is no + * guarantee, however, that listeners will be executed in the order that they + * are added. + *

+ *

Exceptions thrown by a listener will be propagated up to the executor. + * Any exception thrown during {@code Executor.execute} (e.g., a {@code + * RejectedExecutionException} or an exception thrown by {@linkplain + * MoreExecutors#directExecutor direct execution}) will be caught and + * logged. + * + * @author Nishant Thakkar + * @author Sven Mawson + * @since 1.0 + */ +final class ExecutionList { + // Logger to log exceptions caught when running runnables. + private static final Logger log = Logger.getLogger(ExecutionList.class.getName()); + + /** + * The runnable, executor pairs to execute. This acts as a stack threaded through the + * {@link RunnableExecutorPair#next} field. + */ + private RunnableExecutorPair runnables; + private boolean executed; + + /** + * Creates a new, empty {@link ExecutionList}. + */ + public ExecutionList() { + } + + /** + * Submits the given runnable to the given {@link Executor} catching and logging all + * {@linkplain RuntimeException runtime exceptions} thrown by the executor. + */ + private static void executeListener(Runnable runnable, Executor executor) { + try { + executor.execute(runnable); + } catch (RuntimeException e) { + // Log it and keep going, bad runnable and/or executor. Don't + // punish the other runnables if we're given a bad one. We only + // catch RuntimeException because we want Errors to propagate up. + log.log(Level.SEVERE, "RuntimeException while executing runnable " + + runnable + " with executor " + executor, e); + } + } + + /** + * Adds the {@code Runnable} and accompanying {@code Executor} to the list of + * listeners to execute. If execution has already begun, the listener is + * executed immediately. + *

+ *

Note: For fast, lightweight listeners that would be safe to execute in + * any thread, consider {@link MoreExecutors#directExecutor}. For heavier + * listeners, {@code directExecutor()} carries some caveats: First, the + * thread that the listener runs in depends on whether the {@code + * ExecutionList} has been executed at the time it is added. In particular, + * listeners may run in the thread that calls {@code add}. Second, the thread + * that calls {@link #execute} may be an internal implementation thread, such + * as an RPC network thread, and {@code directExecutor()} listeners may + * run in this thread. Finally, during the execution of a {@code + * directExecutor} listener, all other registered but unexecuted + * listeners are prevented from running, even if those listeners are to run + * in other executors. + */ + public void add(Runnable runnable, Executor executor) { + // Fail fast on a null. We throw NPE here because the contract of + // Executor states that it throws NPE on null listener, so we propagate + // that contract up into the add method as well. + Preconditions.checkNotNull(runnable, "Runnable was null."); + Preconditions.checkNotNull(executor, "Executor was null."); + + // Lock while we check state. We must maintain the lock while adding the + // new pair so that another thread can't run the list out from under us. + // We only add to the list if we have not yet started execution. + synchronized (this) { + if (!executed) { + runnables = new RunnableExecutorPair(runnable, executor, runnables); + return; + } + } + // Execute the runnable immediately. Because of scheduling this may end up + // getting called before some of the previously added runnables, but we're + // OK with that. If we want to change the contract to guarantee ordering + // among runnables we'd have to modify the logic here to allow it. + executeListener(runnable, executor); + } + + /** + * Runs this execution list, executing all existing pairs in the order they + * were added. However, note that listeners added after this point may be + * executed before those previously added, and note that the execution order + * of all listeners is ultimately chosen by the implementations of the + * supplied executors. + *

+ *

This method is idempotent. Calling it several times in parallel is + * semantically equivalent to calling it exactly once. + * + * @since 10.0 (present in 1.0 as {@code run}) + */ + public void execute() { + // Lock while we update our state so the add method above will finish adding + // any listeners before we start to run them. + RunnableExecutorPair list; + synchronized (this) { + if (executed) { + return; + } + executed = true; + list = runnables; + runnables = null; // allow GC to free listeners even if this stays around for a while. + } + // If we succeeded then list holds all the runnables we to execute. The pairs in the stack are + // in the opposite order from how they were added so we need to reverse the list to fulfill our + // contract. + // This is somewhat annoying, but turns out to be very fast in practice. Alternatively, we + // could drop the contract on the method that enforces this queue like behavior since depending + // on it is likely to be a bug anyway. + + // N.B. All writes to the list and the next pointers must have happened before the above + // synchronized block, so we can iterate the list without the lock held here. + RunnableExecutorPair reversedList = null; + while (list != null) { + RunnableExecutorPair tmp = list; + list = list.next; + tmp.next = reversedList; + reversedList = tmp; + } + while (reversedList != null) { + executeListener(reversedList.runnable, reversedList.executor); + reversedList = reversedList.next; + } + } + + private static final class RunnableExecutorPair { + final Runnable runnable; + final Executor executor; + RunnableExecutorPair next; + + RunnableExecutorPair(Runnable runnable, Executor executor, RunnableExecutorPair next) { + this.runnable = runnable; + this.executor = executor; + this.next = next; + } + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/ForwardingCollection.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ForwardingCollection.java new file mode 100644 index 0000000000..f00dff293b --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ForwardingCollection.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Collection; +import java.util.Iterator; + +/** + * A collection which forwards all its method calls to another collection. + * Subclasses should override one or more methods to modify the behavior of the + * backing collection as desired per the decorator pattern. + *

+ *

Warning: The methods of {@code ForwardingCollection} forward + * indiscriminately to the methods of the delegate. For example, + * overriding {@link #add} alone will not change the behavior of {@link + * #addAll}, which can lead to unexpected behavior. In this case, you should + * override {@code addAll} as well, either providing your own implementation, or + * delegating to the provided {@code standardAddAll} method. + *

+ *

The {@code standard} methods are not guaranteed to be thread-safe, even + * when all of the methods that they depend on are thread-safe. + * + * @author Kevin Bourrillion + * @author Louis Wasserman + * @since 2.0 (imported from Google Collections Library) + */ +public abstract class ForwardingCollection extends ForwardingObject + implements Collection { + // TODO(user): identify places where thread safety is actually lost + + /** + * Constructor for use by subclasses. + */ + ForwardingCollection() { + } + + @Override + protected abstract Collection delegate(); + + @Override + public Iterator iterator() { + return delegate().iterator(); + } + + @Override + public int size() { + return delegate().size(); + } + + @Override + public boolean removeAll(Collection collection) { + return delegate().removeAll(collection); + } + + @Override + public boolean isEmpty() { + return delegate().isEmpty(); + } + + @Override + public boolean contains(Object object) { + return delegate().contains(object); + } + + @Override + public boolean add(E element) { + return delegate().add(element); + } + + @Override + public boolean remove(Object object) { + return delegate().remove(object); + } + + @Override + public boolean containsAll(Collection collection) { + return delegate().containsAll(collection); + } + + @Override + public boolean addAll(Collection collection) { + return delegate().addAll(collection); + } + + @Override + public boolean retainAll(Collection collection) { + return delegate().retainAll(collection); + } + + @Override + public void clear() { + delegate().clear(); + } + + @Override + public Object[] toArray() { + return delegate().toArray(); + } + + @Override + public T[] toArray(T[] array) { + return delegate().toArray(array); + } + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/ForwardingMapEntry.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ForwardingMapEntry.java new file mode 100644 index 0000000000..3f6b1add3f --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ForwardingMapEntry.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Map.Entry; +import java.util.Objects; + +/** + * A map entry which forwards all its method calls to another map entry. + * Subclasses should override one or more methods to modify the behavior of the + * backing map entry as desired per the decorator pattern. + *

+ *

Warning: The methods of {@code ForwardingMapEntry} forward + * indiscriminately to the methods of the delegate. For example, + * overriding {@link #getValue} alone will not change the behavior of + * {@link #equals}, which can lead to unexpected behavior. In this case, you + * should override {@code equals} as well, either providing your own + * implementation, or delegating to the provided {@code standardEquals} method. + *

+ *

Each of the {@code standard} methods, where appropriate, use {@link + * Objects#equal} to test equality for both keys and values. This may not be + * the desired behavior for map implementations that use non-standard notions of + * key equality, such as the entry of a {@code SortedMap} whose comparator is + * not consistent with {@code equals}. + *

+ *

The {@code standard} methods are not guaranteed to be thread-safe, even + * when all of the methods that they depend on are thread-safe. + * + * @author Mike Bostock + * @author Louis Wasserman + * @since 2.0 (imported from Google Collections Library) + */ +public abstract class ForwardingMapEntry + extends ForwardingObject implements Entry { + // TODO(user): identify places where thread safety is actually lost + + /** + * Constructor for use by subclasses. + */ + ForwardingMapEntry() { + } + + @Override + protected abstract Entry delegate(); + + @Override + public K getKey() { + return delegate().getKey(); + } + + @Override + public V getValue() { + return delegate().getValue(); + } + + @Override + public V setValue(V value) { + return delegate().setValue(value); + } + + @Override + public boolean equals(Object object) { + return delegate().equals(object); + } + + @Override + public int hashCode() { + return delegate().hashCode(); + } + + /** + * A sensible definition of {@link #equals(Object)} in terms of {@link + * #getKey()} and {@link #getValue()}. If you override either of these + * methods, you may wish to override {@link #equals(Object)} to forward to + * this implementation. + * + * @since 7.0 + */ + boolean standardEquals(Object object) { + if (object instanceof Entry) { + Entry that = (Entry) object; + return Objects.equals(this.getKey(), that.getKey()) + && Objects.equals(this.getValue(), that.getValue()); + } + return false; + } + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/ForwardingObject.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ForwardingObject.java new file mode 100644 index 0000000000..3808f01b54 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ForwardingObject.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.io.Serializable; + +/** + * An abstract base class for implementing the decorator pattern. + * The {@link #delegate()} method must be overridden to return the instance + * being decorated. + *

+ *

This class does not forward the {@code hashCode} and {@code equals} + * methods through to the backing object, but relies on {@code Object}'s + * implementation. This is necessary to preserve the symmetry of {@code equals}. + * Custom definitions of equality are usually based on an interface, such as + * {@code Set} or {@code List}, so that the implementation of {@code equals} can + * cast the object being tested for equality to the custom interface. {@code + * ForwardingObject} implements no such custom interfaces directly; they + * are implemented only in subclasses. Therefore, forwarding {@code equals} + * would break symmetry, as the forwarding object might consider itself equal to + * the object being tested, but the reverse could not be true. This behavior is + * consistent with the JDK's collection wrappers, such as + * {@link java.util.Collections#unmodifiableCollection}. Use an + * interface-specific subclass of {@code ForwardingObject}, such as {@link + * ForwardingList}, to preserve equality behavior, or override {@code equals} + * directly. + *

+ *

The {@code toString} method is forwarded to the delegate. Although this + * class does not implement {@link Serializable}, a serializable subclass may be + * created since this class has a parameter-less constructor. + * + * @author Mike Bostock + * @since 2.0 (imported from Google Collections Library) + */ +abstract class ForwardingObject { + + /** + * Constructor for use by subclasses. + */ + ForwardingObject() { + } + + /** + * Returns the backing delegate instance that methods are forwarded to. + * Abstract subclasses generally override this method with an abstract method + * that has a more specific return type, such as {@link + * ForwardingSet#delegate}. Concrete subclasses override this method to supply + * the instance being decorated. + */ + protected abstract Object delegate(); + + /** + * Returns the string representation generated by the delegate's + * {@code toString} method. + */ + @Override + public String toString() { + return delegate().toString(); + } + + /* No equals or hashCode. See class comments for details. */ +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/ForwardingSet.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ForwardingSet.java new file mode 100644 index 0000000000..c2185b340d --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ForwardingSet.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Set; + +/** + * A set which forwards all its method calls to another set. Subclasses should + * override one or more methods to modify the behavior of the backing set as + * desired per the decorator pattern. + *

+ *

Warning: The methods of {@code ForwardingSet} forward + * indiscriminately to the methods of the delegate. For example, + * overriding {@link #add} alone will not change the behavior of {@link + * #addAll}, which can lead to unexpected behavior. In this case, you should + * override {@code addAll} as well, either providing your own implementation, or + * delegating to the provided {@code standardAddAll} method. + *

+ *

The {@code standard} methods are not guaranteed to be thread-safe, even + * when all of the methods that they depend on are thread-safe. + * + * @author Kevin Bourrillion + * @author Louis Wasserman + * @since 2.0 (imported from Google Collections Library) + */ +public abstract class ForwardingSet extends ForwardingCollection + implements Set { + // TODO(user): identify places where thread safety is actually lost + + /** + * Constructor for use by subclasses. + */ + ForwardingSet() { + } + + @Override + protected abstract Set delegate(); + + @Override + public boolean equals(Object object) { + return object == this || delegate().equals(object); + } + + @Override + public int hashCode() { + return delegate().hashCode(); + } + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/ForwardingSortedSet.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ForwardingSortedSet.java new file mode 100644 index 0000000000..64b5d2d9ab --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ForwardingSortedSet.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Comparator; +import java.util.SortedSet; + +/** + * A sorted set which forwards all its method calls to another sorted set. + * Subclasses should override one or more methods to modify the behavior of the + * backing sorted set as desired per the decorator pattern. + *

+ *

Warning: The methods of {@code ForwardingSortedSet} forward + * indiscriminately to the methods of the delegate. For example, + * overriding {@link #add} alone will not change the behavior of {@link + * #addAll}, which can lead to unexpected behavior. In this case, you should + * override {@code addAll} as well, either providing your own implementation, or + * delegating to the provided {@code standardAddAll} method. + *

+ *

Each of the {@code standard} methods, where appropriate, uses the set's + * comparator (or the natural ordering of the elements, if there is no + * comparator) to test element equality. As a result, if the comparator is not + * consistent with equals, some of the standard implementations may violate the + * {@code Set} contract. + *

+ *

The {@code standard} methods and the collection views they return are not + * guaranteed to be thread-safe, even when all of the methods that they depend + * on are thread-safe. + * + * @author Mike Bostock + * @author Louis Wasserman + * @since 2.0 (imported from Google Collections Library) + */ +public abstract class ForwardingSortedSet extends ForwardingSet + implements SortedSet { + + /** + * Constructor for use by subclasses. + */ + ForwardingSortedSet() { + } + + @Override + protected abstract SortedSet delegate(); + + @Override + public Comparator comparator() { + return delegate().comparator(); + } + + @Override + public E first() { + return delegate().first(); + } + + @Override + public SortedSet headSet(E toElement) { + return delegate().headSet(toElement); + } + + @Override + public E last() { + return delegate().last(); + } + + @Override + public SortedSet subSet(E fromElement, E toElement) { + return delegate().subSet(fromElement, toElement); + } + + @Override + public SortedSet tailSet(E fromElement) { + return delegate().tailSet(fromElement); + } + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/Futures.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Futures.java new file mode 100644 index 0000000000..aec3197b32 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Futures.java @@ -0,0 +1,340 @@ +/* + * Copyright (C) 2006 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.lang.reflect.UndeclaredThrowableException; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; + +/** + * Static utility methods pertaining to the {@link Future} interface. + *

+ *

Many of these methods use the {@link ListenableFuture} API; consult the + * Guava User Guide article on + * {@code ListenableFuture}. + * + * @author Kevin Bourrillion + * @author Nishant Thakkar + * @author Sven Mawson + * @since 1.0 + */ +public final class Futures { + private Futures() { + } + + /** + * Creates a {@code ListenableFuture} which has its value set immediately upon + * construction. The getters just return the value. This {@code Future} can't + * be canceled or timed out and its {@code isDone()} method always returns + * {@code true}. + */ + public static ListenableFuture immediateFuture(V value) { + return new ImmediateSuccessfulFuture(value); + } + + /** + * Returns a {@code ListenableFuture} which has an exception set immediately + * upon construction. + *

+ *

The returned {@code Future} can't be cancelled, and its {@code isDone()} + * method always returns {@code true}. Calling {@code get()} will immediately + * throw the provided {@code Throwable} wrapped in an {@code + * ExecutionException}. + */ + public static ListenableFuture immediateFailedFuture( + Throwable throwable) { + checkNotNull(throwable); + return new ImmediateFailedFuture(throwable); + } + + /** + * Returns a new {@code ListenableFuture} whose result is the product of + * applying the given {@code Function} to the result of the given {@code + * Future}. Example: + *

+ *

   {@code
+     *   ListenableFuture queryFuture = ...;
+     *   Function> rowsFunction =
+     *       new Function>() {
+     *         public List apply(QueryResult queryResult) {
+     *           return queryResult.getRows();
+     *         }
+     *       };
+     *   ListenableFuture> rowsFuture =
+     *       transform(queryFuture, rowsFunction);}
+ *

+ *

Note: If the transformation is slow or heavyweight, consider {@linkplain + * #transform(ListenableFuture, Function, Executor) supplying an executor}. + * If you do not supply an executor, {@code transform} will use an inline + * executor, which carries some caveats for heavier operations. For example, + * the call to {@code function.apply} may run on an unpredictable or + * undesirable thread: + *

+ *

    + *
  • If the input {@code Future} is done at the time {@code transform} is + * called, {@code transform} will call {@code function.apply} inline. + *
  • If the input {@code Future} is not yet done, {@code transform} will + * schedule {@code function.apply} to be run by the thread that completes the + * input {@code Future}, which may be an internal system thread such as an + * RPC network thread. + *
+ *

+ *

Also note that, regardless of which thread executes the {@code + * function.apply}, all other registered but unexecuted listeners are + * prevented from running during its execution, even if those listeners are + * to run in other executors. + *

+ *

The returned {@code Future} attempts to keep its cancellation state in + * sync with that of the input future. That is, if the returned {@code Future} + * is cancelled, it will attempt to cancel the input, and if the input is + * cancelled, the returned {@code Future} will receive a callback in which it + * will attempt to cancel itself. + *

+ *

An example use of this method is to convert a serializable object + * returned from an RPC into a POJO. + * + * @param input The future to transform + * @param function A Function to transform the results of the provided future + * to the results of the returned future. This will be run in the thread + * that notifies input it is complete. + * @return A future that holds result of the transformation. + * @since 9.0 (in 1.0 as {@code compose}) + */ + public static ListenableFuture transform(ListenableFuture input, + final Function function) { + checkNotNull(function); + ChainingListenableFuture output = + new ChainingListenableFuture(asAsyncFunction(function), input); + input.addListener(output, MoreExecutors.directExecutor()); + return output; + } + + /** + * Wraps the given function as an AsyncFunction. + */ + private static AsyncFunction asAsyncFunction( + final Function function) { + return new AsyncFunction() { + @Override + public ListenableFuture apply(I input) { + O output = function.apply(input); + return immediateFuture(output); + } + }; + } + + private abstract static class ImmediateFuture + implements ListenableFuture { + + private static final Logger log = + Logger.getLogger(ImmediateFuture.class.getName()); + + @Override + public void addListener(Runnable listener, Executor executor) { + checkNotNull(listener, "Runnable was null."); + checkNotNull(executor, "Executor was null."); + try { + executor.execute(listener); + } catch (RuntimeException e) { + // ListenableFuture's contract is that it will not throw unchecked + // exceptions, so log the bad runnable and/or executor and swallow it. + log.log(Level.SEVERE, "RuntimeException while executing runnable " + + listener + " with executor " + executor, e); + } + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + @Override + public abstract V get() throws ExecutionException; + + @Override + public V get(long timeout, TimeUnit unit) throws ExecutionException { + checkNotNull(unit); + return get(); + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return true; + } + } + + private static class ImmediateSuccessfulFuture extends ImmediateFuture { + + private final V value; + + ImmediateSuccessfulFuture(V value) { + this.value = value; + } + + @Override + public V get() { + return value; + } + } + + private static class ImmediateFailedFuture extends ImmediateFuture { + + private final Throwable thrown; + + ImmediateFailedFuture(Throwable thrown) { + this.thrown = thrown; + } + + @Override + public V get() throws ExecutionException { + throw new ExecutionException(thrown); + } + } + + /** + * An implementation of {@code ListenableFuture} that also implements + * {@code Runnable} so that it can be used to nest ListenableFutures. + * Once the passed-in {@code ListenableFuture} is complete, it calls the + * passed-in {@code Function} to generate the result. + *

+ *

For historical reasons, this class has a special case in its exception + * handling: If the given {@code AsyncFunction} throws an {@code + * UndeclaredThrowableException}, {@code ChainingListenableFuture} unwraps it + * and uses its cause as the output future's exception, rather than + * using the {@code UndeclaredThrowableException} itself as it would for other + * exception types. The reason for this is that {@code Futures.transform} used + * to require a {@code Function}, whose {@code apply} method is not allowed to + * throw checked exceptions. Nowadays, {@code Futures.transform} has an + * overload that accepts an {@code AsyncFunction}, whose {@code apply} method + * is allowed to throw checked exception. Users who wish to throw + * checked exceptions should use that overload instead, and we + * should remove the {@code UndeclaredThrowableException} special case. + */ + private static class ChainingListenableFuture + extends AbstractFuture implements Runnable { + + private AsyncFunction function; + private ListenableFuture inputFuture; + private volatile ListenableFuture outputFuture; + + private ChainingListenableFuture( + AsyncFunction function, + ListenableFuture inputFuture) { + this.function = checkNotNull(function); + this.inputFuture = checkNotNull(inputFuture); + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + /* + * Our additional cancellation work needs to occur even if + * !mayInterruptIfRunning, so we can't move it into interruptTask(). + */ + if (super.cancel(mayInterruptIfRunning)) { + // This should never block since only one thread is allowed to cancel + // this Future. + cancel(inputFuture, mayInterruptIfRunning); + cancel(outputFuture, mayInterruptIfRunning); + return true; + } + return false; + } + + private void cancel(Future future, + boolean mayInterruptIfRunning) { + if (future != null) { + future.cancel(mayInterruptIfRunning); + } + } + + @Override + public void run() { + try { + I sourceResult; + try { + sourceResult = Uninterruptibles.getUninterruptibly(inputFuture); + } catch (CancellationException e) { + // Cancel this future and return. + // At this point, inputFuture is cancelled and outputFuture doesn't + // exist, so the value of mayInterruptIfRunning is irrelevant. + cancel(false); + return; + } catch (ExecutionException e) { + // Set the cause of the exception as this future's exception + setException(e.getCause()); + return; + } + + final ListenableFuture outputFuture = this.outputFuture = + Preconditions.checkNotNull( + function.apply(sourceResult), + "AsyncFunction may not return null."); + if (isCancelled()) { + outputFuture.cancel(wasInterrupted()); + this.outputFuture = null; + return; + } + outputFuture.addListener(new Runnable() { + @Override + public void run() { + try { + set(Uninterruptibles.getUninterruptibly(outputFuture)); + } catch (CancellationException e) { + // Cancel this future and return. + // At this point, inputFuture and outputFuture are done, so the + // value of mayInterruptIfRunning is irrelevant. + cancel(false); + } catch (ExecutionException e) { + // Set the cause of the exception as this future's exception + setException(e.getCause()); + } finally { + // Don't pin inputs beyond completion + Futures.ChainingListenableFuture.this.outputFuture = null; + } + } + }, MoreExecutors.directExecutor()); + } catch (UndeclaredThrowableException e) { + // Set the cause of the exception as this future's exception + setException(e.getCause()); + } catch (Throwable t) { + // This exception is irrelevant in this thread, but useful for the + // client + setException(t); + } finally { + // Don't pin inputs beyond completion + function = null; + inputFuture = null; + } + } + } + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/GenericMapMaker.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/GenericMapMaker.java new file mode 100644 index 0000000000..73faba0132 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/GenericMapMaker.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +/** + * A class exactly like {@link MapMaker}, except restricted in the types of maps it can build. + * For the most part, you should probably just ignore the existence of this class. + * + * @param the base type for all key types of maps built by this map maker + * @param the base type for all value types of maps built by this map maker + * @author Kevin Bourrillion + * @since 7.0 + * @deprecated This class existed only to support the generic paramterization necessary for the + * caching functionality in {@code MapMaker}. That functionality has been moved to {@link + * CacheBuilder}, which is a properly generified class and thus needs no + * "Generic" equivalent; simple use {@code CacheBuilder} naturally. For general migration + * instructions, see the MapMaker Migration + * Guide. + */ +@Deprecated +abstract class GenericMapMaker { + + @SuppressWarnings("unchecked") + // safe covariant cast + MapMaker.RemovalListener getRemovalListener() { + return (MapMaker.RemovalListener) GenericMapMaker.NullListener.INSTANCE; + } + + enum NullListener implements MapMaker.RemovalListener { + INSTANCE; + + @Override + public void onRemoval(MapMaker.RemovalNotification notification) { + } + } + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/HashBasedTable.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/HashBasedTable.java new file mode 100644 index 0000000000..8680cbb3ba --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/HashBasedTable.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +/** + * Implementation of {@link Table} using hash tables. + *

+ *

The views returned by {@link #column}, {@link #columnKeySet()}, and {@link + * #columnMap()} have iterators that don't support {@code remove()}. Otherwise, + * all optional operations are supported. Null row keys, columns keys, and + * values are not supported. + *

+ *

Lookups by row key are often faster than lookups by column key, because + * the data is stored in a {@code Map>}. A method call like {@code + * column(columnKey).get(rowKey)} still runs quickly, since the row key is + * provided. However, {@code column(columnKey).size()} takes longer, since an + * iteration across all row keys occurs. + *

+ *

Note that this implementation is not synchronized. If multiple threads + * access this table concurrently and one of the threads modifies the table, it + * must be synchronized externally. + *

+ *

See the Guava User Guide article on + * {@code Table}. + * + * @author Jared Levy + * @since 7.0 + */ +public class HashBasedTable extends StandardTable { + private static final long serialVersionUID = 0; + + private HashBasedTable(Map> backingMap, Factory factory) { + super(backingMap, factory); + } + + /** + * Creates an empty {@code HashBasedTable}. + */ + public static HashBasedTable create() { + return new HashBasedTable( + new HashMap>(), new Factory(0)); + } + + // Overriding so NullPointerTester test passes. + + @Override + public boolean contains( + Object rowKey, Object columnKey) { + return super.contains(rowKey, columnKey); + } + + @Override + public boolean containsColumn(Object columnKey) { + return super.containsColumn(columnKey); + } + + @Override + public boolean containsValue(Object value) { + return super.containsValue(value); + } + + @Override + public V get(Object rowKey, Object columnKey) { + return super.get(rowKey, columnKey); + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } + + private static class Factory + implements Supplier>, Serializable { + private static final long serialVersionUID = 0; + final int expectedSize; + + Factory(int expectedSize) { + this.expectedSize = expectedSize; + } + + @Override + public Map get() { + return Maps.newHashMapWithExpectedSize(expectedSize); + } + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/HashMultimap.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/HashMultimap.java new file mode 100644 index 0000000000..818000d914 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/HashMultimap.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * Implementation of {@link Multimap} using hash tables. + *

+ *

The multimap does not store duplicate key-value pairs. Adding a new + * key-value pair equal to an existing key-value pair has no effect. + *

+ *

Keys and values may be null. All optional multimap methods are supported, + * and all returned views are modifiable. + *

+ *

This class is not threadsafe when any concurrent operations update the + * multimap. Concurrent read operations will work correctly. To allow concurrent + * update operations, wrap your multimap with a call to {@link + * Multimaps#synchronizedSetMultimap}. + * + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +public final class HashMultimap extends AbstractSetMultimap { + private static final int DEFAULT_VALUES_PER_KEY = 2; + private static final long serialVersionUID = 0; + private transient int expectedValuesPerKey = DEFAULT_VALUES_PER_KEY; + + private HashMultimap() { + super(new HashMap>()); + } + + /** + * Creates a new, empty {@code HashMultimap} with the default initial + * capacities. + */ + public static HashMultimap create() { + return new HashMultimap(); + } + + /** + * {@inheritDoc} + *

+ *

Creates an empty {@code HashSet} for a collection of values for one key. + * + * @return a new {@code HashSet} containing a collection of values for one key + */ + @Override + Set createCollection() { + return Sets.newHashSetWithExpectedSize(expectedValuesPerKey); + } + + /** + * @serialData expectedValuesPerKey, number of distinct keys, and then for + * each distinct key: the key, number of values for that key, and the + * key's values + */ + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeInt(expectedValuesPerKey); + Serialization.writeMultimap(this, stream); + } + + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + expectedValuesPerKey = stream.readInt(); + int distinctKeys = Serialization.readCount(stream); + Map> map = Maps.newHashMapWithExpectedSize(distinctKeys); + setMap(map); + Serialization.populateMultimap(this, stream, distinctKeys); + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/Hashing.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Hashing.java new file mode 100644 index 0000000000..72b992c18b --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Hashing.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +/** + * Static methods for implementing hash-based collections. + * + * @author Kevin Bourrillion + * @author Jesse Wilson + * @author Austin Appleby + */ +final class Hashing { + private static final int C1 = 0xcc9e2d51; + private static final int C2 = 0x1b873593; + + private Hashing() { + } + + /* + * This method was rewritten in Java from an intermediate step of the Murmur hash function in + * http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp, which contained the + * following header: + * + * MurmurHash3 was written by Austin Appleby, and is placed in the public domain. The author + * hereby disclaims copyright to this source code. + */ + static int smear(int hashCode) { + return C2 * Integer.rotateLeft(hashCode * C1, 15); + } + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/ImmutableEntry.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ImmutableEntry.java new file mode 100644 index 0000000000..03c6d0c25d --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ImmutableEntry.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.io.Serializable; + +/** + * @see Maps#immutableEntry(Object, Object) + */ +class ImmutableEntry extends AbstractMapEntry + implements Serializable { + private static final long serialVersionUID = 0; + private final K key; + private final V value; + + ImmutableEntry(K key, V value) { + this.key = key; + this.value = value; + } + + @Override + public final K getKey() { + return key; + } + + @Override + public final V getValue() { + return value; + } + + @Override + public final V setValue(V value) { + throw new UnsupportedOperationException(); + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/InetAddresses.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/InetAddresses.java new file mode 100644 index 0000000000..3936bee1cf --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/InetAddresses.java @@ -0,0 +1,368 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; + +/** + * Static utility methods pertaining to {@link InetAddress} instances. + *

+ *

Important note: Unlike {@code InetAddress.getByName()}, the + * methods of this class never cause DNS services to be accessed. For + * this reason, you should prefer these methods as much as possible over + * their JDK equivalents whenever you are expecting to handle only + * IP address string literals -- there is no blocking DNS penalty for a + * malformed string. + *

+ *

When dealing with {@link Inet4Address} and {@link Inet6Address} + * objects as byte arrays (vis. {@code InetAddress.getAddress()}) they + * are 4 and 16 bytes in length, respectively, and represent the address + * in network byte order. + *

+ *

Examples of IP addresses and their byte representations: + *

    + *
  • The IPv4 loopback address, {@code "127.0.0.1"}.
    + * {@code 7f 00 00 01} + *

    + *

  • The IPv6 loopback address, {@code "::1"}.
    + * {@code 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01} + *

    + *

  • From the IPv6 reserved documentation prefix ({@code 2001:db8::/32}), + * {@code "2001:db8::1"}.
    + * {@code 20 01 0d b8 00 00 00 00 00 00 00 00 00 00 00 01} + *

    + *

  • An IPv6 "IPv4 compatible" (or "compat") address, + * {@code "::192.168.0.1"}.
    + * {@code 00 00 00 00 00 00 00 00 00 00 00 00 c0 a8 00 01} + *

    + *

  • An IPv6 "IPv4 mapped" address, {@code "::ffff:192.168.0.1"}.
    + * {@code 00 00 00 00 00 00 00 00 00 00 ff ff c0 a8 00 01} + *
+ *

+ *

A few notes about IPv6 "IPv4 mapped" addresses and their observed + * use in Java. + *

+ * "IPv4 mapped" addresses were originally a representation of IPv4 + * addresses for use on an IPv6 socket that could receive both IPv4 + * and IPv6 connections (by disabling the {@code IPV6_V6ONLY} socket + * option on an IPv6 socket). Yes, it's confusing. Nevertheless, + * these "mapped" addresses were never supposed to be seen on the + * wire. That assumption was dropped, some say mistakenly, in later + * RFCs with the apparent aim of making IPv4-to-IPv6 transition simpler. + *

+ *

Technically one can create a 128bit IPv6 address with the wire + * format of a "mapped" address, as shown above, and transmit it in an + * IPv6 packet header. However, Java's InetAddress creation methods + * appear to adhere doggedly to the original intent of the "mapped" + * address: all "mapped" addresses return {@link Inet4Address} objects. + *

+ *

For added safety, it is common for IPv6 network operators to filter + * all packets where either the source or destination address appears to + * be a "compat" or "mapped" address. Filtering suggestions usually + * recommend discarding any packets with source or destination addresses + * in the invalid range {@code ::/3}, which includes both of these bizarre + * address formats. For more information on "bogons", including lists + * of IPv6 bogon space, see: + *

+ *

+ * + * @author Erik Kline + * @since 5.0 + */ +public final class InetAddresses { + private static final int IPV4_PART_COUNT = 4; + private static final int IPV6_PART_COUNT = 8; + + private InetAddresses() { + } + + private static byte[] ipStringToBytes(String ipString) { + // Make a first pass to categorize the characters in this string. + boolean hasColon = false; + boolean hasDot = false; + for (int i = 0; i < ipString.length(); i++) { + char c = ipString.charAt(i); + if (c == '.') { + hasDot = true; + } else if (c == ':') { + if (hasDot) { + return null; // Colons must not appear after dots. + } + hasColon = true; + } else if (Character.digit(c, 16) == -1) { + return null; // Everything else must be a decimal or hex digit. + } + } + + // Now decide which address family to parse. + if (hasColon) { + if (hasDot) { + ipString = convertDottedQuadToHex(ipString); + if (ipString == null) { + return null; + } + } + return textToNumericFormatV6(ipString); + } else if (hasDot) { + return textToNumericFormatV4(ipString); + } + return null; + } + + private static byte[] textToNumericFormatV4(String ipString) { + String[] address = ipString.split("\\.", IPV4_PART_COUNT + 1); + if (address.length != IPV4_PART_COUNT) { + return null; + } + + byte[] bytes = new byte[IPV4_PART_COUNT]; + try { + for (int i = 0; i < bytes.length; i++) { + bytes[i] = parseOctet(address[i]); + } + } catch (NumberFormatException ex) { + return null; + } + + return bytes; + } + + private static byte[] textToNumericFormatV6(String ipString) { + // An address can have [2..8] colons, and N colons make N+1 parts. + String[] parts = ipString.split(":", IPV6_PART_COUNT + 2); + if (parts.length < 3 || parts.length > IPV6_PART_COUNT + 1) { + return null; + } + + // Disregarding the endpoints, find "::" with nothing in between. + // This indicates that a run of zeroes has been skipped. + int skipIndex = -1; + for (int i = 1; i < parts.length - 1; i++) { + if (parts[i].length() == 0) { + if (skipIndex >= 0) { + return null; // Can't have more than one :: + } + skipIndex = i; + } + } + + int partsHi; // Number of parts to copy from above/before the "::" + int partsLo; // Number of parts to copy from below/after the "::" + if (skipIndex >= 0) { + // If we found a "::", then check if it also covers the endpoints. + partsHi = skipIndex; + partsLo = parts.length - skipIndex - 1; + if (parts[0].length() == 0 && --partsHi != 0) { + return null; // ^: requires ^:: + } + if (parts[parts.length - 1].length() == 0 && --partsLo != 0) { + return null; // :$ requires ::$ + } + } else { + // Otherwise, allocate the entire address to partsHi. The endpoints + // could still be empty, but parseHextet() will check for that. + partsHi = parts.length; + partsLo = 0; + } + + // If we found a ::, then we must have skipped at least one part. + // Otherwise, we must have exactly the right number of parts. + int partsSkipped = IPV6_PART_COUNT - (partsHi + partsLo); + if (!(skipIndex >= 0 ? partsSkipped >= 1 : partsSkipped == 0)) { + return null; + } + + // Now parse the hextets into a byte array. + ByteBuffer rawBytes = ByteBuffer.allocate(2 * IPV6_PART_COUNT); + try { + for (int i = 0; i < partsHi; i++) { + rawBytes.putShort(parseHextet(parts[i])); + } + for (int i = 0; i < partsSkipped; i++) { + rawBytes.putShort((short) 0); + } + for (int i = partsLo; i > 0; i--) { + rawBytes.putShort(parseHextet(parts[parts.length - i])); + } + } catch (NumberFormatException ex) { + return null; + } + return rawBytes.array(); + } + + private static String convertDottedQuadToHex(String ipString) { + int lastColon = ipString.lastIndexOf(':'); + String initialPart = ipString.substring(0, lastColon + 1); + String dottedQuad = ipString.substring(lastColon + 1); + byte[] quad = textToNumericFormatV4(dottedQuad); + if (quad == null) { + return null; + } + String penultimate = Integer.toHexString(((quad[0] & 0xff) << 8) | (quad[1] & 0xff)); + String ultimate = Integer.toHexString(((quad[2] & 0xff) << 8) | (quad[3] & 0xff)); + return initialPart + penultimate + ":" + ultimate; + } + + private static byte parseOctet(String ipPart) { + // Note: we already verified that this string contains only hex digits. + int octet = Integer.parseInt(ipPart); + // Disallow leading zeroes, because no clear standard exists on + // whether these should be interpreted as decimal or octal. + if (octet > 255 || (ipPart.startsWith("0") && ipPart.length() > 1)) { + throw new NumberFormatException(); + } + return (byte) octet; + } + + private static short parseHextet(String ipPart) { + // Note: we already verified that this string contains only hex digits. + int hextet = Integer.parseInt(ipPart, 16); + if (hextet > 0xffff) { + throw new NumberFormatException(); + } + return (short) hextet; + } + + /** + * Convert a byte array into an InetAddress. + *

+ * {@link InetAddress#getByAddress} is documented as throwing a checked + * exception "if IP address if of illegal length." We replace it with + * an unchecked exception, for use by callers who already know that addr + * is an array of length 4 or 16. + * + * @param addr the raw 4-byte or 16-byte IP address in big-endian order + * @return an InetAddress object created from the raw IP address + */ + private static InetAddress bytesToInetAddress(byte[] addr) { + try { + return InetAddress.getByAddress(addr); + } catch (UnknownHostException e) { + throw new AssertionError(e); + } + } + + /** + * Returns an InetAddress representing the literal IPv4 or IPv6 host + * portion of a URL, encoded in the format specified by RFC 3986 section 3.2.2. + *

+ *

This function is similar to {@link InetAddresses#forString(String)}, + * however, it requires that IPv6 addresses are surrounded by square brackets. + * + * @param hostAddr A RFC 3986 section 3.2.2 encoded IPv4 or IPv6 address + * @return an InetAddress representing the address in {@code hostAddr} + * @throws IllegalArgumentException if {@code hostAddr} is not a valid + * IPv4 address, or IPv6 address surrounded by square brackets + */ + private static InetAddress forUriString(String hostAddr) { + Preconditions.checkNotNull(hostAddr); + + // Decide if this should be an IPv6 or IPv4 address. + String ipString; + int expectBytes; + if (hostAddr.startsWith("[") && hostAddr.endsWith("]")) { + ipString = hostAddr.substring(1, hostAddr.length() - 1); + expectBytes = 16; + } else { + ipString = hostAddr; + expectBytes = 4; + } + + // Parse the address, and make sure the length/version is correct. + byte[] addr = ipStringToBytes(ipString); + if (addr == null || addr.length != expectBytes) { + throw new IllegalArgumentException( + String.format("Not a valid URI IP literal: '%s'", hostAddr)); + } + + return bytesToInetAddress(addr); + } + + /** + * Returns {@code true} if the supplied string is a valid URI IP string + * literal, {@code false} otherwise. + * + * @param ipString {@code String} to evaluated as an IP URI host string literal + * @return {@code true} if the argument is a valid IP URI host + */ + public static boolean isUriInetAddress(String ipString) { + try { + forUriString(ipString); + return true; + } catch (IllegalArgumentException e) { + return false; + } + } + + /** + * Evaluates whether the argument is an "IPv4 mapped" IPv6 address. + *

+ *

An "IPv4 mapped" address is anything in the range ::ffff:0:0/96 + * (sometimes written as ::ffff:0.0.0.0/96), with the last 32 bits + * interpreted as an IPv4 address. + *

+ *

For more on IPv4 mapped addresses see section 2.5.5.2 of + * http://tools.ietf.org/html/rfc4291 + *

+ *

Note: This method takes a {@code String} argument because + * {@link InetAddress} automatically collapses mapped addresses to IPv4. + * (It is actually possible to avoid this using one of the obscure + * {@link Inet6Address} methods, but it would be unwise to depend on such + * a poorly-documented feature.) + * + * @param ipString {@code String} to be examined for embedded IPv4-mapped IPv6 address format + * @return {@code true} if the argument is a valid "mapped" address + * @since 10.0 + */ + public static boolean isMappedIPv4Address(String ipString) { + byte[] bytes = ipStringToBytes(ipString); + if (bytes != null && bytes.length == 16) { + for (int i = 0; i < 10; i++) { + if (bytes[i] != 0) { + return false; + } + } + for (int i = 10; i < 12; i++) { + if (bytes[i] != (byte) 0xff) { + return false; + } + } + return true; + } + return false; + } + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/Ints.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Ints.java new file mode 100644 index 0000000000..cbbafb4b1b --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Ints.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Arrays; + +/** + * Static utility methods pertaining to {@code int} primitives, that are not + * already found in either {@link Integer} or {@link Arrays}. + *

+ *

See the Guava User Guide article on + * primitive utilities. + * + * @author Kevin Bourrillion + * @since 1.0 + */ +final class Ints { + /** + * The largest power of two that can be represented as an {@code int}. + * + * @since 10.0 + */ + public static final int MAX_POWER_OF_TWO = 1 << (Integer.SIZE - 2); + private static final byte[] asciiDigits = new byte[128]; + + static { + Arrays.fill(asciiDigits, (byte) -1); + for (int i = 0; i <= 9; i++) { + asciiDigits['0' + i] = (byte) i; + } + for (int i = 0; i <= 26; i++) { + asciiDigits['A' + i] = (byte) (10 + i); + asciiDigits['a' + i] = (byte) (10 + i); + } + } + + private Ints() { + } + + /** + * Returns the {@code int} nearest in value to {@code value}. + * + * @param value any {@code long} value + * @return the same value cast to {@code int} if it is in the range of the + * {@code int} type, {@link Integer#MAX_VALUE} if it is too large, + * or {@link Integer#MIN_VALUE} if it is too small + */ + public static int saturatedCast(long value) { + if (value > Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } + if (value < Integer.MIN_VALUE) { + return Integer.MIN_VALUE; + } + return (int) value; + } + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/Iterables.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Iterables.java new file mode 100644 index 0000000000..184dea518c --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Iterables.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.function.Predicate; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; + +/** + * This class contains static utility methods that operate on or return objects + * of type {@code Iterable}. Except as noted, each method has a corresponding + * {@link Iterator}-based method in the {@link Iterators} class. + *

+ *

Performance notes: Unless otherwise noted, all of the iterables + * produced in this class are lazy, which means that their iterators + * only advance the backing iteration when absolutely necessary. + *

+ *

See the Guava User Guide article on + * {@code Iterables}. + * + * @author Kevin Bourrillion + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +final class Iterables { + private Iterables() { + } + + /** + * Returns the first element in {@code iterable} or {@code defaultValue} if + * the iterable is empty. The {@link Iterators} analog to this method is + * {@link Iterators#getNext}. + *

+ *

If no default value is desired (and the caller instead wants a + * {@link NoSuchElementException} to be thrown), it is recommended that + * {@code iterable.iterator().next()} is used instead. + * + * @param defaultValue the default value to return if the iterable is empty + * @return the first element of {@code iterable} or the default value + * @since 7.0 + */ + public static T getFirst(Iterable iterable, T defaultValue) { + return Iterators.getNext(iterable.iterator(), defaultValue); + } + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/Iterators.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Iterators.java new file mode 100644 index 0000000000..1df5b9a31d --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Iterators.java @@ -0,0 +1,535 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Predicate; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkArgument; +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; +import static org.glassfish.jersey.internal.guava.Preconditions.checkState; +import static org.glassfish.jersey.internal.guava.Predicates.in; + +/** + * This class contains static utility methods that operate on or return objects + * of type {@link Iterator}. Except as noted, each method has a corresponding + * {@link Iterable}-based method in the {@link Iterables} class. + *

+ *

Performance notes: Unless otherwise noted, all of the iterators + * produced in this class are lazy, which means that they only advance + * the backing iteration when absolutely necessary. + *

+ *

See the Guava User Guide section on + * {@code Iterators}. + * + * @author Kevin Bourrillion + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +public final class Iterators { + private static final UnmodifiableListIterator EMPTY_LIST_ITERATOR + = new UnmodifiableListIterator() { + @Override + public boolean hasNext() { + return false; + } + + @Override + public Object next() { + throw new NoSuchElementException(); + } + + @Override + public boolean hasPrevious() { + return false; + } + + @Override + public Object previous() { + throw new NoSuchElementException(); + } + + @Override + public int nextIndex() { + return 0; + } + + @Override + public int previousIndex() { + return -1; + } + }; + private static final Iterator EMPTY_MODIFIABLE_ITERATOR = + new Iterator() { + @Override + public boolean hasNext() { + return false; + } + + @Override + public Object next() { + throw new NoSuchElementException(); + } + + @Override + public void remove() { + CollectPreconditions.checkRemove(false); + } + }; + + private Iterators() { + } + + /** + * Returns the empty iterator. + *

+ *

The {@link Iterable} equivalent of this method is {@link + * ImmutableSet#of()}. + * + * @deprecated Use {@code ImmutableSet.of().iterator()} instead; or for + * Java 7 or later, {@link Collections#emptyIterator}. This method is + * scheduled for removal in May 2016. + */ + @Deprecated + public static UnmodifiableIterator emptyIterator() { + return emptyListIterator(); + } + + /** + * Returns the empty iterator. + *

+ *

The {@link Iterable} equivalent of this method is {@link + * ImmutableSet#of()}. + */ + // Casting to any type is safe since there are no actual elements. + @SuppressWarnings("unchecked") + private static UnmodifiableListIterator emptyListIterator() { + return (UnmodifiableListIterator) EMPTY_LIST_ITERATOR; + } + + /** + * Returns the empty {@code Iterator} that throws + * {@link IllegalStateException} instead of + * {@link UnsupportedOperationException} on a call to + * {@link Iterator#remove()}. + */ + // Casting to any type is safe since there are no actual elements. + @SuppressWarnings("unchecked") + static Iterator emptyModifiableIterator() { + return (Iterator) EMPTY_MODIFIABLE_ITERATOR; + } + + /** + * Returns an unmodifiable view of {@code iterator}. + */ + public static UnmodifiableIterator unmodifiableIterator( + final Iterator iterator) { + checkNotNull(iterator); + if (iterator instanceof UnmodifiableIterator) { + return (UnmodifiableIterator) iterator; + } + return new UnmodifiableIterator() { + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public T next() { + return iterator.next(); + } + }; + } + + /** + * Returns the number of elements remaining in {@code iterator}. The iterator + * will be left exhausted: its {@code hasNext()} method will return + * {@code false}. + */ + public static int size(Iterator iterator) { + int count = 0; + while (iterator.hasNext()) { + iterator.next(); + count++; + } + return count; + } + + /** + * Traverses an iterator and removes every element that belongs to the + * provided collection. The iterator will be left exhausted: its + * {@code hasNext()} method will return {@code false}. + * + * @param removeFrom the iterator to (potentially) remove elements from + * @param elementsToRemove the elements to remove + * @return {@code true} if any element was removed from {@code iterator} + */ + public static boolean removeAll( + Iterator removeFrom, Collection elementsToRemove) { + return removeIf(removeFrom, in(elementsToRemove)); + } + + /** + * Removes every element that satisfies the provided predicate from the + * iterator. The iterator will be left exhausted: its {@code hasNext()} + * method will return {@code false}. + * + * @param removeFrom the iterator to (potentially) remove elements from + * @param predicate a predicate that determines whether an element should + * be removed + * @return {@code true} if any elements were removed from the iterator + * @since 2.0 + */ + public static boolean removeIf( + Iterator removeFrom, Predicate predicate) { + checkNotNull(predicate); + boolean modified = false; + while (removeFrom.hasNext()) { + if (predicate.test(removeFrom.next())) { + removeFrom.remove(); + modified = true; + } + } + return modified; + } + + /** + * Determines whether two iterators contain equal elements in the same order. + * More specifically, this method returns {@code true} if {@code iterator1} + * and {@code iterator2} contain the same number of elements and every element + * of {@code iterator1} is equal to the corresponding element of + * {@code iterator2}. + *

+ *

Note that this will modify the supplied iterators, since they will have + * been advanced some number of elements forward. + */ + public static boolean elementsEqual( + Iterator iterator1, Iterator iterator2) { + while (iterator1.hasNext()) { + if (!iterator2.hasNext()) { + return false; + } + Object o1 = iterator1.next(); + Object o2 = iterator2.next(); + if (!Objects.equals(o1, o2)) { + return false; + } + } + return !iterator2.hasNext(); + } + + /** + * Adds all elements in {@code iterator} to {@code collection}. The iterator + * will be left exhausted: its {@code hasNext()} method will return + * {@code false}. + * + * @return {@code true} if {@code collection} was modified as a result of this + * operation + */ + public static boolean addAll( + Collection addTo, Iterator iterator) { + checkNotNull(addTo); + checkNotNull(iterator); + boolean wasModified = false; + while (iterator.hasNext()) { + wasModified |= addTo.add(iterator.next()); + } + return wasModified; + } + + /** + * Returns {@code true} if every element returned by {@code iterator} + * satisfies the given predicate. If {@code iterator} is empty, {@code true} + * is returned. + */ + public static boolean all( + Iterator iterator, Predicate predicate) { + checkNotNull(predicate); + while (iterator.hasNext()) { + T element = iterator.next(); + if (!predicate.test(element)) { + return false; + } + } + return true; + } + + /** + * Returns the index in {@code iterator} of the first element that satisfies + * the provided {@code predicate}, or {@code -1} if the Iterator has no such + * elements. + *

+ *

More formally, returns the lowest index {@code i} such that + * {@code predicate.apply(Iterators.get(iterator, i))} returns {@code true}, + * or {@code -1} if there is no such index. + *

+ *

If -1 is returned, the iterator will be left exhausted: its + * {@code hasNext()} method will return {@code false}. Otherwise, + * the iterator will be set to the element which satisfies the + * {@code predicate}. + * + * @since 2.0 + */ + private static int indexOf( + Iterator iterator, Predicate predicate) { + checkNotNull(predicate, "predicate"); + for (int i = 0; iterator.hasNext(); i++) { + T current = iterator.next(); + if (predicate.test(current)) { + return i; + } + } + return -1; + } + + /** + * Returns an iterator that applies {@code function} to each element of {@code + * fromIterator}. + *

+ *

The returned iterator supports {@code remove()} if the provided iterator + * does. After a successful {@code remove()} call, {@code fromIterator} no + * longer contains the corresponding element. + */ + public static Iterator transform(final Iterator fromIterator, + final Function function) { + checkNotNull(function); + return new TransformedIterator(fromIterator) { + @Override + T transform(F from) { + return function.apply(from); + } + }; + } + + /** + * Returns the next element in {@code iterator} or {@code defaultValue} if + * the iterator is empty. The {@link Iterables} analog to this method is + * {@link Iterables#getFirst}. + * + * @param defaultValue the default value to return if the iterator is empty + * @return the next element of {@code iterator} or the default value + * @since 7.0 + */ + public static T getNext(Iterator iterator, T defaultValue) { + return iterator.hasNext() ? iterator.next() : defaultValue; + } + + /** + * Deletes and returns the next value from the iterator, or returns + * {@code null} if there is no such value. + */ + static T pollNext(Iterator iterator) { + if (iterator.hasNext()) { + T result = iterator.next(); + iterator.remove(); + return result; + } else { + return null; + } + } + + // Methods only in Iterators, not in Iterables + + /** + * Clears the iterator using its remove method. + */ + static void clear(Iterator iterator) { + checkNotNull(iterator); + while (iterator.hasNext()) { + iterator.next(); + iterator.remove(); + } + } + + /** + * Returns an iterator containing the elements of {@code array} in order. The + * returned iterator is a view of the array; subsequent changes to the array + * will be reflected in the iterator. + *

+ *

Note: It is often preferable to represent your data using a + * collection type, for example using {@link Arrays#asList(Object[])}, making + * this method unnecessary. + *

+ *

The {@code Iterable} equivalent of this method is either {@link + * Arrays#asList(Object[])}, {@link ImmutableList#copyOf(Object[])}}, + * or {@link ImmutableList#of}. + */ + public static UnmodifiableIterator forArray(final T... array) { + return forArray(array, 0, array.length, 0); + } + + /** + * Returns a list iterator containing the elements in the specified range of + * {@code array} in order, starting at the specified index. + *

+ *

The {@code Iterable} equivalent of this method is {@code + * Arrays.asList(array).subList(offset, offset + length).listIterator(index)}. + */ + static UnmodifiableListIterator forArray( + final T[] array, final int offset, int length, int index) { + checkArgument(length >= 0); + int end = offset + length; + + // Technically we should give a slightly more descriptive error on overflow + Preconditions.checkPositionIndexes(offset, end, array.length); + Preconditions.checkPositionIndex(index, length); + if (length == 0) { + return emptyListIterator(); + } + + /* + * We can't use call the two-arg constructor with arguments (offset, end) + * because the returned Iterator is a ListIterator that may be moved back + * past the beginning of the iteration. + */ + return new AbstractIndexedListIterator(length, index) { + @Override + protected T get(int index) { + return array[offset + index]; + } + }; + } + + /** + * Returns an iterator containing only {@code value}. + *

+ *

The {@link Iterable} equivalent of this method is {@link + * Collections#singleton}. + */ + public static UnmodifiableIterator singletonIterator( + final T value) { + return new UnmodifiableIterator() { + boolean done; + + @Override + public boolean hasNext() { + return !done; + } + + @Override + public T next() { + if (done) { + throw new NoSuchElementException(); + } + done = true; + return value; + } + }; + } + + /** + * Returns a {@code PeekingIterator} backed by the given iterator. + *

+ *

Calls to the {@code peek} method with no intervening calls to {@code + * next} do not affect the iteration, and hence return the same object each + * time. A subsequent call to {@code next} is guaranteed to return the same + * object again. For example:

   {@code
+     * 

+ * PeekingIterator peekingIterator = + * Iterators.peekingIterator(Iterators.forArray("a", "b")); + * String a1 = peekingIterator.peek(); // returns "a" + * String a2 = peekingIterator.peek(); // also returns "a" + * String a3 = peekingIterator.next(); // also returns "a"}

+ *

+ *

Any structural changes to the underlying iteration (aside from those + * performed by the iterator's own {@link PeekingIterator#remove()} method) + * will leave the iterator in an undefined state. + *

+ *

The returned iterator does not support removal after peeking, as + * explained by {@link PeekingIterator#remove()}. + *

+ *

Note: If the given iterator is already a {@code PeekingIterator}, + * it might be returned to the caller, although this is neither + * guaranteed to occur nor required to be consistent. For example, this + * method might choose to pass through recognized implementations of + * {@code PeekingIterator} when the behavior of the implementation is + * known to meet the contract guaranteed by this method. + *

+ *

There is no {@link Iterable} equivalent to this method, so use this + * method to wrap each individual iterator as it is generated. + * + * @param iterator the backing iterator. The {@link PeekingIterator} assumes + * ownership of this iterator, so users should cease making direct calls + * to it after calling this method. + * @return a peeking iterator backed by that iterator. Apart from the + * additional {@link PeekingIterator#peek()} method, this iterator behaves + * exactly the same as {@code iterator}. + */ + public static PeekingIterator peekingIterator( + Iterator iterator) { + if (iterator instanceof PeekingImpl) { + // Safe to cast to because PeekingImpl only uses T + // covariantly (and cannot be subclassed to add non-covariant uses). + @SuppressWarnings("unchecked") + PeekingImpl peeking = (PeekingImpl) iterator; + return peeking; + } + return new PeekingImpl(iterator); + } + + /** + * Implementation of PeekingIterator that avoids peeking unless necessary. + */ + private static class PeekingImpl implements PeekingIterator { + + private final Iterator iterator; + private boolean hasPeeked; + private E peekedElement; + + public PeekingImpl(Iterator iterator) { + this.iterator = checkNotNull(iterator); + } + + @Override + public boolean hasNext() { + return hasPeeked || iterator.hasNext(); + } + + @Override + public E next() { + if (!hasPeeked) { + return iterator.next(); + } + E result = peekedElement; + hasPeeked = false; + peekedElement = null; + return result; + } + + @Override + public void remove() { + checkState(!hasPeeked, "Can't remove after you've peeked at next"); + iterator.remove(); + } + + @Override + public E peek() { + if (!hasPeeked) { + peekedElement = iterator.next(); + hasPeeked = true; + } + return peekedElement; + } + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/Joiner.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Joiner.java new file mode 100644 index 0000000000..75052c3846 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Joiner.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; + +/** + * An object which joins pieces of text (specified as an array, {@link Iterable}, varargs or even a + * {@link Map}) with a separator. It either appends the results to an {@link Appendable} or returns + * them as a {@link String}. Example:

   {@code
+ * 

+ * Joiner joiner = Joiner.on("; ").skipNulls(); + * . . . + * return joiner.join("Harry", null, "Ron", "Hermione");}

+ *

+ *

This returns the string {@code "Harry; Ron; Hermione"}. Note that all input elements are + * converted to strings using {@link Object#toString()} before being appended. + *

+ *

If neither {@link #skipNulls()} nor {@link #useForNull(String)} is specified, the joining + * methods will throw {@link NullPointerException} if any given element is null. + *

+ *

Warning: joiner instances are always immutable; a configuration method such as {@code + * useForNull} has no effect on the instance it is invoked on! You must store and use the new joiner + * instance returned by the method. This makes joiners thread-safe, and safe to store as {@code + * static final} constants.

   {@code
+ * 

+ * // Bad! Do not do this! + * Joiner joiner = Joiner.on(','); + * joiner.skipNulls(); // does nothing! + * return joiner.join("wrong", null, "wrong");}

+ *

+ *

See the Guava User Guide article on {@code Joiner}. + * + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ +public class Joiner { + private final String separator; + + private Joiner(String separator) { + this.separator = Preconditions.checkNotNull(separator); + } + + /** + * Returns a joiner which automatically places {@code separator} between consecutive elements. + */ + public static Joiner on() { + return new Joiner(", "); + } + + /** + * Appends the string representation of each of {@code parts}, using the previously configured + * separator between each, to {@code appendable}. + * + * @since 11.0 + */ + private A appendTo(A appendable, Iterator parts) throws IOException { + Preconditions.checkNotNull(appendable); + if (parts.hasNext()) { + appendable.append(toString(parts.next())); + while (parts.hasNext()) { + appendable.append(separator); + appendable.append(toString(parts.next())); + } + } + return appendable; + } + + /** + * Appends the string representation of each of {@code parts}, using the previously configured + * separator between each, to {@code builder}. Identical to {@link #appendTo(Appendable, + * Iterable)}, except that it does not throw {@link IOException}. + * + * @since 11.0 + */ + private StringBuilder appendTo(StringBuilder builder, Iterator parts) { + try { + appendTo((Appendable) builder, parts); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + return builder; + } + + /** + * Returns a {@code MapJoiner} using the given key-value separator, and the same configuration as + * this {@code Joiner} otherwise. + */ + public MapJoiner withKeyValueSeparator() { + return new MapJoiner(this, "="); + } + + private CharSequence toString(Object part) { + Preconditions.checkNotNull(part); // checkNotNull for GWT (do not optimize). + return (part instanceof CharSequence) ? (CharSequence) part : part.toString(); + } + + /** + * An object that joins map entries in the same manner as {@code Joiner} joins iterables and + * arrays. Like {@code Joiner}, it is thread-safe and immutable. + *

+ *

In addition to operating on {@code Map} instances, {@code MapJoiner} can operate on {@code + * Multimap} entries in two distinct modes: + *

+ *

+ * + * @since 2.0 (imported from Google Collections Library) + */ + public static final class MapJoiner { + private final Joiner joiner; + private final String keyValueSeparator; + + private MapJoiner(Joiner joiner, String keyValueSeparator) { + this.joiner = joiner; // only "this" is ever passed, so don't checkNotNull + this.keyValueSeparator = Preconditions.checkNotNull(keyValueSeparator); + } + + /** + * Appends the string representation of each entry of {@code map}, using the previously + * configured separator and key-value separator, to {@code builder}. Identical to {@link + * #appendTo(Appendable, Map)}, except that it does not throw {@link IOException}. + */ + public StringBuilder appendTo(StringBuilder builder, Map map) { + return appendTo(builder, map.entrySet()); + } + + /** + * Appends the string representation of each entry in {@code entries}, using the previously + * configured separator and key-value separator, to {@code appendable}. + * + * @since 11.0 + */ + public A appendTo(A appendable, Iterator> parts) + throws IOException { + Preconditions.checkNotNull(appendable); + if (parts.hasNext()) { + Entry entry = parts.next(); + appendable.append(joiner.toString(entry.getKey())); + appendable.append(keyValueSeparator); + appendable.append(joiner.toString(entry.getValue())); + while (parts.hasNext()) { + appendable.append(joiner.separator); + Entry e = parts.next(); + appendable.append(joiner.toString(e.getKey())); + appendable.append(keyValueSeparator); + appendable.append(joiner.toString(e.getValue())); + } + } + return appendable; + } + + /** + * Appends the string representation of each entry in {@code entries}, using the previously + * configured separator and key-value separator, to {@code builder}. Identical to {@link + * #appendTo(Appendable, Iterable)}, except that it does not throw {@link IOException}. + * + * @since 10.0 + */ + public StringBuilder appendTo(StringBuilder builder, Iterable> entries) { + return appendTo(builder, entries.iterator()); + } + + /** + * Appends the string representation of each entry in {@code entries}, using the previously + * configured separator and key-value separator, to {@code builder}. Identical to {@link + * #appendTo(Appendable, Iterable)}, except that it does not throw {@link IOException}. + * + * @since 11.0 + */ + public StringBuilder appendTo(StringBuilder builder, Iterator> entries) { + try { + appendTo((Appendable) builder, entries); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + return builder; + } + + } + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/ListMultimap.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ListMultimap.java new file mode 100644 index 0000000000..fbd94a68d5 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ListMultimap.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * A {@code Multimap} that can hold duplicate key-value pairs and that maintains + * the insertion ordering of values for a given key. See the {@link Multimap} + * documentation for information common to all multimaps. + *

+ *

The {@link #get}, {@link #removeAll}, and {@link #replaceValues} methods + * each return a {@link List} of values. Though the method signature doesn't say + * so explicitly, the map returned by {@link #asMap} has {@code List} values. + *

+ *

See the Guava User Guide article on + * {@code Multimap}. + * + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +public interface ListMultimap extends Multimap { + /** + * {@inheritDoc} + *

+ *

Because the values for a given key may have duplicates and follow the + * insertion ordering, this method returns a {@link List}, instead of the + * {@link Collection} specified in the {@link Multimap} interface. + */ + @Override + List get(K key); + + /** + * {@inheritDoc} + *

+ *

Because the values for a given key may have duplicates and follow the + * insertion ordering, this method returns a {@link List}, instead of the + * {@link Collection} specified in the {@link Multimap} interface. + */ + @Override + List removeAll(Object key); + + /** + * {@inheritDoc} + *

+ *

Note: The returned map's values are guaranteed to be of type + * {@link List}. To obtain this map with the more specific generic type + * {@code Map>}, call {@link Multimaps#asMap(ListMultimap)} + * instead. + */ + @Override + Map> asMap(); + + /** + * Compares the specified object to this multimap for equality. + *

+ *

Two {@code ListMultimap} instances are equal if, for each key, they + * contain the same values in the same order. If the value orderings disagree, + * the multimaps will not be considered equal. + *

+ *

An empty {@code ListMultimap} is equal to any other empty {@code + * Multimap}, including an empty {@code SetMultimap}. + */ + @Override + boolean equals(Object obj); +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/ListenableFuture.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ListenableFuture.java new file mode 100644 index 0000000000..d91092d2c2 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ListenableFuture.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; +import java.util.concurrent.RejectedExecutionException; + +/** + * A {@link Future} that accepts completion listeners. Each listener has an + * associated executor, and it is invoked using this executor once the future's + * computation is {@linkplain Future#isDone() complete}. If the computation has + * already completed when the listener is added, the listener will execute + * immediately. + *

+ *

See the Guava User Guide article on + * {@code ListenableFuture}. + *

+ *

Purpose

+ *

+ *

Most commonly, {@code ListenableFuture} is used as an input to another + * derived {@code Future}, as in {@link Futures#allAsList(Iterable) + * Futures.allAsList}. Many such methods are impossible to implement efficiently + * without listener support. + *

+ *

It is possible to call {@link #addListener addListener} directly, but this + * is uncommon because the {@code Runnable} interface does not provide direct + * access to the {@code Future} result. (Users who want such access may prefer + * {@link Futures#addCallback Futures.addCallback}.) Still, direct {@code + * addListener} calls are occasionally useful:

   {@code
+ *   final String name = ...;
+ *   inFlight.add(name);
+ *   ListenableFuture future = service.query(name);
+ *   future.addListener(new Runnable() {
+ *     public void run() {
+ *       processedCount.incrementAndGet();
+ *       inFlight.remove(name);
+ *       lastProcessed.set(name);
+ *       logger.info("Done with {0}", name);
+ *     }
+ *   }, executor);}
+ *

+ *

How to get an instance

+ *

+ *

Developers are encouraged to return {@code ListenableFuture} from their + * methods so that users can take advantages of the utilities built atop the + * class. The way that they will create {@code ListenableFuture} instances + * depends on how they currently create {@code Future} instances: + *

    + *
  • If they are returned from an {@code ExecutorService}, convert that + * service to a {@link ListeningExecutorService}, usually by calling {@link + * MoreExecutors#listeningDecorator(ExecutorService) + * MoreExecutors.listeningDecorator}. (Custom executors may find it more + * convenient to use {@link ListenableFutureTask} directly.) + *
  • If they are manually filled in by a call to {@link FutureTask#set} or a + * similar method, create a {@link SettableFuture} instead. (Users with more + * complex needs may prefer {@link AbstractFuture}.) + *
+ *

+ *

Occasionally, an API will return a plain {@code Future} and it will be + * impossible to change the return type. For this case, we provide a more + * expensive workaround in {@code JdkFutureAdapters}. However, when possible, it + * is more efficient and reliable to create a {@code ListenableFuture} directly. + * + * @author Sven Mawson + * @author Nishant Thakkar + * @since 1.0 + */ +public interface ListenableFuture extends Future { + /** + * Registers a listener to be {@linkplain Executor#execute(Runnable) run} on + * the given executor. The listener will run when the {@code Future}'s + * computation is {@linkplain Future#isDone() complete} or, if the computation + * is already complete, immediately. + *

+ *

There is no guaranteed ordering of execution of listeners, but any + * listener added through this method is guaranteed to be called once the + * computation is complete. + *

+ *

Exceptions thrown by a listener will be propagated up to the executor. + * Any exception thrown during {@code Executor.execute} (e.g., a {@code + * RejectedExecutionException} or an exception thrown by {@linkplain + * MoreExecutors#directExecutor direct execution}) will be caught and + * logged. + *

+ *

Note: For fast, lightweight listeners that would be safe to execute in + * any thread, consider {@link MoreExecutors#directExecutor}. For heavier + * listeners, {@code directExecutor()} carries some caveats. For + * example, the listener may run on an unpredictable or undesirable thread: + *

+ *

    + *
  • If this {@code Future} is done at the time {@code addListener} is + * called, {@code addListener} will execute the listener inline. + *
  • If this {@code Future} is not yet done, {@code addListener} will + * schedule the listener to be run by the thread that completes this {@code + * Future}, which may be an internal system thread such as an RPC network + * thread. + *
+ *

+ *

Also note that, regardless of which thread executes the + * {@code directExecutor()} listener, all other registered but unexecuted + * listeners are prevented from running during its execution, even if those + * listeners are to run in other executors. + *

+ *

This is the most general listener interface. For common operations + * performed using listeners, see {@link + * Futures}. For a simplified but general + * listener interface, see {@link + * Futures#addCallback addCallback()}. + * + * @param listener the listener to run when the computation is complete + * @param executor the executor to run the listener in + * @throws NullPointerException if the executor or listener was null + * @throws RejectedExecutionException if we tried to execute the listener + * immediately but the executor rejected it. + */ + void addListener(Runnable listener, Executor executor); +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/Lists.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Lists.java new file mode 100644 index 0000000000..f8b875cf34 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Lists.java @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.NoSuchElementException; +import java.util.Objects; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkElementIndex; +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; +import static org.glassfish.jersey.internal.guava.Preconditions.checkPositionIndex; +import static org.glassfish.jersey.internal.guava.Preconditions.checkPositionIndexes; +import static org.glassfish.jersey.internal.guava.Preconditions.checkState; + +/** + * Static utility methods pertaining to {@link List} instances. Also see this + * class's counterparts {@link Sets}, {@link Maps} and {@link Queues}. + *

+ *

See the Guava User Guide article on + * {@code Lists}. + * + * @author Kevin Bourrillion + * @author Mike Bostock + * @author Louis Wasserman + * @since 2.0 (imported from Google Collections Library) + */ +public final class Lists { + private Lists() { + } + + // ArrayList + + /** + * Creates a mutable, empty {@code ArrayList} instance (for Java 6 and + * earlier). + *

+ *

Note: if mutability is not required, use {@link + * ImmutableList#of()} instead. + *

+ *

Note for Java 7 and later: this method is now unnecessary and + * should be treated as deprecated. Instead, use the {@code ArrayList} + * {@linkplain ArrayList#ArrayList() constructor} directly, taking advantage + * of the new "diamond" syntax. + */ + private static ArrayList newArrayList() { + return new ArrayList(); + } + + /** + * Creates a mutable {@code ArrayList} instance containing the given + * elements; a very thin shortcut for creating an empty list then calling + * {@link Iterables#addAll}. + *

+ *

Note: if mutability is not required and the elements are + * non-null, use {@link ImmutableList#copyOf(Iterable)} instead. (Or, change + * {@code elements} to be a {@link FluentIterable} and call + * {@code elements.toList()}.) + *

+ *

Note for Java 7 and later: if {@code elements} is a {@link + * Collection}, you don't need this method. Use the {@code ArrayList} + * {@linkplain ArrayList#ArrayList(Collection) constructor} directly, taking + * advantage of the new "diamond" syntax. + */ + public static ArrayList newArrayList(Iterable elements) { + checkNotNull(elements); // for GWT + // Let ArrayList's sizing logic work, if possible + return (elements instanceof Collection) + ? new ArrayList(Collections2.cast(elements)) + : newArrayList(elements.iterator()); + } + + /** + * Creates a mutable {@code ArrayList} instance containing the given + * elements; a very thin shortcut for creating an empty list and then calling + * {@link Iterators#addAll}. + *

+ *

Note: if mutability is not required and the elements are + * non-null, use {@link ImmutableList#copyOf(Iterator)} instead. + */ + public static ArrayList newArrayList(Iterator elements) { + ArrayList list = newArrayList(); + Iterators.addAll(list, elements); + return list; + } + + /** + * Returns a reversed view of the specified list. For example, {@code + * Lists.reverse(Arrays.asList(1, 2, 3))} returns a list containing {@code 3, + * 2, 1}. The returned list is backed by this list, so changes in the returned + * list are reflected in this list, and vice-versa. The returned list supports + * all of the optional list operations supported by this list. + *

+ *

The returned list is random-access if the specified list is random + * access. + * + * @since 7.0 + */ + private static List reverse(List list) { + if (list instanceof ReverseList) { + return ((ReverseList) list).getForwardList(); + } else { + return new ReverseList(list); + } + } + + /** + * An implementation of {@link List#equals(Object)}. + */ + static boolean equalsImpl(List list, Object object) { + if (object == checkNotNull(list)) { + return true; + } + if (!(object instanceof List)) { + return false; + } + + List o = (List) object; + + return list.size() == o.size() + && Iterators.elementsEqual(list.iterator(), o.iterator()); + } + + /** + * An implementation of {@link List#indexOf(Object)}. + */ + static int indexOfImpl(List list, Object element) { + ListIterator listIterator = list.listIterator(); + while (listIterator.hasNext()) { + if (Objects.equals(element, listIterator.next())) { + return listIterator.previousIndex(); + } + } + return -1; + } + + /** + * An implementation of {@link List#lastIndexOf(Object)}. + */ + static int lastIndexOfImpl(List list, Object element) { + ListIterator listIterator = list.listIterator(list.size()); + while (listIterator.hasPrevious()) { + if (Objects.equals(element, listIterator.previous())) { + return listIterator.nextIndex(); + } + } + return -1; + } + + private static class ReverseList extends AbstractList { + private final List forwardList; + + ReverseList(List forwardList) { + this.forwardList = checkNotNull(forwardList); + } + + List getForwardList() { + return forwardList; + } + + private int reverseIndex(int index) { + int size = size(); + checkElementIndex(index, size); + return (size - 1) - index; + } + + private int reversePosition(int index) { + int size = size(); + checkPositionIndex(index, size); + return size - index; + } + + @Override + public void add(int index, T element) { + forwardList.add(reversePosition(index), element); + } + + @Override + public void clear() { + forwardList.clear(); + } + + @Override + public T remove(int index) { + return forwardList.remove(reverseIndex(index)); + } + + @Override + protected void removeRange(int fromIndex, int toIndex) { + subList(fromIndex, toIndex).clear(); + } + + @Override + public T set(int index, T element) { + return forwardList.set(reverseIndex(index), element); + } + + @Override + public T get(int index) { + return forwardList.get(reverseIndex(index)); + } + + @Override + public int size() { + return forwardList.size(); + } + + @Override + public List subList(int fromIndex, int toIndex) { + checkPositionIndexes(fromIndex, toIndex, size()); + return reverse(forwardList.subList( + reversePosition(toIndex), reversePosition(fromIndex))); + } + + @Override + public Iterator iterator() { + return listIterator(); + } + + @Override + public ListIterator listIterator(int index) { + int start = reversePosition(index); + final ListIterator forwardIterator = forwardList.listIterator(start); + return new ListIterator() { + + boolean canRemoveOrSet; + + @Override + public void add(T e) { + forwardIterator.add(e); + forwardIterator.previous(); + canRemoveOrSet = false; + } + + @Override + public boolean hasNext() { + return forwardIterator.hasPrevious(); + } + + @Override + public boolean hasPrevious() { + return forwardIterator.hasNext(); + } + + @Override + public T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + canRemoveOrSet = true; + return forwardIterator.previous(); + } + + @Override + public int nextIndex() { + return reversePosition(forwardIterator.nextIndex()); + } + + @Override + public T previous() { + if (!hasPrevious()) { + throw new NoSuchElementException(); + } + canRemoveOrSet = true; + return forwardIterator.next(); + } + + @Override + public int previousIndex() { + return nextIndex() - 1; + } + + @Override + public void remove() { + CollectPreconditions.checkRemove(canRemoveOrSet); + forwardIterator.remove(); + canRemoveOrSet = false; + } + + @Override + public void set(T e) { + checkState(canRemoveOrSet); + forwardIterator.set(e); + } + }; + } + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/LoadingCache.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/LoadingCache.java new file mode 100644 index 0000000000..f8806bf53f --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/LoadingCache.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.concurrent.ExecutionException; +import java.util.function.Function; + +/** + * A semi-persistent mapping from keys to values. Values are automatically loaded by the cache, + * and are stored in the cache until either evicted or manually invalidated. + *

+ *

Implementations of this interface are expected to be thread-safe, and can be safely accessed + * by multiple concurrent threads. + *

+ *

When evaluated as a {@link Function}, a cache yields the same result as invoking + * {@link #getUnchecked}. + *

+ *

Note that while this class is still annotated as {@link Beta}, the API is frozen from a + * consumer's standpoint. In other words existing methods are all considered {@code non-Beta} and + * won't be changed without going through an 18 month deprecation cycle; however new methods may be + * added at any time. + * + * @author Charles Fry + * @since 11.0 + */ +public interface LoadingCache extends Cache, Function { + + /** + * Returns the value associated with {@code key} in this cache, first loading that value if + * necessary. No observable state associated with this cache is modified until loading completes. + *

+ *

If another call to {@link #get} or {@link #getUnchecked} is currently loading the value for + * {@code key}, simply waits for that thread to finish and returns its loaded value. Note that + * multiple threads can concurrently load values for distinct keys. + *

+ *

Caches loaded by a {@link CacheLoader} will call {@link CacheLoader#load} to load new values + * into the cache. Newly loaded values are added to the cache using + * {@code Cache.asMap().putIfAbsent} after loading has completed; if another value was associated + * with {@code key} while the new value was loading then a removal notification will be sent for + * the new value. + *

+ *

If the cache loader associated with this cache is known not to throw checked + * exceptions, then prefer {@link #getUnchecked} over this method. + * + * @throws ExecutionException if a checked exception was thrown while loading the value. ({@code + * ExecutionException} is thrown even if + * computation was interrupted by an {@code InterruptedException}.) + * @throws UncheckedExecutionException if an unchecked exception was thrown while loading the + * value + * @throws ExecutionError if an error was thrown while loading the value + */ + V get(K key) throws ExecutionException; + + /** + * @throws UncheckedExecutionException if an exception was thrown while loading the value. (As + * described in the documentation for {@link #getUnchecked}, {@code LoadingCache} should be + * used as a {@code Function} only with cache loaders that throw only unchecked exceptions.) + * @deprecated Provided to satisfy the {@code Function} interface; use {@link #get} or + * {@link #getUnchecked} instead. + */ + @Deprecated + @Override + V apply(K key); + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/LocalCache.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/LocalCache.java new file mode 100644 index 0000000000..39944cc00c --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/LocalCache.java @@ -0,0 +1,4123 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.io.Serializable; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.AbstractCollection; +import java.util.AbstractMap; +import java.util.AbstractQueue; +import java.util.AbstractSet; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static org.glassfish.jersey.internal.guava.MoreExecutors.directExecutor; +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; +import static org.glassfish.jersey.internal.guava.Preconditions.checkState; +import static org.glassfish.jersey.internal.guava.Uninterruptibles.getUninterruptibly; + +import static java.util.concurrent.TimeUnit.NANOSECONDS; + +/** + * The concurrent hash map implementation built by {@link CacheBuilder}. + *

+ *

This implementation is heavily derived from revision 1.96 of ConcurrentHashMap.java. + * + * @author Charles Fry + * @author Bob Lee ({@code org.glassfish.jersey.internal.guava.MapMaker}) + * @author Doug Lea ({@code ConcurrentHashMap}) + */ +class LocalCache extends AbstractMap implements ConcurrentMap { + + /* + * The basic strategy is to subdivide the table among Segments, each of which itself is a + * concurrently readable hash table. The map supports non-blocking reads and concurrent writes + * across different segments. + * + * If a maximum size is specified, a best-effort bounding is performed per segment, using a + * page-replacement algorithm to determine which entries to evict when the capacity has been + * exceeded. + * + * The page replacement algorithm's data structures are kept casually consistent with the map. The + * ordering of writes to a segment is sequentially consistent. An update to the map and recording + * of reads may not be immediately reflected on the algorithm's data structures. These structures + * are guarded by a lock and operations are applied in batches to avoid lock contention. The + * penalty of applying the batches is spread across threads so that the amortized cost is slightly + * higher than performing just the operation without enforcing the capacity constraint. + * + * This implementation uses a per-segment queue to record a memento of the additions, removals, + * and accesses that were performed on the map. The queue is drained on writes and when it exceeds + * its capacity threshold. + * + * The Least Recently Used page replacement algorithm was chosen due to its simplicity, high hit + * rate, and ability to be implemented with O(1) time complexity. The initial LRU implementation + * operates per-segment rather than globally for increased implementation simplicity. We expect + * the cache hit rate to be similar to that of a global LRU algorithm. + */ + + // Constants + + /** + * The maximum capacity, used if a higher value is implicitly specified by either of the + * constructors with arguments. MUST be a power of two <= 1<<30 to ensure that entries are + * indexable using ints. + */ + private static final int MAXIMUM_CAPACITY = 1 << 30; + + /** + * The maximum number of segments to allow; used to bound constructor arguments. + */ + private static final int MAX_SEGMENTS = 1 << 16; // slightly conservative + + /** + * Number of (unsynchronized) retries in the containsValue method. + */ + private static final int CONTAINS_VALUE_RETRIES = 3; + + /** + * Number of cache access operations that can be buffered per segment before the cache's recency + * ordering information is updated. This is used to avoid lock contention by recording a memento + * of reads and delaying a lock acquisition until the threshold is crossed or a mutation occurs. + *

+ *

This must be a (2^n)-1 as it is used as a mask. + */ + private static final int DRAIN_THRESHOLD = 0x3F; + + /** + * Maximum number of entries to be drained in a single cleanup run. This applies independently to + * the cleanup queue and both reference queues. + */ + // TODO(fry): empirically optimize this + private static final int DRAIN_MAX = 16; + + // Fields + + private static final Logger logger = Logger.getLogger(LocalCache.class.getName()); + /** + * Placeholder. Indicates that the value hasn't been set yet. + */ + private static final ValueReference UNSET = new ValueReference() { + @Override + public Object get() { + return null; + } + + @Override + public int getWeight() { + return 0; + } + + @Override + public ReferenceEntry getEntry() { + return null; + } + + @Override + public ValueReference copyFor(ReferenceQueue queue, + Object value, ReferenceEntry entry) { + return this; + } + + @Override + public boolean isLoading() { + return false; + } + + @Override + public boolean isActive() { + return false; + } + + @Override + public Object waitForValue() { + return null; + } + + @Override + public void notifyNewValue(Object newValue) { + } + }; + private static final Queue DISCARDING_QUEUE = new AbstractQueue() { + @Override + public boolean offer(Object o) { + return true; + } + + @Override + public Object peek() { + return null; + } + + @Override + public Object poll() { + return null; + } + + @Override + public int size() { + return 0; + } + + @Override + public Iterator iterator() { + return Iterators.emptyIterator(); + } + }; + /** + * Mask value for indexing into segments. The upper bits of a key's hash code are used to choose + * the segment. + */ + private final int segmentMask; + /** + * Shift value for indexing within segments. Helps prevent entries that end up in the same segment + * from also ending up in the same bucket. + */ + private final int segmentShift; + /** + * The segments, each of which is a specialized hash table. + */ + private final Segment[] segments; + /** + * The concurrency level. + */ + private final int concurrencyLevel; + /** + * Strategy for comparing keys. + */ + private final Equivalence keyEquivalence; + /** + * Strategy for comparing values. + */ + private final Equivalence valueEquivalence; + /** + * Strategy for referencing keys. + */ + private final Strength keyStrength; + /** + * Strategy for referencing values. + */ + private final Strength valueStrength; + /** + * The maximum weight of this map. UNSET_INT if there is no maximum. + */ + private final long maxWeight; + /** + * How long after the last access to an entry the map will retain that entry. + */ + private final long expireAfterAccessNanos; + /** + * How long after the last write to an entry the map will retain that entry. + */ + private final long expireAfterWriteNanos; + /** + * How long after the last write an entry becomes a candidate for refresh. + */ + private final long refreshNanos; + /** + * Entries waiting to be consumed by the removal listener. + */ + // TODO(fry): define a new type which creates event objects and automates the clear logic + private final Queue> removalNotificationQueue; + /** + * Measures time in a testable way. + */ + private final Ticker ticker; + /** + * Factory used to create new entries. + */ + private final EntryFactory entryFactory; + /** + * The default cache loader to use on loading operations. + */ + private final CacheLoader defaultLoader; + private Set keySet; + private Collection values; + private Set> entrySet; + + /** + * Creates a new, empty map with the specified strategy, initial capacity and concurrency level. + */ + private LocalCache( + CacheBuilder builder, CacheLoader loader) { + concurrencyLevel = Math.min(builder.getConcurrencyLevel(), MAX_SEGMENTS); + + keyStrength = Strength.STRONG; + valueStrength = Strength.STRONG; + + keyEquivalence = keyStrength.defaultEquivalence(); + valueEquivalence = valueStrength.defaultEquivalence(); + + maxWeight = CacheBuilder.UNSET_INT; + expireAfterAccessNanos = builder.getExpireAfterAccessNanos(); + expireAfterWriteNanos = CacheBuilder.DEFAULT_EXPIRATION_NANOS; + refreshNanos = CacheBuilder.DEFAULT_REFRESH_NANOS; + + removalNotificationQueue = LocalCache.discardingQueue(); + + ticker = recordsTime() ? Ticker.systemTicker() : CacheBuilder.NULL_TICKER; + entryFactory = EntryFactory.getFactory(keyStrength, usesAccessEntries(), usesWriteEntries()); + defaultLoader = loader; + + int initialCapacity = Math.min(CacheBuilder.DEFAULT_INITIAL_CAPACITY, MAXIMUM_CAPACITY); + if (evictsBySize()) { + initialCapacity = Math.min(initialCapacity, (int) maxWeight); + } + + // Find the lowest power-of-two segmentCount that exceeds concurrencyLevel, unless + // maximumSize/Weight is specified in which case ensure that each segment gets at least 10 + // entries. The special casing for size-based eviction is only necessary because that eviction + // happens per segment instead of globally, so too many segments compared to the maximum size + // will result in random eviction behavior. + int segmentShift = 0; + int segmentCount = 1; + while (segmentCount < concurrencyLevel + && (!evictsBySize() || segmentCount * 20 <= maxWeight)) { + ++segmentShift; + segmentCount <<= 1; + } + this.segmentShift = 32 - segmentShift; + segmentMask = segmentCount - 1; + + this.segments = newSegmentArray(segmentCount); + + int segmentCapacity = initialCapacity / segmentCount; + if (segmentCapacity * segmentCount < initialCapacity) { + ++segmentCapacity; + } + + int segmentSize = 1; + while (segmentSize < segmentCapacity) { + segmentSize <<= 1; + } + + if (evictsBySize()) { + // Ensure sum of segment max weights = overall max weights + long maxSegmentWeight = maxWeight / segmentCount + 1; + long remainder = maxWeight % segmentCount; + for (int i = 0; i < this.segments.length; ++i) { + if (i == remainder) { + maxSegmentWeight--; + } + this.segments[i] = + createSegment(segmentSize, maxSegmentWeight); + } + } else { + for (int i = 0; i < this.segments.length; ++i) { + this.segments[i] = + createSegment(segmentSize, CacheBuilder.UNSET_INT); + } + } + } + + /** + * Singleton placeholder that indicates a value is being loaded. + */ + @SuppressWarnings("unchecked") // impl never uses a parameter or returns any non-null value + private static ValueReference unset() { + return (ValueReference) UNSET; + } + + @SuppressWarnings("unchecked") // impl never uses a parameter or returns any non-null value + private static ReferenceEntry nullEntry() { + return (ReferenceEntry) NullEntry.INSTANCE; + } + + /** + * Queue that discards all elements. + */ + @SuppressWarnings("unchecked") // impl never uses a parameter or returns any non-null value + private static Queue discardingQueue() { + return (Queue) DISCARDING_QUEUE; + } + + /** + * Applies a supplemental hash function to a given hash code, which defends against poor quality + * hash functions. This is critical when the concurrent hash map uses power-of-two length hash + * tables, that otherwise encounter collisions for hash codes that do not differ in lower or + * upper bits. + * + * @param h hash code + */ + private static int rehash(int h) { + // Spread bits to regularize both segment and index locations, + // using variant of single-word Wang/Jenkins hash. + // TODO(kevinb): use Hashing/move this to Hashing? + h += (h << 15) ^ 0xffffcd7d; + h ^= (h >>> 10); + h += (h << 3); + h ^= (h >>> 6); + h += (h << 2) + (h << 14); + return h ^ (h >>> 16); + } + + // Guarded By Segment.this + private static void connectAccessOrder(ReferenceEntry previous, ReferenceEntry next) { + previous.setNextInAccessQueue(next); + next.setPreviousInAccessQueue(previous); + } + + // Guarded By Segment.this + private static void nullifyAccessOrder(ReferenceEntry nulled) { + ReferenceEntry nullEntry = nullEntry(); + nulled.setNextInAccessQueue(nullEntry); + nulled.setPreviousInAccessQueue(nullEntry); + } + + // Guarded By Segment.this + private static void connectWriteOrder(ReferenceEntry previous, ReferenceEntry next) { + previous.setNextInWriteQueue(next); + next.setPreviousInWriteQueue(previous); + } + + // Guarded By Segment.this + private static void nullifyWriteOrder(ReferenceEntry nulled) { + ReferenceEntry nullEntry = nullEntry(); + nulled.setNextInWriteQueue(nullEntry); + nulled.setPreviousInWriteQueue(nullEntry); + } + + boolean evictsBySize() { + return maxWeight >= 0; + } + + private boolean expiresAfterWrite() { + return expireAfterWriteNanos > 0; + } + + private boolean expiresAfterAccess() { + return expireAfterAccessNanos > 0; + } + + boolean refreshes() { + return refreshNanos > 0; + } + + boolean usesAccessQueue() { + return expiresAfterAccess() || evictsBySize(); + } + + boolean usesWriteQueue() { + return expiresAfterWrite(); + } + + boolean recordsWrite() { + return expiresAfterWrite() || refreshes(); + } + + boolean recordsAccess() { + return expiresAfterAccess(); + } + + private boolean recordsTime() { + return recordsWrite() || recordsAccess(); + } + + private boolean usesWriteEntries() { + return usesWriteQueue() || recordsWrite(); + } + + private boolean usesAccessEntries() { + return usesAccessQueue() || recordsAccess(); + } + + /* + * Note: All of this duplicate code sucks, but it saves a lot of memory. If only Java had mixins! + * To maintain this code, make a change for the strong reference type. Then, cut and paste, and + * replace "Strong" with "Soft" or "Weak" within the pasted text. The primary difference is that + * strong entries store the key reference directly while soft and weak entries delegate to their + * respective superclasses. + */ + + boolean usesKeyReferences() { + return keyStrength != Strength.STRONG; + } + + boolean usesValueReferences() { + return valueStrength != Strength.STRONG; + } + + private int hash(Object key) { + int h = keyEquivalence.hash(key); + return rehash(h); + } + + void reclaimValue(ValueReference valueReference) { + ReferenceEntry entry = valueReference.getEntry(); + int hash = entry.getHash(); + segmentFor(hash).reclaimValue(entry.getKey(), hash, valueReference); + } + + void reclaimKey(ReferenceEntry entry) { + int hash = entry.getHash(); + segmentFor(hash).reclaimKey(entry, hash); + } + + /** + * Returns the segment that should be used for a key with the given hash. + * + * @param hash the hash code for the key + * @return the segment + */ + private Segment segmentFor(int hash) { + // TODO(fry): Lazily create segments? + return segments[(hash >>> segmentShift) & segmentMask]; + } + + private Segment createSegment( + int initialCapacity, long maxSegmentWeight) { + return new Segment(this, initialCapacity, maxSegmentWeight); + } + + /** + * Gets the value from an entry. Returns null if the entry is invalid, partially-collected, + * loading, or expired. Unlike {@link Segment#getLiveValue} this method does not attempt to + * cleanup stale entries. As such it should only be called outside of a segment context, such as + * during iteration. + */ + private V getLiveValue(ReferenceEntry entry, long now) { + if (entry.getKey() == null) { + return null; + } + V value = entry.getValueReference().get(); + if (value == null) { + return null; + } + + if (isExpired(entry, now)) { + return null; + } + return value; + } + + /** + * Returns true if the entry has expired. + */ + boolean isExpired(ReferenceEntry entry, long now) { + checkNotNull(entry); + if (expiresAfterAccess() + && (now - entry.getAccessTime() >= expireAfterAccessNanos)) { + return true; + } + if (expiresAfterWrite() + && (now - entry.getWriteTime() >= expireAfterWriteNanos)) { + return true; + } + return false; + } + + @SuppressWarnings("unchecked") + private Segment[] newSegmentArray(int ssize) { + return new Segment[ssize]; + } + + @Override + public boolean isEmpty() { + /* + * Sum per-segment modCounts to avoid mis-reporting when elements are concurrently added and + * removed in one segment while checking another, in which case the table was never actually + * empty at any point. (The sum ensures accuracy up through at least 1<<31 per-segment + * modifications before recheck.) Method containsValue() uses similar constructions for + * stability checks. + */ + long sum = 0L; + Segment[] segments = this.segments; + for (int i = 0; i < segments.length; ++i) { + if (segments[i].count != 0) { + return false; + } + sum += segments[i].modCount; + } + + if (sum != 0L) { // recheck unless no modifications + for (int i = 0; i < segments.length; ++i) { + if (segments[i].count != 0) { + return false; + } + sum -= segments[i].modCount; + } + if (sum != 0L) { + return false; + } + } + return true; + } + + private long longSize() { + Segment[] segments = this.segments; + long sum = 0; + for (int i = 0; i < segments.length; ++i) { + sum += segments[i].count; + } + return sum; + } + + @Override + public int size() { + return Ints.saturatedCast(longSize()); + } + + @Override + public V get(Object key) { + if (key == null) { + return null; + } + int hash = hash(key); + return segmentFor(hash).get(key, hash); + } + + V getIfPresent(Object key) { + int hash = hash(checkNotNull(key)); + return segmentFor(hash).get(key, hash); + } + + private V get(K key, CacheLoader loader) throws ExecutionException { + int hash = hash(checkNotNull(key)); + return segmentFor(hash).get(key, hash, loader); + } + + V getOrLoad(K key) throws ExecutionException { + return get(key, defaultLoader); + } + + @Override + public boolean containsKey(Object key) { + // does not impact recency ordering + if (key == null) { + return false; + } + int hash = hash(key); + return segmentFor(hash).containsKey(key, hash); + } + + @Override + public boolean containsValue(Object value) { + // does not impact recency ordering + if (value == null) { + return false; + } + + // This implementation is patterned after ConcurrentHashMap, but without the locking. The only + // way for it to return a false negative would be for the target value to jump around in the map + // such that none of the subsequent iterations observed it, despite the fact that at every point + // in time it was present somewhere int the map. This becomes increasingly unlikely as + // CONTAINS_VALUE_RETRIES increases, though without locking it is theoretically possible. + long now = ticker.read(); + final Segment[] segments = this.segments; + long last = -1L; + for (int i = 0; i < CONTAINS_VALUE_RETRIES; i++) { + long sum = 0L; + for (Segment segment : segments) { + // ensure visibility of most recent completed write + @SuppressWarnings({"UnusedDeclaration", "unused"}) + int c = segment.count; // read-volatile + + AtomicReferenceArray> table = segment.table; + for (int j = 0; j < table.length(); j++) { + for (ReferenceEntry e = table.get(j); e != null; e = e.getNext()) { + V v = segment.getLiveValue(e, now); + if (v != null && valueEquivalence.equivalent(value, v)) { + return true; + } + } + } + sum += segment.modCount; + } + if (sum == last) { + break; + } + last = sum; + } + return false; + } + + @Override + public V put(K key, V value) { + checkNotNull(key); + checkNotNull(value); + int hash = hash(key); + return segmentFor(hash).put(key, hash, value, false); + } + + // expiration + + @Override + public V putIfAbsent(K key, V value) { + checkNotNull(key); + checkNotNull(value); + int hash = hash(key); + return segmentFor(hash).put(key, hash, value, true); + } + + // queues + + @Override + public void putAll(Map m) { + for (Entry e : m.entrySet()) { + put(e.getKey(), e.getValue()); + } + } + + @Override + public V remove(Object key) { + if (key == null) { + return null; + } + int hash = hash(key); + return segmentFor(hash).remove(key, hash); + } + + @Override + public boolean remove(Object key, Object value) { + if (key == null || value == null) { + return false; + } + int hash = hash(key); + return segmentFor(hash).remove(key, hash, value); + } + + @Override + public boolean replace(K key, V oldValue, V newValue) { + checkNotNull(key); + checkNotNull(newValue); + if (oldValue == null) { + return false; + } + int hash = hash(key); + return segmentFor(hash).replace(key, hash, oldValue, newValue); + } + + @Override + public V replace(K key, V value) { + checkNotNull(key); + checkNotNull(value); + int hash = hash(key); + return segmentFor(hash).replace(key, hash, value); + } + + @Override + public void clear() { + for (Segment segment : segments) { + segment.clear(); + } + } + + // Inner Classes + + @Override + public Set keySet() { + // does not impact recency ordering + Set ks = keySet; + return (ks != null) ? ks : (keySet = new KeySet(this)); + } + + @Override + public Collection values() { + // does not impact recency ordering + Collection vs = values; + return (vs != null) ? vs : (values = new Values(this)); + } + + // Queues + + @Override + public Set> entrySet() { + // does not impact recency ordering + Set> es = entrySet; + return (es != null) ? es : (entrySet = new EntrySet(this)); + } + + enum Strength { + /* + * TODO(kevinb): If we strongly reference the value and aren't loading, we needn't wrap the + * value. This could save ~8 bytes per entry. + */ + + STRONG { + @Override + ValueReference referenceValue( + Segment segment, ReferenceEntry entry, V value, int weight) { + return (weight == 1) + ? new StrongValueReference(value) + : new WeightedStrongValueReference(value, weight); + } + + @Override + Equivalence defaultEquivalence() { + return Equivalence.equals(); + } + }, + + WEAK { + @Override + ValueReference referenceValue( + Segment segment, ReferenceEntry entry, V value, int weight) { + return (weight == 1) + ? new WeakValueReference(segment.valueReferenceQueue, value, entry) + : new WeightedWeakValueReference( + segment.valueReferenceQueue, value, entry, weight); + } + + @Override + Equivalence defaultEquivalence() { + return Equivalence.identity(); + } + }; + + /** + * Creates a reference for the given value according to this value strength. + */ + abstract ValueReference referenceValue( + Segment segment, ReferenceEntry entry, V value, int weight); + + /** + * Returns the default equivalence strategy used to compare and hash keys or values referenced + * at this strength. This strategy will be used unless the user explicitly specifies an + * alternate strategy. + */ + abstract Equivalence defaultEquivalence(); + } + + // Cache support + + // ConcurrentMap methods + + /** + * Creates new entries. + */ + enum EntryFactory { + STRONG { + @Override + ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next) { + return new StrongEntry(key, hash, next); + } + }, + STRONG_ACCESS { + @Override + ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next) { + return new StrongAccessEntry(key, hash, next); + } + + @Override + ReferenceEntry copyEntry( + Segment segment, ReferenceEntry original, ReferenceEntry newNext) { + ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + copyAccessEntry(original, newEntry); + return newEntry; + } + }, + STRONG_WRITE { + @Override + ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next) { + return new StrongWriteEntry(key, hash, next); + } + + @Override + ReferenceEntry copyEntry( + Segment segment, ReferenceEntry original, ReferenceEntry newNext) { + ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + copyWriteEntry(original, newEntry); + return newEntry; + } + }, + STRONG_ACCESS_WRITE { + @Override + ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next) { + return new StrongAccessWriteEntry(key, hash, next); + } + + @Override + ReferenceEntry copyEntry( + Segment segment, ReferenceEntry original, ReferenceEntry newNext) { + ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + copyAccessEntry(original, newEntry); + copyWriteEntry(original, newEntry); + return newEntry; + } + }, + + WEAK { + @Override + ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next) { + return new WeakEntry(segment.keyReferenceQueue, key, hash, next); + } + }, + WEAK_ACCESS { + @Override + ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next) { + return new WeakAccessEntry(segment.keyReferenceQueue, key, hash, next); + } + + @Override + ReferenceEntry copyEntry( + Segment segment, ReferenceEntry original, ReferenceEntry newNext) { + ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + copyAccessEntry(original, newEntry); + return newEntry; + } + }, + WEAK_WRITE { + @Override + ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next) { + return new WeakWriteEntry(segment.keyReferenceQueue, key, hash, next); + } + + @Override + ReferenceEntry copyEntry( + Segment segment, ReferenceEntry original, ReferenceEntry newNext) { + ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + copyWriteEntry(original, newEntry); + return newEntry; + } + }, + WEAK_ACCESS_WRITE { + @Override + ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next) { + return new WeakAccessWriteEntry(segment.keyReferenceQueue, key, hash, next); + } + + @Override + ReferenceEntry copyEntry( + Segment segment, ReferenceEntry original, ReferenceEntry newNext) { + ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + copyAccessEntry(original, newEntry); + copyWriteEntry(original, newEntry); + return newEntry; + } + }; + + /** + * Masks used to compute indices in the following table. + */ + static final int ACCESS_MASK = 1; + static final int WRITE_MASK = 2; + static final int WEAK_MASK = 4; + + /** + * Look-up table for factories. + */ + static final EntryFactory[] factories = { + STRONG, STRONG_ACCESS, STRONG_WRITE, STRONG_ACCESS_WRITE, + WEAK, WEAK_ACCESS, WEAK_WRITE, WEAK_ACCESS_WRITE, + }; + + static EntryFactory getFactory(Strength keyStrength, boolean usesAccessQueue, + boolean usesWriteQueue) { + int flags = ((keyStrength == Strength.WEAK) ? WEAK_MASK : 0) + | (usesAccessQueue ? ACCESS_MASK : 0) + | (usesWriteQueue ? WRITE_MASK : 0); + return factories[flags]; + } + + /** + * Creates a new entry. + * + * @param segment to create the entry for + * @param key of the entry + * @param hash of the key + * @param next entry in the same bucket + */ + abstract ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next); + + /** + * Copies an entry, assigning it a new {@code next} entry. + * + * @param original the entry to copy + * @param newNext entry in the same bucket + */ + // Guarded By Segment.this + ReferenceEntry copyEntry( + Segment segment, ReferenceEntry original, ReferenceEntry newNext) { + return newEntry(segment, original.getKey(), original.getHash(), newNext); + } + + // Guarded By Segment.this + void copyAccessEntry(ReferenceEntry original, ReferenceEntry newEntry) { + // TODO(fry): when we link values instead of entries this method can go + // away, as can connectAccessOrder, nullifyAccessOrder. + newEntry.setAccessTime(original.getAccessTime()); + + connectAccessOrder(original.getPreviousInAccessQueue(), newEntry); + connectAccessOrder(newEntry, original.getNextInAccessQueue()); + + nullifyAccessOrder(original); + } + + // Guarded By Segment.this + void copyWriteEntry(ReferenceEntry original, ReferenceEntry newEntry) { + // TODO(fry): when we link values instead of entries this method can go + // away, as can connectWriteOrder, nullifyWriteOrder. + newEntry.setWriteTime(original.getWriteTime()); + + connectWriteOrder(original.getPreviousInWriteQueue(), newEntry); + connectWriteOrder(newEntry, original.getNextInWriteQueue()); + + nullifyWriteOrder(original); + } + } + + private enum NullEntry implements ReferenceEntry { + INSTANCE; + + @Override + public ValueReference getValueReference() { + return null; + } + + @Override + public void setValueReference(ValueReference valueReference) { + } + + @Override + public ReferenceEntry getNext() { + return null; + } + + @Override + public int getHash() { + return 0; + } + + @Override + public Object getKey() { + return null; + } + + @Override + public long getAccessTime() { + return 0; + } + + @Override + public void setAccessTime(long time) { + } + + @Override + public ReferenceEntry getNextInAccessQueue() { + return this; + } + + @Override + public void setNextInAccessQueue(ReferenceEntry next) { + } + + @Override + public ReferenceEntry getPreviousInAccessQueue() { + return this; + } + + @Override + public void setPreviousInAccessQueue(ReferenceEntry previous) { + } + + @Override + public long getWriteTime() { + return 0; + } + + @Override + public void setWriteTime(long time) { + } + + @Override + public ReferenceEntry getNextInWriteQueue() { + return this; + } + + @Override + public void setNextInWriteQueue(ReferenceEntry next) { + } + + @Override + public ReferenceEntry getPreviousInWriteQueue() { + return this; + } + + @Override + public void setPreviousInWriteQueue(ReferenceEntry previous) { + } + } + + /** + * A reference to a value. + */ + interface ValueReference { + /** + * Returns the value. Does not block or throw exceptions. + */ + V get(); + + /** + * Waits for a value that may still be loading. Unlike get(), this method can block (in the + * case of FutureValueReference). + * + * @throws ExecutionException if the loading thread throws an exception + * @throws ExecutionError if the loading thread throws an error + */ + V waitForValue() throws ExecutionException; + + /** + * Returns the weight of this entry. This is assumed to be static between calls to setValue. + */ + int getWeight(); + + /** + * Returns the entry associated with this value reference, or {@code null} if this value + * reference is independent of any entry. + */ + ReferenceEntry getEntry(); + + /** + * Creates a copy of this reference for the given entry. + *

+ *

{@code value} may be null only for a loading reference. + */ + ValueReference copyFor( + ReferenceQueue queue, V value, ReferenceEntry entry); + + /** + * Notifify pending loads that a new value was set. This is only relevant to loading + * value references. + */ + void notifyNewValue(V newValue); + + /** + * Returns true if a new value is currently loading, regardless of whether or not there is an + * existing value. It is assumed that the return value of this method is constant for any given + * ValueReference instance. + */ + boolean isLoading(); + + /** + * Returns true if this reference contains an active value, meaning one that is still considered + * present in the cache. Active values consist of live values, which are returned by cache + * lookups, and dead values, which have been evicted but awaiting removal. Non-active values + * consist strictly of loading values, though during refresh a value may be both active and + * loading. + */ + boolean isActive(); + } + + /** + * An entry in a reference map. + *

+ * Entries in the map can be in the following states: + *

+ * Valid: + * - Live: valid key/value are set + * - Loading: loading is pending + *

+ * Invalid: + * - Expired: time expired (key/value may still be set) + * - Collected: key/value was partially collected, but not yet cleaned up + * - Unset: marked as unset, awaiting cleanup or reuse + */ + interface ReferenceEntry { + /** + * Returns the value reference from this entry. + */ + ValueReference getValueReference(); + + /** + * Sets the value reference for this entry. + */ + void setValueReference(ValueReference valueReference); + + /** + * Returns the next entry in the chain. + */ + ReferenceEntry getNext(); + + /** + * Returns the entry's hash. + */ + int getHash(); + + /** + * Returns the key for this entry. + */ + K getKey(); + + /* + * Used by entries that use access order. Access entries are maintained in a doubly-linked list. + * New entries are added at the tail of the list at write time; stale entries are expired from + * the head of the list. + */ + + /** + * Returns the time that this entry was last accessed, in ns. + */ + long getAccessTime(); + + /** + * Sets the entry access time in ns. + */ + void setAccessTime(long time); + + /** + * Returns the next entry in the access queue. + */ + ReferenceEntry getNextInAccessQueue(); + + /** + * Sets the next entry in the access queue. + */ + void setNextInAccessQueue(ReferenceEntry next); + + /** + * Returns the previous entry in the access queue. + */ + ReferenceEntry getPreviousInAccessQueue(); + + /** + * Sets the previous entry in the access queue. + */ + void setPreviousInAccessQueue(ReferenceEntry previous); + + /* + * Implemented by entries that use write order. Write entries are maintained in a + * doubly-linked list. New entries are added at the tail of the list at write time and stale + * entries are expired from the head of the list. + */ + + /** + * Returns the time that this entry was last written, in ns. + */ + long getWriteTime(); + + /** + * Sets the entry write time in ns. + */ + void setWriteTime(long time); + + /** + * Returns the next entry in the write queue. + */ + ReferenceEntry getNextInWriteQueue(); + + /** + * Sets the next entry in the write queue. + */ + void setNextInWriteQueue(ReferenceEntry next); + + /** + * Returns the previous entry in the write queue. + */ + ReferenceEntry getPreviousInWriteQueue(); + + /** + * Sets the previous entry in the write queue. + */ + void setPreviousInWriteQueue(ReferenceEntry previous); + } + + abstract static class AbstractReferenceEntry implements ReferenceEntry { + @Override + public ValueReference getValueReference() { + throw new UnsupportedOperationException(); + } + + @Override + public void setValueReference(ValueReference valueReference) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getNext() { + throw new UnsupportedOperationException(); + } + + @Override + public int getHash() { + throw new UnsupportedOperationException(); + } + + @Override + public K getKey() { + throw new UnsupportedOperationException(); + } + + @Override + public long getAccessTime() { + throw new UnsupportedOperationException(); + } + + @Override + public void setAccessTime(long time) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getNextInAccessQueue() { + throw new UnsupportedOperationException(); + } + + @Override + public void setNextInAccessQueue(ReferenceEntry next) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getPreviousInAccessQueue() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPreviousInAccessQueue(ReferenceEntry previous) { + throw new UnsupportedOperationException(); + } + + @Override + public long getWriteTime() { + throw new UnsupportedOperationException(); + } + + @Override + public void setWriteTime(long time) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getNextInWriteQueue() { + throw new UnsupportedOperationException(); + } + + @Override + public void setNextInWriteQueue(ReferenceEntry next) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getPreviousInWriteQueue() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPreviousInWriteQueue(ReferenceEntry previous) { + throw new UnsupportedOperationException(); + } + } + + /** + * Used for strongly-referenced keys. + */ + static class StrongEntry extends AbstractReferenceEntry { + final K key; + final int hash; + final ReferenceEntry next; + + // The code below is exactly the same for each entry type. + volatile ValueReference valueReference = unset(); + + StrongEntry(K key, int hash, ReferenceEntry next) { + this.key = key; + this.hash = hash; + this.next = next; + } + + @Override + public K getKey() { + return this.key; + } + + @Override + public ValueReference getValueReference() { + return valueReference; + } + + @Override + public void setValueReference(ValueReference valueReference) { + this.valueReference = valueReference; + } + + @Override + public int getHash() { + return hash; + } + + @Override + public ReferenceEntry getNext() { + return next; + } + } + + static final class StrongAccessEntry extends StrongEntry { + volatile long accessTime = Long.MAX_VALUE; + + // The code below is exactly the same for each access entry type. + // Guarded By Segment.this + ReferenceEntry nextAccess = nullEntry(); + // Guarded By Segment.this + ReferenceEntry previousAccess = nullEntry(); + + StrongAccessEntry(K key, int hash, ReferenceEntry next) { + super(key, hash, next); + } + + @Override + public long getAccessTime() { + return accessTime; + } + + @Override + public void setAccessTime(long time) { + this.accessTime = time; + } + + @Override + public ReferenceEntry getNextInAccessQueue() { + return nextAccess; + } + + @Override + public void setNextInAccessQueue(ReferenceEntry next) { + this.nextAccess = next; + } + + @Override + public ReferenceEntry getPreviousInAccessQueue() { + return previousAccess; + } + + @Override + public void setPreviousInAccessQueue(ReferenceEntry previous) { + this.previousAccess = previous; + } + } + + static final class StrongWriteEntry extends StrongEntry { + volatile long writeTime = Long.MAX_VALUE; + + // The code below is exactly the same for each write entry type. + // Guarded By Segment.this + ReferenceEntry nextWrite = nullEntry(); + // Guarded By Segment.this + ReferenceEntry previousWrite = nullEntry(); + + StrongWriteEntry(K key, int hash, ReferenceEntry next) { + super(key, hash, next); + } + + @Override + public long getWriteTime() { + return writeTime; + } + + @Override + public void setWriteTime(long time) { + this.writeTime = time; + } + + @Override + public ReferenceEntry getNextInWriteQueue() { + return nextWrite; + } + + @Override + public void setNextInWriteQueue(ReferenceEntry next) { + this.nextWrite = next; + } + + @Override + public ReferenceEntry getPreviousInWriteQueue() { + return previousWrite; + } + + @Override + public void setPreviousInWriteQueue(ReferenceEntry previous) { + this.previousWrite = previous; + } + } + + static final class StrongAccessWriteEntry extends StrongEntry { + volatile long accessTime = Long.MAX_VALUE; + + // The code below is exactly the same for each access entry type. + // Guarded By Segment.this + ReferenceEntry nextAccess = nullEntry(); + // Guarded By Segment.this + ReferenceEntry previousAccess = nullEntry(); + volatile long writeTime = Long.MAX_VALUE; + // Guarded By Segment.this + ReferenceEntry nextWrite = nullEntry(); + // Guarded By Segment.this + ReferenceEntry previousWrite = nullEntry(); + + StrongAccessWriteEntry(K key, int hash, ReferenceEntry next) { + super(key, hash, next); + } + + @Override + public long getAccessTime() { + return accessTime; + } + + @Override + public void setAccessTime(long time) { + this.accessTime = time; + } + + @Override + public ReferenceEntry getNextInAccessQueue() { + return nextAccess; + } + + // The code below is exactly the same for each write entry type. + + @Override + public void setNextInAccessQueue(ReferenceEntry next) { + this.nextAccess = next; + } + + @Override + public ReferenceEntry getPreviousInAccessQueue() { + return previousAccess; + } + + @Override + public void setPreviousInAccessQueue(ReferenceEntry previous) { + this.previousAccess = previous; + } + + @Override + public long getWriteTime() { + return writeTime; + } + + @Override + public void setWriteTime(long time) { + this.writeTime = time; + } + + @Override + public ReferenceEntry getNextInWriteQueue() { + return nextWrite; + } + + @Override + public void setNextInWriteQueue(ReferenceEntry next) { + this.nextWrite = next; + } + + @Override + public ReferenceEntry getPreviousInWriteQueue() { + return previousWrite; + } + + @Override + public void setPreviousInWriteQueue(ReferenceEntry previous) { + this.previousWrite = previous; + } + } + + /** + * Used for weakly-referenced keys. + */ + static class WeakEntry extends WeakReference implements ReferenceEntry { + final int hash; + final ReferenceEntry next; + + /* + * It'd be nice to get these for free from AbstractReferenceEntry, but we're already extending + * WeakReference. + */ + + // null access + volatile ValueReference valueReference = unset(); + + WeakEntry(ReferenceQueue queue, K key, int hash, ReferenceEntry next) { + super(key, queue); + this.hash = hash; + this.next = next; + } + + @Override + public K getKey() { + return get(); + } + + @Override + public long getAccessTime() { + throw new UnsupportedOperationException(); + } + + @Override + public void setAccessTime(long time) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getNextInAccessQueue() { + throw new UnsupportedOperationException(); + } + + // null write + + @Override + public void setNextInAccessQueue(ReferenceEntry next) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getPreviousInAccessQueue() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPreviousInAccessQueue(ReferenceEntry previous) { + throw new UnsupportedOperationException(); + } + + @Override + public long getWriteTime() { + throw new UnsupportedOperationException(); + } + + @Override + public void setWriteTime(long time) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getNextInWriteQueue() { + throw new UnsupportedOperationException(); + } + + // The code below is exactly the same for each entry type. + + @Override + public void setNextInWriteQueue(ReferenceEntry next) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getPreviousInWriteQueue() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPreviousInWriteQueue(ReferenceEntry previous) { + throw new UnsupportedOperationException(); + } + + @Override + public ValueReference getValueReference() { + return valueReference; + } + + @Override + public void setValueReference(ValueReference valueReference) { + this.valueReference = valueReference; + } + + @Override + public int getHash() { + return hash; + } + + @Override + public ReferenceEntry getNext() { + return next; + } + } + + static final class WeakAccessEntry extends WeakEntry { + volatile long accessTime = Long.MAX_VALUE; + + // The code below is exactly the same for each access entry type. + // Guarded By Segment.this + ReferenceEntry nextAccess = nullEntry(); + // Guarded By Segment.this + ReferenceEntry previousAccess = nullEntry(); + + WeakAccessEntry( + ReferenceQueue queue, K key, int hash, ReferenceEntry next) { + super(queue, key, hash, next); + } + + @Override + public long getAccessTime() { + return accessTime; + } + + @Override + public void setAccessTime(long time) { + this.accessTime = time; + } + + @Override + public ReferenceEntry getNextInAccessQueue() { + return nextAccess; + } + + @Override + public void setNextInAccessQueue(ReferenceEntry next) { + this.nextAccess = next; + } + + @Override + public ReferenceEntry getPreviousInAccessQueue() { + return previousAccess; + } + + @Override + public void setPreviousInAccessQueue(ReferenceEntry previous) { + this.previousAccess = previous; + } + } + + static final class WeakWriteEntry extends WeakEntry { + volatile long writeTime = Long.MAX_VALUE; + + // The code below is exactly the same for each write entry type. + // Guarded By Segment.this + ReferenceEntry nextWrite = nullEntry(); + // Guarded By Segment.this + ReferenceEntry previousWrite = nullEntry(); + + WeakWriteEntry( + ReferenceQueue queue, K key, int hash, ReferenceEntry next) { + super(queue, key, hash, next); + } + + @Override + public long getWriteTime() { + return writeTime; + } + + @Override + public void setWriteTime(long time) { + this.writeTime = time; + } + + @Override + public ReferenceEntry getNextInWriteQueue() { + return nextWrite; + } + + @Override + public void setNextInWriteQueue(ReferenceEntry next) { + this.nextWrite = next; + } + + @Override + public ReferenceEntry getPreviousInWriteQueue() { + return previousWrite; + } + + @Override + public void setPreviousInWriteQueue(ReferenceEntry previous) { + this.previousWrite = previous; + } + } + + static final class WeakAccessWriteEntry extends WeakEntry { + volatile long accessTime = Long.MAX_VALUE; + + // The code below is exactly the same for each access entry type. + // Guarded By Segment.this + ReferenceEntry nextAccess = nullEntry(); + // Guarded By Segment.this + ReferenceEntry previousAccess = nullEntry(); + volatile long writeTime = Long.MAX_VALUE; + // Guarded By Segment.this + ReferenceEntry nextWrite = nullEntry(); + // Guarded By Segment.this + ReferenceEntry previousWrite = nullEntry(); + + WeakAccessWriteEntry( + ReferenceQueue queue, K key, int hash, ReferenceEntry next) { + super(queue, key, hash, next); + } + + @Override + public long getAccessTime() { + return accessTime; + } + + @Override + public void setAccessTime(long time) { + this.accessTime = time; + } + + @Override + public ReferenceEntry getNextInAccessQueue() { + return nextAccess; + } + + // The code below is exactly the same for each write entry type. + + @Override + public void setNextInAccessQueue(ReferenceEntry next) { + this.nextAccess = next; + } + + @Override + public ReferenceEntry getPreviousInAccessQueue() { + return previousAccess; + } + + @Override + public void setPreviousInAccessQueue(ReferenceEntry previous) { + this.previousAccess = previous; + } + + @Override + public long getWriteTime() { + return writeTime; + } + + @Override + public void setWriteTime(long time) { + this.writeTime = time; + } + + @Override + public ReferenceEntry getNextInWriteQueue() { + return nextWrite; + } + + @Override + public void setNextInWriteQueue(ReferenceEntry next) { + this.nextWrite = next; + } + + @Override + public ReferenceEntry getPreviousInWriteQueue() { + return previousWrite; + } + + @Override + public void setPreviousInWriteQueue(ReferenceEntry previous) { + this.previousWrite = previous; + } + } + + /** + * References a weak value. + */ + static class WeakValueReference + extends WeakReference implements ValueReference { + final ReferenceEntry entry; + + WeakValueReference(ReferenceQueue queue, V referent, ReferenceEntry entry) { + super(referent, queue); + this.entry = entry; + } + + @Override + public int getWeight() { + return 1; + } + + @Override + public ReferenceEntry getEntry() { + return entry; + } + + @Override + public void notifyNewValue(V newValue) { + } + + @Override + public ValueReference copyFor( + ReferenceQueue queue, V value, ReferenceEntry entry) { + return new WeakValueReference(queue, value, entry); + } + + @Override + public boolean isLoading() { + return false; + } + + @Override + public boolean isActive() { + return true; + } + + @Override + public V waitForValue() { + return get(); + } + } + + /** + * References a strong value. + */ + static class StrongValueReference implements ValueReference { + final V referent; + + StrongValueReference(V referent) { + this.referent = referent; + } + + @Override + public V get() { + return referent; + } + + @Override + public int getWeight() { + return 1; + } + + @Override + public ReferenceEntry getEntry() { + return null; + } + + @Override + public ValueReference copyFor( + ReferenceQueue queue, V value, ReferenceEntry entry) { + return this; + } + + @Override + public boolean isLoading() { + return false; + } + + @Override + public boolean isActive() { + return true; + } + + @Override + public V waitForValue() { + return get(); + } + + @Override + public void notifyNewValue(V newValue) { + } + } + + /** + * References a weak value. + */ + static final class WeightedWeakValueReference extends WeakValueReference { + final int weight; + + WeightedWeakValueReference(ReferenceQueue queue, V referent, ReferenceEntry entry, + int weight) { + super(queue, referent, entry); + this.weight = weight; + } + + @Override + public int getWeight() { + return weight; + } + + @Override + public ValueReference copyFor( + ReferenceQueue queue, V value, ReferenceEntry entry) { + return new WeightedWeakValueReference(queue, value, entry, weight); + } + } + + /** + * References a strong value. + */ + static final class WeightedStrongValueReference extends StrongValueReference { + final int weight; + + WeightedStrongValueReference(V referent, int weight) { + super(referent); + this.weight = weight; + } + + @Override + public int getWeight() { + return weight; + } + } + + /** + * Segments are specialized versions of hash tables. This subclass inherits from ReentrantLock + * opportunistically, just to simplify some locking and avoid separate construction. + */ + @SuppressWarnings("serial") // This class is never serialized. + static class Segment extends ReentrantLock { + + /* + * TODO(fry): Consider copying variables (like evictsBySize) from outer class into this class. + * It will require more memory but will reduce indirection. + */ + + /* + * Segments maintain a table of entry lists that are ALWAYS kept in a consistent state, so can + * be read without locking. Next fields of nodes are immutable (final). All list additions are + * performed at the front of each bin. This makes it easy to check changes, and also fast to + * traverse. When nodes would otherwise be changed, new nodes are created to replace them. This + * works well for hash tables since the bin lists tend to be short. (The average length is less + * than two.) + * + * Read operations can thus proceed without locking, but rely on selected uses of volatiles to + * ensure that completed write operations performed by other threads are noticed. For most + * purposes, the "count" field, tracking the number of elements, serves as that volatile + * variable ensuring visibility. This is convenient because this field needs to be read in many + * read operations anyway: + * + * - All (unsynchronized) read operations must first read the "count" field, and should not + * look at table entries if it is 0. + * + * - All (synchronized) write operations should write to the "count" field after structurally + * changing any bin. The operations must not take any action that could even momentarily + * cause a concurrent read operation to see inconsistent data. This is made easier by the + * nature of the read operations in Map. For example, no operation can reveal that the table + * has grown but the threshold has not yet been updated, so there are no atomicity requirements + * for this with respect to reads. + * + * As a guide, all critical volatile reads and writes to the count field are marked in code + * comments. + */ + + final LocalCache map; + /** + * The maximum weight of this segment. UNSET_INT if there is no maximum. + */ + final long maxSegmentWeight; + /** + * The key reference queue contains entries whose keys have been garbage collected, and which + * need to be cleaned up internally. + */ + final ReferenceQueue keyReferenceQueue; + /** + * The value reference queue contains value references whose values have been garbage collected, + * and which need to be cleaned up internally. + */ + final ReferenceQueue valueReferenceQueue; + /** + * The recency queue is used to record which entries were accessed for updating the access + * list's ordering. It is drained as a batch operation when either the DRAIN_THRESHOLD is + * crossed or a write occurs on the segment. + */ + final Queue> recencyQueue; + /** + * A counter of the number of reads since the last write, used to drain queues on a small + * fraction of read operations. + */ + final AtomicInteger readCount = new AtomicInteger(); + /** + * A queue of elements currently in the map, ordered by write time. Elements are added to the + * tail of the queue on write. + */ + final Queue> writeQueue; + /** + * A queue of elements currently in the map, ordered by access time. Elements are added to the + * tail of the queue on access (note that writes count as accesses). + */ + final Queue> accessQueue; + /** + * The number of live elements in this segment's region. + */ + volatile int count; + /** + * The weight of the live elements in this segment's region. + */ + long totalWeight; + /** + * Number of updates that alter the size of the table. This is used during bulk-read methods to + * make sure they see a consistent snapshot: If modCounts change during a traversal of segments + * loading size or checking containsValue, then we might have an inconsistent view of state + * so (usually) must retry. + */ + int modCount; + /** + * The table is expanded when its size exceeds this threshold. (The value of this field is + * always {@code (int) (capacity * 0.75)}.) + */ + int threshold; + /** + * The per-segment table. + */ + volatile AtomicReferenceArray> table; + + Segment(LocalCache map, int initialCapacity, long maxSegmentWeight) { + this.map = map; + this.maxSegmentWeight = maxSegmentWeight; + initTable(newEntryArray(initialCapacity)); + + keyReferenceQueue = map.usesKeyReferences() + ? new ReferenceQueue() : null; + + valueReferenceQueue = map.usesValueReferences() + ? new ReferenceQueue() : null; + + recencyQueue = map.usesAccessQueue() + ? new ConcurrentLinkedQueue>() + : LocalCache.discardingQueue(); + + writeQueue = map.usesWriteQueue() + ? new WriteQueue() + : LocalCache.discardingQueue(); + + accessQueue = map.usesAccessQueue() + ? new AccessQueue() + : LocalCache.discardingQueue(); + } + + AtomicReferenceArray> newEntryArray(int size) { + return new AtomicReferenceArray>(size); + } + + void initTable(AtomicReferenceArray> newTable) { + this.threshold = newTable.length() * 3 / 4; // 0.75 + if (this.threshold == maxSegmentWeight) { + // prevent spurious expansion before eviction + this.threshold++; + } + this.table = newTable; + } + + ReferenceEntry newEntry(K key, int hash, ReferenceEntry next) { + return map.entryFactory.newEntry(this, checkNotNull(key), hash, next); + } + + /** + * Copies {@code original} into a new entry chained to {@code newNext}. Returns the new entry, + * or {@code null} if {@code original} was already garbage collected. + */ + ReferenceEntry copyEntry(ReferenceEntry original, ReferenceEntry newNext) { + if (original.getKey() == null) { + // key collected + return null; + } + + ValueReference valueReference = original.getValueReference(); + V value = valueReference.get(); + if ((value == null) && valueReference.isActive()) { + // value collected + return null; + } + + ReferenceEntry newEntry = map.entryFactory.copyEntry(this, original, newNext); + newEntry.setValueReference(valueReference.copyFor(this.valueReferenceQueue, value, newEntry)); + return newEntry; + } + + /** + * Sets a new value of an entry. Adds newly created entries at the end of the access queue. + */ + void setValue(ReferenceEntry entry, K key, V value, long now) { + ValueReference previous = entry.getValueReference(); + int weight = 1; + checkState(weight >= 0, "Weights must be non-negative"); + + ValueReference valueReference = + map.valueStrength.referenceValue(this, entry, value, weight); + entry.setValueReference(valueReference); + recordWrite(entry, weight, now); + previous.notifyNewValue(value); + } + + // loading + + V get(K key, int hash, CacheLoader loader) throws ExecutionException { + checkNotNull(key); + checkNotNull(loader); + try { + if (count != 0) { // read-volatile + // don't call getLiveEntry, which would ignore loading values + ReferenceEntry e = getEntry(key, hash); + if (e != null) { + long now = map.ticker.read(); + V value = getLiveValue(e, now); + if (value != null) { + recordRead(e, now); + return scheduleRefresh(e, key, hash, value, now, loader); + } + ValueReference valueReference = e.getValueReference(); + if (valueReference.isLoading()) { + return waitForLoadingValue(e, key, valueReference); + } + } + } + + // at this point e is either null or expired; + return lockedGetOrLoad(key, hash, loader); + } catch (ExecutionException ee) { + Throwable cause = ee.getCause(); + if (cause instanceof Error) { + throw new ExecutionError((Error) cause); + } else if (cause instanceof RuntimeException) { + throw new UncheckedExecutionException(cause); + } + throw ee; + } finally { + postReadCleanup(); + } + } + + V lockedGetOrLoad(K key, int hash, CacheLoader loader) + throws ExecutionException { + ReferenceEntry e; + ValueReference valueReference = null; + LoadingValueReference loadingValueReference = null; + boolean createNewEntry = true; + + lock(); + try { + // re-read ticker once inside the lock + long now = map.ticker.read(); + preWriteCleanup(now); + + int newCount = this.count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + valueReference = e.getValueReference(); + if (valueReference.isLoading()) { + createNewEntry = false; + } else { + V value = valueReference.get(); + if (value == null) { + enqueueNotification(entryKey, hash, valueReference, RemovalCause.COLLECTED); + } else if (map.isExpired(e, now)) { + // This is a duplicate check, as preWriteCleanup already purged expired + // entries, but let's accomodate an incorrect expiration queue. + enqueueNotification(entryKey, hash, valueReference, RemovalCause.EXPIRED); + } else { + recordLockedRead(e, now); + // we were concurrent with loading; don't consider refresh + return value; + } + + // immediately reuse invalid entries + writeQueue.remove(e); + accessQueue.remove(e); + this.count = newCount; // write-volatile + } + break; + } + } + + if (createNewEntry) { + loadingValueReference = new LoadingValueReference(); + + if (e == null) { + e = newEntry(key, hash, first); + e.setValueReference(loadingValueReference); + table.set(index, e); + } else { + e.setValueReference(loadingValueReference); + } + } + } finally { + unlock(); + postWriteCleanup(); + } + + if (createNewEntry) { + // Synchronizes on the entry to allow failing fast when a recursive load is + // detected. This may be circumvented when an entry is copied, but will fail fast most + // of the time. + synchronized (e) { + return loadSync(key, hash, loadingValueReference, loader); + } + } else { + // The entry already exists. Wait for loading. + return waitForLoadingValue(e, key, valueReference); + } + } + + V waitForLoadingValue(ReferenceEntry e, K key, ValueReference valueReference) + throws ExecutionException { + if (!valueReference.isLoading()) { + throw new AssertionError(); + } + + checkState(!Thread.holdsLock(e), "Recursive load of: %s", key); + // don't consider expiration as we're concurrent with loading + V value = valueReference.waitForValue(); + if (value == null) { + throw new CacheLoader.InvalidCacheLoadException("CacheLoader returned null for key " + key + "."); + } + // re-read ticker now that loading has completed + long now = map.ticker.read(); + recordRead(e, now); + return value; + + } + + // at most one of loadSync/loadAsync may be called for any given LoadingValueReference + + V loadSync(K key, int hash, LoadingValueReference loadingValueReference, + CacheLoader loader) throws ExecutionException { + ListenableFuture loadingFuture = loadingValueReference.loadFuture(key, loader); + return getAndRecordStats(key, hash, loadingValueReference, loadingFuture); + } + + ListenableFuture loadAsync(final K key, final int hash, + final LoadingValueReference loadingValueReference, CacheLoader loader) { + final ListenableFuture loadingFuture = loadingValueReference.loadFuture(key, loader); + loadingFuture.addListener( + new Runnable() { + @Override + public void run() { + try { + V newValue = getAndRecordStats(key, hash, loadingValueReference, loadingFuture); + } catch (Throwable t) { + logger.log(Level.WARNING, "Exception thrown during refresh", t); + loadingValueReference.setException(t); + } + } + }, directExecutor()); + return loadingFuture; + } + + /** + * Waits uninterruptibly for {@code newValue} to be loaded, and then records loading stats. + */ + V getAndRecordStats(K key, int hash, LoadingValueReference loadingValueReference, + ListenableFuture newValue) throws ExecutionException { + V value = null; + try { + value = getUninterruptibly(newValue); + if (value == null) { + throw new CacheLoader.InvalidCacheLoadException("CacheLoader returned null for key " + key + "."); + } + storeLoadedValue(key, hash, loadingValueReference, value); + return value; + } finally { + if (value == null) { + removeLoadingValue(key, hash, loadingValueReference); + } + } + } + + V scheduleRefresh(ReferenceEntry entry, K key, int hash, V oldValue, long now, + CacheLoader loader) { + if (map.refreshes() && (now - entry.getWriteTime() > map.refreshNanos) + && !entry.getValueReference().isLoading()) { + V newValue = refresh(key, hash, loader, true); + if (newValue != null) { + return newValue; + } + } + return oldValue; + } + + /** + * Refreshes the value associated with {@code key}, unless another thread is already doing so. + * Returns the newly refreshed value associated with {@code key} if it was refreshed inline, or + * {@code null} if another thread is performing the refresh or if an error occurs during + * refresh. + */ + V refresh(K key, int hash, CacheLoader loader, boolean checkTime) { + final LoadingValueReference loadingValueReference = + insertLoadingValueReference(key, hash, checkTime); + if (loadingValueReference == null) { + return null; + } + + ListenableFuture result = loadAsync(key, hash, loadingValueReference, loader); + if (result.isDone()) { + try { + return Uninterruptibles.getUninterruptibly(result); + } catch (Throwable t) { + // don't let refresh exceptions propagate; error was already logged + } + } + return null; + } + + /** + * Returns a newly inserted {@code LoadingValueReference}, or null if the live value reference + * is already loading. + */ + LoadingValueReference insertLoadingValueReference(final K key, final int hash, + boolean checkTime) { + ReferenceEntry e = null; + lock(); + try { + long now = map.ticker.read(); + preWriteCleanup(now); + + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + // Look for an existing entry. + for (e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + // We found an existing entry. + + ValueReference valueReference = e.getValueReference(); + if (valueReference.isLoading() + || (checkTime && (now - e.getWriteTime() < map.refreshNanos))) { + // refresh is a no-op if loading is pending + // if checkTime, we want to check *after* acquiring the lock if refresh still needs + // to be scheduled + return null; + } + + // continue returning old value while loading + ++modCount; + LoadingValueReference loadingValueReference = + new LoadingValueReference(valueReference); + e.setValueReference(loadingValueReference); + return loadingValueReference; + } + } + + ++modCount; + LoadingValueReference loadingValueReference = new LoadingValueReference(); + e = newEntry(key, hash, first); + e.setValueReference(loadingValueReference); + table.set(index, e); + return loadingValueReference; + } finally { + unlock(); + postWriteCleanup(); + } + } + + // reference queues, for garbage collection cleanup + + /** + * Cleanup collected entries when the lock is available. + */ + void tryDrainReferenceQueues() { + if (tryLock()) { + try { + drainReferenceQueues(); + } finally { + unlock(); + } + } + } + + /** + * Drain the key and value reference queues, cleaning up internal entries containing garbage + * collected keys or values. + */ + void drainReferenceQueues() { + if (map.usesKeyReferences()) { + drainKeyReferenceQueue(); + } + if (map.usesValueReferences()) { + drainValueReferenceQueue(); + } + } + + void drainKeyReferenceQueue() { + Reference ref; + int i = 0; + while ((ref = keyReferenceQueue.poll()) != null) { + @SuppressWarnings("unchecked") + ReferenceEntry entry = (ReferenceEntry) ref; + map.reclaimKey(entry); + if (++i == DRAIN_MAX) { + break; + } + } + } + + void drainValueReferenceQueue() { + Reference ref; + int i = 0; + while ((ref = valueReferenceQueue.poll()) != null) { + @SuppressWarnings("unchecked") + ValueReference valueReference = (ValueReference) ref; + map.reclaimValue(valueReference); + if (++i == DRAIN_MAX) { + break; + } + } + } + + /** + * Clears all entries from the key and value reference queues. + */ + void clearReferenceQueues() { + if (map.usesKeyReferences()) { + clearKeyReferenceQueue(); + } + if (map.usesValueReferences()) { + clearValueReferenceQueue(); + } + } + + void clearKeyReferenceQueue() { + while (keyReferenceQueue.poll() != null) { + } + } + + void clearValueReferenceQueue() { + while (valueReferenceQueue.poll() != null) { + } + } + + // recency queue, shared by expiration and eviction + + /** + * Records the relative order in which this read was performed by adding {@code entry} to the + * recency queue. At write-time, or when the queue is full past the threshold, the queue will + * be drained and the entries therein processed. + *

+ *

Note: locked reads should use {@link #recordLockedRead}. + */ + void recordRead(ReferenceEntry entry, long now) { + if (map.recordsAccess()) { + entry.setAccessTime(now); + } + recencyQueue.add(entry); + } + + /** + * Updates the eviction metadata that {@code entry} was just read. This currently amounts to + * adding {@code entry} to relevant eviction lists. + *

+ *

Note: this method should only be called under lock, as it directly manipulates the + * eviction queues. Unlocked reads should use {@link #recordRead}. + */ + void recordLockedRead(ReferenceEntry entry, long now) { + if (map.recordsAccess()) { + entry.setAccessTime(now); + } + accessQueue.add(entry); + } + + /** + * Updates eviction metadata that {@code entry} was just written. This currently amounts to + * adding {@code entry} to relevant eviction lists. + */ + void recordWrite(ReferenceEntry entry, int weight, long now) { + // we are already under lock, so drain the recency queue immediately + drainRecencyQueue(); + totalWeight += weight; + + if (map.recordsAccess()) { + entry.setAccessTime(now); + } + if (map.recordsWrite()) { + entry.setWriteTime(now); + } + accessQueue.add(entry); + writeQueue.add(entry); + } + + /** + * Drains the recency queue, updating eviction metadata that the entries therein were read in + * the specified relative order. This currently amounts to adding them to relevant eviction + * lists (accounting for the fact that they could have been removed from the map since being + * added to the recency queue). + */ + void drainRecencyQueue() { + ReferenceEntry e; + while ((e = recencyQueue.poll()) != null) { + // An entry may be in the recency queue despite it being removed from + // the map . This can occur when the entry was concurrently read while a + // writer is removing it from the segment or after a clear has removed + // all of the segment's entries. + if (accessQueue.contains(e)) { + accessQueue.add(e); + } + } + } + + // expiration + + /** + * Cleanup expired entries when the lock is available. + */ + void tryExpireEntries(long now) { + if (tryLock()) { + try { + expireEntries(now); + } finally { + unlock(); + // don't call postWriteCleanup as we're in a read + } + } + } + + void expireEntries(long now) { + drainRecencyQueue(); + + ReferenceEntry e; + while ((e = writeQueue.peek()) != null && map.isExpired(e, now)) { + if (!removeEntry(e, e.getHash(), RemovalCause.EXPIRED)) { + throw new AssertionError(); + } + } + while ((e = accessQueue.peek()) != null && map.isExpired(e, now)) { + if (!removeEntry(e, e.getHash(), RemovalCause.EXPIRED)) { + throw new AssertionError(); + } + } + } + + // eviction + + void enqueueNotification(ReferenceEntry entry, RemovalCause cause) { + enqueueNotification(entry.getKey(), entry.getHash(), entry.getValueReference(), cause); + } + + void enqueueNotification(K key, int hash, ValueReference valueReference, + RemovalCause cause) { + totalWeight -= valueReference.getWeight(); + if (map.removalNotificationQueue != DISCARDING_QUEUE) { + V value = valueReference.get(); + RemovalNotification notification = new RemovalNotification(key, value, cause); + map.removalNotificationQueue.offer(notification); + } + } + + /** + * Performs eviction if the segment is full. This should only be called prior to adding a new + * entry and increasing {@code count}. + */ + void evictEntries() { + if (!map.evictsBySize()) { + return; + } + + drainRecencyQueue(); + while (totalWeight > maxSegmentWeight) { + ReferenceEntry e = getNextEvictable(); + if (!removeEntry(e, e.getHash(), RemovalCause.SIZE)) { + throw new AssertionError(); + } + } + } + + // TODO(fry): instead implement this with an eviction head + ReferenceEntry getNextEvictable() { + for (ReferenceEntry e : accessQueue) { + int weight = e.getValueReference().getWeight(); + if (weight > 0) { + return e; + } + } + throw new AssertionError(); + } + + /** + * Returns first entry of bin for given hash. + */ + ReferenceEntry getFirst(int hash) { + // read this volatile field only once + AtomicReferenceArray> table = this.table; + return table.get(hash & (table.length() - 1)); + } + + // Specialized implementations of map methods + + ReferenceEntry getEntry(Object key, int hash) { + for (ReferenceEntry e = getFirst(hash); e != null; e = e.getNext()) { + if (e.getHash() != hash) { + continue; + } + + K entryKey = e.getKey(); + if (entryKey == null) { + tryDrainReferenceQueues(); + continue; + } + + if (map.keyEquivalence.equivalent(key, entryKey)) { + return e; + } + } + + return null; + } + + ReferenceEntry getLiveEntry(Object key, int hash, long now) { + ReferenceEntry e = getEntry(key, hash); + if (e == null) { + return null; + } else if (map.isExpired(e, now)) { + tryExpireEntries(now); + return null; + } + return e; + } + + /** + * Gets the value from an entry. Returns null if the entry is invalid, partially-collected, + * loading, or expired. + */ + V getLiveValue(ReferenceEntry entry, long now) { + if (entry.getKey() == null) { + tryDrainReferenceQueues(); + return null; + } + V value = entry.getValueReference().get(); + if (value == null) { + tryDrainReferenceQueues(); + return null; + } + + if (map.isExpired(entry, now)) { + tryExpireEntries(now); + return null; + } + return value; + } + + V get(Object key, int hash) { + try { + if (count != 0) { // read-volatile + long now = map.ticker.read(); + ReferenceEntry e = getLiveEntry(key, hash, now); + if (e == null) { + return null; + } + + V value = e.getValueReference().get(); + if (value != null) { + recordRead(e, now); + return scheduleRefresh(e, e.getKey(), hash, value, now, map.defaultLoader); + } + tryDrainReferenceQueues(); + } + return null; + } finally { + postReadCleanup(); + } + } + + boolean containsKey(Object key, int hash) { + try { + if (count != 0) { // read-volatile + long now = map.ticker.read(); + ReferenceEntry e = getLiveEntry(key, hash, now); + if (e == null) { + return false; + } + return e.getValueReference().get() != null; + } + + return false; + } finally { + postReadCleanup(); + } + } + + V put(K key, int hash, V value, boolean onlyIfAbsent) { + lock(); + try { + long now = map.ticker.read(); + preWriteCleanup(now); + + int newCount = this.count + 1; + if (newCount > this.threshold) { // ensure capacity + expand(); + newCount = this.count + 1; + } + + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + // Look for an existing entry. + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + // We found an existing entry. + + ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + + if (entryValue == null) { + ++modCount; + if (valueReference.isActive()) { + enqueueNotification(key, hash, valueReference, RemovalCause.COLLECTED); + setValue(e, key, value, now); + newCount = this.count; // count remains unchanged + } else { + setValue(e, key, value, now); + newCount = this.count + 1; + } + this.count = newCount; // write-volatile + evictEntries(); + return null; + } else if (onlyIfAbsent) { + // Mimic + // "if (!map.containsKey(key)) ... + // else return map.get(key); + recordLockedRead(e, now); + return entryValue; + } else { + // clobber existing entry, count remains unchanged + ++modCount; + enqueueNotification(key, hash, valueReference, RemovalCause.REPLACED); + setValue(e, key, value, now); + evictEntries(); + return entryValue; + } + } + } + + // Create a new entry. + ++modCount; + ReferenceEntry newEntry = newEntry(key, hash, first); + setValue(newEntry, key, value, now); + table.set(index, newEntry); + newCount = this.count + 1; + this.count = newCount; // write-volatile + evictEntries(); + return null; + } finally { + unlock(); + postWriteCleanup(); + } + } + + /** + * Expands the table if possible. + */ + void expand() { + AtomicReferenceArray> oldTable = table; + int oldCapacity = oldTable.length(); + if (oldCapacity >= MAXIMUM_CAPACITY) { + return; + } + + /* + * Reclassify nodes in each list to new Map. Because we are using power-of-two expansion, the + * elements from each bin must either stay at same index, or move with a power of two offset. + * We eliminate unnecessary node creation by catching cases where old nodes can be reused + * because their next fields won't change. Statistically, at the default threshold, only + * about one-sixth of them need cloning when a table doubles. The nodes they replace will be + * garbage collectable as soon as they are no longer referenced by any reader thread that may + * be in the midst of traversing table right now. + */ + + int newCount = count; + AtomicReferenceArray> newTable = newEntryArray(oldCapacity << 1); + threshold = newTable.length() * 3 / 4; + int newMask = newTable.length() - 1; + for (int oldIndex = 0; oldIndex < oldCapacity; ++oldIndex) { + // We need to guarantee that any existing reads of old Map can + // proceed. So we cannot yet null out each bin. + ReferenceEntry head = oldTable.get(oldIndex); + + if (head != null) { + ReferenceEntry next = head.getNext(); + int headIndex = head.getHash() & newMask; + + // Single node on list + if (next == null) { + newTable.set(headIndex, head); + } else { + // Reuse the consecutive sequence of nodes with the same target + // index from the end of the list. tail points to the first + // entry in the reusable list. + ReferenceEntry tail = head; + int tailIndex = headIndex; + for (ReferenceEntry e = next; e != null; e = e.getNext()) { + int newIndex = e.getHash() & newMask; + if (newIndex != tailIndex) { + // The index changed. We'll need to copy the previous entry. + tailIndex = newIndex; + tail = e; + } + } + newTable.set(tailIndex, tail); + + // Clone nodes leading up to the tail. + for (ReferenceEntry e = head; e != tail; e = e.getNext()) { + int newIndex = e.getHash() & newMask; + ReferenceEntry newNext = newTable.get(newIndex); + ReferenceEntry newFirst = copyEntry(e, newNext); + if (newFirst != null) { + newTable.set(newIndex, newFirst); + } else { + removeCollectedEntry(e); + newCount--; + } + } + } + } + } + table = newTable; + this.count = newCount; + } + + boolean replace(K key, int hash, V oldValue, V newValue) { + lock(); + try { + long now = map.ticker.read(); + preWriteCleanup(now); + + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + if (entryValue == null) { + if (valueReference.isActive()) { + // If the value disappeared, this entry is partially collected. + int newCount = this.count - 1; + ++modCount; + ReferenceEntry newFirst = removeValueFromChain( + first, e, entryKey, hash, valueReference, RemovalCause.COLLECTED); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + } + return false; + } + + if (map.valueEquivalence.equivalent(oldValue, entryValue)) { + ++modCount; + enqueueNotification(key, hash, valueReference, RemovalCause.REPLACED); + setValue(e, key, newValue, now); + evictEntries(); + return true; + } else { + // Mimic + // "if (map.containsKey(key) && map.get(key).equals(oldValue))..." + recordLockedRead(e, now); + return false; + } + } + } + + return false; + } finally { + unlock(); + postWriteCleanup(); + } + } + + V replace(K key, int hash, V newValue) { + lock(); + try { + long now = map.ticker.read(); + preWriteCleanup(now); + + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + if (entryValue == null) { + if (valueReference.isActive()) { + // If the value disappeared, this entry is partially collected. + int newCount = this.count - 1; + ++modCount; + ReferenceEntry newFirst = removeValueFromChain( + first, e, entryKey, hash, valueReference, RemovalCause.COLLECTED); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + } + return null; + } + + ++modCount; + enqueueNotification(key, hash, valueReference, RemovalCause.REPLACED); + setValue(e, key, newValue, now); + evictEntries(); + return entryValue; + } + } + + return null; + } finally { + unlock(); + postWriteCleanup(); + } + } + + V remove(Object key, int hash) { + lock(); + try { + long now = map.ticker.read(); + preWriteCleanup(now); + + int newCount = this.count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + + RemovalCause cause; + if (entryValue != null) { + cause = RemovalCause.EXPLICIT; + } else if (valueReference.isActive()) { + cause = RemovalCause.COLLECTED; + } else { + // currently loading + return null; + } + + ++modCount; + ReferenceEntry newFirst = removeValueFromChain( + first, e, entryKey, hash, valueReference, cause); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + return entryValue; + } + } + + return null; + } finally { + unlock(); + postWriteCleanup(); + } + } + + boolean storeLoadedValue(K key, int hash, LoadingValueReference oldValueReference, + V newValue) { + lock(); + try { + long now = map.ticker.read(); + preWriteCleanup(now); + + int newCount = this.count + 1; + if (newCount > this.threshold) { // ensure capacity + expand(); + newCount = this.count + 1; + } + + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + // replace the old LoadingValueReference if it's live, otherwise + // perform a putIfAbsent + if (oldValueReference == valueReference + || (entryValue == null && valueReference != UNSET)) { + ++modCount; + if (oldValueReference.isActive()) { + RemovalCause cause = + (entryValue == null) ? RemovalCause.COLLECTED : RemovalCause.REPLACED; + enqueueNotification(key, hash, oldValueReference, cause); + newCount--; + } + setValue(e, key, newValue, now); + this.count = newCount; // write-volatile + evictEntries(); + return true; + } + + // the loaded value was already clobbered + valueReference = new WeightedStrongValueReference(newValue, 0); + enqueueNotification(key, hash, valueReference, RemovalCause.REPLACED); + return false; + } + } + + ++modCount; + ReferenceEntry newEntry = newEntry(key, hash, first); + setValue(newEntry, key, newValue, now); + table.set(index, newEntry); + this.count = newCount; // write-volatile + evictEntries(); + return true; + } finally { + unlock(); + postWriteCleanup(); + } + } + + boolean remove(Object key, int hash, Object value) { + lock(); + try { + long now = map.ticker.read(); + preWriteCleanup(now); + + int newCount = this.count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + + RemovalCause cause; + if (map.valueEquivalence.equivalent(value, entryValue)) { + cause = RemovalCause.EXPLICIT; + } else if (entryValue == null && valueReference.isActive()) { + cause = RemovalCause.COLLECTED; + } else { + // currently loading + return false; + } + + ++modCount; + ReferenceEntry newFirst = removeValueFromChain( + first, e, entryKey, hash, valueReference, cause); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + return (cause == RemovalCause.EXPLICIT); + } + } + + return false; + } finally { + unlock(); + postWriteCleanup(); + } + } + + void clear() { + if (count != 0) { // read-volatile + lock(); + try { + AtomicReferenceArray> table = this.table; + for (int i = 0; i < table.length(); ++i) { + for (ReferenceEntry e = table.get(i); e != null; e = e.getNext()) { + // Loading references aren't actually in the map yet. + if (e.getValueReference().isActive()) { + enqueueNotification(e, RemovalCause.EXPLICIT); + } + } + } + for (int i = 0; i < table.length(); ++i) { + table.set(i, null); + } + clearReferenceQueues(); + writeQueue.clear(); + accessQueue.clear(); + readCount.set(0); + + ++modCount; + count = 0; // write-volatile + } finally { + unlock(); + postWriteCleanup(); + } + } + } + + ReferenceEntry removeValueFromChain(ReferenceEntry first, + ReferenceEntry entry, K key, int hash, + ValueReference valueReference, + RemovalCause cause) { + enqueueNotification(key, hash, valueReference, cause); + writeQueue.remove(entry); + accessQueue.remove(entry); + + if (valueReference.isLoading()) { + valueReference.notifyNewValue(null); + return first; + } else { + return removeEntryFromChain(first, entry); + } + } + + ReferenceEntry removeEntryFromChain(ReferenceEntry first, + ReferenceEntry entry) { + int newCount = count; + ReferenceEntry newFirst = entry.getNext(); + for (ReferenceEntry e = first; e != entry; e = e.getNext()) { + ReferenceEntry next = copyEntry(e, newFirst); + if (next != null) { + newFirst = next; + } else { + removeCollectedEntry(e); + newCount--; + } + } + this.count = newCount; + return newFirst; + } + + void removeCollectedEntry(ReferenceEntry entry) { + enqueueNotification(entry, RemovalCause.COLLECTED); + writeQueue.remove(entry); + accessQueue.remove(entry); + } + + /** + * Removes an entry whose key has been garbage collected. + */ + boolean reclaimKey(ReferenceEntry entry, int hash) { + lock(); + try { + int newCount = count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + if (e == entry) { + ++modCount; + ReferenceEntry newFirst = removeValueFromChain( + first, e, e.getKey(), hash, e.getValueReference(), RemovalCause.COLLECTED); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + return true; + } + } + + return false; + } finally { + unlock(); + postWriteCleanup(); + } + } + + /** + * Removes an entry whose value has been garbage collected. + */ + boolean reclaimValue(K key, int hash, ValueReference valueReference) { + lock(); + try { + int newCount = this.count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + ValueReference v = e.getValueReference(); + if (v == valueReference) { + ++modCount; + ReferenceEntry newFirst = removeValueFromChain( + first, e, entryKey, hash, valueReference, RemovalCause.COLLECTED); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + return true; + } + return false; + } + } + + return false; + } finally { + unlock(); + if (!isHeldByCurrentThread()) { // don't cleanup inside of put + postWriteCleanup(); + } + } + } + + boolean removeLoadingValue(K key, int hash, LoadingValueReference valueReference) { + lock(); + try { + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + ValueReference v = e.getValueReference(); + if (v == valueReference) { + if (valueReference.isActive()) { + e.setValueReference(valueReference.getOldValue()); + } else { + ReferenceEntry newFirst = removeEntryFromChain(first, e); + table.set(index, newFirst); + } + return true; + } + return false; + } + } + + return false; + } finally { + unlock(); + postWriteCleanup(); + } + } + + boolean removeEntry(ReferenceEntry entry, int hash, RemovalCause cause) { + int newCount = this.count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + if (e == entry) { + ++modCount; + ReferenceEntry newFirst = removeValueFromChain( + first, e, e.getKey(), hash, e.getValueReference(), cause); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + return true; + } + } + + return false; + } + + /** + * Performs routine cleanup following a read. Normally cleanup happens during writes. If cleanup + * is not observed after a sufficient number of reads, try cleaning up from the read thread. + */ + void postReadCleanup() { + if ((readCount.incrementAndGet() & DRAIN_THRESHOLD) == 0) { + cleanUp(); + } + } + + /** + * Performs routine cleanup prior to executing a write. This should be called every time a + * write thread acquires the segment lock, immediately after acquiring the lock. + *

+ *

Post-condition: expireEntries has been run. + */ + void preWriteCleanup(long now) { + runLockedCleanup(now); + } + + /** + * Performs routine cleanup following a write. + */ + void postWriteCleanup() { + } + + void cleanUp() { + long now = map.ticker.read(); + runLockedCleanup(now); + } + + void runLockedCleanup(long now) { + if (tryLock()) { + try { + drainReferenceQueues(); + expireEntries(now); // calls drainRecencyQueue + readCount.set(0); + } finally { + unlock(); + } + } + } + } + + static class LoadingValueReference implements ValueReference { + // TODO(fry): rename get, then extend AbstractFuture instead of containing SettableFuture + final SettableFuture futureValue = SettableFuture.create(); + final Stopwatch stopwatch = Stopwatch.createUnstarted(); + volatile ValueReference oldValue; + + public LoadingValueReference() { + this(LocalCache.unset()); + } + + public LoadingValueReference(ValueReference oldValue) { + this.oldValue = oldValue; + } + + @Override + public boolean isLoading() { + return true; + } + + @Override + public boolean isActive() { + return oldValue.isActive(); + } + + @Override + public int getWeight() { + return oldValue.getWeight(); + } + + public boolean set(V newValue) { + return futureValue.set(newValue); + } + + public boolean setException(Throwable t) { + return futureValue.setException(t); + } + + private ListenableFuture fullyFailedFuture(Throwable t) { + return Futures.immediateFailedFuture(t); + } + + @Override + public void notifyNewValue(V newValue) { + if (newValue != null) { + // The pending load was clobbered by a manual write. + // Unblock all pending gets, and have them return the new value. + set(newValue); + } else { + // The pending load was removed. Delay notifications until loading completes. + oldValue = unset(); + } + + // TODO(fry): could also cancel loading if we had a handle on its future + } + + public ListenableFuture loadFuture(K key, CacheLoader loader) { + stopwatch.start(); + V previousValue = oldValue.get(); + try { + if (previousValue == null) { + V newValue = loader.load(key); + return set(newValue) ? futureValue : Futures.immediateFuture(newValue); + } + ListenableFuture newValue = loader.reload(key, previousValue); + if (newValue == null) { + return Futures.immediateFuture(null); + } + // To avoid a race, make sure the refreshed value is set into loadingValueReference + // *before* returning newValue from the cache query. + return Futures.transform(newValue, new Function() { + @Override + public V apply(V newValue) { + LoadingValueReference.this.set(newValue); + return newValue; + } + }); + } catch (Throwable t) { + if (t instanceof InterruptedException) { + Thread.currentThread().interrupt(); + } + return setException(t) ? futureValue : fullyFailedFuture(t); + } + } + + @Override + public V waitForValue() throws ExecutionException { + return getUninterruptibly(futureValue); + } + + @Override + public V get() { + return oldValue.get(); + } + + public ValueReference getOldValue() { + return oldValue; + } + + @Override + public ReferenceEntry getEntry() { + return null; + } + + @Override + public ValueReference copyFor( + ReferenceQueue queue, V value, ReferenceEntry entry) { + return this; + } + } + + /** + * A custom queue for managing eviction order. Note that this is tightly integrated with {@code + * ReferenceEntry}, upon which it relies to perform its linking. + *

+ *

Note that this entire implementation makes the assumption that all elements which are in + * the map are also in this queue, and that all elements not in the queue are not in the map. + *

+ *

The benefits of creating our own queue are that (1) we can replace elements in the middle + * of the queue as part of copyWriteEntry, and (2) the contains method is highly optimized + * for the current model. + */ + static final class WriteQueue extends AbstractQueue> { + final ReferenceEntry head = new AbstractReferenceEntry() { + + ReferenceEntry nextWrite = this; + ReferenceEntry previousWrite = this; + + @Override + public long getWriteTime() { + return Long.MAX_VALUE; + } + + @Override + public void setWriteTime(long time) { + } + + @Override + public ReferenceEntry getNextInWriteQueue() { + return nextWrite; + } + + @Override + public void setNextInWriteQueue(ReferenceEntry next) { + this.nextWrite = next; + } + + @Override + public ReferenceEntry getPreviousInWriteQueue() { + return previousWrite; + } + + @Override + public void setPreviousInWriteQueue(ReferenceEntry previous) { + this.previousWrite = previous; + } + }; + + // implements Queue + + @Override + public boolean offer(ReferenceEntry entry) { + // unlink + connectWriteOrder(entry.getPreviousInWriteQueue(), entry.getNextInWriteQueue()); + + // add to tail + connectWriteOrder(head.getPreviousInWriteQueue(), entry); + connectWriteOrder(entry, head); + + return true; + } + + @Override + public ReferenceEntry peek() { + ReferenceEntry next = head.getNextInWriteQueue(); + return (next == head) ? null : next; + } + + @Override + public ReferenceEntry poll() { + ReferenceEntry next = head.getNextInWriteQueue(); + if (next == head) { + return null; + } + + remove(next); + return next; + } + + @Override + @SuppressWarnings("unchecked") + public boolean remove(Object o) { + ReferenceEntry e = (ReferenceEntry) o; + ReferenceEntry previous = e.getPreviousInWriteQueue(); + ReferenceEntry next = e.getNextInWriteQueue(); + connectWriteOrder(previous, next); + nullifyWriteOrder(e); + + return next != NullEntry.INSTANCE; + } + + @Override + @SuppressWarnings("unchecked") + public boolean contains(Object o) { + ReferenceEntry e = (ReferenceEntry) o; + return e.getNextInWriteQueue() != NullEntry.INSTANCE; + } + + @Override + public boolean isEmpty() { + return head.getNextInWriteQueue() == head; + } + + @Override + public int size() { + int size = 0; + for (ReferenceEntry e = head.getNextInWriteQueue(); e != head; + e = e.getNextInWriteQueue()) { + size++; + } + return size; + } + + @Override + public void clear() { + ReferenceEntry e = head.getNextInWriteQueue(); + while (e != head) { + ReferenceEntry next = e.getNextInWriteQueue(); + nullifyWriteOrder(e); + e = next; + } + + head.setNextInWriteQueue(head); + head.setPreviousInWriteQueue(head); + } + + @Override + public Iterator> iterator() { + return new AbstractSequentialIterator>(peek()) { + @Override + protected ReferenceEntry computeNext(ReferenceEntry previous) { + ReferenceEntry next = previous.getNextInWriteQueue(); + return (next == head) ? null : next; + } + }; + } + } + + /** + * A custom queue for managing access order. Note that this is tightly integrated with + * {@code ReferenceEntry}, upon which it reliese to perform its linking. + *

+ *

Note that this entire implementation makes the assumption that all elements which are in + * the map are also in this queue, and that all elements not in the queue are not in the map. + *

+ *

The benefits of creating our own queue are that (1) we can replace elements in the middle + * of the queue as part of copyWriteEntry, and (2) the contains method is highly optimized + * for the current model. + */ + static final class AccessQueue extends AbstractQueue> { + final ReferenceEntry head = new AbstractReferenceEntry() { + + ReferenceEntry nextAccess = this; + ReferenceEntry previousAccess = this; + + @Override + public long getAccessTime() { + return Long.MAX_VALUE; + } + + @Override + public void setAccessTime(long time) { + } + + @Override + public ReferenceEntry getNextInAccessQueue() { + return nextAccess; + } + + @Override + public void setNextInAccessQueue(ReferenceEntry next) { + this.nextAccess = next; + } + + @Override + public ReferenceEntry getPreviousInAccessQueue() { + return previousAccess; + } + + @Override + public void setPreviousInAccessQueue(ReferenceEntry previous) { + this.previousAccess = previous; + } + }; + + // implements Queue + + @Override + public boolean offer(ReferenceEntry entry) { + // unlink + connectAccessOrder(entry.getPreviousInAccessQueue(), entry.getNextInAccessQueue()); + + // add to tail + connectAccessOrder(head.getPreviousInAccessQueue(), entry); + connectAccessOrder(entry, head); + + return true; + } + + @Override + public ReferenceEntry peek() { + ReferenceEntry next = head.getNextInAccessQueue(); + return (next == head) ? null : next; + } + + @Override + public ReferenceEntry poll() { + ReferenceEntry next = head.getNextInAccessQueue(); + if (next == head) { + return null; + } + + remove(next); + return next; + } + + @Override + @SuppressWarnings("unchecked") + public boolean remove(Object o) { + ReferenceEntry e = (ReferenceEntry) o; + ReferenceEntry previous = e.getPreviousInAccessQueue(); + ReferenceEntry next = e.getNextInAccessQueue(); + connectAccessOrder(previous, next); + nullifyAccessOrder(e); + + return next != NullEntry.INSTANCE; + } + + @Override + @SuppressWarnings("unchecked") + public boolean contains(Object o) { + ReferenceEntry e = (ReferenceEntry) o; + return e.getNextInAccessQueue() != NullEntry.INSTANCE; + } + + @Override + public boolean isEmpty() { + return head.getNextInAccessQueue() == head; + } + + @Override + public int size() { + int size = 0; + for (ReferenceEntry e = head.getNextInAccessQueue(); e != head; + e = e.getNextInAccessQueue()) { + size++; + } + return size; + } + + @Override + public void clear() { + ReferenceEntry e = head.getNextInAccessQueue(); + while (e != head) { + ReferenceEntry next = e.getNextInAccessQueue(); + nullifyAccessOrder(e); + e = next; + } + + head.setNextInAccessQueue(head); + head.setPreviousInAccessQueue(head); + } + + @Override + public Iterator> iterator() { + return new AbstractSequentialIterator>(peek()) { + @Override + protected ReferenceEntry computeNext(ReferenceEntry previous) { + ReferenceEntry next = previous.getNextInAccessQueue(); + return (next == head) ? null : next; + } + }; + } + } + + // Iterator Support + + static class LocalManualCache implements Cache, Serializable { + private static final long serialVersionUID = 1; + final LocalCache localCache; + + LocalManualCache(CacheBuilder builder) { + this(new LocalCache(builder, null)); + } + + // Cache methods + + private LocalManualCache(LocalCache localCache) { + this.localCache = localCache; + } + + @Override + public V getIfPresent(Object key) { + return localCache.getIfPresent(key); + } + + // Serialization Support + + @Override + public void put(K key, V value) { + localCache.put(key, value); + } + + } + + static class LocalLoadingCache + extends LocalManualCache implements LoadingCache { + + private static final long serialVersionUID = 1; + + // LoadingCache methods + + LocalLoadingCache(CacheBuilder builder, + CacheLoader loader) { + super(new LocalCache(builder, checkNotNull(loader))); + } + + @Override + public V get(K key) throws ExecutionException { + return localCache.getOrLoad(key); + } + + public V getUnchecked(K key) { + try { + return get(key); + } catch (ExecutionException e) { + throw new UncheckedExecutionException(e.getCause()); + } + } + + // Serialization Support + + @Override + public final V apply(K key) { + return getUnchecked(key); + } + + } + + abstract class HashIterator implements Iterator { + + int nextSegmentIndex; + int nextTableIndex; + Segment currentSegment; + AtomicReferenceArray> currentTable; + ReferenceEntry nextEntry; + WriteThroughEntry nextExternal; + WriteThroughEntry lastReturned; + + HashIterator() { + nextSegmentIndex = segments.length - 1; + nextTableIndex = -1; + advance(); + } + + @Override + public abstract T next(); + + final void advance() { + nextExternal = null; + + if (nextInChain()) { + return; + } + + if (nextInTable()) { + return; + } + + while (nextSegmentIndex >= 0) { + currentSegment = segments[nextSegmentIndex--]; + if (currentSegment.count != 0) { + currentTable = currentSegment.table; + nextTableIndex = currentTable.length() - 1; + if (nextInTable()) { + return; + } + } + } + } + + /** + * Finds the next entry in the current chain. Returns true if an entry was found. + */ + boolean nextInChain() { + if (nextEntry != null) { + for (nextEntry = nextEntry.getNext(); nextEntry != null; nextEntry = nextEntry.getNext()) { + if (advanceTo(nextEntry)) { + return true; + } + } + } + return false; + } + + /** + * Finds the next entry in the current table. Returns true if an entry was found. + */ + boolean nextInTable() { + while (nextTableIndex >= 0) { + if ((nextEntry = currentTable.get(nextTableIndex--)) != null) { + if (advanceTo(nextEntry) || nextInChain()) { + return true; + } + } + } + return false; + } + + /** + * Advances to the given entry. Returns true if the entry was valid, false if it should be + * skipped. + */ + boolean advanceTo(ReferenceEntry entry) { + try { + long now = ticker.read(); + K key = entry.getKey(); + V value = getLiveValue(entry, now); + if (value != null) { + nextExternal = new WriteThroughEntry(key, value); + return true; + } else { + // Skip stale entry. + return false; + } + } finally { + currentSegment.postReadCleanup(); + } + } + + @Override + public boolean hasNext() { + return nextExternal != null; + } + + WriteThroughEntry nextEntry() { + if (nextExternal == null) { + throw new NoSuchElementException(); + } + lastReturned = nextExternal; + advance(); + return lastReturned; + } + + @Override + public void remove() { + checkState(lastReturned != null); + LocalCache.this.remove(lastReturned.getKey()); + lastReturned = null; + } + } + + final class KeyIterator extends HashIterator { + + @Override + public K next() { + return nextEntry().getKey(); + } + } + + final class ValueIterator extends HashIterator { + + @Override + public V next() { + return nextEntry().getValue(); + } + } + + /** + * Custom Entry class used by EntryIterator.next(), that relays setValue changes to the + * underlying map. + */ + final class WriteThroughEntry implements Entry { + final K key; // non-null + final V value; // non-null + + WriteThroughEntry(K key, V value) { + this.key = key; + this.value = value; + } + + @Override + public K getKey() { + return key; + } + + @Override + public V getValue() { + return value; + } + + @Override + public boolean equals(Object object) { + // Cannot use key and value equivalence + if (object instanceof Entry) { + Entry that = (Entry) object; + return key.equals(that.getKey()) && value.equals(that.getValue()); + } + return false; + } + + @Override + public int hashCode() { + // Cannot use key and value equivalence + return key.hashCode() ^ value.hashCode(); + } + + @Override + public V setValue(V newValue) { + throw new UnsupportedOperationException(); + } + + /** + * Returns a string representation of the form {key}={value}. + */ + @Override + public String toString() { + return getKey() + "=" + getValue(); + } + } + + final class EntryIterator extends HashIterator> { + + @Override + public Entry next() { + return nextEntry(); + } + } + + // Serialization Support + + abstract class AbstractCacheSet extends AbstractSet { + final ConcurrentMap map; + + AbstractCacheSet(ConcurrentMap map) { + this.map = map; + } + + @Override + public int size() { + return map.size(); + } + + @Override + public boolean isEmpty() { + return map.isEmpty(); + } + + @Override + public void clear() { + map.clear(); + } + } + + final class KeySet extends AbstractCacheSet { + + KeySet(ConcurrentMap map) { + super(map); + } + + @Override + public Iterator iterator() { + return new KeyIterator(); + } + + @Override + public boolean contains(Object o) { + return map.containsKey(o); + } + + @Override + public boolean remove(Object o) { + return map.remove(o) != null; + } + } + + final class Values extends AbstractCollection { + private final ConcurrentMap map; + + Values(ConcurrentMap map) { + this.map = map; + } + + @Override + public int size() { + return map.size(); + } + + @Override + public boolean isEmpty() { + return map.isEmpty(); + } + + @Override + public void clear() { + map.clear(); + } + + @Override + public Iterator iterator() { + return new ValueIterator(); + } + + @Override + public boolean contains(Object o) { + return map.containsValue(o); + } + } + + final class EntrySet extends AbstractCacheSet> { + + EntrySet(ConcurrentMap map) { + super(map); + } + + @Override + public Iterator> iterator() { + return new EntryIterator(); + } + + @Override + public boolean contains(Object o) { + if (!(o instanceof Entry)) { + return false; + } + Entry e = (Entry) o; + Object key = e.getKey(); + if (key == null) { + return false; + } + V v = LocalCache.this.get(key); + + return v != null && valueEquivalence.equivalent(e.getValue(), v); + } + + @Override + public boolean remove(Object o) { + if (!(o instanceof Entry)) { + return false; + } + Entry e = (Entry) o; + Object key = e.getKey(); + return key != null && LocalCache.this.remove(key, e.getValue()); + } + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/MapMaker.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/MapMaker.java new file mode 100644 index 0000000000..d3aed5f044 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/MapMaker.java @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; +import java.util.ConcurrentModificationException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + *

A builder of {@link ConcurrentMap} instances having any combination of the following features: + *

+ *

    + *
  • keys or values automatically wrapped in {@linkplain WeakReference weak} or {@linkplain + * SoftReference soft} references + *
  • notification of evicted (or otherwise removed) entries + *
  • on-demand computation of values for keys not already present + *
+ *

+ *

Usage example:

   {@code
+ * 

+ * ConcurrentMap timers = new MapMaker() + * .concurrencyLevel(4) + * .weakKeys() + * .makeMap();}

+ *

+ *

These features are all optional; {@code new MapMaker().makeMap()} returns a valid concurrent + * map that behaves similarly to a {@link ConcurrentHashMap}. + *

+ *

The returned map is implemented as a hash table with similar performance characteristics to + * {@link ConcurrentHashMap}. It supports all optional operations of the {@code ConcurrentMap} + * interface. It does not permit null keys or values. + *

+ *

Note: by default, the returned map uses equality comparisons (the {@link Object#equals + * equals} method) to determine equality for keys or values. However, if {@link #weakKeys} was + * specified, the map uses identity ({@code ==}) comparisons instead for keys. Likewise, if {@link + * #weakValues} or {@link #softValues} was specified, the map uses identity comparisons for values. + *

+ *

The view collections of the returned map have weakly consistent iterators. This means + * that they are safe for concurrent use, but if other threads modify the map after the iterator is + * created, it is undefined which of these changes, if any, are reflected in that iterator. These + * iterators never throw {@link ConcurrentModificationException}. + *

+ *

If {@link #weakKeys}, {@link #weakValues}, or {@link #softValues} are requested, it is + * possible for a key or value present in the map to be reclaimed by the garbage collector. Entries + * with reclaimed keys or values may be removed from the map on each map modification or on + * occasional map accesses; such entries may be counted by {@link Map#size}, but will never be + * visible to read or write operations. A partially-reclaimed entry is never exposed to the user. + * Any {@link Map.Entry} instance retrieved from the map's + * {@linkplain Map#entrySet entry set} is a snapshot of that entry's state at the time of + * retrieval; such entries do, however, support {@link Map.Entry#setValue}, which simply + * calls {@link Map#put} on the entry's key. + *

+ *

The maps produced by {@code MapMaker} are serializable, and the deserialized maps retain all + * the configuration properties of the original map. During deserialization, if the original map had + * used soft or weak references, the entries are reconstructed as they were, but it's not unlikely + * they'll be quickly garbage-collected before they are ever accessed. + *

+ *

{@code new MapMaker().weakKeys().makeMap()} is a recommended replacement for {@link + * java.util.WeakHashMap}, but note that it compares keys using object identity whereas {@code + * WeakHashMap} uses {@link Object#equals}. + * + * @author Bob Lee + * @author Charles Fry + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ +public final class MapMaker extends GenericMapMaker { + static final int UNSET_INT = -1; + private static final int DEFAULT_INITIAL_CAPACITY = 16; + private static final int DEFAULT_CONCURRENCY_LEVEL = 4; + private static final int DEFAULT_EXPIRATION_NANOS = 0; + + private final int initialCapacity = UNSET_INT; + private final int concurrencyLevel = UNSET_INT; + final int maximumSize = UNSET_INT; + + private final long expireAfterWriteNanos = UNSET_INT; + private final long expireAfterAccessNanos = UNSET_INT; + + Equivalence getKeyEquivalence() { + return getKeyStrength().defaultEquivalence(); + } + + int getInitialCapacity() { + return (initialCapacity == UNSET_INT) ? DEFAULT_INITIAL_CAPACITY : initialCapacity; + } + + int getConcurrencyLevel() { + return (concurrencyLevel == UNSET_INT) ? DEFAULT_CONCURRENCY_LEVEL : concurrencyLevel; + } + + MapMakerInternalMap.Strength getKeyStrength() { + return MapMakerInternalMap.Strength.STRONG; + } + + MapMakerInternalMap.Strength getValueStrength() { + return MapMakerInternalMap.Strength.STRONG; + } + + long getExpireAfterWriteNanos() { + return (expireAfterWriteNanos == UNSET_INT) ? DEFAULT_EXPIRATION_NANOS : expireAfterWriteNanos; + } + + long getExpireAfterAccessNanos() { + return (expireAfterAccessNanos == UNSET_INT) + ? DEFAULT_EXPIRATION_NANOS : expireAfterAccessNanos; + } + + Ticker getTicker() { + return Ticker.systemTicker(); + } + + /** + * Returns a string representation for this MapMaker instance. The exact form of the returned + * string is not specificed. + */ + @Override + public String toString() { + MoreObjects.ToStringHelper s = MoreObjects.toStringHelper(this); + if (initialCapacity != UNSET_INT) { + s.add("initialCapacity", initialCapacity); + } + if (concurrencyLevel != UNSET_INT) { + s.add("concurrencyLevel", concurrencyLevel); + } + if (maximumSize != UNSET_INT) { + s.add("maximumSize", maximumSize); + } + if (expireAfterWriteNanos != UNSET_INT) { + s.add("expireAfterWrite", expireAfterWriteNanos + "ns"); + } + if (expireAfterAccessNanos != UNSET_INT) { + s.add("expireAfterAccess", expireAfterAccessNanos + "ns"); + } + return s.toString(); + } + + /** + * The reason why an entry was removed. + */ + enum RemovalCause { + /** + * The entry was manually removed by the user. This can result from the user invoking + * {@link Map#remove}, {@link ConcurrentMap#remove}, or {@link java.util.Iterator#remove}. + */ + EXPLICIT { + }, + + /** + * The entry itself was not actually removed, but its value was replaced by the user. This can + * result from the user invoking {@link Map#put}, {@link Map#putAll}, + * {@link ConcurrentMap#replace(Object, Object)}, or + * {@link ConcurrentMap#replace(Object, Object, Object)}. + */ + REPLACED { + }, + + /** + * The entry was removed automatically because its key or value was garbage-collected. This can + * occur when using {@link #softValues}, {@link #weakKeys}, or {@link #weakValues}. + */ + COLLECTED { + }, + + /** + * The entry's expiration timestamp has passed. This can occur when using {@link + * #expireAfterWrite} or {@link #expireAfterAccess}. + */ + EXPIRED { + }, + + /** + * The entry was evicted due to size constraints. This can occur when using {@link + * #maximumSize}. + */ + SIZE { + } + + } + + /** + * An object that can receive a notification when an entry is removed from a map. The removal + * resulting in notification could have occured to an entry being manually removed or replaced, or + * due to eviction resulting from timed expiration, exceeding a maximum size, or garbage + * collection. + *

+ *

An instance may be called concurrently by multiple threads to process different entries. + * Implementations of this interface should avoid performing blocking calls or synchronizing on + * shared resources. + * + * @param the most general type of keys this listener can listen for; for + * example {@code Object} if any key is acceptable + * @param the most general type of values this listener can listen for; for + * example {@code Object} if any key is acceptable + */ + interface RemovalListener { + /** + * Notifies the listener that a removal occurred at some point in the past. + */ + void onRemoval(RemovalNotification notification); + } + + /** + * A notification of the removal of a single entry. The key or value may be null if it was already + * garbage collected. + *

+ *

Like other {@code Map.Entry} instances associated with MapMaker, this class holds strong + * references to the key and value, regardless of the type of references the map may be using. + */ + static final class RemovalNotification extends ImmutableEntry { + private static final long serialVersionUID = 0; + + RemovalNotification(K key, V value, RemovalCause cause) { + super(key, value); + } + + } + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/MapMakerInternalMap.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/MapMakerInternalMap.java new file mode 100644 index 0000000000..82ae6a1829 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/MapMakerInternalMap.java @@ -0,0 +1,3329 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.io.Serializable; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.AbstractCollection; +import java.util.AbstractMap; +import java.util.AbstractQueue; +import java.util.AbstractSet; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.concurrent.locks.ReentrantLock; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; + +/** + * The concurrent hash map implementation built by {@link MapMaker}. + *

+ *

This implementation is heavily derived from revision 1.96 of ConcurrentHashMap.java. + * + * @author Bob Lee + * @author Charles Fry + * @author Doug Lea ({@code ConcurrentHashMap}) + */ +class MapMakerInternalMap + extends AbstractMap implements ConcurrentMap, Serializable { + + /* + * The basic strategy is to subdivide the table among Segments, each of which itself is a + * concurrently readable hash table. The map supports non-blocking reads and concurrent writes + * across different segments. + * + * If a maximum size is specified, a best-effort bounding is performed per segment, using a + * page-replacement algorithm to determine which entries to evict when the capacity has been + * exceeded. + * + * The page replacement algorithm's data structures are kept casually consistent with the map. The + * ordering of writes to a segment is sequentially consistent. An update to the map and recording + * of reads may not be immediately reflected on the algorithm's data structures. These structures + * are guarded by a lock and operations are applied in batches to avoid lock contention. The + * penalty of applying the batches is spread across threads so that the amortized cost is slightly + * higher than performing just the operation without enforcing the capacity constraint. + * + * This implementation uses a per-segment queue to record a memento of the additions, removals, + * and accesses that were performed on the map. The queue is drained on writes and when it exceeds + * its capacity threshold. + * + * The Least Recently Used page replacement algorithm was chosen due to its simplicity, high hit + * rate, and ability to be implemented with O(1) time complexity. The initial LRU implementation + * operates per-segment rather than globally for increased implementation simplicity. We expect + * the cache hit rate to be similar to that of a global LRU algorithm. + */ + + // Constants + + /** + * The maximum capacity, used if a higher value is implicitly specified by either of the + * constructors with arguments. MUST be a power of two <= 1<<30 to ensure that entries are + * indexable using ints. + */ + private static final int MAXIMUM_CAPACITY = Ints.MAX_POWER_OF_TWO; + + /** + * The maximum number of segments to allow; used to bound constructor arguments. + */ + private static final int MAX_SEGMENTS = 1 << 16; // slightly conservative + + /** + * Number of (unsynchronized) retries in the containsValue method. + */ + private static final int CONTAINS_VALUE_RETRIES = 3; + + /** + * Number of cache access operations that can be buffered per segment before the cache's recency + * ordering information is updated. This is used to avoid lock contention by recording a memento + * of reads and delaying a lock acquisition until the threshold is crossed or a mutation occurs. + *

+ *

This must be a (2^n)-1 as it is used as a mask. + */ + private static final int DRAIN_THRESHOLD = 0x3F; + + /** + * Maximum number of entries to be drained in a single cleanup run. This applies independently to + * the cleanup queue and both reference queues. + */ + // TODO(fry): empirically optimize this + private static final int DRAIN_MAX = 16; + + // Fields + /** + * Placeholder. Indicates that the value hasn't been set yet. + */ + private static final ValueReference UNSET = new ValueReference() { + @Override + public Object get() { + return null; + } + + @Override + public ReferenceEntry getEntry() { + return null; + } + + @Override + public ValueReference copyFor(ReferenceQueue queue, + Object value, ReferenceEntry entry) { + return this; + } + + @Override + public boolean isComputingReference() { + return false; + } + + @Override + public void clear(ValueReference newValue) { + } + }; + private static final Queue DISCARDING_QUEUE = new AbstractQueue() { + @Override + public boolean offer(Object o) { + return true; + } + + @Override + public Object peek() { + return null; + } + + @Override + public Object poll() { + return null; + } + + @Override + public int size() { + return 0; + } + + @Override + public Iterator iterator() { + return Iterators.emptyIterator(); + } + }; + private static final Logger logger = Logger.getLogger(MapMakerInternalMap.class.getName()); + private static final long serialVersionUID = 5; + /** + * Mask value for indexing into segments. The upper bits of a key's hash code are used to choose + * the segment. + */ + private final transient int segmentMask; + /** + * Shift value for indexing within segments. Helps prevent entries that end up in the same segment + * from also ending up in the same bucket. + */ + private final transient int segmentShift; + /** + * The segments, each of which is a specialized hash table. + */ + private final transient Segment[] segments; + /** + * The concurrency level. + */ + private final int concurrencyLevel; + /** + * Strategy for comparing keys. + */ + private final Equivalence keyEquivalence; + /** + * Strategy for comparing values. + */ + private final Equivalence valueEquivalence; + /** + * Strategy for referencing keys. + */ + private final Strength keyStrength; + /** + * Strategy for referencing values. + */ + private final Strength valueStrength; + /** + * The maximum size of this map. MapMaker.UNSET_INT if there is no maximum. + */ + private final int maximumSize; + /** + * How long after the last access to an entry the map will retain that entry. + */ + private final long expireAfterAccessNanos; + /** + * How long after the last write to an entry the map will retain that entry. + */ + private final long expireAfterWriteNanos; + /** + * Entries waiting to be consumed by the removal listener. + */ + // TODO(fry): define a new type which creates event objects and automates the clear logic + private final Queue> removalNotificationQueue; + /** + * A listener that is invoked when an entry is removed due to expiration or garbage collection of + * soft/weak entries. + */ + private final MapMaker.RemovalListener removalListener; + /** + * Factory used to create new entries. + */ + private final transient EntryFactory entryFactory; + /** + * Measures time in a testable way. + */ + private final Ticker ticker; + private transient Set keySet; + private transient Collection values; + private transient Set> entrySet; + + /** + * Creates a new, empty map with the specified strategy, initial capacity and concurrency level. + */ + private MapMakerInternalMap(MapMaker builder) { + concurrencyLevel = Math.min(builder.getConcurrencyLevel(), MAX_SEGMENTS); + + keyStrength = builder.getKeyStrength(); + valueStrength = builder.getValueStrength(); + + keyEquivalence = builder.getKeyEquivalence(); + valueEquivalence = valueStrength.defaultEquivalence(); + + maximumSize = builder.maximumSize; + expireAfterAccessNanos = builder.getExpireAfterAccessNanos(); + expireAfterWriteNanos = builder.getExpireAfterWriteNanos(); + + entryFactory = EntryFactory.getFactory(keyStrength, expires(), evictsBySize()); + ticker = builder.getTicker(); + + removalListener = builder.getRemovalListener(); + removalNotificationQueue = (removalListener == GenericMapMaker.NullListener.INSTANCE) + ? MapMakerInternalMap.discardingQueue() + : new ConcurrentLinkedQueue>(); + + int initialCapacity = Math.min(builder.getInitialCapacity(), MAXIMUM_CAPACITY); + if (evictsBySize()) { + initialCapacity = Math.min(initialCapacity, maximumSize); + } + + // Find power-of-two sizes best matching arguments. Constraints: + // (segmentCount <= maximumSize) + // && (concurrencyLevel > maximumSize || segmentCount > concurrencyLevel) + int segmentShift = 0; + int segmentCount = 1; + while (segmentCount < concurrencyLevel + && (!evictsBySize() || segmentCount * 2 <= maximumSize)) { + ++segmentShift; + segmentCount <<= 1; + } + this.segmentShift = 32 - segmentShift; + segmentMask = segmentCount - 1; + + this.segments = newSegmentArray(segmentCount); + + int segmentCapacity = initialCapacity / segmentCount; + if (segmentCapacity * segmentCount < initialCapacity) { + ++segmentCapacity; + } + + int segmentSize = 1; + while (segmentSize < segmentCapacity) { + segmentSize <<= 1; + } + + if (evictsBySize()) { + // Ensure sum of segment max sizes = overall max size + int maximumSegmentSize = maximumSize / segmentCount + 1; + int remainder = maximumSize % segmentCount; + for (int i = 0; i < this.segments.length; ++i) { + if (i == remainder) { + maximumSegmentSize--; + } + this.segments[i] = + createSegment(segmentSize, maximumSegmentSize); + } + } else { + for (int i = 0; i < this.segments.length; ++i) { + this.segments[i] = + createSegment(segmentSize, MapMaker.UNSET_INT); + } + } + } + + /** + * Singleton placeholder that indicates a value is being computed. + */ + @SuppressWarnings("unchecked") // impl never uses a parameter or returns any non-null value + private static ValueReference unset() { + return (ValueReference) UNSET; + } + + @SuppressWarnings("unchecked") // impl never uses a parameter or returns any non-null value + private static ReferenceEntry nullEntry() { + return (ReferenceEntry) NullEntry.INSTANCE; + } + + /** + * Queue that discards all elements. + */ + @SuppressWarnings("unchecked") // impl never uses a parameter or returns any non-null value + private static Queue discardingQueue() { + return (Queue) DISCARDING_QUEUE; + } + + /** + * Applies a supplemental hash function to a given hash code, which defends against poor quality + * hash functions. This is critical when the concurrent hash map uses power-of-two length hash + * tables, that otherwise encounter collisions for hash codes that do not differ in lower or + * upper bits. + * + * @param h hash code + */ + private static int rehash(int h) { + // Spread bits to regularize both segment and index locations, + // using variant of single-word Wang/Jenkins hash. + // TODO(kevinb): use Hashing/move this to Hashing? + h += (h << 15) ^ 0xffffcd7d; + h ^= (h >>> 10); + h += (h << 3); + h ^= (h >>> 6); + h += (h << 2) + (h << 14); + return h ^ (h >>> 16); + } + + // Guarded By Segment.this + private static void connectExpirables(ReferenceEntry previous, ReferenceEntry next) { + previous.setNextExpirable(next); + next.setPreviousExpirable(previous); + } + + // Guarded By Segment.this + private static void nullifyExpirable(ReferenceEntry nulled) { + ReferenceEntry nullEntry = nullEntry(); + nulled.setNextExpirable(nullEntry); + nulled.setPreviousExpirable(nullEntry); + } + + /** + * Links the evitables together. + */ + // Guarded By Segment.this + private static void connectEvictables(ReferenceEntry previous, ReferenceEntry next) { + previous.setNextEvictable(next); + next.setPreviousEvictable(previous); + } + + // Guarded By Segment.this + private static void nullifyEvictable(ReferenceEntry nulled) { + ReferenceEntry nullEntry = nullEntry(); + nulled.setNextEvictable(nullEntry); + nulled.setPreviousEvictable(nullEntry); + } + + boolean evictsBySize() { + return maximumSize != MapMaker.UNSET_INT; + } + + boolean expires() { + return expiresAfterWrite() || expiresAfterAccess(); + } + + private boolean expiresAfterWrite() { + return expireAfterWriteNanos > 0; + } + + /* + * Note: All of this duplicate code sucks, but it saves a lot of memory. If only Java had mixins! + * To maintain this code, make a change for the strong reference type. Then, cut and paste, and + * replace "Strong" with "Soft" or "Weak" within the pasted text. The primary difference is that + * strong entries store the key reference directly while soft and weak entries delegate to their + * respective superclasses. + */ + + boolean expiresAfterAccess() { + return expireAfterAccessNanos > 0; + } + + boolean usesKeyReferences() { + return keyStrength != Strength.STRONG; + } + + boolean usesValueReferences() { + return valueStrength != Strength.STRONG; + } + + private int hash(Object key) { + int h = keyEquivalence.hash(key); + return rehash(h); + } + + void reclaimValue(ValueReference valueReference) { + ReferenceEntry entry = valueReference.getEntry(); + int hash = entry.getHash(); + segmentFor(hash).reclaimValue(entry.getKey(), hash, valueReference); + } + + void reclaimKey(ReferenceEntry entry) { + int hash = entry.getHash(); + segmentFor(hash).reclaimKey(entry, hash); + } + + /** + * Returns the segment that should be used for a key with the given hash. + * + * @param hash the hash code for the key + * @return the segment + */ + private Segment segmentFor(int hash) { + // TODO(fry): Lazily create segments? + return segments[(hash >>> segmentShift) & segmentMask]; + } + + private Segment createSegment(int initialCapacity, int maxSegmentSize) { + return new Segment(this, initialCapacity, maxSegmentSize); + } + + /** + * Gets the value from an entry. Returns {@code null} if the entry is invalid, + * partially-collected, computing, or expired. Unlike {@link Segment#getLiveValue} this method + * does not attempt to clean up stale entries. + */ + private V getLiveValue(ReferenceEntry entry) { + if (entry.getKey() == null) { + return null; + } + V value = entry.getValueReference().get(); + if (value == null) { + return null; + } + + if (expires() && isExpired(entry)) { + return null; + } + return value; + } + + /** + * Returns {@code true} if the entry has expired. + */ + boolean isExpired(ReferenceEntry entry) { + return isExpired(entry, ticker.read()); + } + + /** + * Returns {@code true} if the entry has expired. + */ + boolean isExpired(ReferenceEntry entry, long now) { + // if the expiration time had overflowed, this "undoes" the overflow + return now - entry.getExpirationTime() > 0; + } + + /** + * Notifies listeners that an entry has been automatically removed due to expiration, eviction, + * or eligibility for garbage collection. This should be called every time expireEntries or + * evictEntry is called (once the lock is released). + */ + void processPendingNotifications() { + MapMaker.RemovalNotification notification; + while ((notification = removalNotificationQueue.poll()) != null) { + try { + removalListener.onRemoval(notification); + } catch (Exception e) { + logger.log(Level.WARNING, "Exception thrown by removal listener", e); + } + } + } + + @SuppressWarnings("unchecked") + private Segment[] newSegmentArray(int ssize) { + return new Segment[ssize]; + } + + @Override + public boolean isEmpty() { + /* + * Sum per-segment modCounts to avoid mis-reporting when elements are concurrently added and + * removed in one segment while checking another, in which case the table was never actually + * empty at any point. (The sum ensures accuracy up through at least 1<<31 per-segment + * modifications before recheck.) Method containsValue() uses similar constructions for + * stability checks. + */ + long sum = 0L; + Segment[] segments = this.segments; + for (int i = 0; i < segments.length; ++i) { + if (segments[i].count != 0) { + return false; + } + sum += segments[i].modCount; + } + + if (sum != 0L) { // recheck unless no modifications + for (int i = 0; i < segments.length; ++i) { + if (segments[i].count != 0) { + return false; + } + sum -= segments[i].modCount; + } + if (sum != 0L) { + return false; + } + } + return true; + } + + @Override + public int size() { + Segment[] segments = this.segments; + long sum = 0; + for (int i = 0; i < segments.length; ++i) { + sum += segments[i].count; + } + return Ints.saturatedCast(sum); + } + + @Override + public V get(Object key) { + if (key == null) { + return null; + } + int hash = hash(key); + return segmentFor(hash).get(key, hash); + } + + @Override + public boolean containsKey(Object key) { + if (key == null) { + return false; + } + int hash = hash(key); + return segmentFor(hash).containsKey(key, hash); + } + + @Override + public boolean containsValue(Object value) { + if (value == null) { + return false; + } + + // This implementation is patterned after ConcurrentHashMap, but without the locking. The only + // way for it to return a false negative would be for the target value to jump around in the map + // such that none of the subsequent iterations observed it, despite the fact that at every point + // in time it was present somewhere int the map. This becomes increasingly unlikely as + // CONTAINS_VALUE_RETRIES increases, though without locking it is theoretically possible. + final Segment[] segments = this.segments; + long last = -1L; + for (int i = 0; i < CONTAINS_VALUE_RETRIES; i++) { + long sum = 0L; + for (Segment segment : segments) { + // ensure visibility of most recent completed write + @SuppressWarnings({"UnusedDeclaration", "unused"}) + int c = segment.count; // read-volatile + + AtomicReferenceArray> table = segment.table; + for (int j = 0; j < table.length(); j++) { + for (ReferenceEntry e = table.get(j); e != null; e = e.getNext()) { + V v = segment.getLiveValue(e); + if (v != null && valueEquivalence.equivalent(value, v)) { + return true; + } + } + } + sum += segment.modCount; + } + if (sum == last) { + break; + } + last = sum; + } + return false; + } + + // expiration + + @Override + public V put(K key, V value) { + checkNotNull(key); + checkNotNull(value); + int hash = hash(key); + return segmentFor(hash).put(key, hash, value, false); + } + + @Override + public V putIfAbsent(K key, V value) { + checkNotNull(key); + checkNotNull(value); + int hash = hash(key); + return segmentFor(hash).put(key, hash, value, true); + } + + @Override + public void putAll(Map m) { + for (Entry e : m.entrySet()) { + put(e.getKey(), e.getValue()); + } + } + + @Override + public V remove(Object key) { + if (key == null) { + return null; + } + int hash = hash(key); + return segmentFor(hash).remove(key, hash); + } + + // eviction + + @Override + public boolean remove(Object key, Object value) { + if (key == null || value == null) { + return false; + } + int hash = hash(key); + return segmentFor(hash).remove(key, hash, value); + } + + @Override + public boolean replace(K key, V oldValue, V newValue) { + checkNotNull(key); + checkNotNull(newValue); + if (oldValue == null) { + return false; + } + int hash = hash(key); + return segmentFor(hash).replace(key, hash, oldValue, newValue); + } + + @Override + public V replace(K key, V value) { + checkNotNull(key); + checkNotNull(value); + int hash = hash(key); + return segmentFor(hash).replace(key, hash, value); + } + + @Override + public void clear() { + for (Segment segment : segments) { + segment.clear(); + } + } + + // Inner Classes + + @Override + public Set keySet() { + Set ks = keySet; + return (ks != null) ? ks : (keySet = new KeySet()); + } + + // Queues + + @Override + public Collection values() { + Collection vs = values; + return (vs != null) ? vs : (values = new Values()); + } + + @Override + public Set> entrySet() { + Set> es = entrySet; + return (es != null) ? es : (entrySet = new EntrySet()); + } + + // ConcurrentMap methods + + enum Strength { + /* + * TODO(kevinb): If we strongly reference the value and aren't computing, we needn't wrap the + * value. This could save ~8 bytes per entry. + */ + + STRONG { + @Override + ValueReference referenceValue( + Segment segment, ReferenceEntry entry, V value) { + return new StrongValueReference(value); + } + + @Override + Equivalence defaultEquivalence() { + return Equivalence.equals(); + } + }; + + /** + * Creates a reference for the given value according to this value strength. + */ + abstract ValueReference referenceValue( + Segment segment, ReferenceEntry entry, V value); + + /** + * Returns the default equivalence strategy used to compare and hash keys or values referenced + * at this strength. This strategy will be used unless the user explicitly specifies an + * alternate strategy. + */ + abstract Equivalence defaultEquivalence(); + } + + /** + * Creates new entries. + */ + enum EntryFactory { + STRONG { + @Override + ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next) { + return new StrongEntry(key, hash, next); + } + }, + STRONG_EXPIRABLE { + @Override + ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next) { + return new StrongExpirableEntry(key, hash, next); + } + + @Override + ReferenceEntry copyEntry( + Segment segment, ReferenceEntry original, ReferenceEntry newNext) { + ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + copyExpirableEntry(original, newEntry); + return newEntry; + } + }, + STRONG_EVICTABLE { + @Override + ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next) { + return new StrongEvictableEntry(key, hash, next); + } + + @Override + ReferenceEntry copyEntry( + Segment segment, ReferenceEntry original, ReferenceEntry newNext) { + ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + copyEvictableEntry(original, newEntry); + return newEntry; + } + }, + STRONG_EXPIRABLE_EVICTABLE { + @Override + ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next) { + return new StrongExpirableEvictableEntry(key, hash, next); + } + + @Override + ReferenceEntry copyEntry( + Segment segment, ReferenceEntry original, ReferenceEntry newNext) { + ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + copyExpirableEntry(original, newEntry); + copyEvictableEntry(original, newEntry); + return newEntry; + } + }, + + WEAK { + @Override + ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next) { + return new WeakEntry(segment.keyReferenceQueue, key, hash, next); + } + }, + WEAK_EXPIRABLE { + @Override + ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next) { + return new WeakExpirableEntry(segment.keyReferenceQueue, key, hash, next); + } + + @Override + ReferenceEntry copyEntry( + Segment segment, ReferenceEntry original, ReferenceEntry newNext) { + ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + copyExpirableEntry(original, newEntry); + return newEntry; + } + }, + WEAK_EVICTABLE { + @Override + ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next) { + return new WeakEvictableEntry(segment.keyReferenceQueue, key, hash, next); + } + + @Override + ReferenceEntry copyEntry( + Segment segment, ReferenceEntry original, ReferenceEntry newNext) { + ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + copyEvictableEntry(original, newEntry); + return newEntry; + } + }, + WEAK_EXPIRABLE_EVICTABLE { + @Override + ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next) { + return new WeakExpirableEvictableEntry(segment.keyReferenceQueue, key, hash, next); + } + + @Override + ReferenceEntry copyEntry( + Segment segment, ReferenceEntry original, ReferenceEntry newNext) { + ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + copyExpirableEntry(original, newEntry); + copyEvictableEntry(original, newEntry); + return newEntry; + } + }; + + /** + * Masks used to compute indices in the following table. + */ + static final int EXPIRABLE_MASK = 1; + static final int EVICTABLE_MASK = 2; + + /** + * Look-up table for factories. First dimension is the reference type. The second dimension is + * the result of OR-ing the feature masks. + */ + static final EntryFactory[][] factories = { + {STRONG, STRONG_EXPIRABLE, STRONG_EVICTABLE, STRONG_EXPIRABLE_EVICTABLE}, + {}, // no support for SOFT keys + {WEAK, WEAK_EXPIRABLE, WEAK_EVICTABLE, WEAK_EXPIRABLE_EVICTABLE} + }; + + static EntryFactory getFactory(Strength keyStrength, boolean expireAfterWrite, + boolean evictsBySize) { + int flags = (expireAfterWrite ? EXPIRABLE_MASK : 0) | (evictsBySize ? EVICTABLE_MASK : 0); + return factories[keyStrength.ordinal()][flags]; + } + + /** + * Creates a new entry. + * + * @param segment to create the entry for + * @param key of the entry + * @param hash of the key + * @param next entry in the same bucket + */ + abstract ReferenceEntry newEntry( + Segment segment, K key, int hash, ReferenceEntry next); + + /** + * Copies an entry, assigning it a new {@code next} entry. + * + * @param original the entry to copy + * @param newNext entry in the same bucket + */ + // Guarded By Segment.this + ReferenceEntry copyEntry( + Segment segment, ReferenceEntry original, ReferenceEntry newNext) { + return newEntry(segment, original.getKey(), original.getHash(), newNext); + } + + // Guarded By Segment.this + void copyExpirableEntry(ReferenceEntry original, ReferenceEntry newEntry) { + // TODO(fry): when we link values instead of entries this method can go + // away, as can connectExpirables, nullifyExpirable. + newEntry.setExpirationTime(original.getExpirationTime()); + + connectExpirables(original.getPreviousExpirable(), newEntry); + connectExpirables(newEntry, original.getNextExpirable()); + + nullifyExpirable(original); + } + + // Guarded By Segment.this + void copyEvictableEntry(ReferenceEntry original, ReferenceEntry newEntry) { + // TODO(fry): when we link values instead of entries this method can go + // away, as can connectEvictables, nullifyEvictable. + connectEvictables(original.getPreviousEvictable(), newEntry); + connectEvictables(newEntry, original.getNextEvictable()); + + nullifyEvictable(original); + } + } + + private enum NullEntry implements ReferenceEntry { + INSTANCE; + + @Override + public ValueReference getValueReference() { + return null; + } + + @Override + public void setValueReference(ValueReference valueReference) { + } + + @Override + public ReferenceEntry getNext() { + return null; + } + + @Override + public int getHash() { + return 0; + } + + @Override + public Object getKey() { + return null; + } + + @Override + public long getExpirationTime() { + return 0; + } + + @Override + public void setExpirationTime(long time) { + } + + @Override + public ReferenceEntry getNextExpirable() { + return this; + } + + @Override + public void setNextExpirable(ReferenceEntry next) { + } + + @Override + public ReferenceEntry getPreviousExpirable() { + return this; + } + + @Override + public void setPreviousExpirable(ReferenceEntry previous) { + } + + @Override + public ReferenceEntry getNextEvictable() { + return this; + } + + @Override + public void setNextEvictable(ReferenceEntry next) { + } + + @Override + public ReferenceEntry getPreviousEvictable() { + return this; + } + + @Override + public void setPreviousEvictable(ReferenceEntry previous) { + } + } + + /** + * A reference to a value. + */ + interface ValueReference { + /** + * Gets the value. Does not block or throw exceptions. + */ + V get(); + + /** + * Returns the entry associated with this value reference, or {@code null} if this value + * reference is independent of any entry. + */ + ReferenceEntry getEntry(); + + /** + * Creates a copy of this reference for the given entry. + *

+ *

{@code value} may be null only for a loading reference. + */ + ValueReference copyFor( + ReferenceQueue queue, V value, ReferenceEntry entry); + + /** + * Clears this reference object. + * + * @param newValue the new value reference which will replace this one; this is only used during + * computation to immediately notify blocked threads of the new value + */ + void clear(ValueReference newValue); + + /** + * Returns {@code true} if the value type is a computing reference (regardless of whether or not + * computation has completed). This is necessary to distiguish between partially-collected + * entries and computing entries, which need to be cleaned up differently. + */ + boolean isComputingReference(); + } + + /** + * An entry in a reference map. + *

+ * Entries in the map can be in the following states: + *

+ * Valid: + * - Live: valid key/value are set + * - Computing: computation is pending + *

+ * Invalid: + * - Expired: time expired (key/value may still be set) + * - Collected: key/value was partially collected, but not yet cleaned up + */ + interface ReferenceEntry { + /** + * Gets the value reference from this entry. + */ + ValueReference getValueReference(); + + /** + * Sets the value reference for this entry. + */ + void setValueReference(ValueReference valueReference); + + /** + * Gets the next entry in the chain. + */ + ReferenceEntry getNext(); + + /** + * Gets the entry's hash. + */ + int getHash(); + + /** + * Gets the key for this entry. + */ + K getKey(); + + /* + * Used by entries that are expirable. Expirable entries are maintained in a doubly-linked list. + * New entries are added at the tail of the list at write time; stale entries are expired from + * the head of the list. + */ + + /** + * Gets the entry expiration time in ns. + */ + long getExpirationTime(); + + /** + * Sets the entry expiration time in ns. + */ + void setExpirationTime(long time); + + /** + * Gets the next entry in the recency list. + */ + ReferenceEntry getNextExpirable(); + + /** + * Sets the next entry in the recency list. + */ + void setNextExpirable(ReferenceEntry next); + + /** + * Gets the previous entry in the recency list. + */ + ReferenceEntry getPreviousExpirable(); + + /** + * Sets the previous entry in the recency list. + */ + void setPreviousExpirable(ReferenceEntry previous); + + /* + * Implemented by entries that are evictable. Evictable entries are maintained in a + * doubly-linked list. New entries are added at the tail of the list at write time and stale + * entries are expired from the head of the list. + */ + + /** + * Gets the next entry in the recency list. + */ + ReferenceEntry getNextEvictable(); + + /** + * Sets the next entry in the recency list. + */ + void setNextEvictable(ReferenceEntry next); + + /** + * Gets the previous entry in the recency list. + */ + ReferenceEntry getPreviousEvictable(); + + /** + * Sets the previous entry in the recency list. + */ + void setPreviousEvictable(ReferenceEntry previous); + } + + abstract static class AbstractReferenceEntry implements ReferenceEntry { + @Override + public ValueReference getValueReference() { + throw new UnsupportedOperationException(); + } + + @Override + public void setValueReference(ValueReference valueReference) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getNext() { + throw new UnsupportedOperationException(); + } + + @Override + public int getHash() { + throw new UnsupportedOperationException(); + } + + @Override + public K getKey() { + throw new UnsupportedOperationException(); + } + + @Override + public long getExpirationTime() { + throw new UnsupportedOperationException(); + } + + @Override + public void setExpirationTime(long time) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getNextExpirable() { + throw new UnsupportedOperationException(); + } + + @Override + public void setNextExpirable(ReferenceEntry next) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getPreviousExpirable() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPreviousExpirable(ReferenceEntry previous) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getNextEvictable() { + throw new UnsupportedOperationException(); + } + + @Override + public void setNextEvictable(ReferenceEntry next) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getPreviousEvictable() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPreviousEvictable(ReferenceEntry previous) { + throw new UnsupportedOperationException(); + } + } + + /** + * Used for strongly-referenced keys. + */ + static class StrongEntry implements ReferenceEntry { + final K key; + final int hash; + final ReferenceEntry next; + + // null expiration + volatile ValueReference valueReference = unset(); + + StrongEntry(K key, int hash, ReferenceEntry next) { + this.key = key; + this.hash = hash; + this.next = next; + } + + @Override + public K getKey() { + return this.key; + } + + @Override + public long getExpirationTime() { + throw new UnsupportedOperationException(); + } + + @Override + public void setExpirationTime(long time) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getNextExpirable() { + throw new UnsupportedOperationException(); + } + + // null eviction + + @Override + public void setNextExpirable(ReferenceEntry next) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getPreviousExpirable() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPreviousExpirable(ReferenceEntry previous) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getNextEvictable() { + throw new UnsupportedOperationException(); + } + + // The code below is exactly the same for each entry type. + + @Override + public void setNextEvictable(ReferenceEntry next) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getPreviousEvictable() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPreviousEvictable(ReferenceEntry previous) { + throw new UnsupportedOperationException(); + } + + @Override + public ValueReference getValueReference() { + return valueReference; + } + + @Override + public void setValueReference(ValueReference valueReference) { + ValueReference previous = this.valueReference; + this.valueReference = valueReference; + previous.clear(valueReference); + } + + @Override + public int getHash() { + return hash; + } + + @Override + public ReferenceEntry getNext() { + return next; + } + } + + static final class StrongExpirableEntry extends StrongEntry + implements ReferenceEntry { + volatile long time = Long.MAX_VALUE; + + // The code below is exactly the same for each expirable entry type. + // Guarded By Segment.this + ReferenceEntry nextExpirable = nullEntry(); + // Guarded By Segment.this + ReferenceEntry previousExpirable = nullEntry(); + + StrongExpirableEntry(K key, int hash, ReferenceEntry next) { + super(key, hash, next); + } + + @Override + public long getExpirationTime() { + return time; + } + + @Override + public void setExpirationTime(long time) { + this.time = time; + } + + @Override + public ReferenceEntry getNextExpirable() { + return nextExpirable; + } + + @Override + public void setNextExpirable(ReferenceEntry next) { + this.nextExpirable = next; + } + + @Override + public ReferenceEntry getPreviousExpirable() { + return previousExpirable; + } + + @Override + public void setPreviousExpirable(ReferenceEntry previous) { + this.previousExpirable = previous; + } + } + + static final class StrongEvictableEntry + extends StrongEntry implements ReferenceEntry { + // Guarded By Segment.this + ReferenceEntry nextEvictable = nullEntry(); + + // The code below is exactly the same for each evictable entry type. + // Guarded By Segment.this + ReferenceEntry previousEvictable = nullEntry(); + + StrongEvictableEntry(K key, int hash, ReferenceEntry next) { + super(key, hash, next); + } + + @Override + public ReferenceEntry getNextEvictable() { + return nextEvictable; + } + + @Override + public void setNextEvictable(ReferenceEntry next) { + this.nextEvictable = next; + } + + @Override + public ReferenceEntry getPreviousEvictable() { + return previousEvictable; + } + + @Override + public void setPreviousEvictable(ReferenceEntry previous) { + this.previousEvictable = previous; + } + } + + static final class StrongExpirableEvictableEntry + extends StrongEntry implements ReferenceEntry { + volatile long time = Long.MAX_VALUE; + + // The code below is exactly the same for each expirable entry type. + // Guarded By Segment.this + ReferenceEntry nextExpirable = nullEntry(); + // Guarded By Segment.this + ReferenceEntry previousExpirable = nullEntry(); + // Guarded By Segment.this + ReferenceEntry nextEvictable = nullEntry(); + // Guarded By Segment.this + ReferenceEntry previousEvictable = nullEntry(); + + StrongExpirableEvictableEntry(K key, int hash, ReferenceEntry next) { + super(key, hash, next); + } + + @Override + public long getExpirationTime() { + return time; + } + + @Override + public void setExpirationTime(long time) { + this.time = time; + } + + @Override + public ReferenceEntry getNextExpirable() { + return nextExpirable; + } + + @Override + public void setNextExpirable(ReferenceEntry next) { + this.nextExpirable = next; + } + + // The code below is exactly the same for each evictable entry type. + + @Override + public ReferenceEntry getPreviousExpirable() { + return previousExpirable; + } + + @Override + public void setPreviousExpirable(ReferenceEntry previous) { + this.previousExpirable = previous; + } + + @Override + public ReferenceEntry getNextEvictable() { + return nextEvictable; + } + + @Override + public void setNextEvictable(ReferenceEntry next) { + this.nextEvictable = next; + } + + @Override + public ReferenceEntry getPreviousEvictable() { + return previousEvictable; + } + + @Override + public void setPreviousEvictable(ReferenceEntry previous) { + this.previousEvictable = previous; + } + } + + /** + * Used for weakly-referenced keys. + */ + static class WeakEntry extends WeakReference implements ReferenceEntry { + final int hash; + final ReferenceEntry next; + + // null expiration + volatile ValueReference valueReference = unset(); + + WeakEntry(ReferenceQueue queue, K key, int hash, ReferenceEntry next) { + super(key, queue); + this.hash = hash; + this.next = next; + } + + @Override + public K getKey() { + return get(); + } + + @Override + public long getExpirationTime() { + throw new UnsupportedOperationException(); + } + + @Override + public void setExpirationTime(long time) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getNextExpirable() { + throw new UnsupportedOperationException(); + } + + // null eviction + + @Override + public void setNextExpirable(ReferenceEntry next) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getPreviousExpirable() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPreviousExpirable(ReferenceEntry previous) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getNextEvictable() { + throw new UnsupportedOperationException(); + } + + // The code below is exactly the same for each entry type. + + @Override + public void setNextEvictable(ReferenceEntry next) { + throw new UnsupportedOperationException(); + } + + @Override + public ReferenceEntry getPreviousEvictable() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPreviousEvictable(ReferenceEntry previous) { + throw new UnsupportedOperationException(); + } + + @Override + public ValueReference getValueReference() { + return valueReference; + } + + @Override + public void setValueReference(ValueReference valueReference) { + ValueReference previous = this.valueReference; + this.valueReference = valueReference; + previous.clear(valueReference); + } + + @Override + public int getHash() { + return hash; + } + + @Override + public ReferenceEntry getNext() { + return next; + } + } + + static final class WeakExpirableEntry + extends WeakEntry implements ReferenceEntry { + volatile long time = Long.MAX_VALUE; + + // The code below is exactly the same for each expirable entry type. + // Guarded By Segment.this + ReferenceEntry nextExpirable = nullEntry(); + // Guarded By Segment.this + ReferenceEntry previousExpirable = nullEntry(); + + WeakExpirableEntry( + ReferenceQueue queue, K key, int hash, ReferenceEntry next) { + super(queue, key, hash, next); + } + + @Override + public long getExpirationTime() { + return time; + } + + @Override + public void setExpirationTime(long time) { + this.time = time; + } + + @Override + public ReferenceEntry getNextExpirable() { + return nextExpirable; + } + + @Override + public void setNextExpirable(ReferenceEntry next) { + this.nextExpirable = next; + } + + @Override + public ReferenceEntry getPreviousExpirable() { + return previousExpirable; + } + + @Override + public void setPreviousExpirable(ReferenceEntry previous) { + this.previousExpirable = previous; + } + } + + static final class WeakEvictableEntry + extends WeakEntry implements ReferenceEntry { + // Guarded By Segment.this + ReferenceEntry nextEvictable = nullEntry(); + + // The code below is exactly the same for each evictable entry type. + // Guarded By Segment.this + ReferenceEntry previousEvictable = nullEntry(); + + WeakEvictableEntry( + ReferenceQueue queue, K key, int hash, ReferenceEntry next) { + super(queue, key, hash, next); + } + + @Override + public ReferenceEntry getNextEvictable() { + return nextEvictable; + } + + @Override + public void setNextEvictable(ReferenceEntry next) { + this.nextEvictable = next; + } + + @Override + public ReferenceEntry getPreviousEvictable() { + return previousEvictable; + } + + @Override + public void setPreviousEvictable(ReferenceEntry previous) { + this.previousEvictable = previous; + } + } + + static final class WeakExpirableEvictableEntry + extends WeakEntry implements ReferenceEntry { + volatile long time = Long.MAX_VALUE; + + // The code below is exactly the same for each expirable entry type. + // Guarded By Segment.this + ReferenceEntry nextExpirable = nullEntry(); + // Guarded By Segment.this + ReferenceEntry previousExpirable = nullEntry(); + // Guarded By Segment.this + ReferenceEntry nextEvictable = nullEntry(); + // Guarded By Segment.this + ReferenceEntry previousEvictable = nullEntry(); + + WeakExpirableEvictableEntry( + ReferenceQueue queue, K key, int hash, ReferenceEntry next) { + super(queue, key, hash, next); + } + + @Override + public long getExpirationTime() { + return time; + } + + @Override + public void setExpirationTime(long time) { + this.time = time; + } + + @Override + public ReferenceEntry getNextExpirable() { + return nextExpirable; + } + + @Override + public void setNextExpirable(ReferenceEntry next) { + this.nextExpirable = next; + } + + // The code below is exactly the same for each evictable entry type. + + @Override + public ReferenceEntry getPreviousExpirable() { + return previousExpirable; + } + + @Override + public void setPreviousExpirable(ReferenceEntry previous) { + this.previousExpirable = previous; + } + + @Override + public ReferenceEntry getNextEvictable() { + return nextEvictable; + } + + @Override + public void setNextEvictable(ReferenceEntry next) { + this.nextEvictable = next; + } + + @Override + public ReferenceEntry getPreviousEvictable() { + return previousEvictable; + } + + @Override + public void setPreviousEvictable(ReferenceEntry previous) { + this.previousEvictable = previous; + } + } + + /** + * References a strong value. + */ + static final class StrongValueReference implements ValueReference { + final V referent; + + StrongValueReference(V referent) { + this.referent = referent; + } + + @Override + public V get() { + return referent; + } + + @Override + public ReferenceEntry getEntry() { + return null; + } + + @Override + public ValueReference copyFor( + ReferenceQueue queue, V value, ReferenceEntry entry) { + return this; + } + + @Override + public boolean isComputingReference() { + return false; + } + + @Override + public void clear(ValueReference newValue) { + } + } + + /** + * Segments are specialized versions of hash tables. This subclass inherits from ReentrantLock + * opportunistically, just to simplify some locking and avoid separate construction. + */ + @SuppressWarnings("serial") // This class is never serialized. + static class Segment extends ReentrantLock { + + /* + * TODO(fry): Consider copying variables (like evictsBySize) from outer class into this class. + * It will require more memory but will reduce indirection. + */ + + /* + * Segments maintain a table of entry lists that are ALWAYS kept in a consistent state, so can + * be read without locking. Next fields of nodes are immutable (final). All list additions are + * performed at the front of each bin. This makes it easy to check changes, and also fast to + * traverse. When nodes would otherwise be changed, new nodes are created to replace them. This + * works well for hash tables since the bin lists tend to be short. (The average length is less + * than two.) + * + * Read operations can thus proceed without locking, but rely on selected uses of volatiles to + * ensure that completed write operations performed by other threads are noticed. For most + * purposes, the "count" field, tracking the number of elements, serves as that volatile + * variable ensuring visibility. This is convenient because this field needs to be read in many + * read operations anyway: + * + * - All (unsynchronized) read operations must first read the "count" field, and should not + * look at table entries if it is 0. + * + * - All (synchronized) write operations should write to the "count" field after structurally + * changing any bin. The operations must not take any action that could even momentarily + * cause a concurrent read operation to see inconsistent data. This is made easier by the + * nature of the read operations in Map. For example, no operation can reveal that the table + * has grown but the threshold has not yet been updated, so there are no atomicity requirements + * for this with respect to reads. + * + * As a guide, all critical volatile reads and writes to the count field are marked in code + * comments. + */ + + final MapMakerInternalMap map; + /** + * The maximum size of this map. MapMaker.UNSET_INT if there is no maximum. + */ + final int maxSegmentSize; + /** + * The key reference queue contains entries whose keys have been garbage collected, and which + * need to be cleaned up internally. + */ + final ReferenceQueue keyReferenceQueue; + /** + * The value reference queue contains value references whose values have been garbage collected, + * and which need to be cleaned up internally. + */ + final ReferenceQueue valueReferenceQueue; + /** + * The recency queue is used to record which entries were accessed for updating the eviction + * list's ordering. It is drained as a batch operation when either the DRAIN_THRESHOLD is + * crossed or a write occurs on the segment. + */ + final Queue> recencyQueue; + /** + * A counter of the number of reads since the last write, used to drain queues on a small + * fraction of read operations. + */ + final AtomicInteger readCount = new AtomicInteger(); + /** + * A queue of elements currently in the map, ordered by access time. Elements are added to the + * tail of the queue on access/write. + */ + final Queue> evictionQueue; + /** + * A queue of elements currently in the map, ordered by expiration time (either access or write + * time). Elements are added to the tail of the queue on access/write. + */ + final Queue> expirationQueue; + /** + * The number of live elements in this segment's region. This does not include unset elements + * which are awaiting cleanup. + */ + volatile int count; + /** + * Number of updates that alter the size of the table. This is used during bulk-read methods to + * make sure they see a consistent snapshot: If modCounts change during a traversal of segments + * computing size or checking containsValue, then we might have an inconsistent view of state + * so (usually) must retry. + */ + int modCount; + /** + * The table is expanded when its size exceeds this threshold. (The value of this field is + * always {@code (int) (capacity * 0.75)}.) + */ + int threshold; + /** + * The per-segment table. + */ + volatile AtomicReferenceArray> table; + + Segment(MapMakerInternalMap map, int initialCapacity, int maxSegmentSize) { + this.map = map; + this.maxSegmentSize = maxSegmentSize; + initTable(newEntryArray(initialCapacity)); + + keyReferenceQueue = map.usesKeyReferences() + ? new ReferenceQueue() : null; + + valueReferenceQueue = map.usesValueReferences() + ? new ReferenceQueue() : null; + + recencyQueue = (map.evictsBySize() || map.expiresAfterAccess()) + ? new ConcurrentLinkedQueue>() + : MapMakerInternalMap.discardingQueue(); + + evictionQueue = map.evictsBySize() + ? new EvictionQueue() + : MapMakerInternalMap.discardingQueue(); + + expirationQueue = map.expires() + ? new ExpirationQueue() + : MapMakerInternalMap.discardingQueue(); + } + + AtomicReferenceArray> newEntryArray(int size) { + return new AtomicReferenceArray>(size); + } + + void initTable(AtomicReferenceArray> newTable) { + this.threshold = newTable.length() * 3 / 4; // 0.75 + if (this.threshold == maxSegmentSize) { + // prevent spurious expansion before eviction + this.threshold++; + } + this.table = newTable; + } + + ReferenceEntry newEntry(K key, int hash, ReferenceEntry next) { + return map.entryFactory.newEntry(this, key, hash, next); + } + + /** + * Copies {@code original} into a new entry chained to {@code newNext}. Returns the new entry, + * or {@code null} if {@code original} was already garbage collected. + */ + ReferenceEntry copyEntry(ReferenceEntry original, ReferenceEntry newNext) { + if (original.getKey() == null) { + // key collected + return null; + } + + ValueReference valueReference = original.getValueReference(); + V value = valueReference.get(); + if ((value == null) && !valueReference.isComputingReference()) { + // value collected + return null; + } + + ReferenceEntry newEntry = map.entryFactory.copyEntry(this, original, newNext); + newEntry.setValueReference(valueReference.copyFor(this.valueReferenceQueue, value, newEntry)); + return newEntry; + } + + /** + * Sets a new value of an entry. Adds newly created entries at the end of the expiration queue. + */ + void setValue(ReferenceEntry entry, V value) { + ValueReference valueReference = map.valueStrength.referenceValue(this, entry, value); + entry.setValueReference(valueReference); + recordWrite(entry); + } + + // reference queues, for garbage collection cleanup + + /** + * Cleanup collected entries when the lock is available. + */ + void tryDrainReferenceQueues() { + if (tryLock()) { + try { + drainReferenceQueues(); + } finally { + unlock(); + } + } + } + + /** + * Drain the key and value reference queues, cleaning up internal entries containing garbage + * collected keys or values. + */ + void drainReferenceQueues() { + if (map.usesKeyReferences()) { + drainKeyReferenceQueue(); + } + if (map.usesValueReferences()) { + drainValueReferenceQueue(); + } + } + + void drainKeyReferenceQueue() { + Reference ref; + int i = 0; + while ((ref = keyReferenceQueue.poll()) != null) { + @SuppressWarnings("unchecked") + ReferenceEntry entry = (ReferenceEntry) ref; + map.reclaimKey(entry); + if (++i == DRAIN_MAX) { + break; + } + } + } + + void drainValueReferenceQueue() { + Reference ref; + int i = 0; + while ((ref = valueReferenceQueue.poll()) != null) { + @SuppressWarnings("unchecked") + ValueReference valueReference = (ValueReference) ref; + map.reclaimValue(valueReference); + if (++i == DRAIN_MAX) { + break; + } + } + } + + /** + * Clears all entries from the key and value reference queues. + */ + void clearReferenceQueues() { + if (map.usesKeyReferences()) { + clearKeyReferenceQueue(); + } + if (map.usesValueReferences()) { + clearValueReferenceQueue(); + } + } + + void clearKeyReferenceQueue() { + while (keyReferenceQueue.poll() != null) { + } + } + + void clearValueReferenceQueue() { + while (valueReferenceQueue.poll() != null) { + } + } + + // recency queue, shared by expiration and eviction + + /** + * Records the relative order in which this read was performed by adding {@code entry} to the + * recency queue. At write-time, or when the queue is full past the threshold, the queue will + * be drained and the entries therein processed. + *

+ *

Note: locked reads should use {@link #recordLockedRead}. + */ + void recordRead(ReferenceEntry entry) { + if (map.expiresAfterAccess()) { + recordExpirationTime(entry, map.expireAfterAccessNanos); + } + recencyQueue.add(entry); + } + + /** + * Updates the eviction metadata that {@code entry} was just read. This currently amounts to + * adding {@code entry} to relevant eviction lists. + *

+ *

Note: this method should only be called under lock, as it directly manipulates the + * eviction queues. Unlocked reads should use {@link #recordRead}. + */ + void recordLockedRead(ReferenceEntry entry) { + evictionQueue.add(entry); + if (map.expiresAfterAccess()) { + recordExpirationTime(entry, map.expireAfterAccessNanos); + expirationQueue.add(entry); + } + } + + /** + * Updates eviction metadata that {@code entry} was just written. This currently amounts to + * adding {@code entry} to relevant eviction lists. + */ + void recordWrite(ReferenceEntry entry) { + // we are already under lock, so drain the recency queue immediately + drainRecencyQueue(); + evictionQueue.add(entry); + if (map.expires()) { + // currently MapMaker ensures that expireAfterWrite and + // expireAfterAccess are mutually exclusive + long expiration = map.expiresAfterAccess() + ? map.expireAfterAccessNanos + : map.expireAfterWriteNanos; + recordExpirationTime(entry, expiration); + expirationQueue.add(entry); + } + } + + /** + * Drains the recency queue, updating eviction metadata that the entries therein were read in + * the specified relative order. This currently amounts to adding them to relevant eviction + * lists (accounting for the fact that they could have been removed from the map since being + * added to the recency queue). + */ + void drainRecencyQueue() { + ReferenceEntry e; + while ((e = recencyQueue.poll()) != null) { + // An entry may be in the recency queue despite it being removed from + // the map . This can occur when the entry was concurrently read while a + // writer is removing it from the segment or after a clear has removed + // all of the segment's entries. + if (evictionQueue.contains(e)) { + evictionQueue.add(e); + } + if (map.expiresAfterAccess() && expirationQueue.contains(e)) { + expirationQueue.add(e); + } + } + } + + // expiration + + void recordExpirationTime(ReferenceEntry entry, long expirationNanos) { + // might overflow, but that's okay (see isExpired()) + entry.setExpirationTime(map.ticker.read() + expirationNanos); + } + + /** + * Cleanup expired entries when the lock is available. + */ + void tryExpireEntries() { + if (tryLock()) { + try { + expireEntries(); + } finally { + unlock(); + // don't call postWriteCleanup as we're in a read + } + } + } + + void expireEntries() { + drainRecencyQueue(); + + if (expirationQueue.isEmpty()) { + // There's no point in calling nanoTime() if we have no entries to + // expire. + return; + } + long now = map.ticker.read(); + ReferenceEntry e; + while ((e = expirationQueue.peek()) != null && map.isExpired(e, now)) { + if (!removeEntry(e, e.getHash(), MapMaker.RemovalCause.EXPIRED)) { + throw new AssertionError(); + } + } + } + + // eviction + + void enqueueNotification(ReferenceEntry entry, MapMaker.RemovalCause cause) { + enqueueNotification(entry.getKey(), entry.getHash(), entry.getValueReference().get(), cause); + } + + void enqueueNotification(K key, int hash, V value, MapMaker.RemovalCause cause) { + if (map.removalNotificationQueue != DISCARDING_QUEUE) { + MapMaker.RemovalNotification notification = new MapMaker.RemovalNotification(key, value, cause); + map.removalNotificationQueue.offer(notification); + } + } + + /** + * Performs eviction if the segment is full. This should only be called prior to adding a new + * entry and increasing {@code count}. + * + * @return {@code true} if eviction occurred + */ + boolean evictEntries() { + if (map.evictsBySize() && count >= maxSegmentSize) { + drainRecencyQueue(); + + ReferenceEntry e = evictionQueue.remove(); + if (!removeEntry(e, e.getHash(), MapMaker.RemovalCause.SIZE)) { + throw new AssertionError(); + } + return true; + } + return false; + } + + /** + * Returns first entry of bin for given hash. + */ + ReferenceEntry getFirst(int hash) { + // read this volatile field only once + AtomicReferenceArray> table = this.table; + return table.get(hash & (table.length() - 1)); + } + + // Specialized implementations of map methods + + ReferenceEntry getEntry(Object key, int hash) { + if (count != 0) { // read-volatile + for (ReferenceEntry e = getFirst(hash); e != null; e = e.getNext()) { + if (e.getHash() != hash) { + continue; + } + + K entryKey = e.getKey(); + if (entryKey == null) { + tryDrainReferenceQueues(); + continue; + } + + if (map.keyEquivalence.equivalent(key, entryKey)) { + return e; + } + } + } + + return null; + } + + ReferenceEntry getLiveEntry(Object key, int hash) { + ReferenceEntry e = getEntry(key, hash); + if (e == null) { + return null; + } else if (map.expires() && map.isExpired(e)) { + tryExpireEntries(); + return null; + } + return e; + } + + V get(Object key, int hash) { + try { + ReferenceEntry e = getLiveEntry(key, hash); + if (e == null) { + return null; + } + + V value = e.getValueReference().get(); + if (value != null) { + recordRead(e); + } else { + tryDrainReferenceQueues(); + } + return value; + } finally { + postReadCleanup(); + } + } + + boolean containsKey(Object key, int hash) { + try { + if (count != 0) { // read-volatile + ReferenceEntry e = getLiveEntry(key, hash); + if (e == null) { + return false; + } + return e.getValueReference().get() != null; + } + + return false; + } finally { + postReadCleanup(); + } + } + + V put(K key, int hash, V value, boolean onlyIfAbsent) { + lock(); + try { + preWriteCleanup(); + + int newCount = this.count + 1; + if (newCount > this.threshold) { // ensure capacity + expand(); + newCount = this.count + 1; + } + + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + // Look for an existing entry. + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + // We found an existing entry. + + ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + + if (entryValue == null) { + ++modCount; + setValue(e, value); + if (!valueReference.isComputingReference()) { + enqueueNotification(key, hash, entryValue, MapMaker.RemovalCause.COLLECTED); + newCount = this.count; // count remains unchanged + } else if (evictEntries()) { // evictEntries after setting new value + newCount = this.count + 1; + } + this.count = newCount; // write-volatile + return null; + } else if (onlyIfAbsent) { + // Mimic + // "if (!map.containsKey(key)) ... + // else return map.get(key); + recordLockedRead(e); + return entryValue; + } else { + // clobber existing entry, count remains unchanged + ++modCount; + enqueueNotification(key, hash, entryValue, MapMaker.RemovalCause.REPLACED); + setValue(e, value); + return entryValue; + } + } + } + + // Create a new entry. + ++modCount; + ReferenceEntry newEntry = newEntry(key, hash, first); + setValue(newEntry, value); + table.set(index, newEntry); + if (evictEntries()) { // evictEntries after setting new value + newCount = this.count + 1; + } + this.count = newCount; // write-volatile + return null; + } finally { + unlock(); + postWriteCleanup(); + } + } + + /** + * Expands the table if possible. + */ + void expand() { + AtomicReferenceArray> oldTable = table; + int oldCapacity = oldTable.length(); + if (oldCapacity >= MAXIMUM_CAPACITY) { + return; + } + + /* + * Reclassify nodes in each list to new Map. Because we are using power-of-two expansion, the + * elements from each bin must either stay at same index, or move with a power of two offset. + * We eliminate unnecessary node creation by catching cases where old nodes can be reused + * because their next fields won't change. Statistically, at the default threshold, only + * about one-sixth of them need cloning when a table doubles. The nodes they replace will be + * garbage collectable as soon as they are no longer referenced by any reader thread that may + * be in the midst of traversing table right now. + */ + + int newCount = count; + AtomicReferenceArray> newTable = newEntryArray(oldCapacity << 1); + threshold = newTable.length() * 3 / 4; + int newMask = newTable.length() - 1; + for (int oldIndex = 0; oldIndex < oldCapacity; ++oldIndex) { + // We need to guarantee that any existing reads of old Map can + // proceed. So we cannot yet null out each bin. + ReferenceEntry head = oldTable.get(oldIndex); + + if (head != null) { + ReferenceEntry next = head.getNext(); + int headIndex = head.getHash() & newMask; + + // Single node on list + if (next == null) { + newTable.set(headIndex, head); + } else { + // Reuse the consecutive sequence of nodes with the same target + // index from the end of the list. tail points to the first + // entry in the reusable list. + ReferenceEntry tail = head; + int tailIndex = headIndex; + for (ReferenceEntry e = next; e != null; e = e.getNext()) { + int newIndex = e.getHash() & newMask; + if (newIndex != tailIndex) { + // The index changed. We'll need to copy the previous entry. + tailIndex = newIndex; + tail = e; + } + } + newTable.set(tailIndex, tail); + + // Clone nodes leading up to the tail. + for (ReferenceEntry e = head; e != tail; e = e.getNext()) { + int newIndex = e.getHash() & newMask; + ReferenceEntry newNext = newTable.get(newIndex); + ReferenceEntry newFirst = copyEntry(e, newNext); + if (newFirst != null) { + newTable.set(newIndex, newFirst); + } else { + removeCollectedEntry(e); + newCount--; + } + } + } + } + } + table = newTable; + this.count = newCount; + } + + boolean replace(K key, int hash, V oldValue, V newValue) { + lock(); + try { + preWriteCleanup(); + + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + // If the value disappeared, this entry is partially collected, + // and we should pretend like it doesn't exist. + ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + if (entryValue == null) { + if (isCollected(valueReference)) { + int newCount = this.count - 1; + ++modCount; + enqueueNotification(entryKey, hash, entryValue, MapMaker.RemovalCause.COLLECTED); + ReferenceEntry newFirst = removeFromChain(first, e); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + } + return false; + } + + if (map.valueEquivalence.equivalent(oldValue, entryValue)) { + ++modCount; + enqueueNotification(key, hash, entryValue, MapMaker.RemovalCause.REPLACED); + setValue(e, newValue); + return true; + } else { + // Mimic + // "if (map.containsKey(key) && map.get(key).equals(oldValue))..." + recordLockedRead(e); + return false; + } + } + } + + return false; + } finally { + unlock(); + postWriteCleanup(); + } + } + + V replace(K key, int hash, V newValue) { + lock(); + try { + preWriteCleanup(); + + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + // If the value disappeared, this entry is partially collected, + // and we should pretend like it doesn't exist. + ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + if (entryValue == null) { + if (isCollected(valueReference)) { + int newCount = this.count - 1; + ++modCount; + enqueueNotification(entryKey, hash, entryValue, MapMaker.RemovalCause.COLLECTED); + ReferenceEntry newFirst = removeFromChain(first, e); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + } + return null; + } + + ++modCount; + enqueueNotification(key, hash, entryValue, MapMaker.RemovalCause.REPLACED); + setValue(e, newValue); + return entryValue; + } + } + + return null; + } finally { + unlock(); + postWriteCleanup(); + } + } + + V remove(Object key, int hash) { + lock(); + try { + preWriteCleanup(); + + int newCount = this.count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + + MapMaker.RemovalCause cause; + if (entryValue != null) { + cause = MapMaker.RemovalCause.EXPLICIT; + } else if (isCollected(valueReference)) { + cause = MapMaker.RemovalCause.COLLECTED; + } else { + return null; + } + + ++modCount; + enqueueNotification(entryKey, hash, entryValue, cause); + ReferenceEntry newFirst = removeFromChain(first, e); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + return entryValue; + } + } + + return null; + } finally { + unlock(); + postWriteCleanup(); + } + } + + boolean remove(Object key, int hash, Object value) { + lock(); + try { + preWriteCleanup(); + + int newCount = this.count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + + MapMaker.RemovalCause cause; + if (map.valueEquivalence.equivalent(value, entryValue)) { + cause = MapMaker.RemovalCause.EXPLICIT; + } else if (isCollected(valueReference)) { + cause = MapMaker.RemovalCause.COLLECTED; + } else { + return false; + } + + ++modCount; + enqueueNotification(entryKey, hash, entryValue, cause); + ReferenceEntry newFirst = removeFromChain(first, e); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + return (cause == MapMaker.RemovalCause.EXPLICIT); + } + } + + return false; + } finally { + unlock(); + postWriteCleanup(); + } + } + + void clear() { + if (count != 0) { + lock(); + try { + AtomicReferenceArray> table = this.table; + if (map.removalNotificationQueue != DISCARDING_QUEUE) { + for (int i = 0; i < table.length(); ++i) { + for (ReferenceEntry e = table.get(i); e != null; e = e.getNext()) { + // Computing references aren't actually in the map yet. + if (!e.getValueReference().isComputingReference()) { + enqueueNotification(e, MapMaker.RemovalCause.EXPLICIT); + } + } + } + } + for (int i = 0; i < table.length(); ++i) { + table.set(i, null); + } + clearReferenceQueues(); + evictionQueue.clear(); + expirationQueue.clear(); + readCount.set(0); + + ++modCount; + count = 0; // write-volatile + } finally { + unlock(); + postWriteCleanup(); + } + } + } + + /** + * Removes an entry from within a table. All entries following the removed node can stay, but + * all preceding ones need to be cloned. + *

+ *

This method does not decrement count for the removed entry, but does decrement count for + * all partially collected entries which are skipped. As such callers which are modifying count + * must re-read it after calling removeFromChain. + * + * @param first the first entry of the table + * @param entry the entry being removed from the table + * @return the new first entry for the table + */ + ReferenceEntry removeFromChain(ReferenceEntry first, ReferenceEntry entry) { + evictionQueue.remove(entry); + expirationQueue.remove(entry); + + int newCount = count; + ReferenceEntry newFirst = entry.getNext(); + for (ReferenceEntry e = first; e != entry; e = e.getNext()) { + ReferenceEntry next = copyEntry(e, newFirst); + if (next != null) { + newFirst = next; + } else { + removeCollectedEntry(e); + newCount--; + } + } + this.count = newCount; + return newFirst; + } + + void removeCollectedEntry(ReferenceEntry entry) { + enqueueNotification(entry, MapMaker.RemovalCause.COLLECTED); + evictionQueue.remove(entry); + expirationQueue.remove(entry); + } + + /** + * Removes an entry whose key has been garbage collected. + */ + boolean reclaimKey(ReferenceEntry entry, int hash) { + lock(); + try { + int newCount = count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + if (e == entry) { + ++modCount; + enqueueNotification( + e.getKey(), hash, e.getValueReference().get(), MapMaker.RemovalCause.COLLECTED); + ReferenceEntry newFirst = removeFromChain(first, e); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + return true; + } + } + + return false; + } finally { + unlock(); + postWriteCleanup(); + } + } + + /** + * Removes an entry whose value has been garbage collected. + */ + boolean reclaimValue(K key, int hash, ValueReference valueReference) { + lock(); + try { + int newCount = this.count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null + && map.keyEquivalence.equivalent(key, entryKey)) { + ValueReference v = e.getValueReference(); + if (v == valueReference) { + ++modCount; + enqueueNotification(key, hash, valueReference.get(), MapMaker.RemovalCause.COLLECTED); + ReferenceEntry newFirst = removeFromChain(first, e); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + return true; + } + return false; + } + } + + return false; + } finally { + unlock(); + if (!isHeldByCurrentThread()) { // don't cleanup inside of put + postWriteCleanup(); + } + } + } + + boolean removeEntry(ReferenceEntry entry, int hash, MapMaker.RemovalCause cause) { + int newCount = this.count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & (table.length() - 1); + ReferenceEntry first = table.get(index); + + for (ReferenceEntry e = first; e != null; e = e.getNext()) { + if (e == entry) { + ++modCount; + enqueueNotification(e.getKey(), hash, e.getValueReference().get(), cause); + ReferenceEntry newFirst = removeFromChain(first, e); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; // write-volatile + return true; + } + } + + return false; + } + + /** + * Returns {@code true} if the value has been partially collected, meaning that the value is + * null and it is not computing. + */ + boolean isCollected(ValueReference valueReference) { + if (valueReference.isComputingReference()) { + return false; + } + return (valueReference.get() == null); + } + + /** + * Gets the value from an entry. Returns {@code null} if the entry is invalid, + * partially-collected, computing, or expired. + */ + V getLiveValue(ReferenceEntry entry) { + if (entry.getKey() == null) { + tryDrainReferenceQueues(); + return null; + } + V value = entry.getValueReference().get(); + if (value == null) { + tryDrainReferenceQueues(); + return null; + } + + if (map.expires() && map.isExpired(entry)) { + tryExpireEntries(); + return null; + } + return value; + } + + /** + * Performs routine cleanup following a read. Normally cleanup happens during writes, or from + * the cleanupExecutor. If cleanup is not observed after a sufficient number of reads, try + * cleaning up from the read thread. + */ + void postReadCleanup() { + if ((readCount.incrementAndGet() & DRAIN_THRESHOLD) == 0) { + runCleanup(); + } + } + + /** + * Performs routine cleanup prior to executing a write. This should be called every time a + * write thread acquires the segment lock, immediately after acquiring the lock. + *

+ *

Post-condition: expireEntries has been run. + */ + void preWriteCleanup() { + runLockedCleanup(); + } + + /** + * Performs routine cleanup following a write. + */ + void postWriteCleanup() { + runUnlockedCleanup(); + } + + void runCleanup() { + runLockedCleanup(); + runUnlockedCleanup(); + } + + void runLockedCleanup() { + if (tryLock()) { + try { + drainReferenceQueues(); + expireEntries(); // calls drainRecencyQueue + readCount.set(0); + } finally { + unlock(); + } + } + } + + void runUnlockedCleanup() { + // locked cleanup may generate notifications we can send unlocked + if (!isHeldByCurrentThread()) { + map.processPendingNotifications(); + } + } + + } + + /** + * A custom queue for managing eviction order. Note that this is tightly integrated with {@code + * ReferenceEntry}, upon which it relies to perform its linking. + *

+ *

Note that this entire implementation makes the assumption that all elements which are in + * the map are also in this queue, and that all elements not in the queue are not in the map. + *

+ *

The benefits of creating our own queue are that (1) we can replace elements in the middle + * of the queue as part of copyEvictableEntry, and (2) the contains method is highly optimized + * for the current model. + */ + static final class EvictionQueue extends AbstractQueue> { + final ReferenceEntry head = new AbstractReferenceEntry() { + + ReferenceEntry nextEvictable = this; + ReferenceEntry previousEvictable = this; + + @Override + public ReferenceEntry getNextEvictable() { + return nextEvictable; + } + + @Override + public void setNextEvictable(ReferenceEntry next) { + this.nextEvictable = next; + } + + @Override + public ReferenceEntry getPreviousEvictable() { + return previousEvictable; + } + + @Override + public void setPreviousEvictable(ReferenceEntry previous) { + this.previousEvictable = previous; + } + }; + + // implements Queue + + @Override + public boolean offer(ReferenceEntry entry) { + // unlink + connectEvictables(entry.getPreviousEvictable(), entry.getNextEvictable()); + + // add to tail + connectEvictables(head.getPreviousEvictable(), entry); + connectEvictables(entry, head); + + return true; + } + + @Override + public ReferenceEntry peek() { + ReferenceEntry next = head.getNextEvictable(); + return (next == head) ? null : next; + } + + @Override + public ReferenceEntry poll() { + ReferenceEntry next = head.getNextEvictable(); + if (next == head) { + return null; + } + + remove(next); + return next; + } + + @Override + @SuppressWarnings("unchecked") + public boolean remove(Object o) { + ReferenceEntry e = (ReferenceEntry) o; + ReferenceEntry previous = e.getPreviousEvictable(); + ReferenceEntry next = e.getNextEvictable(); + connectEvictables(previous, next); + nullifyEvictable(e); + + return next != NullEntry.INSTANCE; + } + + @Override + @SuppressWarnings("unchecked") + public boolean contains(Object o) { + ReferenceEntry e = (ReferenceEntry) o; + return e.getNextEvictable() != NullEntry.INSTANCE; + } + + @Override + public boolean isEmpty() { + return head.getNextEvictable() == head; + } + + @Override + public int size() { + int size = 0; + for (ReferenceEntry e = head.getNextEvictable(); e != head; e = e.getNextEvictable()) { + size++; + } + return size; + } + + @Override + public void clear() { + ReferenceEntry e = head.getNextEvictable(); + while (e != head) { + ReferenceEntry next = e.getNextEvictable(); + nullifyEvictable(e); + e = next; + } + + head.setNextEvictable(head); + head.setPreviousEvictable(head); + } + + @Override + public Iterator> iterator() { + return new AbstractSequentialIterator>(peek()) { + @Override + protected ReferenceEntry computeNext(ReferenceEntry previous) { + ReferenceEntry next = previous.getNextEvictable(); + return (next == head) ? null : next; + } + }; + } + } + + // Iterator Support + + /** + * A custom queue for managing expiration order. Note that this is tightly integrated with + * {@code ReferenceEntry}, upon which it reliese to perform its linking. + *

+ *

Note that this entire implementation makes the assumption that all elements which are in + * the map are also in this queue, and that all elements not in the queue are not in the map. + *

+ *

The benefits of creating our own queue are that (1) we can replace elements in the middle + * of the queue as part of copyEvictableEntry, and (2) the contains method is highly optimized + * for the current model. + */ + static final class ExpirationQueue extends AbstractQueue> { + final ReferenceEntry head = new AbstractReferenceEntry() { + + ReferenceEntry nextExpirable = this; + ReferenceEntry previousExpirable = this; + + @Override + public long getExpirationTime() { + return Long.MAX_VALUE; + } + + @Override + public void setExpirationTime(long time) { + } + + @Override + public ReferenceEntry getNextExpirable() { + return nextExpirable; + } + + @Override + public void setNextExpirable(ReferenceEntry next) { + this.nextExpirable = next; + } + + @Override + public ReferenceEntry getPreviousExpirable() { + return previousExpirable; + } + + @Override + public void setPreviousExpirable(ReferenceEntry previous) { + this.previousExpirable = previous; + } + }; + + // implements Queue + + @Override + public boolean offer(ReferenceEntry entry) { + // unlink + connectExpirables(entry.getPreviousExpirable(), entry.getNextExpirable()); + + // add to tail + connectExpirables(head.getPreviousExpirable(), entry); + connectExpirables(entry, head); + + return true; + } + + @Override + public ReferenceEntry peek() { + ReferenceEntry next = head.getNextExpirable(); + return (next == head) ? null : next; + } + + @Override + public ReferenceEntry poll() { + ReferenceEntry next = head.getNextExpirable(); + if (next == head) { + return null; + } + + remove(next); + return next; + } + + @Override + @SuppressWarnings("unchecked") + public boolean remove(Object o) { + ReferenceEntry e = (ReferenceEntry) o; + ReferenceEntry previous = e.getPreviousExpirable(); + ReferenceEntry next = e.getNextExpirable(); + connectExpirables(previous, next); + nullifyExpirable(e); + + return next != NullEntry.INSTANCE; + } + + @Override + @SuppressWarnings("unchecked") + public boolean contains(Object o) { + ReferenceEntry e = (ReferenceEntry) o; + return e.getNextExpirable() != NullEntry.INSTANCE; + } + + @Override + public boolean isEmpty() { + return head.getNextExpirable() == head; + } + + @Override + public int size() { + int size = 0; + for (ReferenceEntry e = head.getNextExpirable(); e != head; e = e.getNextExpirable()) { + size++; + } + return size; + } + + @Override + public void clear() { + ReferenceEntry e = head.getNextExpirable(); + while (e != head) { + ReferenceEntry next = e.getNextExpirable(); + nullifyExpirable(e); + e = next; + } + + head.setNextExpirable(head); + head.setPreviousExpirable(head); + } + + @Override + public Iterator> iterator() { + return new AbstractSequentialIterator>(peek()) { + @Override + protected ReferenceEntry computeNext(ReferenceEntry previous) { + ReferenceEntry next = previous.getNextExpirable(); + return (next == head) ? null : next; + } + }; + } + } + + abstract class HashIterator implements Iterator { + + int nextSegmentIndex; + int nextTableIndex; + Segment currentSegment; + AtomicReferenceArray> currentTable; + ReferenceEntry nextEntry; + WriteThroughEntry nextExternal; + WriteThroughEntry lastReturned; + + HashIterator() { + nextSegmentIndex = segments.length - 1; + nextTableIndex = -1; + advance(); + } + + @Override + public abstract E next(); + + final void advance() { + nextExternal = null; + + if (nextInChain()) { + return; + } + + if (nextInTable()) { + return; + } + + while (nextSegmentIndex >= 0) { + currentSegment = segments[nextSegmentIndex--]; + if (currentSegment.count != 0) { + currentTable = currentSegment.table; + nextTableIndex = currentTable.length() - 1; + if (nextInTable()) { + return; + } + } + } + } + + /** + * Finds the next entry in the current chain. Returns {@code true} if an entry was found. + */ + boolean nextInChain() { + if (nextEntry != null) { + for (nextEntry = nextEntry.getNext(); nextEntry != null; nextEntry = nextEntry.getNext()) { + if (advanceTo(nextEntry)) { + return true; + } + } + } + return false; + } + + /** + * Finds the next entry in the current table. Returns {@code true} if an entry was found. + */ + boolean nextInTable() { + while (nextTableIndex >= 0) { + if ((nextEntry = currentTable.get(nextTableIndex--)) != null) { + if (advanceTo(nextEntry) || nextInChain()) { + return true; + } + } + } + return false; + } + + /** + * Advances to the given entry. Returns {@code true} if the entry was valid, {@code false} if it + * should be skipped. + */ + boolean advanceTo(ReferenceEntry entry) { + try { + K key = entry.getKey(); + V value = getLiveValue(entry); + if (value != null) { + nextExternal = new WriteThroughEntry(key, value); + return true; + } else { + // Skip stale entry. + return false; + } + } finally { + currentSegment.postReadCleanup(); + } + } + + @Override + public boolean hasNext() { + return nextExternal != null; + } + + WriteThroughEntry nextEntry() { + if (nextExternal == null) { + throw new NoSuchElementException(); + } + lastReturned = nextExternal; + advance(); + return lastReturned; + } + + @Override + public void remove() { + CollectPreconditions.checkRemove(lastReturned != null); + MapMakerInternalMap.this.remove(lastReturned.getKey()); + lastReturned = null; + } + } + + final class KeyIterator extends HashIterator { + + @Override + public K next() { + return nextEntry().getKey(); + } + } + + final class ValueIterator extends HashIterator { + + @Override + public V next() { + return nextEntry().getValue(); + } + } + + /** + * Custom Entry class used by EntryIterator.next(), that relays setValue changes to the + * underlying map. + */ + final class WriteThroughEntry extends AbstractMapEntry { + final K key; // non-null + V value; // non-null + + WriteThroughEntry(K key, V value) { + this.key = key; + this.value = value; + } + + @Override + public K getKey() { + return key; + } + + @Override + public V getValue() { + return value; + } + + @Override + public boolean equals(Object object) { + // Cannot use key and value equivalence + if (object instanceof Entry) { + Entry that = (Entry) object; + return key.equals(that.getKey()) && value.equals(that.getValue()); + } + return false; + } + + @Override + public int hashCode() { + // Cannot use key and value equivalence + return key.hashCode() ^ value.hashCode(); + } + + @Override + public V setValue(V newValue) { + V oldValue = put(key, newValue); + value = newValue; // only if put succeeds + return oldValue; + } + } + + final class EntryIterator extends HashIterator> { + + @Override + public Entry next() { + return nextEntry(); + } + } + + private final class KeySet extends AbstractSet { + + @Override + public Iterator iterator() { + return new KeyIterator(); + } + + @Override + public int size() { + return MapMakerInternalMap.this.size(); + } + + @Override + public boolean isEmpty() { + return MapMakerInternalMap.this.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return MapMakerInternalMap.this.containsKey(o); + } + + @Override + public boolean remove(Object o) { + return MapMakerInternalMap.this.remove(o) != null; + } + + @Override + public void clear() { + MapMakerInternalMap.this.clear(); + } + } + + private final class Values extends AbstractCollection { + + @Override + public Iterator iterator() { + return new ValueIterator(); + } + + @Override + public int size() { + return MapMakerInternalMap.this.size(); + } + + @Override + public boolean isEmpty() { + return MapMakerInternalMap.this.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return MapMakerInternalMap.this.containsValue(o); + } + + @Override + public void clear() { + MapMakerInternalMap.this.clear(); + } + } + + // Serialization Support + + private final class EntrySet extends AbstractSet> { + + @Override + public Iterator> iterator() { + return new EntryIterator(); + } + + @Override + public boolean contains(Object o) { + if (!(o instanceof Entry)) { + return false; + } + Entry e = (Entry) o; + Object key = e.getKey(); + if (key == null) { + return false; + } + V v = MapMakerInternalMap.this.get(key); + + return v != null && valueEquivalence.equivalent(e.getValue(), v); + } + + @Override + public boolean remove(Object o) { + if (!(o instanceof Entry)) { + return false; + } + Entry e = (Entry) o; + Object key = e.getKey(); + return key != null && MapMakerInternalMap.this.remove(key, e.getValue()); + } + + @Override + public int size() { + return MapMakerInternalMap.this.size(); + } + + @Override + public boolean isEmpty() { + return MapMakerInternalMap.this.isEmpty(); + } + + @Override + public void clear() { + MapMakerInternalMap.this.clear(); + } + } + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/Maps.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Maps.java new file mode 100644 index 0000000000..f2f6ed22e2 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Maps.java @@ -0,0 +1,579 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.AbstractCollection; +import java.util.AbstractMap; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.function.Function; +import java.util.function.Predicate; + +import org.glassfish.jersey.internal.guava.Joiner.MapJoiner; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkArgument; +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; +import static org.glassfish.jersey.internal.guava.Predicates.compose; + +/** + * Static utility methods pertaining to {@link Map} instances (including instances of + * {@link SortedMap}, {@link BiMap}, etc.). Also see this class's counterparts + * {@link Lists}, {@link Sets} and {@link Queues}. + *

+ *

See the Guava User Guide article on + * {@code Maps}. + * + * @author Kevin Bourrillion + * @author Mike Bostock + * @author Isaac Shum + * @author Louis Wasserman + * @since 2.0 (imported from Google Collections Library) + */ +public final class Maps { + private static final MapJoiner STANDARD_JOINER = + Collections2.STANDARD_JOINER.withKeyValueSeparator(); + + private Maps() { + } + + @SuppressWarnings("unchecked") + private static Function, K> keyFunction() { + return (Function) EntryFunction.KEY; + } + + @SuppressWarnings("unchecked") + private static Function, V> valueFunction() { + return (Function) EntryFunction.VALUE; + } + + private static Iterator keyIterator(Iterator> entryIterator) { + return Iterators.transform(entryIterator, Maps.keyFunction()); + } + + static Iterator valueIterator(Iterator> entryIterator) { + return Iterators.transform(entryIterator, Maps.valueFunction()); + } + + /** + * Creates a {@code HashMap} instance, with a high enough "initial capacity" + * that it should hold {@code expectedSize} elements without growth. + * This behavior cannot be broadly guaranteed, but it is observed to be true + * for OpenJDK 1.6. It also can't be guaranteed that the method isn't + * inadvertently oversizing the returned map. + * + * @param expectedSize the number of elements you expect to add to the + * returned map + * @return a new, empty {@code HashMap} with enough capacity to hold {@code + * expectedSize} elements without resizing + * @throws IllegalArgumentException if {@code expectedSize} is negative + */ + public static HashMap newHashMapWithExpectedSize( + int expectedSize) { + return new HashMap(capacity(expectedSize)); + } + + /** + * Returns a capacity that is sufficient to keep the map from being resized as + * long as it grows no larger than expectedSize and the load factor is >= its + * default (0.75). + */ + static int capacity(int expectedSize) { + if (expectedSize < 3) { + CollectPreconditions.checkNonnegative(expectedSize, "expectedSize"); + return expectedSize + 1; + } + if (expectedSize < Ints.MAX_POWER_OF_TWO) { + return expectedSize + expectedSize / 3; + } + return Integer.MAX_VALUE; // any large value + } + + static Iterator> asMapEntryIterator( + Set set, final Function function) { + return new TransformedIterator>(set.iterator()) { + @Override + Entry transform(final K key) { + return immutableEntry(key, function.apply(key)); + } + }; + } + + private static Set removeOnlySet(final Set set) { + return new ForwardingSet() { + @Override + protected Set delegate() { + return set; + } + + @Override + public boolean add(E element) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(Collection es) { + throw new UnsupportedOperationException(); + } + }; + } + + private static SortedSet removeOnlySortedSet(final SortedSet set) { + return new ForwardingSortedSet() { + @Override + protected SortedSet delegate() { + return set; + } + + @Override + public boolean add(E element) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(Collection es) { + throw new UnsupportedOperationException(); + } + + @Override + public SortedSet headSet(E toElement) { + return removeOnlySortedSet(super.headSet(toElement)); + } + + @Override + public SortedSet subSet(E fromElement, E toElement) { + return removeOnlySortedSet(super.subSet(fromElement, toElement)); + } + + @Override + public SortedSet tailSet(E fromElement) { + return removeOnlySortedSet(super.tailSet(fromElement)); + } + }; + } + + /** + * Returns an immutable map entry with the specified key and value. The {@link + * Entry#setValue} operation throws an {@link UnsupportedOperationException}. + *

+ *

The returned entry is serializable. + * + * @param key the key to be associated with the returned entry + * @param value the value to be associated with the returned entry + */ + public static Entry immutableEntry( + K key, V value) { + + + return new ImmutableEntry(key, value); + } + + static Predicate> keyPredicateOnEntries(Predicate keyPredicate) { + return compose(keyPredicate, Maps.keyFunction()); + } + + static Predicate> valuePredicateOnEntries(Predicate valuePredicate) { + return compose(valuePredicate, Maps.valueFunction()); + } + + /** + * Delegates to {@link Map#get}. Returns {@code null} on {@code + * ClassCastException} and {@code NullPointerException}. + */ + static V safeGet(Map map, Object key) { + checkNotNull(map); + try { + return map.get(key); + } catch (ClassCastException e) { + return null; + } catch (NullPointerException e) { + return null; + } + } + + /** + * Delegates to {@link Map#containsKey}. Returns {@code false} on {@code + * ClassCastException} and {@code NullPointerException}. + */ + static boolean safeContainsKey(Map map, Object key) { + checkNotNull(map); + try { + return map.containsKey(key); + } catch (ClassCastException e) { + return false; + } catch (NullPointerException e) { + return false; + } + } + + /** + * Delegates to {@link Map#remove}. Returns {@code null} on {@code + * ClassCastException} and {@code NullPointerException}. + */ + static V safeRemove(Map map, Object key) { + checkNotNull(map); + try { + return map.remove(key); + } catch (ClassCastException e) { + return null; + } catch (NullPointerException e) { + return null; + } + } + + private enum EntryFunction implements Function, Object> { + KEY { + @Override + public Object apply(Entry entry) { + return entry.getKey(); + } + }, + VALUE { + @Override + public Object apply(Entry entry) { + return entry.getValue(); + } + } + } + + private static class AsMapView extends ImprovedAbstractMap { + + final Function function; + private final Set set; + + AsMapView(Set set, Function function) { + this.set = checkNotNull(set); + this.function = checkNotNull(function); + } + + Set backingSet() { + return set; + } + + @Override + public Set createKeySet() { + return removeOnlySet(backingSet()); + } + + @Override + Collection createValues() { + return Collections2.transform(set, function); + } + + @Override + public int size() { + return backingSet().size(); + } + + @Override + public boolean containsKey(Object key) { + return backingSet().contains(key); + } + + @Override + public V get(Object key) { + if (Collections2.safeContains(backingSet(), key)) { + @SuppressWarnings("unchecked") // unsafe, but Javadoc warns about it + K k = (K) key; + return function.apply(k); + } else { + return null; + } + } + + @Override + public V remove(Object key) { + if (backingSet().remove(key)) { + @SuppressWarnings("unchecked") // unsafe, but Javadoc warns about it + K k = (K) key; + return function.apply(k); + } else { + return null; + } + } + + @Override + public void clear() { + backingSet().clear(); + } + + @Override + protected Set> createEntrySet() { + return new EntrySet() { + @Override + Map map() { + return Maps.AsMapView.this; + } + + @Override + public Iterator> iterator() { + return asMapEntryIterator(backingSet(), function); + } + }; + } + } + + /** + * {@code AbstractMap} extension that implements {@link #isEmpty()} as {@code + * entrySet().isEmpty()} instead of {@code size() == 0} to speed up + * implementations where {@code size()} is O(n), and it delegates the {@code + * isEmpty()} methods of its key set and value collection to this + * implementation. + */ + abstract static class ImprovedAbstractMap extends AbstractMap { + private transient Set> entrySet; + private transient Set keySet; + private transient Collection values; + + /** + * Creates the entry set to be returned by {@link #entrySet()}. This method + * is invoked at most once on a given map, at the time when {@code entrySet} + * is first called. + */ + abstract Set> createEntrySet(); + + @Override + public Set> entrySet() { + Set> result = entrySet; + return (result == null) ? entrySet = createEntrySet() : result; + } + + @Override + public Set keySet() { + Set result = keySet; + return (result == null) ? keySet = createKeySet() : result; + } + + Set createKeySet() { + return new KeySet(this); + } + + @Override + public Collection values() { + Collection result = values; + return (result == null) ? values = createValues() : result; + } + + Collection createValues() { + return new Values(this); + } + } + + static class KeySet extends Sets.ImprovedAbstractSet { + final Map map; + + KeySet(Map map) { + this.map = checkNotNull(map); + } + + Map map() { + return map; + } + + @Override + public Iterator iterator() { + return keyIterator(map().entrySet().iterator()); + } + + @Override + public int size() { + return map().size(); + } + + @Override + public boolean isEmpty() { + return map().isEmpty(); + } + + @Override + public boolean contains(Object o) { + return map().containsKey(o); + } + + @Override + public boolean remove(Object o) { + if (contains(o)) { + map().remove(o); + return true; + } + return false; + } + + @Override + public void clear() { + map().clear(); + } + } + + static class Values extends AbstractCollection { + final Map map; + + Values(Map map) { + this.map = checkNotNull(map); + } + + final Map map() { + return map; + } + + @Override + public Iterator iterator() { + return valueIterator(map().entrySet().iterator()); + } + + @Override + public boolean remove(Object o) { + try { + return super.remove(o); + } catch (UnsupportedOperationException e) { + for (Entry entry : map().entrySet()) { + if (Objects.equals(o, entry.getValue())) { + map().remove(entry.getKey()); + return true; + } + } + return false; + } + } + + @Override + public boolean removeAll(Collection c) { + try { + return super.removeAll(checkNotNull(c)); + } catch (UnsupportedOperationException e) { + Set toRemove = Sets.newHashSet(); + for (Entry entry : map().entrySet()) { + if (c.contains(entry.getValue())) { + toRemove.add(entry.getKey()); + } + } + return map().keySet().removeAll(toRemove); + } + } + + @Override + public boolean retainAll(Collection c) { + try { + return super.retainAll(checkNotNull(c)); + } catch (UnsupportedOperationException e) { + Set toRetain = Sets.newHashSet(); + for (Entry entry : map().entrySet()) { + if (c.contains(entry.getValue())) { + toRetain.add(entry.getKey()); + } + } + return map().keySet().retainAll(toRetain); + } + } + + @Override + public int size() { + return map().size(); + } + + @Override + public boolean isEmpty() { + return map().isEmpty(); + } + + @Override + public boolean contains(Object o) { + return map().containsValue(o); + } + + @Override + public void clear() { + map().clear(); + } + } + + abstract static class EntrySet + extends Sets.ImprovedAbstractSet> { + abstract Map map(); + + @Override + public int size() { + return map().size(); + } + + @Override + public void clear() { + map().clear(); + } + + @Override + public boolean contains(Object o) { + if (o instanceof Entry) { + Entry entry = (Entry) o; + Object key = entry.getKey(); + V value = Maps.safeGet(map(), key); + return Objects.equals(value, entry.getValue()) + && (value != null || map().containsKey(key)); + } + return false; + } + + @Override + public boolean isEmpty() { + return map().isEmpty(); + } + + @Override + public boolean remove(Object o) { + if (contains(o)) { + Entry entry = (Entry) o; + return map().keySet().remove(entry.getKey()); + } + return false; + } + + @Override + public boolean removeAll(Collection c) { + try { + return super.removeAll(checkNotNull(c)); + } catch (UnsupportedOperationException e) { + // if the iterators don't support remove + return Sets.removeAllImpl(this, c.iterator()); + } + } + + @Override + public boolean retainAll(Collection c) { + try { + return super.retainAll(checkNotNull(c)); + } catch (UnsupportedOperationException e) { + // if the iterators don't support remove + Set keys = Sets.newHashSetWithExpectedSize(c.size()); + for (Object o : c) { + if (contains(o)) { + Entry entry = (Entry) o; + keys.add(entry.getKey()); + } + } + return map().keySet().retainAll(keys); + } + } + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/MoreExecutors.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/MoreExecutors.java new file mode 100644 index 0000000000..74905754ad --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/MoreExecutors.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy; + +/** + * Factory and utility methods for {@link Executor}, {@link + * ExecutorService}, and {@link ThreadFactory}. + * + * @author Eric Fellheimer + * @author Kyle Littlefield + * @author Justin Mahoney + * @since 3.0 + */ +public final class MoreExecutors { + private MoreExecutors() { + } + + /** + * Returns an {@link Executor} that runs each task in the thread that invokes + * {@link Executor#execute execute}, as in {@link CallerRunsPolicy}. + *

+ *

This instance is equivalent to:

   {@code
+     *   final class DirectExecutor implements Executor {
+     *     public void execute(Runnable r) {
+     *       r.run();
+     *     }
+     *   }}
+ *

+ *

This should be preferred to {@link #newDirectExecutorService()} because the implementing the + * {@link ExecutorService} subinterface necessitates significant performance overhead. + * + * @since 18.0 + */ + public static Executor directExecutor() { + return DirectExecutor.INSTANCE; + } + + /** + * See {@link #directExecutor} for behavioral notes. + */ + private enum DirectExecutor implements Executor { + INSTANCE; + + @Override + public void execute(Runnable command) { + command.run(); + } + } + + /* + * This following method is a modified version of one found in + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/test/tck/AbstractExecutorServiceTest.java?revision=1.30 + * which contained the following notice: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/MoreObjects.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/MoreObjects.java new file mode 100644 index 0000000000..1ccaaefb98 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/MoreObjects.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2014 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; + +/** + * Helper functions that operate on any {@code Object}, and are not already provided in + * {@link java.util.Objects}. + *

+ *

See the Guava User Guide on writing + * {@code Object} methods with {@code MoreObjects}. + * + * @author Laurence Gonsalves + * @since 18.0 (since 2.0 as {@code Objects}) + */ +public final class MoreObjects { + private MoreObjects() { + } + + /** + * Creates an instance of {@link ToStringHelper}. + *

+ *

This is helpful for implementing {@link Object#toString()}. + * Specification by example:

   {@code
+     *   // Returns "ClassName{}"
+     *   MoreObjects.toStringHelper(this)
+     *       .toString();
+     * 

+ * // Returns "ClassName{x=1}" + * MoreObjects.toStringHelper(this) + * .add("x", 1) + * .toString(); + *

+ * // Returns "MyObject{x=1}" + * MoreObjects.toStringHelper("MyObject") + * .add("x", 1) + * .toString(); + *

+ * // Returns "ClassName{x=1, y=foo}" + * MoreObjects.toStringHelper(this) + * .add("x", 1) + * .add("y", "foo") + * .toString(); + *

+ * // Returns "ClassName{x=1}" + * MoreObjects.toStringHelper(this) + * .omitNullValues() + * .add("x", 1) + * .add("y", null) + * .toString(); + * }}

+ *

+ *

Note that in GWT, class names are often obfuscated. + * + * @param self the object to generate the string for (typically {@code this}), used only for its + * class name + * @since 18.0 (since 2.0 as {@code Objects.toStringHelper()}. + */ + public static ToStringHelper toStringHelper(Object self) { + return new ToStringHelper(simpleName(self.getClass())); + } + + /** + * {@link Class#getSimpleName()} is not GWT compatible yet, so we + * provide our own implementation. + */ + // Package-private so Objects can call it. + private static String simpleName(Class clazz) { + String name = clazz.getName(); + + // the nth anonymous class has a class name ending in "Outer$n" + // and local inner classes have names ending in "Outer.$1Inner" + name = name.replaceAll("\\$[0-9]+", "\\$"); + + // we want the name of the inner class all by its lonesome + int start = name.lastIndexOf('$'); + + // if this isn't an inner class, just find the start of the + // top level class name. + if (start == -1) { + start = name.lastIndexOf('.'); + } + return name.substring(start + 1); + } + + /** + * Support class for {@link MoreObjects#toStringHelper}. + * + * @author Jason Lee + * @since 18.0 (since 2.0 as {@code Objects.ToStringHelper}. + */ + public static final class ToStringHelper { + private final String className; + private final ValueHolder holderHead = new ValueHolder(); + private ValueHolder holderTail = holderHead; + private final boolean omitNullValues = false; + + /** + * Use {@link MoreObjects#toStringHelper(Object)} to create an instance. + */ + private ToStringHelper(String className) { + this.className = Preconditions.checkNotNull(className); + } + + /** + * Adds a name/value pair to the formatted output in {@code name=value} + * format. If {@code value} is {@code null}, the string {@code "null"} + * is used, unless {@link #omitNullValues()} is called, in which case this + * name/value pair will not be added. + */ + public ToStringHelper add(String name, Object value) { + return addHolder(name, value); + } + + /** + * Adds a name/value pair to the formatted output in {@code name=value} + * format. + * + * @since 18.0 (since 11.0 as {@code Objects.ToStringHelper.omitNullValues()}. + */ + public ToStringHelper add(String name, int value) { + return addHolder(name, String.valueOf(value)); + } + + /** + * Adds a name/value pair to the formatted output in {@code name=value} + * format. + * + * @since 18.0 (since 11.0 as {@code Objects.ToStringHelper.omitNullValues()}. + */ + public ToStringHelper add(String name, long value) { + return addHolder(name, String.valueOf(value)); + } + + /** + * Returns a string in the format specified by + * {@link MoreObjects#toStringHelper(Object)}. + *

+ *

After calling this method, you can keep adding more properties to later + * call toString() again and get a more complete representation of the + * same object; but properties cannot be removed, so this only allows + * limited reuse of the helper instance. The helper allows duplication of + * properties (multiple name/value pairs with the same name can be added). + */ + @Override + public String toString() { + // create a copy to keep it consistent in case value changes + String nextSeparator = ""; + StringBuilder builder = new StringBuilder(32).append(className) + .append('{'); + for (ValueHolder valueHolder = holderHead.next; valueHolder != null; + valueHolder = valueHolder.next) { + if (!omitNullValues || valueHolder.value != null) { + builder.append(nextSeparator); + nextSeparator = ", "; + + if (valueHolder.name != null) { + builder.append(valueHolder.name).append('='); + } + builder.append(valueHolder.value); + } + } + return builder.append('}').toString(); + } + + private ValueHolder addHolder() { + ValueHolder valueHolder = new ValueHolder(); + holderTail = holderTail.next = valueHolder; + return valueHolder; + } + + private ToStringHelper addHolder(String name, Object value) { + ValueHolder valueHolder = addHolder(); + valueHolder.value = value; + valueHolder.name = Preconditions.checkNotNull(name); + return this; + } + + private static final class ValueHolder { + String name; + Object value; + ValueHolder next; + } + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/Multimap.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Multimap.java new file mode 100644 index 0000000000..0cb3c15461 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Multimap.java @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * A collection that maps keys to values, similar to {@link Map}, but in which + * each key may be associated with multiple values. You can visualize the + * contents of a multimap either as a map from keys to nonempty + * collections of values: + *

+ *

    + *
  • a → 1, 2 + *
  • b → 3 + *
+ *

+ * ... or as a single "flattened" collection of key-value pairs: + *

+ *

    + *
  • a → 1 + *
  • a → 2 + *
  • b → 3 + *
+ *

+ *

Important: although the first interpretation resembles how most + * multimaps are implemented, the design of the {@code Multimap} API is + * based on the second form. So, using the multimap shown above as an + * example, the {@link #size} is {@code 3}, not {@code 2}, and the {@link + * #values} collection is {@code [1, 2, 3]}, not {@code [[1, 2], [3]]}. For + * those times when the first style is more useful, use the multimap's {@link + * #asMap} view (or create a {@code Map>} in the first place). + *

+ *

Example

+ *

+ *

The following code:

   {@code
+ * 

+ * ListMultimap multimap = ArrayListMultimap.create(); + * for (President pres : US_PRESIDENTS_IN_ORDER) { + * multimap.put(pres.firstName(), pres.lastName()); + * } + * for (String firstName : multimap.keySet()) { + * List lastNames = multimap.get(firstName); + * out.println(firstName + ": " + lastNames); + * }}

+ *

+ * ... produces output such as:

   {@code
+ * 

+ * Zachary: [Taylor] + * John: [Adams, Adams, Tyler, Kennedy] // Remember, Quincy! + * George: [Washington, Bush, Bush] + * Grover: [Cleveland, Cleveland] // Two, non-consecutive terms, rep'ing NJ! + * ...}

+ *

+ *

Views

+ *

+ *

Much of the power of the multimap API comes from the view + * collections it provides. These always reflect the latest state of the + * multimap itself. When they support modification, the changes are + * write-through (they automatically update the backing multimap). These + * view collections are: + *

+ *

    + *
  • {@link #asMap}, mentioned above
  • + *
  • {@link #keys}, {@link #keySet}, {@link #values}, {@link #entries}, which + * are similar to the corresponding view collections of {@link Map} + *
  • and, notably, even the collection returned by {@link #get get(key)} is an + * active view of the values corresponding to {@code key} + *
+ *

+ *

The collections returned by the {@link #replaceValues replaceValues} and + * {@link #removeAll removeAll} methods, which contain values that have just + * been removed from the multimap, are naturally not views. + *

+ *

Subinterfaces

+ *

+ *

Instead of using the {@code Multimap} interface directly, prefer the + * subinterfaces {@link ListMultimap} and {@link SetMultimap}. These take their + * names from the fact that the collections they return from {@code get} behave + * like (and, of course, implement) {@link List} and {@link Set}, respectively. + *

+ *

For example, the "presidents" code snippet above used a {@code + * ListMultimap}; if it had used a {@code SetMultimap} instead, two presidents + * would have vanished, and last names might or might not appear in + * chronological order. + *

+ *

Warning: instances of type {@code Multimap} may not implement + * {@link Object#equals} in the way you expect. Multimaps containing the same + * key-value pairs, even in the same order, may or may not be equal and may or + * may not have the same {@code hashCode}. The recommended subinterfaces + * provide much stronger guarantees. + *

+ *

Comparison to a map of collections

+ *

+ *

Multimaps are commonly used in places where a {@code Map>} would otherwise have appeared. The differences include: + *

+ *

    + *
  • There is no need to populate an empty collection before adding an entry + * with {@link #put put}. + *
  • {@code get} never returns {@code null}, only an empty collection. + *
  • A key is contained in the multimap if and only if it maps to at least + * one value. Any operation that causes a key to have zero associated + * values has the effect of removing that key from the multimap. + *
  • The total entry count is available as {@link #size}. + *
  • Many complex operations become easier; for example, {@code + * Collections.min(multimap.values())} finds the smallest value across all + * keys. + *
+ *

+ *

Implementations

+ *

+ *

As always, prefer the immutable implementations, {@link + * ImmutableListMultimap} and {@link ImmutableSetMultimap}. General-purpose + * mutable implementations are listed above under "All Known Implementing + * Classes". You can also create a custom multimap, backed by any {@code + * Map} and {@link Collection} types, using the {@link Multimaps#newMultimap + * Multimaps.newMultimap} family of methods. Finally, another popular way to + * obtain a multimap is using {@link Multimaps#index Multimaps.index}. See + * the {@link Multimaps} class for these and other static utilities related + * to multimaps. + *

+ *

Other Notes

+ *

+ *

As with {@code Map}, the behavior of a {@code Multimap} is not specified + * if key objects already present in the multimap change in a manner that + * affects {@code equals} comparisons. Use caution if mutable objects are used + * as keys in a {@code Multimap}. + *

+ *

All methods that modify the multimap are optional. The view collections + * returned by the multimap may or may not be modifiable. Any modification + * method that is not supported will throw {@link + * UnsupportedOperationException}. + *

+ *

See the Guava User Guide article on + * {@code Multimap}. + * + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +public interface Multimap { + // Query Operations + + /** + * Returns the number of key-value pairs in this multimap. + *

+ *

Note: this method does not return the number of distinct + * keys in the multimap, which is given by {@code keySet().size()} or + * {@code asMap().size()}. See the opening section of the {@link Multimap} + * class documentation for clarification. + */ + int size(); + + /** + * Returns {@code true} if this multimap contains at least one key-value pair + * with the key {@code key}. + */ + boolean containsKey(Object key); + + /** + * Returns {@code true} if this multimap contains at least one key-value pair + * with the value {@code value}. + */ + boolean containsValue(Object value); + + /** + * Returns {@code true} if this multimap contains at least one key-value pair + * with the key {@code key} and the value {@code value}. + */ + boolean containsEntry(Object key, Object value); + + // Modification Operations + + /** + * Stores a key-value pair in this multimap. + *

+ *

Some multimap implementations allow duplicate key-value pairs, in which + * case {@code put} always adds a new key-value pair and increases the + * multimap size by 1. Other implementations prohibit duplicates, and storing + * a key-value pair that's already in the multimap has no effect. + * + * @return {@code true} if the method increased the size of the multimap, or + * {@code false} if the multimap already contained the key-value pair and + * doesn't allow duplicates + */ + boolean put(K key, V value); + + /** + * Removes a single key-value pair with the key {@code key} and the value + * {@code value} from this multimap, if such exists. If multiple key-value + * pairs in the multimap fit this description, which one is removed is + * unspecified. + * + * @return {@code true} if the multimap changed + */ + boolean remove(Object key, Object value); + + // Bulk Operations + + /** + * Stores a key-value pair in this multimap for each of {@code values}, all + * using the same key, {@code key}. Equivalent to (but expected to be more + * efficient than):

   {@code
+     * 

+ * for (V value : values) { + * put(key, value); + * }}

+ *

+ *

In particular, this is a no-op if {@code values} is empty. + * + * @return {@code true} if the multimap changed + */ + boolean putAll(K key, Iterable values); + + /** + * Removes all values associated with the key {@code key}. + *

+ *

Once this method returns, {@code key} will not be mapped to any values, + * so it will not appear in {@link #keySet()}, {@link #asMap()}, or any other + * views. + * + * @return the values that were removed (possibly empty). The returned + * collection may be modifiable, but updating it will have no + * effect on the multimap. + */ + Collection removeAll(Object key); + + /** + * Removes all key-value pairs from the multimap, leaving it {@linkplain + * #isEmpty empty}. + */ + void clear(); + + // Views + + /** + * Returns a view collection of the values associated with {@code key} in this + * multimap, if any. Note that when {@code containsKey(key)} is false, this + * returns an empty collection, not {@code null}. + *

+ *

Changes to the returned collection will update the underlying multimap, + * and vice versa. + */ + Collection get(K key); + + /** + * Returns a view collection of all distinct keys contained in this + * multimap. Note that the key set contains a key if and only if this multimap + * maps that key to at least one value. + *

+ *

Changes to the returned set will update the underlying multimap, and + * vice versa. However, adding to the returned set is not possible. + */ + Set keySet(); + + /** + * Returns a view collection containing the value from each key-value + * pair contained in this multimap, without collapsing duplicates (so {@code + * values().size() == size()}). + *

+ *

Changes to the returned collection will update the underlying multimap, + * and vice versa. However, adding to the returned collection is not + * possible. + */ + Collection values(); + + /** + * Returns a view collection of all key-value pairs contained in this + * multimap, as {@link Map.Entry} instances. + *

+ *

Changes to the returned collection or the entries it contains will + * update the underlying multimap, and vice versa. However, adding to + * the returned collection is not possible. + */ + Collection> entries(); + + /** + * Returns a view of this multimap as a {@code Map} from each distinct key + * to the nonempty collection of that key's associated values. Note that + * {@code this.asMap().get(k)} is equivalent to {@code this.get(k)} only when + * {@code k} is a key contained in the multimap; otherwise it returns {@code + * null} as opposed to an empty collection. + *

+ *

Changes to the returned map or the collections that serve as its values + * will update the underlying multimap, and vice versa. The map does not + * support {@code put} or {@code putAll}, nor do its entries support {@link + * Map.Entry#setValue setValue}. + */ + Map> asMap(); + + // Comparison and hashing + + /** + * Compares the specified object with this multimap for equality. Two + * multimaps are equal when their map views, as returned by {@link #asMap}, + * are also equal. + *

+ *

In general, two multimaps with identical key-value mappings may or may + * not be equal, depending on the implementation. For example, two + * {@link SetMultimap} instances with the same key-value mappings are equal, + * but equality of two {@link ListMultimap} instances depends on the ordering + * of the values for each key. + *

+ *

A non-empty {@link SetMultimap} cannot be equal to a non-empty + * {@link ListMultimap}, since their {@link #asMap} views contain unequal + * collections as values. However, any two empty multimaps are equal, because + * they both have empty {@link #asMap} views. + */ + @Override + boolean equals(Object obj); + + /** + * Returns the hash code for this multimap. + *

+ *

The hash code of a multimap is defined as the hash code of the map view, + * as returned by {@link Multimap#asMap}. + *

+ *

In general, two multimaps with identical key-value mappings may or may + * not have the same hash codes, depending on the implementation. For + * example, two {@link SetMultimap} instances with the same key-value + * mappings will have the same {@code hashCode}, but the {@code hashCode} + * of {@link ListMultimap} instances depends on the ordering of the values + * for each key. + */ + @Override + int hashCode(); +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/Multimaps.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Multimaps.java new file mode 100644 index 0000000000..2a1a93a348 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Multimaps.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.function.Supplier; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; + +/** + * Provides static methods acting on or generating a {@code Multimap}. + *

+ *

See the Guava User Guide article on + * {@code Multimaps}. + * + * @author Jared Levy + * @author Robert Konigsberg + * @author Mike Bostock + * @author Louis Wasserman + * @since 2.0 (imported from Google Collections Library) + */ +public final class Multimaps { + private Multimaps() { + } + + /** + * Creates a new {@code ListMultimap} that uses the provided map and factory. + * It can generate a multimap based on arbitrary {@link Map} and {@link List} + * classes. + *

+ *

The {@code factory}-generated and {@code map} classes determine the + * multimap iteration order. They also specify the behavior of the + * {@code equals}, {@code hashCode}, and {@code toString} methods for the + * multimap and its returned views. The multimap's {@code get}, {@code + * removeAll}, and {@code replaceValues} methods return {@code RandomAccess} + * lists if the factory does. However, the multimap's {@code get} method + * returns instances of a different class than does {@code factory.get()}. + *

+ *

The multimap is serializable if {@code map}, {@code factory}, the + * lists generated by {@code factory}, and the multimap contents are all + * serializable. + *

+ *

The multimap is not threadsafe when any concurrent operations update the + * multimap, even if {@code map} and the instances generated by + * {@code factory} are. Concurrent read operations will work correctly. To + * allow concurrent update operations, wrap the multimap with a call to + * {@link #synchronizedListMultimap}. + *

+ *

Call this method only when the simpler methods + * {@link ArrayListMultimap#create()} and {@link LinkedListMultimap#create()} + * won't suffice. + *

+ *

Note: the multimap assumes complete ownership over of {@code map} and + * the lists returned by {@code factory}. Those objects should not be manually + * updated, they should be empty when provided, and they should not use soft, + * weak, or phantom references. + * + * @param map place to store the mapping from each key to its corresponding + * values + * @param factory supplier of new, empty lists that will each hold all values + * for a given key + * @throws IllegalArgumentException if {@code map} is not empty + */ + public static ListMultimap newListMultimap( + Map> map, final Supplier> factory) { + return new CustomListMultimap(map, factory); + } + + static boolean equalsImpl(Multimap multimap, Object object) { + if (object == multimap) { + return true; + } + if (object instanceof Multimap) { + Multimap that = (Multimap) object; + return multimap.asMap().equals(that.asMap()); + } + return false; + } + + private static class CustomListMultimap + extends AbstractListMultimap { + private static final long serialVersionUID = 0; + transient Supplier> factory; + + CustomListMultimap(Map> map, + Supplier> factory) { + super(map); + this.factory = checkNotNull(factory); + } + + @Override + protected List createCollection() { + return factory.get(); + } + + /** + * @serialData the factory and the backing map + */ + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(factory); + stream.writeObject(backingMap()); + } + + @SuppressWarnings("unchecked") // reading data stored by writeObject + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + factory = (Supplier>) stream.readObject(); + Map> map = (Map>) stream.readObject(); + setMap(map); + } + } + + /** + * A skeleton implementation of {@link Multimap#entries()}. + */ + abstract static class Entries extends + AbstractCollection> { + abstract Multimap multimap(); + + @Override + public int size() { + return multimap().size(); + } + + @Override + public boolean contains(Object o) { + if (o instanceof Map.Entry) { + Entry entry = (Entry) o; + return multimap().containsEntry(entry.getKey(), entry.getValue()); + } + return false; + } + + @Override + public boolean remove(Object o) { + if (o instanceof Map.Entry) { + Entry entry = (Entry) o; + return multimap().remove(entry.getKey(), entry.getValue()); + } + return false; + } + + @Override + public void clear() { + multimap().clear(); + } + } + + // TODO(jlevy): Create methods that filter a SortedSetMultimap. +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/NaturalOrdering.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/NaturalOrdering.java new file mode 100644 index 0000000000..53bc358d05 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/NaturalOrdering.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.io.Serializable; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; + +/** + * An ordering that uses the natural order of the values. + */ +@SuppressWarnings("unchecked") // TODO(kevinb): the right way to explain this?? +final class NaturalOrdering + extends Ordering implements Serializable { + static final NaturalOrdering INSTANCE = new NaturalOrdering(); + private static final long serialVersionUID = 0; + + private NaturalOrdering() { + } + + @Override + public int compare(Comparable left, Comparable right) { + checkNotNull(left); // for GWT + checkNotNull(right); + return left.compareTo(right); + } + + @Override + public Ordering reverse() { + return (Ordering) ReverseNaturalOrdering.INSTANCE; + } + + // preserving singleton-ness gives equals()/hashCode() for free + private Object readResolve() { + return INSTANCE; + } + + @Override + public String toString() { + return "Ordering.natural()"; + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/NullsFirstOrdering.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/NullsFirstOrdering.java new file mode 100644 index 0000000000..7cc3cb19a1 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/NullsFirstOrdering.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.io.Serializable; + +/** + * An ordering that treats {@code null} as less than all other values. + */ +final class NullsFirstOrdering extends Ordering implements Serializable { + private static final long serialVersionUID = 0; + private final Ordering ordering; + + NullsFirstOrdering(Ordering ordering) { + this.ordering = ordering; + } + + @Override + public int compare(T left, T right) { + if (left == right) { + return 0; + } + if (left == null) { + return RIGHT_IS_GREATER; + } + if (right == null) { + return LEFT_IS_GREATER; + } + return ordering.compare(left, right); + } + + @Override + public Ordering reverse() { + // ordering.reverse() might be optimized, so let it do its thing + return ordering.reverse().nullsLast(); + } + + @SuppressWarnings("unchecked") // still need the right way to explain this + @Override + public Ordering nullsFirst() { + return (Ordering) this; + } + + @Override + public Ordering nullsLast() { + return ordering.nullsLast(); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof NullsFirstOrdering) { + NullsFirstOrdering that = (NullsFirstOrdering) object; + return this.ordering.equals(that.ordering); + } + return false; + } + + @Override + public int hashCode() { + return ordering.hashCode() ^ 957692532; // meaningless + } + + @Override + public String toString() { + return ordering + ".nullsFirst()"; + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/NullsLastOrdering.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/NullsLastOrdering.java new file mode 100644 index 0000000000..505e1f7259 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/NullsLastOrdering.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.io.Serializable; + +/** + * An ordering that treats {@code null} as greater than all other values. + */ +final class NullsLastOrdering extends Ordering implements Serializable { + private static final long serialVersionUID = 0; + private final Ordering ordering; + + NullsLastOrdering(Ordering ordering) { + this.ordering = ordering; + } + + @Override + public int compare(T left, T right) { + if (left == right) { + return 0; + } + if (left == null) { + return LEFT_IS_GREATER; + } + if (right == null) { + return RIGHT_IS_GREATER; + } + return ordering.compare(left, right); + } + + @Override + public Ordering reverse() { + // ordering.reverse() might be optimized, so let it do its thing + return ordering.reverse().nullsFirst(); + } + + @Override + public Ordering nullsFirst() { + return ordering.nullsFirst(); + } + + @SuppressWarnings("unchecked") // still need the right way to explain this + @Override + public Ordering nullsLast() { + return (Ordering) this; + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof NullsLastOrdering) { + NullsLastOrdering that = (NullsLastOrdering) object; + return this.ordering.equals(that.ordering); + } + return false; + } + + @Override + public int hashCode() { + return ordering.hashCode() ^ -921210296; // meaningless + } + + @Override + public String toString() { + return ordering + ".nullsLast()"; + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/ObjectArrays.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ObjectArrays.java new file mode 100644 index 0000000000..f44a255b9c --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ObjectArrays.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +/** + * Static utility methods pertaining to object arrays. + * + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ +final class ObjectArrays { + static final Object[] EMPTY_ARRAY = new Object[0]; + + private ObjectArrays() { + } + + /** + * Returns a new array of the given length with the same type as a reference + * array. + * + * @param reference any array of the desired type + * @param length the length of the new array + */ + public static T[] newArray(T[] reference, int length) { + return Platform.newArray(reference, length); + } + + /** + * GWT safe version of Arrays.copyOf. + */ + static T[] arraysCopyOf(T[] original, int newLength) { + T[] copy = newArray(original, newLength); + System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); + return copy; + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/Ordering.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Ordering.java new file mode 100644 index 0000000000..a544e36037 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Ordering.java @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.function.Function; + +/** + * A comparator, with additional methods to support common operations. This is + * an "enriched" version of {@code Comparator}, in the same sense that {@link + * FluentIterable} is an enriched {@link Iterable}. + *

+ *

The common ways to get an instance of {@code Ordering} are: + *

+ *

    + *
  • Subclass it and implement {@link #compare} instead of implementing + * {@link Comparator} directly + *
  • Pass a pre-existing {@link Comparator} instance to {@link + * #from(Comparator)} + *
  • Use the natural ordering, {@link Ordering#natural} + *
+ *

+ *

Then you can use the chaining methods to get an altered version of + * that {@code Ordering}, including: + *

+ *

    + *
  • {@link #reverse} + *
  • {@link #compound(Comparator)} + *
  • {@link #onResultOf(Function)} + *
  • {@link #nullsFirst} / {@link #nullsLast} + *
+ *

+ *

Finally, use the resulting {@code Ordering} anywhere a {@link Comparator} + * is required, or use any of its special operations, such as:

+ *

+ *

    + *
  • {@link #immutableSortedCopy} + *
  • {@link #isOrdered} / {@link #isStrictlyOrdered} + *
  • {@link #min} / {@link #max} + *
+ *

+ *

Except as noted, the orderings returned by the factory methods of this + * class are serializable if and only if the provided instances that back them + * are. For example, if {@code ordering} and {@code function} can themselves be + * serialized, then {@code ordering.onResultOf(function)} can as well. + *

+ *

See the Guava User Guide article on + * {@code Ordering}. + * + * @author Jesse Wilson + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ +public abstract class Ordering implements Comparator { + // Natural order + + // Never make these public + static final int LEFT_IS_GREATER = 1; + + // Static factories + static final int RIGHT_IS_GREATER = -1; + + /** + * Constructs a new instance of this class (only invokable by the subclass + * constructor, typically implicit). + */ + Ordering() { + } + + // Instance-based factories (and any static equivalents) + + /** + * Returns a serializable ordering that uses the natural order of the values. + * The ordering throws a {@link NullPointerException} when passed a null + * parameter. + *

+ *

The type specification is {@code }, instead of + * the technically correct {@code >}, to + * support legacy types from before Java 5. + */ + @SuppressWarnings("unchecked") // TODO(kevinb): right way to explain this?? + public static Ordering natural() { + return (Ordering) NaturalOrdering.INSTANCE; + } + + /** + * Returns an ordering based on an existing comparator instance. Note + * that it is unnecessary to create a new anonymous inner class + * implementing {@code Comparator} just to pass it in here. Instead, simply + * subclass {@code Ordering} and implement its {@code compare} method + * directly. + * + * @param comparator the comparator that defines the order + * @return comparator itself if it is already an {@code Ordering}; otherwise + * an ordering that wraps that comparator + */ + public static Ordering from(Comparator comparator) { + return (comparator instanceof Ordering) + ? (Ordering) comparator + : new ComparatorOrdering(comparator); + } + + /** + * Returns the reverse of this ordering; the {@code Ordering} equivalent to + * {@link Collections#reverseOrder(Comparator)}. + */ + // type parameter lets us avoid the extra in statements like: + // Ordering o = Ordering.natural().reverse(); + public Ordering reverse() { + return new ReverseOrdering(this); + } + + /** + * Returns an ordering that treats {@code null} as less than all other values + * and uses {@code this} to compare non-null values. + */ + // type parameter lets us avoid the extra in statements like: + // Ordering o = Ordering.natural().nullsFirst(); + Ordering nullsFirst() { + return new NullsFirstOrdering(this); + } + + /** + * Returns an ordering that treats {@code null} as greater than all other + * values and uses this ordering to compare non-null values. + */ + // type parameter lets us avoid the extra in statements like: + // Ordering o = Ordering.natural().nullsLast(); + Ordering nullsLast() { + return new NullsLastOrdering(this); + } + + // Regular instance methods + + // Override to add @Nullable + @Override + public abstract int compare(T left, T right); + + /** + * Returns the least of the specified values according to this ordering. If + * there are multiple least values, the first of those is returned. The + * iterator will be left exhausted: its {@code hasNext()} method will return + * {@code false}. + * + * @param iterator the iterator whose minimum element is to be determined + * @throws NoSuchElementException if {@code iterator} is empty + * @throws ClassCastException if the parameters are not mutually + * comparable under this ordering. + * @since 11.0 + */ + E min(Iterator iterator) { + // let this throw NoSuchElementException as necessary + E minSoFar = iterator.next(); + + while (iterator.hasNext()) { + minSoFar = min(minSoFar, iterator.next()); + } + + return minSoFar; + } + + /** + * Returns the least of the specified values according to this ordering. If + * there are multiple least values, the first of those is returned. + * + * @param iterable the iterable whose minimum element is to be determined + * @throws NoSuchElementException if {@code iterable} is empty + * @throws ClassCastException if the parameters are not mutually + * comparable under this ordering. + */ + E min(Iterable iterable) { + return min(iterable.iterator()); + } + + /** + * Returns the lesser of the two values according to this ordering. If the + * values compare as 0, the first is returned. + *

+ *

Implementation note: this method is invoked by the default + * implementations of the other {@code min} overloads, so overriding it will + * affect their behavior. + * + * @param a value to compare, returned if less than or equal to b. + * @param b value to compare. + * @throws ClassCastException if the parameters are not mutually + * comparable under this ordering. + */ + E min(E a, E b) { + return (compare(a, b) <= 0) ? a : b; + } + + /** + * Returns the least of the specified values according to this ordering. If + * there are multiple least values, the first of those is returned. + * + * @param a value to compare, returned if less than or equal to the rest. + * @param b value to compare + * @param c value to compare + * @param rest values to compare + * @throws ClassCastException if the parameters are not mutually + * comparable under this ordering. + */ + E min( + E a, E b, E c, E... rest) { + E minSoFar = min(min(a, b), c); + + for (E r : rest) { + minSoFar = min(minSoFar, r); + } + + return minSoFar; + } + + /** + * Returns the greatest of the specified values according to this ordering. If + * there are multiple greatest values, the first of those is returned. The + * iterator will be left exhausted: its {@code hasNext()} method will return + * {@code false}. + * + * @param iterator the iterator whose maximum element is to be determined + * @throws NoSuchElementException if {@code iterator} is empty + * @throws ClassCastException if the parameters are not mutually + * comparable under this ordering. + * @since 11.0 + */ + E max(Iterator iterator) { + // let this throw NoSuchElementException as necessary + E maxSoFar = iterator.next(); + + while (iterator.hasNext()) { + maxSoFar = max(maxSoFar, iterator.next()); + } + + return maxSoFar; + } + + /** + * Returns the greatest of the specified values according to this ordering. If + * there are multiple greatest values, the first of those is returned. + * + * @param iterable the iterable whose maximum element is to be determined + * @throws NoSuchElementException if {@code iterable} is empty + * @throws ClassCastException if the parameters are not mutually + * comparable under this ordering. + */ + E max(Iterable iterable) { + return max(iterable.iterator()); + } + + /** + * Returns the greater of the two values according to this ordering. If the + * values compare as 0, the first is returned. + *

+ *

Implementation note: this method is invoked by the default + * implementations of the other {@code max} overloads, so overriding it will + * affect their behavior. + * + * @param a value to compare, returned if greater than or equal to b. + * @param b value to compare. + * @throws ClassCastException if the parameters are not mutually + * comparable under this ordering. + */ + E max(E a, E b) { + return (compare(a, b) >= 0) ? a : b; + } + + /** + * Returns the greatest of the specified values according to this ordering. If + * there are multiple greatest values, the first of those is returned. + * + * @param a value to compare, returned if greater than or equal to the rest. + * @param b value to compare + * @param c value to compare + * @param rest values to compare + * @throws ClassCastException if the parameters are not mutually + * comparable under this ordering. + */ + E max( + E a, E b, E c, E... rest) { + E maxSoFar = max(max(a, b), c); + + for (E r : rest) { + maxSoFar = max(maxSoFar, r); + } + + return maxSoFar; + } + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/PeekingIterator.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/PeekingIterator.java new file mode 100644 index 0000000000..b61a611610 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/PeekingIterator.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * An iterator that supports a one-element lookahead while iterating. + *

+ *

See the Guava User Guide article on + * {@code PeekingIterator}. + * + * @author Mick Killianey + * @since 2.0 (imported from Google Collections Library) + */ +public interface PeekingIterator extends Iterator { + /** + * Returns the next element in the iteration, without advancing the iteration. + *

+ *

Calls to {@code peek()} should not change the state of the iteration, + * except that it may prevent removal of the most recent element via + * {@link #remove()}. + * + * @throws NoSuchElementException if the iteration has no more elements + * according to {@link #hasNext()} + */ + E peek(); + + /** + * {@inheritDoc} + *

+ *

The objects returned by consecutive calls to {@link #peek()} then {@link + * #next()} are guaranteed to be equal to each other. + */ + @Override + E next(); + + /** + * {@inheritDoc} + *

+ *

Implementations may or may not support removal when a call to {@link + * #peek()} has occurred since the most recent call to {@link #next()}. + * + * @throws IllegalStateException if there has been a call to {@link #peek()} + * since the most recent call to {@link #next()} and this implementation + * does not support this sequence of calls (optional) + */ + @Override + void remove(); +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/Platform.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Platform.java new file mode 100644 index 0000000000..e67167239c --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Platform.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.lang.reflect.Array; +import java.util.NavigableSet; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.function.Function; + +/** + * Methods factored out so that they can be emulated differently in GWT. + * + * @author Jesse Wilson + */ +final class Platform { + private Platform() { + } + + /** + * Calls {@link System#nanoTime()}. + */ + static long systemNanoTime() { + return System.nanoTime(); + } + + /** + * Returns a new array of the given length with the same type as a reference + * array. + * + * @param reference any array of the desired type + * @param length the length of the new array + */ + static T[] newArray(T[] reference, int length) { + Class type = reference.getClass().getComponentType(); + + // the cast is safe because + // result.getClass() == reference.getClass().getComponentType() + @SuppressWarnings("unchecked") + T[] result = (T[]) Array.newInstance(type, length); + return result; + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/Preconditions.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Preconditions.java new file mode 100644 index 0000000000..398ad36264 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Preconditions.java @@ -0,0 +1,436 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +/** + * Static convenience methods that help a method or constructor check whether it was invoked + * correctly (whether its preconditions have been met). These methods generally accept a + * {@code boolean} expression which is expected to be {@code true} (or in the case of {@code + * checkNotNull}, an object reference which is expected to be non-null). When {@code false} (or + * {@code null}) is passed instead, the {@code Preconditions} method throws an unchecked exception, + * which helps the calling method communicate to its caller that that caller has made + * a mistake. Example:

   {@code
+ * 

+ * /** + * * Returns the positive square root of the given value. + * * + * * @throws IllegalArgumentException if the value is negative + * *}{@code / + * public static double sqrt(double value) { + * Preconditions.checkArgument(value >= 0.0, "negative value: %s", value); + * // calculate the square root + * } + *

+ * void exampleBadCaller() { + * double d = sqrt(-1.0); + * }}

+ *

+ * In this example, {@code checkArgument} throws an {@code IllegalArgumentException} to indicate + * that {@code exampleBadCaller} made an error in its call to {@code sqrt}. + *

+ *

Warning about performance

+ *

+ *

The goal of this class is to improve readability of code, but in some circumstances this may + * come at a significant performance cost. Remember that parameter values for message construction + * must all be computed eagerly, and autoboxing and varargs array creation may happen as well, even + * when the precondition check then succeeds (as it should almost always do in production). In some + * circumstances these wasted CPU cycles and allocations can add up to a real problem. + * Performance-sensitive precondition checks can always be converted to the customary form: + *

   {@code
+ *
+ *   if (value < 0.0) {
+ *     throw new IllegalArgumentException("negative value: " + value);
+ *   }}
+ *

+ *

Other types of preconditions

+ *

+ *

Not every type of precondition failure is supported by these methods. Continue to throw + * standard JDK exceptions such as {@link java.util.NoSuchElementException} or {@link + * UnsupportedOperationException} in the situations they are intended for. + *

+ *

Non-preconditions

+ *

+ *

It is of course possible to use the methods of this class to check for invalid conditions + * which are not the caller's fault. Doing so is not recommended because it is + * misleading to future readers of the code and of stack traces. See + * Conditional + * failures explained in the Guava User Guide for more advice. + *

+ *

{@code java.util.Objects.requireNonNull()}

+ *

+ *

Projects which use {@code org.glassfish.jersey.internal.guava.common} should generally avoid the use of {@link + * java.util.Objects#requireNonNull(Object)}. Instead, use whichever of {@link + * #checkNotNull(Object)} or {@link Verify#verifyNotNull(Object)} is appropriate to the situation. + * (The same goes for the message-accepting overloads.) + *

+ *

Only {@code %s} is supported

+ *

+ *

In {@code Preconditions} error message template strings, only the {@code "%s"} specifier is + * supported, not the full range of {@link java.util.Formatter} specifiers. + *

+ *

More information

+ *

+ *

See the Guava User Guide on + * using {@code + * Preconditions}. + * + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ +public final class Preconditions { + private Preconditions() { + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + * @param expression a boolean expression + * @throws IllegalArgumentException if {@code expression} is false + */ + public static void checkArgument(boolean expression) { + if (!expression) { + throw new IllegalArgumentException(); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + * @param expression a boolean expression + * @param errorMessage the exception message to use if the check fails; will be converted to a + * string using {@link String#valueOf(Object)} + * @throws IllegalArgumentException if {@code expression} is false + */ + public static void checkArgument(boolean expression, Object errorMessage) { + if (!expression) { + throw new IllegalArgumentException(String.valueOf(errorMessage)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + * @param expression a boolean expression + * @param errorMessageTemplate a template for the exception message should the check fail. The + * message is formed by replacing each {@code %s} placeholder in the template with an + * argument. These are matched by position - the first {@code %s} gets {@code + * errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message + * in square braces. Unmatched placeholders will be left as-is. + * @param errorMessageArgs the arguments to be substituted into the message template. Arguments + * are converted to strings using {@link String#valueOf(Object)}. + * @throws IllegalArgumentException if {@code expression} is false + * @throws NullPointerException if the check fails and either {@code errorMessageTemplate} or + * {@code errorMessageArgs} is null (don't let this happen) + */ + public static void checkArgument(boolean expression, + String errorMessageTemplate, + Object... errorMessageArgs) { + if (!expression) { + throw new IllegalArgumentException(format(errorMessageTemplate, errorMessageArgs)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + * @param expression a boolean expression + * @throws IllegalStateException if {@code expression} is false + */ + public static void checkState(boolean expression) { + if (!expression) { + throw new IllegalStateException(); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + * @param expression a boolean expression + * @param errorMessage the exception message to use if the check fails; will be converted to a + * string using {@link String#valueOf(Object)} + * @throws IllegalStateException if {@code expression} is false + */ + public static void checkState(boolean expression, Object errorMessage) { + if (!expression) { + throw new IllegalStateException(String.valueOf(errorMessage)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + * @param expression a boolean expression + * @param errorMessageTemplate a template for the exception message should the check fail. The + * message is formed by replacing each {@code %s} placeholder in the template with an + * argument. These are matched by position - the first {@code %s} gets {@code + * errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message + * in square braces. Unmatched placeholders will be left as-is. + * @param errorMessageArgs the arguments to be substituted into the message template. Arguments + * are converted to strings using {@link String#valueOf(Object)}. + * @throws IllegalStateException if {@code expression} is false + * @throws NullPointerException if the check fails and either {@code errorMessageTemplate} or + * {@code errorMessageArgs} is null (don't let this happen) + */ + public static void checkState(boolean expression, + String errorMessageTemplate, + Object... errorMessageArgs) { + if (!expression) { + throw new IllegalStateException(format(errorMessageTemplate, errorMessageArgs)); + } + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + * @param reference an object reference + * @return the non-null reference that was validated + * @throws NullPointerException if {@code reference} is null + */ + public static T checkNotNull(T reference) { + if (reference == null) { + throw new NullPointerException(); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + * @param reference an object reference + * @param errorMessage the exception message to use if the check fails; will be converted to a + * string using {@link String#valueOf(Object)} + * @return the non-null reference that was validated + * @throws NullPointerException if {@code reference} is null + */ + public static T checkNotNull(T reference, Object errorMessage) { + if (reference == null) { + throw new NullPointerException(String.valueOf(errorMessage)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + * @param reference an object reference + * @param errorMessageTemplate a template for the exception message should the check fail. The + * message is formed by replacing each {@code %s} placeholder in the template with an + * argument. These are matched by position - the first {@code %s} gets {@code + * errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message + * in square braces. Unmatched placeholders will be left as-is. + * @param errorMessageArgs the arguments to be substituted into the message template. Arguments + * are converted to strings using {@link String#valueOf(Object)}. + * @return the non-null reference that was validated + * @throws NullPointerException if {@code reference} is null + */ + public static T checkNotNull(T reference, + String errorMessageTemplate, + Object... errorMessageArgs) { + if (reference == null) { + // If either of these parameters is null, the right thing happens anyway + throw new NullPointerException(format(errorMessageTemplate, errorMessageArgs)); + } + return reference; + } + + /* + * All recent hotspots (as of 2009) *really* like to have the natural code + * + * if (guardExpression) { + * throw new BadException(messageExpression); + * } + * + * refactored so that messageExpression is moved to a separate String-returning method. + * + * if (guardExpression) { + * throw new BadException(badMsg(...)); + * } + * + * The alternative natural refactorings into void or Exception-returning methods are much slower. + * This is a big deal - we're talking factors of 2-8 in microbenchmarks, not just 10-20%. (This + * is a hotspot optimizer bug, which should be fixed, but that's a separate, big project). + * + * The coding pattern above is heavily used in java.util, e.g. in ArrayList. There is a + * RangeCheckMicroBenchmark in the JDK that was used to test this. + * + * But the methods in this class want to throw different exceptions, depending on the args, so it + * appears that this pattern is not directly applicable. But we can use the ridiculous, devious + * trick of throwing an exception in the middle of the construction of another exception. Hotspot + * is fine with that. + */ + + /** + * Ensures that {@code index} specifies a valid element in an array, list or string of size + * {@code size}. An element index may range from zero, inclusive, to {@code size}, exclusive. + * + * @param index a user-supplied index identifying an element of an array, list or string + * @param size the size of that array, list or string + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is not less than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static int checkElementIndex(int index, int size) { + return checkElementIndex(index, size, "index"); + } + + /** + * Ensures that {@code index} specifies a valid element in an array, list or string of size + * {@code size}. An element index may range from zero, inclusive, to {@code size}, exclusive. + * + * @param index a user-supplied index identifying an element of an array, list or string + * @param size the size of that array, list or string + * @param desc the text to use to describe this index in an error message + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is not less than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + private static int checkElementIndex( + int index, int size, String desc) { + // Carefully optimized for execution by hotspot (explanatory comment above) + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(badElementIndex(index, size, desc)); + } + return index; + } + + private static String badElementIndex(int index, int size, String desc) { + if (index < 0) { + return format("%s (%s) must not be negative", desc, index); + } else if (size < 0) { + throw new IllegalArgumentException("negative size: " + size); + } else { // index >= size + return format("%s (%s) must be less than size (%s)", desc, index, size); + } + } + + /** + * Ensures that {@code index} specifies a valid position in an array, list or string of + * size {@code size}. A position index may range from zero to {@code size}, inclusive. + * + * @param index a user-supplied index identifying a position in an array, list or string + * @param size the size of that array, list or string + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is greater than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static int checkPositionIndex(int index, int size) { + return checkPositionIndex(index, size, "index"); + } + + /** + * Ensures that {@code index} specifies a valid position in an array, list or string of + * size {@code size}. A position index may range from zero to {@code size}, inclusive. + * + * @param index a user-supplied index identifying a position in an array, list or string + * @param size the size of that array, list or string + * @param desc the text to use to describe this index in an error message + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is greater than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + private static int checkPositionIndex(int index, int size, String desc) { + // Carefully optimized for execution by hotspot (explanatory comment above) + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(badPositionIndex(index, size, desc)); + } + return index; + } + + private static String badPositionIndex(int index, int size, String desc) { + if (index < 0) { + return format("%s (%s) must not be negative", desc, index); + } else if (size < 0) { + throw new IllegalArgumentException("negative size: " + size); + } else { // index > size + return format("%s (%s) must not be greater than size (%s)", desc, index, size); + } + } + + /** + * Ensures that {@code start} and {@code end} specify a valid positions in an array, list + * or string of size {@code size}, and are in order. A position index may range from zero to + * {@code size}, inclusive. + * + * @param start a user-supplied index identifying a starting position in an array, list or string + * @param end a user-supplied index identifying a ending position in an array, list or string + * @param size the size of that array, list or string + * @throws IndexOutOfBoundsException if either index is negative or is greater than {@code size}, + * or if {@code end} is less than {@code start} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static void checkPositionIndexes(int start, int end, int size) { + // Carefully optimized for execution by hotspot (explanatory comment above) + if (start < 0 || end < start || end > size) { + throw new IndexOutOfBoundsException(badPositionIndexes(start, end, size)); + } + } + + private static String badPositionIndexes(int start, int end, int size) { + if (start < 0 || start > size) { + return badPositionIndex(start, size, "start index"); + } + if (end < 0 || end > size) { + return badPositionIndex(end, size, "end index"); + } + // end < start + return format("end index (%s) must not be less than start index (%s)", end, start); + } + + /** + * Substitutes each {@code %s} in {@code template} with an argument. These are matched by + * position: the first {@code %s} gets {@code args[0]}, etc. If there are more arguments than + * placeholders, the unmatched arguments will be appended to the end of the formatted message in + * square braces. + * + * @param template a non-null string containing 0 or more {@code %s} placeholders. + * @param args the arguments to be substituted into the message template. Arguments are converted + * to strings using {@link String#valueOf(Object)}. Arguments can be null. + */ + // Note that this is somewhat-improperly used from Verify.java as well. + private static String format(String template, Object... args) { + template = String.valueOf(template); // null -> "null" + + // start substituting the arguments into the '%s' placeholders + StringBuilder builder = new StringBuilder(template.length() + 16 * args.length); + int templateStart = 0; + int i = 0; + while (i < args.length) { + int placeholderStart = template.indexOf("%s", templateStart); + if (placeholderStart == -1) { + break; + } + builder.append(template.substring(templateStart, placeholderStart)); + builder.append(args[i++]); + templateStart = placeholderStart + 2; + } + builder.append(template.substring(templateStart)); + + // if we run out of placeholders, append the extra args in square braces + if (i < args.length) { + builder.append(" ["); + builder.append(args[i++]); + while (i < args.length) { + builder.append(", "); + builder.append(args[i++]); + } + builder.append(']'); + } + + return builder.toString(); + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/Predicates.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Predicates.java new file mode 100644 index 0000000000..84aed5f912 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Predicates.java @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; + +/** + * Static utility methods pertaining to {@code Predicate} instances. + *

+ *

All methods returns serializable predicates as long as they're given + * serializable parameters. + *

+ *

See the Guava User Guide article on the + * use of {@code Predicate}. + * + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ +public final class Predicates { + private static final Joiner COMMA_JOINER = Joiner.on(); + + // TODO(kevinb): considering having these implement a VisitablePredicate + // interface which specifies an accept(PredicateVisitor) method. + + private Predicates() { + } + + /** + * Returns a predicate that always evaluates to {@code true}. + */ + public static Predicate alwaysTrue() { + return ObjectPredicate.ALWAYS_TRUE.withNarrowedType(); + } + + /** + * Returns a predicate that evaluates to {@code true} if the object reference + * being tested is null. + */ + private static Predicate isNull() { + return ObjectPredicate.IS_NULL.withNarrowedType(); + } + + /** + * Returns a predicate that evaluates to {@code true} if the given predicate + * evaluates to {@code false}. + */ + public static Predicate not(Predicate predicate) { + return new NotPredicate(predicate); + } + + /** + * Returns a predicate that evaluates to {@code true} if the object being + * tested {@code equals()} the given target or both are null. + */ + public static Predicate equalTo(T target) { + return (target == null) + ? Predicates.isNull() + : new IsEqualToPredicate(target); + } + + /** + * Returns a predicate that evaluates to {@code true} if the object reference + * being tested is a member of the given collection. It does not defensively + * copy the collection passed in, so future changes to it will alter the + * behavior of the predicate. + *

+ *

This method can technically accept any {@code Collection}, but using + * a typed collection helps prevent bugs. This approach doesn't block any + * potential users since it is always possible to use {@code + * Predicates.in()}. + * + * @param target the collection that may contain the function input + */ + public static Predicate in(Collection target) { + return new InPredicate(target); + } + + // End public API, begin private implementation classes. + + /** + * Returns the composition of a function and a predicate. For every {@code x}, + * the generated predicate returns {@code predicate(function(x))}. + * + * @return the composition of the provided function and predicate + */ + public static Predicate compose( + Predicate predicate, Function function) { + return new CompositionPredicate(predicate, function); + } + + // Package private for GWT serialization. + enum ObjectPredicate implements Predicate { + /** + * @see Predicates#alwaysTrue() + */ + ALWAYS_TRUE { + @Override + public boolean test(Object o) { + return true; + } + + @Override + public String toString() { + return "Predicates.alwaysTrue()"; + } + }, + /** + * @see Predicates#isNull() + */ + IS_NULL { + @Override + public boolean test(Object o) { + return o == null; + } + + @Override + public String toString() { + return "Predicates.isNull()"; + } + }; + + @SuppressWarnings("unchecked") + // safe contravariant cast + Predicate withNarrowedType() { + return (Predicate) this; + } + } + + /** + * @see Predicates#not(Predicate) + */ + private static class NotPredicate implements Predicate, Serializable { + private static final long serialVersionUID = 0; + final Predicate predicate; + + NotPredicate(Predicate predicate) { + this.predicate = Preconditions.checkNotNull(predicate); + } + + @Override + public boolean test(T t) { + return !predicate.test(t); + } + + @Override + public int hashCode() { + return ~predicate.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof NotPredicate) { + NotPredicate that = (NotPredicate) obj; + return predicate.equals(that.predicate); + } + return false; + } + + @Override + public String toString() { + return "Predicates.not(" + predicate.toString() + ")"; + } + } + + /** + * @see Predicates#equalTo(Object) + */ + private static class IsEqualToPredicate + implements Predicate, Serializable { + private static final long serialVersionUID = 0; + private final T target; + + private IsEqualToPredicate(T target) { + this.target = target; + } + + @Override + public boolean test(T t) { + return target.equals(t); + } + + @Override + public int hashCode() { + return target.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof IsEqualToPredicate) { + IsEqualToPredicate that = (IsEqualToPredicate) obj; + return target.equals(that.target); + } + return false; + } + + @Override + public String toString() { + return "Predicates.equalTo(" + target + ")"; + } + } + + /** + * @see Predicates#in(Collection) + */ + private static class InPredicate implements Predicate, Serializable { + private static final long serialVersionUID = 0; + private final Collection target; + + private InPredicate(Collection target) { + this.target = Preconditions.checkNotNull(target); + } + + @Override + public boolean test(T t) { + try { + return target.contains(t); + } catch (NullPointerException e) { + return false; + } catch (ClassCastException e) { + return false; + } + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof InPredicate) { + InPredicate that = (InPredicate) obj; + return target.equals(that.target); + } + return false; + } + + @Override + public int hashCode() { + return target.hashCode(); + } + + @Override + public String toString() { + return "Predicates.in(" + target + ")"; + } + } + + /** + * @see Predicates#compose(Predicate, Function) + */ + private static class CompositionPredicate + implements Predicate, Serializable { + private static final long serialVersionUID = 0; + final Predicate p; + final Function f; + + private CompositionPredicate(Predicate p, Function f) { + this.p = Preconditions.checkNotNull(p); + this.f = Preconditions.checkNotNull(f); + } + + @Override + public boolean test(A a) { + return p.test(f.apply(a)); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof CompositionPredicate) { + CompositionPredicate that = (CompositionPredicate) obj; + return f.equals(that.f) && p.equals(that.p); + } + return false; + } + + @Override + public int hashCode() { + return f.hashCode() ^ p.hashCode(); + } + + @Override + public String toString() { + return p.toString() + "(" + f.toString() + ")"; + } + } + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/Primitives.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Primitives.java new file mode 100644 index 0000000000..f9d5aa3af9 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Primitives.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; + +/** + * Contains static utility methods pertaining to primitive types and their + * corresponding wrapper types. + * + * @author Kevin Bourrillion + * @since 1.0 + */ +public final class Primitives { + /** + * A map from primitive types to their corresponding wrapper types. + */ + private static final Map, Class> PRIMITIVE_TO_WRAPPER_TYPE; + + static { + Map, Class> primToWrap = new HashMap, Class>(16); + + add(primToWrap, boolean.class, Boolean.class); + add(primToWrap, byte.class, Byte.class); + add(primToWrap, char.class, Character.class); + add(primToWrap, double.class, Double.class); + add(primToWrap, float.class, Float.class); + add(primToWrap, int.class, Integer.class); + add(primToWrap, long.class, Long.class); + add(primToWrap, short.class, Short.class); + add(primToWrap, void.class, Void.class); + + PRIMITIVE_TO_WRAPPER_TYPE = Collections.unmodifiableMap(primToWrap); + } + + private Primitives() { + } + + private static void add(Map, Class> forward, + Class key, Class value) { + forward.put(key, value); + } + + /** + * Returns the corresponding wrapper type of {@code type} if it is a primitive + * type; otherwise returns {@code type} itself. Idempotent. + *
+     *     wrap(int.class) == Integer.class
+     *     wrap(Integer.class) == Integer.class
+     *     wrap(String.class) == String.class
+     * 
+ */ + public static Class wrap(Class type) { + checkNotNull(type); + + // cast is safe: long.class and Long.class are both of type Class + @SuppressWarnings("unchecked") + Class wrapped = (Class) PRIMITIVE_TO_WRAPPER_TYPE.get(type); + return (wrapped == null) ? type : wrapped; + } + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/RemovalCause.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/RemovalCause.java new file mode 100644 index 0000000000..c84ce87dcd --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/RemovalCause.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ConcurrentMap; + +/** + * The reason why a cached entry was removed. + * + * @author Charles Fry + * @since 10.0 + */ +public enum RemovalCause { + /** + * The entry was manually removed by the user. This can result from the user invoking + * {@link Cache#invalidate}, {@link Cache#invalidateAll(Iterable)}, {@link Cache#invalidateAll()}, + * {@link Map#remove}, {@link ConcurrentMap#remove}, or {@link Iterator#remove}. + */ + EXPLICIT { + }, + + /** + * The entry itself was not actually removed, but its value was replaced by the user. This can + * result from the user invoking {@link Cache#put}, {@link LoadingCache#refresh}, {@link Map#put}, + * {@link Map#putAll}, {@link ConcurrentMap#replace(Object, Object)}, or + * {@link ConcurrentMap#replace(Object, Object, Object)}. + */ + REPLACED { + }, + + /** + * The entry was removed automatically because its key or value was garbage-collected. This + * can occur when using {@link CacheBuilder#weakKeys}, {@link CacheBuilder#weakValues}, or + * {@link CacheBuilder#softValues}. + */ + COLLECTED { + }, + + /** + * The entry's expiration timestamp has passed. This can occur when using + * {@link CacheBuilder#expireAfterWrite} or {@link CacheBuilder#expireAfterAccess}. + */ + EXPIRED { + }, + + /** + * The entry was evicted due to size constraints. This can occur when using + * {@link CacheBuilder#maximumSize} or {@link CacheBuilder#maximumWeight}. + */ + SIZE { + } + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/RemovalNotification.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/RemovalNotification.java new file mode 100644 index 0000000000..d795a9b416 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/RemovalNotification.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Map.Entry; +import java.util.Objects; + +/** + * A notification of the removal of a single entry. The key and/or value may be null if they were + * already garbage collected. + *

+ *

Like other {@code Map.Entry} instances associated with {@code CacheBuilder}, this class holds + * strong references to the key and value, regardless of the type of references the cache may be + * using. + * + * @author Charles Fry + * @since 10.0 + */ +final class RemovalNotification implements Entry { + private static final long serialVersionUID = 0; + private final K key; + private final V value; + + RemovalNotification(K key, V value, RemovalCause cause) { + this.key = key; + this.value = value; + } + + @Override + public K getKey() { + return key; + } + + @Override + public V getValue() { + return value; + } + + @Override + public final V setValue(V value) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean equals(Object object) { + if (object instanceof Entry) { + Entry that = (Entry) object; + return Objects.equals(this.getKey(), that.getKey()) + && Objects.equals(this.getValue(), that.getValue()); + } + return false; + } + + @Override + public int hashCode() { + K k = getKey(); + V v = getValue(); + return ((k == null) ? 0 : k.hashCode()) ^ ((v == null) ? 0 : v.hashCode()); + } + + /** + * Returns a string representation of the form {key}={value}. + */ + @Override + public String toString() { + return getKey() + "=" + getValue(); + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/ReverseNaturalOrdering.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ReverseNaturalOrdering.java new file mode 100644 index 0000000000..29f387e600 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ReverseNaturalOrdering.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.io.Serializable; +import java.util.Iterator; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; + +/** + * An ordering that uses the reverse of the natural order of the values. + */ +@SuppressWarnings("unchecked") // TODO(kevinb): the right way to explain this?? +final class ReverseNaturalOrdering + extends Ordering implements Serializable { + static final ReverseNaturalOrdering INSTANCE = new ReverseNaturalOrdering(); + private static final long serialVersionUID = 0; + + private ReverseNaturalOrdering() { + } + + // Override the min/max methods to "hoist" delegation outside loops + + @Override + public int compare(Comparable left, Comparable right) { + checkNotNull(left); // right null is caught later + if (left == right) { + return 0; + } + + return right.compareTo(left); + } + + @Override + public Ordering reverse() { + return Ordering.natural(); + } + + @Override + public E min(E a, E b) { + return NaturalOrdering.INSTANCE.max(a, b); + } + + @Override + public E min(E a, E b, E c, E... rest) { + return NaturalOrdering.INSTANCE.max(a, b, c, rest); + } + + @Override + public E min(Iterator iterator) { + return NaturalOrdering.INSTANCE.max(iterator); + } + + @Override + public E min(Iterable iterable) { + return NaturalOrdering.INSTANCE.max(iterable); + } + + @Override + public E max(E a, E b) { + return NaturalOrdering.INSTANCE.min(a, b); + } + + @Override + public E max(E a, E b, E c, E... rest) { + return NaturalOrdering.INSTANCE.min(a, b, c, rest); + } + + @Override + public E max(Iterator iterator) { + return NaturalOrdering.INSTANCE.min(iterator); + } + + @Override + public E max(Iterable iterable) { + return NaturalOrdering.INSTANCE.min(iterable); + } + + // preserving singleton-ness gives equals()/hashCode() for free + private Object readResolve() { + return INSTANCE; + } + + @Override + public String toString() { + return "Ordering.natural().reverse()"; + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/ReverseOrdering.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ReverseOrdering.java new file mode 100644 index 0000000000..9851e7392d --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ReverseOrdering.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.io.Serializable; +import java.util.Iterator; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; + +/** + * An ordering that uses the reverse of a given order. + */ +final class ReverseOrdering extends Ordering implements Serializable { + private static final long serialVersionUID = 0; + private final Ordering forwardOrder; + + ReverseOrdering(Ordering forwardOrder) { + this.forwardOrder = checkNotNull(forwardOrder); + } + + @Override + public int compare(T a, T b) { + return forwardOrder.compare(b, a); + } + + // Override the min/max methods to "hoist" delegation outside loops + + @SuppressWarnings("unchecked") // how to explain? + @Override + public Ordering reverse() { + return (Ordering) forwardOrder; + } + + @Override + public E min(E a, E b) { + return forwardOrder.max(a, b); + } + + @Override + public E min(E a, E b, E c, E... rest) { + return forwardOrder.max(a, b, c, rest); + } + + @Override + public E min(Iterator iterator) { + return forwardOrder.max(iterator); + } + + @Override + public E min(Iterable iterable) { + return forwardOrder.max(iterable); + } + + @Override + public E max(E a, E b) { + return forwardOrder.min(a, b); + } + + @Override + public E max(E a, E b, E c, E... rest) { + return forwardOrder.min(a, b, c, rest); + } + + @Override + public E max(Iterator iterator) { + return forwardOrder.min(iterator); + } + + @Override + public E max(Iterable iterable) { + return forwardOrder.min(iterable); + } + + @Override + public int hashCode() { + return -forwardOrder.hashCode(); + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof ReverseOrdering) { + ReverseOrdering that = (ReverseOrdering) object; + return this.forwardOrder.equals(that.forwardOrder); + } + return false; + } + + @Override + public String toString() { + return forwardOrder + ".reverse()"; + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/Serialization.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Serialization.java new file mode 100644 index 0000000000..11a00b5578 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Serialization.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Collection; +import java.util.Map; + +/** + * Provides static methods for serializing collection classes. + *

+ *

This class assists the implementation of collection classes. Do not use + * this class to serialize collections that are defined elsewhere. + * + * @author Jared Levy + */ +final class Serialization { + private Serialization() { + } + + /** + * Reads a count corresponding to a serialized map, multiset, or multimap. It + * returns the size of a map serialized by {@link + * #writeMap(Map, ObjectOutputStream)}, the number of distinct elements in a + * multiset serialized by {@link + * #writeMultiset(Multiset, ObjectOutputStream)}, or the number of distinct + * keys in a multimap serialized by {@link + * #writeMultimap(Multimap, ObjectOutputStream)}. + *

+ *

The returned count may be used to construct an empty collection of the + * appropriate capacity before calling any of the {@code populate} methods. + */ + static int readCount(ObjectInputStream stream) throws IOException { + return stream.readInt(); + } + + /** + * Stores the contents of a multimap in an output stream, as part of + * serialization. It does not support concurrent multimaps whose content may + * change while the method is running. The {@link Multimap#asMap} view + * determines the ordering in which data is written to the stream. + *

+ *

The serialized output consists of the number of distinct keys, and then + * for each distinct key: the key, the number of values for that key, and the + * key's values. + */ + static void writeMultimap( + Multimap multimap, ObjectOutputStream stream) throws IOException { + stream.writeInt(multimap.asMap().size()); + for (Map.Entry> entry : multimap.asMap().entrySet()) { + stream.writeObject(entry.getKey()); + stream.writeInt(entry.getValue().size()); + for (V value : entry.getValue()) { + stream.writeObject(value); + } + } + } + + /** + * Populates a multimap by reading an input stream, as part of + * deserialization. See {@link #writeMultimap} for the data format. + */ + static void populateMultimap( + Multimap multimap, ObjectInputStream stream) + throws IOException, ClassNotFoundException { + int distinctKeys = stream.readInt(); + populateMultimap(multimap, stream, distinctKeys); + } + + /** + * Populates a multimap by reading an input stream, as part of + * deserialization. See {@link #writeMultimap} for the data format. The number + * of distinct keys is determined by a prior call to {@link #readCount}. + */ + static void populateMultimap( + Multimap multimap, ObjectInputStream stream, int distinctKeys) + throws IOException, ClassNotFoundException { + for (int i = 0; i < distinctKeys; i++) { + @SuppressWarnings("unchecked") // reading data stored by writeMultimap + K key = (K) stream.readObject(); + Collection values = multimap.get(key); + int valueCount = stream.readInt(); + for (int j = 0; j < valueCount; j++) { + @SuppressWarnings("unchecked") // reading data stored by writeMultimap + V value = (V) stream.readObject(); + values.add(value); + } + } + } + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/SetMultimap.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/SetMultimap.java new file mode 100644 index 0000000000..7323a9adb4 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/SetMultimap.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** + * A {@code Multimap} that cannot hold duplicate key-value pairs. Adding a + * key-value pair that's already in the multimap has no effect. See the {@link + * Multimap} documentation for information common to all multimaps. + *

+ *

The {@link #get}, {@link #removeAll}, and {@link #replaceValues} methods + * each return a {@link Set} of values, while {@link #entries} returns a {@code + * Set} of map entries. Though the method signature doesn't say so explicitly, + * the map returned by {@link #asMap} has {@code Set} values. + *

+ *

If the values corresponding to a single key should be ordered according to + * a {@link java.util.Comparator} (or the natural order), see the + * {@link SortedSetMultimap} subinterface. + *

+ *

Since the value collections are sets, the behavior of a {@code SetMultimap} + * is not specified if key or value objects already present in the + * multimap change in a manner that affects {@code equals} comparisons. + * Use caution if mutable objects are used as keys or values in a + * {@code SetMultimap}. + *

+ *

+ *

Because a {@code SetMultimap} has unique values for a given key, this + * method returns a {@link Set}, instead of the {@link Collection} + * specified in the {@link Multimap} interface. + */ + @Override + Set get(K key); + + /** + * {@inheritDoc} + *

+ *

Because a {@code SetMultimap} has unique values for a given key, this + * method returns a {@link Set}, instead of the {@link Collection} + * specified in the {@link Multimap} interface. + */ + @Override + Set removeAll(Object key); + + /** + * {@inheritDoc} + *

+ *

Because a {@code SetMultimap} has unique values for a given key, this + * method returns a {@link Set}, instead of the {@link Collection} + * specified in the {@link Multimap} interface. + */ + @Override + Set> entries(); + + /** + * {@inheritDoc} + *

+ *

Note: The returned map's values are guaranteed to be of type + * {@link Set}. To obtain this map with the more specific generic type + * {@code Map>}, call {@link Multimaps#asMap(SetMultimap)} instead. + */ + @Override + Map> asMap(); + + /** + * Compares the specified object to this multimap for equality. + *

+ *

Two {@code SetMultimap} instances are equal if, for each key, they + * contain the same values. Equality does not depend on the ordering of keys + * or values. + *

+ *

An empty {@code SetMultimap} is equal to any other empty {@code + * Multimap}, including an empty {@code ListMultimap}. + */ + @Override + boolean equals(Object obj); +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/Sets.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Sets.java new file mode 100644 index 0000000000..83c7f0947e --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Sets.java @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.io.Serializable; +import java.util.AbstractSet; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.Iterator; +import java.util.NavigableSet; +import java.util.Set; +import java.util.SortedSet; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; + +/** + * Static utility methods pertaining to {@link Set} instances. Also see this + * class's counterparts {@link Lists}, {@link Maps} and {@link Queues}. + *

+ *

See the Guava User Guide article on + * {@code Sets}. + * + * @author Kevin Bourrillion + * @author Jared Levy + * @author Chris Povirk + * @since 2.0 (imported from Google Collections Library) + */ +public final class Sets { + private Sets() { + } + + /** + * Creates a mutable, empty {@code HashSet} instance. + *

+ *

Note: if mutability is not required, use {@link + * ImmutableSet#of()} instead. + *

+ *

Note: if {@code E} is an {@link Enum} type, use {@link + * EnumSet#noneOf} instead. + * + * @return a new, empty {@code HashSet} + */ + public static HashSet newHashSet() { + return new HashSet(); + } + + // HashSet + + /** + * Creates a {@code HashSet} instance, with a high enough "initial capacity" + * that it should hold {@code expectedSize} elements without growth. + * This behavior cannot be broadly guaranteed, but it is observed to be true + * for OpenJDK 1.6. It also can't be guaranteed that the method isn't + * inadvertently oversizing the returned set. + * + * @param expectedSize the number of elements you expect to add to the + * returned set + * @return a new, empty {@code HashSet} with enough capacity to hold {@code + * expectedSize} elements without resizing + * @throws IllegalArgumentException if {@code expectedSize} is negative + */ + public static HashSet newHashSetWithExpectedSize(int expectedSize) { + return new HashSet(Maps.capacity(expectedSize)); + } + + // LinkedHashSet + + // TreeSet + + /** + * An implementation for {@link Set#hashCode()}. + */ + static int hashCodeImpl(Set s) { + int hashCode = 0; + for (Object o : s) { + hashCode += o != null ? o.hashCode() : 0; + + hashCode = ~~hashCode; + // Needed to deal with unusual integer overflow in GWT. + } + return hashCode; + } + + /** + * An implementation for {@link Set#equals(Object)}. + */ + static boolean equalsImpl(Set s, Object object) { + if (s == object) { + return true; + } + if (object instanceof Set) { + Set o = (Set) object; + + try { + return s.size() == o.size() && s.containsAll(o); + } catch (NullPointerException ignored) { + return false; + } catch (ClassCastException ignored) { + return false; + } + } + return false; + } + + /** + * Returns an unmodifiable view of the specified navigable set. This method + * allows modules to provide users with "read-only" access to internal + * navigable sets. Query operations on the returned set "read through" to the + * specified set, and attempts to modify the returned set, whether direct or + * via its collection views, result in an + * {@code UnsupportedOperationException}. + *

+ *

The returned navigable set will be serializable if the specified + * navigable set is serializable. + * + * @param set the navigable set for which an unmodifiable view is to be + * returned + * @return an unmodifiable view of the specified navigable set + * @since 12.0 + */ + public static NavigableSet unmodifiableNavigableSet( + NavigableSet set) { + return new UnmodifiableNavigableSet(set); + } + + /** + * Remove each element in an iterable from a set. + */ + static boolean removeAllImpl(Set set, Iterator iterator) { + boolean changed = false; + while (iterator.hasNext()) { + changed |= set.remove(iterator.next()); + } + return changed; + } + + static boolean removeAllImpl(Set set, Collection collection) { + checkNotNull(collection); // for GWT + /* + * AbstractSet.removeAll(List) has quadratic behavior if the list size + * is just less than the set's size. We augment the test by + * assuming that sets have fast contains() performance, and other + * collections don't. See + * http://code.google.com/p/guava-libraries/issues/detail?id=1013 + */ + if (collection instanceof Set && collection.size() > set.size()) { + return Iterators.removeAll(set.iterator(), collection); + } else { + return removeAllImpl(set, collection.iterator()); + } + } + + /** + * {@link AbstractSet} substitute without the potentially-quadratic + * {@code removeAll} implementation. + */ + abstract static class ImprovedAbstractSet extends AbstractSet { + @Override + public boolean removeAll(Collection c) { + return removeAllImpl(this, c); + } + + @Override + public boolean retainAll(Collection c) { + return super.retainAll(checkNotNull(c)); // GWT compatibility + } + } + + static final class UnmodifiableNavigableSet + extends ForwardingSortedSet implements NavigableSet, Serializable { + private static final long serialVersionUID = 0; + private final NavigableSet delegate; + private transient UnmodifiableNavigableSet descendingSet; + + UnmodifiableNavigableSet(NavigableSet delegate) { + this.delegate = checkNotNull(delegate); + } + + @Override + protected SortedSet delegate() { + return Collections.unmodifiableSortedSet(delegate); + } + + @Override + public E lower(E e) { + return delegate.lower(e); + } + + @Override + public E floor(E e) { + return delegate.floor(e); + } + + @Override + public E ceiling(E e) { + return delegate.ceiling(e); + } + + @Override + public E higher(E e) { + return delegate.higher(e); + } + + @Override + public E pollFirst() { + throw new UnsupportedOperationException(); + } + + @Override + public E pollLast() { + throw new UnsupportedOperationException(); + } + + @Override + public NavigableSet descendingSet() { + UnmodifiableNavigableSet result = descendingSet; + if (result == null) { + result = descendingSet = new UnmodifiableNavigableSet( + delegate.descendingSet()); + result.descendingSet = this; + } + return result; + } + + @Override + public Iterator descendingIterator() { + return Iterators.unmodifiableIterator(delegate.descendingIterator()); + } + + @Override + public NavigableSet subSet( + E fromElement, + boolean fromInclusive, + E toElement, + boolean toInclusive) { + return unmodifiableNavigableSet(delegate.subSet( + fromElement, + fromInclusive, + toElement, + toInclusive)); + } + + @Override + public NavigableSet headSet(E toElement, boolean inclusive) { + return unmodifiableNavigableSet(delegate.headSet(toElement, inclusive)); + } + + @Override + public NavigableSet tailSet(E fromElement, boolean inclusive) { + return unmodifiableNavigableSet( + delegate.tailSet(fromElement, inclusive)); + } + } + +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/SettableFuture.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/SettableFuture.java new file mode 100644 index 0000000000..591fc0fc9d --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/SettableFuture.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +/** + * A {@link ListenableFuture} whose result may be set by a {@link #set(Object)} + * or {@link #setException(Throwable)} call. It may also be cancelled. + * + * @author Sven Mawson + * @since 9.0 (in 1.0 as {@code ValueFuture}) + */ +public final class SettableFuture extends AbstractFuture { + + /** + * Explicit private constructor, use the {@link #create} factory method to + * create instances of {@code SettableFuture}. + */ + private SettableFuture() { + } + + /** + * Creates a new {@code SettableFuture} in the default state. + */ + public static SettableFuture create() { + return new SettableFuture(); + } + + /** + * Sets the value of this future. This method will return {@code true} if + * the value was successfully set, or {@code false} if the future has already + * been set or cancelled. + * + * @param value the value the future should hold. + * @return true if the value was successfully set. + */ + @Override + public boolean set(V value) { + return super.set(value); + } + + /** + * Sets the future to having failed with the given exception. This exception + * will be wrapped in an {@code ExecutionException} and thrown from the {@code + * get} methods. This method will return {@code true} if the exception was + * successfully set, or {@code false} if the future has already been set or + * cancelled. + * + * @param throwable the exception the future should hold. + * @return true if the exception was successfully set. + */ + @Override + public boolean setException(Throwable throwable) { + return super.setException(throwable); + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/SortedIterable.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/SortedIterable.java new file mode 100644 index 0000000000..ca2bfd99ef --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/SortedIterable.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Comparator; +import java.util.Iterator; + +/** + * An {@code Iterable} whose elements are sorted relative to a {@code Comparator}, typically + * provided at creation time. + * + * @author Louis Wasserman + */ +interface SortedIterable extends Iterable { + /** + * Returns the {@code Comparator} by which the elements of this iterable are ordered, or {@code + * Ordering.natural()} if the elements are ordered by their natural ordering. + */ + Comparator comparator(); + + /** + * Returns an iterator over elements of type {@code T}. The elements are returned in + * nondecreasing order according to the associated {@link #comparator}. + */ + @Override + Iterator iterator(); +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/SortedIterables.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/SortedIterables.java new file mode 100644 index 0000000000..8d1271e169 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/SortedIterables.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Comparator; +import java.util.SortedSet; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; + +/** + * Utilities for dealing with sorted collections of all types. + * + * @author Louis Wasserman + */ +final class SortedIterables { + private SortedIterables() { + } + + /** + * Returns {@code true} if {@code elements} is a sorted collection using an ordering equivalent + * to {@code comparator}. + */ + public static boolean hasSameComparator(Comparator comparator, Iterable elements) { + checkNotNull(comparator); + checkNotNull(elements); + Comparator comparator2; + if (elements instanceof SortedSet) { + comparator2 = comparator((SortedSet) elements); + } else if (elements instanceof SortedIterable) { + comparator2 = ((SortedIterable) elements).comparator(); + } else { + return false; + } + return comparator.equals(comparator2); + } + + @SuppressWarnings("unchecked") + // if sortedSet.comparator() is null, the set must be naturally ordered + private static Comparator comparator(SortedSet sortedSet) { + Comparator result = sortedSet.comparator(); + if (result == null) { + result = (Comparator) Ordering.natural(); + } + return result; + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/SortedLists.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/SortedLists.java new file mode 100644 index 0000000000..b4edde44fe --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/SortedLists.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2010 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.RandomAccess; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; + +/** + * Static methods pertaining to sorted {@link List} instances. + *

+ * In this documentation, the terms greatest, greater, least, and + * lesser are considered to refer to the comparator on the elements, and the terms + * first and last are considered to refer to the elements' ordering in a + * list. + * + * @author Louis Wasserman + */ +final class SortedLists { + private SortedLists() { + } + + /** + * Searches the specified list for the specified object using the binary search algorithm. The + * list must be sorted into ascending order according to the specified comparator (as by the + * {@link Collections#sort(List, Comparator) Collections.sort(List, Comparator)} method), prior + * to making this call. If it is not sorted, the results are undefined. + *

+ *

If there are elements in the list which compare as equal to the key, the choice of + * {@link KeyPresentBehavior} decides which index is returned. If no elements compare as equal to + * the key, the choice of {@link KeyAbsentBehavior} decides which index is returned. + *

+ *

This method runs in log(n) time on random-access lists, which offer near-constant-time + * access to each list element. + * + * @param list the list to be searched. + * @param key the value to be searched for. + * @param comparator the comparator by which the list is ordered. + * @param presentBehavior the specification for what to do if at least one element of the list + * compares as equal to the key. + * @param absentBehavior the specification for what to do if no elements of the list compare as + * equal to the key. + * @return the index determined by the {@code KeyPresentBehavior}, if the key is in the list; + * otherwise the index determined by the {@code KeyAbsentBehavior}. + */ + public static int binarySearch(List list, E key, + Comparator comparator, KeyPresentBehavior presentBehavior, + KeyAbsentBehavior absentBehavior) { + checkNotNull(comparator); + checkNotNull(list); + checkNotNull(presentBehavior); + checkNotNull(absentBehavior); + if (!(list instanceof RandomAccess)) { + list = Lists.newArrayList(list); + } + // TODO(user): benchmark when it's best to do a linear search + + int lower = 0; + int upper = list.size() - 1; + + while (lower <= upper) { + int middle = (lower + upper) >>> 1; + int c = comparator.compare(key, list.get(middle)); + if (c < 0) { + upper = middle - 1; + } else if (c > 0) { + lower = middle + 1; + } else { + return lower + presentBehavior.resultIndex( + comparator, key, list.subList(lower, upper + 1), middle - lower); + } + } + return absentBehavior.resultIndex(lower); + } + + /** + * A specification for which index to return if the list contains at least one element that + * compares as equal to the key. + */ + public enum KeyPresentBehavior { + /** + * Return the index of any list element that compares as equal to the key. No guarantees are + * made as to which index is returned, if more than one element compares as equal to the key. + */ + ANY_PRESENT { + @Override + int resultIndex( + Comparator comparator, E key, List list, int foundIndex) { + return foundIndex; + } + }, + /** + * Return the index of the last list element that compares as equal to the key. + */ + LAST_PRESENT { + @Override + int resultIndex( + Comparator comparator, E key, List list, int foundIndex) { + // Of course, we have to use binary search to find the precise + // breakpoint... + int lower = foundIndex; + int upper = list.size() - 1; + // Everything between lower and upper inclusive compares at >= 0. + while (lower < upper) { + int middle = (lower + upper + 1) >>> 1; + int c = comparator.compare(list.get(middle), key); + if (c > 0) { + upper = middle - 1; + } else { // c == 0 + lower = middle; + } + } + return lower; + } + }, + /** + * Return the index of the first list element that compares as equal to the key. + */ + FIRST_PRESENT { + @Override + int resultIndex( + Comparator comparator, E key, List list, int foundIndex) { + // Of course, we have to use binary search to find the precise + // breakpoint... + int lower = 0; + int upper = foundIndex; + // Of course, we have to use binary search to find the precise breakpoint... + // Everything between lower and upper inclusive compares at <= 0. + while (lower < upper) { + int middle = (lower + upper) >>> 1; + int c = comparator.compare(list.get(middle), key); + if (c < 0) { + lower = middle + 1; + } else { // c == 0 + upper = middle; + } + } + return lower; + } + }, + /** + * Return the index of the first list element that compares as greater than the key, or {@code + * list.size()} if there is no such element. + */ + FIRST_AFTER { + @Override + public int resultIndex( + Comparator comparator, E key, List list, int foundIndex) { + return LAST_PRESENT.resultIndex(comparator, key, list, foundIndex) + 1; + } + }; + + abstract int resultIndex( + Comparator comparator, E key, List list, int foundIndex); + } + + /** + * A specification for which index to return if the list contains no elements that compare as + * equal to the key. + */ + public enum KeyAbsentBehavior { + /** + * Return the index of the next higher element in the list, or {@code list.size()} if there is + * no such element. + */ + NEXT_HIGHER { + @Override + public int resultIndex(int higherIndex) { + return higherIndex; + } + }, + /** + * Return {@code ~insertionIndex}, where {@code insertionIndex} is defined as the point at + * which the key would be inserted into the list: the index of the next higher element in the + * list, or {@code list.size()} if there is no such element. + *

+ *

Note that the return value will be {@code >= 0} if and only if there is an element of the + * list that compares as equal to the key. + *

+ *

This is equivalent to the behavior of + * {@link Collections#binarySearch(List, Object)} when the key isn't present, since + * {@code ~insertionIndex} is equal to {@code -1 - insertionIndex}. + */ + INVERTED_INSERTION_INDEX { + @Override + public int resultIndex(int higherIndex) { + return ~higherIndex; + } + }; + + abstract int resultIndex(int higherIndex); + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/SortedSetMultimap.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/SortedSetMultimap.java new file mode 100644 index 0000000000..380ddfa6c7 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/SortedSetMultimap.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Collection; +import java.util.Comparator; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; + +/** + * A {@code SetMultimap} whose set of values for a given key are kept sorted; + * that is, they comprise a {@link SortedSet}. It cannot hold duplicate + * key-value pairs; adding a key-value pair that's already in the multimap has + * no effect. This interface does not specify the ordering of the multimap's + * keys. See the {@link Multimap} documentation for information common to all + * multimaps. + *

+ *

The {@link #get}, {@link #removeAll}, and {@link #replaceValues} methods + * each return a {@link SortedSet} of values, while {@link Multimap#entries()} + * returns a {@link Set} of map entries. Though the method signature doesn't say + * so explicitly, the map returned by {@link #asMap} has {@code SortedSet} + * values. + *

+ *

See the Guava User Guide article on + * {@code Multimap}. + * + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +public interface SortedSetMultimap extends SetMultimap { + // Following Javadoc copied from Multimap. + + /** + * Returns a collection view of all values associated with a key. If no + * mappings in the multimap have the provided key, an empty collection is + * returned. + *

+ *

Changes to the returned collection will update the underlying multimap, + * and vice versa. + *

+ *

Because a {@code SortedSetMultimap} has unique sorted values for a given + * key, this method returns a {@link SortedSet}, instead of the + * {@link Collection} specified in the {@link Multimap} interface. + */ + @Override + SortedSet get(K key); + + /** + * Removes all values associated with a given key. + *

+ *

Because a {@code SortedSetMultimap} has unique sorted values for a given + * key, this method returns a {@link SortedSet}, instead of the + * {@link Collection} specified in the {@link Multimap} interface. + */ + @Override + SortedSet removeAll(Object key); + + /** + * Returns a map view that associates each key with the corresponding values + * in the multimap. Changes to the returned map, such as element removal, will + * update the underlying multimap. The map does not support {@code setValue()} + * on its entries, {@code put}, or {@code putAll}. + *

+ *

When passed a key that is present in the map, {@code + * asMap().get(Object)} has the same behavior as {@link #get}, returning a + * live collection. When passed a key that is not present, however, {@code + * asMap().get(Object)} returns {@code null} instead of an empty collection. + *

+ *

Note: The returned map's values are guaranteed to be of type + * {@link SortedSet}. To obtain this map with the more specific generic type + * {@code Map>}, call + * {@link Multimaps#asMap(SortedSetMultimap)} instead. + */ + @Override + Map> asMap(); + + /** + * Returns the comparator that orders the multimap values, with {@code null} + * indicating that natural ordering is used. + */ + Comparator valueComparator(); +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/StandardTable.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/StandardTable.java new file mode 100644 index 0000000000..6299361a91 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/StandardTable.java @@ -0,0 +1,952 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; +import static org.glassfish.jersey.internal.guava.Predicates.alwaysTrue; +import static org.glassfish.jersey.internal.guava.Predicates.equalTo; +import static org.glassfish.jersey.internal.guava.Predicates.in; +import static org.glassfish.jersey.internal.guava.Predicates.not; + +/** + * {@link Table} implementation backed by a map that associates row keys with + * column key / value secondary maps. This class provides rapid access to + * records by the row key alone or by both keys, but not by just the column key. + *

+ *

The views returned by {@link #column}, {@link #columnKeySet()}, and {@link + * #columnMap()} have iterators that don't support {@code remove()}. Otherwise, + * all optional operations are supported. Null row keys, columns keys, and + * values are not supported. + *

+ *

Lookups by row key are often faster than lookups by column key, because + * the data is stored in a {@code Map>}. A method call like {@code + * column(columnKey).get(rowKey)} still runs quickly, since the row key is + * provided. However, {@code column(columnKey).size()} takes longer, since an + * iteration across all row keys occurs. + *

+ *

Note that this implementation is not synchronized. If multiple threads + * access this table concurrently and one of the threads modifies the table, it + * must be synchronized externally. + * + * @author Jared Levy + */ +class StandardTable extends AbstractTable implements Serializable { + private static final long serialVersionUID = 0; + private final Map> backingMap; + private final Supplier> factory; + + // Accessors + private transient Set columnKeySet; + private transient Map> rowMap; + private transient ColumnMap columnMap; + + StandardTable(Map> backingMap, + Supplier> factory) { + this.backingMap = backingMap; + this.factory = factory; + } + + @Override + public boolean contains( + Object rowKey, Object columnKey) { + return rowKey != null && columnKey != null && super.contains(rowKey, columnKey); + } + + @Override + public boolean containsColumn(Object columnKey) { + if (columnKey == null) { + return false; + } + for (Map map : backingMap.values()) { + if (Maps.safeContainsKey(map, columnKey)) { + return true; + } + } + return false; + } + + // Mutators + + @Override + public boolean containsRow(Object rowKey) { + return rowKey != null && Maps.safeContainsKey(backingMap, rowKey); + } + + @Override + public boolean containsValue(Object value) { + return value != null && super.containsValue(value); + } + + @Override + public V get(Object rowKey, Object columnKey) { + return (rowKey == null || columnKey == null) + ? null + : super.get(rowKey, columnKey); + } + + @Override + public int size() { + int size = 0; + for (Map map : backingMap.values()) { + size += map.size(); + } + return size; + } + + @Override + public void clear() { + backingMap.clear(); + } + + private Map getOrCreate(R rowKey) { + Map map = backingMap.get(rowKey); + if (map == null) { + map = factory.get(); + backingMap.put(rowKey, map); + } + return map; + } + + @Override + public V put(R rowKey, C columnKey, V value) { + checkNotNull(rowKey); + checkNotNull(columnKey); + checkNotNull(value); + return getOrCreate(rowKey).put(columnKey, value); + } + + // Views + + @Override + public V remove( + Object rowKey, Object columnKey) { + if ((rowKey == null) || (columnKey == null)) { + return null; + } + Map map = Maps.safeGet(backingMap, rowKey); + if (map == null) { + return null; + } + V value = map.remove(columnKey); + if (map.isEmpty()) { + backingMap.remove(rowKey); + } + return value; + } + + private Map removeColumn(Object column) { + Map output = new LinkedHashMap(); + Iterator>> iterator + = backingMap.entrySet().iterator(); + while (iterator.hasNext()) { + Entry> entry = iterator.next(); + V value = entry.getValue().remove(column); + if (value != null) { + output.put(entry.getKey(), value); + if (entry.getValue().isEmpty()) { + iterator.remove(); + } + } + } + return output; + } + + private boolean containsMapping( + Object rowKey, Object columnKey, Object value) { + return value != null && value.equals(get(rowKey, columnKey)); + } + + /** + * Remove a row key / column key / value mapping, if present. + */ + private boolean removeMapping(Object rowKey, Object columnKey, Object value) { + if (containsMapping(rowKey, columnKey, value)) { + remove(rowKey, columnKey); + return true; + } + return false; + } + + /** + * {@inheritDoc} + *

+ *

The set's iterator traverses the mappings for the first row, the + * mappings for the second row, and so on. + *

+ *

Each cell is an immutable snapshot of a row key / column key / value + * mapping, taken at the time the cell is returned by a method call to the + * set or its iterator. + */ + @Override + public Set> cellSet() { + return super.cellSet(); + } + + @Override + Iterator> cellIterator() { + return new CellIterator(); + } + + @Override + public Map row(R rowKey) { + return new Row(rowKey); + } + + /** + * {@inheritDoc} + *

+ *

The returned map's views have iterators that don't support + * {@code remove()}. + */ + @Override + public Map column(C columnKey) { + return new Column(columnKey); + } + + @Override + public Set rowKeySet() { + return rowMap().keySet(); + } + + /** + * {@inheritDoc} + *

+ *

The returned set has an iterator that does not support {@code remove()}. + *

+ *

The set's iterator traverses the columns of the first row, the + * columns of the second row, etc., skipping any columns that have + * appeared previously. + */ + @Override + public Set columnKeySet() { + Set result = columnKeySet; + return (result == null) ? columnKeySet = new ColumnKeySet() : result; + } + + /** + * Creates an iterator that returns each column value with duplicates + * omitted. + */ + private Iterator createColumnKeyIterator() { + return new ColumnKeyIterator(); + } + + @Override + public Map> rowMap() { + Map> result = rowMap; + return (result == null) ? rowMap = createRowMap() : result; + } + + private Map> createRowMap() { + return new RowMap(); + } + + @Override + public Map> columnMap() { + ColumnMap result = columnMap; + return (result == null) ? columnMap = new ColumnMap() : result; + } + + /** + * Abstract set whose {@code isEmpty()} returns whether the table is empty and + * whose {@code clear()} clears all table mappings. + */ + private abstract class TableSet extends Sets.ImprovedAbstractSet { + @Override + public boolean isEmpty() { + return backingMap.isEmpty(); + } + + @Override + public void clear() { + backingMap.clear(); + } + } + + private class CellIterator implements Iterator> { + final Iterator>> rowIterator + = backingMap.entrySet().iterator(); + Entry> rowEntry; + Iterator> columnIterator + = Iterators.emptyModifiableIterator(); + + @Override + public boolean hasNext() { + return rowIterator.hasNext() || columnIterator.hasNext(); + } + + @Override + public Cell next() { + if (!columnIterator.hasNext()) { + rowEntry = rowIterator.next(); + columnIterator = rowEntry.getValue().entrySet().iterator(); + } + Entry columnEntry = columnIterator.next(); + return Tables.immutableCell( + rowEntry.getKey(), columnEntry.getKey(), columnEntry.getValue()); + } + + @Override + public void remove() { + columnIterator.remove(); + if (rowEntry.getValue().isEmpty()) { + rowIterator.remove(); + } + } + } + + class Row extends Maps.ImprovedAbstractMap { + final R rowKey; + Map backingRowMap; + + Row(R rowKey) { + this.rowKey = checkNotNull(rowKey); + } + + Map backingRowMap() { + return (backingRowMap == null + || (backingRowMap.isEmpty() && backingMap.containsKey(rowKey))) + ? backingRowMap = computeBackingRowMap() + : backingRowMap; + } + + Map computeBackingRowMap() { + return backingMap.get(rowKey); + } + + // Call this every time we perform a removal. + void maintainEmptyInvariant() { + if (backingRowMap() != null && backingRowMap.isEmpty()) { + backingMap.remove(rowKey); + backingRowMap = null; + } + } + + @Override + public boolean containsKey(Object key) { + Map backingRowMap = backingRowMap(); + return (key != null && backingRowMap != null) + && Maps.safeContainsKey(backingRowMap, key); + } + + @Override + public V get(Object key) { + Map backingRowMap = backingRowMap(); + return (key != null && backingRowMap != null) + ? Maps.safeGet(backingRowMap, key) + : null; + } + + @Override + public V put(C key, V value) { + checkNotNull(key); + checkNotNull(value); + if (backingRowMap != null && !backingRowMap.isEmpty()) { + return backingRowMap.put(key, value); + } + return StandardTable.this.put(rowKey, key, value); + } + + @Override + public V remove(Object key) { + Map backingRowMap = backingRowMap(); + if (backingRowMap == null) { + return null; + } + V result = Maps.safeRemove(backingRowMap, key); + maintainEmptyInvariant(); + return result; + } + + @Override + public void clear() { + Map backingRowMap = backingRowMap(); + if (backingRowMap != null) { + backingRowMap.clear(); + } + maintainEmptyInvariant(); + } + + @Override + protected Set> createEntrySet() { + return new RowEntrySet(); + } + + private final class RowEntrySet extends Maps.EntrySet { + @Override + Map map() { + return Row.this; + } + + @Override + public int size() { + Map map = backingRowMap(); + return (map == null) ? 0 : map.size(); + } + + @Override + public Iterator> iterator() { + final Map map = backingRowMap(); + if (map == null) { + return Iterators.emptyModifiableIterator(); + } + final Iterator> iterator = map.entrySet().iterator(); + return new Iterator>() { + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public Entry next() { + final Entry entry = iterator.next(); + return new ForwardingMapEntry() { + @Override + protected Entry delegate() { + return entry; + } + + @Override + public V setValue(V value) { + return super.setValue(checkNotNull(value)); + } + + @Override + public boolean equals(Object object) { + // TODO(user): identify why this affects GWT tests + return standardEquals(object); + } + }; + } + + @Override + public void remove() { + iterator.remove(); + maintainEmptyInvariant(); + } + }; + } + } + } + + private class Column extends Maps.ImprovedAbstractMap { + final C columnKey; + + Column(C columnKey) { + this.columnKey = checkNotNull(columnKey); + } + + @Override + public V put(R key, V value) { + return StandardTable.this.put(key, columnKey, value); + } + + @Override + public V get(Object key) { + return StandardTable.this.get(key, columnKey); + } + + @Override + public boolean containsKey(Object key) { + return StandardTable.this.contains(key, columnKey); + } + + @Override + public V remove(Object key) { + return StandardTable.this.remove(key, columnKey); + } + + /** + * Removes all {@code Column} mappings whose row key and value satisfy the + * given predicate. + */ + boolean removeFromColumnIf(Predicate> predicate) { + boolean changed = false; + Iterator>> iterator + = backingMap.entrySet().iterator(); + while (iterator.hasNext()) { + Entry> entry = iterator.next(); + Map map = entry.getValue(); + V value = map.get(columnKey); + if (value != null + && predicate.test(Maps.immutableEntry(entry.getKey(), value))) { + map.remove(columnKey); + changed = true; + if (map.isEmpty()) { + iterator.remove(); + } + } + } + return changed; + } + + @Override + Set> createEntrySet() { + return new EntrySet(); + } + + @Override + Set createKeySet() { + return new KeySet(); + } + + @Override + Collection createValues() { + return new Values(); + } + + private class EntrySet extends Sets.ImprovedAbstractSet> { + @Override + public Iterator> iterator() { + return new EntrySetIterator(); + } + + @Override + public int size() { + int size = 0; + for (Map map : backingMap.values()) { + if (map.containsKey(columnKey)) { + size++; + } + } + return size; + } + + @Override + public boolean isEmpty() { + return !containsColumn(columnKey); + } + + @Override + public void clear() { + removeFromColumnIf(alwaysTrue()); + } + + @Override + public boolean contains(Object o) { + if (o instanceof Entry) { + Entry entry = (Entry) o; + return containsMapping(entry.getKey(), columnKey, entry.getValue()); + } + return false; + } + + @Override + public boolean remove(Object obj) { + if (obj instanceof Entry) { + Entry entry = (Entry) obj; + return removeMapping(entry.getKey(), columnKey, entry.getValue()); + } + return false; + } + + @Override + public boolean retainAll(Collection c) { + return removeFromColumnIf(not(in(c))); + } + } + + private class EntrySetIterator extends AbstractIterator> { + final Iterator>> iterator + = backingMap.entrySet().iterator(); + + @Override + protected Entry computeNext() { + while (iterator.hasNext()) { + final Entry> entry = iterator.next(); + if (entry.getValue().containsKey(columnKey)) { + return new AbstractMapEntry() { + @Override + public R getKey() { + return entry.getKey(); + } + + @Override + public V getValue() { + return entry.getValue().get(columnKey); + } + + @Override + public V setValue(V value) { + return entry.getValue().put(columnKey, checkNotNull(value)); + } + }; + } + } + return endOfData(); + } + } + + private class KeySet extends Maps.KeySet { + KeySet() { + super(StandardTable.Column.this); + } + + @Override + public boolean contains(Object obj) { + return StandardTable.this.contains(obj, columnKey); + } + + @Override + public boolean remove(Object obj) { + return StandardTable.this.remove(obj, columnKey) != null; + } + + @Override + public boolean retainAll(final Collection c) { + return removeFromColumnIf(Maps.keyPredicateOnEntries(not(in(c)))); + } + } + + private class Values extends Maps.Values { + Values() { + super(StandardTable.Column.this); + } + + @Override + public boolean remove(Object obj) { + return obj != null && removeFromColumnIf(Maps.valuePredicateOnEntries(equalTo(obj))); + } + + @Override + public boolean removeAll(final Collection c) { + return removeFromColumnIf(Maps.valuePredicateOnEntries(in(c))); + } + + @Override + public boolean retainAll(final Collection c) { + return removeFromColumnIf(Maps.valuePredicateOnEntries(not(in(c)))); + } + } + } + + private class ColumnKeySet extends TableSet { + @Override + public Iterator iterator() { + return createColumnKeyIterator(); + } + + @Override + public int size() { + return Iterators.size(iterator()); + } + + @Override + public boolean remove(Object obj) { + if (obj == null) { + return false; + } + boolean changed = false; + Iterator> iterator = backingMap.values().iterator(); + while (iterator.hasNext()) { + Map map = iterator.next(); + if (map.keySet().remove(obj)) { + changed = true; + if (map.isEmpty()) { + iterator.remove(); + } + } + } + return changed; + } + + @Override + public boolean removeAll(Collection c) { + checkNotNull(c); + boolean changed = false; + Iterator> iterator = backingMap.values().iterator(); + while (iterator.hasNext()) { + Map map = iterator.next(); + // map.keySet().removeAll(c) can throw a NPE when map is a TreeMap with + // natural ordering and c contains a null. + if (Iterators.removeAll(map.keySet().iterator(), c)) { + changed = true; + if (map.isEmpty()) { + iterator.remove(); + } + } + } + return changed; + } + + @Override + public boolean retainAll(Collection c) { + checkNotNull(c); + boolean changed = false; + Iterator> iterator = backingMap.values().iterator(); + while (iterator.hasNext()) { + Map map = iterator.next(); + if (map.keySet().retainAll(c)) { + changed = true; + if (map.isEmpty()) { + iterator.remove(); + } + } + } + return changed; + } + + @Override + public boolean contains(Object obj) { + return containsColumn(obj); + } + } + + private class ColumnKeyIterator extends AbstractIterator { + // Use the same map type to support TreeMaps with comparators that aren't + // consistent with equals(). + final Map seen = factory.get(); + final Iterator> mapIterator = backingMap.values().iterator(); + Iterator> entryIterator = Iterators.emptyIterator(); + + @Override + protected C computeNext() { + while (true) { + if (entryIterator.hasNext()) { + Entry entry = entryIterator.next(); + if (!seen.containsKey(entry.getKey())) { + seen.put(entry.getKey(), entry.getValue()); + return entry.getKey(); + } + } else if (mapIterator.hasNext()) { + entryIterator = mapIterator.next().entrySet().iterator(); + } else { + return endOfData(); + } + } + } + } + + class RowMap extends Maps.ImprovedAbstractMap> { + @Override + public boolean containsKey(Object key) { + return containsRow(key); + } + + // performing cast only when key is in backing map and has the correct type + @SuppressWarnings("unchecked") + @Override + public Map get(Object key) { + return containsRow(key) ? row((R) key) : null; + } + + @Override + public Map remove(Object key) { + return (key == null) ? null : backingMap.remove(key); + } + + @Override + protected Set>> createEntrySet() { + return new EntrySet(); + } + + class EntrySet extends TableSet>> { + @Override + public Iterator>> iterator() { + return Maps.asMapEntryIterator(backingMap.keySet(), new Function>() { + @Override + public Map apply(R rowKey) { + return row(rowKey); + } + }); + } + + @Override + public int size() { + return backingMap.size(); + } + + @Override + public boolean contains(Object obj) { + if (obj instanceof Entry) { + Entry entry = (Entry) obj; + return entry.getKey() != null + && entry.getValue() instanceof Map + && Collections2.safeContains(backingMap.entrySet(), entry); + } + return false; + } + + @Override + public boolean remove(Object obj) { + if (obj instanceof Entry) { + Entry entry = (Entry) obj; + return entry.getKey() != null + && entry.getValue() instanceof Map + && backingMap.entrySet().remove(entry); + } + return false; + } + } + } + + private class ColumnMap extends Maps.ImprovedAbstractMap> { + // The cast to C occurs only when the key is in the map, implying that it + // has the correct type. + @SuppressWarnings("unchecked") + @Override + public Map get(Object key) { + return containsColumn(key) ? column((C) key) : null; + } + + @Override + public boolean containsKey(Object key) { + return containsColumn(key); + } + + @Override + public Map remove(Object key) { + return containsColumn(key) ? removeColumn(key) : null; + } + + @Override + public Set>> createEntrySet() { + return new ColumnMapEntrySet(); + } + + @Override + public Set keySet() { + return columnKeySet(); + } + + @Override + Collection> createValues() { + return new ColumnMapValues(); + } + + class ColumnMapEntrySet extends TableSet>> { + @Override + public Iterator>> iterator() { + return Maps.asMapEntryIterator(columnKeySet(), new Function>() { + @Override + public Map apply(C columnKey) { + return column(columnKey); + } + }); + } + + @Override + public int size() { + return columnKeySet().size(); + } + + @Override + public boolean contains(Object obj) { + if (obj instanceof Entry) { + Entry entry = (Entry) obj; + if (containsColumn(entry.getKey())) { + // The cast to C occurs only when the key is in the map, implying + // that it has the correct type. + @SuppressWarnings("unchecked") + C columnKey = (C) entry.getKey(); + return get(columnKey).equals(entry.getValue()); + } + } + return false; + } + + @Override + public boolean remove(Object obj) { + if (contains(obj)) { + Entry entry = (Entry) obj; + removeColumn(entry.getKey()); + return true; + } + return false; + } + + @Override + public boolean removeAll(Collection c) { + /* + * We can't inherit the normal implementation (which calls + * Sets.removeAllImpl(Set, *Collection*) because, under some + * circumstances, it attempts to call columnKeySet().iterator().remove, + * which is unsupported. + */ + checkNotNull(c); + return Sets.removeAllImpl(this, c.iterator()); + } + + @Override + public boolean retainAll(Collection c) { + checkNotNull(c); + boolean changed = false; + for (C columnKey : Lists.newArrayList(columnKeySet().iterator())) { + if (!c.contains(Maps.immutableEntry(columnKey, column(columnKey)))) { + removeColumn(columnKey); + changed = true; + } + } + return changed; + } + } + + private class ColumnMapValues extends Maps.Values> { + ColumnMapValues() { + super(StandardTable.ColumnMap.this); + } + + @Override + public boolean remove(Object obj) { + for (Entry> entry : StandardTable.ColumnMap.this.entrySet()) { + if (entry.getValue().equals(obj)) { + removeColumn(entry.getKey()); + return true; + } + } + return false; + } + + @Override + public boolean removeAll(Collection c) { + checkNotNull(c); + boolean changed = false; + for (C columnKey : Lists.newArrayList(columnKeySet().iterator())) { + if (c.contains(column(columnKey))) { + removeColumn(columnKey); + changed = true; + } + } + return changed; + } + + @Override + public boolean retainAll(Collection c) { + checkNotNull(c); + boolean changed = false; + for (C columnKey : Lists.newArrayList(columnKeySet().iterator())) { + if (!c.contains(column(columnKey))) { + removeColumn(columnKey); + changed = true; + } + } + return changed; + } + } + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/Stopwatch.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Stopwatch.java new file mode 100644 index 0000000000..c23b3db242 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Stopwatch.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.concurrent.TimeUnit; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; +import static org.glassfish.jersey.internal.guava.Preconditions.checkState; + +import static java.util.concurrent.TimeUnit.DAYS; +import static java.util.concurrent.TimeUnit.HOURS; +import static java.util.concurrent.TimeUnit.MICROSECONDS; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.MINUTES; +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; + +/** + * An object that measures elapsed time in nanoseconds. It is useful to measure + * elapsed time using this class instead of direct calls to {@link + * System#nanoTime} for a few reasons: + *

+ *

    + *
  • An alternate time source can be substituted, for testing or performance + * reasons. + *
  • As documented by {@code nanoTime}, the value returned has no absolute + * meaning, and can only be interpreted as relative to another timestamp + * returned by {@code nanoTime} at a different time. {@code Stopwatch} is a + * more effective abstraction because it exposes only these relative values, + * not the absolute ones. + *
+ *

+ *

Basic usage: + *

+ *   Stopwatch stopwatch = Stopwatch.{@link #createStarted createStarted}();
+ *   doSomething();
+ *   stopwatch.{@link #stop stop}(); // optional
+ *
+ *   long millis = stopwatch.elapsed(MILLISECONDS);
+ *
+ *   log.info("time: " + stopwatch); // formatted string like "12.3 ms"
+ *

+ *

Stopwatch methods are not idempotent; it is an error to start or stop a + * stopwatch that is already in the desired state. + *

+ *

When testing code that uses this class, use + * {@link #createUnstarted(Ticker)} or {@link #createStarted(Ticker)} to + * supply a fake or mock ticker. + * This allows you to + * simulate any valid behavior of the stopwatch. + *

+ *

Note: This class is not thread-safe. + * + * @author Kevin Bourrillion + * @since 10.0 + */ +public final class Stopwatch { + private final Ticker ticker; + private boolean isRunning; + private long startTick; + + /** + * Creates (but does not start) a new stopwatch using {@link System#nanoTime} + * as its time source. + * + * @deprecated Use {@link Stopwatch#createUnstarted()} instead. + */ + @Deprecated + private Stopwatch() { + this(Ticker.systemTicker()); + } + + /** + * Creates (but does not start) a new stopwatch, using the specified time + * source. + * + * @deprecated Use {@link Stopwatch#createUnstarted(Ticker)} instead. + */ + @Deprecated + private Stopwatch(Ticker ticker) { + this.ticker = Preconditions.checkNotNull(ticker, "ticker"); + } + + /** + * Creates (but does not start) a new stopwatch using {@link System#nanoTime} + * as its time source. + * + * @since 15.0 + */ + public static Stopwatch createUnstarted() { + return new Stopwatch(); + } + + private static TimeUnit chooseUnit(long nanos) { + if (DAYS.convert(nanos, NANOSECONDS) > 0) { + return DAYS; + } + if (HOURS.convert(nanos, NANOSECONDS) > 0) { + return HOURS; + } + if (MINUTES.convert(nanos, NANOSECONDS) > 0) { + return MINUTES; + } + if (SECONDS.convert(nanos, NANOSECONDS) > 0) { + return SECONDS; + } + if (MILLISECONDS.convert(nanos, NANOSECONDS) > 0) { + return MILLISECONDS; + } + if (MICROSECONDS.convert(nanos, NANOSECONDS) > 0) { + return MICROSECONDS; + } + return NANOSECONDS; + } + + private static String abbreviate(TimeUnit unit) { + switch (unit) { + case NANOSECONDS: + return "ns"; + case MICROSECONDS: + return "\u03bcs"; // μs + case MILLISECONDS: + return "ms"; + case SECONDS: + return "s"; + case MINUTES: + return "min"; + case HOURS: + return "h"; + case DAYS: + return "d"; + default: + throw new AssertionError(); + } + } + + /** + * Starts the stopwatch. + * + * @return this {@code Stopwatch} instance + * @throws IllegalStateException if the stopwatch is already running. + */ + public Stopwatch start() { + Preconditions.checkState(!isRunning, "This stopwatch is already running."); + isRunning = true; + startTick = ticker.read(); + return this; + } + + private long elapsedNanos() { + return isRunning ? ticker.read() - startTick : 0L; + } + + /** + * Returns a string representation of the current elapsed time. + */ + @Override + public String toString() { + long nanos = elapsedNanos(); + + TimeUnit unit = chooseUnit(nanos); + double value = (double) nanos / NANOSECONDS.convert(1, unit); + + // Too bad this functionality is not exposed as a regular method call + return String.format("%.4g %s", value, abbreviate(unit)); + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/Table.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Table.java new file mode 100644 index 0000000000..897cd4adb9 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Table.java @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Map; +import java.util.Set; + +/** + * A collection that associates an ordered pair of keys, called a row key and a + * column key, with a single value. A table may be sparse, with only a small + * fraction of row key / column key pairs possessing a corresponding value. + *

+ *

The mappings corresponding to a given row key may be viewed as a {@link + * Map} whose keys are the columns. The reverse is also available, associating a + * column with a row key / value map. Note that, in some implementations, data + * access by column key may have fewer supported operations or worse performance + * than data access by row key. + *

+ *

The methods returning collections or maps always return views of the + * underlying table. Updating the table can change the contents of those + * collections, and updating the collections will change the table. + *

+ *

All methods that modify the table are optional, and the views returned by + * the table may or may not be modifiable. When modification isn't supported, + * those methods will throw an {@link UnsupportedOperationException}. + *

+ *

See the Guava User Guide article on + * {@code Table}. + * + * @param the type of the table row keys + * @param the type of the table column keys + * @param the type of the mapped values + * @author Jared Levy + * @since 7.0 + */ +public interface Table { + // TODO(jlevy): Consider adding methods similar to ConcurrentMap methods. + + // Accessors + + /** + * Returns {@code true} if the table contains a mapping with the specified + * row and column keys. + * + * @param rowKey key of row to search for + * @param columnKey key of column to search for + */ + boolean contains(Object rowKey, Object columnKey); + + /** + * Returns {@code true} if the table contains a mapping with the specified + * row key. + * + * @param rowKey key of row to search for + */ + boolean containsRow(Object rowKey); + + /** + * Returns {@code true} if the table contains a mapping with the specified + * column. + * + * @param columnKey key of column to search for + */ + boolean containsColumn(Object columnKey); + + /** + * Returns {@code true} if the table contains a mapping with the specified + * value. + * + * @param value value to search for + */ + boolean containsValue(Object value); + + /** + * Returns the value corresponding to the given row and column keys, or + * {@code null} if no such mapping exists. + * + * @param rowKey key of row to search for + * @param columnKey key of column to search for + */ + V get(Object rowKey, Object columnKey); + + /** + * Returns the number of row key / column key / value mappings in the table. + */ + int size(); + + /** + * Compares the specified object with this table for equality. Two tables are + * equal when their cell views, as returned by {@link #cellSet}, are equal. + */ + @Override + boolean equals(Object obj); + + /** + * Returns the hash code for this table. The hash code of a table is defined + * as the hash code of its cell view, as returned by {@link #cellSet}. + */ + @Override + int hashCode(); + + // Mutators + + /** + * Removes all mappings from the table. + */ + void clear(); + + /** + * Associates the specified value with the specified keys. If the table + * already contained a mapping for those keys, the old value is replaced with + * the specified value. + * + * @param rowKey row key that the value should be associated with + * @param columnKey column key that the value should be associated with + * @param value value to be associated with the specified keys + * @return the value previously associated with the keys, or {@code null} if + * no mapping existed for the keys + */ + V put(R rowKey, C columnKey, V value); + + /** + * Copies all mappings from the specified table to this table. The effect is + * equivalent to calling {@link #put} with each row key / column key / value + * mapping in {@code table}. + * + * @param table the table to add to this table + */ + void putAll(Table table); + + /** + * Removes the mapping, if any, associated with the given keys. + * + * @param rowKey row key of mapping to be removed + * @param columnKey column key of mapping to be removed + * @return the value previously associated with the keys, or {@code null} if + * no such value existed + */ + V remove(Object rowKey, Object columnKey); + + // Views + + /** + * Returns a view of all mappings that have the given row key. For each row + * key / column key / value mapping in the table with that row key, the + * returned map associates the column key with the value. If no mappings in + * the table have the provided row key, an empty map is returned. + *

+ *

Changes to the returned map will update the underlying table, and vice + * versa. + * + * @param rowKey key of row to search for in the table + * @return the corresponding map from column keys to values + */ + Map row(R rowKey); + + /** + * Returns a view of all mappings that have the given column key. For each row + * key / column key / value mapping in the table with that column key, the + * returned map associates the row key with the value. If no mappings in the + * table have the provided column key, an empty map is returned. + *

+ *

Changes to the returned map will update the underlying table, and vice + * versa. + * + * @param columnKey key of column to search for in the table + * @return the corresponding map from row keys to values + */ + Map column(C columnKey); + + /** + * Returns a set of all row key / column key / value triplets. Changes to the + * returned set will update the underlying table, and vice versa. The cell set + * does not support the {@code add} or {@code addAll} methods. + * + * @return set of table cells consisting of row key / column key / value + * triplets + */ + Set> cellSet(); + + /** + * Returns a set of row keys that have one or more values in the table. + * Changes to the set will update the underlying table, and vice versa. + * + * @return set of row keys + */ + Set rowKeySet(); + + /** + * Returns a set of column keys that have one or more values in the table. + * Changes to the set will update the underlying table, and vice versa. + * + * @return set of column keys + */ + Set columnKeySet(); + + /** + * Returns a view that associates each row key with the corresponding map from + * column keys to values. Changes to the returned map will update this table. + * The returned map does not support {@code put()} or {@code putAll()}, or + * {@code setValue()} on its entries. + *

+ *

In contrast, the maps returned by {@code rowMap().get()} have the same + * behavior as those returned by {@link #row}. Those maps may support {@code + * setValue()}, {@code put()}, and {@code putAll()}. + * + * @return a map view from each row key to a secondary map from column keys to + * values + */ + Map> rowMap(); + + /** + * Returns a view that associates each column key with the corresponding map + * from row keys to values. Changes to the returned map will update this + * table. The returned map does not support {@code put()} or {@code putAll()}, + * or {@code setValue()} on its entries. + *

+ *

In contrast, the maps returned by {@code columnMap().get()} have the + * same behavior as those returned by {@link #column}. Those maps may support + * {@code setValue()}, {@code put()}, and {@code putAll()}. + * + * @return a map view from each column key to a secondary map from row keys to + * values + */ + Map> columnMap(); + + /** + * Row key / column key / value triplet corresponding to a mapping in a table. + * + * @since 7.0 + */ + interface Cell { + /** + * Returns the row key of this cell. + */ + R getRowKey(); + + /** + * Returns the column key of this cell. + */ + C getColumnKey(); + + /** + * Returns the value of this cell. + */ + V getValue(); + + /** + * Compares the specified object with this cell for equality. Two cells are + * equal when they have equal row keys, column keys, and values. + */ + @Override + boolean equals(Object obj); + + /** + * Returns the hash code of this cell. + *

+ *

The hash code of a table cell is equal to {@link + * Objects#hashCode}{@code (e.getRowKey(), e.getColumnKey(), e.getValue())}. + */ + @Override + int hashCode(); + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/Tables.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Tables.java new file mode 100644 index 0000000000..a58c98692c --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Tables.java @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.io.Serializable; +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; + +/** + * Provides static methods that involve a {@code Table}. + *

+ *

See the Guava User Guide article on + * {@code Tables}. + * + * @author Jared Levy + * @author Louis Wasserman + * @since 7.0 + */ +public final class Tables { + private Tables() { + } + + /** + * Returns an immutable cell with the specified row key, column key, and + * value. + *

+ *

The returned cell is serializable. + * + * @param rowKey the row key to be associated with the returned cell + * @param columnKey the column key to be associated with the returned cell + * @param value the value to be associated with the returned cell + */ + public static Table.Cell immutableCell( + R rowKey, C columnKey, V value) { + return new ImmutableCell(rowKey, columnKey, value); + } + + /** + * Creates a transposed view of a given table that flips its row and column + * keys. In other words, calling {@code get(columnKey, rowKey)} on the + * generated table always returns the same value as calling {@code + * get(rowKey, columnKey)} on the original table. Updating the original table + * changes the contents of the transposed table and vice versa. + *

+ *

The returned table supports update operations as long as the input table + * supports the analogous operation with swapped rows and columns. For + * example, in a {@link HashBasedTable} instance, {@code + * rowKeySet().iterator()} supports {@code remove()} but {@code + * columnKeySet().iterator()} doesn't. With a transposed {@link + * HashBasedTable}, it's the other way around. + */ + private static Table transpose(Table table) { + return (table instanceof TransposeTable) + ? ((TransposeTable) table).original + : new TransposeTable(table); + } + + static boolean equalsImpl(Table table, Object obj) { + if (obj == table) { + return true; + } else if (obj instanceof Table) { + Table that = (Table) obj; + return table.cellSet().equals(that.cellSet()); + } else { + return false; + } + } + + static final class ImmutableCell + extends AbstractCell implements Serializable { + private static final long serialVersionUID = 0; + private final R rowKey; + private final C columnKey; + private final V value; + + ImmutableCell( + R rowKey, C columnKey, V value) { + this.rowKey = rowKey; + this.columnKey = columnKey; + this.value = value; + } + + @Override + public R getRowKey() { + return rowKey; + } + + @Override + public C getColumnKey() { + return columnKey; + } + + @Override + public V getValue() { + return value; + } + } + + abstract static class AbstractCell implements Table.Cell { + // needed for serialization + AbstractCell() { + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof Table.Cell) { + Table.Cell other = (Table.Cell) obj; + return Objects.equals(getRowKey(), other.getRowKey()) + && Objects.equals(getColumnKey(), other.getColumnKey()) + && Objects.equals(getValue(), other.getValue()); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(getRowKey(), getColumnKey(), getValue()); + } + + @Override + public String toString() { + return "(" + getRowKey() + "," + getColumnKey() + ")=" + getValue(); + } + } + + private static class TransposeTable extends AbstractTable { + // Will cast TRANSPOSE_CELL to a type that always succeeds + private static final Function, Cell> TRANSPOSE_CELL = + new Function, Cell>() { + @Override + public Cell apply(Cell cell) { + return immutableCell( + cell.getColumnKey(), cell.getRowKey(), cell.getValue()); + } + }; + final Table original; + + TransposeTable(Table original) { + this.original = checkNotNull(original); + } + + @Override + public void clear() { + original.clear(); + } + + @Override + public Map column(R columnKey) { + return original.row(columnKey); + } + + @Override + public Set columnKeySet() { + return original.rowKeySet(); + } + + @Override + public Map> columnMap() { + return original.rowMap(); + } + + @Override + public boolean contains( + Object rowKey, Object columnKey) { + return original.contains(columnKey, rowKey); + } + + @Override + public boolean containsColumn(Object columnKey) { + return original.containsRow(columnKey); + } + + @Override + public boolean containsRow(Object rowKey) { + return original.containsColumn(rowKey); + } + + @Override + public boolean containsValue(Object value) { + return original.containsValue(value); + } + + @Override + public V get(Object rowKey, Object columnKey) { + return original.get(columnKey, rowKey); + } + + @Override + public V put(C rowKey, R columnKey, V value) { + return original.put(columnKey, rowKey, value); + } + + @Override + public void putAll(Table table) { + original.putAll(transpose(table)); + } + + @Override + public V remove(Object rowKey, Object columnKey) { + return original.remove(columnKey, rowKey); + } + + @Override + public Map row(C rowKey) { + return original.column(rowKey); + } + + @Override + public Set rowKeySet() { + return original.columnKeySet(); + } + + @Override + public Map> rowMap() { + return original.columnMap(); + } + + @Override + public int size() { + return original.size(); + } + + @SuppressWarnings("unchecked") + @Override + Iterator> cellIterator() { + return Iterators.transform(original.cellSet().iterator(), (Function) TRANSPOSE_CELL); + } + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/ThreadFactoryBuilder.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ThreadFactoryBuilder.java new file mode 100644 index 0000000000..44624a2917 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/ThreadFactoryBuilder.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2010 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicLong; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; + +/** + * A ThreadFactory builder, providing any combination of these features: + *

    + *
  • whether threads should be marked as {@linkplain Thread#setDaemon daemon} + * threads + *
  • a {@linkplain ThreadFactoryBuilder#setNameFormat naming format} + *
  • a {@linkplain Thread#setPriority thread priority} + *
  • an {@linkplain Thread#setUncaughtExceptionHandler uncaught exception + * handler} + *
  • a {@linkplain ThreadFactory#newThread backing thread factory} + *
+ *

If no backing thread factory is provided, a default backing thread factory is + * used as if by calling {@code setThreadFactory(}{@link + * Executors#defaultThreadFactory()}{@code )}. + * + * @author Kurt Alfred Kluever + * @since 4.0 + */ +public final class ThreadFactoryBuilder { + private String nameFormat = null; + private Boolean daemon = null; + private Integer priority = null; + private UncaughtExceptionHandler uncaughtExceptionHandler = null; + private ThreadFactory backingThreadFactory = null; + + /** + * Creates a new {@link ThreadFactory} builder. + */ + public ThreadFactoryBuilder() { + } + + private static ThreadFactory build(ThreadFactoryBuilder builder) { + final String nameFormat = builder.nameFormat; + final Boolean daemon = builder.daemon; + final Integer priority = builder.priority; + final UncaughtExceptionHandler uncaughtExceptionHandler = + builder.uncaughtExceptionHandler; + final ThreadFactory backingThreadFactory = + (builder.backingThreadFactory != null) + ? builder.backingThreadFactory + : Executors.defaultThreadFactory(); + final AtomicLong count = (nameFormat != null) ? new AtomicLong(0) : null; + return new ThreadFactory() { + @Override + public Thread newThread(Runnable runnable) { + Thread thread = backingThreadFactory.newThread(runnable); + if (nameFormat != null) { + thread.setName(String.format(nameFormat, count.getAndIncrement())); + } + if (daemon != null) { + thread.setDaemon(daemon); + } + if (priority != null) { + thread.setPriority(priority); + } + if (uncaughtExceptionHandler != null) { + thread.setUncaughtExceptionHandler(uncaughtExceptionHandler); + } + return thread; + } + }; + } + + /** + * Sets the naming format to use when naming threads ({@link Thread#setName}) + * which are created with this ThreadFactory. + * + * @param nameFormat a {@link String#format(String, Object...)}-compatible + * format String, to which a unique integer (0, 1, etc.) will be supplied + * as the single parameter. This integer will be unique to the built + * instance of the ThreadFactory and will be assigned sequentially. For + * example, {@code "rpc-pool-%d"} will generate thread names like + * {@code "rpc-pool-0"}, {@code "rpc-pool-1"}, {@code "rpc-pool-2"}, etc. + * @return this for the builder pattern + */ + @SuppressWarnings("ReturnValueIgnored") + public ThreadFactoryBuilder setNameFormat(String nameFormat) { + String.format(nameFormat, 0); // fail fast if the format is bad or null + this.nameFormat = nameFormat; + return this; + } + + /** + * Sets daemon or not for new threads created with this ThreadFactory. + * + * @param daemon whether or not new Threads created with this ThreadFactory + * will be daemon threads + * @return this for the builder pattern + */ + public ThreadFactoryBuilder setDaemon(boolean daemon) { + this.daemon = daemon; + return this; + } + + /** + * Sets the {@link UncaughtExceptionHandler} for new threads created with this + * ThreadFactory. + * + * @param uncaughtExceptionHandler the uncaught exception handler for new + * Threads created with this ThreadFactory + * @return this for the builder pattern + */ + public ThreadFactoryBuilder setUncaughtExceptionHandler( + UncaughtExceptionHandler uncaughtExceptionHandler) { + this.uncaughtExceptionHandler = checkNotNull(uncaughtExceptionHandler); + return this; + } + + /** + * Sets the backing {@link ThreadFactory} for new threads created with this + * ThreadFactory. Threads will be created by invoking #newThread(Runnable) on + * this backing {@link ThreadFactory}. + * + * @param backingThreadFactory the backing {@link ThreadFactory} which will + * be delegated to during thread creation. + * @return this for the builder pattern + * @see MoreExecutors + */ + public ThreadFactoryBuilder setThreadFactory( + ThreadFactory backingThreadFactory) { + this.backingThreadFactory = checkNotNull(backingThreadFactory); + return this; + } + + /** + * Returns a new thread factory using the options supplied during the building + * process. After building, it is still possible to change the options used to + * build the ThreadFactory and/or build again. State is not shared amongst + * built instances. + * + * @return the fully constructed {@link ThreadFactory} + */ + public ThreadFactory build() { + return build(this); + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/Ticker.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Ticker.java new file mode 100644 index 0000000000..fdbabb652b --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Ticker.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +/** + * A time source; returns a time value representing the number of nanoseconds elapsed since some + * fixed but arbitrary point in time. Note that most users should use {@link Stopwatch} instead of + * interacting with this class directly. + *

+ *

Warning: this interface can only be used to measure elapsed time, not wall time. + * + * @author Kevin Bourrillion + * @since 10.0 + * (mostly source-compatible since 9.0) + */ +public abstract class Ticker { + private static final Ticker SYSTEM_TICKER = new Ticker() { + @Override + public long read() { + return Platform.systemNanoTime(); + } + }; + + /** + * Constructor for use by subclasses. + */ + Ticker() { + } + + /** + * A ticker that reads the current time using {@link System#nanoTime}. + * + * @since 10.0 + */ + public static Ticker systemTicker() { + return SYSTEM_TICKER; + } + + /** + * Returns the number of nanoseconds elapsed since this ticker's fixed + * point of reference. + */ + public abstract long read(); +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/TransformedIterator.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/TransformedIterator.java new file mode 100644 index 0000000000..c5a7bfed96 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/TransformedIterator.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Iterator; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; + +/** + * An iterator that transforms a backing iterator; for internal use. This avoids + * the object overhead of constructing a {@link Function} for internal methods. + * + * @author Louis Wasserman + */ +abstract class TransformedIterator implements Iterator { + private final Iterator backingIterator; + + TransformedIterator(Iterator backingIterator) { + this.backingIterator = checkNotNull(backingIterator); + } + + abstract T transform(F from); + + @Override + public final boolean hasNext() { + return backingIterator.hasNext(); + } + + @Override + public final T next() { + return transform(backingIterator.next()); + } + + @Override + public final void remove() { + backingIterator.remove(); + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/TreeMultimap.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/TreeMultimap.java new file mode 100644 index 0000000000..4149e8a015 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/TreeMultimap.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Collection; +import java.util.Comparator; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.SortedSet; +import java.util.TreeMap; +import java.util.TreeSet; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; + +/** + * Implementation of {@code Multimap} whose keys and values are ordered by + * their natural ordering or by supplied comparators. In all cases, this + * implementation uses {@link Comparable#compareTo} or {@link + * Comparator#compare} instead of {@link Object#equals} to determine + * equivalence of instances. + *

+ *

Warning: The comparators or comparables used must be consistent + * with equals as explained by the {@link Comparable} class specification. + * Otherwise, the resulting multiset will violate the general contract of {@link + * SetMultimap}, which it is specified in terms of {@link Object#equals}. + *

+ *

The collections returned by {@code keySet} and {@code asMap} iterate + * through the keys according to the key comparator ordering or the natural + * ordering of the keys. Similarly, {@code get}, {@code removeAll}, and {@code + * replaceValues} return collections that iterate through the values according + * to the value comparator ordering or the natural ordering of the values. The + * collections generated by {@code entries}, {@code keys}, and {@code values} + * iterate across the keys according to the above key ordering, and for each + * key they iterate across the values according to the value ordering. + *

+ *

The multimap does not store duplicate key-value pairs. Adding a new + * key-value pair equal to an existing key-value pair has no effect. + *

+ *

Null keys and values are permitted (provided, of course, that the + * respective comparators support them). All optional multimap methods are + * supported, and all returned views are modifiable. + *

+ *

This class is not threadsafe when any concurrent operations update the + * multimap. Concurrent read operations will work correctly. To allow concurrent + * update operations, wrap your multimap with a call to {@link + * Multimaps#synchronizedSortedSetMultimap}. + *

+ *

See the Guava User Guide article on + * {@code Multimap}. + * + * @author Jared Levy + * @author Louis Wasserman + * @since 2.0 (imported from Google Collections Library) + */ +public class TreeMultimap extends AbstractSortedKeySortedSetMultimap { + private static final long serialVersionUID = 0; + private transient Comparator keyComparator; + private transient Comparator valueComparator; + + private TreeMultimap(Comparator keyComparator, + Comparator valueComparator) { + super(new TreeMap>(keyComparator)); + this.keyComparator = keyComparator; + this.valueComparator = valueComparator; + } + + /** + * Creates an empty {@code TreeMultimap} ordered by the natural ordering of + * its keys and values. + */ + public static + TreeMultimap create() { + return new TreeMultimap(Ordering.natural(), Ordering.natural()); + } + + /** + * {@inheritDoc} + *

+ *

Creates an empty {@code TreeSet} for a collection of values for one key. + * + * @return a new {@code TreeSet} containing a collection of values for one + * key + */ + @Override + SortedSet createCollection() { + return new TreeSet(valueComparator); + } + + @Override + Collection createCollection(K key) { + if (key == null) { + keyComparator().compare(key, key); + } + return super.createCollection(key); + } + + /** + * Returns the comparator that orders the multimap keys. + */ + private Comparator keyComparator() { + return keyComparator; + } + + /* + * The following @GwtIncompatible methods override the methods in + * AbstractSortedKeySortedSetMultimap, so GWT will fall back to the ASKSSM implementations, + * which return SortedSets and SortedMaps. + */ + + @Override + public Comparator valueComparator() { + return valueComparator; + } + + @Override + NavigableMap> backingMap() { + return (NavigableMap>) super.backingMap(); + } + + /** + * @since 14.0 (present with return type {@code SortedSet} since 2.0) + */ + @Override + public NavigableSet get(K key) { + return (NavigableSet) super.get(key); + } + + @Override + Collection unmodifiableCollectionSubclass(Collection collection) { + return Sets.unmodifiableNavigableSet((NavigableSet) collection); + } + + @Override + Collection wrapCollection(K key, Collection collection) { + return new WrappedNavigableSet(key, (NavigableSet) collection, null); + } + + /** + * {@inheritDoc} + *

+ *

Because a {@code TreeMultimap} has unique sorted keys, this method + * returns a {@link NavigableSet}, instead of the {@link java.util.Set} specified + * in the {@link Multimap} interface. + * + * @since 14.0 (present with return type {@code SortedSet} since 2.0) + */ + @Override + public NavigableSet keySet() { + return (NavigableSet) super.keySet(); + } + + @Override + NavigableSet createKeySet() { + return new NavigableKeySet(backingMap()); + } + + /** + * {@inheritDoc} + *

+ *

Because a {@code TreeMultimap} has unique sorted keys, this method + * returns a {@link NavigableMap}, instead of the {@link java.util.Map} specified + * in the {@link Multimap} interface. + * + * @since 14.0 (present with return type {@code SortedMap} since 2.0) + */ + @Override + public NavigableMap> asMap() { + return (NavigableMap>) super.asMap(); + } + + @Override + NavigableMap> createAsMap() { + return new NavigableAsMap(backingMap()); + } + + /** + * @serialData key comparator, value comparator, number of distinct keys, and + * then for each distinct key: the key, number of values for that key, and + * key values + */ + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(keyComparator()); + stream.writeObject(valueComparator()); + Serialization.writeMultimap(this, stream); + } + + @SuppressWarnings("unchecked") // reading data stored by writeObject + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + keyComparator = checkNotNull((Comparator) stream.readObject()); + valueComparator = checkNotNull((Comparator) stream.readObject()); + setMap(new TreeMap>(keyComparator)); + Serialization.populateMultimap(this, stream); + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/UncheckedExecutionException.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/UncheckedExecutionException.java new file mode 100644 index 0000000000..90c753ebbf --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/UncheckedExecutionException.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +/** + * Unchecked variant of {@link java.util.concurrent.ExecutionException}. As with + * {@code ExecutionException}, the exception's {@linkplain #getCause() cause} + * comes from a failed task, possibly run in another thread. + *

+ *

{@code UncheckedExecutionException} is intended as an alternative to + * {@code ExecutionException} when the exception thrown by a task is an + * unchecked exception. However, it may also wrap a checked exception in some + * cases. + *

+ *

When wrapping an {@code Error} from another thread, prefer {@link + * ExecutionError}. When wrapping a checked exception, prefer {@code + * ExecutionException}. + * + * @author Charles Fry + * @since 10.0 + */ +public class UncheckedExecutionException extends RuntimeException { + + private static final long serialVersionUID = 0; + + /** + * Creates a new instance with the given cause. + */ + public UncheckedExecutionException(Throwable cause) { + super(cause); + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/Uninterruptibles.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Uninterruptibles.java new file mode 100644 index 0000000000..b156dcc428 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/Uninterruptibles.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +/** + * Utilities for treating interruptible operations as uninterruptible. + * In all cases, if a thread is interrupted during such a call, the call + * continues to block until the result is available or the timeout elapses, + * and only then re-interrupts the thread. + * + * @author Anthony Zana + * @since 10.0 + */ +final class Uninterruptibles { + + // Implementation Note: As of 3-7-11, the logic for each blocking/timeout + // methods is identical, save for method being invoked. + + private Uninterruptibles() { + } + + /** + * Invokes {@code future.}{@link Future#get() get()} uninterruptibly. + * To get uninterruptibility and remove checked exceptions, see + * {@link Futures#getUnchecked}. + *

+ *

If instead, you wish to treat {@link InterruptedException} uniformly + * with other exceptions, see {@link Futures#get(Future, Class) Futures.get} + * or {@link Futures#makeChecked}. + * + * @throws ExecutionException if the computation threw an exception + * @throws CancellationException if the computation was cancelled + */ + public static V getUninterruptibly(Future future) + throws ExecutionException { + boolean interrupted = false; + try { + while (true) { + try { + return future.get(); + } catch (InterruptedException e) { + interrupted = true; + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/UnmodifiableIterator.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/UnmodifiableIterator.java new file mode 100644 index 0000000000..a4718b1b66 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/UnmodifiableIterator.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.Iterator; + +/** + * An iterator that does not support {@link #remove}. + * + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +public abstract class UnmodifiableIterator implements Iterator { + /** + * Constructor for use by subclasses. + */ + UnmodifiableIterator() { + } + + /** + * Guaranteed to throw an exception and leave the underlying data unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void remove() { + throw new UnsupportedOperationException(); + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/UnmodifiableListIterator.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/UnmodifiableListIterator.java new file mode 100644 index 0000000000..8fc4cd2296 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/UnmodifiableListIterator.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2010 The Guava Authors + * + * Licensed 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.glassfish.jersey.internal.guava; + +import java.util.ListIterator; + +/** + * A list iterator that does not support {@link #remove}, {@link #add}, or + * {@link #set}. + * + * @author Louis Wasserman + * @since 7.0 + */ +public abstract class UnmodifiableListIterator + extends UnmodifiableIterator implements ListIterator { + /** + * Constructor for use by subclasses. + */ + UnmodifiableListIterator() { + } + + /** + * Guaranteed to throw an exception and leave the underlying data unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void add(E e) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the underlying data unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void set(E e) { + throw new UnsupportedOperationException(); + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/guava/package-info.java b/core-common/src/main/java/org/glassfish/jersey/internal/guava/package-info.java new file mode 100644 index 0000000000..c73de2d5cc --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/guava/package-info.java @@ -0,0 +1,44 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +/* + * Code taken from Guava. + */ +package org.glassfish.jersey.internal.guava; diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/inject/AbstractBinder.java b/core-common/src/main/java/org/glassfish/jersey/internal/inject/AbstractBinder.java index 326f265d2b..7843020b9d 100644 --- a/core-common/src/main/java/org/glassfish/jersey/internal/inject/AbstractBinder.java +++ b/core-common/src/main/java/org/glassfish/jersey/internal/inject/AbstractBinder.java @@ -81,9 +81,20 @@ public abstract class AbstractBinder implements Binder { * @return initialized binding builder. */ public ClassBinding bind(Class serviceType) { - ClassBinding descriptor = Bindings.service(serviceType); - bindings.add(descriptor); - return descriptor; + ClassBinding binding = Bindings.service(serviceType); + bindings.add(binding); + return binding; + } + + /** + * Binds the provided binding and return the same instance. + * + * @param binding binding. + * @return the same provided binding. + */ + public Binding bind(Binding binding) { + bindings.add(binding); + return binding; } /** @@ -96,9 +107,9 @@ public ClassBinding bind(Class serviceType) { * @return initialized binding builder. */ public ClassBinding bindAsContract(Class serviceType) { - ClassBinding descriptor = Bindings.serviceAsContract(serviceType); - bindings.add(descriptor); - return descriptor; + ClassBinding binding = Bindings.serviceAsContract(serviceType); + bindings.add(binding); + return binding; } /** @@ -111,9 +122,9 @@ public ClassBinding bindAsContract(Class serviceType) { * @return initialized binding builder. */ public ClassBinding bindAsContract(GenericType serviceType) { - ClassBinding descriptor = Bindings.service(serviceType); - bindings.add(descriptor); - return descriptor; + ClassBinding binding = Bindings.service(serviceType); + bindings.add(binding); + return binding; } /** @@ -125,9 +136,9 @@ public ClassBinding bindAsContract(GenericType serviceType) { * @return initialized binding builder. */ public ClassBinding bindAsContract(Type serviceType) { - ClassBinding descriptor = Bindings.serviceAsContract(serviceType); - bindings.add(descriptor); - return descriptor; + ClassBinding binding = Bindings.serviceAsContract(serviceType); + bindings.add(binding); + return binding; } /** @@ -141,9 +152,9 @@ public ClassBinding bindAsContract(Type serviceType) { * @return initialized binding builder. */ public InstanceBinding bind(T service) { - InstanceBinding descriptor = Bindings.service(service); - bindings.add(descriptor); - return descriptor; + InstanceBinding binding = Bindings.service(service); + bindings.add(binding); + return binding; } /** @@ -196,14 +207,14 @@ public SupplierInstanceBinding bindFactory(Supplier factory) { * There is no need to provide any additional information. Other method on {@link Binding} * will be ignored. * - * @param type of the injection resolver. - * @param resolver injection resolver instance. + * @param type of the injection resolver. + * @param resolver injection resolver instance. * @return initialized binding builder. */ public InjectionResolverBinding bind(T resolver) { - InjectionResolverBinding descriptor = Bindings.injectionResolver(resolver); - bindings.add(descriptor); - return descriptor; + InjectionResolverBinding binding = Bindings.injectionResolver(resolver); + bindings.add(binding); + return binding; } /** diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/inject/InjectionManager.java b/core-common/src/main/java/org/glassfish/jersey/internal/inject/InjectionManager.java index dcc6f2f908..1b30092b54 100644 --- a/core-common/src/main/java/org/glassfish/jersey/internal/inject/InjectionManager.java +++ b/core-common/src/main/java/org/glassfish/jersey/internal/inject/InjectionManager.java @@ -45,54 +45,32 @@ import java.util.List; /** - * Interface provides the communication API between Jersey and Dependency Injection provider + * Interface provides the communication API between Jersey and Dependency Injection provider. *

- * First, the method {@link #initialize(String, Object, Binder...)} should be call to initialize DI provider - * (e.g. create underlying storage for registered services) and to do other stuff needed for successful start of DI provider. + * Lifecycle methods should be called in this order: + *

    + *
  • {@link #completeRegistration()} - notifies that Jersey bootstrap has been finished and DI provider should be ready for a runtime.
  • + *
  • {@link #shutdown()} - Jersey application has been closed and DI provider should make needed cleaning steps.
  • + *
+ *

+ * All {@code getInstance} methods can be called after {@link #completeRegistration()} method has been called because at this all + * components are bound to injection manager and ready for getting. + * In turn, {@link #shutdown()} method stops the possibility to use these methods and closes {@code InjectionManager}. * * @author Petr Bouda (petr.bouda at oracle.com) */ public interface InjectionManager { /** - * Initializes {@code InjectionManager} and underlying DI provider. The method may get the array of binders to - * register {@link Binding} them during initialization process. {@code name} and {@code parent} are not required parameters - * and can be {@code null} without the initialization exception. - * - * @param name Name of the injection manager. - * @param parent Parent object of the underlying DI provider on which new injection manager should be dependent. A specific - * DI provider checks whether the parent object is in the proper type of underlying service storage or - * a proper implementation of {@link InjectionManager}. - * @param binders Binders with descriptions to include them during initialization process. - */ - void initialize(String name, Object parent, Binder... binders); - - /** - * Initializes {@code InjectionManager} and underlying DI provider. The method may get the array of binders to - * register {@link Binding} them during initialization process. {@code parent} is not required parameters and can be - * {@code null} without the initialization exception. - * - * @param parent Parent object of the underlying DI provider on which new injection manager should be dependent. A specific - * DI provider checks whether the parent object is in the proper type of underlying service storage or - * a proper implementation of {@link InjectionManager}. - * @param binders Binders with descriptions to include them during initialization process. + * Completes {@link InjectionManager} and the underlying DI provider. All registered components are bound to injection + * manager and after an invocation of this method all components are available using e.g. {@link #getInstance(Class)}. */ - default void initialize(Object parent, Binder... binders) { - initialize(null, parent, binders); - } + void completeRegistration(); /** - * Initializes {@code InjectionManager} and underlying DI provider. The method may get the array of binders to - * register {@link Binding} them during initialization process. - - * @param binders Binders with descriptions to include them during initialization process. - */ - default void initialize(Binder... binders) { - initialize(null, null, binders); - } - - /** - * Shuts down the entire {@link InjectionManager}and underlying DI provider along with injected executors and schedulers. + * Shuts down the entire {@link InjectionManager} and the underlying DI provider. + *

+ * Shutdown phase is dedicated to make some final cleaning steps regarding underlying DI provider. */ void shutdown(); @@ -145,7 +123,7 @@ default void initialize(Binder... binders) { * @param provider object that can be registered in {@code InjectionManager}. * @throws IllegalArgumentException provider cannot be registered. */ - void register(Object provider); + void register(Object provider) throws IllegalArgumentException; /** * Tests whether the provided {@code clazz} can be registered by the implementation of the {@link InjectionManager}. diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/inject/InjectionManagerFactory.java b/core-common/src/main/java/org/glassfish/jersey/internal/inject/InjectionManagerFactory.java new file mode 100644 index 0000000000..7bc3a0935a --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/inject/InjectionManagerFactory.java @@ -0,0 +1,73 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package org.glassfish.jersey.internal.inject; + +/** + * Factory which is able to create {@link InjectionManager}. Every DI provider must create its own {@link InjectionManagerFactory} + * and register it in META-INF.services. Then the {@code InjectionManagerFactory} can be looked up and {@code InjectionManager} + * can be created. + */ +public interface InjectionManagerFactory { + + /** + * Load a new injection manager without parent and initial binder. + * + * @return initialized injection manager. + */ + InjectionManager create(); + + /** + * Load a new injection manager with parent object. + * + * @param parent injection manager parent or concrete DI specific object which is compatible with DI provider. + * @return initialized injection manager. + */ + InjectionManager create(Object parent); + + /** + * Load a new injection manager with an initial binder. + * + * @param binder an initial which is immediately registered into injection manager. + * @return initialized injection manager. + */ + // TODO: CANDIDATE TO DELETE: is used in RuntimeDelegateImpl super(...). + InjectionManager create(Binder binder); +} diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/inject/Injections.java b/core-common/src/main/java/org/glassfish/jersey/internal/inject/Injections.java index 9f5ab1b909..898bb0f535 100644 --- a/core-common/src/main/java/org/glassfish/jersey/internal/inject/Injections.java +++ b/core-common/src/main/java/org/glassfish/jersey/internal/inject/Injections.java @@ -45,9 +45,7 @@ import javax.ws.rs.WebApplicationException; -import javax.inject.Provider; - -import org.glassfish.jersey.hk2.HK2InjectionManager; +import org.glassfish.jersey.hk2.Hk2InjectionManagerFactory; /** * Injection binding utility methods. @@ -58,69 +56,44 @@ public class Injections { /** - * Create a {@link InjectionManager}. In case the {@code name} is not specified, the locator will be unnamed. - * - * @param name The name of this injection manager. Passing a {@code null} name will result in a newly created injection - * manager with a generated name. - * @param parent The parent of this injection manager. Services can be found in the parent (and all grand-parents). May be - * {@code null}. An underlying DI provider checks whether the parent is in a proper type. - * @param binders custom the {@link Binder binders}. - * @return a injection manager with all the bindings. - */ - public static InjectionManager createInjectionManager(String name, Object parent, Binder... binders) { - return _injectionManager(name, parent, binders); - } - - /** - * Create a {@link InjectionManager}. In case the {@code name} is not specified, the locator - * will be unnamed. + * Creates a {@link InjectionManager} without parent and initial binder. * - * @param binders custom the {@link Binder binders}. * @return a injection manager with all the bindings. */ - public static InjectionManager createInjectionManager(Binder... binders) { - return _injectionManager(null, null, binders); + public static InjectionManager createInjectionManager() { + return lookupInjectionManagerFactory().create(); } /** - * Create a {@link InjectionManager}. In case the {@code name} is not specified, the locator - * will be unnamed. + * Creates a {@link InjectionManager} with initial binder that is immediately registered. * - * @param name The name of this injection manager. Passing a {@code null} - * name will result in a newly created injection manager with a - * generated name. - * @param binders custom the {@link Binder binders}. + * @param binder custom the {@link Binder binder}. * @return a injection manager with all the bindings. */ - public static InjectionManager createInjectionManager(String name, Binder... binders) { - return _injectionManager(name, null, binders); + public static InjectionManager createInjectionManager(Binder binder) { + return lookupInjectionManagerFactory().create(binder); } /** - * Create an unnamed, parented {@link InjectionManager}. In case the {@code parent} injection manager is not specified, the + * Creates an unnamed, parented {@link InjectionManager}. In case the {@code parent} injection manager is not specified, the * locator will not be parented. * * @param parent The parent of this injection manager. Services can be found in the parent (and all grand-parents). May be * {@code null}. An underlying DI provider checks whether the parent is in a proper type. - * @param binders custom the {@link Binder binders}. * @return an injection manager with all the bindings. */ - public static InjectionManager createInjectionManager(Object parent, Binder... binders) { - return _injectionManager(null, parent, binders); + public static InjectionManager createInjectionManager(Object parent) { + return lookupInjectionManagerFactory().create(parent); } - private static InjectionManager _injectionManager(String name, Object parent, Binder... binders) { - Iterator iterator = ServiceLoader.load(InjectionManager.class).iterator(); - InjectionManager injectionManager; + private static InjectionManagerFactory lookupInjectionManagerFactory() { + Iterator iterator = ServiceLoader.load(InjectionManagerFactory.class).iterator(); if (iterator.hasNext()) { - injectionManager = iterator.next(); + return iterator.next(); } else { // TODO: Log that there is no explicitly configured InjectionManager, default is used. - injectionManager = new HK2InjectionManager(); + return new Hk2InjectionManagerFactory(); } - - injectionManager.initialize(name, parent, binders); - return injectionManager; } /** @@ -150,16 +123,4 @@ public static T getOrCreate(InjectionManager injectionManager, final Class instance type. - * @param injectionManager injection manager. - * @param clazz class of the instance to be provider. - * @return provider of contract class. - */ - public static Provider getProvider(final InjectionManager injectionManager, final Class clazz) { - return () -> injectionManager.getInstance(clazz); - } } diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/inject/ProviderBinder.java b/core-common/src/main/java/org/glassfish/jersey/internal/inject/ProviderBinder.java index d057304d90..54f26d7386 100644 --- a/core-common/src/main/java/org/glassfish/jersey/internal/inject/ProviderBinder.java +++ b/core-common/src/main/java/org/glassfish/jersey/internal/inject/ProviderBinder.java @@ -164,7 +164,7 @@ protected void configure() { * Bind all providers contained in {@code providerBag} (classes and instances) using injection manager. Configuration is * also committed. * - * @param componentBag bag of provider classes and instances. + * @param componentBag bag of provider classes and instances. * @param injectionManager injection manager the binder will use to bind the providers into. */ public static void bindProviders(final ComponentBag componentBag, final InjectionManager injectionManager) { @@ -172,7 +172,7 @@ public static void bindProviders(final ComponentBag componentBag, final Injectio } /** - * Bind all providers contained in {@code p roviderBag} (classes and instances) using injection manager. Configuration is + * Bind all providers contained in {@code providerBag} (classes and instances) using injection manager. Configuration is * also committed. * * @param componentBag bag of provider classes and instances. @@ -184,7 +184,8 @@ public static void bindProviders(ComponentBag componentBag, RuntimeType constrainedTo, Set> registeredClasses, InjectionManager injectionManager) { - Predicate filter = ComponentBag.EXCLUDE_EMPTY.and(ComponentBag.excludeMetaProviders(injectionManager)); + Predicate filter = ComponentBag.EXCLUDE_EMPTY + .and(ComponentBag.excludeMetaProviders(injectionManager)); /* * Check the {@code component} whether it is correctly configured for client or server {@link RuntimeType runtime}. @@ -198,8 +199,8 @@ public static void bindProviders(ComponentBag componentBag, false); /* - * These binder will be register to Bean Manager at the and of method because of a bulk registration to avoid register - * each binder alone. + * These binder will be registered to InjectionManager at the end of method because of a bulk registration to avoid a + * registration each binder alone. */ Collection binderToRegister = new ArrayList<>(); @@ -215,7 +216,7 @@ public static void bindProviders(ComponentBag componentBag, binderToRegister.addAll(createProviderBinders(providerClass, model)); } - // Bind pure provider instances except for pure meta-providers and providers with empty contract models (e.g. resources) + // Bind provider instances except for pure meta-providers and providers with empty contract models (e.g. resources) Set instances = componentBag.getInstances(filter); if (constrainedTo != null) { instances = instances.stream() diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/util/JerseyPublisher.java b/core-common/src/main/java/org/glassfish/jersey/internal/util/JerseyPublisher.java index 5abd3cd6dd..52adc6fde6 100644 --- a/core-common/src/main/java/org/glassfish/jersey/internal/util/JerseyPublisher.java +++ b/core-common/src/main/java/org/glassfish/jersey/internal/util/JerseyPublisher.java @@ -41,10 +41,14 @@ package org.glassfish.jersey.internal.util; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executor; import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.function.BiPredicate; import java.util.function.Consumer; +import org.glassfish.jersey.internal.LocalizationMessages; import org.glassfish.jersey.internal.jsr166.Flow; import org.glassfish.jersey.internal.jsr166.SubmissionPublisher; @@ -61,19 +65,65 @@ public class JerseyPublisher implements javax.ws.rs.Flow.Publisher { private static final int DEFAULT_BUFFER_CAPACITY = 256; private SubmissionPublisher submissionPublisher = new SubmissionPublisher<>(); + private final PublisherStrategy strategy; + /** * Creates a new JerseyPublisher using the {@link ForkJoinPool#commonPool()} for async delivery to subscribers * (unless it does not support a parallelism level of at least two, in which case, a new Thread is created to run - * each task), with maximum buffer capacity of {@value DEFAULT_BUFFER_CAPACITY}. + * each task), with maximum buffer capacity of {@value DEFAULT_BUFFER_CAPACITY} and default {@link PublisherStrategy}, + * which is {@link PublisherStrategy#BEST_EFFORT}. */ public JerseyPublisher() { - this(ForkJoinPool.commonPool(), DEFAULT_BUFFER_CAPACITY); + this(ForkJoinPool.commonPool(), DEFAULT_BUFFER_CAPACITY, PublisherStrategy.BEST_EFFORT); + } + + /** + * Creates a new JerseyPublisher using the {@link ForkJoinPool#commonPool()} for async delivery to subscribers + * (unless it does not support a parallelism level of at least two, in which case, a new Thread is created to run + * each task), with maximum buffer capacity of {@value DEFAULT_BUFFER_CAPACITY} and given {@link PublisherStrategy}. + * + * @param strategy publisher delivering strategy + */ + public JerseyPublisher(final PublisherStrategy strategy) { + this(ForkJoinPool.commonPool(), DEFAULT_BUFFER_CAPACITY, strategy); + } + + /** + * Creates a new JerseyPublisher using the given {@link Executor} for async delivery to subscribers, with the default + * maximum buffer size of {@value DEFAULT_BUFFER_CAPACITY} and default {@link PublisherStrategy}, which is + * {@link PublisherStrategy#BEST_EFFORT}. + * + * @param executor {@code Executor} the executor to use for async delivery, + * supporting creation of at least one independent thread + * @throws NullPointerException if executor is null + * @throws IllegalArgumentException if maxBufferCapacity not positive + */ + public JerseyPublisher(final Executor executor) { + this(executor, PublisherStrategy.BEST_EFFORT); + } + + /** + * Creates a new JerseyPublisher using the given {@link Executor} for async delivery to subscribers, with the default + * maximum buffer size of {@value DEFAULT_BUFFER_CAPACITY} and given {@link PublisherStrategy}. + * + * @param executor {@code Executor} the executor to use for async delivery, + * supporting creation of at least one independent thread + * @param strategy publisher delivering strategy + * @throws NullPointerException if executor is null + * @throws IllegalArgumentException if maxBufferCapacity not positive + */ + public JerseyPublisher(final Executor executor, final PublisherStrategy strategy) { + this.strategy = strategy; + submissionPublisher = new SubmissionPublisher<>(executor, DEFAULT_BUFFER_CAPACITY); } + + /** * Creates a new JerseyPublisher using the {@link ForkJoinPool#commonPool()} for async delivery to subscribers * (unless it does not support a parallelism level of at least two, in which case, a new Thread is created to run - * each task), with specified maximum buffer capacity. + * each task), with specified maximum buffer capacity and default {@link PublisherStrategy}, which is + * {@link PublisherStrategy#BEST_EFFORT}. * * @param maxBufferCapacity the maximum capacity for each * subscriber's buffer (the enforced capacity may be rounded up to @@ -82,63 +132,32 @@ public JerseyPublisher() { * returns the actual value) */ public JerseyPublisher(final int maxBufferCapacity) { - this(ForkJoinPool.commonPool(), maxBufferCapacity); + this(ForkJoinPool.commonPool(), maxBufferCapacity, PublisherStrategy.BEST_EFFORT); } /** - * Creates a new JerseyPublisher using the given Executor for async delivery to subscribers, with the given - * maximum buffer size for each subscriber. + * Creates a new JerseyPublisher using the given {@link Executor} for async delivery to subscribers, with the given + * maximum buffer size for each subscriber and given {@link PublisherStrategy}. * - * @param executorService {@code ExecutorService} the executor to use for async delivery, + * @param executor {@code Executor} the executor to use for async delivery, * supporting creation of at least one independent thread * @param maxBufferCapacity the maximum capacity for each * subscriber's buffer (the enforced capacity may be rounded up to * the nearest power of two and/or bounded by the largest value * supported by this implementation; method {@link #getMaxBufferCapacity} * returns the actual value) - * - * @throws NullPointerException if executor is null + * @param strategy publisher delivering strategy + * @throws NullPointerException if executor is null * @throws IllegalArgumentException if maxBufferCapacity not positive */ - public JerseyPublisher(final ExecutorService executorService, final int maxBufferCapacity) { - submissionPublisher = new SubmissionPublisher<>(executorService::execute, maxBufferCapacity); + public JerseyPublisher(final Executor executor, final int maxBufferCapacity, PublisherStrategy strategy) { + this.strategy = strategy; + submissionPublisher = new SubmissionPublisher<>(executor, maxBufferCapacity); } @Override public void subscribe(final javax.ws.rs.Flow.Subscriber subscriber) { - submissionPublisher.subscribe(new Flow.Subscriber() { - - @Override - public void onSubscribe(final Flow.Subscription subscription) { - subscriber.onSubscribe(new javax.ws.rs.Flow.Subscription() { - - @Override - public void request(final long n) { - subscription.request(n); - } - - @Override - public void cancel() { - subscription.cancel(); - } - }); - } - - @Override - public void onNext(final T item) { - subscriber.onNext(item); - } - - @Override - public void onError(final Throwable throwable) { - subscriber.onError(throwable); - } - - @Override - public void onComplete() { - subscriber.onComplete(); - } - }); + submissionPublisher.subscribe(new SubscriberWrapper(subscriber)); } /** @@ -152,7 +171,7 @@ public void onComplete() { * @throws NullPointerException if data is null * @throws java.util.concurrent.RejectedExecutionException if thrown by Executor */ - public int submit(final T data) { + private int submit(final T data) { return submissionPublisher.submit(data); } @@ -171,6 +190,147 @@ public CompletableFuture consume(final Consumer consumer) { return submissionPublisher.consume(consumer); } + /** + * Publishes the given item, if possible, to each current subscriber + * by asynchronously invoking its + * {@link javax.ws.rs.Flow.Subscriber#onNext(Object) onNext} method. + * The item may be dropped by one or more subscribers if resource + * limits are exceeded, in which case the given handler (if non-null) + * is invoked, and if it returns true, retried once. Other calls to + * methods in this class by other threads are blocked while the + * handler is invoked. Unless recovery is assured, options are + * usually limited to logging the error and/or issuing an {@link + * javax.ws.rs.Flow.Subscriber#onError(Throwable) onError} + * signal to the subscriber. + *

+ * This method returns a status indicator: If negative, it + * represents the (negative) number of drops (failed attempts to + * issue the item to a subscriber). Otherwise it is an estimate of + * the maximum lag (number of items submitted but not yet + * consumed) among all current subscribers. This value is at least + * one (accounting for this submitted item) if there are any + * subscribers, else zero. + *

+ * If the Executor for this publisher throws a + * RejectedExecutionException (or any other RuntimeException or + * Error) when attempting to asynchronously notify subscribers, or + * the drop handler throws an exception when processing a dropped + * item, then this exception is rethrown. + * + * @param item the (non-null) item to publish + * @param onDrop if non-null, the handler invoked upon a drop to a + * subscriber, with arguments of the subscriber and item; if it + * returns true, an offer is re-attempted (once) + * @return if negative, the (negative) number of drops; otherwise + * an estimate of maximum lag + * @throws IllegalStateException if closed + * @throws NullPointerException if item is null + * @throws RejectedExecutionException if thrown by Executor + */ + private int offer(T item, BiPredicate, ? super T> onDrop) { + return offer(item, 0, TimeUnit.MILLISECONDS, onDrop); + } + + /** + * Publishes the given item, if possible, to each current subscriber + * by asynchronously invoking its {@link + * javax.ws.rs.Flow.Subscriber#onNext(Object) onNext} method, + * blocking while resources for any subscription are unavailable, + * up to the specified timeout or until the caller thread is + * interrupted, at which point the given handler (if non-null) is + * invoked, and if it returns true, retried once. (The drop handler + * may distinguish timeouts from interrupts by checking whether + * the current thread is interrupted.) + * Other calls to methods in this class by other + * threads are blocked while the handler is invoked. Unless + * recovery is assured, options are usually limited to logging the + * error and/or issuing an + * {@link javax.ws.rs.Flow.Subscriber#onError(Throwable) onError} + * signal to the subscriber. + *

+ * This method returns a status indicator: If negative, it + * represents the (negative) number of drops (failed attempts to + * issue the item to a subscriber). Otherwise it is an estimate of + * the maximum lag (number of items submitted but not yet + * consumed) among all current subscribers. This value is at least + * one (accounting for this submitted item) if there are any + * subscribers, else zero. + *

+ * If the Executor for this publisher throws a + * RejectedExecutionException (or any other RuntimeException or + * Error) when attempting to asynchronously notify subscribers, or + * the drop handler throws an exception when processing a dropped + * item, then this exception is rethrown. + * + * @param item the (non-null) item to publish + * @param timeout how long to wait for resources for any subscriber + * before giving up, in units of {@code unit} + * @param unit a {@code TimeUnit} determining how to interpret the + * {@code timeout} parameter + * @param onDrop if non-null, the handler invoked upon a drop to a + * subscriber, with arguments of the subscriber and item; if it + * returns true, an offer is re-attempted (once) + * @return if negative, the (negative) number of drops; otherwise + * an estimate of maximum lag + * @throws IllegalStateException if closed + * @throws NullPointerException if item is null + * @throws RejectedExecutionException if thrown by Executor + */ + private int offer(T item, + long timeout, + TimeUnit unit, + BiPredicate, ? super T> onDrop) { + + + BiPredicate, ? super T> callback; + + callback = onDrop == null + ? this::onDrop + : (BiPredicate, T>) + (subscriber, data) -> { + onDrop.test(getSubscriberWrapper(subscriber).getWrappedSubscriber(), data); + return false; + }; + + return submissionPublisher.offer(item, timeout, unit, callback); + } + + private boolean onDrop(Flow.Subscriber subscriber, T t) { + subscriber.onError(new IllegalStateException(LocalizationMessages.SLOW_SUBSCRIBER(t))); + getSubscriberWrapper(subscriber).getSubscription().cancel(); + return false; + } + + private SubscriberWrapper getSubscriberWrapper(Flow.Subscriber subscriber) { + if (subscriber instanceof SubscriberWrapper) { + return ((SubscriberWrapper) subscriber); + } else { + throw new IllegalArgumentException(LocalizationMessages.UNKNOWN_SUBSCRIBER()); + } + + } + + /** + * Publishes the given item to all current subscribers by invoking its {@code onNext() method} using {@code Executor} + * provided as constructor parameter (or the default {@code Executor} if not provided). + *

+ * Concrete behaviour is specified by {@link PublisherStrategy} selected upon {@code JerseyPublisher} creation. + * + * @param item the (non-null) item to publish. + * @return if negative, the (negative) number of drops; otherwise an estimate of maximum lag. + * @throws IllegalStateException if closed + * @throws NullPointerException if item is null + * @throws RejectedExecutionException if thrown by {@code Executor} + */ + public int publish(T item) { + if (PublisherStrategy.BLOCKING == strategy) { + return submit(item); + } else { + // PublisherStrategy.BEST_EFFORT + return submissionPublisher.offer(item, this::onDrop); + } + } + /** * Unless already closed, issues {@code onComplete()} signals to current subscribers, and disallows subsequent * attempts to publish. Upon return, this method does NOT guarantee that all subscribers have yet @@ -225,4 +385,76 @@ public Throwable getClosedException() { public int getMaxBufferCapacity() { return submissionPublisher.getMaxBufferCapacity(); } + + public static class SubscriberWrapper implements Flow.Subscriber { + private javax.ws.rs.Flow.Subscriber subscriber; + private Flow.Subscription subscription = null; + + public SubscriberWrapper(javax.ws.rs.Flow.Subscriber subscriber) { + this.subscriber = subscriber; + } + + @Override + public void onSubscribe(final Flow.Subscription subscription) { + this.subscription = subscription; + subscriber.onSubscribe(new javax.ws.rs.Flow.Subscription() { + @Override + public void request(final long n) { + subscription.request(n); + } + + @Override + public void cancel() { + subscription.cancel(); + } + }); + } + + @Override + public void onNext(final T item) { + subscriber.onNext(item); + } + + @Override + public void onError(final Throwable throwable) { + subscriber.onError(throwable); + } + + @Override + public void onComplete() { + subscriber.onComplete(); + } + + public javax.ws.rs.Flow.Subscriber getWrappedSubscriber() { + return subscriber; + } + + /** + * Get reference to subscriber's {@link Flow.Subscription}. + * + * @return subscriber's {@code subscription} + */ + public Flow.Subscription getSubscription() { + return this.subscription; + } + } + + public enum PublisherStrategy{ + /** + * Blocking publisher strategy - tries to deliver to all subscribers regardless the cost. + * + * The thread is blocked uninterruptibly while resources for any subscriber are unavailable. + * This strategy comes with a risk of thread exhaustion, that will lead to publisher being completely blocked by slow + * or incorrectly implemented subscribers. + */ + BLOCKING, + + /** + * Best effort publisher strategy - tries to deliver to all subscribers if possible without blocking the processing. + * + * If the buffer is full, publisher invokes {@code onError()} and cancels subscription on a subscriber, that is not + * capable of read the messages at a speed sufficient to unblock the processing. + */ + BEST_EFFORT, + } } diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/util/ReflectionHelper.java b/core-common/src/main/java/org/glassfish/jersey/internal/util/ReflectionHelper.java index ba48b49f1c..9aeb5d19ef 100644 --- a/core-common/src/main/java/org/glassfish/jersey/internal/util/ReflectionHelper.java +++ b/core-common/src/main/java/org/glassfish/jersey/internal/util/ReflectionHelper.java @@ -64,11 +64,14 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; import javax.ws.rs.core.GenericEntity; import javax.ws.rs.core.GenericType; @@ -80,11 +83,6 @@ import org.osgi.framework.Bundle; import org.osgi.framework.FrameworkUtil; -import jersey.repackaged.com.google.common.base.Function; -import jersey.repackaged.com.google.common.collect.Collections2; -import jersey.repackaged.com.google.common.collect.Lists; -import jersey.repackaged.com.google.common.collect.Sets; - /** * Utility methods for Java reflection. * @@ -512,13 +510,9 @@ public static List> getGenericTypeArgumentClasses(final Type type) thro return Collections.emptyList(); } - return Lists.newArrayList(Collections2.transform(Arrays.asList(types), new Function>() { - - @Override - public Class apply(final Type input) { - return erasure(input); - } - })); + return Arrays.stream(types) + .map((Function>) ReflectionHelper::erasure) + .collect(Collectors.toList()); } /** @@ -550,13 +544,9 @@ public static List getTypeArgumentAndClass(final Type type) throw return Collections.emptyList(); } - return Lists.newArrayList(Collections2.transform(Arrays.asList(types), new Function() { - - @Override - public ClassTypePair apply(final Type input) { - return ClassTypePair.of(erasure(input), input); - } - })); + return Arrays.stream(types) + .map(type1 -> ClassTypePair.of(erasure(type1), type1)) + .collect(Collectors.toList()); } /** @@ -893,7 +883,7 @@ public Constructor run() { */ public static Collection> getAnnotationTypes(final AnnotatedElement annotatedElement, final Class metaAnnotation) { - final Set> result = Sets.newIdentityHashSet(); + final Set> result = Collections.newSetFromMap(new IdentityHashMap<>()); for (final Annotation a : annotatedElement.getAnnotations()) { final Class aType = a.annotationType(); if (metaAnnotation == null || aType.getAnnotation(metaAnnotation) != null) { diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Refs.java b/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Refs.java index d6fd6b5a21..837fef9e3a 100644 --- a/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Refs.java +++ b/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Refs.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2010-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -39,8 +39,6 @@ */ package org.glassfish.jersey.internal.util.collection; -import jersey.repackaged.com.google.common.base.MoreObjects; - /** * A collection of {@link Ref reference} factory & utility methods. * @@ -75,7 +73,9 @@ public void set(final T value) throws IllegalStateException { @Override public String toString() { - return MoreObjects.toStringHelper(this).add("reference", reference).toString(); + return "ImmutableRefImpl{" + + "reference=" + reference + + '}'; } @Override @@ -126,7 +126,9 @@ public void set(final T value) throws IllegalStateException { @Override public String toString() { - return MoreObjects.toStringHelper(this).add("reference", reference).toString(); + return "DefaultRefImpl{" + + "reference=" + reference + + '}'; } @Override @@ -178,7 +180,9 @@ public void set(final T value) throws IllegalStateException { @Override public String toString() { - return MoreObjects.toStringHelper(this).add("reference", reference).toString(); + return "ThreadSafeRefImpl{" + + "reference=" + reference + + '}'; } @Override diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Views.java b/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Views.java new file mode 100644 index 0000000000..b837d1c9a3 --- /dev/null +++ b/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Views.java @@ -0,0 +1,286 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2016-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package org.glassfish.jersey.internal.util.collection; + +import java.util.AbstractMap; +import java.util.AbstractSequentialList; +import java.util.AbstractSet; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; + +/** + * Collections utils, which provide transforming views for {@link List} and {@link Map}. + * + * @author Pavel Bucek (pavel.bucek at oracle.com) + */ +public class Views { + + private Views() { + // prevent instantiation. + } + + /** + * Create a {@link List} view, which transforms the values of provided original list. + *

+ * Removing elements from the view is supported, adding and setting isn't and + * throws {@link UnsupportedOperationException} when invoked. + * + * @param originalList original list. + * @param transformer transforming functions. + * @param transformed type parameter. + * @param type of the element from provided list. + * @return transformed list view. + */ + public static List listView(List originalList, Function transformer) { + return new AbstractSequentialList() { + @Override + public ListIterator listIterator(int index) { + return new ListIterator() { + + final ListIterator iterator = originalList.listIterator(index); + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public T next() { + return transformer.apply(iterator.next()); + } + + @Override + public boolean hasPrevious() { + return iterator.hasPrevious(); + } + + @Override + public T previous() { + return transformer.apply(iterator.previous()); + } + + @Override + public int nextIndex() { + return iterator.nextIndex(); + } + + @Override + public int previousIndex() { + return iterator.previousIndex(); + } + + @Override + public void remove() { + iterator.remove(); + } + + @Override + public void set(T t) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public void add(T t) { + throw new UnsupportedOperationException("Not supported."); + } + }; + } + + @Override + public int size() { + return originalList.size(); + } + }; + } + + /** + * Create a {@link Map} view, which transforms the values of provided original map. + *

+ * Removing elements from the map view is supported, adding and setting isn't and + * throws {@link UnsupportedOperationException} when invoked. + * + * @param originalMap provided map. + * @param valuesTransformer values transformer. + * @param key type. + * @param transformed value type. + * @param original value type. + * @return transformed map view. + */ + public static Map mapView(Map originalMap, Function valuesTransformer) { + return new AbstractMap() { + @Override + public Set> entrySet() { + return new AbstractSet>() { + + + Set> originalSet = originalMap.entrySet(); + Iterator> original = originalSet.iterator(); + + @Override + public Iterator> iterator() { + return new Iterator>() { + @Override + public boolean hasNext() { + return original.hasNext(); + } + + @Override + public Entry next() { + + Entry next = original.next(); + + return new Entry() { + @Override + public K getKey() { + return next.getKey(); + } + + @Override + public V getValue() { + return valuesTransformer.apply(next.getValue()); + } + + @Override + public V setValue(V value) { + throw new UnsupportedOperationException("Not supported."); + } + }; + } + + @Override + public void remove() { + original.remove(); + } + }; + } + + @Override + public int size() { + return originalSet.size(); + } + }; + } + }; + } + + /** + * Create a view of an union of provided sets. + *

+ * View is updated whenever any of the provided set changes. + * + * @param set1 first set. + * @param set2 second set. + * @param set item type. + * @return union view of given sets. + */ + public static Set setUnionView(final Set set1, final Set set2) { + checkNotNull(set1, "set1"); + checkNotNull(set2, "set2"); + + return new AbstractSet() { + @Override + public Iterator iterator() { + return getUnion(set1, set2).iterator(); + } + + @Override + public int size() { + return getUnion(set1, set2).size(); + } + + private Set getUnion(Set set1, Set set2) { + HashSet hashSet = new HashSet<>(set1); + hashSet.addAll(set2); + return hashSet; + } + }; + } + + /** + * Create a view of a difference of provided sets. + *

+ * View is updated whenever any of the provided set changes. + * + * @param set1 first set. + * @param set2 second set. + * @param set item type. + * @return union view of given sets. + */ + public static Set setDiffView(final Set set1, final Set set2) { + checkNotNull(set1, "set1"); + checkNotNull(set2, "set2"); + + return new AbstractSet() { + @Override + public Iterator iterator() { + return getDiff(set1, set2).iterator(); + } + + @Override + public int size() { + return getDiff(set1, set2).size(); + } + + private Set getDiff(Set set1, Set set2) { + HashSet hashSet = new HashSet<>(); + + hashSet.addAll(set1); + hashSet.addAll(set2); + + return hashSet.stream().filter(new Predicate() { + @Override + public boolean test(E e) { + return set1.contains(e) && !set2.contains(e); + } + }).collect(Collectors.toSet()); + } + }; + } +} diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/CommittingOutputStream.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/CommittingOutputStream.java index 0a2f2b77a3..631a1ab73e 100644 --- a/core-common/src/main/java/org/glassfish/jersey/message/internal/CommittingOutputStream.java +++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/CommittingOutputStream.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2010-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -47,8 +47,7 @@ import java.util.logging.Logger; import org.glassfish.jersey.internal.LocalizationMessages; - -import jersey.repackaged.com.google.common.base.Preconditions; +import org.glassfish.jersey.internal.guava.Preconditions; /** * A committing output stream with optional serialized entity buffering functionality diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/HeaderUtils.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/HeaderUtils.java index ee2de800b9..db0ed4daee 100644 --- a/core-common/src/main/java/org/glassfish/jersey/message/internal/HeaderUtils.java +++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/HeaderUtils.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2010-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -47,6 +47,7 @@ import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; import javax.ws.rs.core.AbstractMultivaluedMap; import javax.ws.rs.core.MultivaluedMap; @@ -56,11 +57,7 @@ import org.glassfish.jersey.internal.LocalizationMessages; import org.glassfish.jersey.internal.util.collection.ImmutableMultivaluedMap; import org.glassfish.jersey.internal.util.collection.StringKeyIgnoreCaseMultivaluedMap; - -import jersey.repackaged.com.google.common.base.Function; -import jersey.repackaged.com.google.common.collect.ImmutableMap; -import jersey.repackaged.com.google.common.collect.Lists; -import jersey.repackaged.com.google.common.collect.Maps; +import org.glassfish.jersey.internal.util.collection.Views; /** * Utility class supporting the processing of message headers. @@ -159,13 +156,9 @@ public static List asStringList(final List headerValues, final R delegate = rd; } - return Lists.transform(headerValues, new Function() { - @Override - public String apply(final Object input) { - return (input == null) ? "[null]" : HeaderUtils.asString(input, delegate); - } - - }); + return Views.listView(headerValues, input -> (input == null) + ? "[null]" + : HeaderUtils.asString(input, delegate)); } /** @@ -182,12 +175,7 @@ public static MultivaluedMap asStringHeaders(final MultivaluedMa final RuntimeDelegate rd = RuntimeDelegate.getInstance(); return new AbstractMultivaluedMap( - Maps.transformValues(headers, new Function, List>() { - @Override - public List apply(final List input) { - return HeaderUtils.asStringList(input, rd); - } - }) + Views.mapView(headers, input -> HeaderUtils.asStringList(input, rd)) ) { }; } @@ -207,11 +195,11 @@ public static Map asStringHeadersSingleValue(final MultivaluedMa } final RuntimeDelegate rd = RuntimeDelegate.getInstance(); - final ImmutableMap.Builder immutableMapBuilder = new ImmutableMap.Builder(); - for (final Map.Entry> entry : headers.entrySet()) { - immutableMapBuilder.put(entry.getKey(), asHeaderString(entry.getValue(), rd)); - } - return immutableMapBuilder.build(); + + return Collections.unmodifiableMap(headers.entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + entry -> asHeaderString(entry.getValue(), rd)))); } /** diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java index f19ed1e78c..4aab9c0ec5 100644 --- a/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java +++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -60,6 +60,7 @@ import java.util.Map; import java.util.Set; import java.util.StringTokenizer; +import java.util.function.Function; import javax.ws.rs.ProcessingException; import javax.ws.rs.core.Cookie; @@ -78,8 +79,6 @@ import org.glassfish.jersey.internal.PropertiesDelegate; import org.glassfish.jersey.message.MessageBodyWorkers; -import jersey.repackaged.com.google.common.base.Function; - /** * Base inbound message context implementation. * diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/MediaTypes.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/MediaTypes.java index c3f34886ce..4db0de2819 100644 --- a/core-common/src/main/java/org/glassfish/jersey/message/internal/MediaTypes.java +++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/MediaTypes.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2010-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -46,14 +46,13 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Predicate; +import java.util.stream.Collectors; import javax.ws.rs.Consumes; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; -import jersey.repackaged.com.google.common.base.Predicate; -import jersey.repackaged.com.google.common.collect.Maps; - /** * Common media types and functionality. * @@ -155,13 +154,9 @@ private MediaType getLeastSpecific(List l) { /** * Predicate for constructing filtering parameter maps that ignore the "q" and "qs" parameters. */ - private static final Predicate QUALITY_PARAM_FILTERING_PREDICATE = new Predicate() { - @Override - public boolean apply(String input) { - return !Quality.QUALITY_SOURCE_PARAMETER_NAME.equals(input) - && !Quality.QUALITY_PARAMETER_NAME.equals(input); - } - }; + private static final Predicate QUALITY_PARAM_FILTERING_PREDICATE = + input -> !Quality.QUALITY_SOURCE_PARAMETER_NAME.equals(input) + && !Quality.QUALITY_PARAMETER_NAME.equals(input); /** * Prevents initialization. @@ -364,7 +359,10 @@ public static MediaType stripQualityParams(MediaType mediaType) { } return new MediaType(mediaType.getType(), mediaType.getSubtype(), - Maps.filterKeys(oldParameters, QUALITY_PARAM_FILTERING_PREDICATE)); + oldParameters.entrySet() + .stream() + .filter(entry -> QUALITY_PARAM_FILTERING_PREDICATE.test(entry.getKey())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); } /** diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/MessageBodyFactory.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/MessageBodyFactory.java index 3baef1025e..f691c07dd0 100644 --- a/core-common/src/main/java/org/glassfish/jersey/message/internal/MessageBodyFactory.java +++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/MessageBodyFactory.java @@ -53,13 +53,17 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; import javax.ws.rs.Consumes; import javax.ws.rs.Produces; @@ -73,14 +77,16 @@ import javax.ws.rs.ext.ReaderInterceptor; import javax.ws.rs.ext.WriterInterceptor; -import javax.inject.Inject; -import javax.inject.Singleton; import javax.xml.transform.Source; +import org.glassfish.jersey.internal.BootstrapBag; +import org.glassfish.jersey.internal.BootstrapConfigurator; import org.glassfish.jersey.internal.LocalizationMessages; import org.glassfish.jersey.internal.PropertiesDelegate; -import org.glassfish.jersey.internal.inject.AbstractBinder; +import org.glassfish.jersey.internal.guava.Primitives; +import org.glassfish.jersey.internal.inject.Bindings; import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.internal.inject.InstanceBinding; import org.glassfish.jersey.internal.inject.Providers; import org.glassfish.jersey.internal.util.PropertiesHelper; import org.glassfish.jersey.internal.util.ReflectionHelper; @@ -95,11 +101,6 @@ import org.glassfish.jersey.message.ReaderModel; import org.glassfish.jersey.message.WriterModel; -import jersey.repackaged.com.google.common.base.Function; -import jersey.repackaged.com.google.common.collect.Lists; -import jersey.repackaged.com.google.common.collect.Sets; -import jersey.repackaged.com.google.common.primitives.Primitives; - /** * A factory for managing {@link MessageBodyReader}, {@link MessageBodyWriter} instances. * @@ -112,13 +113,28 @@ public class MessageBodyFactory implements MessageBodyWorkers { private static final Logger LOGGER = Logger.getLogger(MessageBodyFactory.class.getName()); /** - * Message body factory injection binder. + * Configurator which initializes and register {@link MessageBodyWorkers} instance into {@link InjectionManager} and + * {@link BootstrapBag}. + * + * @author Petr Bouda (petr.bouda at oracle.com) */ - public static class Binder extends AbstractBinder { + public static class MessageBodyWorkersConfigurator implements BootstrapConfigurator { + + private MessageBodyFactory messageBodyFactory; + + @Override + public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) { + messageBodyFactory = new MessageBodyFactory(bootstrapBag.getConfiguration()); + InstanceBinding binding = + Bindings.service(messageBodyFactory) + .to(MessageBodyWorkers.class); + injectionManager.register(binding); + } @Override - protected void configure() { - bindAsContract(MessageBodyFactory.class).to(MessageBodyWorkers.class).in(Singleton.class); + public void postInit(InjectionManager injectionManager, BootstrapBag bootstrapBag) { + messageBodyFactory.initialize(injectionManager); + bootstrapBag.setMessageBodyWorkers(messageBodyFactory); } } @@ -173,17 +189,15 @@ private int compare(List mediaTypeList1, List mediaTypeLis } }; - private final InjectionManager injectionManager; + private InjectionManager injectionManager; private final Boolean legacyProviderOrdering; - private final List readers; - private final List writers; + private List readers; + private List writers; - private final Map> readersCache = - new KeyComparatorHashMap>(MEDIA_TYPE_KEY_COMPARATOR); - private final Map> writersCache = - new KeyComparatorHashMap>(MEDIA_TYPE_KEY_COMPARATOR); + private final Map> readersCache = new KeyComparatorHashMap<>(MEDIA_TYPE_KEY_COMPARATOR); + private final Map> writersCache = new KeyComparatorHashMap<>(MEDIA_TYPE_KEY_COMPARATOR); private static final int LOOKUP_CACHE_INITIAL_CAPACITY = 32; private static final float LOOKUP_CACHE_LOAD_FACTOR = 0.75f; @@ -203,19 +217,24 @@ private int compare(List mediaTypeList1, List mediaTypeLis LOOKUP_CACHE_INITIAL_CAPACITY, LOOKUP_CACHE_LOAD_FACTOR, DataStructures.DEFAULT_CONCURENCY_LEVEL); /** - * Create new message body workers factory. + * Create a new message body factory. * - * @param injectionManager injection manager. * @param configuration configuration. Optional - can be null. */ - @Inject - public MessageBodyFactory(final InjectionManager injectionManager, final Configuration configuration) { - this.injectionManager = injectionManager; + public MessageBodyFactory(Configuration configuration) { this.legacyProviderOrdering = configuration != null && PropertiesHelper.isProperty(configuration.getProperty(MessageProperties.LEGACY_WORKERS_ORDERING)); + } + /** + * Must be initialize at the time of completed populated {@link InjectionManager}. + * + * @param injectionManager completed injection manager. + */ + public void initialize(InjectionManager injectionManager) { + this.injectionManager = injectionManager; // Initialize readers - this.readers = new ArrayList(); + this.readers = new ArrayList<>(); final Set customMbrs = Providers.getCustomProviders(injectionManager, MessageBodyReader.class); final Set mbrs = Providers.getProviders(injectionManager, MessageBodyReader.class); @@ -224,14 +243,14 @@ public MessageBodyFactory(final InjectionManager injectionManager, final Configu addReaders(readers, mbrs, false); if (legacyProviderOrdering) { - Collections.sort(readers, new LegacyWorkerComparator(MessageBodyReader.class)); + readers.sort(new LegacyWorkerComparator<>(MessageBodyReader.class)); for (final ReaderModel model : readers) { for (final MediaType mt : model.declaredTypes()) { List readerList = readersCache.get(mt); if (readerList == null) { - readerList = new ArrayList(); + readerList = new ArrayList<>(); readersCache.put(mt, readerList); } readerList.add(model.provider()); @@ -240,7 +259,7 @@ public MessageBodyFactory(final InjectionManager injectionManager, final Configu } // Initialize writers - this.writers = new ArrayList(); + this.writers = new ArrayList<>(); final Set customMbws = Providers.getCustomProviders(injectionManager, MessageBodyWriter.class); final Set mbws = Providers.getProviders(injectionManager, MessageBodyWriter.class); @@ -250,14 +269,14 @@ public MessageBodyFactory(final InjectionManager injectionManager, final Configu addWriters(writers, mbws, false); if (legacyProviderOrdering) { - Collections.sort(writers, new LegacyWorkerComparator(MessageBodyWriter.class)); + writers.sort(new LegacyWorkerComparator<>(MessageBodyWriter.class)); for (final AbstractEntityProviderModel model : writers) { for (final MediaType mt : model.declaredTypes()) { List writerList = writersCache.get(mt); if (writerList == null) { - writerList = new ArrayList(); + writerList = new ArrayList<>(); writersCache.put(mt, writerList); } writerList.add(model.provider()); @@ -280,7 +299,7 @@ public MessageBodyFactory(final InjectionManager injectionManager, final Configu private static class DeclarationDistanceComparator implements Comparator { private final Class declared; - private final Map distanceMap = new HashMap(); + private final Map distanceMap = new HashMap<>(); DeclarationDistanceComparator(final Class declared) { this.declared = declared; @@ -413,8 +432,8 @@ private Iterator> getClassHierarchyIterator(final Class classParam) return Collections.>emptyList().iterator(); } - final ArrayList> classes = new ArrayList>(); - final LinkedList> unprocessed = new LinkedList>(); + final ArrayList> classes = new ArrayList<>(); + final LinkedList> unprocessed = new LinkedList<>(); unprocessed.add(classParam); while (!unprocessed.isEmpty()) { @@ -448,7 +467,7 @@ private static class LegacyWorkerComparator implements Comparator distanceComparator; private LegacyWorkerComparator(final Class type) { - distanceComparator = new DeclarationDistanceComparator(type); + distanceComparator = new DeclarationDistanceComparator<>(type); } @Override @@ -518,18 +537,14 @@ private static void addWriters(final List models, final Set> getReaders(final MediaType mediaType) { - final Map> subSet = - new KeyComparatorLinkedHashMap>(MEDIA_TYPE_KEY_COMPARATOR); - + final Map> subSet = new KeyComparatorLinkedHashMap<>(MEDIA_TYPE_KEY_COMPARATOR); getCompatibleProvidersMap(mediaType, readers, subSet); return subSet; } @Override public Map> getWriters(final MediaType mediaType) { - final Map> subSet = - new KeyComparatorLinkedHashMap>(MEDIA_TYPE_KEY_COMPARATOR); - + final Map> subSet = new KeyComparatorLinkedHashMap<>(MEDIA_TYPE_KEY_COMPARATOR); getCompatibleProvidersMap(mediaType, writers, subSet); return subSet; } @@ -593,7 +608,7 @@ public MessageBodyReader getMessageBodyReader(final Class c, final Typ public List getMessageBodyReaderMediaTypes(final Class type, final Type genericType, final Annotation[] annotations) { - final Set readableMediaTypes = Sets.newLinkedHashSet(); + final Set readableMediaTypes = new LinkedHashSet<>(); for (final ReaderModel model : readers) { boolean readableWorker = false; @@ -612,8 +627,8 @@ public List getMessageBodyReaderMediaTypes(final Class type, } } - final List mtl = Lists.newArrayList(readableMediaTypes); - Collections.sort(mtl, MediaTypes.PARTIAL_ORDER_COMPARATOR); + final List mtl = new ArrayList<>(readableMediaTypes); + mtl.sort(MediaTypes.PARTIAL_ORDER_COMPARATOR); return mtl; } @@ -653,14 +668,14 @@ private MessageBodyReader _getMessageBodyReader(final Class c, final T final ModelLookupKey lookupKey = new ModelLookupKey(c, lookupType); List readers = mbrLookupCache.get(lookupKey); if (readers == null) { - readers = new ArrayList(); + readers = new ArrayList<>(); for (final ReaderModel model : models) { if (isCompatible(model, c, mediaType)) { readers.add(model); } } - Collections.sort(readers, new WorkerComparator(c, mediaType)); + readers.sort(new WorkerComparator<>(c, mediaType)); mbrLookupCache.put(lookupKey, readers); } @@ -773,14 +788,14 @@ private MessageBodyWriter _getMessageBodyWriter(final Class c, final T List writers = mbwLookupCache.get(lookupKey); if (writers == null) { - writers = new ArrayList(); + writers = new ArrayList<>(); for (final WriterModel model : models) { if (isCompatible(model, c, mediaType)) { writers.add(model); } } - Collections.sort(writers, new WorkerComparator(c, mediaType)); + writers.sort(new WorkerComparator<>(c, mediaType)); mbwLookupCache.put(lookupKey, writers); } @@ -869,7 +884,7 @@ private static void getCompatibleProvidersList( final List> set, final Map> subSet) { - final List providers = new ArrayList(); + final List providers = new ArrayList<>(); for (final AbstractEntityProviderModel model : set) { if (model.declaredTypes().contains(mediaType)) { @@ -885,7 +900,7 @@ private static void getCompatibleProvidersList( @Override @SuppressWarnings("unchecked") public List getMessageBodyWriterMediaTypes(final Class c, final Type t, final Annotation[] as) { - final Set writeableMediaTypes = Sets.newLinkedHashSet(); + final Set writeableMediaTypes = new LinkedHashSet<>(); for (final WriterModel model : writers) { boolean writeableWorker = false; @@ -904,23 +919,17 @@ public List getMessageBodyWriterMediaTypes(final Class c, final Ty } } - final List mtl = Lists.newArrayList(writeableMediaTypes); - Collections.sort(mtl, MediaTypes.PARTIAL_ORDER_COMPARATOR); + final List mtl = new ArrayList<>(writeableMediaTypes); + mtl.sort(MediaTypes.PARTIAL_ORDER_COMPARATOR); return mtl; } - private static final Function MODEL_TO_WRITER = - new Function() { - @Override - public MessageBodyWriter apply(final WriterModel input) { - return input.provider(); - } - }; + private static final Function MODEL_TO_WRITER = AbstractEntityProviderModel::provider; @Override @SuppressWarnings("unchecked") public List getMessageBodyWritersForType(final Class type) { - return Lists.transform(getWritersModelsForType(type), MODEL_TO_WRITER); + return getWritersModelsForType(type).stream().map(MODEL_TO_WRITER).collect(Collectors.toList()); } @Override @@ -933,7 +942,7 @@ public List getWritersModelsForType(final Class type) { } private List processMessageBodyWritersForType(final Class clazz) { - final List suitableWriters = Lists.newArrayList(); + final List suitableWriters = new ArrayList<>(); if (Response.class.isAssignableFrom(clazz)) { suitableWriters.addAll(writers); @@ -950,7 +959,7 @@ private List processMessageBodyWritersForType(final Class clazz) } } // Type -> Writer. - Collections.sort(suitableWriters, WORKER_BY_TYPE_COMPARATOR); + suitableWriters.sort(WORKER_BY_TYPE_COMPARATOR); mbwTypeLookupCache.put(clazz, suitableWriters); // Type -> MediaType. @@ -979,28 +988,22 @@ public List getMessageBodyReaderMediaTypesByType(final Class type) private static List getMessageBodyWorkersMediaTypesByType( final List> workerModels) { - final Set mediaTypeSet = Sets.newHashSet(); + final Set mediaTypeSet = new HashSet<>(); for (final AbstractEntityProviderModel model : workerModels) { mediaTypeSet.addAll(model.declaredTypes()); } - final List mediaTypes = Lists.newArrayList(mediaTypeSet); - Collections.sort(mediaTypes, MediaTypes.PARTIAL_ORDER_COMPARATOR); + final List mediaTypes = new ArrayList<>(mediaTypeSet); + mediaTypes.sort(MediaTypes.PARTIAL_ORDER_COMPARATOR); return mediaTypes; } - private static final Function MODEL_TO_READER = - new Function() { - @Override - public MessageBodyReader apply(final ReaderModel input) { - return input.provider(); - } - }; + private static final Function MODEL_TO_READER = AbstractEntityProviderModel::provider; @Override @SuppressWarnings("unchecked") public List getMessageBodyReadersForType(final Class type) { - return Lists.transform(getReaderModelsForType(type), MODEL_TO_READER); + return getReaderModelsForType(type).stream().map(MODEL_TO_READER).collect(Collectors.toList()); } @Override @@ -1013,7 +1016,7 @@ public List getReaderModelsForType(final Class type) { } private List processMessageBodyReadersForType(final Class clazz) { - final List suitableReaders = Lists.newArrayList(); + final List suitableReaders = new ArrayList<>(); final Class wrapped = Primitives.wrap(clazz); for (final ReaderModel reader : readers) { @@ -1025,7 +1028,7 @@ private List processMessageBodyReadersForType(final Class clazz) } // Type -> Writer. - Collections.sort(suitableReaders, WORKER_BY_TYPE_COMPARATOR); + suitableReaders.sort(WORKER_BY_TYPE_COMPARATOR); mbrTypeLookupCache.put(clazz, suitableReaders); // Type -> MediaType. diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundJaxrsResponse.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundJaxrsResponse.java index 89cf2a1d38..a3513ba8d0 100644 --- a/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundJaxrsResponse.java +++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundJaxrsResponse.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2016 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -69,8 +69,6 @@ import org.glassfish.jersey.internal.LocalizationMessages; -import jersey.repackaged.com.google.common.base.MoreObjects; - /** * An outbound JAX-RS response message. * @@ -307,14 +305,12 @@ public MultivaluedMap getMetadata() { @Override public String toString() { - return MoreObjects - .toStringHelper(this) - .add("status", status.getStatusCode()) - .add("reason", status.getReasonPhrase()) - .add("hasEntity", context.hasEntity()) - .add("closed", closed) - .add("buffered", buffered) - .toString(); + return "OutboundJaxrsResponse{" + + "status=" + status.getStatusCode() + + ", reason=" + status.getReasonPhrase() + + ", hasEntity=" + context.hasEntity() + + ", closed=" + closed + + ", buffered=" + buffered + "}"; } /** diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java index c53052b4fb..a049e5fdf9 100644 --- a/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java +++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2016 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -54,8 +54,10 @@ import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; import javax.ws.rs.ProcessingException; import javax.ws.rs.core.Configuration; @@ -74,10 +76,6 @@ import org.glassfish.jersey.internal.LocalizationMessages; import org.glassfish.jersey.internal.util.ReflectionHelper; -import jersey.repackaged.com.google.common.base.Function; -import jersey.repackaged.com.google.common.collect.Collections2; -import jersey.repackaged.com.google.common.collect.Lists; - /** * Base outbound message context implementation. * @@ -324,12 +322,11 @@ public List getAcceptableMediaTypes() { if (conversionApplied) { // cache converted - headers.put(HttpHeaders.ACCEPT, Lists.transform(result, new Function() { - @Override - public Object apply(MediaType input) { - return input; - } - })); + // cache converted + headers.put(HttpHeaders.ACCEPT, + result.stream() + .map((Function) mediaType -> mediaType) + .collect(Collectors.toList())); } return Collections.unmodifiableList(result); @@ -357,15 +354,10 @@ public List getAcceptableLanguages() { } else { conversionApplied = true; try { - result.addAll(Lists.transform( - HttpHeaderReader.readAcceptLanguage(HeaderUtils.asString(value, rd)), - new Function() { - - @Override - public Locale apply(AcceptableLanguageTag input) { - return input.getAsLocale(); - } - })); + result.addAll(HttpHeaderReader.readAcceptLanguage(HeaderUtils.asString(value, rd)) + .stream() + .map(LanguageTag::getAsLocale) + .collect(Collectors.toList())); } catch (java.text.ParseException e) { throw exception(HttpHeaders.ACCEPT_LANGUAGE, value, e); } @@ -374,12 +366,10 @@ public Locale apply(AcceptableLanguageTag input) { if (conversionApplied) { // cache converted - headers.put(HttpHeaders.ACCEPT_LANGUAGE, Lists.transform(result, new Function() { - @Override - public Object apply(Locale input) { - return input; - } - })); + headers.put(HttpHeaders.ACCEPT_LANGUAGE, + result.stream() + .map((Function) locale -> locale) + .collect(Collectors.toList())); } return Collections.unmodifiableList(result); @@ -583,13 +573,10 @@ public Set getLinks() { if (conversionApplied) { // cache converted - headers.put(HttpHeaders.LINK, new ArrayList(Collections2 - .transform(result, new Function() { - @Override - public Object apply(Link input) { - return input; - } - }))); + headers.put(HttpHeaders.LINK, + result.stream() + .map((Function) link -> link) + .collect(Collectors.toList())); } return Collections.unmodifiableSet(result); diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/VariantSelector.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/VariantSelector.java index 53c7fdaab8..17c09b5814 100644 --- a/core-common/src/main/java/org/glassfish/jersey/message/internal/VariantSelector.java +++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/VariantSelector.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2011-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -46,6 +46,7 @@ import java.util.List; import java.util.Locale; import java.util.Set; +import java.util.stream.Collectors; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; @@ -53,9 +54,6 @@ import org.glassfish.jersey.internal.util.collection.Ref; -import jersey.repackaged.com.google.common.base.Function; -import jersey.repackaged.com.google.common.collect.Lists; - /** * Utility for selecting variant that best matches request from a list of variants. * @@ -355,12 +353,9 @@ public static List selectVariants(final InboundMessageContext context, if (!varyValue.isEmpty()) { varyHeaderValue.set(varyValue); } - return Lists.transform(vhs, new Function() { - @Override - public Variant apply(final VariantHolder holder) { - return holder.v; - } - }); + return vhs.stream() + .map(variantHolder -> variantHolder.v) + .collect(Collectors.toList()); } } } diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/WriterInterceptorExecutor.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/WriterInterceptorExecutor.java index 594f678883..b3ce84c547 100644 --- a/core-common/src/main/java/org/glassfish/jersey/message/internal/WriterInterceptorExecutor.java +++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/WriterInterceptorExecutor.java @@ -49,6 +49,8 @@ import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import javax.ws.rs.ProcessingException; import javax.ws.rs.WebApplicationException; @@ -64,8 +66,6 @@ import org.glassfish.jersey.internal.inject.InjectionManagerSupplier; import org.glassfish.jersey.message.MessageBodyWorkers; -import jersey.repackaged.com.google.common.collect.Lists; - /** * Represents writer interceptor chain executor for both client and server side. * It constructs wrapped interceptor chain and invokes it. At the end of the chain @@ -128,7 +128,8 @@ public WriterInterceptorExecutor(final Object entity, final Class rawType, this.outputStream = entityStream; this.injectionManager = injectionManager; - final List effectiveInterceptors = Lists.newArrayList(writerInterceptors); + final List effectiveInterceptors = StreamSupport.stream(writerInterceptors.spliterator(), false) + .collect(Collectors.toList()); effectiveInterceptors.add(new TerminalWriterInterceptor(workers)); this.iterator = effectiveInterceptors.iterator(); diff --git a/core-common/src/main/java/org/glassfish/jersey/model/ContractProvider.java b/core-common/src/main/java/org/glassfish/jersey/model/ContractProvider.java index 1a045103b1..59476ddefc 100644 --- a/core-common/src/main/java/org/glassfish/jersey/model/ContractProvider.java +++ b/core-common/src/main/java/org/glassfish/jersey/model/ContractProvider.java @@ -43,14 +43,15 @@ import java.lang.annotation.Annotation; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; +import java.util.IdentityHashMap; import java.util.Map; import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; import javax.inject.Singleton; -import jersey.repackaged.com.google.common.collect.Maps; -import jersey.repackaged.com.google.common.collect.Sets; - /** * Jersey contract provider model. * @@ -92,9 +93,9 @@ public static final class Builder { private Class implementationClass = null; private Class scope = null; - private Map, Integer> contracts = Maps.newHashMap(); + private Map, Integer> contracts = new HashMap<>(); private int defaultPriority = NO_PRIORITY; - private Set> nameBindings = Sets.newIdentityHashSet(); + private Set> nameBindings = Collections.newSetFromMap(new IdentityHashMap<>()); private Builder(Class implementationClass) { this.implementationClass = implementationClass; @@ -236,12 +237,13 @@ public ContractProvider build() { final Map, Integer> _contracts = (contracts.isEmpty()) ? Collections., Integer>emptyMap() - : Maps.transformEntries(contracts, new Maps.EntryTransformer, Integer, Integer>() { - @Override - public Integer transformEntry(final Class contract, final Integer priority) { - return (priority != NO_PRIORITY) ? priority : defaultPriority; - } - }); + : contracts.entrySet() + .stream() + .collect(Collectors.toMap((Function, Integer>, Class>) Map.Entry::getKey, + classIntegerEntry -> { + Integer priority = classIntegerEntry.getValue(); + return (priority != NO_PRIORITY) ? priority : defaultPriority; + })); final Set> bindings = (nameBindings.isEmpty()) ? Collections.>emptySet() : Collections.unmodifiableSet(nameBindings); diff --git a/core-common/src/main/java/org/glassfish/jersey/model/internal/CommonConfig.java b/core-common/src/main/java/org/glassfish/jersey/model/internal/CommonConfig.java index 84108f0c69..1521d8dc06 100644 --- a/core-common/src/main/java/org/glassfish/jersey/model/internal/CommonConfig.java +++ b/core-common/src/main/java/org/glassfish/jersey/model/internal/CommonConfig.java @@ -210,13 +210,13 @@ public int hashCode() { public CommonConfig(final RuntimeType type, final Predicate registrationStrategy) { this.type = type; - this.properties = new HashMap(); + this.properties = new HashMap<>(); this.immutablePropertiesView = Collections.unmodifiableMap(properties); this.immutablePropertyNames = Collections.unmodifiableCollection(properties.keySet()); this.componentBag = ComponentBag.newInstance(registrationStrategy); - this.newFeatureRegistrations = new LinkedList(); + this.newFeatureRegistrations = new LinkedList<>(); this.enabledFeatureClasses = Collections.newSetFromMap(new IdentityHashMap<>()); this.enabledFeatures = new HashSet<>(); @@ -232,7 +232,7 @@ public CommonConfig(final RuntimeType type, final Predicate re public CommonConfig(final CommonConfig config) { this.type = config.type; - this.properties = new HashMap(config.properties.size()); + this.properties = new HashMap<>(config.properties.size()); this.immutablePropertiesView = Collections.unmodifiableMap(this.properties); this.immutablePropertyNames = Collections.unmodifiableCollection(this.properties.keySet()); @@ -324,7 +324,7 @@ public boolean isRegistered(final Class componentClass) { @Override public Map, Integer> getContracts(final Class componentClass) { final ContractProvider model = componentBag.getModel(componentClass); - return (model == null) ? Collections., Integer>emptyMap() : model.getContractMap(); + return (model == null) ? Collections.emptyMap() : model.getContractMap(); } @Override @@ -576,10 +576,11 @@ private void checkComponentClassNotNull(final Class componentClass) { /** * Configure {@link AutoDiscoverable auto-discoverables} in the injection manager. * - * @param injectionManager locator in which the auto-discoverables should be configured. + * @param injectionManager injection manager in which the auto-discoverables should be configured. * @param forcedOnly defines whether all or only forced auto-discoverables should be configured. */ - public void configureAutoDiscoverableProviders(final InjectionManager injectionManager, final boolean forcedOnly) { + public void configureAutoDiscoverableProviders(final InjectionManager injectionManager, + final boolean forcedOnly) { // Check whether meta providers have been initialized for a config this config has been loaded from. if (!disableMetaProviderConfiguration) { final Set providers = new TreeSet<>((o1, o2) -> { @@ -622,7 +623,7 @@ public void configureAutoDiscoverableProviders(final InjectionManager injectionM /** * Configure binders in the injection manager and enable JAX-RS features. * - * @param injectionManager locator in which the binders and features should be configured. + * @param injectionManager injection manager in which the binders and features should be configured. */ public void configureMetaProviders(InjectionManager injectionManager) { // First, configure existing binders diff --git a/core-common/src/main/java/org/glassfish/jersey/model/internal/ComponentBag.java b/core-common/src/main/java/org/glassfish/jersey/model/internal/ComponentBag.java index 4c375af0f2..77ac129214 100644 --- a/core-common/src/main/java/org/glassfish/jersey/model/internal/ComponentBag.java +++ b/core-common/src/main/java/org/glassfish/jersey/model/internal/ComponentBag.java @@ -124,10 +124,10 @@ public static Predicate excludeMetaProviders(InjectionManager /** * A filtering strategy that includes only models that contain contract registrable by - * {@link org.glassfish.jersey.internal.inject.InjectionManager}. + * {@link InjectionManager}. *

* This filter predicate returns {@code true} for all {@link org.glassfish.jersey.model.ContractProvider contract provider models} - * that represent an object which can be registered using specific {@link org.glassfish.jersey.internal.inject.InjectionManager} + * that represent an object which can be registered using specific {@link InjectionManager} * contract. *

*/ diff --git a/core-common/src/main/java/org/glassfish/jersey/model/internal/ManagedObjectsFinalizer.java b/core-common/src/main/java/org/glassfish/jersey/model/internal/ManagedObjectsFinalizer.java index ae6156c042..f9264a6148 100644 --- a/core-common/src/main/java/org/glassfish/jersey/model/internal/ManagedObjectsFinalizer.java +++ b/core-common/src/main/java/org/glassfish/jersey/model/internal/ManagedObjectsFinalizer.java @@ -65,11 +65,20 @@ @Singleton public class ManagedObjectsFinalizer { - @Inject - private InjectionManager injectionManager; + private final InjectionManager injectionManager; private final Set managedObjects = new HashSet<>(); + /** + * Creates a new instance of {@link ManagedObjectsFinalizer}. + * + * @param injectionManager injection manager call {@code preDestroy} on managed objects. + */ + @Inject + public ManagedObjectsFinalizer(final InjectionManager injectionManager) { + this.injectionManager = injectionManager; + } + /** * Register an object for invocation of its {@link PreDestroy} method. * It will be invoked when the injection manager is shut down. diff --git a/core-common/src/main/java/org/glassfish/jersey/process/internal/RequestScope.java b/core-common/src/main/java/org/glassfish/jersey/process/internal/RequestScope.java index d20318b856..c6c228c81f 100644 --- a/core-common/src/main/java/org/glassfish/jersey/process/internal/RequestScope.java +++ b/core-common/src/main/java/org/glassfish/jersey/process/internal/RequestScope.java @@ -41,6 +41,7 @@ package org.glassfish.jersey.process.internal; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicInteger; @@ -49,21 +50,17 @@ import javax.inject.Singleton; -import org.glassfish.jersey.hk2.RequestContext; +import org.glassfish.jersey.internal.BootstrapBag; +import org.glassfish.jersey.internal.BootstrapConfigurator; import org.glassfish.jersey.internal.Errors; -import org.glassfish.jersey.internal.inject.AbstractBinder; +import org.glassfish.jersey.internal.inject.Bindings; import org.glassfish.jersey.internal.inject.ForeignDescriptor; +import org.glassfish.jersey.internal.inject.InjectionManager; import org.glassfish.jersey.internal.util.ExtendedLogger; import org.glassfish.jersey.internal.util.LazyUid; import org.glassfish.jersey.internal.util.Producer; -import org.glassfish.hk2.api.Context; -import org.glassfish.hk2.api.TypeLiteral; - -import jersey.repackaged.com.google.common.base.MoreObjects; -import jersey.repackaged.com.google.common.collect.Sets; - -import static jersey.repackaged.com.google.common.base.Preconditions.checkState; +import static org.glassfish.jersey.internal.guava.Preconditions.checkState; /** * Scopes a single request/response processing execution on a single thread. @@ -156,13 +153,17 @@ public void shutdown() { } /** - * Request scope injection binder. + * Configurator which initializes and register {@link RequestScope} instance int {@link InjectionManager} and + * {@link BootstrapBag}. + * + * @author Petr Bouda (petr.bouda at oracle.com) */ - public static class Binder extends AbstractBinder { + public static class RequestScopeConfigurator implements BootstrapConfigurator { @Override - protected void configure() { - bind(new RequestScope()).to(RequestScope.class); + public void init(InjectionManager injectionManagerFactory, BootstrapBag bootstrapBag) { + bootstrapBag.setRequestScope(new RequestScope()); + injectionManagerFactory.register(Bindings.service(bootstrapBag.getRequestScope()).to(RequestScope.class)); } } @@ -508,9 +509,7 @@ public boolean contains(ForeignDescriptor provider) { public void release() { if (referenceCounter.decrementAndGet() < 1) { try { - for (ForeignDescriptor descriptor : Sets.newHashSet(store.keySet())) { - remove(descriptor); - } + new HashSet<>(store.keySet()).forEach(this::remove); } finally { logger.debugLog("Released scope instance {0}", this); } @@ -519,8 +518,11 @@ public void release() { @Override public String toString() { - return MoreObjects.toStringHelper(this).add("id", id.value()).add("referenceCounter", referenceCounter.get()) - .add("store size", store.size()).toString(); + return "Instance{" + + "id=" + id + + ", referenceCounter=" + referenceCounter + + ", store size=" + store.size() + + '}'; } } } diff --git a/core-common/src/main/java/org/glassfish/jersey/process/internal/Stage.java b/core-common/src/main/java/org/glassfish/jersey/process/internal/Stage.java index 5acb0b66bb..21af17dbe3 100644 --- a/core-common/src/main/java/org/glassfish/jersey/process/internal/Stage.java +++ b/core-common/src/main/java/org/glassfish/jersey/process/internal/Stage.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2014 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -39,7 +39,7 @@ */ package org.glassfish.jersey.process.internal; -import jersey.repackaged.com.google.common.base.Function; +import java.util.function.Function; /** * Data processing stage that can be used to create dynamic data processing chains. diff --git a/core-common/src/main/java/org/glassfish/jersey/process/internal/Stages.java b/core-common/src/main/java/org/glassfish/jersey/process/internal/Stages.java index ce857e78b9..86c6dda3ea 100644 --- a/core-common/src/main/java/org/glassfish/jersey/process/internal/Stages.java +++ b/core-common/src/main/java/org/glassfish/jersey/process/internal/Stages.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2010-2014 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -41,12 +41,11 @@ import java.util.Deque; import java.util.LinkedList; +import java.util.function.Function; import org.glassfish.jersey.internal.util.collection.Ref; import org.glassfish.jersey.process.Inflector; -import jersey.repackaged.com.google.common.base.Function; - /** * A stage-related collection of utility methods. * diff --git a/core-common/src/main/java/org/glassfish/jersey/spi/AbstractThreadPoolProvider.java b/core-common/src/main/java/org/glassfish/jersey/spi/AbstractThreadPoolProvider.java index 74f2ff5cc2..b13670a338 100644 --- a/core-common/src/main/java/org/glassfish/jersey/spi/AbstractThreadPoolProvider.java +++ b/core-common/src/main/java/org/glassfish/jersey/spi/AbstractThreadPoolProvider.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -53,14 +53,13 @@ import java.util.logging.Logger; import org.glassfish.jersey.internal.LocalizationMessages; +import org.glassfish.jersey.internal.guava.ThreadFactoryBuilder; import org.glassfish.jersey.internal.util.ExtendedLogger; import org.glassfish.jersey.internal.util.collection.LazyValue; import org.glassfish.jersey.internal.util.collection.Value; import org.glassfish.jersey.internal.util.collection.Values; import org.glassfish.jersey.process.JerseyProcessingUncaughtExceptionHandler; -import jersey.repackaged.com.google.common.util.concurrent.ThreadFactoryBuilder; - /** * Abstract thread pool executor provider. *

@@ -81,8 +80,7 @@ * @author Marek Potociar (marek.potociar at oracle.com) * @since 2.18 */ -// TODO implement AutoCloseable once switched to Java SE 7+ -public abstract class AbstractThreadPoolProvider { +public abstract class AbstractThreadPoolProvider implements AutoCloseable { private static final ExtendedLogger LOGGER = new ExtendedLogger( Logger.getLogger(AbstractThreadPoolProvider.class.getName()), Level.FINEST); @@ -95,13 +93,7 @@ public abstract class AbstractThreadPoolProvider { private final String name; private final AtomicBoolean closed = new AtomicBoolean(false); private final LazyValue lazyExecutorServiceProvider = - Values.lazy(new Value() { - - @Override - public E get() { - return createExecutor(getCorePoolSize(), createThreadFactory(), getRejectedExecutionHandler()); - } - }); + Values.lazy((Value) () -> createExecutor(getCorePoolSize(), createThreadFactory(), getRejectedExecutionHandler())); /** * Inheritance constructor. @@ -211,11 +203,8 @@ protected int getCorePoolSize() { * @see #createExecutor */ protected RejectedExecutionHandler getRejectedExecutionHandler() { - return new RejectedExecutionHandler() { - @Override - public void rejectedExecution(final Runnable r, final ThreadPoolExecutor executor) { - // TODO: implement the rejected execution handler method. - } + return (r, executor) -> { + // TODO: implement the rejected execution handler method. }; } @@ -345,51 +334,47 @@ private static PrivilegedAction shutdownExecutor( final int terminationTimeout, final TimeUnit terminationTimeUnit) { - return new PrivilegedAction() { + return (PrivilegedAction) () -> { + if (!executorService.isShutdown()) { + executorService.shutdown(); + } + if (executorService.isTerminated()) { + return null; + } - @Override - public Void run() { - if (!executorService.isShutdown()) { - executorService.shutdown(); - } - if (executorService.isTerminated()) { - return null; + boolean terminated = false; + boolean interrupted = false; + try { + terminated = executorService.awaitTermination(terminationTimeout, terminationTimeUnit); + } catch (InterruptedException e) { + if (LOGGER.isDebugLoggable()) { + LOGGER.log(LOGGER.getDebugLevel(), + "Interrupted while waiting for thread pool executor " + executorName + " to shutdown.", e); } + interrupted = true; + } - boolean terminated = false; - boolean interrupted = false; - try { - terminated = executorService.awaitTermination(terminationTimeout, terminationTimeUnit); - } catch (InterruptedException e) { - if (LOGGER.isDebugLoggable()) { - LOGGER.log(LOGGER.getDebugLevel(), - "Interrupted while waiting for thread pool executor " + executorName + " to shutdown.", e); - } - interrupted = true; - } - - try { - if (!terminated) { - final List cancelledTasks = executorService.shutdownNow(); - for (Runnable cancelledTask : cancelledTasks) { - if (cancelledTask instanceof Future) { - ((Future) cancelledTask).cancel(true); - } - } - - if (LOGGER.isDebugLoggable()) { - LOGGER.debugLog("Thread pool executor {0} forced-shut down. List of cancelled tasks: {1}", - executorName, cancelledTasks); + try { + if (!terminated) { + final List cancelledTasks = executorService.shutdownNow(); + for (Runnable cancelledTask : cancelledTasks) { + if (cancelledTask instanceof Future) { + ((Future) cancelledTask).cancel(true); } } - } finally { - if (interrupted) { - // restoring the interrupt flag - Thread.currentThread().interrupt(); + + if (LOGGER.isDebugLoggable()) { + LOGGER.debugLog("Thread pool executor {0} forced-shut down. List of cancelled tasks: {1}", + executorName, cancelledTasks); } } - return null; + } finally { + if (interrupted) { + // restoring the interrupt flag + Thread.currentThread().interrupt(); + } } + return null; }; } } diff --git a/core-common/src/main/java/org/glassfish/jersey/spi/ContentEncoder.java b/core-common/src/main/java/org/glassfish/jersey/spi/ContentEncoder.java index a548804666..d476f4939a 100644 --- a/core-common/src/main/java/org/glassfish/jersey/spi/ContentEncoder.java +++ b/core-common/src/main/java/org/glassfish/jersey/spi/ContentEncoder.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -46,6 +46,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.Set; +import java.util.stream.Collectors; import javax.ws.rs.Priorities; import javax.ws.rs.WebApplicationException; @@ -57,8 +58,6 @@ import javax.annotation.Priority; -import jersey.repackaged.com.google.common.collect.Sets; - /** * Standard contract for plugging in content encoding support. Provides a standard way of implementing encoding * {@link WriterInterceptor} and decoding {@link ReaderInterceptor}. Implementing this class ensures the encoding @@ -81,7 +80,7 @@ protected ContentEncoder(String... supportedEncodings) { if (supportedEncodings.length == 0) { throw new IllegalArgumentException(); } - this.supportedEncodings = Collections.unmodifiableSet(Sets.newHashSet(Arrays.asList(supportedEncodings))); + this.supportedEncodings = Collections.unmodifiableSet(Arrays.stream(supportedEncodings).collect(Collectors.toSet())); } /** diff --git a/core-common/src/main/java/org/glassfish/jersey/uri/UriTemplate.java b/core-common/src/main/java/org/glassfish/jersey/uri/UriTemplate.java index cf6bc94b0e..63f895cc7b 100644 --- a/core-common/src/main/java/org/glassfish/jersey/uri/UriTemplate.java +++ b/core-common/src/main/java/org/glassfish/jersey/uri/UriTemplate.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2010-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -51,10 +51,9 @@ import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; +import org.glassfish.jersey.internal.guava.Preconditions; import org.glassfish.jersey.uri.internal.UriTemplateParser; -import jersey.repackaged.com.google.common.base.Preconditions; - /** * A URI template. * diff --git a/core-common/src/main/java/org/glassfish/jersey/uri/internal/JerseyUriBuilder.java b/core-common/src/main/java/org/glassfish/jersey/uri/internal/JerseyUriBuilder.java index 548d4042cb..883319b0e2 100644 --- a/core-common/src/main/java/org/glassfish/jersey/uri/internal/JerseyUriBuilder.java +++ b/core-common/src/main/java/org/glassfish/jersey/uri/internal/JerseyUriBuilder.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2010-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -44,6 +44,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.security.AccessController; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -53,14 +54,12 @@ import javax.ws.rs.core.UriBuilderException; import org.glassfish.jersey.internal.LocalizationMessages; +import org.glassfish.jersey.internal.guava.InetAddresses; import org.glassfish.jersey.internal.util.ReflectionHelper; import org.glassfish.jersey.internal.util.collection.MultivaluedStringMap; import org.glassfish.jersey.uri.UriComponent; import org.glassfish.jersey.uri.UriTemplate; -import jersey.repackaged.com.google.common.collect.Maps; -import jersey.repackaged.com.google.common.net.InetAddresses; - /** * A Jersey implementation of {@link UriBuilder}. * @@ -641,7 +640,7 @@ private JerseyUriBuilder resolveTemplate(final String name, throw new IllegalArgumentException(LocalizationMessages.PARAM_NULL("value")); } - final Map templateValues = Maps.newHashMap(); + final Map templateValues = new HashMap<>(); templateValues.put(name, value); resolveTemplates(templateValues, encode, encodeSlashInPath); return this; diff --git a/core-common/src/main/resources/org/glassfish/jersey/hk2/localization.properties b/core-common/src/main/resources/org/glassfish/jersey/hk2/localization.properties index f48c452e7b..5836b83d8b 100644 --- a/core-common/src/main/resources/org/glassfish/jersey/hk2/localization.properties +++ b/core-common/src/main/resources/org/glassfish/jersey/hk2/localization.properties @@ -43,4 +43,5 @@ hk2.clearing.cache=Clearing Jersey HK2 caches. Service cache size: {0}, reflecti hk2.reification.error=HK2 service reification failed for [{0}] with an exception:\n{1} hk2.unknown.error=Unknown HK2 failure detected:\n{0} hk2.unknown.parent.injection.manager=Unknown parent of InjectionManager, ServiceLocator should be used instead of: {0}. -hk2.failure.outside.error.scope=HK2 failure has been detected in a code that does not run in an active Jersey Error scope. \ No newline at end of file +hk2.failure.outside.error.scope=HK2 failure has been detected in a code that does not run in an active Jersey Error scope. +hk2.provider.not.registrable=Provider registered to HJ2InjectionManager cannot be process because of incompatible type: {0}. \ No newline at end of file diff --git a/core-common/src/main/resources/org/glassfish/jersey/internal/localization.properties b/core-common/src/main/resources/org/glassfish/jersey/internal/localization.properties index 5e7a510184..8e34590b66 100644 --- a/core-common/src/main/resources/org/glassfish/jersey/internal/localization.properties +++ b/core-common/src/main/resources/org/glassfish/jersey/internal/localization.properties @@ -151,6 +151,7 @@ some.headers.not.sent=There are some request headers that have not been sent by those headers in WriterInterceptor or MessageBodyWriter. That feature is not supported by the connector. Please, \ do not modify headers in WriterInterceptor or MessageBodyWriter or use default HttpUrlConnector instead.\n\ Unsent header changes: {1} +slow.subscriber=Slow Subscriber. Subscription will be canceled. Item {0} and all the items sent after will not be received. ssl.ctx.algorithm.not.supported=Error creating SSL context (algorithm not supported). ssl.ctx.init.failed=Error initializing SSL context (operation failed). ssl.ks.cert.load.error=Cannot load key store certificates. @@ -186,6 +187,7 @@ type.to.class.conversion.not.supported=Type-to-class conversion not supported fo unable.to.parse.header.value=Unable to parse "{0}" header value: "{1}" unhandled.exception.detected=Unhandled exception detected on thread {0}. unknown.descriptor.type=Unable to register a service because of unknown descriptor type: {0}. +unknown.subscriber=Unknown subscriber. uri.builder.scheme.part.null=Supplied scheme-specific part parameter is null. uri.builder.scheme.part.unexpected.component=Supplied scheme-specific URI part "{0}" contains unexpected URI Scheme component: {1}. uri.builder.uri.part.fragment=Supplied scheme-specific URI part "{0}" contains URI Fragment component: {1}. diff --git a/core-common/src/test/java/org/glassfish/jersey/config/ServiceFinderBinderTest.java b/core-common/src/test/java/org/glassfish/jersey/config/ServiceFinderBinderTest.java index e382ec11a5..5cf1cd4bf0 100644 --- a/core-common/src/test/java/org/glassfish/jersey/config/ServiceFinderBinderTest.java +++ b/core-common/src/test/java/org/glassfish/jersey/config/ServiceFinderBinderTest.java @@ -42,6 +42,7 @@ import java.util.Collection; import java.util.Set; +import java.util.stream.Collectors; import javax.ws.rs.RuntimeType; @@ -57,9 +58,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import jersey.repackaged.com.google.common.base.Function; -import jersey.repackaged.com.google.common.collect.Collections2; - /** * Service finder injection binder unit test. * @@ -94,13 +92,10 @@ public void testConfigure() { final Set providers = Providers.getProviders(injectionManager, TestContract.class); assertEquals(4, providers.size()); - final Collection providerNames = Collections2.transform(providers, new Function() { - - @Override - public String apply(TestContract input) { - return input.name(); - } - }); + final Collection providerNames = + providers.stream() + .map(TestContract::name) + .collect(Collectors.toList()); assertTrue(providerNames.contains(TestServiceA.class.getName())); assertTrue(providerNames.contains(TestServiceB.class.getName())); diff --git a/core-common/src/test/java/org/glassfish/jersey/hk2/BindingTestHelper.java b/core-common/src/test/java/org/glassfish/jersey/hk2/BindingTestHelper.java index 0a003e2a9c..449965f7bd 100644 --- a/core-common/src/test/java/org/glassfish/jersey/hk2/BindingTestHelper.java +++ b/core-common/src/test/java/org/glassfish/jersey/hk2/BindingTestHelper.java @@ -67,6 +67,7 @@ protected void configure() { }; injectionManager.register(binder); + injectionManager.completeRegistration(); } /** @@ -75,8 +76,6 @@ protected void configure() { * @return newly created {@code InjectionManager}. */ static InjectionManager createInjectionManager() { - HK2InjectionManager injectionManager = new HK2InjectionManager(); - injectionManager.initialize(); - return injectionManager; + return new ImmediateHk2InjectionManager(); } } diff --git a/core-common/src/test/java/org/glassfish/jersey/hk2/InjectionManagerTest.java b/core-common/src/test/java/org/glassfish/jersey/hk2/InjectionManagerTest.java index 55ab8f2b18..d32ada2f5f 100644 --- a/core-common/src/test/java/org/glassfish/jersey/hk2/InjectionManagerTest.java +++ b/core-common/src/test/java/org/glassfish/jersey/hk2/InjectionManagerTest.java @@ -70,33 +70,31 @@ protected void configure() { }; ServiceLocator parentLocator = ServiceLocatorUtilities.bind(binder); - InjectionManager injectionManager = new HK2InjectionManager(); - injectionManager.initialize(parentLocator); + InjectionManager injectionManager = new ImmediateHk2InjectionManager(parentLocator); + injectionManager.completeRegistration(); assertNotNull(injectionManager.getInstance(EnglishGreeting.class)); } @Test public void testInjectionManagerParent() { ClassBinding greetingBinding = Bindings.serviceAsContract(EnglishGreeting.class); - InjectionManager parentInjectionManager = new HK2InjectionManager(); - parentInjectionManager.initialize(); + InjectionManager parentInjectionManager = new ImmediateHk2InjectionManager(); parentInjectionManager.register(greetingBinding); + parentInjectionManager.completeRegistration(); - InjectionManager injectionManager = new HK2InjectionManager(); - injectionManager.initialize(parentInjectionManager); + InjectionManager injectionManager = new ImmediateHk2InjectionManager(parentInjectionManager); + injectionManager.completeRegistration(); assertNotNull(injectionManager.getInstance(EnglishGreeting.class)); } @Test(expected = IllegalArgumentException.class) public void testUnknownParent() { - InjectionManager parentInjectionManager = new HK2InjectionManager(); - parentInjectionManager.initialize(new Object()); - Injections.createInjectionManager(parentInjectionManager); + Injections.createInjectionManager(new Object()); } @Test public void testIsRegistrable() { - InjectionManager injectionManager = new HK2InjectionManager(); + InjectionManager injectionManager = new ImmediateHk2InjectionManager(); assertTrue(injectionManager.isRegistrable(Binder.class)); assertTrue(injectionManager.isRegistrable(AbstractBinder.class)); assertFalse(injectionManager.isRegistrable(org.glassfish.jersey.internal.inject.AbstractBinder.class)); @@ -112,16 +110,15 @@ protected void configure() { } }; - InjectionManager injectionManager = new HK2InjectionManager(); - injectionManager.initialize(); + InjectionManager injectionManager = new ImmediateHk2InjectionManager(); injectionManager.register(binder); + injectionManager.completeRegistration(); assertNotNull(injectionManager.getInstance(EnglishGreeting.class)); } @Test(expected = IllegalArgumentException.class) public void testRegisterUnknownProvider() { - InjectionManager injectionManager = new HK2InjectionManager(); - injectionManager.initialize(); + InjectionManager injectionManager = new ImmediateHk2InjectionManager(); injectionManager.register(new Object()); } } diff --git a/core-common/src/test/java/org/glassfish/jersey/internal/ContextResolverFactoryTest.java b/core-common/src/test/java/org/glassfish/jersey/internal/ContextResolverFactoryTest.java index 3f265d39e9..410210778e 100644 --- a/core-common/src/test/java/org/glassfish/jersey/internal/ContextResolverFactoryTest.java +++ b/core-common/src/test/java/org/glassfish/jersey/internal/ContextResolverFactoryTest.java @@ -130,11 +130,19 @@ public ContextResolverFactoryTest() { @Before public void setUp() { - final InjectionManager locator = Injections.createInjectionManager(new ContextResolverFactory.Binder(), new Binder()); - final ProviderBinder providerBinder = new ProviderBinder(locator); + InjectionManager injectionManager = Injections.createInjectionManager(); + ProviderBinder providerBinder = new ProviderBinder(injectionManager); providerBinder.bindClasses(Collections.singleton(CustomIntegerResolverC.class)); + injectionManager.register(new Binder()); - crf = locator.getInstance(ContextResolverFactory.class); + BootstrapBag bootstrapBag = new BootstrapBag(); + ContextResolverFactory.ContextResolversConfigurator configurator = + new ContextResolverFactory.ContextResolversConfigurator(); + configurator.init(injectionManager, bootstrapBag); + injectionManager.completeRegistration(); + configurator.postInit(injectionManager, bootstrapBag); + + crf = injectionManager.getInstance(ContextResolverFactory.class); } @Test diff --git a/core-common/src/test/java/org/glassfish/jersey/internal/JaxrsProvidersTest.java b/core-common/src/test/java/org/glassfish/jersey/internal/JaxrsProvidersTest.java index 3c69cb9dd7..346eda4194 100644 --- a/core-common/src/test/java/org/glassfish/jersey/internal/JaxrsProvidersTest.java +++ b/core-common/src/test/java/org/glassfish/jersey/internal/JaxrsProvidersTest.java @@ -41,6 +41,8 @@ package org.glassfish.jersey.internal; import java.lang.annotation.Annotation; +import java.util.Arrays; +import java.util.List; import java.util.concurrent.Callable; import javax.ws.rs.RuntimeType; @@ -54,6 +56,7 @@ import org.glassfish.jersey.internal.inject.AbstractBinder; import org.glassfish.jersey.internal.inject.InjectionManager; import org.glassfish.jersey.internal.inject.Injections; +import org.glassfish.jersey.message.internal.MessageBodyFactory; import org.glassfish.jersey.message.internal.MessagingBinders; import org.glassfish.jersey.model.internal.CommonConfig; import org.glassfish.jersey.model.internal.ComponentBag; @@ -63,7 +66,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; -/** +/**® * @author Marek Potociar (marek.potociar at oracle.com) */ public class JaxrsProvidersTest { @@ -90,34 +93,39 @@ public JaxrsProvidersTest() { @Test public void testProviders() throws Exception { - final InjectionManager injectionManager = Injections.createInjectionManager( - new MessagingBinders.MessageBodyProviders(null, RuntimeType.SERVER), new Binder()); - - injectionManager.register(new TestBinder(injectionManager)); + InjectionManager injectionManager = Injections.createInjectionManager(); + injectionManager.register(new MessagingBinders.MessageBodyProviders(null, RuntimeType.SERVER)); + injectionManager.register(new Binder()); + + BootstrapBag bootstrapBag = new BootstrapBag(); + List bootstrapConfigurators = Arrays.asList( + new RequestScope.RequestScopeConfigurator(), + new TestConfigConfigurator(), + new ContextResolverFactory.ContextResolversConfigurator(), + new MessageBodyFactory.MessageBodyWorkersConfigurator(), + new ExceptionMapperFactory.ExceptionMappersConfigurator()); + injectionManager.register(new TestBinder()); TestBinder.initProviders(injectionManager); - RequestScope scope = injectionManager.getInstance(RequestScope.class); - - scope.runInScope(new Callable() { + bootstrapConfigurators.forEach(configurator -> configurator.init(injectionManager, bootstrapBag)); + injectionManager.completeRegistration(); + bootstrapConfigurators.forEach(configurator -> configurator.postInit(injectionManager, bootstrapBag)); - @Override - public Object call() throws Exception { - Providers instance = injectionManager.getInstance(Providers.class); + RequestScope scope = bootstrapBag.getRequestScope(); - assertNotNull(instance); - assertSame(JaxrsProviders.class, instance.getClass()); + scope.runInScope((Callable) () -> { + Providers instance = injectionManager.getInstance(Providers.class); - assertNotNull(instance.getExceptionMapper(Throwable.class)); - assertNotNull(instance.getMessageBodyReader(String.class, String.class, new Annotation[0], - MediaType.TEXT_PLAIN_TYPE)); - assertNotNull(instance.getMessageBodyWriter(String.class, String.class, new Annotation[0], - MediaType.TEXT_PLAIN_TYPE)); - assertNotNull(instance.getContextResolver(String.class, MediaType.TEXT_PLAIN_TYPE)); - - return null; - } + assertNotNull(instance); + assertSame(JaxrsProviders.class, instance.getClass()); + assertNotNull(instance.getExceptionMapper(Throwable.class)); + assertNotNull(instance.getMessageBodyReader(String.class, String.class, new Annotation[0], + MediaType.TEXT_PLAIN_TYPE)); + assertNotNull(instance.getMessageBodyWriter(String.class, String.class, new Annotation[0], + MediaType.TEXT_PLAIN_TYPE)); + assertNotNull(instance.getContextResolver(String.class, MediaType.TEXT_PLAIN_TYPE)); + return null; }); - } } diff --git a/core-common/src/test/java/org/glassfish/jersey/internal/ProviderBinderTest.java b/core-common/src/test/java/org/glassfish/jersey/internal/ProviderBinderTest.java index e66e75138c..6acb9c8ae1 100644 --- a/core-common/src/test/java/org/glassfish/jersey/internal/ProviderBinderTest.java +++ b/core-common/src/test/java/org/glassfish/jersey/internal/ProviderBinderTest.java @@ -67,6 +67,7 @@ import javax.inject.Singleton; import org.glassfish.jersey.internal.inject.Binder; +import org.glassfish.jersey.internal.inject.CompositeBinder; import org.glassfish.jersey.internal.inject.CustomAnnotationLiteral; import org.glassfish.jersey.internal.inject.InjectionManager; import org.glassfish.jersey.internal.inject.Injections; @@ -122,12 +123,10 @@ public void writeTo(Object t, Class type, Type genericType, Annotation[] annotat } } - private static Binder[] initBinders(Binder... binders) { + private static Binder initBinders(Binder... binders) { List binderList = Arrays.stream(binders).collect(Collectors.toList()); - binderList.add(new MessagingBinders.MessageBodyProviders(null, RuntimeType.SERVER)); - - return binderList.toArray(new Binder[binderList.size()]); + return CompositeBinder.wrap(binderList); } public ProviderBinderTest() { @@ -137,6 +136,7 @@ public ProviderBinderTest() { @Test public void testServicesNotEmpty() { InjectionManager injectionManager = Injections.createInjectionManager(initBinders()); + injectionManager.completeRegistration(); Set providers = Providers.getProviders(injectionManager, MessageBodyReader.class); assertTrue(providers.size() > 0); } @@ -144,6 +144,7 @@ public void testServicesNotEmpty() { @Test public void testServicesMbr() { InjectionManager injectionManager = Injections.createInjectionManager(initBinders()); + injectionManager.completeRegistration(); Set providers = Providers.getProviders(injectionManager, MessageBodyReader.class); assertTrue(providers.size() > 0); } @@ -151,6 +152,7 @@ public void testServicesMbr() { @Test public void testServicesMbw() { InjectionManager injectionManager = Injections.createInjectionManager(initBinders()); + injectionManager.completeRegistration(); Set providers = Providers.getProviders(injectionManager, MessageBodyWriter.class); assertTrue(providers.size() > 0); } @@ -160,6 +162,8 @@ public void testProvidersMbr() { InjectionManager injectionManager = Injections.createInjectionManager(initBinders()); ProviderBinder providerBinder = new ProviderBinder(injectionManager); providerBinder.bindClasses(Collections.singleton(MyProvider.class)); + + injectionManager.completeRegistration(); Set providers = Providers.getCustomProviders(injectionManager, MessageBodyReader.class); assertEquals(1, instancesOfType(MyProvider.class, providers).size()); } @@ -170,6 +174,7 @@ public void testProvidersMbw() { ProviderBinder providerBinder = new ProviderBinder(injectionManager); providerBinder.bindClasses(Collections.singleton(MyProvider.class)); + injectionManager.completeRegistration(); Set providers = Providers.getCustomProviders(injectionManager, MessageBodyWriter.class); final Collection myProviders = instancesOfType(MyProvider.class, providers); assertEquals(1, myProviders.size()); @@ -180,6 +185,8 @@ public void testProvidersMbrInstance() { InjectionManager injectionManager = Injections.createInjectionManager(initBinders()); ProviderBinder providerBinder = new ProviderBinder(injectionManager); providerBinder.bindInstances(Collections.singleton(new MyProvider())); + + injectionManager.completeRegistration(); Set providers = Providers.getCustomProviders(injectionManager, MessageBodyReader.class); assertEquals(1, instancesOfType(MyProvider.class, providers).size()); } @@ -190,6 +197,7 @@ public void testProvidersMbwInstance() { ProviderBinder providerBinder = new ProviderBinder(injectionManager); providerBinder.bindInstances(Collections.singleton(new MyProvider())); + injectionManager.completeRegistration(); Set providers = Providers.getCustomProviders(injectionManager, MessageBodyWriter.class); assertEquals(instancesOfType(MyProvider.class, providers).size(), 1); } @@ -210,6 +218,7 @@ public void testCustomRegistration() { ProviderBinder providerBinder = new ProviderBinder(injectionManager); providerBinder.bindClasses(Child.class); providerBinder.bindClasses(NotFilterChild.class); + injectionManager.completeRegistration(); ContainerRequestFilter requestFilter = getRequestFilter(injectionManager); ContainerRequestFilter requestFilter2 = getRequestFilter(injectionManager); diff --git a/core-common/src/test/java/org/glassfish/jersey/internal/TestBinder.java b/core-common/src/test/java/org/glassfish/jersey/internal/TestBinder.java index 52563ba826..dac693d602 100644 --- a/core-common/src/test/java/org/glassfish/jersey/internal/TestBinder.java +++ b/core-common/src/test/java/org/glassfish/jersey/internal/TestBinder.java @@ -49,9 +49,7 @@ import org.glassfish.jersey.internal.inject.AbstractBinder; import org.glassfish.jersey.internal.inject.InjectionManager; import org.glassfish.jersey.internal.inject.ProviderBinder; -import org.glassfish.jersey.message.internal.MessageBodyFactory; import org.glassfish.jersey.message.internal.MessagingBinders; -import org.glassfish.jersey.process.internal.RequestScope; /** * Binder for testing purposes. @@ -60,12 +58,6 @@ */ public class TestBinder extends AbstractBinder { - private final InjectionManager injectionManager; - - public TestBinder(InjectionManager injectionManager) { - this.injectionManager = injectionManager; - } - public static void initProviders(final InjectionManager injectionManager) { initProviders(injectionManager, Collections.emptySet(), Collections.emptySet()); } @@ -81,11 +73,7 @@ public static void initProviders(final InjectionManager injectionManager, @Override protected void configure() { install( - new RequestScope.Binder(), new MessagingBinders.MessageBodyProviders(null, RuntimeType.SERVER), - new MessageBodyFactory.Binder(), - new ExceptionMapperFactory.Binder(), - new ContextResolverFactory.Binder(), new JaxrsProviders.Binder()); bind(new ExceptionMapper() { diff --git a/core-common/src/test/java/org/glassfish/jersey/internal/TestConfigConfigurator.java b/core-common/src/test/java/org/glassfish/jersey/internal/TestConfigConfigurator.java new file mode 100644 index 0000000000..6507873db4 --- /dev/null +++ b/core-common/src/test/java/org/glassfish/jersey/internal/TestConfigConfigurator.java @@ -0,0 +1,65 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package org.glassfish.jersey.internal; + +import javax.ws.rs.RuntimeType; + +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.model.internal.CommonConfig; +import org.glassfish.jersey.model.internal.ComponentBag; + +/** + * Configurator which initializes configuration. + * + * @author Petr Bouda (petr.bouda at oracle.com) + */ +public class TestConfigConfigurator implements BootstrapConfigurator { + + @Override + public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) { + bootstrapBag.setConfiguration(new CommonConfig(RuntimeType.SERVER, ComponentBag.EXCLUDE_EMPTY)); + } + + @Override + public void postInit(InjectionManager injectionManager, BootstrapBag bootstrapBag) { + } + +} \ No newline at end of file diff --git a/core-common/src/test/java/org/glassfish/jersey/internal/util/JerseyPublisherTest.java b/core-common/src/test/java/org/glassfish/jersey/internal/util/JerseyPublisherTest.java index 35589f9a85..f38e850b9b 100644 --- a/core-common/src/test/java/org/glassfish/jersey/internal/util/JerseyPublisherTest.java +++ b/core-common/src/test/java/org/glassfish/jersey/internal/util/JerseyPublisherTest.java @@ -43,15 +43,17 @@ import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; +import java.util.concurrent.atomic.AtomicInteger; import javax.ws.rs.Flow; import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; /** @@ -61,8 +63,6 @@ */ public class JerseyPublisherTest { - private static final Logger LOGGER = Logger.getLogger(JerseyPublisherTest.class.getName()); - @Test public void test() throws InterruptedException { @@ -76,7 +76,7 @@ public void test() throws InterruptedException { final CountDownLatch closeLatch = new CountDownLatch(3); - final JerseyPublisher publisher = new JerseyPublisher<>(); + final JerseyPublisher publisher = new JerseyPublisher<>(JerseyPublisher.PublisherStrategy.BLOCKING); final PublisherTestSubscriber subscriber1 = new PublisherTestSubscriber("SUBSCRIBER-1", openLatch1, writeLatch1, closeLatch); final PublisherTestSubscriber subscriber2 = @@ -84,26 +84,26 @@ public void test() throws InterruptedException { final PublisherTestSubscriber subscriber3 = new PublisherTestSubscriber("SUBSCRIBER-3", openLatch3, writeLatch3, closeLatch); - publisher.submit("START"); // sent before any subscriber subscribed - should not be received + publisher.publish("START"); // sent before any subscriber subscribed - should not be received publisher.subscribe(subscriber1); - publisher.submit("Zero"); // before receive, but should be received by SUBSCRIBER-1 + publisher.publish("Zero"); // before receive, but should be received by SUBSCRIBER-1 assertTrue(openLatch1.await(200, TimeUnit.MILLISECONDS)); subscriber1.receive(3); - publisher.submit("One"); // should be received by SUBSCRIBER-1 + publisher.publish("One"); // should be received by SUBSCRIBER-1 publisher.subscribe(subscriber2); assertTrue(openLatch2.await(200, TimeUnit.MILLISECONDS)); subscriber2.receive(5); - publisher.submit("Two"); // should be received by SUBSCRIBER-1 and SUBSCRIBER-2 + publisher.publish("Two"); // should be received by SUBSCRIBER-1 and SUBSCRIBER-2 publisher.subscribe(subscriber3); assertTrue(openLatch3.await(200, TimeUnit.MILLISECONDS)); subscriber3.receive(5); - publisher.submit("Three"); // should be received by SUBSCRIBER-2 and SUBSCRIBER-3 + publisher.publish("Three"); // should be received by SUBSCRIBER-2 and SUBSCRIBER-3 assertTrue(writeLatch1.await(1000, TimeUnit.MILLISECONDS)); assertTrue(writeLatch2.await(1000, TimeUnit.MILLISECONDS)); @@ -127,17 +127,71 @@ public void test() throws InterruptedException { publisher.close(); subscriber1.receive(1); // --> with this, the CDL is successfully counted down and await returns true assertTrue(closeLatch.await(10000, TimeUnit.MILLISECONDS)); - // - the strange thing is, that all SubmissionPublisher attempts to invoke all the three callbacks - // and gets to the sysout behind the onComplete() call for all the three subscribers (see output) + // this behaviour is a little counter-intuitive, but confirmed as correct by Flow.SubmissionPublisher author, + // Dough Lea on the JDK mailing list + } + + @Test + public void testNonBlocking() throws InterruptedException { + final int MSG_COUNT = 300; + + final JerseyPublisher publisher = new JerseyPublisher<>(); + + final CountDownLatch openLatchActive = new CountDownLatch(1); + final CountDownLatch writeLatch = new CountDownLatch(MSG_COUNT); + final CountDownLatch closeLatch = new CountDownLatch(1); + + final CountDownLatch openLatchDead = new CountDownLatch(1); + + final PublisherTestSubscriber deadSubscriber = + new PublisherTestSubscriber("dead", openLatchDead, new CountDownLatch(0), new CountDownLatch(0)); + + final PublisherTestSubscriber activeSubscriber = + new PublisherTestSubscriber("active", openLatchActive, writeLatch, closeLatch); + + // subscribe to publisher + publisher.subscribe(deadSubscriber); + assertTrue(openLatchDead.await(200, TimeUnit.MILLISECONDS)); + publisher.subscribe(activeSubscriber); + assertTrue(openLatchActive.await(200, TimeUnit.MILLISECONDS)); + + activeSubscriber.receive(1000); + + AtomicInteger counter = new AtomicInteger(0); + final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1); + scheduledExecutorService.scheduleAtFixedRate(() -> { + int i = counter.getAndIncrement(); + publisher.publish("MSG-" + i); + if (i > MSG_COUNT - 1) { + scheduledExecutorService.shutdown(); + } + }, 0, 10, TimeUnit.MILLISECONDS); + + assertTrue(writeLatch.await(6000, TimeUnit.MILLISECONDS)); + + assertEquals(MSG_COUNT, activeSubscriber.getReceivedData().size()); + assertEquals(0, deadSubscriber.getReceivedData().size()); + + assertFalse(activeSubscriber.hasError()); + assertTrue(deadSubscriber.hasError()); + + publisher.close(); + + assertTrue(closeLatch.await(6000, TimeUnit.MILLISECONDS)); + assertTrue(activeSubscriber.isCompleted()); + assertFalse(deadSubscriber.isCompleted()); } class PublisherTestSubscriber implements Flow.Subscriber { + private final String name; private final CountDownLatch openLatch; private final CountDownLatch writeLatch; private final CountDownLatch closeLatch; private Flow.Subscription subscription; private final Queue data; + private boolean hasError = false; + private boolean completed = false; PublisherTestSubscriber(final String name, final CountDownLatch openLatch, @@ -160,17 +214,17 @@ public void onSubscribe(final Flow.Subscription subscription) { public void onNext(final String item) { data.add(item); writeLatch.countDown(); - LOGGER.info("[" + name + "] (" + Thread.currentThread().getName() + ") " + item); } @Override public void onError(final Throwable throwable) { - LOGGER.log(Level.INFO, "[" + name + "] (" + Thread.currentThread().getName() + ") onError()", throwable); + throwable.printStackTrace(); + hasError = true; } @Override public void onComplete() { - LOGGER.info("[" + name + "] (" + Thread.currentThread().getName() + ") onComplete()"); + completed = true; closeLatch.countDown(); } @@ -182,8 +236,6 @@ public String toString() { public void receive(final long n) { if (subscription != null) { subscription.request(n); - } else { - System.out.println("[" + this.name + "] Subscription is null"); } } @@ -196,13 +248,12 @@ Queue getReceivedData() { return this.data; } - /** - * Instruct subscriber to request {@code n} items. - * - * @param n amount of items to request - */ - public void request(final long n) { - this.subscription.request(n); + public boolean hasError() { + return hasError; + } + + public boolean isCompleted() { + return completed; } } } diff --git a/core-common/src/test/java/org/glassfish/jersey/internal/util/PropertiesHelperTest.java b/core-common/src/test/java/org/glassfish/jersey/internal/util/PropertiesHelperTest.java index 6d2f0cbcf5..7f7185f8aa 100644 --- a/core-common/src/test/java/org/glassfish/jersey/internal/util/PropertiesHelperTest.java +++ b/core-common/src/test/java/org/glassfish/jersey/internal/util/PropertiesHelperTest.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -40,6 +40,7 @@ package org.glassfish.jersey.internal.util; +import java.util.HashMap; import java.util.Map; import javax.ws.rs.RuntimeType; @@ -48,8 +49,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; -import jersey.repackaged.com.google.common.collect.Maps; - /** * @author Miroslav Fuksa * @@ -58,7 +57,7 @@ public class PropertiesHelperTest { @Test public void testGetValueWithType() { - Map properties = Maps.newHashMap(); + Map properties = new HashMap<>(); final String key = "my.property"; properties.put(key, "15"); @@ -69,7 +68,7 @@ public void testGetValueWithType() { @Test public void testGetValueWithTypeAndDefaultValue() { - Map properties = Maps.newHashMap(); + Map properties = new HashMap<>(); final String key = "my.property"; properties.put(key, "15"); @@ -80,7 +79,7 @@ public void testGetValueWithTypeAndDefaultValue() { @Test public void testGetValueWithDefaultValue() { - Map properties = Maps.newHashMap(); + Map properties = new HashMap<>(); final String key = "my.property"; properties.put(key, "15"); @@ -91,7 +90,7 @@ public void testGetValueWithDefaultValue() { @Test public void testGetValueByIgnoredRuntime() { - Map properties = Maps.newHashMap(); + Map properties = new HashMap<>(); final String key = "my.property"; properties.put(key, "15"); @@ -103,7 +102,7 @@ public void testGetValueByIgnoredRuntime() { @Test public void testGetValueByRuntime1() { - Map properties = Maps.newHashMap(); + Map properties = new HashMap<>(); final String key = "jersey.config.my.property"; properties.put(key, "15"); properties.put("jersey.config.client.my.property", "999"); @@ -121,7 +120,7 @@ public void testGetValueByRuntime1() { @Test public void testGetValueByRuntime2() { - Map properties = Maps.newHashMap(); + Map properties = new HashMap<>(); final String key = "jersey.config.my.property"; properties.put(key, "15"); properties.put("jersey.config.client.my.property", "999"); @@ -140,7 +139,7 @@ public void testGetValueByRuntime2() { @Test public void testGetValueByRuntime3() { - Map properties = Maps.newHashMap(); + Map properties = new HashMap<>(); final String key = "jersey.config.my.property"; properties.put("jersey.config.client.my.property", "999"); @@ -155,7 +154,7 @@ public void testGetValueByRuntime3() { */ @Test public void testGetValueNoTransformation() { - Map properties = Maps.newHashMap(); + Map properties = new HashMap<>(); final String key = "my.property"; properties.put(key, Boolean.TRUE); @@ -166,10 +165,10 @@ public void testGetValueNoTransformation() { @Test public void testFallback() { - Map fallback = Maps.newHashMap(); + Map fallback = new HashMap<>(); fallback.put("my.property", "my.old.property"); - Map properties = Maps.newHashMap(); + Map properties = new HashMap<>(); properties.put("my.old.property", "foo"); assertEquals("foo", PropertiesHelper.getValue(properties, "my.property", String.class, fallback)); diff --git a/core-common/src/test/java/org/glassfish/jersey/message/DeflateEncodingTest.java b/core-common/src/test/java/org/glassfish/jersey/message/DeflateEncodingTest.java index 66ac0d49c7..5125113ede 100644 --- a/core-common/src/test/java/org/glassfish/jersey/message/DeflateEncodingTest.java +++ b/core-common/src/test/java/org/glassfish/jersey/message/DeflateEncodingTest.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -61,8 +61,6 @@ import org.junit.Test; -import jersey.repackaged.com.google.common.collect.Maps; - /** * @author Martin Matula */ @@ -73,7 +71,7 @@ private static class DummyConfiguration implements Configuration, Provider properties; public DummyConfiguration(boolean noZLib) { - properties = Maps.newHashMap(); + properties = new HashMap<>(); properties.put(MessageProperties.DEFLATE_WITHOUT_ZLIB, noZLib); } diff --git a/core-common/src/test/java/org/glassfish/jersey/message/internal/CommittingOutputStreamTest.java b/core-common/src/test/java/org/glassfish/jersey/message/internal/CommittingOutputStreamTest.java index 1951ef8746..a4fd21f641 100644 --- a/core-common/src/test/java/org/glassfish/jersey/message/internal/CommittingOutputStreamTest.java +++ b/core-common/src/test/java/org/glassfish/jersey/message/internal/CommittingOutputStreamTest.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -43,6 +43,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.util.HashMap; import java.util.Map; import javax.ws.rs.RuntimeType; @@ -58,8 +59,6 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import jersey.repackaged.com.google.common.collect.Maps; - /** * Test the {@link CommittingOutputStream}. * @@ -239,7 +238,7 @@ private void check(ByteArrayOutputStream baos, byte... bytes) { @Test public void testPropertiesWithMessageContext() throws IOException { final int size = 20; - Map properties = Maps.newHashMap(); + Map properties = new HashMap<>(); properties.put(CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, size); final RuntimeType runtime = RuntimeType.CLIENT; @@ -249,7 +248,7 @@ public void testPropertiesWithMessageContext() throws IOException { @Test public void testPropertiesWithMessageContextVeryBigBuffer() throws IOException { final int size = 200000; - Map properties = Maps.newHashMap(); + Map properties = new HashMap<>(); properties.put(CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, size); final RuntimeType runtime = RuntimeType.CLIENT; @@ -259,7 +258,7 @@ public void testPropertiesWithMessageContextVeryBigBuffer() throws IOException { @Test public void testPropertiesWithMessageContextMissingServerSpecific() throws IOException { final int size = 22; - Map properties = Maps.newHashMap(); + Map properties = new HashMap<>(); properties.put(CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, size); properties.put(CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER + ".client", size * 2); checkBufferSize(size, properties, RuntimeType.SERVER); @@ -268,7 +267,7 @@ public void testPropertiesWithMessageContextMissingServerSpecific() throws IOExc @Test public void testPropertiesWithMessageContextMissingServerAtAll() throws IOException { final int size = 22; - Map properties = Maps.newHashMap(); + Map properties = new HashMap<>(); properties.put(PropertiesHelper.getPropertyNameForRuntime( CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, RuntimeType.CLIENT), size); checkBufferSize(CommittingOutputStream.DEFAULT_BUFFER_SIZE, properties, RuntimeType.SERVER); @@ -278,7 +277,7 @@ public void testPropertiesWithMessageContextMissingServerAtAll() throws IOExcept @Test public void testPropertiesWithMessageContextClientOverrides() throws IOException { final int size = 22; - Map properties = Maps.newHashMap(); + Map properties = new HashMap<>(); properties.put(CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, size); properties.put(PropertiesHelper.getPropertyNameForRuntime(CommonProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, RuntimeType.CLIENT), size * 2); @@ -289,7 +288,7 @@ public void testPropertiesWithMessageContextClientOverrides() throws IOException @Test public void testPropertiesWithMessageContextDefaultNoProps() throws IOException { - Map properties = Maps.newHashMap(); + Map properties = new HashMap<>(); final RuntimeType runtime = RuntimeType.CLIENT; checkBufferSize(CommittingOutputStream.DEFAULT_BUFFER_SIZE, properties, runtime); diff --git a/core-common/src/test/java/org/glassfish/jersey/message/internal/HeaderUtilsTest.java b/core-common/src/test/java/org/glassfish/jersey/message/internal/HeaderUtilsTest.java index e30070afed..979c59cf93 100644 --- a/core-common/src/test/java/org/glassfish/jersey/message/internal/HeaderUtilsTest.java +++ b/core-common/src/test/java/org/glassfish/jersey/message/internal/HeaderUtilsTest.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -37,10 +37,13 @@ * only if the new code is made subject to such option by the copyright * holder. */ + package org.glassfish.jersey.message.internal; import java.net.URI; -import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; import java.util.List; import javax.ws.rs.core.AbstractMultivaluedMap; @@ -58,8 +61,6 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import jersey.repackaged.com.google.common.collect.Lists; - /** * {@link HeaderUtils} unit tests. * @@ -135,21 +136,24 @@ public void testAsStringList() throws Exception { assertTrue(HeaderUtils.asStringList(null, null).isEmpty()); final URI uri = new URI("test"); - final ArrayList values = Lists.newArrayList("value", null, uri); + final List values = new LinkedList() {{ + add("value"); + add(null); + add(uri); + }}; // test string values final List stringList = HeaderUtils.asStringList(values, null); - assertEquals(Lists.newArrayList("value", "[null]", uri.toASCIIString()), - stringList); + assertEquals(Arrays.asList("value", "[null]", uri.toASCIIString()), + stringList); // tests live view values.add("value2"); - assertEquals(Lists.newArrayList("value", "[null]", uri.toASCIIString(), "value2"), - stringList); + assertEquals(Arrays.asList("value", "[null]", uri.toASCIIString(), "value2"), + stringList); values.remove(1); - assertEquals(Lists.newArrayList("value", uri.toASCIIString(), "value2"), - stringList); - + assertEquals(Arrays.asList("value", uri.toASCIIString(), "value2"), + stringList); } @Test @@ -169,22 +173,22 @@ public void testAsStringHeaders() throws Exception { final MultivaluedMap stringHeaders = HeaderUtils.asStringHeaders(headers); // test string values - assertEquals(Lists.newArrayList("value", "value2"), - stringHeaders.get("k1")); - assertEquals(Lists.newArrayList(uri.toASCIIString()), - stringHeaders.get("k2")); - assertEquals(Lists.newArrayList("value3"), - stringHeaders.get("k3")); + assertEquals(Arrays.asList("value", "value2"), + stringHeaders.get("k1")); + assertEquals(Collections.singletonList(uri.toASCIIString()), + stringHeaders.get("k2")); + assertEquals(Collections.singletonList("value3"), + stringHeaders.get("k3")); // test live view headers.get("k1").remove(1); headers.add("k2", "value4"); headers.remove("k3"); - assertEquals(Lists.newArrayList("value"), - stringHeaders.get("k1")); - assertEquals(Lists.newArrayList(uri.toASCIIString(), "value4"), - stringHeaders.get("k2")); + assertEquals(Collections.singletonList("value"), + stringHeaders.get("k1")); + assertEquals(Arrays.asList(uri.toASCIIString(), "value4"), + stringHeaders.get("k2")); assertFalse(stringHeaders.containsKey("k3")); } @@ -193,7 +197,7 @@ public void testAsHeaderString() throws Exception { assertNull(HeaderUtils.asHeaderString(null, null)); final URI uri = new URI("test"); - final ArrayList values = Lists.newArrayList("value", null, uri); + final List values = Arrays.asList("value", null, uri); // test string values final String result = HeaderUtils.asHeaderString(values, null); diff --git a/core-common/src/test/java/org/glassfish/jersey/message/internal/MediaTypesTest.java b/core-common/src/test/java/org/glassfish/jersey/message/internal/MediaTypesTest.java index 6cda2a7089..57aa999423 100644 --- a/core-common/src/test/java/org/glassfish/jersey/message/internal/MediaTypesTest.java +++ b/core-common/src/test/java/org/glassfish/jersey/message/internal/MediaTypesTest.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -40,6 +40,7 @@ package org.glassfish.jersey.message.internal; import java.text.ParseException; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; @@ -51,8 +52,6 @@ import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; -import jersey.repackaged.com.google.common.collect.Lists; - /** * MediaTypes utility method tests. * @@ -63,21 +62,20 @@ public class MediaTypesTest { @Test public void testConvertToString() { - final List emptyList = Lists.newArrayList(); + final List emptyList = Collections.emptyList(); Assert.assertEquals("", MediaTypes.convertToString(emptyList)); - Assert.assertEquals("\"text/plain\"", MediaTypes.convertToString(Lists.newArrayList( - MediaType.TEXT_PLAIN_TYPE))); + Assert.assertEquals("\"text/plain\"", MediaTypes.convertToString(Collections.singleton(MediaType.TEXT_PLAIN_TYPE))); Assert.assertEquals("\"text/plain\", \"application/json\"", - MediaTypes.convertToString(Lists.newArrayList(MediaType.TEXT_PLAIN_TYPE, - MediaType.APPLICATION_JSON_TYPE))); + MediaTypes.convertToString(Arrays.asList(MediaType.TEXT_PLAIN_TYPE, + MediaType.APPLICATION_JSON_TYPE))); Assert.assertEquals("\"text/plain\", \"application/json\", \"text/html\"", - MediaTypes.convertToString(Lists.newArrayList(MediaType.TEXT_PLAIN_TYPE, - MediaType.APPLICATION_JSON_TYPE, - MediaType.TEXT_HTML_TYPE))); + MediaTypes.convertToString(Arrays.asList(MediaType.TEXT_PLAIN_TYPE, + MediaType.APPLICATION_JSON_TYPE, + MediaType.TEXT_HTML_TYPE))); } @Test diff --git a/core-common/src/test/java/org/glassfish/jersey/model/internal/CommonConfigTest.java b/core-common/src/test/java/org/glassfish/jersey/model/internal/CommonConfigTest.java index 82d1bb1a48..be5da3a02f 100644 --- a/core-common/src/test/java/org/glassfish/jersey/model/internal/CommonConfigTest.java +++ b/core-common/src/test/java/org/glassfish/jersey/model/internal/CommonConfigTest.java @@ -42,6 +42,7 @@ import java.io.IOException; import java.util.Collection; +import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.Map; @@ -83,8 +84,6 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import jersey.repackaged.com.google.common.collect.Maps; - /** * Test cases for {@link javax.ws.rs.core.Configuration}. * @@ -114,7 +113,7 @@ public void testSetProperties() throws Exception { config = config.property("foo", "bar"); assertEquals("bar", config.getConfiguration().getProperty("foo")); - final Map properties = Maps.newHashMap(); + final Map properties = new HashMap<>(); properties.put("hello", "world"); config = config.setProperties(properties); @@ -125,7 +124,7 @@ public void testSetProperties() throws Exception { assertEquals(1, config.getConfiguration().getProperties().size()); assertNull(config.getConfiguration().getProperty("one")); - config = config.setProperties(Maps.newHashMap()); + config = config.setProperties(new HashMap()); assertTrue(config.getConfiguration().getProperties().isEmpty()); } @@ -594,13 +593,15 @@ public Object aroundReadFrom(final ReaderInterceptorContext context) throws IOEx @Test public void testProviderOrderManual() throws Exception { - final InjectionManager injectionManager = Injections.createInjectionManager(); + InjectionManager injectionManager = Injections.createInjectionManager(); config.register(MidPriorityProvider.class, 500); config.register(LowPriorityProvider.class, 20); config.register(HighPriorityProvider.class, 150); ProviderBinder.bindProviders(config.getComponentBag(), injectionManager); + + injectionManager.completeRegistration(); final Iterable allProviders = Providers.getAllProviders(injectionManager, WriterInterceptor.class, new RankedComparator<>()); @@ -614,13 +615,14 @@ public void testProviderOrderManual() throws Exception { @Test public void testProviderOrderSemiAutomatic() throws Exception { - final InjectionManager injectionManager = Injections.createInjectionManager(); + InjectionManager injectionManager = Injections.createInjectionManager(); config.register(MidPriorityProvider.class, 50); config.register(LowPriorityProvider.class, 2000); config.register(HighPriorityProvider.class); ProviderBinder.bindProviders(config.getComponentBag(), injectionManager); + injectionManager.completeRegistration(); final Iterable allProviders = Providers.getAllProviders(injectionManager, WriterInterceptor.class, new RankedComparator<>()); @@ -634,15 +636,16 @@ public void testProviderOrderSemiAutomatic() throws Exception { @Test public void testProviderOrderAutomatic() throws Exception { - final InjectionManager injectionManager = Injections.createInjectionManager(); - + InjectionManager injectionManager = Injections.createInjectionManager(); config.register(MidPriorityProvider.class); config.register(LowPriorityProvider.class); config.register(HighPriorityProvider.class); ProviderBinder.bindProviders(config.getComponentBag(), injectionManager); + injectionManager.completeRegistration(); + final Iterable allProviders = - Providers.getAllProviders(injectionManager, WriterInterceptor.class, new RankedComparator()); + Providers.getAllProviders(injectionManager, WriterInterceptor.class, new RankedComparator<>()); final Iterator iterator = allProviders.iterator(); @@ -655,7 +658,7 @@ public void testProviderOrderAutomatic() throws Exception { @Test @SuppressWarnings("unchecked") public void testProviderOrderDifForContracts() throws Exception { - final Map, Integer> contracts = new IdentityHashMap, Integer>(); + final Map, Integer> contracts = new IdentityHashMap<>(); contracts.put(WriterInterceptor.class, ContractProvider.NO_PRIORITY); contracts.put(ReaderInterceptor.class, 2000); @@ -672,10 +675,12 @@ public void testProviderOrderDifForContracts() throws Exception { config.register(HighPriorityProvider.class, contracts); contracts.clear(); - final InjectionManager injectionManager = Injections.createInjectionManager(); + InjectionManager injectionManager = Injections.createInjectionManager(); ProviderBinder.bindProviders(config.getComponentBag(), injectionManager); + + injectionManager.completeRegistration(); final Iterable writerInterceptors = - Providers.getAllProviders(injectionManager, WriterInterceptor.class, new RankedComparator()); + Providers.getAllProviders(injectionManager, WriterInterceptor.class, new RankedComparator<>()); final Iterator writerIterator = writerInterceptors.iterator(); @@ -685,7 +690,7 @@ public void testProviderOrderDifForContracts() throws Exception { assertFalse(writerIterator.hasNext()); final Iterable readerInterceptors = - Providers.getAllProviders(injectionManager, ReaderInterceptor.class, new RankedComparator()); + Providers.getAllProviders(injectionManager, ReaderInterceptor.class, new RankedComparator<>()); final Iterator readerIterator = readerInterceptors.iterator(); @@ -806,7 +811,8 @@ public boolean configure(final FeatureContext config) { public void testConfigureFeatureHierarchy() throws Exception { config.register(ComplexFeature.class); - config.configureMetaProviders(Injections.createInjectionManager()); + InjectionManager injectionManager = Injections.createInjectionManager(); + config.configureMetaProviders(injectionManager); assertTrue(config.getConfiguration().isEnabled(ComplexFeature.class)); @@ -817,7 +823,8 @@ public void testConfigureFeatureHierarchy() throws Exception { @Test public void testConfigureFeatureRecursive() throws Exception { config.register(RecursiveFeature.class); - config.configureMetaProviders(Injections.createInjectionManager()); + InjectionManager injectionManager = Injections.createInjectionManager(); + config.configureMetaProviders(injectionManager); assertTrue(config.getConfiguration().isEnabled(RecursiveFeature.class)); assertEquals(1, config.getInstances().size()); @@ -831,7 +838,8 @@ public void testConfigureFeatureInstances() throws Exception { final SimpleFeatureA f2 = new SimpleFeatureA(true); config.register(f2); - config.configureMetaProviders(Injections.createInjectionManager()); + InjectionManager injectionManager = Injections.createInjectionManager(); + config.configureMetaProviders(injectionManager); assertTrue(config.getConfiguration().isEnabled(f1)); assertFalse(config.getConfiguration().isEnabled(f2)); @@ -912,8 +920,9 @@ public boolean configure(final FeatureContext context) { @Test public void testBinderConfiguringFeature() throws Exception { config.register(ContractBinderFeature.class); - final InjectionManager injectionManager = Injections.createInjectionManager(); + InjectionManager injectionManager = Injections.createInjectionManager(); config.configureMetaProviders(injectionManager); + injectionManager.completeRegistration(); assertTrue(config.isEnabled(ContractBinderFeature.class)); assertEquals(1, config.getInstances().size()); @@ -975,7 +984,7 @@ protected void configure() { } }); - final InjectionManager injectionManager = Injections.createInjectionManager(); + InjectionManager injectionManager = Injections.createInjectionManager(); config.configureMetaProviders(injectionManager); assertThat("Feature instance not injected", config.getProperty("instance-injected").toString(), is("true")); @@ -989,7 +998,7 @@ public void testFeatureInjectionsBindInFeature() throws Exception { config.register(InjectIntoFeatureClass.class); config.register(new InjectIntoFeatureInstance()); - final InjectionManager injectionManager = Injections.createInjectionManager(); + InjectionManager injectionManager = Injections.createInjectionManager(); config.configureMetaProviders(injectionManager); assertThat("Feature instance not injected", config.getProperty("instance-injected").toString(), is("true")); diff --git a/core-common/src/test/java/org/glassfish/jersey/process/internal/ExecutorProvidersTest.java b/core-common/src/test/java/org/glassfish/jersey/process/internal/ExecutorProvidersTest.java index 0413eec9d1..e6b46bf091 100644 --- a/core-common/src/test/java/org/glassfish/jersey/process/internal/ExecutorProvidersTest.java +++ b/core-common/src/test/java/org/glassfish/jersey/process/internal/ExecutorProvidersTest.java @@ -229,6 +229,7 @@ protected void configure() { public void setup() { injectionManager = Injections.createInjectionManager(this); ExecutorProviders.createInjectionBindings(injectionManager); + injectionManager.completeRegistration(); } /** diff --git a/core-common/src/test/java/org/glassfish/jersey/process/internal/RankedComparatorTest.java b/core-common/src/test/java/org/glassfish/jersey/process/internal/RankedComparatorTest.java index cfbd79d628..fe915c559b 100644 --- a/core-common/src/test/java/org/glassfish/jersey/process/internal/RankedComparatorTest.java +++ b/core-common/src/test/java/org/glassfish/jersey/process/internal/RankedComparatorTest.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -40,6 +40,7 @@ package org.glassfish.jersey.process.internal; import java.util.Collections; +import java.util.LinkedList; import java.util.List; import javax.annotation.Priority; @@ -50,8 +51,6 @@ import org.junit.Test; import static org.junit.Assert.assertTrue; -import jersey.repackaged.com.google.common.collect.Lists; - /** * Tests {@link org.glassfish.jersey.model.internal.RankedComparator}. * @author Miroslav Fuksa @@ -61,7 +60,7 @@ public class RankedComparatorTest { @Test public void testPriorityComparator() { - List> list = Lists.newLinkedList(); + List> list = new LinkedList<>(); list.add(new RankedProvider(new F1000())); list.add(new RankedProvider(new FF200())); list.add(new RankedProvider(new F0())); diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ApplicationConfigurator.java b/core-server/src/main/java/org/glassfish/jersey/server/ApplicationConfigurator.java new file mode 100644 index 0000000000..0c3a086dab --- /dev/null +++ b/core-server/src/main/java/org/glassfish/jersey/server/ApplicationConfigurator.java @@ -0,0 +1,150 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package org.glassfish.jersey.server; + +import java.util.Collection; +import java.util.Collections; + +import javax.ws.rs.core.Application; + +import javax.inject.Singleton; + +import org.glassfish.jersey.internal.BootstrapBag; +import org.glassfish.jersey.internal.BootstrapConfigurator; +import org.glassfish.jersey.internal.inject.Bindings; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.internal.util.collection.Value; +import org.glassfish.jersey.server.spi.ComponentProvider; + +/** + * Configurator which initializes and register {@link Application} instance into {@link InjectionManager} and + * {@link BootstrapBag}. + * + * @author Petr Bouda (petr.bouda at oracle.com) + */ +class ApplicationConfigurator implements BootstrapConfigurator { + + private Application application; + private Class applicationClass; + + /** + * Initialize {@link Application} from provided instance. + * + * @param application application instance. + */ + ApplicationConfigurator(Application application) { + this.application = application; + } + + /** + * Initialize {@link Application} from provided class. + * + * @param applicationClass application class. + */ + ApplicationConfigurator(Class applicationClass) { + this.applicationClass = applicationClass; + } + + @Override + public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) { + ServerBootstrapBag serverBag = (ServerBootstrapBag) bootstrapBag; + Application resultApplication; + + // ApplicationConfigurer was created with an Application instance. + if (application != null) { + if (application instanceof ResourceConfig) { + ResourceConfig rc = (ResourceConfig) application; + if (rc.getApplicationClass() != null) { + rc.setApplication(createApplication( + injectionManager, rc.getApplicationClass(), serverBag.getComponentProviders())); + } + } + resultApplication = application; + + // ApplicationConfigurer was created with an Application class. + } else { + resultApplication = createApplication(injectionManager, applicationClass, serverBag.getComponentProviders()); + } + + serverBag.setApplication(resultApplication); + injectionManager.register(Bindings.service(resultApplication).to(Application.class)); + } + + private static Application createApplication( + InjectionManager injectionManager, + Class applicationClass, + Value> componentProvidersValue) { + // need to handle ResourceConfig and Application separately as invoking forContract() on these + // will trigger the factories which we don't want at this point + if (applicationClass == ResourceConfig.class) { + return new ResourceConfig(); + } else if (applicationClass == Application.class) { + return new Application(); + } else { + Collection componentProviders = componentProvidersValue.get(); + boolean appClassBound = false; + for (ComponentProvider cp : componentProviders) { + if (cp.bind(applicationClass, Collections.emptySet())) { + appClassBound = true; + break; + } + } + if (!appClassBound) { + if (applicationClass.isAnnotationPresent(Singleton.class)) { + injectionManager.register(Bindings.serviceAsContract(applicationClass).in(Singleton.class)); + appClassBound = true; + } + } + final Application app = appClassBound + ? injectionManager.getInstance(applicationClass) + : injectionManager.createAndInitialize(applicationClass); + if (app instanceof ResourceConfig) { + final ResourceConfig _rc = (ResourceConfig) app; + final Class innerAppClass = _rc.getApplicationClass(); + if (innerAppClass != null) { + Application innerApp = createApplication(injectionManager, innerAppClass, componentProvidersValue); + _rc.setApplication(innerApp); + } + } + return app; + } + } +} diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ApplicationHandler.java b/core-server/src/main/java/org/glassfish/jersey/server/ApplicationHandler.java index 37f8b70e0a..3078237182 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/ApplicationHandler.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/ApplicationHandler.java @@ -42,79 +42,77 @@ import java.io.OutputStream; import java.lang.annotation.Annotation; -import java.lang.reflect.Type; import java.security.Principal; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.IdentityHashMap; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Spliterator; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.function.BiPredicate; -import java.util.function.Supplier; +import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import javax.ws.rs.HttpMethod; -import javax.ws.rs.NameBinding; import javax.ws.rs.RuntimeType; -import javax.ws.rs.container.ContainerRequestFilter; -import javax.ws.rs.container.ContainerResponseFilter; -import javax.ws.rs.container.DynamicFeature; -import javax.ws.rs.container.PreMatching; import javax.ws.rs.core.Application; import javax.ws.rs.core.Configuration; import javax.ws.rs.core.GenericType; import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.SecurityContext; import javax.ws.rs.ext.MessageBodyReader; import javax.ws.rs.ext.MessageBodyWriter; -import javax.ws.rs.ext.ReaderInterceptor; -import javax.ws.rs.ext.WriterInterceptor; - -import javax.inject.Singleton; import org.glassfish.jersey.CommonProperties; +import org.glassfish.jersey.internal.BootstrapBag; +import org.glassfish.jersey.internal.BootstrapConfigurator; +import org.glassfish.jersey.internal.ContextResolverFactory; import org.glassfish.jersey.internal.Errors; -import org.glassfish.jersey.internal.ServiceConfigurationError; -import org.glassfish.jersey.internal.ServiceFinder; +import org.glassfish.jersey.internal.ExceptionMapperFactory; +import org.glassfish.jersey.internal.ServiceFinderBinder; import org.glassfish.jersey.internal.Version; import org.glassfish.jersey.internal.inject.AbstractBinder; import org.glassfish.jersey.internal.inject.Binder; +import org.glassfish.jersey.internal.inject.Bindings; import org.glassfish.jersey.internal.inject.CompositeBinder; import org.glassfish.jersey.internal.inject.InjectionManager; import org.glassfish.jersey.internal.inject.Injections; +import org.glassfish.jersey.internal.inject.InstanceBinding; import org.glassfish.jersey.internal.inject.ProviderBinder; import org.glassfish.jersey.internal.inject.Providers; -import org.glassfish.jersey.internal.util.ReflectionHelper; -import org.glassfish.jersey.internal.util.collection.LazyValue; +import org.glassfish.jersey.internal.spi.AutoDiscoverable; import org.glassfish.jersey.internal.util.collection.Ref; -import org.glassfish.jersey.internal.util.collection.Value; -import org.glassfish.jersey.internal.util.collection.Values; import org.glassfish.jersey.message.MessageBodyWorkers; +import org.glassfish.jersey.message.internal.MessageBodyFactory; +import org.glassfish.jersey.message.internal.MessagingBinders; import org.glassfish.jersey.message.internal.NullOutputStream; import org.glassfish.jersey.model.ContractProvider; import org.glassfish.jersey.model.internal.ComponentBag; import org.glassfish.jersey.model.internal.RankedComparator; -import org.glassfish.jersey.model.internal.RankedComparator.Order; import org.glassfish.jersey.model.internal.RankedProvider; import org.glassfish.jersey.process.internal.ChainableStage; import org.glassfish.jersey.process.internal.ExecutorProviders; +import org.glassfish.jersey.process.internal.RequestScope; import org.glassfish.jersey.process.internal.Stage; import org.glassfish.jersey.process.internal.Stages; import org.glassfish.jersey.server.internal.JerseyRequestTimeoutHandler; import org.glassfish.jersey.server.internal.JerseyResourceContext; import org.glassfish.jersey.server.internal.LocalizationMessages; import org.glassfish.jersey.server.internal.ProcessingProviders; +import org.glassfish.jersey.server.internal.inject.ParamConverterConfigurator; +import org.glassfish.jersey.server.internal.inject.ParamExtractorConfigurator; +import org.glassfish.jersey.server.internal.inject.ValueSupplierProviderConfigurator; import org.glassfish.jersey.server.internal.monitoring.ApplicationEventImpl; import org.glassfish.jersey.server.internal.monitoring.CompositeApplicationEventListener; import org.glassfish.jersey.server.internal.monitoring.MonitoringContainerListener; @@ -127,19 +125,15 @@ import org.glassfish.jersey.server.model.Resource; import org.glassfish.jersey.server.model.ResourceModel; import org.glassfish.jersey.server.model.internal.ModelErrors; +import org.glassfish.jersey.server.model.internal.ResourceMethodInvokerConfigurator; import org.glassfish.jersey.server.monitoring.ApplicationEvent; import org.glassfish.jersey.server.monitoring.ApplicationEventListener; import org.glassfish.jersey.server.spi.ComponentProvider; import org.glassfish.jersey.server.spi.Container; import org.glassfish.jersey.server.spi.ContainerLifecycleListener; +import org.glassfish.jersey.server.spi.ContainerProvider; import org.glassfish.jersey.server.spi.ContainerResponseWriter; -import org.glassfish.jersey.server.spi.ExternalRequestScope; - -import jersey.repackaged.com.google.common.base.Function; -import jersey.repackaged.com.google.common.collect.Collections2; -import jersey.repackaged.com.google.common.collect.Lists; -import jersey.repackaged.com.google.common.collect.Sets; -import jersey.repackaged.com.google.common.util.concurrent.AbstractFuture; +import org.glassfish.jersey.server.spi.internal.ValueSupplierProvider; /** * Jersey server-side application handler. @@ -209,39 +203,40 @@ public String getAuthenticationScheme() { } }; - private class ApplicationBinder extends AbstractBinder { - - private class JaxrsApplicationProvider implements Supplier { + /** + * Configurator which initializes and register {@link ApplicationHandler} and {@link Configuration} instances into + * {@link InjectionManager} and {@link BootstrapBag}. + * + * @author Petr Bouda (petr.bouda at oracle.com) + */ + private class RuntimeConfigConfigurator implements BootstrapConfigurator { - @Override - public Application get() { - return ApplicationHandler.this.application; - } - } + @Override + public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) { + ServerBootstrapBag serverBag = (ServerBootstrapBag) bootstrapBag; + serverBag.setApplicationHandler(ApplicationHandler.this); + serverBag.setConfiguration(ResourceConfig.createRuntimeConfig(serverBag.getApplication())); - private class RuntimeConfigProvider implements Supplier { + // TODO: Do we really need these three bindings in DI provider? What JAX-RS specification says? + InstanceBinding handlerBinding = + Bindings.service(ApplicationHandler.this) + .to(ApplicationHandler.class); - @Override - public ServerConfig get() { - return ApplicationHandler.this.runtimeConfig; - } - } + InstanceBinding configBinding = + Bindings.service(serverBag.getRuntimeConfig()) + .to(Configuration.class) + .to(ServerConfig.class); - @Override - protected void configure() { - bindFactory(new RuntimeConfigProvider()).to(ServerConfig.class).to(Configuration.class).in(Singleton.class); - bindFactory(new JaxrsApplicationProvider()).to(Application.class).in(Singleton.class); - bind(ApplicationHandler.this).to(ApplicationHandler.class); + injectionManager.register(handlerBinding); + injectionManager.register(configBinding); } } - private final Application application; - private final ResourceConfig runtimeConfig; - private final ServerRuntime runtime; - private final Iterable containerLifecycleListeners; - - private final InjectionManager injectionManager; - + private Application application; + private ResourceConfig runtimeConfig; + private ServerRuntime runtime; + private Iterable containerLifecycleListeners; + private InjectionManager injectionManager; private MessageBodyWorkers msgBodyWorkers; /** @@ -260,14 +255,7 @@ public ApplicationHandler() { * application handler. */ public ApplicationHandler(final Class jaxrsApplicationClass) { - this.injectionManager = Injections.createInjectionManager(); - this.injectionManager.register(CompositeBinder.wrap(new ServerBinder(null, injectionManager), new ApplicationBinder())); - - LazyValue> componentProviders = getLazyInitializedComponentProviders(injectionManager); - this.application = createApplication(jaxrsApplicationClass, componentProviders); - this.runtimeConfig = ResourceConfig.createRuntimeConfig(application); - this.runtime = Errors.processWithException(() -> initialize(componentProviders.get())); - this.containerLifecycleListeners = Providers.getAllProviders(injectionManager, ContainerLifecycleListener.class); + initialize(new ApplicationConfigurator(jaxrsApplicationClass), Injections.createInjectionManager(), null); } /** @@ -303,87 +291,52 @@ public ApplicationHandler(final Application application, final Binder customBind * @param parentManager parent used in {@link InjectionManager} for a specific DI provider. */ public ApplicationHandler(final Application application, final Binder customBinder, final Object parentManager) { - this.injectionManager = Injections.createInjectionManager(parentManager); - this.injectionManager.register(CompositeBinder.wrap( - new ServerBinder(application.getProperties(), injectionManager), new ApplicationBinder(), customBinder)); - - final LazyValue> componentProviders = getLazyInitializedComponentProviders(injectionManager); - - this.application = application; - if (application instanceof ResourceConfig) { - final ResourceConfig rc = (ResourceConfig) application; - if (rc.getApplicationClass() != null) { - rc.setApplication(createApplication(rc.getApplicationClass(), componentProviders)); - } - } - this.runtimeConfig = ResourceConfig.createRuntimeConfig(application); - this.runtime = Errors.processWithException(() -> initialize(componentProviders.get())); - this.containerLifecycleListeners = Providers.getAllProviders(injectionManager, ContainerLifecycleListener.class); + initialize(new ApplicationConfigurator(application), Injections.createInjectionManager(parentManager), customBinder); } - private Application createApplication(final Class applicationClass, - final Value> componentProvidersValue) { - // need to handle ResourceConfig and Application separately as invoking forContract() on these - // will trigger the factories which we don't want at this point - if (applicationClass == ResourceConfig.class) { - return new ResourceConfig(); - } else if (applicationClass == Application.class) { - return new Application(); - } else { - Iterable componentProviders = componentProvidersValue.get(); - boolean appClassBound = false; - for (ComponentProvider cp : componentProviders) { - if (cp.bind(applicationClass, Collections.emptySet())) { - appClassBound = true; - break; - } - } - if (!appClassBound) { - if (applicationClass.isAnnotationPresent(Singleton.class)) { - Binder binder = new AbstractBinder() { - @Override - protected void configure() { - bindAsContract(applicationClass).in(Singleton.class); - } - }; - injectionManager.register(binder); - appClassBound = true; - } - } - final Application app = appClassBound - ? injectionManager.getInstance(applicationClass) : injectionManager.createAndInitialize(applicationClass); - if (app instanceof ResourceConfig) { - final ResourceConfig _rc = (ResourceConfig) app; - final Class innerAppClass = _rc.getApplicationClass(); - if (innerAppClass != null) { - final Application innerApp = createApplication(innerAppClass, componentProvidersValue); - _rc.setApplication(innerApp); - } - } - return app; - } - } - - private static LazyValue> getLazyInitializedComponentProviders( - InjectionManager injectionManager) { - return Values.lazy((Value>) () -> { - // Registering Injection Bindings - List result = new LinkedList<>(); - - // Registering Injection Bindings - for (final RankedProvider rankedProvider : getRankedComponentProviders()) { - final ComponentProvider provider = rankedProvider.getProvider(); - provider.initialize(injectionManager); - result.add(provider); - } - return result; - }); + private void initialize(ApplicationConfigurator applicationConfigurator, InjectionManager injectionManager, + Binder customBinder) { + this.injectionManager = injectionManager; + this.injectionManager.register(CompositeBinder.wrap(new ServerBinder(), customBinder)); + + ServerBootstrapBag bootstrapBag = new ServerBootstrapBag(); + List bootstrapConfigurators = Arrays.asList( + new RequestScope.RequestScopeConfigurator(), + new ParamConverterConfigurator(), + new ParamExtractorConfigurator(), + new ValueSupplierProviderConfigurator(), + new JerseyResourceContextConfigurator(), + new ComponentProviderConfigurator(), + applicationConfigurator, + new RuntimeConfigConfigurator(), + new ContextResolverFactory.ContextResolversConfigurator(), + new MessageBodyFactory.MessageBodyWorkersConfigurator(), + new ExceptionMapperFactory.ExceptionMappersConfigurator(), + new ResourceMethodInvokerConfigurator(), + new ProcessingProvidersConfigurator()); + + bootstrapConfigurators.forEach(configurator -> configurator.init(injectionManager, bootstrapBag)); + + this.application = bootstrapBag.getApplication(); + this.runtimeConfig = bootstrapBag.getRuntimeConfig(); + + AbstractBinder dependentBinders = + CompositeBinder.wrap(new MessagingBinders.MessageBodyProviders(application.getProperties(), RuntimeType.SERVER), + new ServiceFinderBinder<>(ContainerProvider.class, application.getProperties(), RuntimeType.SERVER), + new ServiceFinderBinder<>(AutoDiscoverable.class, application.getProperties(), RuntimeType.SERVER)); + injectionManager.register(dependentBinders); + + this.runtime = Errors.processWithException( + () -> initialize(injectionManager, bootstrapConfigurators, bootstrapBag)); + this.containerLifecycleListeners = Providers.getAllProviders(injectionManager, ContainerLifecycleListener.class); } /** * Assumes the configuration field is initialized with a valid ResourceConfig. */ - private ServerRuntime initialize(Iterable componentProviders) { + private ServerRuntime initialize(InjectionManager injectionManager, + List bootstrapConfigurators, ServerBootstrapBag bootstrapBag) { + this.injectionManager = injectionManager; LOGGER.config(LocalizationMessages.INIT_MSG(Version.getBuildId())); // Lock original ResourceConfig. @@ -400,9 +353,17 @@ private ServerRuntime initialize(Iterable componentProviders) Boolean.FALSE, Boolean.class); + // Temporary way to eliminate injection manager in deeper bootstrap processing. + final java.util.function.Function, ?> createServiceFunction = + serviceType -> Injections.getOrCreate(injectionManager, serviceType); + + final Collection valueSupplierProviders; final ResourceBag resourceBag; final ProcessingProviders processingProviders; + final JerseyResourceContext jerseyResourceContext = bootstrapBag.getResourceContext(); + final Collection componentProviders = bootstrapBag.getComponentProviders().get(); final ComponentBag componentBag; + ResourceModel resourceModel; CompositeApplicationEventListener compositeListener = null; @@ -419,74 +380,38 @@ private ServerRuntime initialize(Iterable componentProviders) // Configure binders and features. runtimeConfig.configureMetaProviders(injectionManager); - final ResourceBag.Builder resourceBagBuilder = new ResourceBag.Builder(); + ResourceBagConfigurator resourceBagConfigurator = new ResourceBagConfigurator(); + resourceBagConfigurator.init(injectionManager, bootstrapBag); + resourceBag = bootstrapBag.getResourceBag(); - // Adding programmatic resource models - for (final Resource programmaticResource : runtimeConfig.getResources()) { - resourceBagBuilder.registerProgrammaticResource(programmaticResource); - } + runtimeConfig.lock(); - // Introspecting classes & instances - for (final Class c : runtimeConfig.getClasses()) { - try { - final Resource resource = Resource.from(c, disableValidation); - if (resource != null) { - resourceBagBuilder.registerResource(c, resource); - } - } catch (final IllegalArgumentException ex) { - LOGGER.warning(ex.getMessage()); - } - } + componentBag = runtimeConfig.getComponentBag(); - for (final Object o : runtimeConfig.getSingletons()) { - try { - final Resource resource = Resource.from(o.getClass(), disableValidation); - if (resource != null) { - resourceBagBuilder.registerResource(o, resource); - } - } catch (final IllegalArgumentException ex) { - LOGGER.warning(ex.getMessage()); - } - } + ExternalRequestScopeConfigurator externalRequestScopeConfigurator = new ExternalRequestScopeConfigurator(); + externalRequestScopeConfigurator.init(injectionManager, bootstrapBag); - resourceBag = resourceBagBuilder.build(); + // Adds all providers from resource config to InjectionManager -> BootstrapConfigurators are able to work with these + // services and get them. + bindProvidersAndResources(injectionManager, bootstrapBag, componentBag, resourceBag.classes, + resourceBag.instances); - runtimeConfig.lock(); + resourceModel = new ResourceModel.Builder(resourceBag.getRootResources(), false).build(); + resourceModel = processResourceModel(resourceModel); + bindEnhancingResourceClasses(injectionManager, bootstrapBag, resourceModel, resourceBag); - componentBag = runtimeConfig.getComponentBag(); - final Class[] extScopes = ServiceFinder.find(ExternalRequestScope.class, true).toClassArray(); - - boolean extScopeBound = false; - - if (extScopes.length == 1) { - for (final ComponentProvider p : componentProviders) { - if (p.bind(extScopes[0], new HashSet>() {{ - add(ExternalRequestScope.class); - }})) { - extScopeBound = true; - break; - } - } - } else if (extScopes.length > 1) { - if (LOGGER.isLoggable(Level.WARNING)) { - final StringBuilder scopeList = new StringBuilder("\n"); - for (final Class ers : extScopes) { - scopeList.append(" ").append(ers.getTypeParameters()[0]).append('\n'); - } - LOGGER.warning(LocalizationMessages.WARNING_TOO_MANY_EXTERNAL_REQ_SCOPES(scopeList.toString())); - } - } + // All service are registered in InjectionManager + injectionManager.completeRegistration(); - if (!extScopeBound) { - injectionManager.register(new ServerRuntime.NoopExternalRequestScopeBinder()); - } + bootstrapConfigurators.forEach(configurator -> configurator.postInit(injectionManager, bootstrapBag)); - bindProvidersAndResources(componentProviders, componentBag, resourceBag.classes, resourceBag.instances); - for (final ComponentProvider componentProvider : componentProviders) { - componentProvider.done(); - } + componentProviders.forEach(ComponentProvider::done); + + msgBodyWorkers = bootstrapBag.getMessageBodyWorkers(); + processingProviders = bootstrapBag.getProcessingProviders(); + valueSupplierProviders = bootstrapBag.getValueSupplierProviders(); - final Iterable appEventListeners = + Iterable appEventListeners = Providers.getAllProviders(injectionManager, ApplicationEventListener.class, new RankedComparator<>()); if (appEventListeners.iterator().hasNext()) { @@ -496,19 +421,8 @@ private ServerRuntime initialize(Iterable componentProviders) null)); } - processingProviders = getProcessingProviders(componentBag); - - // initialize processing provider reference - final GenericType> refGenericType = new GenericType>() { - }; - final Ref refProcessingProvider = injectionManager.getInstance(refGenericType.getType()); - refProcessingProvider.set(processingProviders); - - resourceModel = new ResourceModel.Builder(resourceBag.getRootResources(), false).build(); - resourceModel = processResourceModel(resourceModel); - if (!disableValidation) { - final ComponentModelValidator validator = new ComponentModelValidator(injectionManager); + final ComponentModelValidator validator = new ComponentModelValidator(valueSupplierProviders, msgBodyWorkers); validator.validate(resourceModel); } @@ -525,31 +439,38 @@ private ServerRuntime initialize(Iterable componentProviders) } } - bindEnhancingResourceClasses(resourceModel, resourceBag, componentProviders); - + // TODO: replace by ExecutorProviderConfigurator ExecutorProviders.createInjectionBindings(injectionManager); // initiate resource model into JerseyResourceContext - final JerseyResourceContext jerseyResourceContext = injectionManager.getInstance(JerseyResourceContext.class); jerseyResourceContext.setResourceModel(resourceModel); - msgBodyWorkers = injectionManager.getInstance(MessageBodyWorkers.class); - // assembly request processing chain - final ReferencesInitializer referencesInitializer = injectionManager.createAndInitialize(ReferencesInitializer.class); + GenericType> requestProcessingType = new GenericType>() {}; + ReferencesInitializer referencesInitializer = new ReferencesInitializer(injectionManager, + () -> injectionManager.getInstance(requestProcessingType.getType())); + + Iterable> allRankedProviders = + Providers.getAllRankedProviders(injectionManager, ModelProcessor.class); + Iterable modelProcessors = + Providers.sortRankedProviders(new RankedComparator<>(), allRankedProviders); + final ContainerFilteringStage preMatchRequestFilteringStage = new ContainerFilteringStage( processingProviders.getPreMatchFilters(), processingProviders.getGlobalResponseFilters()); final ChainableStage routingStage = Routing.forModel(resourceModel.getRuntimeResourceModel()) - .beanManager(injectionManager) .resourceContext(jerseyResourceContext) .configuration(runtimeConfig) .entityProviders(msgBodyWorkers) + .valueSupplierProviders(valueSupplierProviders) + .modelProcessors(modelProcessors) + .createService(createServiceFunction) .processingProviders(processingProviders) + .resourceMethodInvokerBuilder(bootstrapBag.getResourceMethodInvokerBuilder()) .buildStage(); final ContainerFilteringStage resourceFilteringStage = new ContainerFilteringStage(processingProviders.getGlobalRequestFilters(), null); - /** + /* * Root linear request acceptor. This is the main entry point for the whole request processing. */ final Stage rootStage = Stages @@ -559,8 +480,8 @@ private ServerRuntime initialize(Iterable componentProviders) .to(resourceFilteringStage) .build(Routing.matchedEndpointExtractor()); - final ServerRuntime serverRuntime = injectionManager.createAndInitialize(ServerRuntime.Builder.class) - .build(rootStage, compositeListener, processingProviders); + ServerRuntime serverRuntime = ServerRuntime.createServerRuntime( + injectionManager, bootstrapBag, rootStage, compositeListener, processingProviders); // Inject instances. for (final Object instance : componentBag.getInstances(ComponentBag.excludeMetaProviders(injectionManager))) { @@ -612,8 +533,13 @@ private static void logApplicationInitConfiguration(final InjectionManager injec final Set messageBodyWriters; if (LOGGER.isLoggable(Level.FINE)) { - messageBodyReaders = Sets.newHashSet(Providers.getAllProviders(injectionManager, MessageBodyReader.class)); - messageBodyWriters = Sets.newHashSet(Providers.getAllProviders(injectionManager, MessageBodyWriter.class)); + Spliterator mbrSpliterator = + Providers.getAllProviders(injectionManager, MessageBodyReader.class).spliterator(); + messageBodyReaders = StreamSupport.stream(mbrSpliterator, false).collect(Collectors.toSet()); + + Spliterator mbwSpliterator = + Providers.getAllProviders(injectionManager, MessageBodyWriter.class).spliterator(); + messageBodyWriters = StreamSupport.stream(mbwSpliterator, false).collect(Collectors.toSet()); } else { messageBodyReaders = Providers.getCustomProviders(injectionManager, MessageBodyReader.class); messageBodyWriters = Providers.getCustomProviders(injectionManager, MessageBodyWriter.class); @@ -640,9 +566,9 @@ private static void logApplicationInitConfiguration(final InjectionManager injec printProviders(LocalizationMessages.LOGGING_DYNAMIC_FEATURES(), processingProviders.getDynamicFeatures(), sb); printProviders(LocalizationMessages.LOGGING_MESSAGE_BODY_READERS(), - Collections2.transform(messageBodyReaders, new WorkersToStringTransform()), sb); + messageBodyReaders.stream().map(new WorkersToStringTransform<>()).collect(Collectors.toList()), sb); printProviders(LocalizationMessages.LOGGING_MESSAGE_BODY_WRITERS(), - Collections2.transform(messageBodyWriters, new WorkersToStringTransform()), sb); + messageBodyWriters.stream().map(new WorkersToStringTransform<>()).collect(Collectors.toList()), sb); LOGGER.log(Level.CONFIG, sb.toString()); } @@ -687,84 +613,11 @@ private static void printProviders(final String title, final Iterable pro } } - private static Iterable> getRankedComponentProviders() throws ServiceConfigurationError { - final List> result = new LinkedList<>(); - - for (final ComponentProvider provider : ServiceFinder.find(ComponentProvider.class)) { - result.add(new RankedProvider<>(provider)); - } - Collections.sort(result, new RankedComparator(Order.DESCENDING)); - return result; - } - - private ProcessingProviders getProcessingProviders(final ComponentBag componentBag) { - - // scan for NameBinding annotations attached to the application class - final Collection> applicationNameBindings = ReflectionHelper.getAnnotationTypes( - ResourceConfig.unwrapApplication(runtimeConfig).getClass(), NameBinding.class); - - final MultivaluedMap, Class> nameBoundRespFiltersInverse = - new MultivaluedHashMap<>(); - final MultivaluedMap, Class> nameBoundReqFiltersInverse = - new MultivaluedHashMap<>(); - final MultivaluedMap, Class> nameBoundReaderInterceptorsInverse = - new MultivaluedHashMap<>(); - final MultivaluedMap, Class> nameBoundWriterInterceptorsInverse = - new MultivaluedHashMap<>(); - - // find all filters, interceptors and dynamic features - final Iterable> responseFilters = - Providers.getAllRankedProviders(injectionManager, ContainerResponseFilter.class); - - final MultivaluedMap, RankedProvider> nameBoundResponseFilters = - filterNameBound(responseFilters, null, componentBag, applicationNameBindings, nameBoundRespFiltersInverse); - - final Iterable> requestFilters = - Providers.getAllRankedProviders(injectionManager, ContainerRequestFilter.class); - - final List> preMatchFilters = Lists.newArrayList(); - - final MultivaluedMap, RankedProvider> nameBoundReqFilters = - filterNameBound(requestFilters, preMatchFilters, componentBag, applicationNameBindings, - nameBoundReqFiltersInverse); - - final Iterable> readerInterceptors = - Providers.getAllRankedProviders(injectionManager, ReaderInterceptor.class); - - final MultivaluedMap, RankedProvider> nameBoundReaderInterceptors = - filterNameBound(readerInterceptors, null, componentBag, applicationNameBindings, - nameBoundReaderInterceptorsInverse); - - final Iterable> writerInterceptors = - Providers.getAllRankedProviders(injectionManager, WriterInterceptor.class); - - final MultivaluedMap, RankedProvider> nameBoundWriterInterceptors = - filterNameBound(writerInterceptors, null, componentBag, applicationNameBindings, - nameBoundWriterInterceptorsInverse); - - final Iterable dynamicFeatures = Providers.getAllProviders(injectionManager, DynamicFeature.class); - - return new ProcessingProviders(nameBoundReqFilters, - nameBoundReqFiltersInverse, - nameBoundResponseFilters, - nameBoundRespFiltersInverse, - nameBoundReaderInterceptors, - nameBoundReaderInterceptorsInverse, - nameBoundWriterInterceptors, - nameBoundWriterInterceptorsInverse, - requestFilters, - preMatchFilters, - responseFilters, - readerInterceptors, - writerInterceptors, - dynamicFeatures); - } - private ResourceModel processResourceModel(ResourceModel resourceModel) { - final Iterable> allRankedProviders = Providers.getAllRankedProviders(injectionManager, - ModelProcessor.class); - final Iterable modelProcessors = Providers.sortRankedProviders(new RankedComparator(), - allRankedProviders); + Iterable> allRankedProviders = + Providers.getAllRankedProviders(injectionManager, ModelProcessor.class); + Iterable modelProcessors = + Providers.sortRankedProviders(new RankedComparator<>(), allRankedProviders); for (final ModelProcessor modelProcessor : modelProcessors) { resourceModel = modelProcessor.processResourceModel(resourceModel, getConfiguration()); @@ -773,12 +626,12 @@ private ResourceModel processResourceModel(ResourceModel resourceModel) { } private void bindEnhancingResourceClasses( - final ResourceModel resourceModel, - final ResourceBag resourceBag, - final Iterable componentProviders) { - - final Set> newClasses = Sets.newHashSet(); - final Set newInstances = Sets.newHashSet(); + InjectionManager injectionManager, + ServerBootstrapBag bootstrapBag, + ResourceModel resourceModel, + ResourceBag resourceBag) { + final Set> newClasses = new HashSet<>(); + final Set newInstances = new HashSet<>(); for (final Resource res : resourceModel.getRootResources()) { newClasses.addAll(res.getHandlerClasses()); newInstances.addAll(res.getHandlerInstances()); @@ -787,91 +640,19 @@ private void bindEnhancingResourceClasses( newInstances.removeAll(resourceBag.instances); final ComponentBag emptyComponentBag = ComponentBag.newInstance(input -> false); - bindProvidersAndResources(componentProviders, emptyComponentBag, newClasses, newInstances); - } - - /** - * Takes collection of all filters/interceptors (either request/reader or response/writer) - * and separates out all name-bound filters/interceptors, returns them as a separate MultivaluedMap, - * mapping the name-bound annotation to the list of name-bound filters/interceptors. The same key values - * are also added into the inverse map passed in {@code inverseNameBoundMap}. - *

- * Note, the name-bound filters/interceptors are removed from the original filters/interceptors collection. - * If non-null collection is passed in the postMatching parameter (applicable for filters only), - * this method also removes all the global - * postMatching filters from the original collection and adds them to the collection passed in the postMatching - * parameter. - * - * @param all Collection of all filters to be processed. - * @param preMatchingFilters Collection into which pre-matching filters should be added. - * @param componentBag Component bag - * @param applicationNameBindings Collection of name binding annotations attached to the JAX-RS application. - * @param inverseNameBoundMap Inverse name bound map into which the name bound providers should be inserted. The keys - * are providers (filters, interceptor) - * @return {@link MultivaluedMap} of all name-bound filters. - */ - private static MultivaluedMap, RankedProvider> filterNameBound( - final Iterable> all, - final Collection> preMatchingFilters, - final ComponentBag componentBag, - final Collection> applicationNameBindings, - final MultivaluedMap, Class> inverseNameBoundMap) { - - final MultivaluedMap, RankedProvider> result - = new MultivaluedHashMap<>(); - - for (final Iterator> it = all.iterator(); it.hasNext(); ) { - final RankedProvider provider = it.next(); - Class providerClass = provider.getProvider().getClass(); - final Set contractTypes = provider.getContractTypes(); - if (contractTypes != null && !contractTypes.contains(providerClass)) { - providerClass = ReflectionHelper.theMostSpecificTypeOf(contractTypes); - } - - ContractProvider model = componentBag.getModel(providerClass); - if (model == null) { - // the provider was (most likely) bound in HK2 externally - model = ComponentBag.modelFor(providerClass); - } - - final boolean preMatching = providerClass.getAnnotation(PreMatching.class) != null; - if (preMatching && preMatchingFilters != null) { - it.remove(); - preMatchingFilters.add(new RankedProvider<>((ContainerRequestFilter) provider.getProvider(), - model.getPriority(ContainerRequestFilter.class))); - } - - boolean nameBound = model.isNameBound(); - if (nameBound - && !applicationNameBindings.isEmpty() - && applicationNameBindings.containsAll(model.getNameBindings())) { - // override the name-bound flag - nameBound = false; - } - - if (nameBound) { // not application-bound - if (!preMatching) { - it.remove(); - for (final Class binding : model.getNameBindings()) { - result.add(binding, provider); - inverseNameBoundMap.add(provider, binding); - } - } else { - LOGGER.warning(LocalizationMessages.PREMATCHING_ALSO_NAME_BOUND(providerClass)); - } - } - } - - return result; + bindProvidersAndResources(injectionManager, bootstrapBag, emptyComponentBag, newClasses, newInstances); } private void bindProvidersAndResources( - final Iterable componentProviders, - final ComponentBag componentBag, - final Collection> resourceClasses, - final Collection resourceInstances) { + InjectionManager injectionManager, + ServerBootstrapBag bootstrapBag, + ComponentBag componentBag, + Collection> resourceClasses, + Collection resourceInstances) { + + Collection componentProviders = bootstrapBag.getComponentProviders().get(); + JerseyResourceContext resourceContext = bootstrapBag.getResourceContext(); - JerseyResourceContext resourceContext = injectionManager.getInstance(JerseyResourceContext.class); Set> registeredClasses = runtimeConfig.getRegisteredClasses(); /* @@ -895,13 +676,14 @@ private void bindProvidersAndResources( !registeredClasses.contains(resourceClass), true); - Set> componentClassses = componentBag.getClasses(ComponentBag.excludeMetaProviders(injectionManager)).stream() + Set> componentClasses = + componentBag.getClasses(ComponentBag.excludeMetaProviders(injectionManager)).stream() .filter(correctlyConfigured) .collect(Collectors.toSet()); // Merge programmatic resource classes with component classes. Set> classes = Collections.newSetFromMap(new IdentityHashMap<>()); - classes.addAll(componentClassses); + classes.addAll(componentClasses); classes.addAll(resourceClasses); // Bind classes. @@ -920,14 +702,15 @@ private void bindProvidersAndResources( if (model != null && !correctlyConfiguredResource.test(componentClass, model)) { model = null; } - resourceContext.unsafeBindResource(componentClass, model, injectionManager); + resourceContext.unsafeBindResource(componentClass, model); } else { ProviderBinder.bindProvider(componentClass, model, injectionManager); } } // Merge programmatic resource instances with other component instances. - Set instances = componentBag.getInstances(ComponentBag.excludeMetaProviders(injectionManager)).stream() + Set instances = + componentBag.getInstances(ComponentBag.excludeMetaProviders(injectionManager)).stream() .filter(instance -> correctlyConfigured.test(instance.getClass())) .collect(Collectors.toSet()); instances.addAll(resourceInstances); @@ -939,7 +722,7 @@ private void bindProvidersAndResources( if (model != null && !correctlyConfiguredResource.test(component.getClass(), model)) { model = null; } - resourceContext.unsafeBindResource(component, model, injectionManager); + resourceContext.unsafeBindResource(component, model); } else { ProviderBinder.bindProvider(component, model, injectionManager); } @@ -951,7 +734,7 @@ private boolean bindWithComponentProvider( final ContractProvider providerModel, final Iterable componentProviders) { - final Set> contracts = providerModel == null ? Collections.>emptySet() : providerModel.getContracts(); + final Set> contracts = providerModel == null ? Collections.emptySet() : providerModel.getContracts(); for (final ComponentProvider provider : componentProviders) { if (provider.bind(component, contracts)) { return true; @@ -992,7 +775,7 @@ public Future apply(final ContainerRequest request, return responseFuture; } - private static class FutureResponseWriter extends AbstractFuture implements ContainerResponseWriter { + private static class FutureResponseWriter extends CompletableFuture implements ContainerResponseWriter { private ContainerResponse response = null; @@ -1040,26 +823,20 @@ public void commit() { current.setEntity(null); } requestTimeoutHandler.close(); - super.set(current); + super.complete(current); } } @Override public void failure(final Throwable error) { requestTimeoutHandler.close(); - super.setException(error); + super.completeExceptionally(error); } @Override public boolean enableResponseBuffering() { return true; } - - @Override - protected void interruptTask() { - // TODO implement cancellation logic. - } - } /** diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ComponentProviderConfigurator.java b/core-server/src/main/java/org/glassfish/jersey/server/ComponentProviderConfigurator.java new file mode 100644 index 0000000000..af3df6bd01 --- /dev/null +++ b/core-server/src/main/java/org/glassfish/jersey/server/ComponentProviderConfigurator.java @@ -0,0 +1,90 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package org.glassfish.jersey.server; + +import java.util.Collection; +import java.util.Comparator; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import org.glassfish.jersey.internal.BootstrapBag; +import org.glassfish.jersey.internal.BootstrapConfigurator; +import org.glassfish.jersey.internal.ServiceConfigurationError; +import org.glassfish.jersey.internal.ServiceFinder; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.internal.util.collection.LazyValue; +import org.glassfish.jersey.internal.util.collection.Value; +import org.glassfish.jersey.internal.util.collection.Values; +import org.glassfish.jersey.model.internal.RankedComparator; +import org.glassfish.jersey.model.internal.RankedProvider; +import org.glassfish.jersey.server.spi.ComponentProvider; + +/** + * Configurator which initializes and register {@link ComponentProvider} instances into {@link BootstrapBag}. + * + * @author Petr Bouda (petr.bouda at oracle.com) + */ +class ComponentProviderConfigurator implements BootstrapConfigurator { + + private static final Comparator> RANKED_COMPARATOR = + new RankedComparator<>(RankedComparator.Order.DESCENDING); + + @Override + public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) { + ServerBootstrapBag serverBag = (ServerBootstrapBag) bootstrapBag; + + // There are situation in which ComponentProviders are not needed therefore their entire initialization is wrapped + // into a lazy block. + LazyValue> componentProviders = + Values.lazy((Value>) () -> getRankedComponentProviders().stream() + .map(RankedProvider::getProvider) + .peek(provider -> provider.initialize(injectionManager)) + .collect(Collectors.toList())); + serverBag.setComponentProviders(componentProviders); + } + + private static Collection> getRankedComponentProviders() throws ServiceConfigurationError { + return StreamSupport.stream(ServiceFinder.find(ComponentProvider.class).spliterator(), false) + .map(RankedProvider::new) + .sorted(RANKED_COMPARATOR) + .collect(Collectors.toList()); + } +} diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ContainerMessageBodyWorkersInitializer.java b/core-server/src/main/java/org/glassfish/jersey/server/ContainerMessageBodyWorkersInitializer.java index f898b8b5be..593e7f127a 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/ContainerMessageBodyWorkersInitializer.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/ContainerMessageBodyWorkersInitializer.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2014 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -39,14 +39,14 @@ */ package org.glassfish.jersey.server; +import java.util.function.Function; + import javax.inject.Inject; import javax.inject.Provider; import org.glassfish.jersey.message.MessageBodyWorkers; import org.glassfish.jersey.server.internal.process.RequestProcessingContext; -import jersey.repackaged.com.google.common.base.Function; - /** * Function that can be put to an acceptor chain to properly initialize * {@link org.glassfish.jersey.message.MessageBodyWorkers} instance on a current request and response. diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ContainerRequest.java b/core-server/src/main/java/org/glassfish/jersey/server/ContainerRequest.java index f539b6e061..e5f17df345 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/ContainerRequest.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/ContainerRequest.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2016 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -51,6 +51,8 @@ import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerRequestFilter; @@ -71,12 +73,13 @@ import javax.ws.rs.ext.WriterInterceptor; import org.glassfish.jersey.internal.PropertiesDelegate; +import org.glassfish.jersey.internal.guava.Preconditions; import org.glassfish.jersey.internal.util.collection.Ref; import org.glassfish.jersey.internal.util.collection.Refs; -import org.glassfish.jersey.message.internal.AcceptableLanguageTag; import org.glassfish.jersey.message.internal.AcceptableMediaType; import org.glassfish.jersey.message.internal.HttpHeaderReader; import org.glassfish.jersey.message.internal.InboundMessageContext; +import org.glassfish.jersey.message.internal.LanguageTag; import org.glassfish.jersey.message.internal.MatchingEntityTag; import org.glassfish.jersey.message.internal.OutboundJaxrsResponse; import org.glassfish.jersey.message.internal.TracingAwarePropertiesDelegate; @@ -93,10 +96,6 @@ import org.glassfish.jersey.uri.UriComponent; import org.glassfish.jersey.uri.internal.JerseyUriBuilder; -import jersey.repackaged.com.google.common.base.Function; -import jersey.repackaged.com.google.common.base.Preconditions; -import jersey.repackaged.com.google.common.collect.Lists; - /** * Jersey container request context. *

@@ -581,23 +580,14 @@ public Map getCookies() { @Override public List getAcceptableMediaTypes() { - return Lists.transform(getQualifiedAcceptableMediaTypes(), new Function() { - @Override - public MediaType apply(final AcceptableMediaType input) { - return input; - } - }); + return getQualifiedAcceptableMediaTypes().stream() + .map((Function) input -> input) + .collect(Collectors.toList()); } @Override public List getAcceptableLanguages() { - return Lists.transform(getQualifiedAcceptableLanguages(), new Function() { - - @Override - public Locale apply(final AcceptableLanguageTag input) { - return input.getAsLocale(); - } - }); + return getQualifiedAcceptableLanguages().stream().map(LanguageTag::getAsLocale).collect(Collectors.toList()); } // JAX-RS request diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ExternalRequestScopeConfigurator.java b/core-server/src/main/java/org/glassfish/jersey/server/ExternalRequestScopeConfigurator.java new file mode 100644 index 0000000000..5976c0055c --- /dev/null +++ b/core-server/src/main/java/org/glassfish/jersey/server/ExternalRequestScopeConfigurator.java @@ -0,0 +1,122 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package org.glassfish.jersey.server; + +import java.util.Collections; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.glassfish.jersey.internal.BootstrapBag; +import org.glassfish.jersey.internal.BootstrapConfigurator; +import org.glassfish.jersey.internal.ServiceFinder; +import org.glassfish.jersey.internal.inject.AbstractBinder; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.server.internal.LocalizationMessages; +import org.glassfish.jersey.server.spi.ComponentProvider; +import org.glassfish.jersey.server.spi.ExternalRequestContext; +import org.glassfish.jersey.server.spi.ExternalRequestScope; + +/** + * Configurator which initializes and register {@link ExternalRequestScope} instance into {@link InjectionManager}. + * + * @author Petr Bouda (petr.bouda at oracle.com) + */ +class ExternalRequestScopeConfigurator implements BootstrapConfigurator { + + private static final Logger LOGGER = Logger.getLogger(ExternalRequestScopeConfigurator.class.getName()); + + @Override + public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) { + ServerBootstrapBag serverBag = (ServerBootstrapBag) bootstrapBag; + + Class[] extScopes = ServiceFinder.find(ExternalRequestScope.class, true).toClassArray(); + boolean extScopeBound = false; + + if (extScopes.length == 1) { + for (ComponentProvider p : serverBag.getComponentProviders().get()) { + if (p.bind(extScopes[0], Collections.singleton(ExternalRequestScope.class))) { + extScopeBound = true; + break; + } + } + } else if (extScopes.length > 1) { + if (LOGGER.isLoggable(Level.WARNING)) { + StringBuilder scopeList = new StringBuilder("\n"); + for (Class ers : extScopes) { + scopeList.append(" ").append(ers.getTypeParameters()[0]).append('\n'); + } + LOGGER.warning(LocalizationMessages.WARNING_TOO_MANY_EXTERNAL_REQ_SCOPES(scopeList.toString())); + } + } + + if (!extScopeBound) { + injectionManager.register(new NoopExternalRequestScopeBinder()); + } + } + + private static class NoopExternalRequestScopeBinder extends AbstractBinder { + + @Override + protected void configure() { + bind(NOOP_EXTERNAL_REQ_SCOPE).to(ExternalRequestScope.class); + } + } + + private static final ExternalRequestScope NOOP_EXTERNAL_REQ_SCOPE = new ExternalRequestScope() { + + @Override + public ExternalRequestContext open(InjectionManager injectionManager) { + return null; + } + + @Override + public void close() { + } + + @Override + public void suspend(ExternalRequestContext o, InjectionManager injectionManager) { + } + + @Override + public void resume(ExternalRequestContext o, InjectionManager injectionManager) { + } + }; +} diff --git a/core-server/src/main/java/org/glassfish/jersey/server/JerseyResourceContextConfigurator.java b/core-server/src/main/java/org/glassfish/jersey/server/JerseyResourceContextConfigurator.java new file mode 100644 index 0000000000..874e08f345 --- /dev/null +++ b/core-server/src/main/java/org/glassfish/jersey/server/JerseyResourceContextConfigurator.java @@ -0,0 +1,85 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package org.glassfish.jersey.server; + +import java.util.function.Consumer; +import java.util.function.Function; + +import javax.ws.rs.container.ResourceContext; + +import org.glassfish.jersey.internal.BootstrapBag; +import org.glassfish.jersey.internal.BootstrapConfigurator; +import org.glassfish.jersey.internal.inject.Binding; +import org.glassfish.jersey.internal.inject.Bindings; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.internal.inject.Injections; +import org.glassfish.jersey.internal.inject.InstanceBinding; +import org.glassfish.jersey.server.internal.JerseyResourceContext; + +/** + * Configurator which initializes and register {@link JerseyResourceContext} instance into {@link InjectionManager} and + * {@link BootstrapBag}. + * + * @author Petr Bouda (petr.bouda at oracle.com) + */ +class JerseyResourceContextConfigurator implements BootstrapConfigurator { + + @Override + public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) { + ServerBootstrapBag serverBag = (ServerBootstrapBag) bootstrapBag; + Consumer registerBinding = injectionManager::register; + Function, ?> getOrCreateInstance = clazz -> Injections.getOrCreate(injectionManager, clazz); + Consumer injectInstance = injectionManager::inject; + + // Initialize and register Resource Context + JerseyResourceContext resourceContext = new JerseyResourceContext(getOrCreateInstance, injectInstance, registerBinding); + InstanceBinding resourceContextBinding = + Bindings.service(resourceContext) + .to(ResourceContext.class) + .to(ExtendedResourceContext.class); + injectionManager.register(resourceContextBinding); + serverBag.setResourceContext(resourceContext); + } + + @Override + public void postInit(InjectionManager injectionManager, BootstrapBag bootstrapBag) { + } +} diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ProcessingProvidersConfigurator.java b/core-server/src/main/java/org/glassfish/jersey/server/ProcessingProvidersConfigurator.java new file mode 100644 index 0000000000..954cc72fe1 --- /dev/null +++ b/core-server/src/main/java/org/glassfish/jersey/server/ProcessingProvidersConfigurator.java @@ -0,0 +1,217 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package org.glassfish.jersey.server; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.logging.Logger; + +import javax.ws.rs.NameBinding; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.ContainerResponseFilter; +import javax.ws.rs.container.DynamicFeature; +import javax.ws.rs.container.PreMatching; +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.ReaderInterceptor; +import javax.ws.rs.ext.WriterInterceptor; + +import org.glassfish.jersey.internal.BootstrapBag; +import org.glassfish.jersey.internal.BootstrapConfigurator; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.internal.inject.Providers; +import org.glassfish.jersey.internal.util.ReflectionHelper; +import org.glassfish.jersey.model.ContractProvider; +import org.glassfish.jersey.model.internal.ComponentBag; +import org.glassfish.jersey.model.internal.RankedProvider; +import org.glassfish.jersey.server.internal.LocalizationMessages; +import org.glassfish.jersey.server.internal.ProcessingProviders; + +/** + * Configurator which initializes and register {@link ProcessingProviders} instance into {@link BootstrapBag}. + * + * @author Petr Bouda (petr.bouda at oracle.com) + */ +class ProcessingProvidersConfigurator implements BootstrapConfigurator { + + private static final Logger LOGGER = Logger.getLogger(ProcessingProvidersConfigurator.class.getName()); + + @Override + public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) { + } + + @Override + public void postInit(InjectionManager injectionManager, BootstrapBag bootstrapBag) { + ServerBootstrapBag serverBag = (ServerBootstrapBag) bootstrapBag; + + ComponentBag componentBag = serverBag.getRuntimeConfig().getComponentBag(); + // scan for NameBinding annotations attached to the application class + Collection> applicationNameBindings = ReflectionHelper.getAnnotationTypes( + ResourceConfig.unwrapApplication(serverBag.getRuntimeConfig()).getClass(), NameBinding.class); + + MultivaluedMap, Class> nameBoundRespFiltersInverse = + new MultivaluedHashMap<>(); + MultivaluedMap, Class> nameBoundReqFiltersInverse = + new MultivaluedHashMap<>(); + MultivaluedMap, Class> nameBoundReaderInterceptorsInverse = + new MultivaluedHashMap<>(); + MultivaluedMap, Class> nameBoundWriterInterceptorsInverse = + new MultivaluedHashMap<>(); + + Iterable> responseFilters = + Providers.getAllRankedProviders(injectionManager, ContainerResponseFilter.class); + + MultivaluedMap, RankedProvider> nameBoundResponseFilters = + filterNameBound(responseFilters, null, componentBag, applicationNameBindings, nameBoundRespFiltersInverse); + + Iterable> requestFilters = + Providers.getAllRankedProviders(injectionManager, ContainerRequestFilter.class); + + List> preMatchFilters = new ArrayList<>(); + + MultivaluedMap, RankedProvider> nameBoundReqFilters = + filterNameBound(requestFilters, preMatchFilters, componentBag, applicationNameBindings, + nameBoundReqFiltersInverse); + + Iterable> readerInterceptors = + Providers.getAllRankedProviders(injectionManager, ReaderInterceptor.class); + + MultivaluedMap, RankedProvider> nameBoundReaderInterceptors = + filterNameBound(readerInterceptors, null, componentBag, applicationNameBindings, + nameBoundReaderInterceptorsInverse); + + Iterable> writerInterceptors = + Providers.getAllRankedProviders(injectionManager, WriterInterceptor.class); + + MultivaluedMap, RankedProvider> nameBoundWriterInterceptors = + filterNameBound(writerInterceptors, null, componentBag, applicationNameBindings, + nameBoundWriterInterceptorsInverse); + + Iterable dynamicFeatures = Providers.getAllProviders(injectionManager, DynamicFeature.class); + + ProcessingProviders processingProviders = new ProcessingProviders(nameBoundReqFilters, nameBoundReqFiltersInverse, + nameBoundResponseFilters, nameBoundRespFiltersInverse, nameBoundReaderInterceptors, + nameBoundReaderInterceptorsInverse, nameBoundWriterInterceptors, nameBoundWriterInterceptorsInverse, + requestFilters, preMatchFilters, responseFilters, readerInterceptors, writerInterceptors, dynamicFeatures); + + serverBag.setProcessingProviders(processingProviders); + } + + /** + * Takes collection of all filters/interceptors (either request/reader or response/writer) + * and separates out all name-bound filters/interceptors, returns them as a separate MultivaluedMap, + * mapping the name-bound annotation to the list of name-bound filters/interceptors. The same key values + * are also added into the inverse map passed in {@code inverseNameBoundMap}. + *

+ * Note, the name-bound filters/interceptors are removed from the original filters/interceptors collection. + * If non-null collection is passed in the postMatching parameter (applicable for filters only), + * this method also removes all the global + * postMatching filters from the original collection and adds them to the collection passed in the postMatching + * parameter. + * + * @param all Collection of all filters to be processed. + * @param preMatchingFilters Collection into which pre-matching filters should be added. + * @param componentBag Component bag + * @param applicationNameBindings Collection of name binding annotations attached to the JAX-RS application. + * @param inverseNameBoundMap Inverse name bound map into which the name bound providers should be inserted. The keys + * are providers (filters, interceptor) + * @return {@link MultivaluedMap} of all name-bound filters. + */ + private static MultivaluedMap, RankedProvider> filterNameBound( + final Iterable> all, + final Collection> preMatchingFilters, + final ComponentBag componentBag, + final Collection> applicationNameBindings, + final MultivaluedMap, Class> inverseNameBoundMap) { + + final MultivaluedMap, RankedProvider> result + = new MultivaluedHashMap<>(); + + for (final Iterator> it = all.iterator(); it.hasNext(); ) { + final RankedProvider provider = it.next(); + Class providerClass = provider.getProvider().getClass(); + final Set contractTypes = provider.getContractTypes(); + if (contractTypes != null && !contractTypes.contains(providerClass)) { + providerClass = ReflectionHelper.theMostSpecificTypeOf(contractTypes); + } + + ContractProvider model = componentBag.getModel(providerClass); + if (model == null) { + // the provider was (most likely) bound in HK2 externally + model = ComponentBag.modelFor(providerClass); + } + + final boolean preMatching = providerClass.getAnnotation(PreMatching.class) != null; + if (preMatching && preMatchingFilters != null) { + it.remove(); + preMatchingFilters.add(new RankedProvider<>((ContainerRequestFilter) provider.getProvider(), + model.getPriority(ContainerRequestFilter.class))); + } + + boolean nameBound = model.isNameBound(); + if (nameBound + && !applicationNameBindings.isEmpty() + && applicationNameBindings.containsAll(model.getNameBindings())) { + // override the name-bound flag + nameBound = false; + } + + if (nameBound) { // not application-bound + if (!preMatching) { + it.remove(); + for (final Class binding : model.getNameBindings()) { + result.add(binding, provider); + inverseNameBoundMap.add(provider, binding); + } + } else { + LOGGER.warning(LocalizationMessages.PREMATCHING_ALSO_NAME_BOUND(providerClass)); + } + } + } + + return result; + } +} diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ResourceBag.java b/core-server/src/main/java/org/glassfish/jersey/server/ResourceBag.java index 19467e2970..4defc50093 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/ResourceBag.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/ResourceBag.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2014 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -41,7 +41,9 @@ package org.glassfish.jersey.server; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.IdentityHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -49,8 +51,6 @@ import org.glassfish.jersey.server.model.Resource; -import jersey.repackaged.com.google.common.collect.Sets; - /** * A container for application resource models used during the {@link ApplicationHandler} * initialization. @@ -65,11 +65,11 @@ public static final class Builder { /** * Resource handler classes for the models in this resource bag. */ - private final Set> classes = Sets.newIdentityHashSet(); + private final Set> classes = Collections.newSetFromMap(new IdentityHashMap<>()); /** * Resource handler instance for the models in this resource bag. */ - private final Set instances = Sets.newIdentityHashSet(); + private final Set instances = Collections.newSetFromMap(new IdentityHashMap<>()); /** * Resource models. */ diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ResourceBagConfigurator.java b/core-server/src/main/java/org/glassfish/jersey/server/ResourceBagConfigurator.java new file mode 100644 index 0000000000..e5a1cb4b4f --- /dev/null +++ b/core-server/src/main/java/org/glassfish/jersey/server/ResourceBagConfigurator.java @@ -0,0 +1,101 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package org.glassfish.jersey.server; + +import java.util.logging.Logger; + +import org.glassfish.jersey.internal.BootstrapBag; +import org.glassfish.jersey.internal.BootstrapConfigurator; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.server.model.Resource; + +/** + * Configurator which initializes and register {@link ResourceBag} instance into {@link BootstrapBag}. + * + * @author Petr Bouda (petr.bouda at oracle.com) + */ +class ResourceBagConfigurator implements BootstrapConfigurator { + + private static final Logger LOGGER = Logger.getLogger(ResourceBagConfigurator.class.getName()); + + @Override + public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) { + ServerBootstrapBag serverBag = (ServerBootstrapBag) bootstrapBag; + ResourceConfig runtimeConfig = serverBag.getRuntimeConfig(); + + final boolean disableValidation = ServerProperties.getValue(runtimeConfig.getProperties(), + ServerProperties.RESOURCE_VALIDATION_DISABLE, + Boolean.FALSE, + Boolean.class); + + final ResourceBag.Builder resourceBagBuilder = new ResourceBag.Builder(); + + // Adding programmatic resource models + for (final Resource programmaticResource : runtimeConfig.getResources()) { + resourceBagBuilder.registerProgrammaticResource(programmaticResource); + } + + // Introspecting classes & instances + for (final Class c : runtimeConfig.getClasses()) { + try { + final Resource resource = Resource.from(c, disableValidation); + if (resource != null) { + resourceBagBuilder.registerResource(c, resource); + } + } catch (final IllegalArgumentException ex) { + LOGGER.warning(ex.getMessage()); + } + } + + for (final Object o : runtimeConfig.getSingletons()) { + try { + final Resource resource = Resource.from(o.getClass(), disableValidation); + if (resource != null) { + resourceBagBuilder.registerResource(o, resource); + } + } catch (final IllegalArgumentException ex) { + LOGGER.warning(ex.getMessage()); + } + } + + serverBag.setResourceBag(resourceBagBuilder.build()); + } +} diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ResourceConfig.java b/core-server/src/main/java/org/glassfish/jersey/server/ResourceConfig.java index e5dce524b2..904cbafacb 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/ResourceConfig.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/ResourceConfig.java @@ -43,13 +43,16 @@ import java.io.IOException; import java.io.InputStream; import java.security.AccessController; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import java.util.IdentityHashMap; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; import javax.ws.rs.Path; import javax.ws.rs.RuntimeType; @@ -75,9 +78,6 @@ import org.glassfish.jersey.server.internal.scanning.PackageNamesScanner; import org.glassfish.jersey.server.model.Resource; -import jersey.repackaged.com.google.common.base.Predicate; -import jersey.repackaged.com.google.common.collect.Sets; - /** * The resource configuration for configuring a web application. * @@ -113,9 +113,9 @@ public State() { super(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL); this.classLoader = AccessController.doPrivileged(ReflectionHelper.getContextClassLoaderPA()); - this.resourceFinders = Sets.newHashSet(); + this.resourceFinders = new HashSet<>(); - this.resources = Sets.newHashSet(); + this.resources = new HashSet<>(); this.resourcesView = Collections.unmodifiableSet(this.resources); } @@ -124,10 +124,10 @@ public State(final State original) { this.classLoader = original.classLoader; this.applicationName = original.applicationName; - this.resources = Sets.newHashSet(original.resources); + this.resources = new HashSet<>(original.resources); this.resourcesView = Collections.unmodifiableSet(this.resources); - this.resourceFinders = Sets.newHashSet(original.resourceFinders); + this.resourceFinders = new HashSet<>(original.resourceFinders); } public void setClassLoader(final ClassLoader classLoader) { @@ -368,7 +368,7 @@ public ResourceConfig(final Set> classes) { * @param classes application-specific resource and/or provider classes. */ public ResourceConfig(final Class... classes) { - this(Sets.newHashSet(classes)); + this(Arrays.stream(classes).collect(Collectors.toSet())); } /** @@ -519,7 +519,7 @@ public final ResourceConfig registerClasses(final Class... classes) { return this; } - return registerClasses(Sets.newHashSet(classes)); + return registerClasses(Arrays.stream(classes).collect(Collectors.toSet())); } /** @@ -564,7 +564,7 @@ public final ResourceConfig registerInstances(final Object... instances) { return this; } - return registerInstances(Sets.newHashSet(instances)); + return registerInstances(Arrays.stream(instances).collect(Collectors.toSet())); } /** @@ -578,7 +578,7 @@ public final ResourceConfig registerResources(final Resource... resources) { return this; } - return registerResources(Sets.newHashSet(resources)); + return registerResources(Arrays.stream(resources).collect(Collectors.toSet())); } /** @@ -814,8 +814,8 @@ final void configureForcedAutoDiscoverableProviders(final InjectionManager injec state.configureAutoDiscoverableProviders(injectionManager, true); } - final void configureMetaProviders(InjectionManager InjectionManager) { - state.configureMetaProviders(InjectionManager); + final void configureMetaProviders(InjectionManager injectionManager) { + state.configureMetaProviders(injectionManager); } @Override @@ -861,10 +861,10 @@ Set> _getClasses() { } private Set> scanClasses() { - final Set> result = Sets.newHashSet(); + final Set> result = new HashSet<>(); final ResourceConfig.State _state = state; - final Set rfs = Sets.newHashSet(_state.getResourceFinders()); + final Set rfs = new HashSet<>(_state.getResourceFinders()); // In case new entity is registered the available finders should be reset. resetFinders = true; @@ -948,7 +948,7 @@ Set> getRegisteredClasses() { * @return set of configured resource and/or provider instances. */ Set _getSingletons() { - final Set result = Sets.newHashSet(); + final Set result = new HashSet<>(); result.addAll(state.getInstances()); return result; } @@ -1032,7 +1032,7 @@ private static class WrappingResourceConfig extends ResourceConfig { private Application application; private Class applicationClass; - private final Set> defaultClasses = Sets.newHashSet(); + private final Set> defaultClasses = new HashSet<>(); public WrappingResourceConfig( final Application application, final Class applicationClass, @@ -1132,7 +1132,7 @@ private void mergeApplications(final Application application) { @Override Set> _getClasses() { - final Set> result = Sets.newHashSet(); + final Set> result = new HashSet<>(); final Set> applicationClasses = application.getClasses(); result.addAll(applicationClasses == null ? new HashSet>() : applicationClasses); if (result.isEmpty() && getSingletons().isEmpty()) { @@ -1179,25 +1179,20 @@ private RuntimeConfig(final ResourceConfig original) { registerComponentsOf(customRootApp); } - originalRegistrations = Sets.newIdentityHashSet(); + originalRegistrations = Collections.newSetFromMap(new IdentityHashMap<>()); originalRegistrations.addAll(super.getRegisteredClasses()); // Register externally provided instances. - final Set externalInstances = Sets.filter(original.getSingletons(), new Predicate() { - @Override - public boolean apply(final Object external) { - return !originalRegistrations.contains(external.getClass()); - } - }); + final Set externalInstances = + original.getSingletons().stream() + .filter(external -> !originalRegistrations.contains(external.getClass())) + .collect(Collectors.toSet()); registerInstances(externalInstances); // Register externally provided classes. - final Set> externalClasses = Sets.filter(original.getClasses(), new Predicate>() { - @Override - public boolean apply(final Class external) { - return !originalRegistrations.contains(external); - } - }); + final Set> externalClasses = original.getClasses().stream() + .filter(external -> !originalRegistrations.contains(external)) + .collect(Collectors.toSet()); registerClasses(externalClasses); } @@ -1209,28 +1204,29 @@ public void run() { // in case of duplicate registrations final Set singletons = application.getSingletons(); if (singletons != null) { - registerInstances(Sets.filter(singletons, new Predicate() { - @Override - public boolean apply(final Object input) { - if (input == null) { - Errors.warning(application, LocalizationMessages.NON_INSTANTIABLE_COMPONENT(null)); - } - return input != null; - } - })); + registerInstances( + singletons.stream() + .filter(input -> { + if (input == null) { + Errors.warning(application, + LocalizationMessages.NON_INSTANTIABLE_COMPONENT(null)); + } + return input != null; + }) + .collect(Collectors.toSet())); } final Set> classes = application.getClasses(); if (classes != null) { - registerClasses(Sets.filter(classes, new Predicate>() { - @Override - public boolean apply(final Class input) { - if (input == null) { - Errors.warning(application, LocalizationMessages.NON_INSTANTIABLE_COMPONENT(null)); - } - return input != null; - } - })); + registerClasses(classes.stream() + .filter(input -> { + if (input == null) { + Errors.warning(application, + LocalizationMessages.NON_INSTANTIABLE_COMPONENT(null)); + } + return input != null; + }) + .collect(Collectors.toSet())); } } }); diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ServerBinder.java b/core-server/src/main/java/org/glassfish/jersey/server/ServerBinder.java index f67d454f4c..cc9cfcef38 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/ServerBinder.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/ServerBinder.java @@ -40,33 +40,18 @@ package org.glassfish.jersey.server; -import java.util.Map; - -import javax.ws.rs.RuntimeType; import javax.ws.rs.ext.MessageBodyWriter; import javax.ws.rs.ext.WriterInterceptor; import javax.inject.Singleton; -import org.glassfish.jersey.internal.ContextResolverFactory; -import org.glassfish.jersey.internal.ExceptionMapperFactory; import org.glassfish.jersey.internal.JaxrsProviders; -import org.glassfish.jersey.internal.ServiceFinderBinder; import org.glassfish.jersey.internal.inject.AbstractBinder; -import org.glassfish.jersey.internal.inject.InjectionManager; -import org.glassfish.jersey.internal.spi.AutoDiscoverable; -import org.glassfish.jersey.message.internal.MessageBodyFactory; -import org.glassfish.jersey.message.internal.MessagingBinders; -import org.glassfish.jersey.process.internal.RequestScope; -import org.glassfish.jersey.server.internal.JerseyResourceContext; import org.glassfish.jersey.server.internal.JsonWithPaddingInterceptor; import org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor; -import org.glassfish.jersey.server.internal.ProcessingProviders; -import org.glassfish.jersey.server.internal.inject.ParameterInjectionBinder; import org.glassfish.jersey.server.internal.monitoring.MonitoringContainerListener; import org.glassfish.jersey.server.internal.process.ServerProcessingBinder; import org.glassfish.jersey.server.model.internal.ResourceModelBinder; -import org.glassfish.jersey.server.spi.ContainerProvider; /** * Server injection binder. @@ -76,35 +61,11 @@ */ class ServerBinder extends AbstractBinder { - private final Map applicationProperties; - - private final InjectionManager injectionManager; - - /** - * Create new {@code ServerBinder} instance. - * - * @param applicationProperties map of application-specific properties. - */ - public ServerBinder(Map applicationProperties, InjectionManager injectionManager) { - this.applicationProperties = applicationProperties; - this.injectionManager = injectionManager; - } - @Override protected void configure() { - install(new RequestScope.Binder(), // must go first as it registers the request scope instance. - new ServerProcessingBinder(), - new ParameterInjectionBinder(injectionManager), - new MessagingBinders.MessageBodyProviders(applicationProperties, RuntimeType.SERVER), - new MessageBodyFactory.Binder(), - new ExceptionMapperFactory.Binder(), - new ContextResolverFactory.Binder(), + install(new ServerProcessingBinder(), new JaxrsProviders.Binder(), - new ProcessingProviders.Binder(), new ResourceModelBinder(), - new ServiceFinderBinder<>(ContainerProvider.class, applicationProperties, RuntimeType.SERVER), - new JerseyResourceContext.Binder(), - new ServiceFinderBinder<>(AutoDiscoverable.class, applicationProperties, RuntimeType.SERVER), new MappableExceptionWrapperInterceptor.Binder(), new MonitoringContainerListener.Binder()); diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ServerBootstrapBag.java b/core-server/src/main/java/org/glassfish/jersey/server/ServerBootstrapBag.java new file mode 100644 index 0000000000..ea62f97ed2 --- /dev/null +++ b/core-server/src/main/java/org/glassfish/jersey/server/ServerBootstrapBag.java @@ -0,0 +1,331 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package org.glassfish.jersey.server; + +import java.util.Collection; + +import javax.ws.rs.core.Application; +import javax.ws.rs.core.Configuration; +import javax.ws.rs.core.GenericType; + +import org.glassfish.jersey.internal.BootstrapBag; +import org.glassfish.jersey.internal.util.collection.LazyValue; +import org.glassfish.jersey.message.MessageBodyWorkers; +import org.glassfish.jersey.process.internal.RequestScope; +import org.glassfish.jersey.server.internal.JerseyResourceContext; +import org.glassfish.jersey.server.internal.ProcessingProviders; +import org.glassfish.jersey.server.internal.inject.MultivaluedParameterExtractorProvider; +import org.glassfish.jersey.server.model.ResourceMethodInvoker; +import org.glassfish.jersey.server.spi.ComponentProvider; +import org.glassfish.jersey.server.spi.internal.ValueSupplierProvider; +import org.glassfish.jersey.spi.ContextResolvers; +import org.glassfish.jersey.spi.ExceptionMappers; + +/** + * {@inheritDoc} + *

+ * This bootstrap bag is specialized for server part of Jersey. + * + * @author Petr Bouda (petr.bouda at oracle.com) + */ +public class ServerBootstrapBag extends BootstrapBag { + + private Application application; + private ApplicationHandler applicationHandler; + private Collection valueSupplierProviders; + private MultivaluedParameterExtractorProvider multivaluedParameterExtractorProvider; + private ProcessingProviders processingProviders; + private JerseyResourceContext resourceContext; + private LazyValue> componentProviders; + private ResourceMethodInvoker.Builder resourceMethodInvokerBuilder; + private ResourceBag resourceBag; + + public ResourceBag getResourceBag() { + requireNonNull(resourceBag, ResourceBag.class); + return resourceBag; + } + + public void setResourceBag(ResourceBag resourceBag) { + this.resourceBag = resourceBag; + } + + public ResourceConfig getRuntimeConfig() { + return (ResourceConfig) getConfiguration(); + } + + public Application getApplication() { + requireNonNull(application, Application.class); + return application; + } + + public void setApplication(Application application) { + this.application = application; + } + + public ApplicationHandler getApplicationHandler() { + requireNonNull(applicationHandler, ApplicationHandler.class); + return applicationHandler; + } + + public void setApplicationHandler(ApplicationHandler applicationHandler) { + this.applicationHandler = applicationHandler; + } + + public ProcessingProviders getProcessingProviders() { + requireNonNull(processingProviders, ProcessingProviders.class); + return processingProviders; + } + + public void setProcessingProviders(ProcessingProviders processingProviders) { + this.processingProviders = processingProviders; + } + + public MultivaluedParameterExtractorProvider getMultivaluedParameterExtractorProvider() { + requireNonNull(multivaluedParameterExtractorProvider, MultivaluedParameterExtractorProvider.class); + return multivaluedParameterExtractorProvider; + } + + public void setMultivaluedParameterExtractorProvider(MultivaluedParameterExtractorProvider provider) { + this.multivaluedParameterExtractorProvider = provider; + } + + public Collection getValueSupplierProviders() { + requireNonNull(valueSupplierProviders, new GenericType>() {}.getType()); + return valueSupplierProviders; + } + + public void setValueSupplierProviders(Collection valueSupplierProviders) { + this.valueSupplierProviders = valueSupplierProviders; + } + + public JerseyResourceContext getResourceContext() { + requireNonNull(resourceContext, JerseyResourceContext.class); + return resourceContext; + } + + public void setResourceContext(JerseyResourceContext resourceContext) { + this.resourceContext = resourceContext; + } + + public LazyValue> getComponentProviders() { + requireNonNull(componentProviders, new GenericType>>() {}.getType()); + return componentProviders; + } + + public void setComponentProviders(LazyValue> componentProviders) { + this.componentProviders = componentProviders; + } + + public ResourceMethodInvoker.Builder getResourceMethodInvokerBuilder() { + requireNonNull(resourceMethodInvokerBuilder, ResourceMethodInvoker.Builder.class); + return resourceMethodInvokerBuilder; + } + + public void setResourceMethodInvokerBuilder(ResourceMethodInvoker.Builder resourceMethodInvokerBuilder) { + this.resourceMethodInvokerBuilder = resourceMethodInvokerBuilder; + } + + /** + * Creates an immutable version of bootstrap bag. + * + * @return immutable bootstrap bag. + */ + public ServerBootstrapBag toImmutable() { + return new ImmutableServerBootstrapBag(this); + } + + /** + * Immutable version of {@link BootstrapBag}. + */ + static class ImmutableServerBootstrapBag extends ServerBootstrapBag { + + private final ServerBootstrapBag delegate; + + private ImmutableServerBootstrapBag(ServerBootstrapBag delegate) { + this.delegate = delegate; + } + + @Override + public ResourceBag getResourceBag() { + return delegate.getResourceBag(); + } + + @Override + public void setResourceBag(ResourceBag resourceBag) { + throw new UnsupportedOperationException(); + } + + @Override + public ResourceConfig getRuntimeConfig() { + return delegate.getRuntimeConfig(); + } + + @Override + public Application getApplication() { + return delegate.getApplication(); + } + + @Override + public void setApplication(Application application) { + throw new UnsupportedOperationException(); + } + + @Override + public ApplicationHandler getApplicationHandler() { + return delegate.getApplicationHandler(); + } + + @Override + public void setApplicationHandler(ApplicationHandler applicationHandler) { + throw new UnsupportedOperationException(); + } + + @Override + public ProcessingProviders getProcessingProviders() { + return delegate.getProcessingProviders(); + } + + @Override + public void setProcessingProviders(ProcessingProviders processingProviders) { + throw new UnsupportedOperationException(); + } + + @Override + public MultivaluedParameterExtractorProvider getMultivaluedParameterExtractorProvider() { + return delegate.getMultivaluedParameterExtractorProvider(); + } + + @Override + public void setMultivaluedParameterExtractorProvider(MultivaluedParameterExtractorProvider provider) { + throw new UnsupportedOperationException(); + } + + @Override + public Collection getValueSupplierProviders() { + return delegate.getValueSupplierProviders(); + } + + @Override + public void setValueSupplierProviders(Collection valueSupplierProviders) { + throw new UnsupportedOperationException(); + } + + @Override + public JerseyResourceContext getResourceContext() { + return delegate.getResourceContext(); + } + + @Override + public void setResourceContext(JerseyResourceContext resourceContext) { + throw new UnsupportedOperationException(); + } + + @Override + public LazyValue> getComponentProviders() { + return delegate.getComponentProviders(); + } + + @Override + public void setComponentProviders(LazyValue> componentProviders) { + throw new UnsupportedOperationException(); + } + + @Override + public ResourceMethodInvoker.Builder getResourceMethodInvokerBuilder() { + return delegate.getResourceMethodInvokerBuilder(); + } + + @Override + public void setResourceMethodInvokerBuilder(ResourceMethodInvoker.Builder resourceMethodInvokerBuilder) { + throw new UnsupportedOperationException(); + } + + @Override + public RequestScope getRequestScope() { + return delegate.getRequestScope(); + } + + @Override + public void setRequestScope(RequestScope requestScope) { + throw new UnsupportedOperationException(); + } + + @Override + public MessageBodyWorkers getMessageBodyWorkers() { + return delegate.getMessageBodyWorkers(); + } + + @Override + public void setMessageBodyWorkers(MessageBodyWorkers messageBodyWorkers) { + throw new UnsupportedOperationException(); + } + + @Override + public Configuration getConfiguration() { + return delegate.getConfiguration(); + } + + @Override + public void setConfiguration(Configuration configuration) { + throw new UnsupportedOperationException(); + } + + @Override + public ExceptionMappers getExceptionMappers() { + return delegate.getExceptionMappers(); + } + + @Override + public void setExceptionMappers(ExceptionMappers exceptionMappers) { + throw new UnsupportedOperationException(); + } + + @Override + public ContextResolvers getContextResolvers() { + return delegate.getContextResolvers(); + } + + @Override + public void setContextResolvers(ContextResolvers contextResolvers) { + throw new UnsupportedOperationException(); + } + } +} diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ServerRuntime.java b/core-server/src/main/java/org/glassfish/jersey/server/ServerRuntime.java index 7f7a184dbb..cc1358648f 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/ServerRuntime.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/ServerRuntime.java @@ -74,10 +74,9 @@ import javax.ws.rs.core.Response; import javax.ws.rs.ext.ExceptionMapper; -import javax.inject.Inject; import javax.inject.Provider; -import org.glassfish.jersey.internal.inject.AbstractBinder; +import org.glassfish.jersey.internal.guava.Preconditions; import org.glassfish.jersey.internal.inject.InjectionManager; import org.glassfish.jersey.internal.inject.Injections; import org.glassfish.jersey.internal.inject.Providers; @@ -120,8 +119,6 @@ import static org.glassfish.jersey.server.internal.process.AsyncContext.State.RUNNING; import static org.glassfish.jersey.server.internal.process.AsyncContext.State.SUSPENDED; -import jersey.repackaged.com.google.common.base.Preconditions; - /** * Server-side request processing runtime. * @@ -153,86 +150,30 @@ public class ServerRuntime { /** Resolve relative URIs according to RFC7231 (not JAX-RS 2.0 compliant */ private final boolean rfc7231LocationHeaderRelativeUriResolution; - /** - * Binds no-operation external request scope if there are no configured using service locator. - */ - public static class NoopExternalRequestScopeBinder extends AbstractBinder { - - @Override - protected void configure() { - bind(NOOP_EXTERNAL_REQ_SCOPE).to(ExternalRequestScope.class); - } - } - - /*package */ static final ExternalRequestScope NOOP_EXTERNAL_REQ_SCOPE = new ExternalRequestScope() { - - @Override - public ExternalRequestContext open(final InjectionManager injectionManager) { - return null; - } - - @Override - public void close() { - } - - @Override - public void suspend(final ExternalRequestContext o, final InjectionManager injectionManager) { - } - - @Override - public void resume(final ExternalRequestContext o, final InjectionManager injectionManager) { - } - }; - - /** - * Server-side request processing runtime builder. - */ - public static class Builder { - - @Inject - private InjectionManager injectionManager; - @Inject - @BackgroundScheduler - private ScheduledExecutorService backgroundScheduler; - @Inject - @ManagedAsyncExecutor - private Provider asyncExecutorProvider; - @Inject - private RequestScope requestScope; - @Inject - private ExceptionMappers exceptionMappers; - @Inject - private Configuration configuration; - @Inject - private ExternalRequestScope externalRequestScope; - - /** - * Create new server-side request processing runtime. - * - * @param processingRoot application request processing root stage. - * @param eventListener application event listener registered for this runtime. - * @param processingProviders application processing providers. - * @return new server-side request processing runtime. - */ - public ServerRuntime build( - final Stage processingRoot, - final ApplicationEventListener eventListener, - final ProcessingProviders processingProviders) { - - final ExternalRequestScope externalScope = - externalRequestScope != null ? externalRequestScope : NOOP_EXTERNAL_REQ_SCOPE; - - return new ServerRuntime( - processingRoot, - processingProviders, injectionManager, - backgroundScheduler, - asyncExecutorProvider, - requestScope, - exceptionMappers, - eventListener, - externalScope, - configuration); - } + static ServerRuntime createServerRuntime( + InjectionManager injectionManager, + ServerBootstrapBag bootstrapBag, + Stage processingRoot, + ApplicationEventListener eventListener, + ProcessingProviders processingProviders) { + + ScheduledExecutorService scheduledExecutorServiceSupplier = + injectionManager.getInstance(ScheduledExecutorService.class, BackgroundSchedulerLiteral.INSTANCE); + + Provider asyncExecutorServiceSupplier = + () -> injectionManager.getInstance(ExecutorService.class, ManagedAsyncExecutorLiteral.INSTANCE); + + return new ServerRuntime( + processingRoot, + processingProviders, + injectionManager, + scheduledExecutorServiceSupplier, + asyncExecutorServiceSupplier, + bootstrapBag.getRequestScope(), + bootstrapBag.getExceptionMappers(), + eventListener, + injectionManager.getInstance(ExternalRequestScope.class), + bootstrapBag.getConfiguration()); } private ServerRuntime(final Stage requestProcessingRoot, @@ -307,7 +248,6 @@ public void process(final ContainerRequest request) { final Responder responder = new Responder(context, ServerRuntime.this); final RequestScope.Instance requestScopeInstance = requestScope.createInstance(); - // TODO: Will we need EXTERNAL_REQUEST_SCOPE after the injection task? final AsyncResponderHolder asyncResponderHolder = new AsyncResponderHolder(responder, externalRequestScope, requestScopeInstance, externalRequestScope.open(injectionManager)); diff --git a/core-server/src/main/java/org/glassfish/jersey/server/TracingUtils.java b/core-server/src/main/java/org/glassfish/jersey/server/TracingUtils.java index 75c223fd56..2bab018c05 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/TracingUtils.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/TracingUtils.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -39,6 +39,7 @@ */ package org.glassfish.jersey.server; +import java.util.ArrayList; import java.util.List; import javax.ws.rs.core.Configuration; @@ -47,8 +48,6 @@ import org.glassfish.jersey.message.internal.TracingLogger; import org.glassfish.jersey.server.internal.ServerTraceEvent; -import jersey.repackaged.com.google.common.collect.Lists; - /** * Utilities for tracing support. * @@ -57,7 +56,7 @@ */ public final class TracingUtils { - private static final List SUMMARY_HEADERS = Lists.newArrayList(); + private static final List SUMMARY_HEADERS = new ArrayList<>(); static { SUMMARY_HEADERS.add(HttpHeaders.ACCEPT.toLowerCase()); diff --git a/core-server/src/main/java/org/glassfish/jersey/server/filter/EncodingFilter.java b/core-server/src/main/java/org/glassfish/jersey/server/filter/EncodingFilter.java index 1b21615206..93545480c7 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/filter/EncodingFilter.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/filter/EncodingFilter.java @@ -42,9 +42,11 @@ import java.io.IOException; import java.text.ParseException; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.SortedSet; +import java.util.TreeSet; import java.util.logging.Level; import java.util.logging.Logger; @@ -63,9 +65,6 @@ import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.spi.ContentEncoder; -import jersey.repackaged.com.google.common.collect.Lists; -import jersey.repackaged.com.google.common.collect.Sets; - /** * Container filter that supports encoding-based content negotiation. The filter examines what * content encodings are supported by the container (by looking up all the @@ -127,7 +126,7 @@ public void filter(ContainerRequestContext request, ContainerResponseContext res } // convert encodings from String to Encoding objects - List encodings = Lists.newArrayList(); + List encodings = new ArrayList<>(); for (String input : acceptEncoding) { String[] tokens = input.split(","); for (String token : tokens) { @@ -148,7 +147,7 @@ public void filter(ContainerRequestContext request, ContainerResponseContext res encodings.add(new ContentEncoding(IDENTITY_ENCODING, -1)); // get a copy of supported encoding (we'll be modifying this set, hence the copy) - SortedSet acceptedEncodings = Sets.newTreeSet(getSupportedEncodings()); + SortedSet acceptedEncodings = new TreeSet<>(getSupportedEncodings()); // indicates that we can pick any of the encodings that remained in the acceptedEncodings set boolean anyRemaining = false; @@ -243,7 +242,7 @@ SortedSet getSupportedEncodings() { // no need for synchronization - in case of a race condition, the property // may be set twice, but it does not break anything if (supportedEncodings == null) { - SortedSet se = Sets.newTreeSet(); + SortedSet se = new TreeSet<>(); List encoders = injectionManager.getAllInstances(ContentEncoder.class); for (ContentEncoder encoder : encoders) { se.addAll(encoder.getSupportedEncodings()); diff --git a/core-server/src/main/java/org/glassfish/jersey/server/filter/UriConnegFilter.java b/core-server/src/main/java/org/glassfish/jersey/server/filter/UriConnegFilter.java index 55652c1f96..0ca8081688 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/filter/UriConnegFilter.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/filter/UriConnegFilter.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2010-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -65,8 +65,6 @@ import org.glassfish.jersey.server.internal.LocalizationMessages; import org.glassfish.jersey.uri.UriComponent; -import jersey.repackaged.com.google.common.collect.Maps; - /** * A URI-based content negotiation filter mapping a dot-declared suffix in * URI to media type that is the value of the Accept header @@ -247,7 +245,7 @@ private static Map parseAndValidateMappings(final String property return (Map) mappings; } - final HashMap mappingsMap = Maps.newHashMap(); + final HashMap mappingsMap = new HashMap<>(); if (mappings instanceof String) { parseMappings(property, (String) mappings, mappingsMap, parser); diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyResourceContext.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyResourceContext.java index 33a34125f2..980615a198 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyResourceContext.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/JerseyResourceContext.java @@ -42,23 +42,23 @@ import java.lang.annotation.Annotation; import java.util.Collection; +import java.util.Collections; +import java.util.IdentityHashMap; import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; import javax.ws.rs.container.ResourceContext; -import javax.inject.Inject; import javax.inject.Scope; import javax.inject.Singleton; -import org.glassfish.jersey.internal.inject.AbstractBinder; import org.glassfish.jersey.internal.inject.Binding; import org.glassfish.jersey.internal.inject.Bindings; import org.glassfish.jersey.internal.inject.ClassBinding; import org.glassfish.jersey.internal.inject.CustomAnnotationLiteral; -import org.glassfish.jersey.internal.inject.InjectionManager; -import org.glassfish.jersey.internal.inject.Injections; import org.glassfish.jersey.internal.inject.Providers; import org.glassfish.jersey.internal.util.ReflectionHelper; import org.glassfish.jersey.model.ContractProvider; @@ -66,8 +66,6 @@ import org.glassfish.jersey.server.ExtendedResourceContext; import org.glassfish.jersey.server.model.ResourceModel; -import jersey.repackaged.com.google.common.collect.Sets; - /** * Jersey implementation of JAX-RS {@link ResourceContext resource context}. * @@ -75,7 +73,9 @@ */ public class JerseyResourceContext implements ExtendedResourceContext { - private final InjectionManager injectionManager; + private final Function, ?> getOrCreateInstance; + private final Consumer injectInstance; + private final Consumer registerBinding; private final Set> bindingCache; private final Object bindingCacheLock; @@ -83,22 +83,28 @@ public class JerseyResourceContext implements ExtendedResourceContext { private volatile ResourceModel resourceModel; /** - * Create new JerseyResourceContext. + * Creates a new JerseyResourceContext. * - * @param injectionManager injection manager. + * @param getOrCreateInstance function to create or get existing instance. + * @param injectInstance consumer to inject instances into an unmanaged instance. + * @param registerBinding consumer to register a new binding into injection manager. */ - @Inject - JerseyResourceContext(InjectionManager injectionManager) { - this.injectionManager = injectionManager; - - this.bindingCache = Sets.newIdentityHashSet(); + public JerseyResourceContext( + Function, ?> getOrCreateInstance, + Consumer injectInstance, + Consumer registerBinding) { + this.getOrCreateInstance = getOrCreateInstance; + this.injectInstance = injectInstance; + this.registerBinding = registerBinding; + this.bindingCache = Collections.newSetFromMap(new IdentityHashMap<>()); this.bindingCacheLock = new Object(); } @Override + @SuppressWarnings("unchecked") public T getResource(Class resourceClass) { try { - return Injections.getOrCreate(injectionManager, resourceClass); + return (T) getOrCreateInstance.apply(resourceClass); } catch (Exception ex) { Logger.getLogger(JerseyResourceContext.class.getName()).log(Level.WARNING, LocalizationMessages.RESOURCE_LOOKUP_FAILED(resourceClass), ex); @@ -108,7 +114,7 @@ public T getResource(Class resourceClass) { @Override public T initResource(T resource) { - injectionManager.inject(resource); + injectInstance.accept(resource); return resource; } @@ -132,7 +138,7 @@ public void bindResource(Class resourceClass) { if (bindingCache.contains(resourceClass)) { return; } - unsafeBindResource(resourceClass, null, injectionManager); + unsafeBindResource(resourceClass, null); } } @@ -146,6 +152,7 @@ public void bindResource(Class resourceClass) { * annotated with {@link javax.inject.Singleton Singleton annotation} it * will be ignored by this method. */ + @SuppressWarnings("unchecked") public void bindResourceIfSingleton(T resource) { final Class resourceClass = resource.getClass(); if (bindingCache.contains(resourceClass)) { @@ -157,15 +164,7 @@ public void bindResourceIfSingleton(T resource) { return; } if (getScope(resourceClass) == Singleton.class) { - org.glassfish.jersey.internal.inject.Binder binder = new AbstractBinder() { - @Override - @SuppressWarnings("unchecked") - protected void configure() { - bind(resource).to((Class) resourceClass); - } - }; - - injectionManager.register(binder); + registerBinding.accept(Bindings.service(resource).to((Class) resourceClass)); } bindingCache.add(resourceClass); @@ -173,7 +172,7 @@ protected void configure() { } /** - * Bind a resource instance in a HK2 context. + * Bind a resource instance in a InjectionManager. * * The bound resource instance is internally cached to make sure any sub-sequent attempts to service the * class are silently ignored. @@ -186,7 +185,7 @@ protected void configure() { * @param providerModel provider model for the resource class. If not {@code null}, the class * wil be bound as a contract provider too. */ - public void unsafeBindResource(Object resource, ContractProvider providerModel, InjectionManager injectionManager) { + public void unsafeBindResource(Object resource, ContractProvider providerModel) { Binding binding; Class resourceClass = resource.getClass(); if (providerModel != null) { @@ -201,7 +200,7 @@ public void unsafeBindResource(Object resource, ContractProvider providerModel, } else { binding = Bindings.serviceAsContract(resourceClass); } - injectionManager.register(binding); + registerBinding.accept(binding); bindingCache.add(resourceClass); } @@ -227,8 +226,7 @@ private static Class getScope(Class resourceClass) { * @param providerModel provider model for the class. If not {@code null}, the class * wil be bound as a contract provider too. */ - public void unsafeBindResource( - Class resourceClass, ContractProvider providerModel, InjectionManager injectionManager) { + public void unsafeBindResource(Class resourceClass, ContractProvider providerModel) { ClassBinding descriptor; if (providerModel != null) { Class scope = providerModel.getScope(); @@ -243,7 +241,7 @@ public void unsafeBindResource( } else { descriptor = Bindings.serviceAsContract(resourceClass).in(getScope(resourceClass)); } - injectionManager.register(descriptor); + registerBinding.accept(descriptor); bindingCache.add(resourceClass); } @@ -260,17 +258,4 @@ public ResourceModel getResourceModel() { public void setResourceModel(ResourceModel resourceModel) { this.resourceModel = resourceModel; } - - /** - * Injection binder for {@link JerseyResourceContext}. - */ - public static class Binder extends AbstractBinder { - - @Override - protected void configure() { - bindAsContract(JerseyResourceContext.class).to(ResourceContext.class).to(ExtendedResourceContext.class) - .in(Singleton.class); - } - } - } diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/JsonWithPaddingInterceptor.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/JsonWithPaddingInterceptor.java index 5096beb90c..26581b845c 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/JsonWithPaddingInterceptor.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/JsonWithPaddingInterceptor.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -41,9 +41,12 @@ import java.io.IOException; import java.lang.annotation.Annotation; +import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; @@ -62,9 +65,6 @@ import org.glassfish.jersey.server.ContainerRequest; import org.glassfish.jersey.server.JSONP; -import jersey.repackaged.com.google.common.collect.Maps; -import jersey.repackaged.com.google.common.collect.Sets; - /** * A {@link WriterInterceptor} implementation for JSONP format. This interceptor wraps a JSON stream obtained by a underlying * JSON provider into a callback function that can be defined by the {@link JSONP} annotation. @@ -80,10 +80,12 @@ public class JsonWithPaddingInterceptor implements WriterInterceptor { private static final Map> JAVASCRIPT_TYPES; static { - JAVASCRIPT_TYPES = Maps.newHashMapWithExpectedSize(2); + JAVASCRIPT_TYPES = new HashMap<>(2); - JAVASCRIPT_TYPES.put("application", Sets.newHashSet("x-javascript", "ecmascript", "javascript")); - JAVASCRIPT_TYPES.put("text", Sets.newHashSet("javascript", "x-javascript", "ecmascript", "jscript")); + JAVASCRIPT_TYPES.put("application", Arrays.asList("x-javascript", "ecmascript", "javascript") + .stream().collect(Collectors.toSet())); + JAVASCRIPT_TYPES.put("text", Arrays.asList("javascript", "x-javascript", "ecmascript", "jscript") + .stream().collect(Collectors.toSet())); } @Inject diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/ProcessingProviders.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/ProcessingProviders.java index 810b2c8969..ec888c3f28 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/ProcessingProviders.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/ProcessingProviders.java @@ -46,19 +46,11 @@ import javax.ws.rs.container.ContainerRequestFilter; import javax.ws.rs.container.ContainerResponseFilter; import javax.ws.rs.container.DynamicFeature; -import javax.ws.rs.core.GenericType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.ReaderInterceptor; import javax.ws.rs.ext.WriterInterceptor; -import javax.inject.Inject; -import javax.inject.Provider; -import javax.inject.Singleton; - -import org.glassfish.jersey.internal.inject.AbstractBinder; import org.glassfish.jersey.internal.inject.Providers; -import org.glassfish.jersey.internal.inject.ReferencingFactory; -import org.glassfish.jersey.internal.util.collection.Ref; import org.glassfish.jersey.model.internal.RankedComparator; import org.glassfish.jersey.model.internal.RankedProvider; @@ -143,15 +135,10 @@ public ProcessingProviders( this.globalReaderInterceptors = globalReaderInterceptors; this.globalWriterInterceptors = globalWriterInterceptors; this.dynamicFeatures = dynamicFeatures; - this.sortedGlobalReaderInterceptors = Providers.sortRankedProviders(new RankedComparator(), - globalReaderInterceptors); - this.sortedGlobalWriterInterceptors = Providers.sortRankedProviders(new RankedComparator(), - globalWriterInterceptors); - - this.sortedGlobalRequestFilters = Providers.sortRankedProviders(new RankedComparator(), - globalRequestFilters); - this.sortedGlobalResponseFilters = Providers.sortRankedProviders(new RankedComparator(), - globalResponseFilters); + this.sortedGlobalReaderInterceptors = Providers.sortRankedProviders(new RankedComparator<>(), globalReaderInterceptors); + this.sortedGlobalWriterInterceptors = Providers.sortRankedProviders(new RankedComparator<>(), globalWriterInterceptors); + this.sortedGlobalRequestFilters = Providers.sortRankedProviders(new RankedComparator<>(), globalRequestFilters); + this.sortedGlobalResponseFilters = Providers.sortRankedProviders(new RankedComparator<>(), globalResponseFilters); } /** @@ -326,28 +313,4 @@ public Iterable getDynamicFeatures() { public List> getPreMatchFilters() { return preMatchFilters; } - - /** - * Processing provider binder. - */ - public static class Binder extends AbstractBinder { - - @Override - protected void configure() { - - bindFactory(ProcessingProvidersReferencingFactory.class).to(ProcessingProviders.class); - bindFactory(ReferencingFactory.referenceFactory()) - .to(new GenericType>() { - }).in(Singleton.class); - } - - private static class ProcessingProvidersReferencingFactory extends ReferencingFactory { - - @Inject - public ProcessingProvidersReferencingFactory(Provider> referenceFactory) { - super(referenceFactory); - } - } - - } } diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/RuntimeDelegateImpl.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/RuntimeDelegateImpl.java index 6d55dbd78f..77c9316d5b 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/RuntimeDelegateImpl.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/RuntimeDelegateImpl.java @@ -60,8 +60,7 @@ public class RuntimeDelegateImpl extends AbstractRuntimeDelegate { public RuntimeDelegateImpl() { - // TODO add more binders as necessary - super(Injections.createInjectionManager("jersey-server-rd-locator", new MessagingBinders.HeaderDelegateProviders())); + super(Injections.createInjectionManager(new MessagingBinders.HeaderDelegateProviders())); } @Override diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/AbstractValueSupplierProvider.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/AbstractValueSupplierProvider.java index 517e8ed78d..88bf6aafed 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/AbstractValueSupplierProvider.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/AbstractValueSupplierProvider.java @@ -62,7 +62,7 @@ */ public abstract class AbstractValueSupplierProvider implements ValueSupplierProvider { - private final MultivaluedParameterExtractorProvider mpep; + private final Provider mpep; private final Set compatibleSources; private final Provider requestProvider; @@ -73,7 +73,7 @@ public abstract class AbstractValueSupplierProvider implements ValueSupplierProv * @param requestProvider container request provider. * @param compatibleSources compatible parameter sources. */ - protected AbstractValueSupplierProvider(MultivaluedParameterExtractorProvider mpep, + protected AbstractValueSupplierProvider(Provider mpep, Provider requestProvider, Parameter.Source... compatibleSources) { this.mpep = mpep; @@ -92,7 +92,7 @@ protected AbstractValueSupplierProvider(MultivaluedParameterExtractorProvider mp * any default values set on the parameter. */ protected final MultivaluedParameterExtractor get(Parameter parameter) { - return mpep.get(parameter); + return mpep.get().get(parameter); } /** diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/BeanParamValueSupplierProvider.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/BeanParamValueSupplierProvider.java index 1fe6f8bef5..af83ae7f5d 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/BeanParamValueSupplierProvider.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/BeanParamValueSupplierProvider.java @@ -107,7 +107,7 @@ public Object get() { * @param mpep multivalued parameter extractor provider. * @param requestProvider request provider. */ - public BeanParamValueSupplierProvider(MultivaluedParameterExtractorProvider mpep, + public BeanParamValueSupplierProvider(Provider mpep, Provider requestProvider, InjectionManager injectionManager) { super(mpep, requestProvider, Parameter.Source.BEAN_PARAM); this.injectionManager = injectionManager; diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/CookieParamValueSupplierProvider.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/CookieParamValueSupplierProvider.java index 9f11a7ce25..7e1ea77f3b 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/CookieParamValueSupplierProvider.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/CookieParamValueSupplierProvider.java @@ -114,7 +114,7 @@ public Cookie get() { * @param mpep multivalued parameter extractor provider. * @param requestProvider request provider. */ - public CookieParamValueSupplierProvider(MultivaluedParameterExtractorProvider mpep, + public CookieParamValueSupplierProvider(Provider mpep, Provider requestProvider) { super(mpep, requestProvider, Parameter.Source.COOKIE); } diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/DelegatedInjectionValueSupplierProvider.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/DelegatedInjectionValueSupplierProvider.java index cadb7c9617..37c9ce5e3b 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/DelegatedInjectionValueSupplierProvider.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/DelegatedInjectionValueSupplierProvider.java @@ -53,6 +53,7 @@ import org.glassfish.jersey.internal.inject.Injectee; import org.glassfish.jersey.internal.inject.InjecteeImpl; import org.glassfish.jersey.internal.util.collection.Cache; +import org.glassfish.jersey.internal.util.collection.LazyValue; import org.glassfish.jersey.process.internal.RequestScoped; import org.glassfish.jersey.server.model.Parameter; import org.glassfish.jersey.server.model.Parameter.Source; @@ -67,7 +68,7 @@ @Singleton class DelegatedInjectionValueSupplierProvider implements ValueSupplierProvider { - private final ContextInjectionResolver resolver; + private final LazyValue resolver; private final Function foreignDescriptorFactory; @@ -77,7 +78,7 @@ class DelegatedInjectionValueSupplierProvider implements ValueSupplierProvider { * @param resolver context injection resolver. * @param foreignDescriptorFactory function that is able to create a new foreign descriptor. */ - public DelegatedInjectionValueSupplierProvider(ContextInjectionResolver resolver, + public DelegatedInjectionValueSupplierProvider(LazyValue resolver, Function foreignDescriptorFactory) { this.resolver = resolver; this.foreignDescriptorFactory = foreignDescriptorFactory; @@ -87,7 +88,7 @@ public DelegatedInjectionValueSupplierProvider(ContextInjectionResolver resolver public Supplier getValueSupplier(final Parameter parameter) { Source paramSource = parameter.getSource(); if (paramSource == Parameter.Source.CONTEXT) { - return () -> resolver.resolve(getInjectee(parameter)); + return () -> resolver.get().resolve(getInjectee(parameter)); } return null; } diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/EntityParamValueSupplierProvider.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/EntityParamValueSupplierProvider.java index 42bf29f104..2efc2057b2 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/EntityParamValueSupplierProvider.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/EntityParamValueSupplierProvider.java @@ -67,7 +67,8 @@ class EntityParamValueSupplierProvider extends AbstractValueSupplierProvider { * @param mpep Injected multivaluedParameterExtractor provider. * @param requestProvider Request provider. */ - EntityParamValueSupplierProvider(MultivaluedParameterExtractorProvider mpep, Provider requestProvider) { + EntityParamValueSupplierProvider(Provider mpep, + Provider requestProvider) { super(mpep, requestProvider, Parameter.Source.ENTITY); } diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/FormParamValueSupplierProvider.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/FormParamValueSupplierProvider.java index 1ff781a98c..ba9e730fbb 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/FormParamValueSupplierProvider.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/FormParamValueSupplierProvider.java @@ -82,9 +82,9 @@ final class FormParamValueSupplierProvider extends AbstractValueSupplierProvider * Injection constructor. * * @param mpep extractor provider. - * @param requestProvider requestProvider. + * @param requestProvider requestSupplier. */ - public FormParamValueSupplierProvider(MultivaluedParameterExtractorProvider mpep, + public FormParamValueSupplierProvider(Provider mpep, Provider requestProvider) { super(mpep, requestProvider, Parameter.Source.FORM); } diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/HeaderParamValueSupplierProvider.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/HeaderParamValueSupplierProvider.java index 6b868c1f84..d63c587561 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/HeaderParamValueSupplierProvider.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/HeaderParamValueSupplierProvider.java @@ -65,7 +65,7 @@ final class HeaderParamValueSupplierProvider extends AbstractValueSupplierProvid * @param mpep multivalued map parameter extractor provider. * @param requestProvider request provider. */ - public HeaderParamValueSupplierProvider(MultivaluedParameterExtractorProvider mpep, + public HeaderParamValueSupplierProvider(Provider mpep, Provider requestProvider) { super(mpep, requestProvider, Parameter.Source.HEADER); } diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/MatrixParamValueSupplierProvider.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/MatrixParamValueSupplierProvider.java index fa88de87e0..2a8dd14b0b 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/MatrixParamValueSupplierProvider.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/MatrixParamValueSupplierProvider.java @@ -68,7 +68,7 @@ final class MatrixParamValueSupplierProvider extends AbstractValueSupplierProvid * @param mpep multivalued map parameter extractor provider. * @param requestProvider request provider. */ - public MatrixParamValueSupplierProvider(MultivaluedParameterExtractorProvider mpep, + public MatrixParamValueSupplierProvider(Provider mpep, Provider requestProvider) { super(mpep, requestProvider, Parameter.Source.MATRIX); } diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/ParamConverterConfigurator.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/ParamConverterConfigurator.java new file mode 100644 index 0000000000..9839ec8599 --- /dev/null +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/ParamConverterConfigurator.java @@ -0,0 +1,65 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package org.glassfish.jersey.server.internal.inject; + +import javax.ws.rs.ext.ParamConverterProvider; + +import org.glassfish.jersey.internal.BootstrapBag; +import org.glassfish.jersey.internal.BootstrapConfigurator; +import org.glassfish.jersey.internal.inject.Bindings; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.internal.inject.InstanceBinding; + +/** + * Configurator which initializes and register {@link ParamConverters.AggregatedProvider} instances into {@link InjectionManager}. + * + * @author Petr Bouda (petr.bouda at oracle.com) + */ +public class ParamConverterConfigurator implements BootstrapConfigurator { + + @Override + public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) { + InstanceBinding aggregatedConverters = + Bindings.service(new ParamConverters.AggregatedProvider()) + .to(ParamConverterProvider.class); + injectionManager.register(aggregatedConverters); + } +} diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/ParamExtractorConfigurator.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/ParamExtractorConfigurator.java new file mode 100644 index 0000000000..403fa36546 --- /dev/null +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/ParamExtractorConfigurator.java @@ -0,0 +1,79 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package org.glassfish.jersey.server.internal.inject; + +import javax.ws.rs.ext.ParamConverterProvider; + +import org.glassfish.jersey.internal.BootstrapBag; +import org.glassfish.jersey.internal.BootstrapConfigurator; +import org.glassfish.jersey.internal.inject.Bindings; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.internal.inject.Providers; +import org.glassfish.jersey.internal.util.collection.LazyValue; +import org.glassfish.jersey.internal.util.collection.Value; +import org.glassfish.jersey.internal.util.collection.Values; +import org.glassfish.jersey.server.ServerBootstrapBag; + +/** + * Configurator which initializes and register {@link MultivaluedParameterExtractorProvider} instance into + * {@link InjectionManager}. + * + * @author Petr Bouda (petr.bouda at oracle.com) + */ +public class ParamExtractorConfigurator implements BootstrapConfigurator { + + @Override + public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) { + ServerBootstrapBag serverBag = (ServerBootstrapBag) bootstrapBag; + + // Param Converters must be initialized Lazy and created at the time of the call on extractor + LazyValue lazyParamConverterFactory = + Values.lazy((Value) () -> new ParamConverterFactory( + Providers.getProviders(injectionManager, ParamConverterProvider.class), + Providers.getCustomProviders(injectionManager, ParamConverterProvider.class))); + + MultivaluedParameterExtractorFactory multiExtractor = new MultivaluedParameterExtractorFactory(lazyParamConverterFactory); + serverBag.setMultivaluedParameterExtractorProvider(multiExtractor); + injectionManager.register( + Bindings.service(multiExtractor) + .to(MultivaluedParameterExtractorProvider.class)); + } +} diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/ParameterInjectionBinder.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/ParameterInjectionBinder.java deleted file mode 100644 index abd63e09d5..0000000000 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/ParameterInjectionBinder.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright (c) 2011-2017 Oracle and/or its affiliates. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common Development - * and Distribution License("CDDL") (collectively, the "License"). You - * may not use this file except in compliance with the License. You can - * obtain a copy of the License at - * http://glassfish.java.net/public/CDDL+GPL_1_1.html - * or packager/legal/LICENSE.txt. See the License for the specific - * language governing permissions and limitations under the License. - * - * When distributing the software, include this License Header Notice in each - * file and include the License file at packager/legal/LICENSE.txt. - * - * GPL Classpath Exception: - * Oracle designates this particular file as subject to the "Classpath" - * exception as provided by Oracle in the GPL Version 2 section of the License - * file that accompanied this code. - * - * Modifications: - * If applicable, add the following below the License Header, with the fields - * enclosed by brackets [] replaced by your own identifying information: - * "Portions Copyright [year] [name of copyright owner]" - * - * Contributor(s): - * If you wish your version of this file to be governed by only the CDDL or - * only the GPL Version 2, indicate your decision by adding "[Contributor] - * elects to include this software in this distribution under the [CDDL or GPL - * Version 2] license." If you don't indicate a single choice of license, a - * recipient has the option to distribute your version of this file under - * either the CDDL, the GPL Version 2 or to extend the choice of license to - * its licensees as provided above. However, if you add GPL Version 2 code - * and therefore, elected the GPL Version 2 license, then the option applies - * only if the new code is made subject to such option by the copyright - * holder. - */ - -package org.glassfish.jersey.server.internal.inject; - -import java.util.function.Function; - -import javax.ws.rs.BeanParam; -import javax.ws.rs.CookieParam; -import javax.ws.rs.FormParam; -import javax.ws.rs.HeaderParam; -import javax.ws.rs.MatrixParam; -import javax.ws.rs.PathParam; -import javax.ws.rs.QueryParam; -import javax.ws.rs.container.Suspended; -import javax.ws.rs.core.Configuration; -import javax.ws.rs.ext.ParamConverterProvider; - -import javax.inject.Provider; - -import org.glassfish.jersey.internal.inject.AbstractBinder; -import org.glassfish.jersey.internal.inject.ContextInjectionResolver; -import org.glassfish.jersey.internal.inject.InjectionManager; -import org.glassfish.jersey.internal.inject.Injections; -import org.glassfish.jersey.internal.inject.Providers; -import org.glassfish.jersey.internal.util.collection.LazyValue; -import org.glassfish.jersey.internal.util.collection.Value; -import org.glassfish.jersey.internal.util.collection.Values; -import org.glassfish.jersey.server.ContainerRequest; -import org.glassfish.jersey.server.Uri; -import org.glassfish.jersey.server.internal.process.AsyncContext; -import org.glassfish.jersey.server.spi.internal.ValueSupplierProvider; - -/** - * Injection binder providing support for JAX-RS and Jersey injection annotations. - * Namely, standard injection support for the following set of JAX-RS and Jersey - * annotations is provided by the binder: - *
- *

- *

{@link javax.ws.rs.core.Context @Context}
- *
- * Generic support for the {@code @Context}-based injection is provided so that - * the {@code @Context} annotation can be used interchangeably with e.g. standard - * {@code @Inject} dependency injection annotation. - *
- *

- *

{@link javax.ws.rs.CookieParam @CookieParam}
- *
- * Support for cookie parameter injection as defined by the JAX-RS specification. - *
- *

- *

{@link javax.ws.rs.FormParam @FormParam}
- *
- * Support for form parameter injection as defined by the JAX-RS specification. - *
- *

- *

{@link javax.ws.rs.HeaderParam @HeaderParam}
- *
- * Support for request header parameter injection as defined by the JAX-RS specification. - *
- *

- *

{@link javax.ws.rs.MatrixParam @MatrixParam}
- *
- * Support for request URI matrix path parameter injection as defined by the JAX-RS specification. - *
- *

- *

{@link javax.ws.rs.PathParam @PathParam}
- *
- * Support for request URI path parameter injection as defined by the JAX-RS specification. - *
- *

- *

{@link javax.ws.rs.QueryParam @QueryParam}
- *
- * Support for request URI query parameter injection as defined by the JAX-RS specification. - *
- *

- *

{@link javax.ws.rs.container.Suspended @Suspended}
- *
- * Support for {@link javax.ws.rs.container.AsyncResponse} injection as defined by the JAX-RS specification. - *
- *

- *

{@link org.glassfish.jersey.server.Uri @Uri}
- *
- * Jersey-specific support for {@link javax.ws.rs.client.WebTarget} injection. - *
- *

- *

- * - * @author Marek Potociar (marek.potociar at oracle.com) - */ -public class ParameterInjectionBinder extends AbstractBinder { - - private final InjectionManager injectionManager; - - public ParameterInjectionBinder(InjectionManager injectionManager) { - this.injectionManager = injectionManager; - } - - @Override - public void configure() { - // Param converter providers - // TODO: Replace by non-di version - bind(new ParamConverters.AggregatedProvider()).to(ParamConverterProvider.class); - - Provider requestProvider = Injections.getProvider(injectionManager, ContainerRequest.class); - Provider asyncContextProvider = Injections.getProvider(injectionManager, AsyncContext.class); - Function, Configuration> clientConfigProvider = clientConfigClass -> Injections - .getOrCreate(injectionManager, clientConfigClass); - - // Param Converters must be initialized Lazy and created at the time of the call on extractor - LazyValue lazyParamConverterFactory = Values.lazy((Value) - () -> new ParamConverterFactory( - Providers.getProviders(injectionManager, ParamConverterProvider.class), - Providers.getCustomProviders(injectionManager, ParamConverterProvider.class))); - - LazyValue lazyConfiguration = Values - .lazy((Value) () -> injectionManager.getInstance(Configuration.class)); - - MultivaluedParameterExtractorFactory paramExtractor = new MultivaluedParameterExtractorFactory(lazyParamConverterFactory); - bind(paramExtractor).to(MultivaluedParameterExtractorProvider.class); - - // Parameter injection value providers - AsyncResponseValueSupplierProvider asyncSupplier = new AsyncResponseValueSupplierProvider(asyncContextProvider); - bindValueSupplier(asyncSupplier); - CookieParamValueSupplierProvider cookieSupplier = new CookieParamValueSupplierProvider(paramExtractor, requestProvider); - bindValueSupplier(cookieSupplier); - EntityParamValueSupplierProvider entitySupplier = new EntityParamValueSupplierProvider(paramExtractor, requestProvider); - bindValueSupplier(entitySupplier); - FormParamValueSupplierProvider formSupplier = new FormParamValueSupplierProvider(paramExtractor, requestProvider); - bindValueSupplier(formSupplier); - HeaderParamValueSupplierProvider headerSupplier = new HeaderParamValueSupplierProvider(paramExtractor, requestProvider); - bindValueSupplier(headerSupplier); - MatrixParamValueSupplierProvider matrixSupplier = new MatrixParamValueSupplierProvider(paramExtractor, requestProvider); - bindValueSupplier(matrixSupplier); - PathParamValueSupplierProvider pathSupplier = new PathParamValueSupplierProvider(paramExtractor, requestProvider); - bindValueSupplier(pathSupplier); - QueryParamValueSupplierProvider querySupplier = new QueryParamValueSupplierProvider(paramExtractor, requestProvider); - bindValueSupplier(querySupplier); - WebTargetValueSupplierProvider webTargetSupplier = new WebTargetValueSupplierProvider(requestProvider, lazyConfiguration, - clientConfigProvider); - bindValueSupplier(webTargetSupplier); - BeanParamValueSupplierProvider beanSupplier = new BeanParamValueSupplierProvider(paramExtractor, requestProvider, - injectionManager); - bindValueSupplier(beanSupplier); - - // Register InjectionResolvers with param providers - // TODO: RENAME INJECTION RESOLVER - bind(new ParamInjectionResolver<>(cookieSupplier, CookieParam.class)); - bind(new ParamInjectionResolver<>(formSupplier, FormParam.class)); - bind(new ParamInjectionResolver<>(headerSupplier, HeaderParam.class)); - bind(new ParamInjectionResolver<>(matrixSupplier, MatrixParam.class)); - bind(new ParamInjectionResolver<>(querySupplier, QueryParam.class)); - bind(new ParamInjectionResolver<>(pathSupplier, PathParam.class)); - bind(new ParamInjectionResolver<>(asyncSupplier, Suspended.class)); - bind(new ParamInjectionResolver<>(webTargetSupplier, Uri.class)); - bind(new ParamInjectionResolver<>(beanSupplier, BeanParam.class)); - - // Delegated value supplier for Context InjectionResolver which is implemented directly in DI provider - ContextInjectionResolver contextInjectionResolver = injectionManager.getInstance(ContextInjectionResolver.class); - bind(new DelegatedInjectionValueSupplierProvider(contextInjectionResolver, - injectionManager::createForeignDescriptor)).to(ValueSupplierProvider.class); - } - - private void bindValueSupplier(T supplier) { - bind(supplier).to(ValueSupplierProvider.class); - } -} diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/PathParamValueSupplierProvider.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/PathParamValueSupplierProvider.java index 7f5987ce6d..1a1f325f57 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/PathParamValueSupplierProvider.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/PathParamValueSupplierProvider.java @@ -69,7 +69,7 @@ final class PathParamValueSupplierProvider extends AbstractValueSupplierProvider * @param mpep multivalued map parameter extractor provider. * @param requestProvider request provider. */ - public PathParamValueSupplierProvider(MultivaluedParameterExtractorProvider mpep, + public PathParamValueSupplierProvider(Provider mpep, Provider requestProvider) { super(mpep, requestProvider, Parameter.Source.PATH); } diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/QueryParamValueSupplierProvider.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/QueryParamValueSupplierProvider.java index a725dc49f4..7b4e18d89e 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/QueryParamValueSupplierProvider.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/QueryParamValueSupplierProvider.java @@ -65,7 +65,7 @@ final class QueryParamValueSupplierProvider extends AbstractValueSupplierProvide * @param mpep multivalued map parameter extractor provider. * @param requestProvider request provider. */ - public QueryParamValueSupplierProvider(MultivaluedParameterExtractorProvider mpep, + public QueryParamValueSupplierProvider(Provider mpep, Provider requestProvider) { super(mpep, requestProvider, Parameter.Source.QUERY); } diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/ValueSupplierProviderConfigurator.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/ValueSupplierProviderConfigurator.java new file mode 100644 index 0000000000..c5fe889779 --- /dev/null +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/ValueSupplierProviderConfigurator.java @@ -0,0 +1,176 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package org.glassfish.jersey.server.internal.inject; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; + +import javax.ws.rs.BeanParam; +import javax.ws.rs.CookieParam; +import javax.ws.rs.FormParam; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.MatrixParam; +import javax.ws.rs.PathParam; +import javax.ws.rs.QueryParam; +import javax.ws.rs.container.Suspended; +import javax.ws.rs.core.Configuration; + +import javax.inject.Provider; + +import org.glassfish.jersey.internal.BootstrapBag; +import org.glassfish.jersey.internal.BootstrapConfigurator; +import org.glassfish.jersey.internal.inject.Bindings; +import org.glassfish.jersey.internal.inject.ContextInjectionResolver; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.internal.inject.Injections; +import org.glassfish.jersey.internal.util.collection.LazyValue; +import org.glassfish.jersey.internal.util.collection.Value; +import org.glassfish.jersey.internal.util.collection.Values; +import org.glassfish.jersey.server.ContainerRequest; +import org.glassfish.jersey.server.ServerBootstrapBag; +import org.glassfish.jersey.server.Uri; +import org.glassfish.jersey.server.internal.process.AsyncContext; +import org.glassfish.jersey.server.spi.internal.ValueSupplierProvider; + +/** + * Configurator which initializes and register {@link ValueSupplierProvider} instances into {@link InjectionManager} and + * {@link BootstrapBag}. + * + * @author Petr Bouda (petr.bouda at oracle.com) + */ +public class ValueSupplierProviderConfigurator implements BootstrapConfigurator { + + public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) { + ServerBootstrapBag serverBag = (ServerBootstrapBag) bootstrapBag; + + Provider requestProvider = () -> injectionManager.getInstance(ContainerRequest.class); + Provider asyncContextProvider = () -> injectionManager.getInstance(AsyncContext.class); + + Function, Configuration> clientConfigProvider = + clientConfigClass -> Injections.getOrCreate(injectionManager, clientConfigClass); + + LazyValue lazyConfiguration = + Values.lazy((Value) () -> injectionManager.getInstance(Configuration.class)); + + LazyValue lazyContextResolver = + Values.lazy((Value) () -> injectionManager.getInstance(ContextInjectionResolver.class)); + + Provider paramExtractor = serverBag::getMultivaluedParameterExtractorProvider; + + // Parameter injection value providers + Collection suppliers = new ArrayList<>(); + + AsyncResponseValueSupplierProvider asyncProvider = new AsyncResponseValueSupplierProvider(asyncContextProvider); + suppliers.add(asyncProvider); + + CookieParamValueSupplierProvider cookieProvider = new CookieParamValueSupplierProvider(paramExtractor, requestProvider); + suppliers.add(cookieProvider); + + EntityParamValueSupplierProvider entityProvider = new EntityParamValueSupplierProvider(paramExtractor, requestProvider); + suppliers.add(entityProvider); + + FormParamValueSupplierProvider formProvider = new FormParamValueSupplierProvider(paramExtractor, requestProvider); + suppliers.add(formProvider); + + HeaderParamValueSupplierProvider headerProvider = new HeaderParamValueSupplierProvider(paramExtractor, requestProvider); + suppliers.add(headerProvider); + + MatrixParamValueSupplierProvider matrixProvider = new MatrixParamValueSupplierProvider(paramExtractor, requestProvider); + suppliers.add(matrixProvider); + + PathParamValueSupplierProvider pathProvider = new PathParamValueSupplierProvider(paramExtractor, requestProvider); + suppliers.add(pathProvider); + + QueryParamValueSupplierProvider queryProvider = new QueryParamValueSupplierProvider(paramExtractor, requestProvider); + suppliers.add(queryProvider); + + BeanParamValueSupplierProvider beanProvider = + new BeanParamValueSupplierProvider(paramExtractor, requestProvider, injectionManager); + suppliers.add(beanProvider); + + WebTargetValueSupplierProvider webTargetProvider = + new WebTargetValueSupplierProvider(requestProvider, lazyConfiguration, clientConfigProvider); + suppliers.add(webTargetProvider); + + DelegatedInjectionValueSupplierProvider contextProvider = + new DelegatedInjectionValueSupplierProvider(lazyContextResolver, injectionManager::createForeignDescriptor); + suppliers.add(contextProvider); + + serverBag.setValueSupplierProviders(Collections.unmodifiableCollection(suppliers)); + + // Needs to be in InjectionManager because of CdiComponentProvider + injectionManager.register(Bindings.service(asyncProvider).to(ValueSupplierProvider.class)); + injectionManager.register(Bindings.service(cookieProvider).to(ValueSupplierProvider.class)); + injectionManager.register(Bindings.service(formProvider).to(ValueSupplierProvider.class)); + injectionManager.register(Bindings.service(headerProvider).to(ValueSupplierProvider.class)); + injectionManager.register(Bindings.service(matrixProvider).to(ValueSupplierProvider.class)); + injectionManager.register(Bindings.service(pathProvider).to(ValueSupplierProvider.class)); + injectionManager.register(Bindings.service(queryProvider).to(ValueSupplierProvider.class)); + injectionManager.register(Bindings.service(webTargetProvider).to(ValueSupplierProvider.class)); + injectionManager.register(Bindings.service(beanProvider).to(ValueSupplierProvider.class)); + injectionManager.register(Bindings.service(entityProvider).to(ValueSupplierProvider.class)); + injectionManager.register(Bindings.service(contextProvider).to(ValueSupplierProvider.class)); + + injectionManager.register(Bindings.injectionResolver(new ParamInjectionResolver<>(asyncProvider, Suspended.class))); + injectionManager.register(Bindings.injectionResolver(new ParamInjectionResolver<>(cookieProvider, CookieParam.class))); + injectionManager.register(Bindings.injectionResolver(new ParamInjectionResolver<>(formProvider, FormParam.class))); + injectionManager.register(Bindings.injectionResolver(new ParamInjectionResolver<>(headerProvider, HeaderParam.class))); + injectionManager.register(Bindings.injectionResolver(new ParamInjectionResolver<>(matrixProvider, MatrixParam.class))); + injectionManager.register(Bindings.injectionResolver(new ParamInjectionResolver<>(pathProvider, PathParam.class))); + injectionManager.register(Bindings.injectionResolver(new ParamInjectionResolver<>(queryProvider, QueryParam.class))); + injectionManager.register(Bindings.injectionResolver(new ParamInjectionResolver<>(webTargetProvider, Uri.class))); + injectionManager.register(Bindings.injectionResolver(new ParamInjectionResolver<>(beanProvider, BeanParam.class))); + } + + @Override + public void postInit(InjectionManager injectionManager, BootstrapBag bootstrapBag) { + // Add the ValueSupplierProviders which has been added to ResourceConfig/Feature + List addedInstances = injectionManager.getAllInstances(ValueSupplierProvider.class); + if (!addedInstances.isEmpty()) { + ServerBootstrapBag serverBag = (ServerBootstrapBag) bootstrapBag; + addedInstances.addAll(serverBag.getValueSupplierProviders()); + serverBag.setValueSupplierProviders(Collections.unmodifiableCollection(addedInstances)); + } + } +} diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/WebTargetValueSupplierProvider.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/WebTargetValueSupplierProvider.java index 21015b124e..667d3271ba 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/WebTargetValueSupplierProvider.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/WebTargetValueSupplierProvider.java @@ -44,10 +44,12 @@ import java.security.AccessController; import java.util.Arrays; import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.Function; +import java.util.stream.Collectors; import javax.ws.rs.ProcessingException; import javax.ws.rs.client.Client; @@ -74,10 +76,6 @@ import org.glassfish.jersey.server.model.Parameter; import org.glassfish.jersey.uri.internal.JerseyUriBuilder; -import jersey.repackaged.com.google.common.base.Predicate; -import jersey.repackaged.com.google.common.collect.Collections2; -import jersey.repackaged.com.google.common.collect.Maps; - /** * Value supplier provider supporting the {@link Uri} injection annotation. * @@ -134,12 +132,10 @@ public static BindingModel create(Annotation binding) { */ public static BindingModel create(final Collection bindingCandidates) { final Collection filtered = - Collections2.filter(bindingCandidates, new Predicate() { - @Override - public boolean apply(Annotation input) { - return input != null && input.annotationType().getAnnotation(ClientBinding.class) != null; - } - }); + bindingCandidates.stream() + .filter(input -> input != null + && input.annotationType().getAnnotation(ClientBinding.class) != null) + .collect(Collectors.toList()); if (filtered.isEmpty()) { return EMPTY; @@ -254,7 +250,15 @@ public WebTarget get() { final ExtendedUriInfo uriInfo = getRequest().getUriInfo(); final Map pathParamValues = - Maps.transformValues(uriInfo.getPathParameters(), input -> input.isEmpty() ? null : input.get(0)); + uriInfo.getPathParameters().entrySet() + .stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + (Function>, Object>) stringObjectEntry -> { + List input = stringObjectEntry.getValue(); + return input.isEmpty() ? null : input.get(0); + })); + JerseyUriBuilder uriBuilder = new JerseyUriBuilder().uri(this.uri).resolveTemplates(pathParamValues); final ManagedClient managedClient = client.get(); @@ -359,12 +363,10 @@ public ManagedClient get() { final String propertyPrefix = prefix + "property."; Collection clientProperties = - Collections2.filter(serverConfig.get().getPropertyNames(), new Predicate() { - @Override - public boolean apply(String property) { - return property.startsWith(propertyPrefix); - } - }); + serverConfig.get().getPropertyNames() + .stream() + .filter(property -> property.startsWith(propertyPrefix)) + .collect(Collectors.toSet()); for (String property : clientProperties) { cfg.property(property.substring(propertyPrefix.length()), diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/AggregatingTrimmer.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/AggregatingTrimmer.java index cbe44112d4..bfaa33fe41 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/AggregatingTrimmer.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/AggregatingTrimmer.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -49,9 +49,9 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import static org.glassfish.jersey.server.internal.monitoring.ReservoirConstants.COLLISION_BUFFER_POWER; +import org.glassfish.jersey.internal.guava.TreeMultimap; -import jersey.repackaged.com.google.common.collect.TreeMultimap; +import static org.glassfish.jersey.server.internal.monitoring.ReservoirConstants.COLLISION_BUFFER_POWER; /** * An aggregating trimmer for sliding window measurements. This trimmer updates registered time reservoirs with the aggregated diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/CompositeApplicationEventListener.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/CompositeApplicationEventListener.java index 92f399487a..c2ce9d94c2 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/CompositeApplicationEventListener.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/CompositeApplicationEventListener.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -40,6 +40,7 @@ package org.glassfish.jersey.server.internal.monitoring; +import java.util.ArrayList; import java.util.List; import org.glassfish.jersey.server.monitoring.ApplicationEvent; @@ -47,8 +48,6 @@ import org.glassfish.jersey.server.monitoring.RequestEvent; import org.glassfish.jersey.server.monitoring.RequestEventListener; -import jersey.repackaged.com.google.common.collect.Lists; - /** * {@link ApplicationEventListener application event listener} that aggregates more event listeners into one. * Calling listener methods on this listener will cause calling methods on all aggregated listeners. @@ -77,7 +76,7 @@ public void onEvent(final ApplicationEvent event) { @Override public RequestEventListener onRequest(final RequestEvent requestEvent) { - final List requestEventListeners = Lists.newArrayList(); + final List requestEventListeners = new ArrayList<>(); for (final ApplicationEventListener applicationEventListener : applicationEventListeners) { final RequestEventListener requestEventListener = applicationEventListener.onRequest(requestEvent); if (requestEventListener != null) { diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/ExceptionMapperStatisticsImpl.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/ExceptionMapperStatisticsImpl.java index fa2aa7da81..e118a19d53 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/ExceptionMapperStatisticsImpl.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/ExceptionMapperStatisticsImpl.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -46,8 +46,6 @@ import org.glassfish.jersey.server.monitoring.ExceptionMapperStatistics; -import jersey.repackaged.com.google.common.collect.Maps; - /** * Exception mapper statistics. * @@ -62,7 +60,7 @@ final class ExceptionMapperStatisticsImpl implements ExceptionMapperStatistics { */ static class Builder { - private Map, Long> exceptionMapperExecutionCountMap = Maps.newHashMap(); + private Map, Long> exceptionMapperExecutionCountMap = new HashMap<>(); private long successfulMappings; private long unsuccessfulMappings; private long totalMappings; diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/ExecutionStatisticsImpl.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/ExecutionStatisticsImpl.java index 181748b8e6..361704dced 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/ExecutionStatisticsImpl.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/ExecutionStatisticsImpl.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -40,6 +40,7 @@ package org.glassfish.jersey.server.internal.monitoring; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -50,9 +51,6 @@ import org.glassfish.jersey.server.monitoring.ExecutionStatistics; import org.glassfish.jersey.server.monitoring.TimeWindowStatistics; -import jersey.repackaged.com.google.common.collect.ImmutableList; -import jersey.repackaged.com.google.common.collect.Maps; - /** * Immutable Execution statistics. * @@ -90,7 +88,8 @@ public Builder() { final TimeWindowStatisticsImpl.Builder infiniteIntervalWindowBuilder = new TimeWindowStatisticsImpl.Builder<>(new UniformTimeReservoir(nowMillis, TimeUnit.MILLISECONDS)); - this.updatableIntervalStatistics = ImmutableList.of(infiniteIntervalWindowBuilder, oneSecondIntervalWindowBuilder); + this.updatableIntervalStatistics = + Arrays.asList(infiniteIntervalWindowBuilder, oneSecondIntervalWindowBuilder); // create unmodifiable map to ensure that an iteration in the build() won't have multi-threading issues final HashMap tmpIntervalStatistics = new HashMap<>(6); @@ -142,7 +141,7 @@ void addExecution(final long startTime, final long duration) { * @return new instance of execution statistics. */ public ExecutionStatisticsImpl build() { - final Map newIntervalStatistics = Maps.newHashMap(); + final Map newIntervalStatistics = new HashMap<>(); for (final Map.Entry builderEntry : intervalStatistics.entrySet()) { newIntervalStatistics.put(builderEntry.getKey(), builderEntry.getValue().build()); } diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/MonitoringEventListener.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/MonitoringEventListener.java index 6cf88f767f..5e6574e676 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/MonitoringEventListener.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/MonitoringEventListener.java @@ -40,10 +40,13 @@ package org.glassfish.jersey.server.internal.monitoring; +import java.util.Collections; import java.util.List; import java.util.Queue; +import java.util.concurrent.ArrayBlockingQueue; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; import javax.ws.rs.ProcessingException; @@ -60,9 +63,6 @@ import org.glassfish.jersey.server.monitoring.RequestEventListener; import org.glassfish.jersey.uri.UriTemplate; -import jersey.repackaged.com.google.common.collect.Lists; -import jersey.repackaged.com.google.common.collect.Queues; - /** * {@link ApplicationEventListener application event listener} that listens to {@link ApplicationEvent application} * and {@link RequestEvent request} events and supplies data to {@link MonitoringStatisticsProcessor} which @@ -88,9 +88,9 @@ public final class MonitoringEventListener implements ApplicationEventListener { @Inject private InjectionManager injectionManager; - private final Queue requestQueuedItems = Queues.newArrayBlockingQueue(EVENT_QUEUE_SIZE); - private final Queue responseStatuses = Queues.newArrayBlockingQueue(EVENT_QUEUE_SIZE); - private final Queue exceptionMapperEvents = Queues.newArrayBlockingQueue(EVENT_QUEUE_SIZE); + private final Queue requestQueuedItems = new ArrayBlockingQueue<>(EVENT_QUEUE_SIZE); + private final Queue responseStatuses = new ArrayBlockingQueue<>(EVENT_QUEUE_SIZE); + private final Queue exceptionMapperEvents = new ArrayBlockingQueue<>(EVENT_QUEUE_SIZE); private volatile MonitoringStatisticsProcessor monitoringStatisticsProcessor; /** @@ -273,7 +273,13 @@ public void onEvent(final RequestEvent event) { } } final StringBuilder sb = new StringBuilder(); - final List orderedTemplates = Lists.reverse(event.getUriInfo().getMatchedTemplates()); + final List orderedTemplates = + event.getUriInfo().getMatchedTemplates() + .stream() + .collect(Collectors.collectingAndThen(Collectors.toList(), uriTemplates -> { + Collections.reverse(uriTemplates); + return uriTemplates; + })); for (final UriTemplate uriTemplate : orderedTemplates) { sb.append(uriTemplate.getTemplate()); diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/MonitoringStatisticsImpl.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/MonitoringStatisticsImpl.java index 2c541ce0cc..5426b2aa51 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/MonitoringStatisticsImpl.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/MonitoringStatisticsImpl.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -41,10 +41,12 @@ package org.glassfish.jersey.server.internal.monitoring; import java.util.Collections; -import java.util.Comparator; import java.util.Map; import java.util.SortedMap; +import java.util.TreeMap; +import java.util.function.Function; +import org.glassfish.jersey.internal.util.collection.Views; import org.glassfish.jersey.server.model.Resource; import org.glassfish.jersey.server.model.ResourceMethod; import org.glassfish.jersey.server.model.ResourceModel; @@ -54,9 +56,6 @@ import org.glassfish.jersey.server.monitoring.ResourceStatistics; import org.glassfish.jersey.server.monitoring.ResponseStatistics; -import jersey.repackaged.com.google.common.base.Function; -import jersey.repackaged.com.google.common.collect.Maps; - /** * Monitoring statistics implementation. *

@@ -90,25 +89,15 @@ final class MonitoringStatisticsImpl implements MonitoringStatistics { static class Builder { private static final Function BUILDING_FUNCTION = - new Function() { - @Override - public ResourceStatistics apply(final ResourceStatisticsImpl.Builder builder) { - return builder.build(); - } - }; + ResourceStatisticsImpl.Builder::build; private final ResponseStatisticsImpl.Builder responseStatisticsBuilder; private final ExceptionMapperStatisticsImpl.Builder exceptionMapperStatisticsBuilder; private final ResourceMethodStatisticsImpl.Factory methodFactory = new ResourceMethodStatisticsImpl.Factory(); - private final SortedMap uriStatistics = Maps.newTreeMap(); + private final SortedMap uriStatistics = new TreeMap<>(); private final SortedMap, ResourceStatisticsImpl.Builder> resourceClassStatistics - = Maps.newTreeMap(new Comparator>() { - @Override - public int compare(final Class o1, final Class o2) { - return o1.getName().compareTo(o2.getName()); - } - }); + = new TreeMap<>((o1, o2) -> o1.getName().compareTo(o2.getName())); private ExecutionStatisticsImpl.Builder executionStatisticsBuilder; @@ -212,7 +201,7 @@ void addExecution(final String uri, final ResourceMethod resourceMethod, // Resource method stats. methodFactory.getOrCreate(resourceMethod) - .addResourceMethodExecution(methodTime, methodDuration, requestTime, requestDuration); + .addResourceMethodExecution(methodTime, methodDuration, requestTime, requestDuration); } /** @@ -231,9 +220,9 @@ void addResponseCode(final int responseCode) { */ MonitoringStatisticsImpl build() { final Map uriStats = Collections.unmodifiableMap( - Maps.transformValues(uriStatistics, BUILDING_FUNCTION)); + Views.mapView(uriStatistics, BUILDING_FUNCTION)); final Map, ResourceStatistics> classStats = Collections.unmodifiableMap( - Maps.transformValues(resourceClassStatistics, BUILDING_FUNCTION)); + Views.mapView(resourceClassStatistics, BUILDING_FUNCTION)); final ExecutionStatistics requestStats = executionStatisticsBuilder == null ? ExecutionStatisticsImpl.EMPTY : executionStatisticsBuilder.build(); @@ -274,7 +263,7 @@ public ResponseStatistics getResponseStatistics() { } /** - * Refreshed (re-built) on every access. (uses {@link Maps#transformValues(Map, Function)}) + * Refreshed (re-built) on every access. * * @return resource statistics */ @@ -284,7 +273,7 @@ public Map getUriStatistics() { } /** - * Refreshed (re-built) on every access. (uses {@link Maps#transformValues(Map, Function)}) + * Refreshed (re-built) on every access. * * @return resource statistics */ diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/ResourceStatisticsImpl.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/ResourceStatisticsImpl.java index 09642eb4b4..22bd0efa97 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/ResourceStatisticsImpl.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/ResourceStatisticsImpl.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -41,6 +41,7 @@ package org.glassfish.jersey.server.internal.monitoring; import java.util.Collections; +import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -52,8 +53,6 @@ import org.glassfish.jersey.server.monitoring.ResourceMethodStatistics; import org.glassfish.jersey.server.monitoring.ResourceStatistics; -import jersey.repackaged.com.google.common.collect.Maps; - /** * Immutable resource statistics implementation. * @@ -112,7 +111,7 @@ ResourceStatisticsImpl build() { return cachedReference; } - final Map resourceMethods = Maps.newHashMap(); + final Map resourceMethods = new HashMap<>(); for (final ResourceMethodStatisticsImpl.Builder builder : methodsBuilders.keySet()) { final ResourceMethodStatisticsImpl stats = builder.build(); resourceMethods.put(stats.getResourceMethod(), stats); diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/ResponseStatisticsImpl.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/ResponseStatisticsImpl.java index 0576a085fa..911f155740 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/ResponseStatisticsImpl.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/ResponseStatisticsImpl.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -46,8 +46,6 @@ import org.glassfish.jersey.server.monitoring.ResponseStatistics; -import jersey.repackaged.com.google.common.collect.Maps; - /** * Immutable response statistics. * @@ -63,7 +61,7 @@ final class ResponseStatisticsImpl implements ResponseStatistics { */ static class Builder { - private final Map responseCodesMap = Maps.newHashMap(); + private final Map responseCodesMap = new HashMap<>(); private Integer lastResponseCode = null; private ResponseStatisticsImpl cached = null; diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/ApplicationMXBeanImpl.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/ApplicationMXBeanImpl.java index 27617b5df7..3d0581135b 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/ApplicationMXBeanImpl.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/ApplicationMXBeanImpl.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -41,16 +41,15 @@ package org.glassfish.jersey.server.internal.monitoring.jmx; import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Set; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.server.internal.LocalizationMessages; -import org.glassfish.jersey.server.monitoring.ApplicationMXBean; import org.glassfish.jersey.server.monitoring.ApplicationInfo; - -import jersey.repackaged.com.google.common.collect.Maps; -import jersey.repackaged.com.google.common.collect.Sets; +import org.glassfish.jersey.server.monitoring.ApplicationMXBean; /** * MXBean implementing {@link org.glassfish.jersey.server.monitoring.ApplicationMXBean} MXBean interface. @@ -76,9 +75,9 @@ public class ApplicationMXBeanImpl implements ApplicationMXBean { */ public ApplicationMXBeanImpl(final ApplicationInfo applicationInfo, final MBeanExposer mBeanExposer, final String parentName) { - this.providers = Sets.newHashSet(); - this.registeredClasses = Sets.newHashSet(); - this.registeredInstances = Sets.newHashSet(); + this.providers = new HashSet<>(); + this.registeredClasses = new HashSet<>(); + this.registeredInstances = new HashSet<>(); for (final Class provider : applicationInfo.getProviders()) { this.providers.add(provider.getName()); @@ -95,7 +94,7 @@ public ApplicationMXBeanImpl(final ApplicationInfo applicationInfo, final MBeanE final ResourceConfig resourceConfig = applicationInfo.getResourceConfig(); this.applicationName = resourceConfig.getApplicationName(); this.applicationClass = resourceConfig.getApplication().getClass().getName(); - this.configurationProperties = Maps.newHashMap(); + this.configurationProperties = new HashMap<>(); for (final Map.Entry entry : resourceConfig.getProperties().entrySet()) { final Object value = entry.getValue(); String stringValue; diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/ExceptionMapperMXBeanImpl.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/ExceptionMapperMXBeanImpl.java index 5f3d2d1ed4..df809afc1f 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/ExceptionMapperMXBeanImpl.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/ExceptionMapperMXBeanImpl.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -40,13 +40,12 @@ package org.glassfish.jersey.server.internal.monitoring.jmx; +import java.util.HashMap; import java.util.Map; import org.glassfish.jersey.server.monitoring.ExceptionMapperMXBean; import org.glassfish.jersey.server.monitoring.ExceptionMapperStatistics; -import jersey.repackaged.com.google.common.collect.Maps; - /** * MXBean implementing a {@link org.glassfish.jersey.server.monitoring.ExceptionMapperMXBean} mxbean interface. * @@ -54,7 +53,7 @@ */ public class ExceptionMapperMXBeanImpl implements ExceptionMapperMXBean { private volatile ExceptionMapperStatistics mapperStatistics; - private volatile Map mapperExcecutions = Maps.newHashMap(); + private volatile Map mapperExcecutions = new HashMap<>(); /** * Create a new MXBean and register it into mbean server using {@code mBeanExposer}. diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/ExecutionStatisticsDynamicBean.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/ExecutionStatisticsDynamicBean.java index 3b9688d995..509c6aad03 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/ExecutionStatisticsDynamicBean.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/ExecutionStatisticsDynamicBean.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -40,6 +40,7 @@ package org.glassfish.jersey.server.internal.monitoring.jmx; +import java.util.HashMap; import java.util.Map; import javax.management.Attribute; @@ -56,8 +57,6 @@ import org.glassfish.jersey.server.monitoring.ExecutionStatistics; import org.glassfish.jersey.server.monitoring.TimeWindowStatistics; -import jersey.repackaged.com.google.common.collect.Maps; - /** * Dynamic MBean that exposes information about execution statistics. The exposed information contains * execution statistics for various time window sizes. @@ -67,7 +66,7 @@ public class ExecutionStatisticsDynamicBean implements DynamicMBean { private volatile ExecutionStatistics executionStatistics; - private final Map> attributeValues = Maps.newHashMap(); + private final Map> attributeValues = new HashMap<>(); private final MBeanInfo mBeanInfo; diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/MBeanExposer.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/MBeanExposer.java index aab57e24ce..c201002066 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/MBeanExposer.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/MBeanExposer.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -41,16 +41,17 @@ package org.glassfish.jersey.server.internal.monitoring.jmx; import java.lang.management.ManagementFactory; +import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; -import javax.inject.Inject; -import javax.inject.Provider; import javax.ws.rs.ProcessingException; +import javax.inject.Inject; +import javax.inject.Provider; import javax.management.JMException; import javax.management.MBeanServer; import javax.management.ObjectName; @@ -63,8 +64,6 @@ import org.glassfish.jersey.server.spi.AbstractContainerLifecycleListener; import org.glassfish.jersey.server.spi.Container; -import jersey.repackaged.com.google.common.collect.Maps; - /** * The main exposer class of Jersey JMX MBeans. The class creates MBeans and contains methods that * register and unregister MBeans. @@ -99,7 +98,7 @@ public class MBeanExposer extends AbstractContainerLifecycleListener implements private Map transformToStringKeys(Map, ResourceStatistics> stats) { - Map newMap = Maps.newHashMap(); + Map newMap = new HashMap<>(); for (Map.Entry, ResourceStatistics> entry : stats.entrySet()) { newMap.put(entry.getKey().getName(), entry.getValue()); } diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/ResourceMxBeanImpl.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/ResourceMxBeanImpl.java index 197c0cd270..a66188bbb9 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/ResourceMxBeanImpl.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/ResourceMxBeanImpl.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -40,6 +40,7 @@ package org.glassfish.jersey.server.internal.monitoring.jmx; +import java.util.HashMap; import java.util.Map; import org.glassfish.jersey.server.internal.monitoring.MonitoringUtils; @@ -48,8 +49,6 @@ import org.glassfish.jersey.server.monitoring.ResourceMethodStatistics; import org.glassfish.jersey.server.monitoring.ResourceStatistics; -import jersey.repackaged.com.google.common.collect.Maps; - /** * MXBean implementing the {@link org.glassfish.jersey.server.monitoring.ResourceMethodMXBean} MXBean interface. * @@ -59,7 +58,7 @@ public class ResourceMxBeanImpl implements ResourceMXBean { private final String name; private volatile ExecutionStatisticsDynamicBean methodsExecutionStatisticsBean; private volatile ExecutionStatisticsDynamicBean requestExecutionStatisticsBean; - private final Map resourceMethods = Maps.newHashMap(); + private final Map resourceMethods = new HashMap<>(); private final String resourcePropertyName; private final boolean uriResource; private final MBeanExposer mBeanExposer; diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/ResourcesMBeanGroup.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/ResourcesMBeanGroup.java index 2a7cc5ef49..bf274b048a 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/ResourcesMBeanGroup.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/jmx/ResourcesMBeanGroup.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -40,19 +40,18 @@ package org.glassfish.jersey.server.internal.monitoring.jmx; +import java.util.HashMap; import java.util.Map; import org.glassfish.jersey.server.monitoring.ResourceStatistics; -import jersey.repackaged.com.google.common.collect.Maps; - /** * Group of resource MXBeans. * * @author Miroslav Fuksa */ public class ResourcesMBeanGroup { - private final Map exposedResourceMBeans = Maps.newHashMap(); + private final Map exposedResourceMBeans = new HashMap<>(); private final String parentName; private final boolean uriResource; private final MBeanExposer exposer; diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/process/DefaultCloseableService.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/process/DefaultCloseableService.java index 52c9ad8f04..d9fb1282b7 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/process/DefaultCloseableService.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/process/DefaultCloseableService.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2014-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -40,6 +40,8 @@ package org.glassfish.jersey.server.internal.process; import java.io.Closeable; +import java.util.Collections; +import java.util.IdentityHashMap; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; @@ -48,8 +50,6 @@ import org.glassfish.jersey.server.CloseableService; import org.glassfish.jersey.server.internal.LocalizationMessages; -import jersey.repackaged.com.google.common.collect.Sets; - /** * Default implementation of {@link CloseableService}. * @@ -63,7 +63,7 @@ class DefaultCloseableService implements CloseableService { private static final Logger LOGGER = Logger.getLogger(DefaultCloseableService.class.getName()); private final AtomicBoolean closed = new AtomicBoolean(false); - private final Set closeables = Sets.newIdentityHashSet(); + private final Set closeables = Collections.newSetFromMap(new IdentityHashMap<>()); @Override public boolean add(final Closeable closeable) { diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/process/DefaultRespondingContext.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/process/DefaultRespondingContext.java index 8c5e43e135..fb5780d071 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/process/DefaultRespondingContext.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/process/DefaultRespondingContext.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2011-2014 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -39,13 +39,13 @@ */ package org.glassfish.jersey.server.internal.process; +import java.util.function.Function; + import org.glassfish.jersey.process.internal.ChainableStage; import org.glassfish.jersey.process.internal.Stage; import org.glassfish.jersey.process.internal.Stages; import org.glassfish.jersey.server.ContainerResponse; -import jersey.repackaged.com.google.common.base.Function; - /** * Default implementation of the request-scoped * {@link org.glassfish.jersey.server.internal.process.RespondingContext responding context}. diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/process/ReferencesInitializer.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/process/ReferencesInitializer.java index 658334ccb3..3bcff118cd 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/process/ReferencesInitializer.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/process/ReferencesInitializer.java @@ -40,15 +40,14 @@ package org.glassfish.jersey.server.internal.process; -import javax.inject.Inject; +import java.util.function.Function; + import javax.inject.Provider; import org.glassfish.jersey.internal.inject.InjectionManager; import org.glassfish.jersey.internal.util.collection.Ref; import org.glassfish.jersey.server.spi.RequestScopedInitializer; -import jersey.repackaged.com.google.common.base.Function; - /** * Request/response scoped injection support initialization stage. * @@ -65,10 +64,8 @@ public final class ReferencesInitializer implements Function> processingContextRefProvider) { + public ReferencesInitializer( + InjectionManager injectionManager, Provider> processingContextRefProvider) { this.injectionManager = injectionManager; this.processingContextRefProvider = processingContextRefProvider; } @@ -76,6 +73,7 @@ public final class ReferencesInitializer implements Function mediaTypeList1, List mediaTypeLis this.consumesProducesAcceptors = new HashMap<>(); - final Set httpMethods = Sets.newHashSet(); + final Set httpMethods = new HashSet<>(); for (final MethodRouting methodRouting : methodRoutings) { final String httpMethod = methodRouting.method.getHttpMethod(); httpMethods.add(httpMethod); @@ -383,7 +382,7 @@ private void addAllConsumesProducesCombinations(final List acceptorSet = Sets.newHashSet(); + final Set acceptorSet = new HashSet<>(); for (MediaType consumes : effectiveInputTypes) { for (MediaType produces : effectiveOutputTypes) { @@ -467,7 +466,7 @@ private List getMethodRouter(final RequestProcessingContext context) { } final List satisfyingAcceptors = new LinkedList<>(); - final Set differentInvokableMethods = Sets.newIdentityHashSet(); + final Set differentInvokableMethods = Collections.newSetFromMap(new IdentityHashMap<>()); for (ConsumesProducesAcceptor cpi : acceptors) { if (cpi.isConsumable(request)) { satisfyingAcceptors.add(cpi); @@ -559,7 +558,7 @@ private MediaType determineResponseMediaType( // Media types producible by method. final List methodProducesTypes = !resourceMethod.getProducedTypes().isEmpty() - ? resourceMethod.getProducedTypes() : Lists.newArrayList(MediaType.WILDCARD_TYPE); + ? resourceMethod.getProducedTypes() : Collections.singletonList(MediaType.WILDCARD_TYPE); // Applicable entity providers final List writersForEntityType = workers.getWritersModelsForType(responseEntityClass); @@ -746,7 +745,7 @@ private void reportMethodSelectionAmbiguity(List acceptable StringBuilder msgBuilder = new StringBuilder(LocalizationMessages.AMBIGUOUS_RESOURCE_METHOD(acceptableTypes)).append('\n'); msgBuilder.append('\t').append(selected.methodRouting.method).append('\n'); - final Set reportedMethods = Sets.newHashSet(); + final Set reportedMethods = new HashSet<>(); reportedMethods.add(selected.methodRouting.method); for (RequestSpecificConsumesProducesAcceptor i : sameFitnessAcceptors) { if (!reportedMethods.contains(i.methodRouting.method)) { diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/Routing.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/Routing.java index 703bf556b2..1e2a6eb8dd 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/Routing.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/Routing.java @@ -40,17 +40,21 @@ package org.glassfish.jersey.server.internal.routing; +import java.util.Collection; +import java.util.function.Function; + import javax.ws.rs.core.Configuration; -import org.glassfish.jersey.internal.inject.InjectionManager; import org.glassfish.jersey.message.MessageBodyWorkers; import org.glassfish.jersey.process.internal.ChainableStage; import org.glassfish.jersey.process.internal.Stage; import org.glassfish.jersey.server.internal.JerseyResourceContext; import org.glassfish.jersey.server.internal.ProcessingProviders; import org.glassfish.jersey.server.internal.process.RequestProcessingContext; +import org.glassfish.jersey.server.model.ModelProcessor; import org.glassfish.jersey.server.model.ResourceMethodInvoker; import org.glassfish.jersey.server.model.RuntimeResourceModel; +import org.glassfish.jersey.server.spi.internal.ValueSupplierProvider; /** * Jersey routing entry point. @@ -93,11 +97,14 @@ public static final class Builder { private final RuntimeResourceModel resourceModel; - private InjectionManager injectionManager; private JerseyResourceContext resourceContext; private Configuration config; private MessageBodyWorkers entityProviders; + private Collection valueSuppliers; + private Iterable modelProcessors; + private Function, ?> createServiceFunction; private ProcessingProviders processingProviders; + private ResourceMethodInvoker.Builder resourceMethodInvokerBuilder; private Builder(RuntimeResourceModel resourceModel) { if (resourceModel == null) { @@ -107,17 +114,6 @@ private Builder(RuntimeResourceModel resourceModel) { this.resourceModel = resourceModel; } - /** - * Set runtime DI injection manager. - * - * @param injectionManager DI injection manager. - * @return updated routing builder. - */ - public Builder beanManager(InjectionManager injectionManager) { - this.injectionManager = injectionManager; - return this; - } - /** * Set resource context. * @@ -151,6 +147,17 @@ public Builder entityProviders(MessageBodyWorkers workers) { return this; } + /** + * Set value suppliers. + * + * @param valueSuppliers all registered value suppliers. + * @return updated routing builder. + */ + public Builder valueSupplierProviders(Collection valueSuppliers) { + this.valueSuppliers = valueSuppliers; + return this; + } + /** * Set request/response processing providers. * @@ -162,6 +169,39 @@ public Builder processingProviders(ProcessingProviders processingProviders) { return this; } + /** + * Set model processors. + * + * @param modelProcessors all registered model processors. + * @return updated routing builder. + */ + public Builder modelProcessors(Iterable modelProcessors) { + this.modelProcessors = modelProcessors; + return this; + } + + /** + * Set model processors. + * + * @param createServiceFunction all registered model processors. + * @return updated routing builder. + */ + public Builder createService(Function, ?> createServiceFunction) { + this.createServiceFunction = createServiceFunction; + return this; + } + + /** + * Set builder of ResourceMethodInvoker. + * + * @param resourceMethodInvokerBuilder resource method invoker builder. + * @return updated routing builder. + */ + public Builder resourceMethodInvokerBuilder(ResourceMethodInvoker.Builder resourceMethodInvokerBuilder) { + this.resourceMethodInvokerBuilder = resourceMethodInvokerBuilder; + return this; + } + /** * Build routing stage. * @@ -169,9 +209,6 @@ public Builder processingProviders(ProcessingProviders processingProviders) { */ public ChainableStage buildStage() { // No L10N - internally used class - if (injectionManager == null) { - throw new NullPointerException("DI injection manager is not set."); - } if (resourceContext == null) { throw new NullPointerException("Resource context is not set."); } @@ -181,16 +218,31 @@ public ChainableStage buildStage() { if (entityProviders == null) { throw new NullPointerException("Entity providers are not set."); } + if (valueSuppliers == null) { + throw new NullPointerException("Value supplier providers are not set."); + } + if (modelProcessors == null) { + throw new NullPointerException("Model processors are not set."); + } + if (createServiceFunction == null) { + throw new NullPointerException("Create function is not set."); + } if (processingProviders == null) { throw new NullPointerException("Processing providers are not set."); } + if (resourceMethodInvokerBuilder == null) { + throw new NullPointerException("ResourceMethodInvokerBuilder is not set."); + } - final RuntimeModelBuilder runtimeModelBuilder = new RuntimeModelBuilder(injectionManager, + final RuntimeModelBuilder runtimeModelBuilder = new RuntimeModelBuilder( resourceContext, config, entityProviders, + valueSuppliers, processingProviders, - injectionManager.getInstance(ResourceMethodInvoker.Builder.class)); + resourceMethodInvokerBuilder, + modelProcessors, + createServiceFunction); return new RoutingStage(runtimeModelBuilder.buildModel(resourceModel, false)); } diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/RuntimeLocatorModelBuilder.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/RuntimeLocatorModelBuilder.java index 6ead5e0338..8d677264cb 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/RuntimeLocatorModelBuilder.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/RuntimeLocatorModelBuilder.java @@ -40,8 +40,10 @@ package org.glassfish.jersey.server.internal.routing; +import java.util.Collection; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; @@ -49,10 +51,10 @@ import javax.ws.rs.core.Configuration; import org.glassfish.jersey.internal.Errors; -import org.glassfish.jersey.internal.inject.InjectionManager; -import org.glassfish.jersey.internal.inject.Providers; -import org.glassfish.jersey.model.internal.RankedComparator; -import org.glassfish.jersey.model.internal.RankedProvider; +import org.glassfish.jersey.internal.guava.CacheBuilder; +import org.glassfish.jersey.internal.guava.CacheLoader; +import org.glassfish.jersey.internal.guava.LoadingCache; +import org.glassfish.jersey.message.MessageBodyWorkers; import org.glassfish.jersey.server.ServerProperties; import org.glassfish.jersey.server.internal.JerseyResourceContext; import org.glassfish.jersey.server.internal.LocalizationMessages; @@ -64,10 +66,7 @@ import org.glassfish.jersey.server.model.ResourceModel; import org.glassfish.jersey.server.model.ResourceModelComponent; import org.glassfish.jersey.server.model.internal.ModelErrors; - -import jersey.repackaged.com.google.common.cache.CacheBuilder; -import jersey.repackaged.com.google.common.cache.CacheLoader; -import jersey.repackaged.com.google.common.cache.LoadingCache; +import org.glassfish.jersey.server.spi.internal.ValueSupplierProvider; /** * Base for sub-resource locator runtime model builder. @@ -78,11 +77,13 @@ final class RuntimeLocatorModelBuilder { private static final Logger LOGGER = Logger.getLogger(RuntimeLocatorModelBuilder.class.getName()); - private final InjectionManager injectionManager; private final Configuration config; private final RuntimeModelBuilder runtimeModelBuilder; + private final MessageBodyWorkers messageBodyWorkers; + private final Collection valueSuppliers; private final JerseyResourceContext resourceContext; - + private final Iterable modelProcessors; + private final Function, ?> createServiceFunction; private final LoadingCache cache; // Configuration. @@ -93,20 +94,29 @@ final class RuntimeLocatorModelBuilder { /** * Create a new instance of the runtime model builder for sub-resource locators. * - * @param injectionManager DI injection manager. - * @param config configuration of the application. - * @param resourceContext resource context to bind sub-resource locator singleton instances. - * @param runtimeModelBuilder runtime model builder to build routers for locator models. + * @param config configuration of the application. + * @param messageBodyWorkers message body workers registred in an application. + * @param valueSuppliers all value registered value providers. + * @param resourceContext resource context to bind sub-resource locator singleton instances. + * @param runtimeModelBuilder runtime model builder to build routers for locator models. + * @param modelProcessors all registered model processors. + * @param createServiceFunction function that is able to create and initialize new service. */ - RuntimeLocatorModelBuilder(final InjectionManager injectionManager, - final Configuration config, + RuntimeLocatorModelBuilder(final Configuration config, + final MessageBodyWorkers messageBodyWorkers, + final Collection valueSuppliers, final JerseyResourceContext resourceContext, - final RuntimeModelBuilder runtimeModelBuilder) { + final RuntimeModelBuilder runtimeModelBuilder, + final Iterable modelProcessors, + final Function, ?> createServiceFunction) { - this.injectionManager = injectionManager; this.config = config; + this.messageBodyWorkers = messageBodyWorkers; + this.valueSuppliers = valueSuppliers; this.runtimeModelBuilder = runtimeModelBuilder; this.resourceContext = resourceContext; + this.modelProcessors = modelProcessors; + this.createServiceFunction = createServiceFunction; // Configuration. this.disableValidation = ServerProperties.getValue(config.getProperties(), @@ -162,7 +172,7 @@ public LocatorRouting load(final LocatorCacheKey key) throws Exception { * @return sub-resource locator router. */ Router getRouter(final ResourceMethod resourceMethod) { - return new SubResourceLocatorRouter(injectionManager, resourceMethod, resourceContext, this); + return new SubResourceLocatorRouter(createServiceFunction, valueSuppliers, resourceMethod, resourceContext, this); } /** @@ -242,7 +252,7 @@ private void validateResource(final ResourceModelComponent component) { Errors.process(new Runnable() { @Override public void run() { - final ComponentModelValidator validator = new ComponentModelValidator(injectionManager); + final ComponentModelValidator validator = new ComponentModelValidator(valueSuppliers, messageBodyWorkers); validator.validate(component); if (Errors.fatalIssuesFound() && !ignoreValidationErrors) { @@ -254,11 +264,6 @@ public void run() { } private ResourceModel enhance(ResourceModel subResourceModel) { - final Iterable> allRankedProviders = Providers - .getAllRankedProviders(injectionManager, ModelProcessor.class); - final Iterable modelProcessors = Providers - .sortRankedProviders(new RankedComparator(), allRankedProviders); - for (final ModelProcessor modelProcessor : modelProcessors) { subResourceModel = modelProcessor.processSubResource(subResourceModel, config); validateSubResource(subResourceModel); diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/RuntimeModelBuilder.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/RuntimeModelBuilder.java index 249f3b702d..21f7411feb 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/RuntimeModelBuilder.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/RuntimeModelBuilder.java @@ -41,23 +41,26 @@ package org.glassfish.jersey.server.internal.routing; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.function.Function; import javax.ws.rs.core.Configuration; -import org.glassfish.jersey.internal.inject.InjectionManager; import org.glassfish.jersey.internal.util.collection.Value; import org.glassfish.jersey.internal.util.collection.Values; import org.glassfish.jersey.message.MessageBodyWorkers; import org.glassfish.jersey.server.internal.JerseyResourceContext; import org.glassfish.jersey.server.internal.ProcessingProviders; import org.glassfish.jersey.server.internal.process.Endpoint; +import org.glassfish.jersey.server.model.ModelProcessor; import org.glassfish.jersey.server.model.Resource; import org.glassfish.jersey.server.model.ResourceMethod; import org.glassfish.jersey.server.model.ResourceMethodInvoker; import org.glassfish.jersey.server.model.RuntimeResource; import org.glassfish.jersey.server.model.RuntimeResourceModel; +import org.glassfish.jersey.server.spi.internal.ValueSupplierProvider; import org.glassfish.jersey.uri.PathPattern; import org.glassfish.jersey.uri.UriTemplate; @@ -71,7 +74,7 @@ final class RuntimeModelBuilder { private final ResourceMethodInvoker.Builder resourceMethodInvokerBuilder; - private final MessageBodyWorkers workers; + private final MessageBodyWorkers messageBodyWorkers; private final ProcessingProviders processingProviders; // SubResourceLocator Model Builder. @@ -80,31 +83,30 @@ final class RuntimeModelBuilder { /** * Create a new instance of the runtime model builder. * - * @param injectionManager DI injection manager. * @param resourceContext Jersey resource context. * @param config configuration of the application. - * @param workers message body workers. + * @param messageBodyWorkers message body messageBodyWorkers. * @param processingProviders processing providers. * @param resourceMethodInvokerBuilder method invoker builder. + * @param modelProcessors all registered model processors. + * @param createServiceFunction function that is able to create and initialize new service. */ public RuntimeModelBuilder( - final InjectionManager injectionManager, final JerseyResourceContext resourceContext, final Configuration config, - final MessageBodyWorkers workers, + final MessageBodyWorkers messageBodyWorkers, + final Collection valueSuppliers, final ProcessingProviders processingProviders, - final ResourceMethodInvoker.Builder resourceMethodInvokerBuilder) { + final ResourceMethodInvoker.Builder resourceMethodInvokerBuilder, + final Iterable modelProcessors, + final Function, ?> createServiceFunction) { this.resourceMethodInvokerBuilder = resourceMethodInvokerBuilder; - this.workers = workers; + this.messageBodyWorkers = messageBodyWorkers; this.processingProviders = processingProviders; - - this.locatorBuilder = Values.lazy(new Value() { - @Override - public RuntimeLocatorModelBuilder get() { - return new RuntimeLocatorModelBuilder(injectionManager, config, resourceContext, RuntimeModelBuilder.this); - } - }); + this.locatorBuilder = Values.lazy((Value) + () -> new RuntimeLocatorModelBuilder(config, messageBodyWorkers, valueSuppliers, resourceContext, + RuntimeModelBuilder.this, modelProcessors, createServiceFunction)); } private Router createMethodRouter(final ResourceMethod resourceMethod) { @@ -136,7 +138,7 @@ private Router createRootRouter(final PathMatchingRouterBuilder lastRoutedBuilde if (lastRoutedBuilder != null) { routingRoot = lastRoutedBuilder.build(); } else { - /** + /* * Create an empty routing root that accepts any request, does not do * anything and does not return any inflector. This will cause 404 being * returned for every request. @@ -171,7 +173,7 @@ public Router buildModel(final RuntimeResourceModel resourceModel, final boolean // resource methods if (!resource.getResourceMethods().isEmpty()) { final List methodRoutings = createResourceMethodRouters(resource, subResourceMode); - final Router methodSelectingRouter = new MethodSelectingRouter(workers, methodRoutings); + final Router methodSelectingRouter = new MethodSelectingRouter(messageBodyWorkers, methodRoutings); if (subResourceMode) { currentRouterBuilder = startNextRoute(currentRouterBuilder, PathPattern.END_OF_PATH_PATTERN) .to(resourcePushingRouter) @@ -200,7 +202,7 @@ public Router buildModel(final RuntimeResourceModel resourceModel, final boolean srRoutedBuilder = startNextRoute(srRoutedBuilder, childClosedPattern) .to(uriPushingRouter) .to(childResourcePushingRouter) - .to(new MethodSelectingRouter(workers, childMethodRoutings)); + .to(new MethodSelectingRouter(messageBodyWorkers, childMethodRoutings)); } // sub resource locator @@ -309,7 +311,7 @@ private List createResourceMethodRouters( createMethodRouter(resourceMethod))); } } - return methodRoutings.isEmpty() ? Collections.emptyList() : methodRoutings; + return methodRoutings.isEmpty() ? Collections.emptyList() : methodRoutings; } private PathToRouterBuilder startNextRoute(final PathMatchingRouterBuilder currentRouterBuilder, PathPattern routingPattern) { diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/SubResourceLocatorRouter.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/SubResourceLocatorRouter.java index 8530a386c5..f6737732c3 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/SubResourceLocatorRouter.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/SubResourceLocatorRouter.java @@ -44,15 +44,15 @@ import java.lang.reflect.Method; import java.lang.reflect.UndeclaredThrowableException; import java.security.PrivilegedAction; +import java.util.Collection; import java.util.List; +import java.util.function.Function; import javax.ws.rs.NotFoundException; import javax.ws.rs.ProcessingException; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.SecurityContext; -import org.glassfish.jersey.internal.inject.InjectionManager; -import org.glassfish.jersey.internal.inject.Injections; import org.glassfish.jersey.server.SubjectSecurityContext; import org.glassfish.jersey.server.internal.JerseyResourceContext; import org.glassfish.jersey.server.internal.LocalizationMessages; @@ -63,6 +63,7 @@ import org.glassfish.jersey.server.monitoring.RequestEvent; import org.glassfish.jersey.server.spi.internal.ParamValueFactoryWithSource; import org.glassfish.jersey.server.spi.internal.ParameterValueHelper; +import org.glassfish.jersey.server.spi.internal.ValueSupplierProvider; /** * An methodAcceptorPair to accept sub-resource requests. @@ -81,27 +82,27 @@ final class SubResourceLocatorRouter implements Router { private final List> valueProviders; private final RuntimeLocatorModelBuilder runtimeLocatorBuilder; private final JerseyResourceContext resourceContext; - - private final InjectionManager injectionManager; + private final Function, ?> createFunction; /** * Create a new sub-resource locator router. * - * @param injectionManager DI injection manager. - * @param locatorModel resource locator method model. - * @param resourceContext resource context to bind sub-resource locator singleton instances. - * @param runtimeLocatorBuilder original runtime model builder. + * @param createServiceFunction function to create a new service and make other operations (injection). + * @param valueSuppliers all registered value suppliers. + * @param locatorModel resource locator method model. + * @param resourceContext resource context to bind sub-resource locator singleton instances. + * @param runtimeLocatorBuilder original runtime model builder. */ - SubResourceLocatorRouter(final InjectionManager injectionManager, + SubResourceLocatorRouter(final Function, ?> createServiceFunction, + final Collection valueSuppliers, final ResourceMethod locatorModel, final JerseyResourceContext resourceContext, final RuntimeLocatorModelBuilder runtimeLocatorBuilder) { this.runtimeLocatorBuilder = runtimeLocatorBuilder; this.locatorModel = locatorModel; this.resourceContext = resourceContext; - this.injectionManager = injectionManager; - - this.valueProviders = ParameterValueHelper.createValueProviders(injectionManager, locatorModel.getInvocable()); + this.createFunction = createServiceFunction; + this.valueProviders = ParameterValueHelper.createValueProviders(valueSuppliers, locatorModel.getInvocable()); } @Override @@ -128,7 +129,7 @@ public Continuation apply(final RequestProcessingContext processingContext) { if (!runtimeLocatorBuilder.isCached(locatorClass)) { // If we can't create an instance of the class, don't proceed. - subResourceInstance = Injections.getOrCreate(injectionManager, locatorClass); + subResourceInstance = createFunction.apply(locatorClass); } } routingContext.pushMatchedResource(subResourceInstance); @@ -150,26 +151,21 @@ private Object getResource(final RequestProcessingContext context) { context.triggerEvent(RequestEvent.Type.LOCATOR_MATCHED); - final PrivilegedAction invokeMethodAction = new PrivilegedAction() { - @Override - public Object run() { - try { - - return handlingMethod.invoke(resource, parameterValues); - - } catch (IllegalAccessException | IllegalArgumentException | UndeclaredThrowableException ex) { - throw new ProcessingException(LocalizationMessages.ERROR_RESOURCE_JAVA_METHOD_INVOCATION(), ex); - } catch (final InvocationTargetException ex) { - final Throwable cause = ex.getCause(); - if (cause instanceof WebApplicationException) { - throw (WebApplicationException) cause; - } - - // handle all exceptions as potentially mappable (incl. ProcessingException) - throw new MappableException(cause); - } catch (final Throwable t) { - throw new ProcessingException(t); + final PrivilegedAction invokeMethodAction = () -> { + try { + return handlingMethod.invoke(resource, parameterValues); + } catch (IllegalAccessException | IllegalArgumentException | UndeclaredThrowableException ex) { + throw new ProcessingException(LocalizationMessages.ERROR_RESOURCE_JAVA_METHOD_INVOCATION(), ex); + } catch (final InvocationTargetException ex) { + final Throwable cause = ex.getCause(); + if (cause instanceof WebApplicationException) { + throw (WebApplicationException) cause; } + + // handle all exceptions as potentially mappable (incl. ProcessingException) + throw new MappableException(cause); + } catch (final Throwable t) { + throw new ProcessingException(t); } }; diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/UriRoutingContext.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/UriRoutingContext.java index e3e2a4c112..0295b3e63b 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/UriRoutingContext.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/routing/UriRoutingContext.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2011-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -41,12 +41,15 @@ import java.lang.reflect.Method; import java.net.URI; +import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.function.Function; import java.util.regex.MatchResult; +import java.util.stream.Collectors; import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.MultivaluedMap; @@ -66,9 +69,6 @@ import org.glassfish.jersey.uri.UriTemplate; import org.glassfish.jersey.uri.internal.JerseyUriBuilder; -import jersey.repackaged.com.google.common.base.Function; -import jersey.repackaged.com.google.common.collect.Lists; - /** * Default implementation of the routing context as well as URI information provider. * @@ -76,18 +76,18 @@ */ public class UriRoutingContext implements RoutingContext { - private final LinkedList matchResults = Lists.newLinkedList(); - private final LinkedList matchedResources = Lists.newLinkedList(); - private final LinkedList templates = Lists.newLinkedList(); + private final LinkedList matchResults = new LinkedList<>(); + private final LinkedList matchedResources = new LinkedList<>(); + private final LinkedList templates = new LinkedList<>(); private final MultivaluedHashMap encodedTemplateValues = new MultivaluedHashMap<>(); private final ImmutableMultivaluedMap encodedTemplateValuesView = new ImmutableMultivaluedMap<>(encodedTemplateValues); - private final LinkedList paths = Lists.newLinkedList(); - private final LinkedList matchedRuntimeResources = Lists.newLinkedList(); - private final LinkedList matchedLocators = Lists.newLinkedList(); - private final LinkedList locatorSubResources = Lists.newLinkedList(); + private final LinkedList paths = new LinkedList<>(); + private final LinkedList matchedRuntimeResources = new LinkedList<>(); + private final LinkedList matchedLocators = new LinkedList<>(); + private final LinkedList locatorSubResources = new LinkedList<>(); private final TracingLogger tracingLogger; @@ -268,21 +268,13 @@ public List getMatchedURIs() { return getMatchedURIs(true); } - // TODO Replace with Java SE 8 lambda sometime in the future. - private static final Function PATH_DECODER = new Function() { - - @Override - public String apply(final String input) { - return UriComponent.decode(input, UriComponent.Type.PATH); - } - - }; + private static final Function PATH_DECODER = input -> UriComponent.decode(input, UriComponent.Type.PATH); @Override public List getMatchedURIs(final boolean decode) { final List result; if (decode) { - result = Lists.transform(paths, PATH_DECODER); + result = paths.stream().map(PATH_DECODER).collect(Collectors.toList()); } else { result = paths; } @@ -315,13 +307,8 @@ public MultivaluedMap getPathParameters(final boolean decode) { decodedTemplateValues.put( UriComponent.decode(e.getKey(), UriComponent.Type.PATH_SEGMENT), // we need to keep the ability to add new entries - new LinkedList<>(Lists.transform(e.getValue(), new Function() { - - @Override - public String apply(final String input) { - return UriComponent.decode(input, UriComponent.Type.PATH); - } - }))); + e.getValue().stream().map(s -> UriComponent.decode(s, UriComponent.Type.PATH)) + .collect(Collectors.toCollection(ArrayList::new))); } } decodedTemplateValuesView = new ImmutableMultivaluedMap<>(decodedTemplateValues); diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/ComponentModelValidator.java b/core-server/src/main/java/org/glassfish/jersey/server/model/ComponentModelValidator.java index e508f63598..22c652c242 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/model/ComponentModelValidator.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/model/ComponentModelValidator.java @@ -51,15 +51,16 @@ */ package org.glassfish.jersey.server.model; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; import java.util.List; import org.glassfish.jersey.Severity; import org.glassfish.jersey.internal.Errors; -import org.glassfish.jersey.internal.inject.InjectionManager; import org.glassfish.jersey.message.MessageBodyWorkers; import org.glassfish.jersey.server.model.internal.ModelErrors; - -import jersey.repackaged.com.google.common.collect.Lists; +import org.glassfish.jersey.server.spi.internal.ValueSupplierProvider; /** * A resource model validator that checks the given resource model. @@ -89,13 +90,13 @@ */ public final class ComponentModelValidator { - private final List issueList = Lists.newLinkedList(); + private final List issueList = new LinkedList<>(); - public ComponentModelValidator(InjectionManager injectionManager) { - validators = Lists.newArrayList(); + public ComponentModelValidator(Collection valueSupplierProviders, MessageBodyWorkers msgBodyWorkers) { + validators = new ArrayList<>(); validators.add(new ResourceValidator()); - validators.add(new RuntimeResourceModelValidator(injectionManager.getInstance(MessageBodyWorkers.class))); - validators.add(new ResourceMethodValidator(injectionManager)); + validators.add(new RuntimeResourceModelValidator(msgBodyWorkers)); + validators.add(new ResourceMethodValidator(valueSupplierProviders)); validators.add(new InvocableValidator()); } diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/Invocable.java b/core-server/src/main/java/org/glassfish/jersey/server/model/Invocable.java index d1fe3627f7..368e198660 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/model/Invocable.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/model/Invocable.java @@ -46,17 +46,13 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.function.Supplier; import javax.ws.rs.core.GenericType; import javax.ws.rs.core.Request; -import org.glassfish.jersey.internal.inject.InjectionManager; import org.glassfish.jersey.internal.util.ReflectionHelper; import org.glassfish.jersey.internal.util.collection.ClassTypePair; import org.glassfish.jersey.process.Inflector; -import org.glassfish.jersey.server.spi.internal.ParameterValueHelper; -import org.glassfish.jersey.server.spi.internal.ValueSupplierProvider; /** * A common interface for invocable resource components. This includes resource @@ -309,18 +305,6 @@ public boolean isInflector() { return APPLY_INFLECTOR_METHOD == definitionMethod || APPLY_INFLECTOR_METHOD.equals(definitionMethod); } - /** - * Returns list of {@link ValueSupplierProvider value providers} which provides - * values for parameters of this Invocable returned by {@link #getParameters()}. Value providers are ordered in the same - * order as parameters. - * - * @param injectionManager injection manager. - * @return Set of value providers for this Invocable. - */ - public List> getValueProviders(InjectionManager injectionManager) { - return ParameterValueHelper.createValueProviders(injectionManager, this); - } - @Override public boolean requiresEntity() { for (Parameter p : getParameters()) { diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/Resource.java b/core-server/src/main/java/org/glassfish/jersey/server/model/Resource.java index a818860765..3e54da8e97 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/model/Resource.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/model/Resource.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2010-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -40,27 +40,28 @@ package org.glassfish.jersey.server.model; import java.lang.reflect.Modifier; +import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; +import java.util.IdentityHashMap; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import javax.ws.rs.Path; import org.glassfish.jersey.Severity; import org.glassfish.jersey.internal.Errors; +import org.glassfish.jersey.internal.guava.Preconditions; import org.glassfish.jersey.internal.util.collection.Value; import org.glassfish.jersey.internal.util.collection.Values; import org.glassfish.jersey.server.internal.LocalizationMessages; import org.glassfish.jersey.server.model.internal.ModelHelper; import org.glassfish.jersey.uri.PathPattern; -import jersey.repackaged.com.google.common.base.Function; -import jersey.repackaged.com.google.common.base.Preconditions; -import jersey.repackaged.com.google.common.collect.Lists; -import jersey.repackaged.com.google.common.collect.Sets; - /** * Model of a single resource component. *

@@ -220,12 +221,12 @@ public static final class Builder { private boolean extended; private Builder(final Resource.Builder parentResource) { - this.methodBuilders = Sets.newLinkedHashSet(); - this.childResourceBuilders = Sets.newLinkedHashSet(); - this.childResources = Lists.newLinkedList(); - this.resourceMethods = Lists.newLinkedList(); - this.handlerClasses = Sets.newIdentityHashSet(); - this.handlerInstances = Sets.newIdentityHashSet(); + this.methodBuilders = new LinkedHashSet<>(); + this.childResourceBuilders = new LinkedHashSet<>(); + this.childResources = new LinkedList<>(); + this.resourceMethods = new LinkedList<>(); + this.handlerClasses = Collections.newSetFromMap(new IdentityHashMap<>()); + this.handlerInstances = Collections.newSetFromMap(new IdentityHashMap<>()); this.parentResource = parentResource; name("[unnamed]"); @@ -265,7 +266,8 @@ private boolean isEmpty() { * @see org.glassfish.jersey.server.model.Resource#getName() */ public Builder name(final String name) { - this.names = Lists.newArrayList(name); + this.names = new ArrayList<>(); + this.names.add(name); return this; } @@ -564,7 +566,7 @@ private void onBuildChildResource(Builder childResourceBuilder, Resource.Data ch } private List mergeResources(List resources) { - List mergedResources = Lists.newArrayList(); + List mergedResources = new ArrayList<>(); for (int i = 0; i < resources.size(); i++) { Resource.Data outer = resources.get(i); Resource.Builder builder = null; @@ -600,8 +602,8 @@ private Data buildResourceData() { processChildResourceBuilders(); final List mergedChildResources = mergeResources(childResources); - Set> classes = Sets.newHashSet(handlerClasses); - Set instances = Sets.newHashSet(handlerInstances); + Set> classes = new HashSet<>(handlerClasses); + Set instances = new HashSet<>(handlerInstances); for (Data childResource : mergedChildResources) { classes.addAll(childResource.handlerClasses); instances.addAll(childResource.handlerInstances); @@ -868,16 +870,11 @@ private static Builder builder(Resource.Data resourceData) { } private static List transform(final Resource parent, final List list) { - return Lists.transform(list, new Function() { - @Override - public Resource apply(Data data) { - return new Resource(parent, data); - } - }); + return list.stream().map(data -> new Resource(parent, data)).collect(Collectors.toList()); } private static List immutableCopy(List list) { - return list.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList(Lists.newArrayList(list)); + return list.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList(list); } private static Set immutableCopy(Set set) { @@ -885,7 +882,7 @@ private static Set immutableCopy(Set set) { return Collections.emptySet(); } - final Set result = Sets.newIdentityHashSet(); + final Set result = Collections.newSetFromMap(new IdentityHashMap<>()); result.addAll(set); return set; } @@ -988,7 +985,7 @@ public ResourceMethod getResourceLocator() { * @return List of resource methods and resource locator. */ public List getAllMethods() { - final LinkedList methodsAndLocators = Lists.newLinkedList(getResourceMethods()); + final LinkedList methodsAndLocators = new LinkedList<>(getResourceMethods()); final ResourceMethod loc = getResourceLocator(); if (loc != null) { methodsAndLocators.add(loc); diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethod.java b/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethod.java index 9cae083dc8..6f84d32230 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethod.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethod.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2010-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -42,13 +42,17 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Type; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; import javax.ws.rs.NameBinding; import javax.ws.rs.container.AsyncResponse; @@ -60,11 +64,6 @@ import org.glassfish.jersey.process.Inflector; import org.glassfish.jersey.uri.PathPattern; -import jersey.repackaged.com.google.common.base.Function; -import jersey.repackaged.com.google.common.collect.Collections2; -import jersey.repackaged.com.google.common.collect.Lists; -import jersey.repackaged.com.google.common.collect.Sets; - /** * Model of a method available on a resource. Covers resource method, sub-resource * method and sub-resource locator. @@ -188,9 +187,8 @@ public static final class Builder { this.httpMethod = null; - this.consumedTypes = Sets.newLinkedHashSet(); - this.producedTypes = Sets.newLinkedHashSet(); - + this.consumedTypes = new LinkedHashSet<>(); + this.producedTypes = new LinkedHashSet<>(); this.suspended = false; this.suspendTimeout = AsyncResponse.NO_TIMEOUT; this.suspendTimeoutUnit = TimeUnit.MILLISECONDS; @@ -199,7 +197,7 @@ public static final class Builder { this.encodedParams = false; - this.nameBindings = Sets.newLinkedHashSet(); + this.nameBindings = new LinkedHashSet<>(); } /** @@ -210,12 +208,12 @@ public static final class Builder { */ /* package */ Builder(final Resource.Builder parent, ResourceMethod originalMethod) { this.parent = parent; - this.consumedTypes = Sets.newLinkedHashSet(originalMethod.getConsumedTypes()); - this.producedTypes = Sets.newLinkedHashSet(originalMethod.getProducedTypes()); + this.consumedTypes = new LinkedHashSet<>(originalMethod.getConsumedTypes()); + this.producedTypes = new LinkedHashSet<>(originalMethod.getProducedTypes()); this.suspended = originalMethod.isSuspendDeclared(); this.suspendTimeout = originalMethod.getSuspendTimeout(); this.suspendTimeoutUnit = originalMethod.getSuspendTimeoutUnit(); - this.handlerParameters = Sets.newLinkedHashSet(originalMethod.getInvocable().getHandler().getParameters()); + this.handlerParameters = new LinkedHashSet<>(originalMethod.getInvocable().getHandler().getParameters()); this.nameBindings = originalMethod.getNameBindings(); this.httpMethod = originalMethod.getHttpMethod(); this.managedAsync = originalMethod.isManagedAsyncDeclared(); @@ -343,13 +341,10 @@ public final Builder nameBindings(final Class... nameBindi * @return updated builder object. */ public Builder nameBindings(final Annotation... nameBindings) { - return nameBindings(Collections2.transform(Arrays.asList(nameBindings), - new Function>() { - @Override - public Class apply(Annotation input) { - return input.annotationType(); - } - }) + return nameBindings( + Arrays.stream(nameBindings) + .map((Function>) Annotation::annotationType) + .collect(Collectors.toList()) ); } @@ -594,14 +589,14 @@ private Data(final String httpMethod, this.httpMethod = (httpMethod == null) ? httpMethod : httpMethod.toUpperCase(); - this.consumedTypes = Collections.unmodifiableList(Lists.newArrayList(consumedTypes)); - this.producedTypes = Collections.unmodifiableList(Lists.newArrayList(producedTypes)); + this.consumedTypes = Collections.unmodifiableList(new ArrayList<>(consumedTypes)); + this.producedTypes = Collections.unmodifiableList(new ArrayList<>(producedTypes)); this.invocable = invocable; this.suspended = suspended; this.suspendTimeout = suspendTimeout; this.suspendTimeoutUnit = suspendTimeoutUnit; - this.nameBindings = Collections.unmodifiableCollection(Lists.newArrayList(nameBindings)); + this.nameBindings = Collections.unmodifiableCollection(new ArrayList<>(nameBindings)); this.extended = extended; } @@ -730,12 +725,9 @@ public String toString() { * @return transformed resource method models. */ static List transform(final Resource parent, final List list) { - return Lists.transform(list, new Function() { - @Override - public ResourceMethod apply(Data data) { - return (data == null) ? null : new ResourceMethod(parent, data); - } - }); + return list.stream() + .map(data1 -> (data1 == null) ? null : new ResourceMethod(parent, data1)) + .collect(Collectors.toList()); } private final Data data; diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethodConfig.java b/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethodConfig.java index 7030fb863d..a0ae177acc 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethodConfig.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethodConfig.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -41,6 +41,7 @@ package org.glassfish.jersey.server.model; import java.util.Collections; +import java.util.IdentityHashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; @@ -58,8 +59,6 @@ import org.glassfish.jersey.process.Inflector; import org.glassfish.jersey.server.internal.LocalizationMessages; -import jersey.repackaged.com.google.common.collect.Sets; - /** * Default {@link javax.ws.rs.core.Configuration configuration} for resource methods. * The only allowed contract types for this configuration are: @@ -80,7 +79,7 @@ class ResourceMethodConfig extends CommonConfig { static { //noinspection unchecked - Set> tempSet = Sets.newIdentityHashSet(); + Set> tempSet = Collections.newSetFromMap(new IdentityHashMap<>()); tempSet.add(ContainerRequestFilter.class); tempSet.add(ContainerResponseFilter.class); tempSet.add(ReaderInterceptor.class); diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethodInvoker.java b/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethodInvoker.java index 8a5aa11023..e55bd38f0b 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethodInvoker.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethodInvoker.java @@ -44,12 +44,17 @@ import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import javax.ws.rs.ProcessingException; import javax.ws.rs.container.ContainerRequestFilter; @@ -63,13 +68,10 @@ import javax.ws.rs.ext.ReaderInterceptor; import javax.ws.rs.ext.WriterInterceptor; -import javax.inject.Inject; - import org.glassfish.jersey.internal.inject.AbstractBinder; import org.glassfish.jersey.internal.inject.InjectionManager; import org.glassfish.jersey.internal.inject.Injections; import org.glassfish.jersey.internal.inject.Providers; -import org.glassfish.jersey.internal.util.Producer; import org.glassfish.jersey.model.ContractProvider; import org.glassfish.jersey.model.NameBound; import org.glassfish.jersey.model.internal.ComponentBag; @@ -89,9 +91,6 @@ import org.glassfish.jersey.server.spi.internal.ResourceMethodDispatcher; import org.glassfish.jersey.server.spi.internal.ResourceMethodInvocationHandlerProvider; -import jersey.repackaged.com.google.common.base.Function; -import jersey.repackaged.com.google.common.collect.Lists; - /** * Server-side request-response {@link Inflector inflector} for invoking methods * of annotation-based resource classes. @@ -108,29 +107,80 @@ public class ResourceMethodInvoker implements Endpoint, ResourceInfo { private final ResourceMethodDispatcher dispatcher; private final Method resourceMethod; private final Class resourceClass; - private final List> requestFilters = Lists.newArrayList(); - private final List> responseFilters = Lists.newArrayList(); + private final List> requestFilters = new ArrayList<>(); + private final List> responseFilters = new ArrayList<>(); private final Iterable readerInterceptors; private final Iterable writerInterceptors; /** - * Resource method invoker "assisted" injection helper. + * Resource method invoker helper. * - * The injectable builder API provides means for constructing a properly - * injected {@link ResourceMethodInvoker resource method invoker} instances. + * The builder API provides means for constructing a properly initialized + * {@link ResourceMethodInvoker resource method invoker} instances. */ public static class Builder { - @Inject - private ResourceMethodDispatcherFactory dispatcherProviderFactory; - @Inject - private ResourceMethodInvocationHandlerFactory invocationHandlerProviderFactory; - @Inject + private ResourceMethodDispatcherFactory resourceMethodDispatcherFactory; + private ResourceMethodInvocationHandlerFactory resourceMethodInvocationHandlerFactory; private InjectionManager injectionManager; - @Inject - private Configuration globalConfig; - @Inject - private javax.inject.Provider validatorProvider; + private Configuration configuration; + private Supplier configurationValidator; + + /** + * Set resource method dispatcher factory. + * + * @param resourceMethodDispatcherFactory resource method dispatcher factory. + * @return updated builder. + */ + public Builder resourceMethodDispatcherFactory(ResourceMethodDispatcherFactory resourceMethodDispatcherFactory) { + this.resourceMethodDispatcherFactory = resourceMethodDispatcherFactory; + return this; + } + + /** + * Set resource method invocation handler factory. + * + * @param resourceMethodInvocationHandlerFactory resource method invocation handler factory. + * @return updated builder. + */ + public Builder resourceMethodInvocationHandlerFactory( + ResourceMethodInvocationHandlerFactory resourceMethodInvocationHandlerFactory) { + this.resourceMethodInvocationHandlerFactory = resourceMethodInvocationHandlerFactory; + return this; + } + + /** + * Set runtime DI injection manager. + * + * @param injectionManager DI injection manager. + * @return updated builder. + */ + public Builder injectionManager(InjectionManager injectionManager) { + this.injectionManager = injectionManager; + return this; + } + + /** + * Set global configuration. + * + * @param configuration global configuration. + * @return updated builder. + */ + public Builder configuration(Configuration configuration) { + this.configuration = configuration; + return this; + } + + /** + * Set global configuration validator. + * + * @param configurationValidator configuration validator. + * @return updated builder. + */ + public Builder configurationValidator(Supplier configurationValidator) { + this.configurationValidator = configurationValidator; + return this; + } /** * Build a new resource method invoker instance. @@ -139,17 +189,30 @@ public static class Builder { * @param processingProviders Processing providers. * @return new resource method invoker instance. */ - public ResourceMethodInvoker build( - final ResourceMethod method, - final ProcessingProviders processingProviders - ) { + public ResourceMethodInvoker build(ResourceMethod method, ProcessingProviders processingProviders) { + if (resourceMethodDispatcherFactory == null) { + throw new NullPointerException("ResourceMethodDispatcherFactory is not set."); + } + if (resourceMethodInvocationHandlerFactory == null) { + throw new NullPointerException("ResourceMethodInvocationHandlerFactory is not set."); + } + if (injectionManager == null) { + throw new NullPointerException("DI injection manager is not set."); + } + if (configuration == null) { + throw new NullPointerException("Configuration is not set."); + } + if (configurationValidator == null) { + throw new NullPointerException("Configuration validator is not set."); + } + return new ResourceMethodInvoker( - dispatcherProviderFactory, - invocationHandlerProviderFactory, + resourceMethodDispatcherFactory, + resourceMethodInvocationHandlerFactory, method, processingProviders, injectionManager, - globalConfig, - validatorProvider.get()); + configuration, + configurationValidator.get()); } } @@ -178,13 +241,14 @@ private ResourceMethodInvoker( } final ComponentBag componentBag = config.getComponentBag(); - final List providers = Lists.newArrayList( + final List providers = new ArrayList<>( componentBag.getInstances(ComponentBag.excludeMetaProviders(injectionManager))); // Get instances of providers. final Set> providerClasses = componentBag.getClasses(ComponentBag.excludeMetaProviders(injectionManager)); if (!providerClasses.isEmpty()) { - injectionManager = Injections.createInjectionManager(injectionManager, new AbstractBinder() { + injectionManager = Injections.createInjectionManager(injectionManager); + injectionManager.register(new AbstractBinder() { @Override protected void configure() { bind(config).to(Configuration.class); @@ -196,10 +260,10 @@ protected void configure() { } } - final List> _readerInterceptors = Lists.newLinkedList(); - final List> _writerInterceptors = Lists.newLinkedList(); - final List> _requestFilters = Lists.newLinkedList(); - final List> _responseFilters = Lists.newLinkedList(); + final List> _readerInterceptors = new LinkedList<>(); + final List> _writerInterceptors = new LinkedList<>(); + final List> _requestFilters = new LinkedList<>(); + final List> _responseFilters = new LinkedList<>(); for (final Object provider : providers) { final ContractProvider model = componentBag.getModel(provider.getClass()); @@ -234,8 +298,12 @@ protected void configure() { } } - _readerInterceptors.addAll(Lists.newLinkedList(processingProviders.getGlobalReaderInterceptors())); - _writerInterceptors.addAll(Lists.newLinkedList(processingProviders.getGlobalWriterInterceptors())); + _readerInterceptors.addAll( + StreamSupport.stream(processingProviders.getGlobalReaderInterceptors().spliterator(), false) + .collect(Collectors.toList())); + _writerInterceptors.addAll( + StreamSupport.stream(processingProviders.getGlobalWriterInterceptors().spliterator(), false) + .collect(Collectors.toList())); if (resourceMethod != null) { addNameBoundFiltersAndInterceptors( @@ -244,10 +312,10 @@ protected void configure() { method); } - this.readerInterceptors = Collections.unmodifiableList(Lists.newArrayList(Providers.sortRankedProviders( - new RankedComparator(), _readerInterceptors))); - this.writerInterceptors = Collections.unmodifiableList(Lists.newArrayList(Providers.sortRankedProviders( - new RankedComparator(), _writerInterceptors))); + this.readerInterceptors = Collections.unmodifiableList(StreamSupport.stream(Providers.sortRankedProviders( + new RankedComparator<>(), _readerInterceptors).spliterator(), false).collect(Collectors.toList())); + this.writerInterceptors = Collections.unmodifiableList(StreamSupport.stream(Providers.sortRankedProviders( + new RankedComparator<>(), _writerInterceptors).spliterator(), false).collect(Collectors.toList())); this.requestFilters.addAll(_requestFilters); this.responseFilters.addAll(_responseFilters); @@ -330,16 +398,13 @@ public ContainerResponse apply(final RequestProcessingContext processingContext) } if (method.isManagedAsyncDeclared()) { - processingContext.asyncContext().invokeManaged(new Producer() { - @Override - public Response call() { - final Response response = invoke(processingContext, resource); - if (method.isSuspendDeclared()) { - // we ignore any response returned from a method that injects AsyncResponse - return null; - } - return response; + processingContext.asyncContext().invokeManaged(() -> { + final Response response = invoke(processingContext, resource); + if (method.isSuspendDeclared()) { + // we ignore any response returned from a method that injects AsyncResponse + return null; } + return response; }); return null; // return null on current thread } else { @@ -353,36 +418,33 @@ private Response invoke(final RequestProcessingContext context, final Object res Response jaxrsResponse; context.triggerEvent(RequestEvent.Type.RESOURCE_METHOD_START); - context.push(new Function() { - @Override - public ContainerResponse apply(final ContainerResponse response) { - // Need to check whether the response is null or mapped from exception. In these cases we don't want to modify - // response with resource method metadata. - if (response == null - || response.isMappedFromException()) { - return response; - } - - final Annotation[] entityAnn = response.getEntityAnnotations(); - if (methodAnnotations.length > 0) { - if (entityAnn.length == 0) { - response.setEntityAnnotations(methodAnnotations); - } else { - final Annotation[] mergedAnn = Arrays.copyOf(methodAnnotations, - methodAnnotations.length + entityAnn.length); - System.arraycopy(entityAnn, 0, mergedAnn, methodAnnotations.length, entityAnn.length); - response.setEntityAnnotations(mergedAnn); - } - } + context.push(response -> { + // Need to check whether the response is null or mapped from exception. In these cases we don't want to modify + // response with resource method metadata. + if (response == null + || response.isMappedFromException()) { + return response; + } - if (canUseInvocableResponseType - && response.hasEntity() - && !(response.getEntityType() instanceof ParameterizedType)) { - response.setEntityType(invocableResponseType); + final Annotation[] entityAnn = response.getEntityAnnotations(); + if (methodAnnotations.length > 0) { + if (entityAnn.length == 0) { + response.setEntityAnnotations(methodAnnotations); + } else { + final Annotation[] mergedAnn = Arrays.copyOf(methodAnnotations, + methodAnnotations.length + entityAnn.length); + System.arraycopy(entityAnn, 0, mergedAnn, methodAnnotations.length, entityAnn.length); + response.setEntityAnnotations(mergedAnn); } + } - return response; + if (canUseInvocableResponseType + && response.hasEntity() + && !(response.getEntityType() instanceof ParameterizedType)) { + response.setEntityType(invocableResponseType); } + + return response; }); try { diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethodValidator.java b/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethodValidator.java index e83c91555d..4dbbcb9b3d 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethodValidator.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceMethodValidator.java @@ -44,6 +44,7 @@ import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; @@ -63,8 +64,9 @@ import javax.ws.rs.sse.SseEventSink; import org.glassfish.jersey.internal.Errors; -import org.glassfish.jersey.internal.inject.InjectionManager; import org.glassfish.jersey.server.internal.LocalizationMessages; +import org.glassfish.jersey.server.spi.internal.ParameterValueHelper; +import org.glassfish.jersey.server.spi.internal.ValueSupplierProvider; /** * Validator checking resource methods and sub resource locators. The validator mainly checks the parameters of resource @@ -74,15 +76,10 @@ */ class ResourceMethodValidator extends AbstractResourceModelVisitor { - private final InjectionManager injectionManager; + private final Collection valueSupplierProviders; - /** - * Create new resource method validator. - * - * @param injectionManager injection manager. - */ - public ResourceMethodValidator(InjectionManager injectionManager) { - this.injectionManager = injectionManager; + ResourceMethodValidator(Collection valueSupplierProviders) { + this.valueSupplierProviders = valueSupplierProviders; } @Override @@ -176,7 +173,8 @@ private void checkMethod(ResourceMethod method) { } private void checkValueProviders(ResourceMethod method) { - final List> valueProviders = method.getInvocable().getValueProviders(injectionManager); + List> valueProviders = + ParameterValueHelper.createValueProviders(valueSupplierProviders, method.getInvocable()); if (valueProviders.contains(null)) { int index = valueProviders.indexOf(null); Errors.fatal(method, LocalizationMessages.ERROR_PARAMETER_MISSING_VALUE_PROVIDER(index, method.getInvocable() diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceModel.java b/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceModel.java index 39947eef3e..d592ba55f4 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceModel.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/model/ResourceModel.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -40,6 +40,10 @@ package org.glassfish.jersey.server.model; +import java.util.ArrayList; +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -47,11 +51,6 @@ import org.glassfish.jersey.internal.util.collection.Value; import org.glassfish.jersey.internal.util.collection.Values; -import jersey.repackaged.com.google.common.collect.Lists; -import jersey.repackaged.com.google.common.collect.Maps; -import jersey.repackaged.com.google.common.collect.Sets; - - /** * Resource model of the deployed application which contains set of root resources. As it implements {@link * ResourceModelComponent} it can be validated by {@link ComponentModelValidator component model validator} which will perform @@ -101,7 +100,7 @@ public Builder(List resources, boolean subResourceModel) { * {@code false} if it is a application root resource model. */ public Builder(boolean subResourceModel) { - this.resources = Lists.newArrayList(); + this.resources = new ArrayList<>(); this.subResourceModel = subResourceModel; } @@ -123,8 +122,9 @@ public Builder addResource(Resource resource) { * @return Resource model. */ public ResourceModel build() { - Map resourceMap = Maps.newLinkedHashMap(); - final Set separateResources = Sets.newIdentityHashSet(); // resource with no path that should not be merged + Map resourceMap = new LinkedHashMap<>(); + // resource with no path that should not be merged + final Set separateResources = Collections.newSetFromMap(new IdentityHashMap<>()); for (Resource resource : resources) { final String path = resource.getPath(); @@ -139,8 +139,8 @@ public ResourceModel build() { } } } - List rootResources = Lists.newArrayList(); - List allResources = Lists.newArrayList(); + List rootResources = new ArrayList<>(); + List allResources = new ArrayList<>(); for (Map.Entry entry : resourceMap.entrySet()) { if (entry.getKey() != null) { @@ -203,7 +203,7 @@ public void accept(ResourceModelVisitor visitor) { @Override public List getComponents() { - List components = Lists.newArrayList(); + List components = new ArrayList<>(); components.addAll(resources); components.addAll(getRuntimeResourceModel().getRuntimeResources()); diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/RuntimeResource.java b/core-server/src/main/java/org/glassfish/jersey/server/model/RuntimeResource.java index e07e441ba7..9d84caaa5d 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/model/RuntimeResource.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/model/RuntimeResource.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -40,15 +40,14 @@ package org.glassfish.jersey.server.model; +import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.stream.Collectors; import org.glassfish.jersey.uri.PathPattern; -import jersey.repackaged.com.google.common.base.Function; -import jersey.repackaged.com.google.common.collect.Lists; - /** * Runtime resource is a group of {@link Resource resources} with the same {@link Resource#getPath() path} * regular expression. Runtime resource is constructed from {@link Resource resources} creating @@ -175,12 +174,12 @@ private RuntimeResource(List resources, this.parent = parent; this.pathPattern = resources.get(0).getPathPattern(); - this.resources = Lists.newArrayList(resources); + this.resources = new ArrayList<>(resources); this.regex = regex; - this.resourceMethods = Lists.newArrayList(); - this.resourceLocators = Lists.newArrayList(); - this.childRuntimeResources = Lists.newArrayList(); + this.resourceMethods = new ArrayList<>(); + this.resourceLocators = new ArrayList<>(); + this.childRuntimeResources = new ArrayList<>(); for (Builder childRuntimeResourceBuilder : childRuntimeResourceBuilders) { this.childRuntimeResources.add(childRuntimeResourceBuilder.build(this)); } @@ -289,12 +288,7 @@ public String getFullPathRegex() { * this runtime resource is the parent resource. */ public List getParentResources() { - return Lists.transform(resources, new Function() { - @Override - public Resource apply(Resource child) { - return (child == null) ? null : child.getParent(); - } - }); + return resources.stream().map(child -> (child == null) ? null : child.getParent()).collect(Collectors.toList()); } /** diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/RuntimeResourceModel.java b/core-server/src/main/java/org/glassfish/jersey/server/model/RuntimeResourceModel.java index 849574a597..cb6aa26979 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/model/RuntimeResourceModel.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/model/RuntimeResourceModel.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -40,15 +40,14 @@ package org.glassfish.jersey.server.model; +import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import org.glassfish.jersey.uri.PathTemplate; -import jersey.repackaged.com.google.common.collect.Lists; -import jersey.repackaged.com.google.common.collect.Maps; - /** * Runtime Resource model contains structured information about runtime resources. @@ -65,7 +64,7 @@ public class RuntimeResourceModel { * @param resources List of all resource that should be base for the model. */ public RuntimeResourceModel(List resources) { - this.runtimeResources = Lists.newArrayList(); + this.runtimeResources = new ArrayList<>(); for (RuntimeResource.Builder builder : getRuntimeResources(resources)) { runtimeResources.add(builder.build(null)); } @@ -73,7 +72,7 @@ public RuntimeResourceModel(List resources) { } private List getRuntimeResources(List resources) { - Map> regexMap = Maps.newHashMap(); + Map> regexMap = new HashMap<>(); for (Resource resource : resources) { String path = resource.getPath(); @@ -87,17 +86,17 @@ private List getRuntimeResources(List resourc List listFromMap = regexMap.get(regex); if (listFromMap == null) { - listFromMap = Lists.newArrayList(); + listFromMap = new ArrayList<>(); regexMap.put(regex, listFromMap); } listFromMap.add(resource); } - List runtimeResources = Lists.newArrayList(); + List runtimeResources = new ArrayList<>(); for (Map.Entry> entry : regexMap.entrySet()) { final List resourcesWithSameRegex = entry.getValue(); - List childResources = Lists.newArrayList(); + List childResources = new ArrayList<>(); for (final Resource res : resourcesWithSameRegex) { childResources.addAll(res.getChildResources()); } diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/RuntimeResourceModelValidator.java b/core-server/src/main/java/org/glassfish/jersey/server/model/RuntimeResourceModelValidator.java index 3534570467..ef22f9aa7b 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/model/RuntimeResourceModelValidator.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/model/RuntimeResourceModelValidator.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -40,6 +40,7 @@ package org.glassfish.jersey.server.model; +import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; @@ -52,8 +53,6 @@ import org.glassfish.jersey.message.internal.MediaTypes; import org.glassfish.jersey.server.internal.LocalizationMessages; -import jersey.repackaged.com.google.common.collect.Lists; - /** * Runtime resource model validator validating ambiguity of resource methods. * @@ -78,7 +77,7 @@ public void visitRuntimeResource(RuntimeResource runtimeResource) { } private void checkMethods(RuntimeResource resource) { - final List resourceMethods = Lists.newArrayList(resource.getResourceMethods()); + final List resourceMethods = new ArrayList<>(resource.getResourceMethods()); resourceMethods.addAll(resource.getResourceLocators()); if (resourceMethods.size() >= 2) { for (ResourceMethod m1 : resourceMethods.subList(0, resourceMethods.size() - 1)) { diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/internal/JavaResourceMethodDispatcherProvider.java b/core-server/src/main/java/org/glassfish/jersey/server/model/internal/JavaResourceMethodDispatcherProvider.java index d492bc9669..18828fe612 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/model/internal/JavaResourceMethodDispatcherProvider.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/model/internal/JavaResourceMethodDispatcherProvider.java @@ -42,6 +42,7 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Type; +import java.util.Collection; import java.util.List; import javax.ws.rs.ProcessingException; @@ -49,9 +50,6 @@ import javax.ws.rs.core.Response; import javax.ws.rs.sse.SseEventSink; -import javax.inject.Inject; - -import org.glassfish.jersey.internal.inject.InjectionManager; import org.glassfish.jersey.server.ContainerRequest; import org.glassfish.jersey.server.internal.inject.ConfiguredValidator; import org.glassfish.jersey.server.model.Invocable; @@ -59,6 +57,7 @@ import org.glassfish.jersey.server.spi.internal.ParamValueFactoryWithSource; import org.glassfish.jersey.server.spi.internal.ParameterValueHelper; import org.glassfish.jersey.server.spi.internal.ResourceMethodDispatcher; +import org.glassfish.jersey.server.spi.internal.ValueSupplierProvider; /** * An implementation of {@link ResourceMethodDispatcher.Provider} that @@ -69,15 +68,18 @@ */ class JavaResourceMethodDispatcherProvider implements ResourceMethodDispatcher.Provider { - @Inject - private InjectionManager injectionManager; + private final Collection allValueProviders; + + JavaResourceMethodDispatcherProvider(Collection allValueProviders) { + this.allValueProviders = allValueProviders; + } @Override public ResourceMethodDispatcher create(final Invocable resourceMethod, final InvocationHandler invocationHandler, final ConfiguredValidator validator) { final List> valueProviders = - ParameterValueHelper.createValueProviders(injectionManager, resourceMethod); + ParameterValueHelper.createValueProviders(allValueProviders, resourceMethod); final Class returnType = resourceMethod.getHandlingMethod().getReturnType(); ResourceMethodDispatcher resourceMethodDispatcher = null; @@ -109,21 +111,9 @@ public ResourceMethodDispatcher create(final Invocable resourceMethod, } } - // Inject validator. - injectionManager.inject(resourceMethodDispatcher); - return resourceMethodDispatcher; } - /** - * Get the application-configured injection manager. - * - * @return application-configured injection manager. - */ - final InjectionManager getInjectionManager() { - return injectionManager; - } - private abstract static class AbstractMethodParamInvoker extends AbstractJavaResourceMethodDispatcher { private final List> valueProviders; diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/internal/ModelErrors.java b/core-server/src/main/java/org/glassfish/jersey/server/model/internal/ModelErrors.java index 8191fa9433..f15f06b382 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/model/internal/ModelErrors.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/model/internal/ModelErrors.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -40,13 +40,11 @@ package org.glassfish.jersey.server.model.internal; import java.util.List; +import java.util.stream.Collectors; import org.glassfish.jersey.internal.Errors; import org.glassfish.jersey.server.model.ResourceModelIssue; -import jersey.repackaged.com.google.common.base.Function; -import jersey.repackaged.com.google.common.collect.Lists; - /** * Utility to transform {@link Errors.ErrorMessage error messages} to {@link ResourceModelIssue}s. * @@ -69,12 +67,9 @@ public static List getErrorsAsResourceModelIssues() { * @return non-null list of resource model issues. */ public static List getErrorsAsResourceModelIssues(final boolean afterMark) { - return Lists.transform(Errors.getErrorMessages(afterMark), new Function() { - @Override - public ResourceModelIssue apply(final Errors.ErrorMessage input) { - return new ResourceModelIssue(input.getSource(), input.getMessage(), input.getSeverity()); - } - }); + return Errors.getErrorMessages(afterMark).stream() + .map(input -> new ResourceModelIssue(input.getSource(), input.getMessage(), input.getSeverity())) + .collect(Collectors.toList()); } } diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/internal/ModelProcessorUtil.java b/core-server/src/main/java/org/glassfish/jersey/server/model/internal/ModelProcessorUtil.java index 9a51f96122..fc9afd42bc 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/model/internal/ModelProcessorUtil.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/model/internal/ModelProcessorUtil.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -41,6 +41,7 @@ package org.glassfish.jersey.server.model.internal; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Set; @@ -55,8 +56,6 @@ import org.glassfish.jersey.server.model.ResourceModel; import org.glassfish.jersey.server.model.RuntimeResource; -import jersey.repackaged.com.google.common.collect.Sets; - /** * Helper class with methods supporting processing resource model by {@link org.glassfish.jersey.server.model.ModelProcessor * model processors}. @@ -78,7 +77,7 @@ private ModelProcessorUtil() { */ public static Set getAllowedMethods(RuntimeResource resource) { boolean getFound = false; - Set allowedMethods = Sets.newHashSet(); + Set allowedMethods = new HashSet<>(); for (ResourceMethod resourceMethod : resource.getResourceMethods()) { final String httpMethod = resourceMethod.getHttpMethod(); allowedMethods.add(httpMethod); @@ -304,7 +303,7 @@ public static void enhanceResource(RuntimeResource resource, ResourceModel.Build if (methodsSuitableForResource(firstResource, methods)) { for (Method method : methods) { - final Set produces = Sets.newHashSet(method.produces); + final Set produces = new HashSet<>(method.produces); for (ResourceMethod resourceMethod : resource.getResourceMethods()) { for (final MediaType produce : method.produces) { diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/internal/ResourceMethodDispatcherFactory.java b/core-server/src/main/java/org/glassfish/jersey/server/model/internal/ResourceMethodDispatcherFactory.java index 12d13d2feb..d31c64e165 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/model/internal/ResourceMethodDispatcherFactory.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/model/internal/ResourceMethodDispatcherFactory.java @@ -41,15 +41,12 @@ package org.glassfish.jersey.server.model.internal; import java.lang.reflect.InvocationHandler; -import java.util.Set; +import java.util.Collection; import java.util.logging.Level; import java.util.logging.Logger; -import javax.inject.Inject; import javax.inject.Singleton; -import org.glassfish.jersey.internal.inject.InjectionManager; -import org.glassfish.jersey.internal.inject.Providers; import org.glassfish.jersey.server.internal.LocalizationMessages; import org.glassfish.jersey.server.internal.inject.ConfiguredValidator; import org.glassfish.jersey.server.model.Invocable; @@ -80,11 +77,10 @@ public final class ResourceMethodDispatcherFactory implements ResourceMethodDispatcher.Provider { private static final Logger LOGGER = Logger.getLogger(ResourceMethodDispatcherFactory.class.getName()); - private final Set providers; + private final Collection providers; - @Inject - ResourceMethodDispatcherFactory(InjectionManager injectionManager) { - providers = Providers.getProviders(injectionManager, ResourceMethodDispatcher.Provider.class); + ResourceMethodDispatcherFactory(Collection providers) { + this.providers = providers; } // ResourceMethodDispatchProvider diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/internal/ResourceMethodInvocationHandlerFactory.java b/core-server/src/main/java/org/glassfish/jersey/server/model/internal/ResourceMethodInvocationHandlerFactory.java index 5161c51ea3..963b249e50 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/model/internal/ResourceMethodInvocationHandlerFactory.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/model/internal/ResourceMethodInvocationHandlerFactory.java @@ -45,11 +45,13 @@ import java.util.logging.Level; import java.util.logging.Logger; -import javax.inject.Inject; import javax.inject.Singleton; import org.glassfish.jersey.internal.inject.InjectionManager; import org.glassfish.jersey.internal.inject.Providers; +import org.glassfish.jersey.internal.util.collection.LazyValue; +import org.glassfish.jersey.internal.util.collection.Value; +import org.glassfish.jersey.internal.util.collection.Values; import org.glassfish.jersey.server.internal.LocalizationMessages; import org.glassfish.jersey.server.model.Invocable; import org.glassfish.jersey.server.spi.internal.ResourceMethodInvocationHandlerProvider; @@ -73,17 +75,17 @@ public final class ResourceMethodInvocationHandlerFactory implements ResourceMet private static final InvocationHandler DEFAULT_HANDLER = (target, method, args) -> method.invoke(target, args); private static final Logger LOGGER = Logger.getLogger(ResourceMethodInvocationHandlerFactory.class.getName()); - private final Set providers; + private final LazyValue> providers; - @Inject ResourceMethodInvocationHandlerFactory(InjectionManager injectionManager) { - providers = Providers.getProviders(injectionManager, ResourceMethodInvocationHandlerProvider.class); + this.providers = Values.lazy((Value>) + () -> Providers.getProviders(injectionManager, ResourceMethodInvocationHandlerProvider.class)); } // ResourceMethodInvocationHandlerProvider @Override public InvocationHandler create(Invocable resourceMethod) { - for (ResourceMethodInvocationHandlerProvider provider : providers) { + for (ResourceMethodInvocationHandlerProvider provider : providers.get()) { try { InvocationHandler handler = provider.create(resourceMethod); if (handler != null) { diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/internal/ResourceMethodInvokerConfigurator.java b/core-server/src/main/java/org/glassfish/jersey/server/model/internal/ResourceMethodInvokerConfigurator.java new file mode 100644 index 0000000000..60933934a9 --- /dev/null +++ b/core-server/src/main/java/org/glassfish/jersey/server/model/internal/ResourceMethodInvokerConfigurator.java @@ -0,0 +1,82 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package org.glassfish.jersey.server.model.internal; + +import java.util.Arrays; +import java.util.List; + +import org.glassfish.jersey.internal.BootstrapBag; +import org.glassfish.jersey.internal.BootstrapConfigurator; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.server.ServerBootstrapBag; +import org.glassfish.jersey.server.internal.inject.ConfiguredValidator; +import org.glassfish.jersey.server.model.ResourceMethodInvoker; +import org.glassfish.jersey.server.spi.internal.ResourceMethodDispatcher; + +/** + * Configurator which initializes and register {@link ResourceMethodDispatcher} instance into {@link BootstrapBag}. + * + * @author Petr Bouda (petr.bouda at oracle.com) + */ +public class ResourceMethodInvokerConfigurator implements BootstrapConfigurator { + + @Override + public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) { + } + + @Override + public void postInit(InjectionManager injectionManager, BootstrapBag bootstrapBag) { + ServerBootstrapBag serverBag = (ServerBootstrapBag) bootstrapBag; + + List providers = Arrays.asList( + new VoidVoidDispatcherProvider(serverBag.getResourceContext()), + new JavaResourceMethodDispatcherProvider(serverBag.getValueSupplierProviders())); + + ResourceMethodInvoker.Builder builder = new ResourceMethodInvoker.Builder() + .injectionManager(injectionManager) + .resourceMethodDispatcherFactory(new ResourceMethodDispatcherFactory(providers)) + .resourceMethodInvocationHandlerFactory(new ResourceMethodInvocationHandlerFactory(injectionManager)) + .configuration(bootstrapBag.getConfiguration()) + .configurationValidator(() -> injectionManager.getInstance(ConfiguredValidator.class)); + + serverBag.setResourceMethodInvokerBuilder(builder); + } +} diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/internal/ResourceModelBinder.java b/core-server/src/main/java/org/glassfish/jersey/server/model/internal/ResourceModelBinder.java index b4ecf97ba5..f5d1711de4 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/model/internal/ResourceModelBinder.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/model/internal/ResourceModelBinder.java @@ -42,8 +42,6 @@ import org.glassfish.jersey.internal.inject.AbstractBinder; import org.glassfish.jersey.server.model.ModelProcessor; -import org.glassfish.jersey.server.model.ResourceMethodInvoker; -import org.glassfish.jersey.server.spi.internal.ResourceMethodDispatcher; import org.glassfish.jersey.server.wadl.processor.OptionsMethodProcessor; /** @@ -55,15 +53,7 @@ public class ResourceModelBinder extends AbstractBinder { @Override protected void configure() { - // Resource method invocation bindings - bindAsContract(ResourceMethodInvoker.Builder.class); - bindAsContract(ResourceMethodDispatcherFactory.class); - bindAsContract(ResourceMethodInvocationHandlerFactory.class); - // Dispatcher providers - bind(VoidVoidDispatcherProvider.class).to(ResourceMethodDispatcher.Provider.class); - bind(JavaResourceMethodDispatcherProvider.class).to(ResourceMethodDispatcher.Provider.class); - bind(OptionsMethodProcessor.class).to(ModelProcessor.class); } } diff --git a/core-server/src/main/java/org/glassfish/jersey/server/model/internal/VoidVoidDispatcherProvider.java b/core-server/src/main/java/org/glassfish/jersey/server/model/internal/VoidVoidDispatcherProvider.java index ff14bcaa41..18631cd9d4 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/model/internal/VoidVoidDispatcherProvider.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/model/internal/VoidVoidDispatcherProvider.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2011-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -43,7 +43,6 @@ import javax.ws.rs.ProcessingException; import javax.ws.rs.container.ResourceContext; -import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; import javax.inject.Singleton; @@ -51,7 +50,6 @@ import org.glassfish.jersey.server.ContainerRequest; import org.glassfish.jersey.server.internal.inject.ConfiguredValidator; import org.glassfish.jersey.server.model.Invocable; -import org.glassfish.jersey.server.spi.ValidationInterceptor; import org.glassfish.jersey.server.spi.internal.ResourceMethodDispatcher; /** @@ -65,8 +63,11 @@ @Singleton final class VoidVoidDispatcherProvider implements ResourceMethodDispatcher.Provider { - @Context - private ResourceContext resourceContext; + private final ResourceContext resourceContext; + + VoidVoidDispatcherProvider(ResourceContext resourceContext) { + this.resourceContext = resourceContext; + } private static class VoidToVoidDispatcher extends AbstractJavaResourceMethodDispatcher { diff --git a/core-server/src/main/java/org/glassfish/jersey/server/monitoring/MonitoringStatistics.java b/core-server/src/main/java/org/glassfish/jersey/server/monitoring/MonitoringStatistics.java index fe2ddba6c1..9445d4f7d5 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/monitoring/MonitoringStatistics.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/monitoring/MonitoringStatistics.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -42,7 +42,6 @@ import java.util.Map; - /** * Monitoring statistics return statistic information about application run like number of requests received, * duration of request processing, number of successfully processed requests, statistical information about diff --git a/core-server/src/main/java/org/glassfish/jersey/server/spi/internal/ParameterValueHelper.java b/core-server/src/main/java/org/glassfish/jersey/server/spi/internal/ParameterValueHelper.java index 3192ccc269..de2e9ea316 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/spi/internal/ParameterValueHelper.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/spi/internal/ParameterValueHelper.java @@ -52,8 +52,6 @@ import javax.ws.rs.ProcessingException; import javax.ws.rs.WebApplicationException; -import org.glassfish.jersey.internal.inject.InjectionManager; -import org.glassfish.jersey.internal.inject.Providers; import org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException; import org.glassfish.jersey.server.internal.process.MappableException; import org.glassfish.jersey.server.model.Parameter; @@ -113,20 +111,19 @@ public static Object[] getParameterValues(List> v * Create list of parameter value providers for the given {@link Parameterized * parameterized} resource model component. * - * @param injectionManager injection manager. - * @param parameterized parameterized resource model component. + * @param valueSuppliers all registered value suppliers. + * @param parameterized parameterized resource model component. * @return list of parameter value providers for the parameterized component. */ - public static List> createValueProviders(InjectionManager injectionManager, - Parameterized parameterized) { + public static List> createValueProviders(Collection valueSuppliers, + Parameterized parameterized) { if ((null == parameterized.getParameters()) || (0 == parameterized.getParameters().size())) { return Collections.emptyList(); } - List valueSupplierProviders = - Providers.getProviders(injectionManager, ValueSupplierProvider.class).stream() - .sorted((o1, o2) -> o2.getPriority().getWeight() - o1.getPriority().getWeight()) - .collect(Collectors.toList()); + List valueSupplierProviders = valueSuppliers.stream() + .sorted((o1, o2) -> o2.getPriority().getWeight() - o1.getPriority().getWeight()) + .collect(Collectors.toList()); boolean entityParamFound = false; final List> providers = new ArrayList<>(parameterized.getParameters().size()); diff --git a/core-server/src/main/java/org/glassfish/jersey/server/wadl/internal/WadlBuilder.java b/core-server/src/main/java/org/glassfish/jersey/server/wadl/internal/WadlBuilder.java index 3892fa5afb..4cab464da0 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/wadl/internal/WadlBuilder.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/wadl/internal/WadlBuilder.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2010-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -44,6 +44,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -72,8 +73,6 @@ import com.sun.research.ws.wadl.Resources; import com.sun.research.ws.wadl.Response; -import jersey.repackaged.com.google.common.collect.Lists; - /** * This class implements the algorithm how the wadl is built for one or more {@link org.glassfish.jersey.server.model.Resource} * classes. Wadl artifacts are created by a {@link org.glassfish.jersey.server.wadl.WadlGenerator}. Created on: Jun 18, 2008
@@ -234,7 +233,7 @@ private Request generateRequest(org.glassfish.jersey.server.model.Resource paren final org.glassfish.jersey.server.model.ResourceMethod resourceMethod, Map wadlResourceParams) { try { - final List requestParams = Lists.newLinkedList(resourceMethod.getInvocable().getParameters()); + final List requestParams = new LinkedList<>(resourceMethod.getInvocable().getParameters()); // Adding handler instance parameters to the list of potential request parameters. requestParams.addAll(resourceMethod.getInvocable().getHandler().getParameters()); diff --git a/core-server/src/main/java/org/glassfish/jersey/server/wadl/processor/OptionsMethodProcessor.java b/core-server/src/main/java/org/glassfish/jersey/server/wadl/processor/OptionsMethodProcessor.java index e86f155b6b..2239bd2dc2 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/wadl/processor/OptionsMethodProcessor.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/wadl/processor/OptionsMethodProcessor.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -40,6 +40,7 @@ package org.glassfish.jersey.server.wadl.processor; +import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -60,8 +61,6 @@ import org.glassfish.jersey.server.model.ResourceModel; import org.glassfish.jersey.server.model.internal.ModelProcessorUtil; -import jersey.repackaged.com.google.common.collect.Lists; - /** * {@link ModelProcessor Model processor} enhancing {@link ResourceModel resource model} and {@link Resource sub resources} * by default OPTIONS methods defined by JAX-RS specification. @@ -77,7 +76,7 @@ public class OptionsMethodProcessor implements ModelProcessor { * Creates new instance. */ public OptionsMethodProcessor() { - methodList = Lists.newArrayList(); + methodList = new ArrayList<>(); methodList.add(new ModelProcessorUtil.Method(HttpMethod.OPTIONS, MediaType.WILDCARD_TYPE, MediaType.TEXT_PLAIN_TYPE, PlainTextOptionsInflector.class)); diff --git a/core-server/src/main/java/org/glassfish/jersey/server/wadl/processor/WadlModelProcessor.java b/core-server/src/main/java/org/glassfish/jersey/server/wadl/processor/WadlModelProcessor.java index a335bc7c24..464198a95c 100644 --- a/core-server/src/main/java/org/glassfish/jersey/server/wadl/processor/WadlModelProcessor.java +++ b/core-server/src/main/java/org/glassfish/jersey/server/wadl/processor/WadlModelProcessor.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -41,6 +41,7 @@ import java.io.ByteArrayOutputStream; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -73,8 +74,6 @@ import com.sun.research.ws.wadl.Application; -import jersey.repackaged.com.google.common.collect.Lists; - /** * WADL {@link ModelProcessor model processor} which enhance resource model by WADL related resources (like "/application.wadl"). * The provider should be registered using @@ -94,7 +93,7 @@ public class WadlModelProcessor implements ModelProcessor { * Create new WADL model processor instance. */ public WadlModelProcessor() { - methodList = Lists.newArrayList(); + methodList = new ArrayList<>(); methodList.add(new ModelProcessorUtil.Method(HttpMethod.OPTIONS, MediaType.WILDCARD_TYPE, MediaTypes.WADL_TYPE, OptionsHandler.class)); } diff --git a/core-server/src/test/java/org/glassfish/jersey/server/ApplicationTest.java b/core-server/src/test/java/org/glassfish/jersey/server/ApplicationTest.java index 4e81755d76..7ecfbd4c04 100644 --- a/core-server/src/test/java/org/glassfish/jersey/server/ApplicationTest.java +++ b/core-server/src/test/java/org/glassfish/jersey/server/ApplicationTest.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2014 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -39,6 +39,7 @@ */ package org.glassfish.jersey.server; +import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -47,8 +48,6 @@ import org.junit.Test; -import jersey.repackaged.com.google.common.collect.Sets; - /** * @author Pavel Bucek (pavel.bucek at oracle.com) */ @@ -64,12 +63,15 @@ public void testGetClassesContainsNull() { Application a = new Application() { @Override public Set> getClasses() { - return Sets.>newHashSet(null, DummyResource.class); + return new HashSet>() {{ + add(null); + add(DummyResource.class); + }}; } @Override public Set getSingletons() { - return new HashSet(); + return Collections.emptySet(); } }; @@ -81,12 +83,15 @@ public void testGetSingletonsContainsNull() { Application a = new Application() { @Override public Set> getClasses() { - return new HashSet>(); + return Collections.emptySet(); } @Override public Set getSingletons() { - return Sets.newHashSet(null, new DummyResource()); + return new HashSet() {{ + add(null); + add(new DummyResource()); + }}; } }; @@ -98,7 +103,7 @@ public void testGetSingletonsNull() { Application a = new Application() { @Override public Set> getClasses() { - return new HashSet>(); + return Collections.emptySet(); } @Override @@ -120,7 +125,7 @@ public Set> getClasses() { @Override public Set getSingletons() { - return new HashSet(); + return Collections.emptySet(); } }; diff --git a/core-server/src/test/java/org/glassfish/jersey/server/InjectionManagerFactory.java b/core-server/src/test/java/org/glassfish/jersey/server/TestConfigConfigurator.java similarity index 60% rename from core-server/src/test/java/org/glassfish/jersey/server/InjectionManagerFactory.java rename to core-server/src/test/java/org/glassfish/jersey/server/TestConfigConfigurator.java index 444fb29741..9d73b8ceec 100644 --- a/core-server/src/test/java/org/glassfish/jersey/server/InjectionManagerFactory.java +++ b/core-server/src/test/java/org/glassfish/jersey/server/TestConfigConfigurator.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -40,41 +40,24 @@ package org.glassfish.jersey.server; -import java.util.Map; - +import org.glassfish.jersey.internal.BootstrapBag; +import org.glassfish.jersey.internal.BootstrapConfigurator; import org.glassfish.jersey.internal.inject.InjectionManager; -import org.glassfish.jersey.internal.inject.Injections; /** - * Utility class to create initialized server-side injection manager. + * Configurator which initializes and register configuration. * - * @author Marek Potociar (marek.potociar at oracle.com) + * @author Petr Bouda (petr.bouda at oracle.com) */ -public final class InjectionManagerFactory { - private InjectionManagerFactory() { - // prevents instantiation - } +public class TestConfigConfigurator implements BootstrapConfigurator { - /** - * Create new initialized server injection manager. - * - * @return new initialized server injection manager. - */ - public static InjectionManager createInjectionManager() { - InjectionManager injectionManager = Injections.createInjectionManager(); - injectionManager.register(new ServerBinder(null, injectionManager)); - return injectionManager; + @Override + public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) { + bootstrapBag.setConfiguration(new ResourceConfig()); } - /** - * Create new initialized server injection manager. - * - * @param applicationProperties map of application-specific properties. - * @return new initialized server injection manager. - */ - public static InjectionManager createInjectionManager(Map applicationProperties) { - InjectionManager injectionManager = Injections.createInjectionManager(); - injectionManager.register(new ServerBinder(applicationProperties, injectionManager)); - return injectionManager; + @Override + public void postInit(InjectionManager injectionManager, BootstrapBag bootstrapBag) { } -} + +} \ No newline at end of file diff --git a/core-server/src/test/java/org/glassfish/jersey/server/TestInjectionManagerFactory.java b/core-server/src/test/java/org/glassfish/jersey/server/TestInjectionManagerFactory.java new file mode 100644 index 0000000000..4366e7fc49 --- /dev/null +++ b/core-server/src/test/java/org/glassfish/jersey/server/TestInjectionManagerFactory.java @@ -0,0 +1,134 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * http://glassfish.java.net/public/CDDL+GPL_1_1.html + * or packager/legal/LICENSE.txt. See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at packager/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * Oracle designates this particular file as subject to the "Classpath" + * exception as provided by Oracle in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ + +package org.glassfish.jersey.server; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import javax.ws.rs.RuntimeType; + +import org.glassfish.jersey.internal.BootstrapConfigurator; +import org.glassfish.jersey.internal.ContextResolverFactory; +import org.glassfish.jersey.internal.ExceptionMapperFactory; +import org.glassfish.jersey.internal.ServiceFinderBinder; +import org.glassfish.jersey.internal.inject.AbstractBinder; +import org.glassfish.jersey.internal.inject.CompositeBinder; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.internal.inject.Injections; +import org.glassfish.jersey.internal.spi.AutoDiscoverable; +import org.glassfish.jersey.message.internal.MessageBodyFactory; +import org.glassfish.jersey.message.internal.MessagingBinders; +import org.glassfish.jersey.process.internal.RequestScope; +import org.glassfish.jersey.server.internal.inject.ParamConverterConfigurator; +import org.glassfish.jersey.server.internal.inject.ParamExtractorConfigurator; +import org.glassfish.jersey.server.internal.inject.ValueSupplierProviderConfigurator; +import org.glassfish.jersey.server.model.internal.ResourceMethodInvokerConfigurator; +import org.glassfish.jersey.server.spi.ContainerProvider; + +/** + * Utility class to create initialized server-side injection manager. + * + * @author Marek Potociar (marek.potociar at oracle.com) + */ +public final class TestInjectionManagerFactory { + + private TestInjectionManagerFactory() { + // prevents instantiation + } + + /** + * Create new initialized server injection manager. + * + * @return new initialized server injection manager. + */ + public static BootstrapResult createInjectionManager() { + return createInjectionManager(null); + } + + /** + * Create new initialized server injection manager. + * + * @param applicationProperties map of application-specific properties. + * @return new initialized server injection manager. + */ + public static BootstrapResult createInjectionManager(Map applicationProperties) { + InjectionManager injectionManager = Injections.createInjectionManager(); + injectionManager.register(new ServerBinder()); + + ServerBootstrapBag bootstrapBag = new ServerBootstrapBag(); + List bootstrapConfigurators = Arrays.asList( + new RequestScope.RequestScopeConfigurator(), + new ParamConverterConfigurator(), + new ParamExtractorConfigurator(), + new ValueSupplierProviderConfigurator(), + new JerseyResourceContextConfigurator(), + new ComponentProviderConfigurator(), + new TestConfigConfigurator(), + new ContextResolverFactory.ContextResolversConfigurator(), + new MessageBodyFactory.MessageBodyWorkersConfigurator(), + new ExceptionMapperFactory.ExceptionMappersConfigurator(), + new ResourceMethodInvokerConfigurator(), + new ProcessingProvidersConfigurator()); + + bootstrapConfigurators.forEach(configurator -> configurator.init(injectionManager, bootstrapBag)); + + AbstractBinder dependentBinders = + CompositeBinder.wrap(new MessagingBinders.MessageBodyProviders(applicationProperties, RuntimeType.SERVER), + new ServiceFinderBinder<>(ContainerProvider.class, applicationProperties, RuntimeType.SERVER), + new ServiceFinderBinder<>(AutoDiscoverable.class, applicationProperties, RuntimeType.SERVER)); + injectionManager.register(dependentBinders); + + injectionManager.completeRegistration(); + bootstrapConfigurators.forEach(configurator -> configurator.postInit(injectionManager, bootstrapBag)); + return new BootstrapResult(injectionManager, bootstrapBag); + } + + public static class BootstrapResult { + public InjectionManager injectionManager; + public ServerBootstrapBag bootstrapBag; + + BootstrapResult(InjectionManager injectionManager, ServerBootstrapBag bootstrapBag) { + this.injectionManager = injectionManager; + this.bootstrapBag = bootstrapBag; + } + } +} diff --git a/core-server/src/test/java/org/glassfish/jersey/server/filter/ApplicationFilterTest.java b/core-server/src/test/java/org/glassfish/jersey/server/filter/ApplicationFilterTest.java index 2e59f929c2..dab09c3cef 100644 --- a/core-server/src/test/java/org/glassfish/jersey/server/filter/ApplicationFilterTest.java +++ b/core-server/src/test/java/org/glassfish/jersey/server/filter/ApplicationFilterTest.java @@ -41,6 +41,7 @@ package org.glassfish.jersey.server.filter; import java.io.IOException; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -67,8 +68,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import jersey.repackaged.com.google.common.collect.Lists; - /** * Test for JAX-RS filters. * @@ -110,7 +109,7 @@ public void testSingleRequestFilter() throws Exception { final AtomicInteger called = new AtomicInteger(0); - final List requestFilters = Lists.newArrayList(); + final List requestFilters = new ArrayList<>(); requestFilters.add(new ContainerRequestFilter() { @Override public void filter(final ContainerRequestContext context) throws IOException { @@ -140,7 +139,7 @@ public Response apply(final ContainerRequestContext request) { public void testSingleResponseFilter() throws Exception { final AtomicInteger called = new AtomicInteger(0); - final List responseFilterList = Lists.newArrayList(); + final List responseFilterList = new ArrayList<>(); responseFilterList.add(new ContainerResponseFilter() { @Override public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext) @@ -294,7 +293,7 @@ public void testMultipleFiltersWithBindingPriority() throws Exception { filter10.setFilters(filter1, filter100); filter100.setFilters(filter1, filter10); - final List requestFilterList = Lists.newArrayList(); + final List requestFilterList = new ArrayList<>(); requestFilterList.add(filter100); requestFilterList.add(filter1); requestFilterList.add(filter10); @@ -327,7 +326,7 @@ public void filter(final ContainerRequestContext context) throws IOException { @Test public void testFilterExceptionHandling() throws Exception { - final List requestFilterList = Lists.newArrayList(); + final List requestFilterList = new ArrayList<>(); requestFilterList.add(new ExceptionFilter()); final ResourceConfig resourceConfig = new ResourceConfig() diff --git a/core-server/src/test/java/org/glassfish/jersey/server/filter/UriConnegFilterTest.java b/core-server/src/test/java/org/glassfish/jersey/server/filter/UriConnegFilterTest.java index 5fc089d351..eaf178d88a 100644 --- a/core-server/src/test/java/org/glassfish/jersey/server/filter/UriConnegFilterTest.java +++ b/core-server/src/test/java/org/glassfish/jersey/server/filter/UriConnegFilterTest.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -40,6 +40,7 @@ package org.glassfish.jersey.server.filter; +import java.util.HashMap; import java.util.Map; import javax.ws.rs.GET; @@ -57,8 +58,6 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; -import jersey.repackaged.com.google.common.collect.Maps; - /** * * @author Martin Matula @@ -84,7 +83,7 @@ public String getBar() { @Before public void setUp() { - Map mediaTypes = Maps.newHashMap(); + Map mediaTypes = new HashMap<>(); mediaTypes.put("foo", MediaType.valueOf("application/foo")); mediaTypes.put("bar", MediaType.valueOf("application/bar")); diff --git a/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/BeanParamMemoryLeakTest.java b/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/BeanParamMemoryLeakTest.java index b9ea047d7e..771cf5f657 100644 --- a/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/BeanParamMemoryLeakTest.java +++ b/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/BeanParamMemoryLeakTest.java @@ -47,7 +47,7 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.Request; -import org.glassfish.jersey.hk2.HK2InjectionManager; +import org.glassfish.jersey.hk2.ImmediateHk2InjectionManager; import org.glassfish.jersey.internal.inject.InjectionManager; import org.glassfish.jersey.server.ContainerResponse; import org.glassfish.jersey.server.RequestContextBuilder; @@ -95,11 +95,11 @@ public void testBeanParam() throws Exception { initiateWebApplication(BeanParamInjectionResource.class); final InjectionManager injectionManager = app().getInjectionManager(); - if (!(injectionManager instanceof HK2InjectionManager)) { + if (!(injectionManager instanceof ImmediateHk2InjectionManager)) { throw new RuntimeException("Bean Manager is not an injection manager"); } - HK2InjectionManager hk2BeanManager = (HK2InjectionManager) injectionManager; + ImmediateHk2InjectionManager hk2BeanManager = (ImmediateHk2InjectionManager) injectionManager; ServiceLocator serviceLocator = hk2BeanManager.getServiceLocator(); // we do not expect any descriptor registered yet diff --git a/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/ParamConverterInternalTest.java b/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/ParamConverterInternalTest.java index 96f7bcd22c..cec3d629d0 100644 --- a/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/ParamConverterInternalTest.java +++ b/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/ParamConverterInternalTest.java @@ -50,6 +50,7 @@ import java.util.Date; import java.util.List; import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; @@ -75,9 +76,6 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; -import jersey.repackaged.com.google.common.base.Function; -import jersey.repackaged.com.google.common.collect.Lists; - /** * Tests {@link ParamConverter param converters}. * @@ -245,12 +243,7 @@ public ParamConverter getConverter(final Class rawType, public T fromString(final String value) { final List values = Arrays.asList(value.split(",")); - return rawType.cast(Lists.transform(values, new Function() { - @Override - public Integer apply(final String input) { - return Integer.valueOf(input); - } - })); + return rawType.cast(values.stream().map(Integer::valueOf).collect(Collectors.toList())); } @Override diff --git a/core-server/src/test/java/org/glassfish/jersey/server/internal/monitoring/MonitoringStatisticsTest.java b/core-server/src/test/java/org/glassfish/jersey/server/internal/monitoring/MonitoringStatisticsTest.java index f885e1cf48..29b5f115a0 100644 --- a/core-server/src/test/java/org/glassfish/jersey/server/internal/monitoring/MonitoringStatisticsTest.java +++ b/core-server/src/test/java/org/glassfish/jersey/server/internal/monitoring/MonitoringStatisticsTest.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -40,6 +40,8 @@ package org.glassfish.jersey.server.internal.monitoring; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -63,8 +65,6 @@ import org.junit.Assert; import org.junit.Test; -import jersey.repackaged.com.google.common.collect.Lists; - /** * @author Miroslav Fuksa */ @@ -136,8 +136,8 @@ public void testSimpleUris() { } private MonitoringStatisticsImpl getSimpleStats() { - final List resources = Lists.newArrayList(Resource.from(TestResource.class), - Resource.from(HelloResource.class)); + final List resources = Arrays.asList(Resource.from(TestResource.class), + Resource.from(HelloResource.class)); ResourceModel model = new ResourceModel.Builder(resources, false).build(); MonitoringStatisticsImpl.Builder monBuilder = new MonitoringStatisticsImpl.Builder(model); @@ -147,8 +147,9 @@ private MonitoringStatisticsImpl getSimpleStats() { private MonitoringStatisticsImpl.Builder getProgStats() { final Resource.Builder testBuilder = Resource.builder(TestResource.class); testBuilder.addChildResource("/prog-child").addMethod("GET").handledBy(MyInflector.class); - final List resources = Lists.newArrayList(testBuilder.build(), - Resource.from(HelloResource.class)); + final List resources = new ArrayList<>(); + resources.add(testBuilder.build()); + resources.add(Resource.from(HelloResource.class)); final Resource.Builder prog = Resource.builder("prog"); prog.addMethod("GET").handledBy(MyInflector.class); diff --git a/core-server/src/test/java/org/glassfish/jersey/server/model/ConstrainedToServerTest.java b/core-server/src/test/java/org/glassfish/jersey/server/model/ConstrainedToServerTest.java index 72b8e5f776..622af3d319 100644 --- a/core-server/src/test/java/org/glassfish/jersey/server/model/ConstrainedToServerTest.java +++ b/core-server/src/test/java/org/glassfish/jersey/server/model/ConstrainedToServerTest.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -75,8 +75,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; -import jersey.repackaged.com.google.common.collect.Sets; - /** * Tests whether providers are correctly validated in the server runtime (for example if provider constrained to * client runtime is skipped on the server). @@ -147,7 +145,7 @@ public void testSpecificApplication() throws ExecutionException, InterruptedExce Application app = new Application() { @Override public Set> getClasses() { - final HashSet> classes = Sets.newHashSet(); + final HashSet> classes = new HashSet<>(); classes.add(Resource.class); classes.add(MyClientFilter.class); classes.add(MyServerWrongFilter.class); diff --git a/core-server/src/test/java/org/glassfish/jersey/server/model/ResourceModelTest.java b/core-server/src/test/java/org/glassfish/jersey/server/model/ResourceModelTest.java index ae85924e87..213ef2b57f 100644 --- a/core-server/src/test/java/org/glassfish/jersey/server/model/ResourceModelTest.java +++ b/core-server/src/test/java/org/glassfish/jersey/server/model/ResourceModelTest.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -39,6 +39,7 @@ */ package org.glassfish.jersey.server.model; +import java.util.ArrayList; import java.util.List; import javax.ws.rs.DELETE; @@ -54,8 +55,6 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; -import jersey.repackaged.com.google.common.collect.Lists; - /** * Test {@link ResourceModel} and {@link RuntimeResourceModel}. * @@ -254,7 +253,7 @@ private void testTemplate(List runtimeResources) { } private ResourceModel getResourceModel() { - final List resources = Lists.newArrayList(); + final List resources = new ArrayList<>(); resources.add(Resource.from(SimpleResourceA.class)); resources.add(Resource.from(SimpleResourceB.class)); resources.add(Resource.from(ResourceTemplateA.class)); diff --git a/core-server/src/test/java/org/glassfish/jersey/server/model/ValidatorTest.java b/core-server/src/test/java/org/glassfish/jersey/server/model/ValidatorTest.java index d5fef8b71d..61fa4e22a9 100644 --- a/core-server/src/test/java/org/glassfish/jersey/server/model/ValidatorTest.java +++ b/core-server/src/test/java/org/glassfish/jersey/server/model/ValidatorTest.java @@ -41,7 +41,10 @@ package org.glassfish.jersey.server.model; import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.logging.Logger; @@ -60,32 +63,33 @@ import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; -import javax.ws.rs.RuntimeType; import javax.ws.rs.container.AsyncResponse; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerRequestFilter; import javax.ws.rs.container.Suspended; -import javax.ws.rs.core.Configuration; import javax.ws.rs.core.Context; import javax.ws.rs.sse.SseEventSink; import javax.inject.Singleton; import org.glassfish.jersey.Severity; +import org.glassfish.jersey.internal.BootstrapConfigurator; import org.glassfish.jersey.internal.Errors; -import org.glassfish.jersey.internal.inject.AbstractBinder; import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.internal.inject.Injections; import org.glassfish.jersey.internal.inject.PerLookup; import org.glassfish.jersey.internal.util.Producer; -import org.glassfish.jersey.model.internal.CommonConfig; -import org.glassfish.jersey.model.internal.ComponentBag; +import org.glassfish.jersey.message.internal.MessageBodyFactory; import org.glassfish.jersey.server.ApplicationHandler; import org.glassfish.jersey.server.ContainerRequest; import org.glassfish.jersey.server.ContainerResponse; -import org.glassfish.jersey.server.InjectionManagerFactory; import org.glassfish.jersey.server.RequestContextBuilder; import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.server.ServerBootstrapBag; import org.glassfish.jersey.server.ServerProperties; +import org.glassfish.jersey.server.TestConfigConfigurator; +import org.glassfish.jersey.server.internal.inject.ParamExtractorConfigurator; +import org.glassfish.jersey.server.internal.inject.ValueSupplierProviderConfigurator; import org.glassfish.jersey.server.model.internal.ModelErrors; import org.junit.Ignore; @@ -95,8 +99,6 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; -import jersey.repackaged.com.google.common.collect.Lists; - /** * Taken from Jersey 1: jersey-server:com.sun.jersey.server.impl.modelapi.validation.ResourceModelValidatorTest.java * @@ -130,7 +132,9 @@ public void testRootResourceNonAmbigConstructors() throws Exception { LOGGER.info("No issue should be reported if more public ctors exists with the same number of params, " + "but another just one is presented with more params at a root resource:"); Resource resource = Resource.builder(TestRootResourceNonAmbigCtors.class).build(); - ComponentModelValidator validator = new ComponentModelValidator(createInjectionManager()); + ServerBootstrapBag serverBag = initializeApplication(); + ComponentModelValidator validator = new ComponentModelValidator( + serverBag.getValueSupplierProviders(), serverBag.getMessageBodyWorkers()); validator.validate(resource); assertTrue(validator.getIssueList().isEmpty()); } @@ -346,7 +350,7 @@ private List testResourceValidation(final Class... resour return Errors.process(new Producer>() { @Override public List call() { - List resources = Lists.newArrayList(); + List resources = new ArrayList<>(); for (Class clazz : resourceClasses) { final Resource res = Resource.builder(clazz).build(); if (res.getPath() != null) { @@ -355,8 +359,9 @@ public List call() { } ResourceModel model = new ResourceModel.Builder(resources, false).build(); - InjectionManager injectionManager = createInjectionManager(); - ComponentModelValidator validator = new ComponentModelValidator(injectionManager); + ServerBootstrapBag serverBag = initializeApplication(); + ComponentModelValidator validator = new ComponentModelValidator( + serverBag.getValueSupplierProviders(), serverBag.getMessageBodyWorkers()); validator.validate(model); return ModelErrors.getErrorsAsResourceModelIssues(); } @@ -478,7 +483,9 @@ public void srLocator() { public void testSRLReturningVoid() throws Exception { LOGGER.info("An issue should be reported if a sub-resource locator returns void:"); Resource resource = Resource.builder(TestSRLReturningVoid.class).build(); - ComponentModelValidator validator = new ComponentModelValidator(createInjectionManager()); + ServerBootstrapBag serverBag = initializeApplication(); + ComponentModelValidator validator = new ComponentModelValidator( + serverBag.getValueSupplierProviders(), serverBag.getMessageBodyWorkers()); validator.validate(resource); assertTrue(validator.fatalIssuesFound()); } @@ -554,7 +561,9 @@ public void testMultipleHttpMethodDesignatorsRM() throws Exception { LOGGER.info("An issue should be reported if more than one HTTP method designator exist on a resource " + "method:"); Resource resource = Resource.builder(TestMultipleHttpMethodDesignatorsRM.class).build(); - ComponentModelValidator validator = new ComponentModelValidator(createInjectionManager()); + ServerBootstrapBag serverBag = initializeApplication(); + ComponentModelValidator validator = new ComponentModelValidator( + serverBag.getValueSupplierProviders(), serverBag.getMessageBodyWorkers()); validator.validate(resource); assertTrue(validator.fatalIssuesFound()); } @@ -575,7 +584,9 @@ public void testMultipleHttpMethodDesignatorsSRM() throws Exception { LOGGER.info("An issue should be reported if more than one HTTP method designator exist on a sub-resource " + "method:"); Resource resource = Resource.builder(TestMultipleHttpMethodDesignatorsSRM.class).build(); - ComponentModelValidator validator = new ComponentModelValidator(createInjectionManager()); + ServerBootstrapBag serverBag = initializeApplication(); + ComponentModelValidator validator = new ComponentModelValidator( + serverBag.getValueSupplierProviders(), serverBag.getMessageBodyWorkers()); validator.validate(resource); assertTrue(validator.fatalIssuesFound()); } @@ -593,7 +604,9 @@ public String locator(String s) { public void testEntityParamOnSRL() throws Exception { LOGGER.info("An issue should be reported if an entity parameter exists on a sub-resource locator:"); Resource resource = Resource.builder(TestEntityParamOnSRL.class).build(); - ComponentModelValidator validator = new ComponentModelValidator(createInjectionManager()); + ServerBootstrapBag serverBag = initializeApplication(); + ComponentModelValidator validator = new ComponentModelValidator( + serverBag.getValueSupplierProviders(), serverBag.getMessageBodyWorkers()); validator.validate(resource); assertTrue(validator.fatalIssuesFound()); } @@ -672,7 +685,9 @@ public void testAmbiguousParams() throws Exception { @Override public void run() { Resource resource = Resource.builder(TestAmbiguousParams.class).build(); - ComponentModelValidator validator = new ComponentModelValidator(createInjectionManager()); + ServerBootstrapBag serverBag = initializeApplication(); + ComponentModelValidator validator = new ComponentModelValidator( + serverBag.getValueSupplierProviders(), serverBag.getMessageBodyWorkers()); validator.validate(resource); assertTrue(!validator.fatalIssuesFound()); @@ -696,7 +711,9 @@ public String get() { public void testEmptyPathSegment() throws Exception { LOGGER.info("A warning should be reported if @Path with \"/\" or empty string value is seen"); Resource resource = Resource.builder(TestEmptyPathSegment.class).build(); - ComponentModelValidator validator = new ComponentModelValidator(createInjectionManager()); + ServerBootstrapBag serverBag = initializeApplication(); + ComponentModelValidator validator = new ComponentModelValidator( + serverBag.getValueSupplierProviders(), serverBag.getMessageBodyWorkers()); validator.validate(resource); assertTrue(!validator.fatalIssuesFound()); @@ -739,7 +756,9 @@ public void testTypeVariableResource() throws Exception { @Override public void run() { Resource resource = Resource.builder(TypeVariableResource.class).build(); - ComponentModelValidator validator = new ComponentModelValidator(createInjectionManager()); + ServerBootstrapBag serverBag = initializeApplication(); + ComponentModelValidator validator = new ComponentModelValidator( + serverBag.getValueSupplierProviders(), serverBag.getMessageBodyWorkers()); validator.validate(resource); assertTrue(!validator.fatalIssuesFound()); @@ -784,7 +803,9 @@ public static class ConcreteParameterizedTypeResource extends ParameterizedTypeR public void testParameterizedTypeResource() throws Exception { LOGGER.info(""); Resource resource = Resource.builder(ConcreteParameterizedTypeResource.class).build(); - ComponentModelValidator validator = new ComponentModelValidator(createInjectionManager()); + ServerBootstrapBag serverBag = initializeApplication(); + ComponentModelValidator validator = new ComponentModelValidator( + serverBag.getValueSupplierProviders(), serverBag.getMessageBodyWorkers()); validator.validate(resource); assertTrue(!validator.fatalIssuesFound()); @@ -821,7 +842,9 @@ public static class ConcreteGenericArrayResource extends GenericArrayResource errorMessages = validator.getIssueList(); @@ -1194,14 +1219,17 @@ public void testDisableFailOnErrors() throws ExecutionException, InterruptedExce assertEquals("PASSED", response.getEntity()); } - private InjectionManager createInjectionManager() { - InjectionManager injectionManager = InjectionManagerFactory.createInjectionManager(); - injectionManager.register(new AbstractBinder() { - @Override - protected void configure() { - bind(new CommonConfig(RuntimeType.SERVER, ComponentBag.EXCLUDE_EMPTY)).to(Configuration.class); - } - }); - return injectionManager; + private ServerBootstrapBag initializeApplication() { + List bootstrapConfigurators = Collections.unmodifiableList(Arrays.asList( + new TestConfigConfigurator(), + new ParamExtractorConfigurator(), + new ValueSupplierProviderConfigurator(), + new MessageBodyFactory.MessageBodyWorkersConfigurator())); + + InjectionManager injectionManager = Injections.createInjectionManager(); + ServerBootstrapBag bootstrapBag = new ServerBootstrapBag(); + bootstrapConfigurators.forEach(configurer -> configurer.init(injectionManager, bootstrapBag)); + bootstrapConfigurators.forEach(configurer -> configurer.postInit(injectionManager, bootstrapBag)); + return bootstrapBag; } } diff --git a/core-server/src/test/java/org/glassfish/jersey/server/model/internal/ResourceMethodDispatcherFactoryTest.java b/core-server/src/test/java/org/glassfish/jersey/server/model/internal/ResourceMethodDispatcherFactoryTest.java index 9f9d26ba81..152a52996d 100644 --- a/core-server/src/test/java/org/glassfish/jersey/server/model/internal/ResourceMethodDispatcherFactoryTest.java +++ b/core-server/src/test/java/org/glassfish/jersey/server/model/internal/ResourceMethodDispatcherFactoryTest.java @@ -42,17 +42,19 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.List; import java.util.concurrent.ExecutionException; import javax.ws.rs.core.Request; import javax.ws.rs.core.Response; -import org.glassfish.jersey.internal.inject.InjectionManager; -import org.glassfish.jersey.server.InjectionManagerFactory; +import org.glassfish.jersey.server.TestInjectionManagerFactory; import org.glassfish.jersey.server.model.Invocable; import org.glassfish.jersey.server.model.Resource; import org.glassfish.jersey.server.model.ResourceMethod; import org.glassfish.jersey.server.model.ResourceModelComponent; +import org.glassfish.jersey.server.spi.internal.ResourceMethodDispatcher; import org.junit.Before; import org.junit.Test; @@ -68,10 +70,14 @@ public class ResourceMethodDispatcherFactoryTest { @Before public void setupApplication() { - InjectionManager locator = InjectionManagerFactory.createInjectionManager(); + TestInjectionManagerFactory.BootstrapResult result = TestInjectionManagerFactory.createInjectionManager(); - rmdf = locator.getInstance(ResourceMethodDispatcherFactory.class); - rmihf = locator.getInstance(ResourceMethodInvocationHandlerFactory.class); + List providers = Arrays.asList( + new VoidVoidDispatcherProvider(result.bootstrapBag.getResourceContext()), + new JavaResourceMethodDispatcherProvider(result.bootstrapBag.getValueSupplierProviders())); + + rmdf = new ResourceMethodDispatcherFactory(providers); + rmihf = new ResourceMethodInvocationHandlerFactory(result.injectionManager); } @Test diff --git a/core-server/src/test/java/org/glassfish/jersey/server/modelapi/annotation/IntrospectionModellerTest.java b/core-server/src/test/java/org/glassfish/jersey/server/modelapi/annotation/IntrospectionModellerTest.java index 73c399fef9..d5328c28f4 100644 --- a/core-server/src/test/java/org/glassfish/jersey/server/modelapi/annotation/IntrospectionModellerTest.java +++ b/core-server/src/test/java/org/glassfish/jersey/server/modelapi/annotation/IntrospectionModellerTest.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2011-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -41,6 +41,7 @@ import java.util.Collection; import java.util.List; +import java.util.stream.Collectors; import javax.ws.rs.Consumes; import javax.ws.rs.GET; @@ -64,9 +65,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; -import jersey.repackaged.com.google.common.base.Function; -import jersey.repackaged.com.google.common.collect.Collections2; - /** * @author Jakub Podlesak (jakub.podlesak at oracle.com) */ @@ -223,13 +221,8 @@ private ResourceMethod find(List methods, String javaMethodName) private void assertSources(Collection parameters, Parameter.Source... sources) { assertThat("Expected sources not found in the collection", - Collections2.transform(parameters, new Function() { - @Override - public Parameter.Source apply(final Parameter parameter) { - return parameter.getSource(); - } - }), - Matchers.containsInAnyOrder(sources) + parameters.stream().map(Parameter::getSource).collect(Collectors.toList()), + Matchers.containsInAnyOrder(sources) ); } } diff --git a/core-server/src/test/java/org/glassfish/jersey/server/wadl/config/WadlGeneratorConfigTest.java b/core-server/src/test/java/org/glassfish/jersey/server/wadl/config/WadlGeneratorConfigTest.java index 8294d73210..106f1bfd1f 100644 --- a/core-server/src/test/java/org/glassfish/jersey/server/wadl/config/WadlGeneratorConfigTest.java +++ b/core-server/src/test/java/org/glassfish/jersey/server/wadl/config/WadlGeneratorConfigTest.java @@ -48,8 +48,7 @@ import javax.ws.rs.core.MediaType; -import org.glassfish.jersey.internal.inject.InjectionManager; -import org.glassfish.jersey.server.InjectionManagerFactory; +import org.glassfish.jersey.server.TestInjectionManagerFactory; import org.glassfish.jersey.server.model.Parameter; import org.glassfish.jersey.server.model.ResourceMethod; import org.glassfish.jersey.server.wadl.WadlGenerator; @@ -86,8 +85,8 @@ public void testBuildWadlGeneratorFromGenerators() { .generator(generator2) .build(); - final InjectionManager locator = InjectionManagerFactory.createInjectionManager(); - WadlGenerator wadlGenerator = config.createWadlGenerator(locator); + TestInjectionManagerFactory.BootstrapResult result = TestInjectionManagerFactory.createInjectionManager(); + WadlGenerator wadlGenerator = config.createWadlGenerator(result.injectionManager); Assert.assertEquals(MyWadlGenerator2.class, wadlGenerator.getClass()); Assert.assertEquals(MyWadlGenerator.class, ((MyWadlGenerator2) wadlGenerator).getDelegate().getClass()); @@ -95,12 +94,12 @@ public void testBuildWadlGeneratorFromGenerators() { @Test public void testBuildWadlGeneratorFromDescriptions() { - final InjectionManager locator = InjectionManagerFactory.createInjectionManager(); + TestInjectionManagerFactory.BootstrapResult result = TestInjectionManagerFactory.createInjectionManager(); final String propValue = "bar"; WadlGeneratorConfig config = WadlGeneratorConfig.generator(MyWadlGenerator.class) .prop("foo", propValue) .build(); - WadlGenerator wadlGenerator = config.createWadlGenerator(locator); + WadlGenerator wadlGenerator = config.createWadlGenerator(result.injectionManager); Assert.assertEquals(MyWadlGenerator.class, wadlGenerator.getClass()); Assert.assertEquals(((MyWadlGenerator) wadlGenerator).getFoo(), propValue); @@ -109,7 +108,7 @@ public void testBuildWadlGeneratorFromDescriptions() { .prop("foo", propValue).generator(MyWadlGenerator2.class) .prop("bar", propValue2) .build(); - wadlGenerator = config.createWadlGenerator(locator); + wadlGenerator = config.createWadlGenerator(result.injectionManager); Assert.assertEquals(MyWadlGenerator2.class, wadlGenerator.getClass()); final MyWadlGenerator2 wadlGenerator2 = (MyWadlGenerator2) wadlGenerator; Assert.assertEquals(wadlGenerator2.getBar(), propValue2); @@ -134,10 +133,10 @@ public List configure() { } } - final InjectionManager locator = InjectionManagerFactory.createInjectionManager(); + TestInjectionManagerFactory.BootstrapResult result = TestInjectionManagerFactory.createInjectionManager(); WadlGeneratorConfig config = new MyWadlGeneratorConfig(); - WadlGenerator wadlGenerator = config.createWadlGenerator(locator); + WadlGenerator wadlGenerator = config.createWadlGenerator(result.injectionManager); Assert.assertEquals(MyWadlGenerator2.class, wadlGenerator.getClass()); final MyWadlGenerator2 wadlGenerator2 = (MyWadlGenerator2) wadlGenerator; @@ -293,8 +292,8 @@ public void testBuildWadlGeneratorFromDescriptionsWithTypes() { .generator(MyWadlGenerator3.class) .prop("foo", "string") .prop("bar", new Bar()).build(); - final InjectionManager locator = InjectionManagerFactory.createInjectionManager(); - WadlGenerator wadlGenerator = config.createWadlGenerator(locator); + TestInjectionManagerFactory.BootstrapResult result = TestInjectionManagerFactory.createInjectionManager(); + WadlGenerator wadlGenerator = config.createWadlGenerator(result.injectionManager); Assert.assertEquals(MyWadlGenerator3.class, wadlGenerator.getClass()); diff --git a/core-server/src/test/java/org/glassfish/jersey/server/wadl/config/WadlGeneratorConfigurationLoaderTest.java b/core-server/src/test/java/org/glassfish/jersey/server/wadl/config/WadlGeneratorConfigurationLoaderTest.java index 737ec0f080..178cf8e121 100644 --- a/core-server/src/test/java/org/glassfish/jersey/server/wadl/config/WadlGeneratorConfigurationLoaderTest.java +++ b/core-server/src/test/java/org/glassfish/jersey/server/wadl/config/WadlGeneratorConfigurationLoaderTest.java @@ -45,10 +45,9 @@ import javax.ws.rs.core.MediaType; -import org.glassfish.jersey.internal.inject.InjectionManager; -import org.glassfish.jersey.server.InjectionManagerFactory; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.server.ServerProperties; +import org.glassfish.jersey.server.TestInjectionManagerFactory; import org.glassfish.jersey.server.model.Parameter; import org.glassfish.jersey.server.model.ResourceMethod; import org.glassfish.jersey.server.wadl.WadlGenerator; @@ -82,9 +81,10 @@ public void testLoadConfigClass() throws URISyntaxException { resourceConfig.property(ServerProperties.WADL_GENERATOR_CONFIG, MyWadlGeneratorConfig.class.getName()); - final InjectionManager locator = InjectionManagerFactory.createInjectionManager(resourceConfig.getProperties()); + TestInjectionManagerFactory.BootstrapResult result = + TestInjectionManagerFactory.createInjectionManager(resourceConfig.getProperties()); final WadlGenerator wadlGenerator = WadlGeneratorConfigLoader.loadWadlGeneratorsFromConfig(resourceConfig.getProperties()) - .createWadlGenerator(locator); + .createWadlGenerator(result.injectionManager); Assert.assertEquals(MyWadlGenerator.class, wadlGenerator.getClass()); } @@ -95,9 +95,10 @@ public void testLoadConfigInstance() { final ResourceConfig resourceConfig = new ResourceConfig(); resourceConfig.property(ServerProperties.WADL_GENERATOR_CONFIG, config); - final InjectionManager locator = InjectionManagerFactory.createInjectionManager(resourceConfig.getProperties()); + TestInjectionManagerFactory.BootstrapResult result = + TestInjectionManagerFactory.createInjectionManager(resourceConfig.getProperties()); final WadlGenerator wadlGenerator = WadlGeneratorConfigLoader.loadWadlGeneratorsFromConfig(resourceConfig.getProperties()) - .createWadlGenerator(locator); + .createWadlGenerator(result.injectionManager); Assert.assertTrue(wadlGenerator instanceof MyWadlGenerator); } diff --git a/core-server/src/test/java/org/glassfish/jersey/server/wadl/config/WadlGeneratorLoaderTest.java b/core-server/src/test/java/org/glassfish/jersey/server/wadl/config/WadlGeneratorLoaderTest.java index 1ce5af4c44..be0b5850e1 100644 --- a/core-server/src/test/java/org/glassfish/jersey/server/wadl/config/WadlGeneratorLoaderTest.java +++ b/core-server/src/test/java/org/glassfish/jersey/server/wadl/config/WadlGeneratorLoaderTest.java @@ -55,8 +55,7 @@ import javax.ws.rs.core.MediaType; -import org.glassfish.jersey.internal.inject.InjectionManager; -import org.glassfish.jersey.server.InjectionManagerFactory; +import org.glassfish.jersey.server.TestInjectionManagerFactory; import org.glassfish.jersey.server.model.Parameter; import org.glassfish.jersey.server.model.ResourceMethod; import org.glassfish.jersey.server.wadl.WadlGenerator; @@ -84,12 +83,14 @@ public class WadlGeneratorLoaderTest { @Test public void testLoadFileFromClasspathRelative() throws Exception { - final InjectionManager injectionManager = InjectionManagerFactory.createInjectionManager(); + TestInjectionManagerFactory.BootstrapResult result = + TestInjectionManagerFactory.createInjectionManager(); final Properties props = new Properties(); props.put("testFile", "classpath:testfile.xml"); final WadlGeneratorDescription description = new WadlGeneratorDescription(MyWadlGenerator2.class, props); - final WadlGenerator wadlGenerator = WadlGeneratorLoader.loadWadlGeneratorDescriptions(injectionManager, description); + final WadlGenerator wadlGenerator = + WadlGeneratorLoader.loadWadlGeneratorDescriptions(result.injectionManager, description); Assert.assertEquals(MyWadlGenerator2.class, wadlGenerator.getClass()); final URL resource = getClass().getResource("testfile.xml"); @@ -100,13 +101,14 @@ public void testLoadFileFromClasspathRelative() throws Exception { @Test public void testLoadFileFromClasspathAbsolute() throws Exception { - final InjectionManager injectionManager = InjectionManagerFactory.createInjectionManager(); + TestInjectionManagerFactory.BootstrapResult result = TestInjectionManagerFactory.createInjectionManager(); final Properties props = new Properties(); final String path = "classpath:/" + getClass().getPackage().getName().replaceAll("\\.", "/") + "/testfile.xml"; props.put("testFile", path); final WadlGeneratorDescription description = new WadlGeneratorDescription(MyWadlGenerator2.class, props); - final WadlGenerator wadlGenerator = WadlGeneratorLoader.loadWadlGeneratorDescriptions(injectionManager, description); + final WadlGenerator wadlGenerator = + WadlGeneratorLoader.loadWadlGeneratorDescriptions(result.injectionManager, description); Assert.assertEquals(MyWadlGenerator2.class, wadlGenerator.getClass()); final URL resource = getClass().getResource("testfile.xml"); @@ -117,7 +119,7 @@ public void testLoadFileFromClasspathAbsolute() throws Exception { @Test public void testLoadFileFromAbsolutePath() throws Exception { - final InjectionManager injectionManager = InjectionManagerFactory.createInjectionManager(); + TestInjectionManagerFactory.BootstrapResult result = TestInjectionManagerFactory.createInjectionManager(); final URL resource = getClass().getResource("testfile.xml"); final Properties props = new Properties(); @@ -125,7 +127,8 @@ public void testLoadFileFromAbsolutePath() throws Exception { props.put("testFile", path); final WadlGeneratorDescription description = new WadlGeneratorDescription(MyWadlGenerator2.class, props); - final WadlGenerator wadlGenerator = WadlGeneratorLoader.loadWadlGeneratorDescriptions(injectionManager, description); + final WadlGenerator wadlGenerator = + WadlGeneratorLoader.loadWadlGeneratorDescriptions(result.injectionManager, description); Assert.assertEquals(MyWadlGenerator2.class, wadlGenerator.getClass()); Assert.assertEquals(new File(resource.toURI()).getAbsolutePath(), ((MyWadlGenerator2) wadlGenerator).getTestFile() @@ -134,13 +137,14 @@ public void testLoadFileFromAbsolutePath() throws Exception { @Test public void testLoadStream() throws Exception { - final InjectionManager injectionManager = InjectionManagerFactory.createInjectionManager(); + TestInjectionManagerFactory.BootstrapResult result = TestInjectionManagerFactory.createInjectionManager(); final Properties props = new Properties(); final String path = getClass().getPackage().getName().replaceAll("\\.", "/") + "/testfile.xml"; props.put("testStream", path); final WadlGeneratorDescription description = new WadlGeneratorDescription(MyWadlGenerator2.class, props); - final WadlGenerator wadlGenerator = WadlGeneratorLoader.loadWadlGeneratorDescriptions(injectionManager, description); + final WadlGenerator wadlGenerator = + WadlGeneratorLoader.loadWadlGeneratorDescriptions(result.injectionManager, description); Assert.assertEquals(MyWadlGenerator2.class, wadlGenerator.getClass()); final URL resource = getClass().getResource("testfile.xml"); diff --git a/core-server/src/test/java/org/glassfish/jersey/server/wadl/generators/resourcedoc/WadlGeneratorResourceDocSupportTest.java b/core-server/src/test/java/org/glassfish/jersey/server/wadl/generators/resourcedoc/WadlGeneratorResourceDocSupportTest.java index 1733a2e48a..7c3a6fa581 100644 --- a/core-server/src/test/java/org/glassfish/jersey/server/wadl/generators/resourcedoc/WadlGeneratorResourceDocSupportTest.java +++ b/core-server/src/test/java/org/glassfish/jersey/server/wadl/generators/resourcedoc/WadlGeneratorResourceDocSupportTest.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2010-2014 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -44,6 +44,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.util.Collections; import javax.ws.rs.POST; @@ -68,8 +69,6 @@ import com.sun.research.ws.wadl.Application; -import jersey.repackaged.com.google.common.collect.Lists; - public class WadlGeneratorResourceDocSupportTest { @Test public void wadlIsGeneratedWithUnknownCustomParameterAnnotation() throws JAXBException { @@ -99,7 +98,7 @@ public void wadlIsGeneratedWithUnknownCustomParameterAnnotation() throws JAXBExc WadlBuilder wb = new WadlBuilder(wg, false, null); Resource resource = Resource.from(TestResource.class); - ApplicationDescription app = wb.generate(Lists.newArrayList(resource)); + ApplicationDescription app = wb.generate(Collections.singletonList(resource)); /* Confirm that it can be marshalled without error */ @@ -127,4 +126,4 @@ public String method(@CustomParam("x") Object param) { public @interface CustomParam { String value(); } -} \ No newline at end of file +} diff --git a/etc/config/copyright-exclude b/etc/config/copyright-exclude index b9ce05c20d..b38c11b95d 100644 --- a/etc/config/copyright-exclude +++ b/etc/config/copyright-exclude @@ -59,10 +59,11 @@ build.readme /tests/e2e/server/mvc/MvcEncodingTest/FreemarkerResource.ftl /tests/e2e/server/mvc/MvcEncodingTest/MustacheResource.mustache /test/resources/org/glassfish/jersey/server/config/jaxrs-components -/tests/e2e/src/test/resources/org/glassfish/jersey/tests/e2e/entity/xxe.txt +/tests/e2e-entity/src/test/resources/org/glassfish/jersey/tests/e2e/entity/xxe.txt /core-server/src/main/java/com/sun/research/ws/wadl /core-common/src/main/java/org/glassfish/jersey/internal/jsr166 /tests/performance/etc/data/MEASUREMENT_DATA +/core-common/src/main/java/org/glassfish/jersey/internal/guava/ /core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/AbstractSlidingWindowTimeReservoir.java /core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/TimeReservoir.java /core-server/src/main/java/org/glassfish/jersey/server/internal/monitoring/UniformTimeReservoir.java diff --git a/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/entity/BookmarkEntity.java b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/entity/BookmarkEntity.java index 672d5401f9..fb01a01012 100644 --- a/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/entity/BookmarkEntity.java +++ b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/entity/BookmarkEntity.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -54,8 +54,6 @@ import javax.persistence.Temporal; import javax.persistence.TemporalType; -import jersey.repackaged.com.google.common.base.MoreObjects; - /** * Entity class BookmarkEntity. * @@ -285,7 +283,10 @@ public boolean equals(Object object) { */ @Override public String toString() { - return MoreObjects.toStringHelper(this).add("bookmarkEntityPK", bookmarkEntityPK).toString(); + return "BookmarkEntity{" + + "bookmarkEntityPK=" + bookmarkEntityPK + + '}'; } + } diff --git a/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/entity/BookmarkEntityPK.java b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/entity/BookmarkEntityPK.java index 5860a52f3e..f8057ec791 100644 --- a/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/entity/BookmarkEntityPK.java +++ b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/entity/BookmarkEntityPK.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -44,8 +44,6 @@ import javax.persistence.Column; import javax.persistence.Embeddable; -import jersey.repackaged.com.google.common.base.MoreObjects; - /** * Primary Key class BookmarkEntityPK for entity class BookmarkEntity. * @@ -164,7 +162,9 @@ public boolean equals(Object object) { */ @Override public String toString() { - return MoreObjects.toStringHelper(this).add("bmid", bmid).add("userid", userid).toString(); + return "BookmarkEntityPK{" + + "userid='" + userid + '\'' + + ", bmid='" + bmid + '\'' + + '}'; } - } diff --git a/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/entity/UserEntity.java b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/entity/UserEntity.java index e8b0a633d7..ad569712f2 100644 --- a/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/entity/UserEntity.java +++ b/examples/bookmark-em/src/main/java/org/glassfish/jersey/examples/bookmark_em/entity/UserEntity.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2012-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -51,8 +51,6 @@ import javax.persistence.OneToMany; import javax.persistence.Table; -import jersey.repackaged.com.google.common.base.MoreObjects; - /** * Entity class UserEntity. * @@ -242,7 +240,8 @@ public boolean equals(Object object) { */ @Override public String toString() { - return MoreObjects.toStringHelper(this).add("userid", userid).toString(); + return "UserEntity{" + + "userid='" + userid + '\'' + + '}'; } - } diff --git a/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/entity/BookmarkEntity.java b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/entity/BookmarkEntity.java index 78421cbe02..a5e31aecb2 100644 --- a/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/entity/BookmarkEntity.java +++ b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/entity/BookmarkEntity.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2010-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -53,8 +53,6 @@ import javax.persistence.Temporal; import javax.persistence.TemporalType; -import jersey.repackaged.com.google.common.base.MoreObjects; - /** * Entity class BookmarkEntity. * @@ -283,7 +281,9 @@ public boolean equals(Object object) { */ @Override public String toString() { - return MoreObjects.toStringHelper(this).add("bookmarkEntityPK", bookmarkEntityPK).toString(); + return "BookmarkEntity{" + + "bookmarkEntityPK=" + bookmarkEntityPK + + '}'; } } diff --git a/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/entity/BookmarkEntityPK.java b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/entity/BookmarkEntityPK.java index 5851294c05..5e3ab3ca61 100644 --- a/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/entity/BookmarkEntityPK.java +++ b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/entity/BookmarkEntityPK.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2010-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -44,8 +44,6 @@ import javax.persistence.Column; import javax.persistence.Embeddable; -import jersey.repackaged.com.google.common.base.MoreObjects; - /** * Primary Key class BookmarkEntityPK for entity class BookmarkEntity. * @@ -164,7 +162,9 @@ public boolean equals(Object object) { */ @Override public String toString() { - return MoreObjects.toStringHelper(this).add("bmid", bmid).add("userid", userid).toString(); + return "BookmarkEntityPK{" + + "userid='" + userid + '\'' + + ", bmid='" + bmid + '\'' + + '}'; } - } diff --git a/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/entity/UserEntity.java b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/entity/UserEntity.java index 869df915bd..39fa02f303 100644 --- a/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/entity/UserEntity.java +++ b/examples/bookmark/src/main/java/org/glassfish/jersey/examples/bookmark/entity/UserEntity.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2010-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -52,8 +52,6 @@ import javax.persistence.OneToMany; import javax.persistence.Table; -import jersey.repackaged.com.google.common.base.MoreObjects; - /** * Entity class UserEntity. * @@ -245,7 +243,8 @@ public boolean equals(Object object) { */ @Override public String toString() { - return MoreObjects.toStringHelper(this).add("userid", userid).toString(); + return "UserEntity{" + + "userid='" + userid + '\'' + + '}'; } - } diff --git a/examples/bookstore-webapp/src/main/java/org/glassfish/jersey/examples/bookstore/webapp/resource/Item.java b/examples/bookstore-webapp/src/main/java/org/glassfish/jersey/examples/bookstore/webapp/resource/Item.java index 577fc6d797..b40c7a5525 100644 --- a/examples/bookstore-webapp/src/main/java/org/glassfish/jersey/examples/bookstore/webapp/resource/Item.java +++ b/examples/bookstore-webapp/src/main/java/org/glassfish/jersey/examples/bookstore/webapp/resource/Item.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -50,8 +50,6 @@ import org.glassfish.jersey.server.mvc.Template; -import jersey.repackaged.com.google.common.base.MoreObjects; - @Template @Produces(MediaType.TEXT_HTML) @XmlRootElement @@ -85,9 +83,9 @@ public Item getXml() { @Override public String toString() { - return MoreObjects.toStringHelper(Item.class) - .add("title", title) - .add("author", author) - .toString(); + return "Item{" + + "title='" + title + '\'' + + ", author='" + author + '\'' + + '}'; } } diff --git a/examples/extended-wadl-webapp/src/test/java/org/glassfish/jersey/examples/extendedwadl/ExtendedWadlWebappOsgiTest.java b/examples/extended-wadl-webapp/src/test/java/org/glassfish/jersey/examples/extendedwadl/ExtendedWadlWebappOsgiTest.java index 540bea9e5f..4a7df0c7d9 100644 --- a/examples/extended-wadl-webapp/src/test/java/org/glassfish/jersey/examples/extendedwadl/ExtendedWadlWebappOsgiTest.java +++ b/examples/extended-wadl-webapp/src/test/java/org/glassfish/jersey/examples/extendedwadl/ExtendedWadlWebappOsgiTest.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2010-2016 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -149,8 +149,6 @@ public static Option[] configuration() { mavenBundle().groupId("org.glassfish.jersey.media").artifactId("jersey-media-jaxb").versionAsInProject(), mavenBundle().groupId("org.glassfish.jersey.core").artifactId("jersey-server").versionAsInProject(), mavenBundle().groupId("org.glassfish.jersey.core").artifactId("jersey-client").versionAsInProject(), - // Guava - mavenBundle().groupId("org.glassfish.jersey.bundles.repackaged").artifactId("jersey-guava").versionAsInProject(), // jettison mavenBundle().groupId("org.codehaus.jettison").artifactId("jettison").versionAsInProject(), diff --git a/examples/feed-combiner-java8-webapp/src/test/java/org/glassfish/jersey/examples/feedcombiner/store/ReadWriteLockDataStoreTest.java b/examples/feed-combiner-java8-webapp/src/test/java/org/glassfish/jersey/examples/feedcombiner/store/ReadWriteLockDataStoreTest.java index 61b9d10852..41892c474c 100644 --- a/examples/feed-combiner-java8-webapp/src/test/java/org/glassfish/jersey/examples/feedcombiner/store/ReadWriteLockDataStoreTest.java +++ b/examples/feed-combiner-java8-webapp/src/test/java/org/glassfish/jersey/examples/feedcombiner/store/ReadWriteLockDataStoreTest.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -46,6 +46,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.Set; import org.glassfish.jersey.examples.feedcombiner.model.CombinedFeed; @@ -66,8 +67,6 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; -import jersey.repackaged.com.google.common.base.Objects; - /** * @author Petr Bouda (petr.bouda at oracle.com) */ @@ -338,8 +337,8 @@ public void testLoadDatastore() { Collection newlySavedEntities = captureSaveAll.getValue(); assertEquals(1, newlySavedEntities.size()); boolean exists = newlySavedEntities.stream() - .map(CombinedFeed.class::cast) - .anyMatch(entity -> Objects.equal(entity.getId(), id2)); + .map(CombinedFeed.class::cast) + .anyMatch(entity -> Objects.equals(entity.getId(), id2)); if (!exists) { fail("The new stored CombinedFeed was not found."); } diff --git a/examples/json-processing-webapp/src/main/java/org/glassfish/jersey/examples/jsonp/service/DocumentStorage.java b/examples/json-processing-webapp/src/main/java/org/glassfish/jersey/examples/jsonp/service/DocumentStorage.java index 1d26aabcd7..6adedb4402 100644 --- a/examples/json-processing-webapp/src/main/java/org/glassfish/jersey/examples/jsonp/service/DocumentStorage.java +++ b/examples/json-processing-webapp/src/main/java/org/glassfish/jersey/examples/jsonp/service/DocumentStorage.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -41,13 +41,12 @@ package org.glassfish.jersey.examples.jsonp.service; import java.util.Collection; +import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import javax.json.JsonObject; -import jersey.repackaged.com.google.common.collect.Maps; - /** * Storage of documents. * @@ -55,7 +54,7 @@ */ public final class DocumentStorage { - private static final Map storage = Maps.newLinkedHashMap(); + private static final Map storage = new LinkedHashMap<>(); private static final AtomicInteger counter = new AtomicInteger(0); public static int store(final JsonObject document) { diff --git a/examples/json-processing-webapp/src/test/java/org/glassfish/jersey/examples/jsonp/JsonProcessingResourceTest.java b/examples/json-processing-webapp/src/test/java/org/glassfish/jersey/examples/jsonp/JsonProcessingResourceTest.java index f70c47c0e3..01440feba4 100644 --- a/examples/json-processing-webapp/src/test/java/org/glassfish/jersey/examples/jsonp/JsonProcessingResourceTest.java +++ b/examples/json-processing-webapp/src/test/java/org/glassfish/jersey/examples/jsonp/JsonProcessingResourceTest.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -40,8 +40,11 @@ package org.glassfish.jersey.examples.jsonp; import java.net.URI; -import java.util.HashSet; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; import javax.ws.rs.client.Entity; import javax.ws.rs.client.WebTarget; @@ -62,15 +65,12 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import jersey.repackaged.com.google.common.collect.Lists; -import jersey.repackaged.com.google.common.collect.Sets; - /** * @author Michal Gajdos */ public class JsonProcessingResourceTest extends JerseyTest { - private static final List documents = Lists.newArrayList(); + private static final List documents = new ArrayList<>(); static { documents.add(Json.createObjectBuilder() @@ -175,7 +175,7 @@ public void testFilterDocuments() throws Exception { private void checkFilteredDocuments(final JsonArray filtered, final int size, final String... properties) { assertEquals(size, filtered.size()); - final HashSet strings = Sets.newHashSet(properties); + Set strings = Arrays.stream(properties).collect(Collectors.toSet()); for (final JsonObject document : filtered.getValuesAs(JsonObject.class)) { for (final String property : document.keySet()) { assertTrue(strings.contains(property)); diff --git a/examples/osgi-helloworld-webapp/functional-test/src/test/java/org/glassfish/jersey/examples/helloworld/test/AbstractWebAppTest.java b/examples/osgi-helloworld-webapp/functional-test/src/test/java/org/glassfish/jersey/examples/helloworld/test/AbstractWebAppTest.java index 029d086951..d3faa3e8fc 100644 --- a/examples/osgi-helloworld-webapp/functional-test/src/test/java/org/glassfish/jersey/examples/helloworld/test/AbstractWebAppTest.java +++ b/examples/osgi-helloworld-webapp/functional-test/src/test/java/org/glassfish/jersey/examples/helloworld/test/AbstractWebAppTest.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2010-2016 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -181,8 +181,6 @@ public List

See the Guava User Guide article on + * {@code Multimap}. + * + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ +public interface SetMultimap extends Multimap { + /** + * {@inheritDoc} + *