Repository: wso2/msf4j Branch: master Commit: 76897238aaef Files: 937 Total size: 3.4 MB Directory structure: gitextract_cwpary60/ ├── .gitignore ├── LICENSE ├── README.md ├── analytics/ │ ├── README.md │ ├── das-setup/ │ │ ├── capps/ │ │ │ ├── msf4j_http_monitoring_capp.car │ │ │ └── org_wso2_carbon_metrics-1.0.0.car │ │ └── setup.sh │ ├── msf4j-analytics/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── wso2/ │ │ │ │ └── msf4j/ │ │ │ │ └── analytics/ │ │ │ │ ├── AnalyticUtils.java │ │ │ │ ├── httpmonitoring/ │ │ │ │ │ ├── HTTPMonitored.java │ │ │ │ │ ├── HTTPMonitoringDataPublisher.java │ │ │ │ │ ├── HTTPMonitoringEvent.java │ │ │ │ │ ├── HTTPMonitoringInterceptor.java │ │ │ │ │ └── config/ │ │ │ │ │ ├── HTTPMonitoringConfigBuilder.java │ │ │ │ │ └── model/ │ │ │ │ │ ├── DasConfig.java │ │ │ │ │ └── HTTPMonitoringConfig.java │ │ │ │ ├── internal/ │ │ │ │ │ ├── AnalyticsSC.java │ │ │ │ │ ├── DataHolder.java │ │ │ │ │ └── InterceptorCapabilityProvider.java │ │ │ │ ├── metrics/ │ │ │ │ │ ├── Metrics.java │ │ │ │ │ ├── MetricsComponent.java │ │ │ │ │ └── MetricsInterceptor.java │ │ │ │ └── tracing/ │ │ │ │ └── MSF4JTracingInterceptor.java │ │ │ └── resources/ │ │ │ └── http-monitoring.yml │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── wso2/ │ │ │ └── msf4j/ │ │ │ └── analytics/ │ │ │ └── HTTPMonitoringConfigTest.java │ │ └── resources/ │ │ └── deployment.yaml │ ├── msf4j-analytics-common/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── analytics/ │ │ └── common/ │ │ └── tracing/ │ │ ├── MSF4JClientTracingFilter.java │ │ ├── TraceEvent.java │ │ ├── TracingConstants.java │ │ ├── TracingEventTracker.java │ │ └── TracingUtil.java │ ├── msf4j_http_monitoring_capp_source/ │ │ ├── build.xml │ │ └── msf4j_http_monitoring_capp/ │ │ ├── artifacts.xml │ │ ├── http_event_receiver_1.0.0/ │ │ │ ├── artifact.xml │ │ │ └── http_event_receiver.xml │ │ ├── http_event_store_1.0.0/ │ │ │ ├── artifact.xml │ │ │ └── org_wso2_msf4j_analytics_httpmonitoring.xml │ │ ├── http_event_stream_1.0.0/ │ │ │ ├── artifact.xml │ │ │ └── org.wso2.msf4j.analytics.httpmonitoring_1.0.0.json │ │ └── spark_script_1.0.0/ │ │ ├── artifact.xml │ │ └── http_event_script.xml │ ├── wso2das-tracing-capp/ │ │ ├── capp-content/ │ │ │ ├── Dashboard_1.0.0/ │ │ │ │ ├── artifact.xml │ │ │ │ └── msf4j-message-tracing.json │ │ │ ├── Eventreceiver_1.0.0/ │ │ │ │ ├── artifact.xml │ │ │ │ └── msf4jtracereceiver.xml │ │ │ ├── Eventstore_1.0.0/ │ │ │ │ ├── artifact.xml │ │ │ │ └── msf4j-tracing.xml │ │ │ ├── Eventstream_1.0.0/ │ │ │ │ ├── artifact.xml │ │ │ │ └── msf4j-tracing_1.0.0.json │ │ │ ├── GadgetMSF4JTracing_1.0.0/ │ │ │ │ ├── artifact.xml │ │ │ │ └── msf4j-tracing/ │ │ │ │ ├── css/ │ │ │ │ │ └── main.css │ │ │ │ ├── gadget.json │ │ │ │ ├── index.xml │ │ │ │ └── js/ │ │ │ │ ├── libs/ │ │ │ │ │ └── jquery.base64.js │ │ │ │ └── main.js │ │ │ └── artifacts.xml │ │ ├── pom.xml │ │ └── zip.xml │ └── zipkin-tracing/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── analytics/ │ │ └── zipkintracing/ │ │ ├── MSF4JZipkinTracingInterceptor.java │ │ ├── TraceableHttpClientRequest.java │ │ ├── TraceableHttpClientResponse.java │ │ ├── TraceableHttpServerRequest.java │ │ └── TraceableHttpServerResponse.java │ └── test/ │ ├── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── analytics/ │ │ └── zipkintracing/ │ │ ├── TraceableHttpClientRequestTest.java │ │ ├── TraceableHttpClientResponseTest.java │ │ ├── TraceableHttpServerRequestTest.java │ │ └── TraceableHttpServerResponseTest.java │ └── resources/ │ └── testng.xml ├── archetypes/ │ ├── README.md │ └── msf4j-microservice/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── resources/ │ ├── META-INF/ │ │ └── maven/ │ │ └── archetype-metadata.xml │ └── archetype-resources/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ ├── Application.java │ └── __serviceClass__.java ├── client/ │ ├── README.md │ ├── pom.xml │ ├── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── client/ │ │ ├── ApacheHttpClient.java │ │ ├── FeginZipkinTracingClient.java │ │ ├── FeignClientWrapper.java │ │ ├── FeignTracingClient.java │ │ ├── MSF4JClient.java │ │ ├── ModelUtils.java │ │ ├── codec/ │ │ │ ├── DefaultErrorDecoder.java │ │ │ ├── DefaultRestErrorResponse.java │ │ │ ├── MSF4JDecoder.java │ │ │ ├── MSF4JJacksonDecoder.java │ │ │ └── RestErrorResponseMapper.java │ │ └── exception/ │ │ └── RestServiceException.java │ └── test/ │ ├── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── client/ │ │ └── test/ │ │ ├── ClientTest.java │ │ ├── ModelUtils.java │ │ ├── client/ │ │ │ ├── api/ │ │ │ │ ├── CustomerServiceAPI.java │ │ │ │ └── InvoiceServiceAPI.java │ │ │ └── exception/ │ │ │ ├── CustomerNotFoundResponseMapper.java │ │ │ ├── CustomerNotFoundRestServiceException.java │ │ │ ├── InvoiceNotFoundResponseMapper.java │ │ │ └── InvoiceNotFoundRestServiceException.java │ │ ├── exception/ │ │ │ ├── CustomerNotFoundException.java │ │ │ ├── CustomerNotFoundMapper.java │ │ │ ├── EntityNotFoundException.java │ │ │ ├── EntityNotFoundMapper.java │ │ │ ├── GenericServerErrorException.java │ │ │ ├── GenericServerErrorMapper.java │ │ │ ├── InvoiceNotFoundException.java │ │ │ └── InvoiceNotFoundMapper.java │ │ ├── model/ │ │ │ ├── Customer.java │ │ │ ├── Invoice.java │ │ │ ├── InvoiceReport.java │ │ │ └── ServiceErrorResponse.java │ │ └── service/ │ │ ├── CustomerService.java │ │ ├── InvoiceService.java │ │ └── ReportService.java │ └── resources/ │ └── testng.xml ├── core/ │ ├── deployment.yaml │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── wso2/ │ │ │ └── msf4j/ │ │ │ ├── AbstractSessionManager.java │ │ │ ├── DefaultSessionManager.java │ │ │ ├── HttpStreamHandler.java │ │ │ ├── HttpStreamer.java │ │ │ ├── Interceptor.java │ │ │ ├── Microservice.java │ │ │ ├── MicroservicesRegistry.java │ │ │ ├── MicroservicesRunner.java │ │ │ ├── MicroservicesServer.java │ │ │ ├── PersistentSessionManager.java │ │ │ ├── Request.java │ │ │ ├── Response.java │ │ │ ├── ServiceMethodInfo.java │ │ │ ├── Session.java │ │ │ ├── SessionManager.java │ │ │ ├── SwaggerService.java │ │ │ ├── beanconversion/ │ │ │ │ ├── BeanConversionException.java │ │ │ │ └── MediaTypeConverter.java │ │ │ ├── config/ │ │ │ │ └── MSF4JConfig.java │ │ │ ├── exception/ │ │ │ │ ├── InterceptorException.java │ │ │ │ └── OSGiDeclarativeServiceException.java │ │ │ ├── formparam/ │ │ │ │ ├── FileInfo.java │ │ │ │ ├── FormDataParam.java │ │ │ │ ├── FormItem.java │ │ │ │ ├── FormParamIterator.java │ │ │ │ ├── MultipartStream.java │ │ │ │ ├── ParameterParser.java │ │ │ │ ├── RequestContext.java │ │ │ │ ├── exception/ │ │ │ │ │ ├── FormUploadException.java │ │ │ │ │ ├── InvalidContentTypeException.java │ │ │ │ │ └── InvalidFileNameException.java │ │ │ │ └── util/ │ │ │ │ ├── Closeable.java │ │ │ │ ├── FormItemHeader.java │ │ │ │ ├── StreamUtil.java │ │ │ │ ├── mime/ │ │ │ │ │ ├── Base64Decoder.java │ │ │ │ │ ├── MimeUtility.java │ │ │ │ │ ├── ParseException.java │ │ │ │ │ ├── QuotedPrintableDecoder.java │ │ │ │ │ └── package-info.java │ │ │ │ └── package-info.java │ │ │ ├── interceptor/ │ │ │ │ ├── InterceptorExecutor.java │ │ │ │ ├── OSGiInterceptorConfig.java │ │ │ │ ├── RequestInterceptor.java │ │ │ │ ├── ResponseInterceptor.java │ │ │ │ └── annotation/ │ │ │ │ ├── RequestInterceptor.java │ │ │ │ └── ResponseInterceptor.java │ │ │ ├── internal/ │ │ │ │ ├── ClassComparator.java │ │ │ │ ├── DataHolder.java │ │ │ │ ├── HttpConnectorPortBindingListener.java │ │ │ │ ├── HttpHeadersImpl.java │ │ │ │ ├── MSF4JConstants.java │ │ │ │ ├── MSF4JHttpConnectorListener.java │ │ │ │ ├── MSF4JThreadFactory.java │ │ │ │ ├── MSF4JWSConnectorListener.java │ │ │ │ ├── MicroservicesLCException.java │ │ │ │ ├── MicroservicesRegistryImpl.java │ │ │ │ ├── MicroservicesServerActivator.java │ │ │ │ ├── MicroservicesServerImpl.java │ │ │ │ ├── MicroservicesServerSC.java │ │ │ │ ├── beanconversion/ │ │ │ │ │ ├── BeanConverter.java │ │ │ │ │ ├── JsonConverter.java │ │ │ │ │ ├── TextPlainConverter.java │ │ │ │ │ └── XmlConverter.java │ │ │ │ ├── entitywriter/ │ │ │ │ │ ├── EntityWriter.java │ │ │ │ │ ├── EntityWriterRegistry.java │ │ │ │ │ ├── FileEntityWriter.java │ │ │ │ │ ├── InputStreamEntityWriter.java │ │ │ │ │ ├── ObjectEntityWriter.java │ │ │ │ │ └── StreamingOutputEntityWriter.java │ │ │ │ ├── mime/ │ │ │ │ │ ├── MimeMapper.java │ │ │ │ │ └── MimeMappingException.java │ │ │ │ ├── router/ │ │ │ │ │ ├── HandlerException.java │ │ │ │ │ ├── HttpMethodInfo.java │ │ │ │ │ ├── HttpMethodInfoBuilder.java │ │ │ │ │ ├── HttpResourceModel.java │ │ │ │ │ ├── HttpResourceModelProcessor.java │ │ │ │ │ ├── ImmutablePair.java │ │ │ │ │ ├── MicroserviceMetadata.java │ │ │ │ │ ├── ParamConvertUtils.java │ │ │ │ │ ├── PatternPathRouter.java │ │ │ │ │ ├── SubresourceKey.java │ │ │ │ │ ├── Util.java │ │ │ │ │ └── package-info.java │ │ │ │ ├── session/ │ │ │ │ │ └── SessionIdGenerator.java │ │ │ │ └── websocket/ │ │ │ │ ├── CloseCodeImpl.java │ │ │ │ ├── EndpointDispatcher.java │ │ │ │ ├── EndpointValidator.java │ │ │ │ ├── EndpointsRegistryImpl.java │ │ │ │ ├── WebSocketPongMessage.java │ │ │ │ └── WebSocketServerSC.java │ │ │ ├── security/ │ │ │ │ ├── JWTSecurityInterceptor.java │ │ │ │ ├── MSF4JSecurityException.java │ │ │ │ ├── SecurityErrorCode.java │ │ │ │ ├── basic/ │ │ │ │ │ └── AbstractBasicAuthSecurityInterceptor.java │ │ │ │ └── oauth2/ │ │ │ │ ├── IntrospectionResponse.java │ │ │ │ └── OAuth2SecurityInterceptor.java │ │ │ ├── template/ │ │ │ │ ├── RuntimeTemplateException.java │ │ │ │ └── TemplateEngine.java │ │ │ ├── util/ │ │ │ │ ├── BufferUtil.java │ │ │ │ ├── Defaults.java │ │ │ │ ├── HttpUtil.java │ │ │ │ ├── Primitives.java │ │ │ │ ├── QueryStringDecoderUtil.java │ │ │ │ ├── ReflectionUtils.java │ │ │ │ ├── RuntimeAnnotations.java │ │ │ │ ├── SystemVariableUtil.java │ │ │ │ └── Utils.java │ │ │ └── websocket/ │ │ │ ├── WebSocketEndpoint.java │ │ │ ├── WebSocketEndpointsRegistry.java │ │ │ └── exception/ │ │ │ ├── WebSocketEndpointAnnotationException.java │ │ │ ├── WebSocketEndpointMethodReturnTypeException.java │ │ │ └── WebSocketMethodParameterException.java │ │ └── resources/ │ │ ├── deployment.yaml │ │ ├── log4j.properties │ │ └── mime-map.properties │ └── test/ │ ├── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ ├── DeprecatedInterceptorTest.java │ │ ├── ExtendedServiceTest.java │ │ ├── HostBindingTest.java │ │ ├── HttpResourceModelTest.java │ │ ├── HttpServerTest.java │ │ ├── HttpsServerTest.java │ │ ├── InterceptorTest.java │ │ ├── InterceptorTestBase.java │ │ ├── MSF4JResponseTest.java │ │ ├── MimeMapperTest.java │ │ ├── MutualAuthServerTest.java │ │ ├── PathRouterTest.java │ │ ├── SSLKeyStoreTest.java │ │ ├── TransportConfigurationTest.java │ │ ├── TransportConfigurationTest2.java │ │ ├── beanconversion/ │ │ │ └── BeanConverterTest.java │ │ ├── conf/ │ │ │ ├── Constants.java │ │ │ ├── SSLClientContext.java │ │ │ ├── SSLConfig.java │ │ │ ├── SSLHandlerFactory.java │ │ │ └── TrustManagerFactory.java │ │ ├── exception/ │ │ │ ├── MappedException.java │ │ │ ├── MappedException2.java │ │ │ ├── TestExceptionMapper.java │ │ │ └── TestExceptionMapper2.java │ │ ├── interceptor/ │ │ │ ├── HighPriorityClassRequestInterceptor.java │ │ │ ├── HighPriorityClassResponseInterceptor.java │ │ │ ├── HighPriorityGlobalRequestInterceptor.java │ │ │ ├── HighPriorityGlobalResponseInterceptor.java │ │ │ ├── HighPriorityMethodRequestInterceptor.java │ │ │ ├── HighPriorityMethodResponseInterceptor.java │ │ │ ├── LowPriorityClassRequestInterceptor.java │ │ │ ├── LowPriorityClassResponseInterceptor.java │ │ │ ├── LowPriorityGlobalRequestInterceptor.java │ │ │ ├── LowPriorityGlobalResponseInterceptor.java │ │ │ ├── LowPriorityMethodRequestInterceptor.java │ │ │ ├── LowPriorityMethodResponseInterceptor.java │ │ │ ├── MediumPriorityClassRequestInterceptor.java │ │ │ ├── MediumPriorityClassResponseInterceptor.java │ │ │ ├── MediumPriorityGlobalRequestInterceptor.java │ │ │ ├── MediumPriorityGlobalResponseInterceptor.java │ │ │ ├── MediumPriorityMethodRequestInterceptor.java │ │ │ ├── MediumPriorityMethodResponseInterceptor.java │ │ │ ├── PriorityDataHolder.java │ │ │ ├── TestBreakRequestInterceptor.java │ │ │ ├── TestBreakResponseInterceptor.java │ │ │ ├── TestExceptionBreakRequestInterceptor.java │ │ │ ├── TestInterceptor.java │ │ │ ├── TestInterceptorDeprecated.java │ │ │ ├── TestRequestInterceptor.java │ │ │ └── TestResponseInterceptor.java │ │ ├── internal/ │ │ │ ├── HttpHeadersImplTest.java │ │ │ └── MicroservicesRegistryTest.java │ │ ├── pojo/ │ │ │ ├── Category.java │ │ │ ├── Company.java │ │ │ ├── Person.java │ │ │ ├── Pet.java │ │ │ ├── TextBean.java │ │ │ └── XmlBean.java │ │ ├── service/ │ │ │ ├── ExtendedTestMicroservice.java │ │ │ ├── InterceptorTestMicroService.java │ │ │ ├── PriorityInterceptorTestMicroService.java │ │ │ ├── SecondService.java │ │ │ ├── TestMicroServiceWithDynamicPath.java │ │ │ ├── TestMicroservice.java │ │ │ └── sub/ │ │ │ ├── Player.java │ │ │ └── Team.java │ │ ├── session/ │ │ │ └── SessionIdGeneratorTest.java │ │ ├── util/ │ │ │ ├── QueryStringDecoderUtilTest.java │ │ │ └── client/ │ │ │ └── websocket/ │ │ │ ├── WebSocketClient.java │ │ │ └── WebSocketClientHandler.java │ │ └── websocket/ │ │ ├── DeploymentTest.java │ │ ├── EndpointRegistryTest.java │ │ ├── ValidatorTest.java │ │ └── endpoint/ │ │ ├── ChatAppEndpoint.java │ │ ├── EchoEndpoint.java │ │ ├── TestEndpoint.java │ │ ├── TestEndpointWithAllCorrect.java │ │ ├── TestEndpointWithMandatoryParameters.java │ │ └── error/ │ │ ├── TestEndpoinWithOnTextError.java │ │ ├── TestEndpointWithMandatoryParametersMissing.java │ │ ├── TestEndpointWithOnBinaryError.java │ │ ├── TestEndpointWithOnCloseError.java │ │ ├── TestEndpointWithOnError.java │ │ ├── TestEndpointWithOnOpenError.java │ │ ├── TestEndpointWithOnPongError.java │ │ ├── TestEndpointWithReturnTypeError.java │ │ └── TestEndpointWithServerEndpointError.java │ └── resources/ │ ├── cert.jks │ ├── client.jks │ ├── deployment.yaml │ ├── netty-transports-1.yaml │ ├── netty-transports-2.yaml │ ├── netty-transports-3.yaml │ ├── netty-transports-4.yaml │ ├── testTxtFile.txt │ └── testng.xml ├── deployer/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── deployer/ │ │ ├── MicroserviceDeploymentException.java │ │ ├── MicroserviceDeploymentUtils.java │ │ └── internal/ │ │ ├── DataHolder.java │ │ ├── MSF4JDeployerSC.java │ │ └── MicroservicesDeployer.java │ └── test/ │ ├── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── deployer/ │ │ └── MSF4JDeployerTest.java │ └── resources/ │ └── testng.xml ├── distribution/ │ ├── binary/ │ │ ├── LICENSE.txt │ │ ├── README.md │ │ ├── bin.xml │ │ ├── conf/ │ │ │ ├── netty-transports.yml │ │ │ └── wso2carbon.jks │ │ └── pom.xml │ └── msf4j-all/ │ ├── README.md │ └── pom.xml ├── features/ │ ├── etc/ │ │ └── feature.properties │ ├── feature-test/ │ │ ├── pom.xml │ │ └── src/ │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── wso2/ │ │ │ └── msf4j/ │ │ │ └── osgi/ │ │ │ └── test/ │ │ │ └── MSF4JStartupTest.java │ │ └── resources/ │ │ ├── carbon-home/ │ │ │ └── conf/ │ │ │ ├── carbon.yml │ │ │ └── log4j2.xml │ │ └── testng.xml │ ├── org.wso2.msf4j.deployer.feature/ │ │ ├── pom.xml │ │ └── resources/ │ │ └── p2.inf │ └── org.wso2.msf4j.feature/ │ ├── pom.xml │ └── resources/ │ └── netty-transports.yml ├── issue_template.md ├── jaxrs-delegates/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── wso2/ │ │ │ └── msf4j/ │ │ │ └── delegates/ │ │ │ ├── CookieHeaderProvider.java │ │ │ ├── MSF4JResponse.java │ │ │ ├── MSF4JRuntimeDelegate.java │ │ │ └── MediaTypeHeaderProvider.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ └── javax.ws.rs.ext.RuntimeDelegate │ └── test/ │ ├── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ ├── delegates/ │ │ │ ├── CookieHeaderProviderTest.java │ │ │ └── MediaTypeHeaderProviderTest.java │ │ ├── models/ │ │ │ └── SampleEntity.java │ │ └── service/ │ │ └── ClientTestMicroService.java │ └── resources/ │ └── testng.xml ├── perf-benchmark/ │ ├── README.md │ ├── Samples/ │ │ ├── dropwizard/ │ │ │ ├── README.md │ │ │ ├── pom.xml │ │ │ ├── run.sh │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── wso2/ │ │ │ └── msf4j/ │ │ │ └── perftest/ │ │ │ └── echo/ │ │ │ └── dropwizard/ │ │ │ ├── AppStart.java │ │ │ └── EchoService.java │ │ ├── ninja-echo-message/ │ │ │ ├── README.md │ │ │ ├── pom.xml │ │ │ ├── run.sh │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ ├── conf/ │ │ │ │ ├── Module.java │ │ │ │ ├── Routes.java │ │ │ │ ├── application.conf │ │ │ │ └── messages.properties │ │ │ ├── controllers/ │ │ │ │ └── ApplicationController.java │ │ │ ├── ehcache.xml │ │ │ ├── logback.xml │ │ │ └── util/ │ │ │ └── BodyParserEngineTextPlain.java │ │ ├── spark/ │ │ │ ├── README.md │ │ │ ├── pom.xml │ │ │ ├── run.sh │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── wso2/ │ │ │ └── msf4j/ │ │ │ └── perftest/ │ │ │ └── echo/ │ │ │ └── spark/ │ │ │ └── Application.java │ │ ├── springboot/ │ │ │ ├── README.md │ │ │ ├── pom.xml │ │ │ ├── run.sh │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── wso2/ │ │ │ └── msf4j/ │ │ │ └── perftest/ │ │ │ └── echo/ │ │ │ └── springboot/ │ │ │ ├── Application.java │ │ │ └── EchoService.java │ │ ├── wildfly/ │ │ │ ├── README.md │ │ │ ├── pom.xml │ │ │ ├── run.sh │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── wso2/ │ │ │ │ └── msf4j/ │ │ │ │ └── perftest/ │ │ │ │ └── echo/ │ │ │ │ └── wildfly/ │ │ │ │ ├── EchoService.java │ │ │ │ └── WFApplication.java │ │ │ └── webapp/ │ │ │ └── WEB-INF/ │ │ │ └── beans.xml │ │ └── wso2msf4j/ │ │ ├── README.md │ │ ├── pom.xml │ │ ├── run.sh │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── perftest/ │ │ └── echo/ │ │ └── wso2msf4j/ │ │ ├── Application.java │ │ └── EchoService.java │ ├── excecute-tests.sh │ ├── run-latency.sh │ └── run.sh ├── pom.xml ├── poms/ │ ├── msf4j-service/ │ │ ├── README.md │ │ └── pom.xml │ └── parent/ │ └── pom.xml ├── pull_request_template.md ├── samples/ │ ├── README.md │ ├── basicauth-security/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── example/ │ │ ├── Application.java │ │ ├── Helloworld.java │ │ └── UsernamePasswordSecurityInterceptor.java │ ├── circuitbreaker/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── example/ │ │ └── service/ │ │ ├── Application.java │ │ ├── CircuitBreakerCommand.java │ │ ├── Stock.java │ │ ├── StockQuoteDatabase.java │ │ └── StockQuoteService.java │ ├── fileserver/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── example/ │ │ ├── Application.java │ │ └── FileServer.java │ ├── formparam/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── wso2/ │ │ │ └── msf4j/ │ │ │ └── example/ │ │ │ ├── Application.java │ │ │ ├── FormService.java │ │ │ ├── SampleClient.java │ │ │ └── bean/ │ │ │ ├── Company.java │ │ │ └── Person.java │ │ └── resources/ │ │ └── sample.txt │ ├── helloworld/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── example/ │ │ ├── Application.java │ │ └── HelloService.java │ ├── http-monitoring/ │ │ ├── README.md │ │ ├── client-truststore.jks │ │ ├── data-agent-config.xml │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── wso2/ │ │ │ └── msf4j/ │ │ │ └── sample/ │ │ │ └── httpmonitoring/ │ │ │ ├── Application.java │ │ │ └── service/ │ │ │ ├── Student.java │ │ │ └── StudentService.java │ │ └── resources/ │ │ ├── deployment.yaml │ │ ├── http-monitoring.yaml │ │ └── metrics.yaml │ ├── http-session/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── example/ │ │ ├── Application.java │ │ └── CounterService.java │ ├── interceptor/ │ │ ├── README.md │ │ ├── deployable-jar-interceptor-service/ │ │ │ ├── README.md │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── wso2/ │ │ │ └── msf4j/ │ │ │ └── samples/ │ │ │ └── deployablejarinterceptorservice/ │ │ │ ├── InterceptorService.java │ │ │ └── interceptors/ │ │ │ ├── HTTPRequestLogger.java │ │ │ ├── HTTPResponseLogger.java │ │ │ ├── LogTextRequestInterceptor.java │ │ │ ├── LogTextResponseInterceptor.java │ │ │ ├── PropertyAddRequestInterceptor.java │ │ │ └── PropertyGetResponseInterceptor.java │ │ ├── fatjar-interceptor-service/ │ │ │ ├── README.md │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── wso2/ │ │ │ └── msf4j/ │ │ │ └── samples/ │ │ │ └── fatjarinterceptorservice/ │ │ │ ├── Application.java │ │ │ └── InterceptorService.java │ │ ├── interceptor-common/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── wso2/ │ │ │ └── msf4j/ │ │ │ └── samples/ │ │ │ └── interceptor/ │ │ │ └── common/ │ │ │ ├── HTTPRequestLogger.java │ │ │ ├── HTTPResponseLogger.java │ │ │ ├── LogTextRequestInterceptor.java │ │ │ ├── LogTextResponseInterceptor.java │ │ │ ├── PropertyAddRequestInterceptor.java │ │ │ └── PropertyGetResponseInterceptor.java │ │ ├── osgi-interceptor-service/ │ │ │ ├── README.md │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── wso2/ │ │ │ └── msf4j/ │ │ │ └── samples/ │ │ │ └── osgiinterceptorservice/ │ │ │ ├── InterceptorService.java │ │ │ └── config/ │ │ │ └── SampleInterceptorConfig.java │ │ └── spring-fatjar-interceptor-service/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── samples/ │ │ └── springfatjarinterceptorservice/ │ │ ├── Application.java │ │ ├── CustomerService.java │ │ └── ReceptionService.java │ ├── jpa/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── wso2/ │ │ │ └── msf4j/ │ │ │ └── example/ │ │ │ ├── Application.java │ │ │ ├── dao/ │ │ │ │ ├── AbstractRepository.java │ │ │ │ └── UserRepository.java │ │ │ ├── model/ │ │ │ │ └── User.java │ │ │ └── resource/ │ │ │ └── UserResource.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── persistence.xml │ ├── jwt-claims/ │ │ ├── JWTAccessTokenBuilder/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── wso2/ │ │ │ └── jwt/ │ │ │ └── token/ │ │ │ └── builder/ │ │ │ ├── Constants.java │ │ │ ├── JWTAccessTokenBuilder.java │ │ │ └── internal/ │ │ │ └── JWTAccessTokenBuilderDSComponent.java │ │ ├── README.md │ │ ├── jwt-sample/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── wso2/ │ │ │ │ └── msf4j/ │ │ │ │ └── example/ │ │ │ │ ├── Application.java │ │ │ │ ├── CustomJWTClaimsInterceptor.java │ │ │ │ └── Helloworld.java │ │ │ └── resources/ │ │ │ └── wso2carbon.jks │ │ ├── pom.xml │ │ ├── sso-agent-for-jwt-webapp/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── wso2/ │ │ │ │ └── sample/ │ │ │ │ └── is/ │ │ │ │ └── sso/ │ │ │ │ └── agent/ │ │ │ │ ├── ForwardingServlet.java │ │ │ │ ├── SSOAgentSampleFilter.java │ │ │ │ ├── SampleAttributesRequestor.java │ │ │ │ └── SampleContextEventListener.java │ │ │ ├── resources/ │ │ │ │ ├── avis.properties │ │ │ │ ├── travelocity.properties │ │ │ │ └── wso2carbon.jks │ │ │ └── webapp/ │ │ │ ├── WEB-INF/ │ │ │ │ └── web.xml │ │ │ ├── css/ │ │ │ │ └── cart-styles.css │ │ │ ├── errors.jsp │ │ │ ├── home.jsp │ │ │ └── index.jsp │ │ └── sso-agent-sample/ │ │ └── pom.xml │ ├── lifecycle/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── example/ │ │ ├── Application.java │ │ └── Helloworld.java │ ├── log-interceptor-bundle/ │ │ └── pom.xml │ ├── message-tracing/ │ │ ├── das/ │ │ │ ├── README.md │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── wso2/ │ │ │ └── msf4j/ │ │ │ └── example/ │ │ │ ├── Application.java │ │ │ ├── ModelUtils.java │ │ │ ├── client/ │ │ │ │ ├── api/ │ │ │ │ │ ├── CustomerServiceAPI.java │ │ │ │ │ └── InvoiceServiceAPI.java │ │ │ │ └── exception/ │ │ │ │ ├── CustomerNotFoundResponseMapper.java │ │ │ │ ├── CustomerNotFoundRestServiceException.java │ │ │ │ ├── InvoiceNotFoundResponseMapper.java │ │ │ │ └── InvoiceNotFoundRestServiceException.java │ │ │ ├── exception/ │ │ │ │ ├── CustomerNotFoundException.java │ │ │ │ ├── CustomerNotFoundMapper.java │ │ │ │ ├── EntityNotFoundException.java │ │ │ │ ├── EntityNotFoundMapper.java │ │ │ │ ├── GenericServerErrorException.java │ │ │ │ ├── GenericServerErrorMapper.java │ │ │ │ ├── InvoiceNotFoundException.java │ │ │ │ └── InvoiceNotFoundMapper.java │ │ │ ├── model/ │ │ │ │ ├── Customer.java │ │ │ │ ├── Invoice.java │ │ │ │ ├── InvoiceReport.java │ │ │ │ └── ServiceErrorResponse.java │ │ │ └── service/ │ │ │ ├── CustomerService.java │ │ │ ├── InvoiceService.java │ │ │ └── ReportService.java │ │ └── zipkin/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── example/ │ │ ├── Application.java │ │ ├── TraceableService.java │ │ └── TraceableServiceInterface.java │ ├── metrics/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── wso2/ │ │ │ └── msf4j/ │ │ │ └── sample/ │ │ │ └── metrics/ │ │ │ ├── Application.java │ │ │ └── service/ │ │ │ └── DemoService.java │ │ └── resources/ │ │ ├── deployment.yaml │ │ └── metrics.yaml │ ├── oauth2-security/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── example/ │ │ ├── Application.java │ │ └── service/ │ │ └── Helloworld.java │ ├── petstore/ │ │ ├── README.md │ │ ├── deployment/ │ │ │ ├── docker-compose/ │ │ │ │ ├── README.md │ │ │ │ ├── clean.sh │ │ │ │ ├── das/ │ │ │ │ │ └── Dockerfile │ │ │ │ ├── docker-compose.yaml │ │ │ │ └── run.sh │ │ │ └── kubernetes/ │ │ │ ├── README.md │ │ │ ├── clean.sh │ │ │ ├── external-endpoints/ │ │ │ │ ├── das-endpoint.yaml │ │ │ │ ├── das-service.yaml │ │ │ │ ├── is-endpoint.yaml │ │ │ │ └── is-service.yaml │ │ │ ├── packs/ │ │ │ │ └── README.md │ │ │ ├── path.sh │ │ │ ├── petstore.sh │ │ │ └── scale.sh │ │ ├── microservices/ │ │ │ ├── fileserver/ │ │ │ │ ├── README.md │ │ │ │ ├── client-truststore.jks │ │ │ │ ├── container/ │ │ │ │ │ ├── docker/ │ │ │ │ │ │ ├── Dockerfile │ │ │ │ │ │ └── README.md │ │ │ │ │ └── kubernetes/ │ │ │ │ │ ├── README.md │ │ │ │ │ ├── fileserver-rc.yaml │ │ │ │ │ └── fileserver-service.yaml │ │ │ │ ├── data-agent-conf.xml │ │ │ │ ├── pom.xml │ │ │ │ └── src/ │ │ │ │ └── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── wso2/ │ │ │ │ │ └── msf4j/ │ │ │ │ │ └── examples/ │ │ │ │ │ └── petstore/ │ │ │ │ │ └── fileserver/ │ │ │ │ │ ├── FileServerService.java │ │ │ │ │ └── Runner.java │ │ │ │ └── resources/ │ │ │ │ ├── http-monitoring.yml │ │ │ │ ├── metrics.yml │ │ │ │ └── wso2carbon.jks │ │ │ ├── frontend-admin/ │ │ │ │ └── container/ │ │ │ │ ├── .gitignore │ │ │ │ ├── README.md │ │ │ │ ├── docker/ │ │ │ │ │ ├── Dockerfile │ │ │ │ │ ├── html/ │ │ │ │ │ │ ├── .htaccess │ │ │ │ │ │ ├── add-pet-types.php │ │ │ │ │ │ ├── add-pets.php │ │ │ │ │ │ ├── config/ │ │ │ │ │ │ │ └── config.php │ │ │ │ │ │ ├── controllers/ │ │ │ │ │ │ │ ├── API/ │ │ │ │ │ │ │ │ └── curl_api.php │ │ │ │ │ │ │ ├── authentication/ │ │ │ │ │ │ │ │ └── auth.php │ │ │ │ │ │ │ └── rest.php │ │ │ │ │ │ ├── css/ │ │ │ │ │ │ │ └── custom.css │ │ │ │ │ │ ├── includes/ │ │ │ │ │ │ │ ├── header.php │ │ │ │ │ │ │ └── navbar.php │ │ │ │ │ │ ├── index.php │ │ │ │ │ │ ├── js/ │ │ │ │ │ │ │ ├── custom.js │ │ │ │ │ │ │ └── login.js │ │ │ │ │ │ ├── libs/ │ │ │ │ │ │ │ ├── font-awesome_4.3.0/ │ │ │ │ │ │ │ │ ├── css/ │ │ │ │ │ │ │ │ │ └── font-awesome.css │ │ │ │ │ │ │ │ └── fonts/ │ │ │ │ │ │ │ │ └── FontAwesome.otf │ │ │ │ │ │ │ ├── font-wso2_1.2/ │ │ │ │ │ │ │ │ └── css/ │ │ │ │ │ │ │ │ └── font-wso2.css │ │ │ │ │ │ │ ├── jquery_1.11.3/ │ │ │ │ │ │ │ │ └── jquery-1.11.3.js │ │ │ │ │ │ │ └── noty_2.3.5/ │ │ │ │ │ │ │ ├── jquery.noty.js │ │ │ │ │ │ │ ├── layouts/ │ │ │ │ │ │ │ │ ├── bottom.js │ │ │ │ │ │ │ │ ├── bottomCenter.js │ │ │ │ │ │ │ │ ├── bottomLeft.js │ │ │ │ │ │ │ │ ├── bottomRight.js │ │ │ │ │ │ │ │ ├── center.js │ │ │ │ │ │ │ │ ├── centerLeft.js │ │ │ │ │ │ │ │ ├── centerRight.js │ │ │ │ │ │ │ │ ├── inline.js │ │ │ │ │ │ │ │ ├── top.js │ │ │ │ │ │ │ │ ├── topCenter.js │ │ │ │ │ │ │ │ ├── topLeft.js │ │ │ │ │ │ │ │ └── topRight.js │ │ │ │ │ │ │ ├── packaged/ │ │ │ │ │ │ │ │ └── jquery.noty.packaged.js │ │ │ │ │ │ │ ├── promise.js │ │ │ │ │ │ │ └── themes/ │ │ │ │ │ │ │ ├── bootstrap.js │ │ │ │ │ │ │ ├── default.js │ │ │ │ │ │ │ └── relax.js │ │ │ │ │ │ ├── login.php │ │ │ │ │ │ ├── logout.php │ │ │ │ │ │ ├── pet-types.php │ │ │ │ │ │ └── pets.php │ │ │ │ │ └── init.sh │ │ │ │ └── kubernetes/ │ │ │ │ ├── admin-fe-controller.yaml │ │ │ │ ├── admin-fe-service.yaml │ │ │ │ ├── redis-master-controller.yaml │ │ │ │ └── redis-master-service.yaml │ │ │ ├── frontend-user/ │ │ │ │ └── container/ │ │ │ │ ├── .gitignore │ │ │ │ ├── README.md │ │ │ │ ├── docker/ │ │ │ │ │ ├── Dockerfile │ │ │ │ │ ├── html/ │ │ │ │ │ │ ├── cart.php │ │ │ │ │ │ ├── checkout.php │ │ │ │ │ │ ├── config/ │ │ │ │ │ │ │ └── config.php │ │ │ │ │ │ ├── controllers/ │ │ │ │ │ │ │ ├── API/ │ │ │ │ │ │ │ │ └── curl_api.php │ │ │ │ │ │ │ ├── authentication/ │ │ │ │ │ │ │ │ └── auth.php │ │ │ │ │ │ │ └── cart/ │ │ │ │ │ │ │ └── cart.php │ │ │ │ │ │ ├── css/ │ │ │ │ │ │ │ └── custom.css │ │ │ │ │ │ ├── includes/ │ │ │ │ │ │ │ ├── header.php │ │ │ │ │ │ │ └── navbar.php │ │ │ │ │ │ ├── index.php │ │ │ │ │ │ ├── js/ │ │ │ │ │ │ │ ├── custom.js │ │ │ │ │ │ │ └── login.js │ │ │ │ │ │ ├── libs/ │ │ │ │ │ │ │ ├── font-awesome_4.3.0/ │ │ │ │ │ │ │ │ ├── css/ │ │ │ │ │ │ │ │ │ └── font-awesome.css │ │ │ │ │ │ │ │ └── fonts/ │ │ │ │ │ │ │ │ └── FontAwesome.otf │ │ │ │ │ │ │ ├── font-wso2_1.2/ │ │ │ │ │ │ │ │ └── css/ │ │ │ │ │ │ │ │ └── font-wso2.css │ │ │ │ │ │ │ ├── jquery_1.11.3/ │ │ │ │ │ │ │ │ └── jquery-1.11.3.js │ │ │ │ │ │ │ └── noty_2.3.5/ │ │ │ │ │ │ │ ├── jquery.noty.js │ │ │ │ │ │ │ ├── layouts/ │ │ │ │ │ │ │ │ ├── bottom.js │ │ │ │ │ │ │ │ ├── bottomCenter.js │ │ │ │ │ │ │ │ ├── bottomLeft.js │ │ │ │ │ │ │ │ ├── bottomRight.js │ │ │ │ │ │ │ │ ├── center.js │ │ │ │ │ │ │ │ ├── centerLeft.js │ │ │ │ │ │ │ │ ├── centerRight.js │ │ │ │ │ │ │ │ ├── inline.js │ │ │ │ │ │ │ │ ├── top.js │ │ │ │ │ │ │ │ ├── topCenter.js │ │ │ │ │ │ │ │ ├── topLeft.js │ │ │ │ │ │ │ │ └── topRight.js │ │ │ │ │ │ │ ├── packaged/ │ │ │ │ │ │ │ │ └── jquery.noty.packaged.js │ │ │ │ │ │ │ ├── promise.js │ │ │ │ │ │ │ └── themes/ │ │ │ │ │ │ │ ├── bootstrap.js │ │ │ │ │ │ │ ├── default.js │ │ │ │ │ │ │ └── relax.js │ │ │ │ │ │ ├── login.php │ │ │ │ │ │ └── logout.php │ │ │ │ │ └── init.sh │ │ │ │ └── kubernetes/ │ │ │ │ ├── user-fe-controller.yaml │ │ │ │ └── user-fe-service.yaml │ │ │ ├── pet/ │ │ │ │ ├── README.md │ │ │ │ ├── client-truststore.jks │ │ │ │ ├── container/ │ │ │ │ │ ├── docker/ │ │ │ │ │ │ ├── Dockerfile │ │ │ │ │ │ ├── README.md │ │ │ │ │ │ └── ssh/ │ │ │ │ │ │ └── Dockerfile │ │ │ │ │ └── kubernetes/ │ │ │ │ │ ├── pet-controller.yaml │ │ │ │ │ └── pet-service.yaml │ │ │ │ ├── data-agent-conf.xml │ │ │ │ ├── etc/ │ │ │ │ │ └── init.sh │ │ │ │ ├── pom.xml │ │ │ │ └── src/ │ │ │ │ └── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── wso2/ │ │ │ │ │ └── msf4j/ │ │ │ │ │ └── examples/ │ │ │ │ │ └── petstore/ │ │ │ │ │ └── pet/ │ │ │ │ │ ├── PetCategoryService.java │ │ │ │ │ ├── PetConstants.java │ │ │ │ │ ├── PetService.java │ │ │ │ │ └── Runner.java │ │ │ │ └── resources/ │ │ │ │ ├── http-monitoring.yml │ │ │ │ ├── metrics.yml │ │ │ │ └── wso2carbon.jks │ │ │ ├── redis/ │ │ │ │ └── container/ │ │ │ │ └── kubernetes/ │ │ │ │ ├── README.md │ │ │ │ ├── redis-controller.yaml │ │ │ │ ├── redis-master.yaml │ │ │ │ ├── redis-sentinel-controller.yaml │ │ │ │ └── redis-sentinel-service.yaml │ │ │ ├── security/ │ │ │ │ ├── client-truststore.jks │ │ │ │ ├── container/ │ │ │ │ │ ├── docker/ │ │ │ │ │ │ └── Dockerfile │ │ │ │ │ └── kubernetes/ │ │ │ │ │ ├── README.md │ │ │ │ │ ├── security-controller.yaml │ │ │ │ │ └── security-service.yaml │ │ │ │ ├── data-agent-conf.xml │ │ │ │ ├── pom.xml │ │ │ │ └── src/ │ │ │ │ └── main/ │ │ │ │ ├── java/ │ │ │ │ │ ├── README.md │ │ │ │ │ └── org/ │ │ │ │ │ └── wso2/ │ │ │ │ │ └── msf4j/ │ │ │ │ │ └── examples/ │ │ │ │ │ └── petstore/ │ │ │ │ │ └── security/ │ │ │ │ │ ├── JWTGenerator.java │ │ │ │ │ ├── Runner.java │ │ │ │ │ ├── UserAuthenticationService.java │ │ │ │ │ └── ldap/ │ │ │ │ │ ├── LDAPUserStoreManager.java │ │ │ │ │ └── server/ │ │ │ │ │ ├── ApacheDirectoryServerActivator.java │ │ │ │ │ └── LDAPServerConfigurationBuilder.java │ │ │ │ └── resources/ │ │ │ │ ├── http-monitoring.yml │ │ │ │ ├── metrics.yml │ │ │ │ ├── repository/ │ │ │ │ │ └── conf/ │ │ │ │ │ └── embedded-ldap.xml │ │ │ │ └── wso2carbon.jks │ │ │ └── transaction/ │ │ │ ├── README.md │ │ │ ├── client-truststore.jks │ │ │ ├── container/ │ │ │ │ ├── docker/ │ │ │ │ │ └── Dockerfile │ │ │ │ └── kubernetes/ │ │ │ │ ├── txn-controller.yaml │ │ │ │ └── txn-service.yaml │ │ │ ├── data-agent-conf.xml │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── wso2/ │ │ │ │ └── msf4j/ │ │ │ │ └── examples/ │ │ │ │ └── petstore/ │ │ │ │ └── transaction/ │ │ │ │ ├── Runner.java │ │ │ │ ├── TxnConstants.java │ │ │ │ └── TxnService.java │ │ │ └── resources/ │ │ │ ├── http-monitoring.yml │ │ │ ├── metrics.yml │ │ │ └── wso2carbon.jks │ │ ├── pom.xml │ │ └── util/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── examples/ │ │ └── petstore/ │ │ └── util/ │ │ ├── JedisUtil.java │ │ └── model/ │ │ ├── Category.java │ │ ├── CreditCard.java │ │ ├── Order.java │ │ ├── Pet.java │ │ └── User.java │ ├── regex-pathparam/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── example/ │ │ ├── Application.java │ │ └── RegexPathParam.java │ ├── spring-helloworld/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── example/ │ │ ├── Application.java │ │ ├── Hello.java │ │ ├── HelloService.java │ │ ├── InvalidNameException.java │ │ ├── InvalidNameExceptionMapper.java │ │ ├── LogHeadersInterceptor.java │ │ └── TransportConfiguration.java │ ├── spring-profile/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── wso2/ │ │ │ └── msf4j/ │ │ │ └── example/ │ │ │ ├── Application.java │ │ │ ├── Hello.java │ │ │ ├── HelloService.java │ │ │ └── TransportConfig.java │ │ └── resources/ │ │ └── application.yml │ ├── stockquote/ │ │ ├── bundle/ │ │ │ ├── README.md │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── wso2/ │ │ │ └── msf4j/ │ │ │ └── stockquote/ │ │ │ ├── Stock.java │ │ │ ├── StockQuoteService.java │ │ │ ├── Stocks.java │ │ │ └── exception/ │ │ │ ├── DuplicateSymbolException.java │ │ │ ├── DuplicateSymbolMapper.java │ │ │ ├── EntityNotFoundException.java │ │ │ ├── EntityNotFoundMapper.java │ │ │ ├── SymbolNotFoundException.java │ │ │ └── SymbolNotFoundMapper.java │ │ ├── deployable-jar/ │ │ │ ├── README.md │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── wso2/ │ │ │ └── msf4j/ │ │ │ └── stockquote/ │ │ │ └── example/ │ │ │ ├── Stock.java │ │ │ ├── StockQuoteService.java │ │ │ ├── Stocks.java │ │ │ └── exception/ │ │ │ ├── DuplicateSymbolException.java │ │ │ ├── DuplicateSymbolMapper.java │ │ │ ├── EntityNotFoundException.java │ │ │ ├── EntityNotFoundMapper.java │ │ │ ├── SymbolNotFoundException.java │ │ │ └── SymbolNotFoundMapper.java │ │ └── fatjar/ │ │ ├── README.md │ │ ├── conf/ │ │ │ ├── netty-transports.yml │ │ │ └── wso2carbon.jks │ │ ├── container/ │ │ │ ├── docker/ │ │ │ │ └── Dockerfile │ │ │ └── kubernetes/ │ │ │ ├── stockquote-controller.yaml │ │ │ └── stockquote-service.yaml │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── example/ │ │ ├── Application.java │ │ ├── Stock.java │ │ ├── StockQuoteService.java │ │ ├── Stocks.java │ │ └── exception/ │ │ ├── DuplicateSymbolException.java │ │ ├── DuplicateSymbolMapper.java │ │ ├── EntityNotFoundException.java │ │ ├── EntityNotFoundMapper.java │ │ ├── SymbolNotFoundException.java │ │ └── SymbolNotFoundMapper.java │ ├── subresource/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── example/ │ │ ├── Application.java │ │ ├── Bowler.java │ │ ├── Country.java │ │ ├── Player.java │ │ └── Team.java │ ├── template/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── wso2/ │ │ │ └── msf4j/ │ │ │ └── example/ │ │ │ ├── Application.java │ │ │ └── TemplateService.java │ │ └── resources/ │ │ └── templates/ │ │ └── hello.mustache │ └── websocket/ │ ├── README.md │ └── chatApp/ │ ├── bundle/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── sample/ │ │ └── websocket/ │ │ └── chatapp/ │ │ └── ChatAppEndpoint.java │ ├── deployable-jar/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── sample/ │ │ └── websocket/ │ │ └── chatapp/ │ │ └── ChatAppEndpoint.java │ ├── fatjar/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── sample/ │ │ └── websocket/ │ │ └── chatapp/ │ │ ├── Application.java │ │ └── ChatAppEndpoint.java │ └── js-client/ │ └── index.html ├── spring/ │ ├── deployment.yaml │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── wso2/ │ │ │ └── msf4j/ │ │ │ └── spring/ │ │ │ ├── MSF4JBeanDefinitionRegistryPostProcessor.java │ │ │ ├── MSF4JSpringApplication.java │ │ │ ├── MSF4JSpringConfiguration.java │ │ │ ├── SpringConstants.java │ │ │ ├── SpringMicroservicesRunner.java │ │ │ ├── property/ │ │ │ │ └── YamlFileApplicationContextInitializer.java │ │ │ └── transport/ │ │ │ ├── HTTPSTransportConfig.java │ │ │ ├── HTTPTransportConfig.java │ │ │ └── TransportConfig.java │ │ └── resources/ │ │ └── log4j.xml │ └── test/ │ ├── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── spring/ │ │ ├── ServerConfig.java │ │ ├── SpringHttpServerTest.java │ │ ├── SpringInterceptorTest.java │ │ ├── exception/ │ │ │ ├── SpringTestExceptionMapper.java │ │ │ └── SpringTestExceptionMapper2.java │ │ └── service/ │ │ ├── SpringTestMicroservice.java │ │ └── second/ │ │ ├── CustomService.java │ │ ├── SecondService.java │ │ └── TestMicroServiceWithDynamicPath.java │ └── resources/ │ ├── cert.jks │ ├── client.jks │ ├── deployment.yaml │ ├── netty-transports-1.yml │ ├── netty-transports-2.yml │ ├── testTxtFile.txt │ └── testng.xml ├── swagger/ │ ├── deployment.yaml │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── wso2/ │ │ │ └── msf4j/ │ │ │ └── swagger/ │ │ │ ├── ExtendedSwaggerReader.java │ │ │ ├── MSF4JBeanConfig.java │ │ │ ├── SwaggerDefinitionService.java │ │ │ └── internal/ │ │ │ ├── DataHolder.java │ │ │ └── SwaggerDefinitionSC.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ └── org.wso2.msf4j.SwaggerService │ └── test/ │ ├── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── swagger/ │ │ └── SwaggerTest.java │ └── resources/ │ ├── deployment.yaml │ └── testng.xml ├── templating/ │ └── msf4j-mustache-template/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── template/ │ │ └── MustacheTemplateEngine.java │ └── test/ │ ├── java/ │ │ └── org/ │ │ └── wso2/ │ │ └── msf4j/ │ │ └── template/ │ │ └── MustacheTemplateEngineTest.java │ └── resources/ │ └── templates/ │ ├── nomodel.mustache │ └── withmodel.mustache ├── tests/ │ ├── osgi-tests/ │ │ ├── pom.xml │ │ └── src/ │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── wso2/ │ │ │ └── msf4j/ │ │ │ └── osgi/ │ │ │ └── test/ │ │ │ ├── MSF4JOSGiTest.java │ │ │ └── service/ │ │ │ ├── SecondService.java │ │ │ └── TestService.java │ │ └── resources/ │ │ ├── carbon-home/ │ │ │ ├── conf/ │ │ │ │ ├── carbon.yml │ │ │ │ ├── etc/ │ │ │ │ │ └── pax-logging.properties │ │ │ │ ├── log4j2.xml │ │ │ │ └── osgi/ │ │ │ │ └── launch.properties │ │ │ └── deployment/ │ │ │ └── README.txt │ │ └── testng.xml │ ├── pom.xml │ └── test-distribution/ │ ├── carbon-home/ │ │ └── conf/ │ │ └── deployment.yaml │ ├── pom.xml │ └── src/ │ └── assembly/ │ └── bin.xml └── tooling/ ├── LICENSE ├── README.md ├── features/ │ ├── org.wso2.developerstudio.msf4j.feature/ │ │ ├── build.properties │ │ ├── feature.xml │ │ └── pom.xml │ └── pom.xml ├── plugins/ │ ├── org.wso2.developerstudio.msf4j.artifact/ │ │ ├── .gitignore │ │ ├── META-INF/ │ │ │ └── MANIFEST.MF │ │ ├── build.properties │ │ ├── plugin.xml │ │ ├── pom.xml │ │ ├── project_wizard.xml │ │ └── src/ │ │ └── org/ │ │ └── wso2/ │ │ └── developerstudio/ │ │ └── msf4j/ │ │ └── artifact/ │ │ ├── Activator.java │ │ ├── generator/ │ │ │ └── SwaggerToJavaGenerator.java │ │ ├── model/ │ │ │ └── MSF4JProjectModel.java │ │ ├── project/ │ │ │ └── nature/ │ │ │ └── MSF4JArtifactProjectNature.java │ │ ├── ui/ │ │ │ └── wizard/ │ │ │ ├── MSF4JPerspective.java │ │ │ └── MSF4JProjectCreationWizard.java │ │ ├── util/ │ │ │ ├── GeneratorUtils.java │ │ │ ├── LibraryUtils.java │ │ │ ├── MSF4JArtifactConstants.java │ │ │ ├── MSF4JDependencyResolverJob.java │ │ │ ├── MSF4JImageUtils.java │ │ │ ├── MSF4JMavenDependencyResolverJob.java │ │ │ └── MSF4JProjectImporter.java │ │ └── validators/ │ │ └── MSF4JArtifactFieldController.java │ └── pom.xml ├── pom.xml ├── repository/ │ ├── composite/ │ │ ├── category.xml │ │ └── pom.xml │ ├── main/ │ │ ├── category.xml │ │ └── pom.xml │ └── pom.xml └── swagger-mustache/ ├── 1.4/ │ ├── ApiException.mustache │ ├── ApiOriginFilter.mustache │ ├── ApiResponseMessage.mustache │ ├── NotFoundException.mustache │ ├── README.mustache │ ├── api.mustache │ ├── apiService.mustache │ ├── apiServiceFactory.mustache │ ├── apiServiceImpl.mustache │ ├── bodyParams.mustache │ ├── formParams.mustache │ ├── generatedAnnotation.mustache │ ├── headerParams.mustache │ ├── model.mustache │ ├── pathParams.mustache │ ├── pom.mustache │ ├── queryParams.mustache │ ├── returnTypes.mustache │ ├── serviceBodyParams.mustache │ ├── serviceFormParams.mustache │ ├── serviceHeaderParams.mustache │ ├── servicePathParams.mustache │ ├── serviceQueryParams.mustache │ └── web.mustache └── README.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.class # Mobile Tools for Java (J2ME) .mtj.tmp/ # Package Files # *.jar *.war *.ear # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* # Ignore everything in this directory target .classpath .settings .project *.iml *.iws *.ipr .idea # Ignore dependency-reduced-pom.xml file created by Apache Maven Shade Plugin, see https://maven.apache.org/plugins/maven-shade-plugin/shade-mojo.html dependency-reduced-pom.xml ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} 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. ================================================ FILE: README.md ================================================ [Build status:](https://wso2.org/jenkins/job/products/job/msf4j/) ![Build Status](https://wso2.org/jenkins/job/products/job/msf4j/badge/icon) # WSO2 Microservices Framework for Java (MSF4J) WSO2 Microservices Framework for Java (MSF4J) is a lightweight high performance framework for developing & running microservices. WSO2 MSF4J is one of the highest performing lightweight Java microservices frameworks. The following graphs show the throughput, memory consumption & latency characteristics of MSF4J against other microservices frameworks. ![EchoThroughput](perf-benchmark/graphs/echotps.png) An echo service which accepts a 1KB request & echoes it back directly and using a temp file was developed for the respective frameworks, and requests were sent for different concurrency values. The test was repeated for each concurrency value for each framework and the average throughput was calculated. Tests were run out of the box without any tuning on 32 core 64GB server in JVM v1.8.0_60 with default configuration. ![EchoMemory](perf-benchmark/graphs/echomem.png) Memory usage for each framework was observed after running the 1KB payload echo microservice on each framework & sending a number of requests at different concurrency levels to each service. The graph above shows the averaged out values after several runs for each framework. Latency results was observed using the Apache bench provided percentile values. Results were plotted for various concurrency levels the simple echo test. ![MeanLatency](perf-benchmark/graphs/meanlatency.png) Tests were run out of the box without any tuning on 32 core 64GB server in JVM v1.8.0_60 with default configuration. More details about the performance test can found [here](perf-benchmark). ## Hello world with MSF4J It is really easy to define & deploy a Java microservice using WSO2 MSF4J. You simply need to annotate your service and deploy it using a single line of code. Let's get started by writing a hello world MSF4J microservice. You can use the [msf4j-microservice](archetypes) Maven [archetype](http://maven.apache.org/archetype/maven-archetype-plugin/generate-mojo.html) to create your first MSF4J project. Make sure you have JDK 1.8 and Maven 3.x installed, & run the following command. ``` mvn archetype:generate -DarchetypeGroupId=org.wso2.msf4j \ -DarchetypeArtifactId=msf4j-microservice -DarchetypeVersion=2.6.2 \ -DgroupId=org.example -DartifactId=Hello-Service -Dversion=0.1-SNAPSHOT \ -Dpackage=org.example.service -DserviceClass=HelloService ``` This will generate a project structure for you to quickly get started. Next navigate to the Hello-Service directory. You will find a pom.xml file and also an src directory. #### pom.xml This pom file inherits from the msf4j-service/pom.xml. It provides a way of setting things up quickly with minimum amount of configuration. Click [here](poms/msf4j-service) for more information. ```xml org.wso2.msf4j msf4j-service 2.6.2 4.0.0 org.example Hello-Service 0.1-SNAPSHOT WSO2 MSF4J Microservice org.example.service.Application ``` You don't need to change anything in this pom.xml file. #### HelloService.java Change the org.example.service.HelloService class as follows to echo back the name input parameter. You can remove the auto generated code and replace it with the following code segment: ```java package org.example.service; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; @Path("/hello") public class HelloService { @GET @Path("/{name}") public String hello(@PathParam("name") String name) { return "Hello " + name; } } ``` #### Application.java This is the one-liner to deploy your service using WSO2 MSF4J. ```java public class Application { public static void main(String[] args) { new MicroservicesRunner() .deploy(new HelloService()) .start(); } } ``` You can also pass in the port(s) as an argument to the MicroservicesRunner class constructor. When passing the port(s) as an argument, by default it binds to 0.0.0.0 host. Use "msf4j.host" environment variable to override the host value. ### Build the Service Run the following Maven command. This will create the fat jar **Hello-Service-0.1-SNAPSHOT.jar** in the **target** directory. ``` mvn package ``` This fat jar is a jar file that contains your microservice as well as all its dependencies. ### Run the Service You just have to run the following command to get your service up and running. ``` java -jar target/Hello-Service-*.jar ``` ### Test the Service with cURL Run the following command or simply go to [http://localhost:8080/hello/wso2] (http://localhost:8080/hello/wso2) from your browser. ``` curl http://localhost:8080/hello/wso2 ``` You should see a response that prints "Hello wso2" ## Supported Annotations In this section, we will look at the annotations used in MSF4J microservices. As you may have already noticed, we support a subset of the JAXRS annotations. ### Class level annotations ##### @Path Root path for resource methods. All the paths specified in the resource methods will be sub paths of this. ##### @Consumes Default consume media type(s) for resource methods. The resource methods that do not specify @Consume annotation will inherit this consume media type(s). ##### @Produces Default produce media type(s) for resource methods. The resource methods that do not specify @Produce annotation will inherit this produce media type(s). ### Method level annotations ##### @Path Endpoint of the resource method relative to @Path of the container resource class. ##### @Consumes Media type(s) that the method can consume. This overrides the class level @Consumes media types. ##### @Produces Media type(s) that is produced by the method. This overrides the class level @Produces media types. ##### @GET HTTP GET method. Specify that the resource method supports HTTP GET method. ##### @PUT HTTP PUT method. Specify that the resource method supports HTTP PUT method. ##### @POST HTTP POST method. Specify that the resource method supports HTTP POST method. ##### @DELETE HTTP DELETE method. Specify that the resource method supports HTTP DELETE method. ##### @HEAD HTTP HEAD method. Specify that the resource method supports HTTP HEAD method. ##### @OPTIONS HTTP OPTIONS method. Specify that the resource method supports HTTP OPTIONS method. ### Parameter level annotations ##### @DefaultValue Specify a default value for a resource method parameter. The value will be automatically converted to the corresponding parameter's type. ##### @Context Inject additional objects to a resource method. Currently supports injection of the following objects. * org.wso2.msf4j.Request - This object can be used to access properties of the HTTP request. The transport session (org.wso2.msf4j.Session) can also be accessed via org.wso2.msf4j.Request#getSession(). See the [Session-aware service](samples/http-session) sample. * org.wso2.msf4j.Response - This object can be used to send HTTP responses. You can make responses more clean way by returning an instance of javax.ws.rs.core.Response or a POJO. See the [StockQuote-Service] (samples/stockquote/fatjar) sample. * org.wso2.msf4j.HttpStreamer - This object can be used to stream a chunked request body and process it while the request is streaming. See the [FileServer](samples/fileserver) sample. * org.wso2.msf4j.formparam.FormParamIterator - This object can be used to stream a HTML form submission request body and process it while the request is streaming. See the [FormParam](samples/formparam) sample. ##### @PathParam /StockQuote/{symbol} to get value of symbol. The value will be automatically converted to the corresponding parameter type and assigned to that parameter. ##### @QueryParam /Students?age=18 to get value of age. The value will be automatically converted to the corresponding parameter type and assigned to that parameter. ##### @HeaderParam To read HTTP request header values. The value will be automatically converted to the corresponding parameter type and assigned to that parameter. ##### @CookieParam Extracts the value from the specified cookie, converts to the corresponding parameter type and assigns the value to that parameter. ##### @FormParam To support HTML form submission with application/x-www-form-urlencoded and multipart/form-data The value will be automatically converted to the corresponding parameter type and assigned to that parameter ##### @FormDataParam To support complex form submission with multipart/form-data content type. E.g file uploads and beans. The values will be automatically converted to the corresponding parameter type and assigned to that parameter ### Lifecycle Callback Methods Support following Java lifecycle callback method annotations. ##### @PostConstruct Invoke by the container on newly constructed service instances after all dependency injection has completed and before transport starts. ##### @PreDestroy Invoke by the container during server shutdown before the container removes the service instance. For a detailed example, check out the lifecycle sample [here](https://github.com/wso2/msf4j/tree/master/samples/lifecycle). ## MSF4J Interceptors Please do refer the following for complete instructions. * [General instructions](/samples/interceptor/README.md) * [MSF4J Interceptors - Stand alone mode instructions](/samples/interceptor/fatjar-interceptor-service/README.md) * [MSF4J Interceptors - Deployable Jar mode instructions ](/samples/interceptor/deployable-jar-interceptor-service/README.md) * [MSF4J Interceptors - OSGi mode instructions](/samples/interceptor/osgi-interceptor-service/README.md) * [MSF4J Interceptors with MSF4J Spring - Fat Jar mode](/samples/interceptor/spring-fatjar-interceptor-service/README.md) ## Develop and configure MSF4J services using Spring framework Spring is a popular Java application development framework which supports concepts like Dependency Injection(DI) and Convention over Configuration. Spring support for MSF4J provides following features. 1. Develop MSF4J services as Spring beans 2. Develop and configure MSF4J components such as Interceptors and ExceptionMappers using Spring. 3. Use Annotation or XML based Spring configuration to configure internals of MSF4J framework such as ports, SSL etc. Following example illustrates how to use Spring annotations together with MSF4J annotations to build a RESTful service. The main advantage here is service developers can consume Spring features such as dependency injection , Spring AOP etc. in the Spring way. ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.ws.rs.GET; import javax.ws.rs.Path; @Component @Path("/greeting") public class Hello { @Autowired private HelloService helloService; @GET public String message() { return helloService.hello(" World"); } } ``` For further details refer Spring [Helloworld sample](samples/spring-helloworld). ## Annotations for Analytics In this section, we will look at the annotations available for analytics purposes MSF4J microservices. There are annotations for Metrics and HTTP Monitoring. You will need to configure [analytics](analytics) when you want to publish Metrics and HTTP Monitoring events to WSO2 Data Analytics Server (DAS). The Metrics data will be published to WSO2 DAS periodically. However the HTTP Monitoring events are published on each request. For a detailed example, check out the [Metrics and HTTP Monitoring sample](samples/http-monitoring). ### Metrics Annotations You can use the Metrics annnotations in your MSF4J microservices to understand how your microservices work in production. There are three metrics annotations supported. Those are @Counted, @Metered and @Timed. Each metric must have a unique name and the MSF4J will use the fully qualified method name as the metric name by default. You can give a custom name by using the "name" parameter. This custom name will be appended to the fully qualified method name with a dot character. You can set the "absolute" parameter to "true" if you want use only the given name as the metric name. For example, see following table to understand how the final metric name is built by the Metrics runtime. Let's asssume that the annotation is added to the "hello" method of "HelloService" shown above. | Metrics Annotation | Metric Name | | ------------------------------------------------ | --------------------------------------------------- | | @Counted | org.example.service.HelloService.hello | | @Counted(name = "helloCounter") | org.example.service.HelloService.hello.helloCounter | | @Counted(name = "helloCounter", absolute = true) | helloCounter | The Metrics annotations can be used at the Method level and Class level. You can also configure a level in Metrics Annotations. For more information about Metric Levels, see [WSO2 Carbon Metrics](https://github.com/wso2/carbon-metrics). In MSF4J, you can load the metrics level configuration via the System Property named `metrics.level.conf`. ##### @Counted Count the method invocations. There is a parameter named "monotonic" and it is set to false by default. This means that the counter is decremented when the method returns. This is useful to count the current invocations of the annotated method. Use `@Counted(monotonic = true)` when you want the counter to increase monotonically. This is useful to count the total invocations of the annotated method ##### @Metered Measure the rate of method invocations. This also keeps a count of the total invocations of the annotated method. ##### @Timed Maintain a histogram of durations of each method invocation. This also measures the rate of method invocations and keeps a count of the total invocations of the annotated method. ### HTTP Monitoring Annotation You can use the annotation provided for HTTP Monitoring when you want to monitor each HTTP request and see the summary in "HTTP Monitoring Dashboard". ##### @HTTPMonitored Monitor each HTTP request. This annotation can be used at the Class level and the Method level. ### HTTP Message Tracing MSF4J supports visual message tracing through user friendly dashboards in WSO2 DAS or Zipkin. MSF4J message tracing provides a detailed insight to the complex microservices interactions in a system making monitoring, trouble shooting and optimization of microservices very easy. Please check [WSO2 DAS Tracing](samples/message-tracing/das) and [Zipkin Tracing](samples/message-tracing/zipkin) samples for more details. ### Swagger Annotations [Swagger](http://swagger.io/getting-started/) is a standard, language-agnostic interface to REST APIs which allows both humans and computers to discover and understand the capabilities of the service without access to source code, documentation, or through network traffic inspection. MSF4J supports all [Swagger annotations](https://github.com/swagger-api/swagger-core/wiki/Annotations-1.5.X) out of the box. To enable swagger support you need to add the following dependency to your project ```xml org.wso2.msf4j msf4j-swagger 2.6.2 ``` In order to retrieve Swagger definitions of your microservice, go to
http://<host>:<port>/swagger?path=<service_base_path>. e.g. [http://localhost:8080/swagger?path=/hello](http://localhost:8080/swagger?path=/hello) To retrieve Swagger definitions of all microservices in your runtime, go to http://<host>:<port>/swagger. e.g. [http://localhost:8080/swagger](http://localhost:8080/swagger) NOTE: Even without any Swagger annotation, default Swagger definitions will be generated using the JAXRS annotation in your MSF4J microservice. The [StockQuote sample](samples/stockquote/fatjar) demonstrates Swagger annotations in action. ### ExceptionMapper MSF4J supports [JAXRS ExceptionMapper](https://docs.oracle.com/javaee/7/api/javax/ws/rs/ext/ExceptionMapper.html) which allows creation of custom responses when exceptions are thrown from MSF4J services. The [StockQuote sample](samples/stockquote/fatjar) demonstrates ExceptionMapper in action. The following code segment shows how ExceptionMappers are registered with the MSF4J runtime. ```java new MicroservicesRunner().addExceptionMapper(new SymbolNotFoundMapper(), new DuplicateSymbolMapper()); ``` ### Circuit Breaker Nygard's circuit breaker pattern is supported in MSF4J using the Netflix Hystrix library. For more details see the [circuit breaker sample](samples/circuitbreaker) ### Complete Feature List * Annotation based definition of microservices * High performance Netty based transport * WSO2 Developer Studio based tooling for microservices development starting from a Swagger API definition * Generate Swagger definition using [Swagger annotations](https://github.com/swagger-api/swagger-core/wiki/Annotations-1.5.X) * Ability to develop and Configure MSF4J services using Spring framework * HTTP request & response streaming including support for javax.ws.rs.core.StreamingOutput. * [ExceptionMapper]((https://docs.oracle.com/javaee/7/api/javax/ws/rs/ext/ExceptionMapper.html)) * Support for metrics & visualization of metrics using WSO2 Data Analytics Server (DAS) dashboards * Message tracing with WSO2 DAS or Zipkin to get a detailed visual insight to the microservices interactions * Supports circuit breaker using Netflix Hystrix. * Support for securing microservices * Integration with rendering engines such as Mustache * Comprehensive samples demonstrating how to develop microservices based solutions ================================================ FILE: analytics/README.md ================================================ # WSO2 MSF4J - Analytics with WSO2 Data Analytics Server (DAS) This directory contains the files related to publishing metrics to WSO2 Data Analytics Server (DAS) Download WSO2 DAS ------------------------------------------ [Download](http://wso2.com/products/data-analytics-server/) WSO2 DAS and unpack it to some directory. This will be the DAS_HOME directory. Configure DAS ------------------------------------------ Run "das-setup/setup.sh" to setup DAS. Note that the DAS Home directory in the above step has to be provided as an input to that script. The setup script will also copy the already built MSF4J HTTP Monitoring Carbon App (CAPP) to DAS. Start DAS ------------------------------------------ From DAS_HOME, run, bin/wso2server.sh to start DAS and make sure that it starts properly. Run a sample that publishes data to DAS ------------------------------------------ Run the [HTTP Monitoring Sample](../samples/http-monitoring) included in the distribution. This sample will publish data to DAS. Accessing the dashboard ------------------------------------------ Go to [http://127.0.0.1:9763/monitoring/](http://127.0.0.1:9763/monitoring/). If everything works fine, you should see the metrics & information related to your microservices on this dashboard. Please allow a few minutes for the dashboard to be updated because the dashboard update batch task runs every few minutes. For Advanced Users ------------------------------------------ If you are an advanced WSO2 DAS user, you can go to the DAS Management Console at [https://localhost:9443/](https://localhost:9443/) and login with username/password admin/admin. Once you login, you can view and manually execute the *http_event_script* in the console to immediately see the results in the dashboard. The sources for MSF4J HTTP Monitoring Carbon Application (CAPP) can be found inside "msf4j_http_monitoring_capp_source" directory. This CAPP is already built and copied to WSO2 DAS when you Configure DAS as mentioned above. ================================================ FILE: analytics/das-setup/setup.sh ================================================ #!/bin/bash # Copyright 2015 WSO2 Inc. (http://wso2.org) # # 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. # # ---------------------------------------------------------------------------- # A script for setting up DAS # ---------------------------------------------------------------------------- set -e config_dir=$(dirname "$0") das_dir="" function help { echo "" echo "Usage: " echo "setup.sh -d " echo "" echo "-d: WSO2 Data Analytics Server (DAS) home directory" echo "" } checkcommand () { command -v $1 >/dev/null 2>&1 || { echo >&2 "Command $1 not found."; exit 1; } } while getopts "d:" opts do case $opts in d) das_dir=${OPTARG} ;; \?) help exit 1 ;; esac done if [[ ! -d $das_dir ]]; then echo "Please specify the WSO2 DAS Home." help exit 1 fi checkcommand unzip echo "Copying Carbon Apps to DAS." mkdir -p $das_dir/repository/deployment/server/carbonapps cp $config_dir/capps/*.car $das_dir/repository/deployment/server/carbonapps echo "Setting up HTTP Monitoring Dashboard." if [[ ! -d $das_dir/repository/deployment/server/jaggeryapps/monitoring ]]; then mkdir -p $das_dir/repository/deployment/server/jaggeryapps/monitoring unzip -q $config_dir/httpmon-dashboard/monitoring.zip -d $das_dir/repository/deployment/server/jaggeryapps/monitoring fi unzip -q -o $config_dir/httpmon-dashboard/modules.zip -d $das_dir/modules/ echo "Completed..." ================================================ FILE: analytics/msf4j-analytics/pom.xml ================================================ 4.0.0 org.wso2.msf4j msf4j-parent 2.8.14-SNAPSHOT ../../poms/parent/pom.xml msf4j-analytics bundle WSO2 MSF4J Analytics WSO2 MSF4J Analytics https://github.com/wso2/msf4j org.wso2.msf4j msf4j-core org.wso2.carbon.metrics org.wso2.carbon.metrics.core org.wso2.carbon.metrics org.wso2.carbon.metrics.jdbc.core org.wso2.carbon.metrics org.wso2.carbon.metrics.das.core org.wso2.msf4j msf4j-analytics-common org.wso2.carbon.config org.wso2.carbon.config ${carbon.config.version} org.testng testng test org.apache.maven.plugins maven-compiler-plugin org.apache.rat apache-rat-plugin org.apache.maven.plugins maven-checkstyle-plugin org.apache.maven.plugins maven-surefire-plugin org.apache.maven.plugins maven-deploy-plugin org.apache.felix maven-bundle-plugin org.wso2.carbon.config org.wso2.carbon.config.maven.plugin coverage org.apache.maven.plugins maven-surefire-plugin ${argLine} -Xmx512m org.jacoco jacoco-maven-plugin prepare-agent prepare-agent report prepare-package report org.jacoco jacoco-maven-plugin release org.apache.maven.plugins maven-source-plugin true attach-sources package jar-no-fork org.apache.maven.plugins maven-javadoc-plugin true http://docs.oracle.com/javaee/6/api/ *.internal.* Licensed under the Apache License, Version 2.0]]> attach-javadoc package jar org.apache.maven.plugins maven-gpg-plugin ${gpg.passphrase} ${gpg.useagent} sign org.apache.maven.plugins maven-source-plugin org.apache.maven.plugins maven-javadoc-plugin org.apache.maven.plugins maven-gpg-plugin org.wso2.msf4j.analytics.*;version="${msf4j.version}" javax.annotation.*, org.slf4j.*;version="${slf4j.version.range}", org.apache.commons.beanutils.*;version="${beanutils.version.range}", org.osgi.framework.*;version="${osgi.framework.import.version.range}", org.osgi.util.tracker; version="${osgi.service.tracker.import.version.range}", org.wso2.carbon.utils.*;version="${carbon.utils.package.import.version.range}", org.wso2.carbon.config.*;version="${carbon.config.package.import.version.range}", org.wso2.carbon.metrics.core.*;version="${carbon.metrics.version.range}", org.wso2.carbon.kernel.startupresolver.*;version="${carbon.kernel.version.range}", org.wso2.msf4j.*, org.wso2.msf4j.util.*, javax.ws.rs.*, osgi.service;objectClass="org.wso2.carbon.kernel.startupresolver.CapabilityProvider";capabilityName="org.wso2.msf4j.Interceptor" ================================================ FILE: analytics/msf4j-analytics/src/main/java/org/wso2/msf4j/analytics/AnalyticUtils.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.analytics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.carbon.config.ConfigProviderFactory; import org.wso2.carbon.config.ConfigurationException; import org.wso2.carbon.config.provider.ConfigProvider; import org.wso2.msf4j.analytics.internal.DataHolder; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Paths; /** * Utils contains utility methods to use analytic purposes */ public class AnalyticUtils { private static final Logger logger = LoggerFactory.getLogger(AnalyticUtils.class); private static final String DEPLOYMENT_YAML_SYS_PROPERTY = "msf4j.conf"; private static final String DEPLOYMENT_YAML_FILE = "deployment.yaml"; /** * Retrieve Configuration Provider Object to read analytic configurations * @return configProvider object */ public static ConfigProvider getConfigurationProvider() { ConfigProvider configProvider = DataHolder.getInstance().getConfigProvider(); if (configProvider == null) { if (DataHolder.getInstance().getBundleContext() != null) { throw new RuntimeException( "Failed to populate HTTPMonitoringConfig Configuration. Config Provider is Null."); } //Standalone mode String deploymentYamlPath = System.getProperty(DEPLOYMENT_YAML_SYS_PROPERTY); if (deploymentYamlPath == null || deploymentYamlPath.isEmpty()) { logger.info("System property '" + DEPLOYMENT_YAML_SYS_PROPERTY + "' is not set. Default deployment.yaml file will be used."); deploymentYamlPath = DEPLOYMENT_YAML_FILE; try (InputStream configInputStream = AnalyticUtils.class.getClassLoader() .getResourceAsStream(DEPLOYMENT_YAML_FILE)) { if (configInputStream == null) { throw new RuntimeException("Couldn't find " + deploymentYamlPath); } if (Files.notExists(Paths.get(deploymentYamlPath))) { Files.copy(configInputStream, Paths.get(deploymentYamlPath)); } } catch (IOException e) { throw new RuntimeException("Couldn't read configuration from file " + deploymentYamlPath, e); } } else if (!Files.exists(Paths.get(deploymentYamlPath))) { throw new RuntimeException("Couldn't find " + deploymentYamlPath); } try { configProvider = ConfigProviderFactory.getConfigProvider(Paths.get(deploymentYamlPath), null); DataHolder.getInstance().setConfigProvider(configProvider); } catch (ConfigurationException e) { throw new RuntimeException("Error loading deployment.yaml Configuration", e); } } return configProvider; } } ================================================ FILE: analytics/msf4j-analytics/src/main/java/org/wso2/msf4j/analytics/httpmonitoring/HTTPMonitored.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.analytics.httpmonitoring; 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; /** * Monitor HTTP Requests. */ @Inherited @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.METHOD }) public @interface HTTPMonitored { boolean tracing() default false; } ================================================ FILE: analytics/msf4j-analytics/src/main/java/org/wso2/msf4j/analytics/httpmonitoring/HTTPMonitoringDataPublisher.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.analytics.httpmonitoring; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.carbon.databridge.agent.AgentHolder; import org.wso2.carbon.databridge.agent.DataPublisher; import org.wso2.carbon.databridge.agent.exception.DataEndpointAgentConfigurationException; import org.wso2.carbon.databridge.agent.exception.DataEndpointAuthenticationException; import org.wso2.carbon.databridge.agent.exception.DataEndpointConfigurationException; import org.wso2.carbon.databridge.agent.exception.DataEndpointException; import org.wso2.carbon.databridge.commons.Event; import org.wso2.carbon.databridge.commons.exception.TransportException; import org.wso2.carbon.databridge.commons.utils.DataBridgeCommonsUtils; import org.wso2.msf4j.analytics.httpmonitoring.config.model.DasConfig; import org.wso2.msf4j.util.SystemVariableUtil; import java.net.Inet4Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.net.UnknownHostException; import java.util.Enumeration; import java.util.Map; /** * A utility class to initialize/destroy HTTP Monitoring Data Publisher for DAS. */ public final class HTTPMonitoringDataPublisher { private static final Logger logger = LoggerFactory.getLogger(HTTPMonitoringDataPublisher.class); private static final String HTTP_MONITORING_STREAM = "org.wso2.msf4j.analytics.httpmonitoring"; private static final String VERSION = "1.0.0"; private static final String HTTP_MONITORING_STREAM_ID; private static final String SERVER_HOST_ADDRESS; private static final String SERVER_HOSTNAME; private static final String MICROSERVICE = "Microservice"; private DataPublisher dataPublisher; private Map arbitraryAttributes; static { HTTP_MONITORING_STREAM_ID = DataBridgeCommonsUtils.generateStreamId(HTTP_MONITORING_STREAM, VERSION); try { InetAddress localAddress = getLocalAddress(); SERVER_HOST_ADDRESS = localAddress.getHostAddress(); SERVER_HOSTNAME = localAddress.getHostName(); } catch (SocketException | UnknownHostException e) { throw new IllegalStateException("Cannot determine server host address", e); } } public HTTPMonitoringDataPublisher(DasConfig dasConfig) { init(dasConfig); // Destroy data publisher at shutdown Thread thread = new Thread(this::destroy); Runtime.getRuntime().addShutdownHook(thread); } private static InetAddress getLocalAddress() throws SocketException, UnknownHostException { Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); while (interfaces.hasMoreElements()) { NetworkInterface iface = interfaces.nextElement(); Enumeration addresses = iface.getInetAddresses(); if (iface.isUp()) { while (addresses.hasMoreElements()) { InetAddress addr = addresses.nextElement(); if (addr instanceof Inet4Address && !addr.isLoopbackAddress()) { return addr; } } } } return InetAddress.getLocalHost(); } private void init(DasConfig dasConfig) { if (logger.isInfoEnabled()) { logger.info("Initializing HTTP Monitoring Data Publisher"); } String type = dasConfig.getType(); String receiverURL = dasConfig.getReceiverURL(); String authURL = dasConfig.getAuthURL(); String username = dasConfig.getUsername(); String password = dasConfig.getPassword(); String dataAgentConfigPath = dasConfig.getDataAgentConfigPath(); if (type == null) { throw new IllegalArgumentException("Type cannot be null"); } if (receiverURL == null) { throw new IllegalArgumentException("Data Receiver URL cannot be null"); } if (username == null) { throw new IllegalArgumentException("Username cannot be null"); } if (password == null) { throw new IllegalArgumentException("Password cannot be null"); } if (dataAgentConfigPath == null) { throw new IllegalArgumentException("Data Agent Configuration Path cannot be null"); } AgentHolder.setConfigPath(dataAgentConfigPath); arbitraryAttributes = SystemVariableUtil.getArbitraryAttributes(); try { dataPublisher = new DataPublisher(type, receiverURL, authURL, username, password); } catch (DataEndpointAgentConfigurationException | DataEndpointException | DataEndpointConfigurationException | DataEndpointAuthenticationException | TransportException e) { throw new IllegalStateException("Error when initializing the Data Publisher", e); } } private void destroy() { if (dataPublisher != null) { try { dataPublisher.shutdownWithAgent(); } catch (DataEndpointException e) { logger.error("Error shutting down the data publisher with agent", e); } finally { dataPublisher = null; } } } public void publishEvent(HTTPMonitoringEvent httpMonitoringEvent) { Object[] meta = new Object[4]; meta[0] = httpMonitoringEvent.getTimestamp(); meta[1] = SERVER_HOST_ADDRESS; meta[2] = SERVER_HOSTNAME; meta[3] = MICROSERVICE; Object[] correlation = new Object[2]; correlation[0] = httpMonitoringEvent.getActivityId(); correlation[1] = httpMonitoringEvent.getParentRequest(); Object[] payload = new Object[11]; payload[0] = httpMonitoringEvent.getServiceClass(); payload[1] = httpMonitoringEvent.getServiceName(); payload[2] = httpMonitoringEvent.getServiceMethod(); payload[3] = httpMonitoringEvent.getRequestUri(); payload[4] = httpMonitoringEvent.getServiceContext(); payload[5] = httpMonitoringEvent.getHttpMethod(); payload[6] = httpMonitoringEvent.getContentType(); payload[7] = httpMonitoringEvent.getRequestSizeBytes(); payload[8] = httpMonitoringEvent.getReferrer(); payload[9] = httpMonitoringEvent.getResponseHttpStatusCode(); payload[10] = httpMonitoringEvent.getResponseTime(); Event event = new Event(HTTP_MONITORING_STREAM_ID, httpMonitoringEvent.getTimestamp(), meta, correlation, payload, arbitraryAttributes); dataPublisher.publish(event); } } ================================================ FILE: analytics/msf4j-analytics/src/main/java/org/wso2/msf4j/analytics/httpmonitoring/HTTPMonitoringEvent.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.analytics.httpmonitoring; /** * Monitoring Event. */ public class HTTPMonitoringEvent { private long timestamp; private String serverName; private String serverAddress; private long startNanoTime; private String serviceClass; private String serviceName; private String serviceContext; private String serviceMethod; private String requestUri; private String httpMethod; private String contentType; private Integer responseHttpStatusCode; private String referrer; private long responseTime; private long requestSizeBytes; private String activityId; private String parentRequest; public long getTimestamp() { return timestamp; } public void setTimestamp(long timestamp) { this.timestamp = timestamp; } public String getServerName() { return serverName; } public void setServerName(String serverName) { this.serverName = serverName; } public String getServerAddress() { return serverAddress; } public void setServerAddress(String serverAddress) { this.serverAddress = serverAddress; } public long getStartNanoTime() { return startNanoTime; } public void setStartNanoTime(long startNanoTime) { this.startNanoTime = startNanoTime; } public String getServiceClass() { return serviceClass; } public void setServiceClass(String serviceClass) { this.serviceClass = serviceClass; } public String getServiceName() { return serviceName; } public void setServiceName(String serviceName) { this.serviceName = serviceName; } public String getServiceContext() { return serviceContext; } public void setServiceContext(String serviceContext) { this.serviceContext = serviceContext; } public String getServiceMethod() { return serviceMethod; } public void setServiceMethod(String serviceMethod) { this.serviceMethod = serviceMethod; } public String getRequestUri() { return requestUri; } public void setRequestUri(String requestUri) { this.requestUri = requestUri; } public String getHttpMethod() { return httpMethod; } public void setHttpMethod(String httpMethod) { this.httpMethod = httpMethod; } public String getContentType() { return contentType; } public void setContentType(String contentType) { this.contentType = contentType; } public Integer getResponseHttpStatusCode() { return responseHttpStatusCode; } public void setResponseHttpStatusCode(Integer responseHttpStatusCode) { this.responseHttpStatusCode = responseHttpStatusCode; } public String getReferrer() { return referrer; } public void setReferrer(String referrer) { this.referrer = referrer; } public long getResponseTime() { return responseTime; } public void setResponseTime(long responseTime) { this.responseTime = responseTime; } public long getRequestSizeBytes() { return requestSizeBytes; } public void setRequestSizeBytes(long requestSizeBytes) { this.requestSizeBytes = requestSizeBytes; } public String getActivityId() { return activityId; } public void setActivityId(String activityId) { this.activityId = activityId; } public String getParentRequest() { return parentRequest; } public void setParentRequest(String parentRequest) { this.parentRequest = parentRequest; } } ================================================ FILE: analytics/msf4j-analytics/src/main/java/org/wso2/msf4j/analytics/httpmonitoring/HTTPMonitoringInterceptor.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.analytics.httpmonitoring; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.msf4j.Interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import org.wso2.msf4j.ServiceMethodInfo; import org.wso2.msf4j.analytics.httpmonitoring.config.HTTPMonitoringConfigBuilder; import org.wso2.msf4j.analytics.httpmonitoring.config.model.HTTPMonitoringConfig; import java.lang.reflect.Method; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import javax.ws.rs.Path; import javax.ws.rs.core.HttpHeaders; /** * Monitor HTTP Requests for methods with {@link HTTPMonitored} annotations. */ public class HTTPMonitoringInterceptor implements Interceptor { private static final Logger logger = LoggerFactory.getLogger(HTTPMonitoringInterceptor.class); public static final String REFERER = "Referer"; private Map map = new ConcurrentHashMap<>(); private final boolean enabled; private final HTTPMonitoringDataPublisher httpMonitoringDataPublisher; public HTTPMonitoringInterceptor() { if (logger.isDebugEnabled()) { logger.debug("Creating HTTP Monitoring Interceptor"); } HTTPMonitoringConfig httpMonitoringConfig = HTTPMonitoringConfigBuilder.build(); enabled = httpMonitoringConfig.isEnabled(); httpMonitoringDataPublisher = enabled ? new HTTPMonitoringDataPublisher(httpMonitoringConfig.getDas()) : null; } /** * Returns the final annotation that is application to the given method. For example, * the {@link HTTPMonitored} annotation can be mentioned in class level, and also in * the target method, but the method only have tracing enabled. Then we should get the * setting as tracing is disabled for that specific method. */ private HTTPMonitored extractFinalAnnotation(Method method) { HTTPMonitored httpMon = method.getAnnotation(HTTPMonitored.class); if (httpMon == null) { httpMon = method.getDeclaringClass().getAnnotation(HTTPMonitored.class); } return httpMon; } @Override public boolean preCall(Request request, Response responder, ServiceMethodInfo serviceMethodInfo) throws Exception { if (!enabled) { return true; } Method method = serviceMethodInfo.getMethod(); MethodInterceptor methodInterceptor = map.get(method); if (methodInterceptor == null || !methodInterceptor.annotationScanned) { HTTPMonitored httpMon = extractFinalAnnotation(method); Interceptor interceptor = null; if (httpMon != null) { interceptor = new HTTPInterceptor(httpMon.tracing()); } methodInterceptor = new MethodInterceptor(true, interceptor); map.put(method, methodInterceptor); } return methodInterceptor.preCall(request, responder, serviceMethodInfo); } @Override public void postCall(Request request, int status, ServiceMethodInfo serviceMethodInfo) throws Exception { if (!enabled) { return; } Method method = serviceMethodInfo.getMethod(); MethodInterceptor methodInterceptor = map.get(method); if (methodInterceptor != null) { methodInterceptor.postCall(request, status, serviceMethodInfo); } } private static class MethodInterceptor implements Interceptor { private final boolean annotationScanned; private final Interceptor interceptor; MethodInterceptor(boolean annotationScanned, Interceptor interceptor) { this.annotationScanned = annotationScanned; this.interceptor = interceptor; } @Override public boolean preCall(Request request, Response responder, ServiceMethodInfo serviceMethodInfo) throws Exception { if (interceptor != null) { return interceptor.preCall(request, responder, serviceMethodInfo); } return true; } @Override public void postCall(Request request, int status, ServiceMethodInfo serviceMethodInfo) throws Exception { if (interceptor != null) { interceptor.postCall(request, status, serviceMethodInfo); } } } private class HTTPInterceptor implements Interceptor { private static final String DEFAULT_TRACE_ID = "DEFAULT"; private static final String DEFAULT_PARENT_REQUEST = "DEFAULT"; private static final String MONITORING_EVENT = "MONITORING_EVENT"; private static final String ACTIVITY_ID = "activity-id"; private static final String PARENT_REQUEST = "parent-request"; private String serviceClass; private String serviceName; private String serviceMethod; private String servicePath; private boolean tracing; private HTTPInterceptor(boolean tracing) { this.tracing = tracing; } boolean isTracing() { return tracing; } private String generateTraceId() { return UUID.randomUUID().toString(); } private void handleTracing(Request request, HTTPMonitoringEvent httpMonitoringEvent) { String traceId, parentRequest; if (this.isTracing()) { traceId = request.getHeader(ACTIVITY_ID); if (traceId == null) { traceId = this.generateTraceId(); } parentRequest = request.getHeader(PARENT_REQUEST); } else { traceId = DEFAULT_TRACE_ID; parentRequest = DEFAULT_PARENT_REQUEST; } httpMonitoringEvent.setActivityId(traceId); httpMonitoringEvent.setParentRequest(parentRequest); } @Override public boolean preCall(Request request, Response responder, ServiceMethodInfo serviceMethodInfo) { HTTPMonitoringEvent httpMonitoringEvent = new HTTPMonitoringEvent(); httpMonitoringEvent.setTimestamp(System.currentTimeMillis()); httpMonitoringEvent.setStartNanoTime(System.nanoTime()); if (serviceClass == null) { Method method = serviceMethodInfo.getMethod(); Class serviceClass = method.getDeclaringClass(); this.serviceClass = serviceClass.getName(); serviceName = serviceClass.getSimpleName(); serviceMethod = method.getName(); if (serviceClass.isAnnotationPresent(Path.class)) { Path path = serviceClass.getAnnotation(Path.class); servicePath = path.value(); } } httpMonitoringEvent.setServiceClass(serviceClass); httpMonitoringEvent.setServiceName(serviceName); httpMonitoringEvent.setServiceMethod(serviceMethod); httpMonitoringEvent.setRequestUri(request.getUri()); httpMonitoringEvent.setServiceContext(servicePath); HttpHeaders httpHeaders = request.getHeaders(); httpMonitoringEvent.setHttpMethod(request.getHttpMethod()); httpMonitoringEvent.setContentType(httpHeaders.getHeaderString(HttpHeaders.CONTENT_TYPE)); String contentLength = httpHeaders.getHeaderString(HttpHeaders.CONTENT_LENGTH); if (contentLength != null) { httpMonitoringEvent.setRequestSizeBytes(Long.parseLong(contentLength)); } httpMonitoringEvent.setReferrer(httpHeaders.getHeaderString(REFERER)); this.handleTracing(request, httpMonitoringEvent); serviceMethodInfo.setAttribute(MONITORING_EVENT, httpMonitoringEvent); return true; } @Override public void postCall(Request request, int status, ServiceMethodInfo serviceMethodInfo) { HTTPMonitoringEvent httpMonitoringEvent = (HTTPMonitoringEvent) serviceMethodInfo.getAttribute(MONITORING_EVENT); httpMonitoringEvent.setResponseTime( TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - httpMonitoringEvent.getStartNanoTime())); httpMonitoringEvent.setResponseHttpStatusCode(status); httpMonitoringDataPublisher.publishEvent(httpMonitoringEvent); } } } ================================================ FILE: analytics/msf4j-analytics/src/main/java/org/wso2/msf4j/analytics/httpmonitoring/config/HTTPMonitoringConfigBuilder.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.analytics.httpmonitoring.config; import org.wso2.carbon.config.ConfigurationException; import org.wso2.carbon.config.provider.ConfigProvider; import org.wso2.msf4j.analytics.AnalyticUtils; import org.wso2.msf4j.analytics.httpmonitoring.config.model.HTTPMonitoringConfig; /** * Build {@link HTTPMonitoringConfig} from the YAML file */ public final class HTTPMonitoringConfigBuilder { public static HTTPMonitoringConfig build() { HTTPMonitoringConfig configurationObject; ConfigProvider configProvider = AnalyticUtils.getConfigurationProvider(); try { configurationObject = configProvider.getConfigurationObject(HTTPMonitoringConfig.class); } catch (ConfigurationException e) { throw new RuntimeException( "Error while loading " + HTTPMonitoringConfig.class.getName() + " from config provider", e); } return configurationObject; } } ================================================ FILE: analytics/msf4j-analytics/src/main/java/org/wso2/msf4j/analytics/httpmonitoring/config/model/DasConfig.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.analytics.httpmonitoring.config.model; import org.wso2.carbon.config.annotation.Configuration; import org.wso2.carbon.config.annotation.Element; import org.wso2.carbon.config.annotation.Ignore; /** * Configuration for connecting with Data Analytics Server (DAS) */ @Configuration(description = "DAS configuration") public class DasConfig { @Element(description = "The type used with Data Publisher") private String type = "thrift"; @Element(description = "Data Receiver URL used by the Data Publisher") private String receiverURL = "tcp://localhost:7611"; @Ignore private String authURL; private String username = "admin"; private String password = "admin"; @Element(description = "The path for Data Bridge Agent configuration") private String dataAgentConfigPath = "data-agent-config.xml"; public String getType() { return type; } public void setType(String type) { this.type = type; } public String getReceiverURL() { return receiverURL; } public void setReceiverURL(String receiverURL) { this.receiverURL = receiverURL; } public String getAuthURL() { return authURL; } public void setAuthURL(String authURL) { this.authURL = authURL; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getDataAgentConfigPath() { return dataAgentConfigPath; } public void setDataAgentConfigPath(String dataAgentConfigPath) { this.dataAgentConfigPath = dataAgentConfigPath; } } ================================================ FILE: analytics/msf4j-analytics/src/main/java/org/wso2/msf4j/analytics/httpmonitoring/config/model/HTTPMonitoringConfig.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.analytics.httpmonitoring.config.model; import org.wso2.carbon.config.annotation.Configuration; import org.wso2.carbon.config.annotation.Element; /** * Configuration for HTTP Monitoring */ @Configuration(namespace = "wso2.msf4j.analytics.configuration", description = "MSF4J Analytics configuration") public class HTTPMonitoringConfig { @Element(description = "Whether HTTP Monitoring is enables or not") private boolean enabled = false; @Element(description = "Configuration for DAS") private DasConfig das = new DasConfig(); public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public DasConfig getDas() { return das; } public void setDas(DasConfig das) { this.das = das; } } ================================================ FILE: analytics/msf4j-analytics/src/main/java/org/wso2/msf4j/analytics/internal/AnalyticsSC.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.analytics.internal; import org.osgi.framework.BundleContext; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.ReferenceCardinality; import org.osgi.service.component.annotations.ReferencePolicy; import org.wso2.carbon.config.provider.ConfigProvider; import org.wso2.msf4j.Interceptor; import org.wso2.msf4j.analytics.httpmonitoring.HTTPMonitoringInterceptor; /** * * Bundle Activator for msf4j-analytics bundle. */ @Component( name = "org.wso2.msf4j.analytics.httpmonitoringsc", immediate = true, property = { "componentName=msf4j-analytics-sc" } ) public class AnalyticsSC { @Reference( name = "carbon-config", service = ConfigProvider.class, cardinality = ReferenceCardinality.MANDATORY, policy = ReferencePolicy.DYNAMIC, unbind = "unregisterConfigProvider" ) protected void registerConfigProvider(ConfigProvider configProvider) { DataHolder.getInstance().setConfigProvider(configProvider); } protected void unregisterConfigProvider(ConfigProvider configProvider) { DataHolder.getInstance().setConfigProvider(null); } @Activate protected void activate(BundleContext bundleContext) { DataHolder.getInstance().setBundleContext(bundleContext); bundleContext.registerService(Interceptor.class, new HTTPMonitoringInterceptor(), null); } @Deactivate public void stop(BundleContext bundleContext) throws Exception { DataHolder.getInstance().setBundleContext(null); } } ================================================ FILE: analytics/msf4j-analytics/src/main/java/org/wso2/msf4j/analytics/internal/DataHolder.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.analytics.internal; import org.osgi.framework.BundleContext; import org.wso2.carbon.config.provider.ConfigProvider; /** * Data holder for analytics bundle. */ public class DataHolder { private static final DataHolder instance = new DataHolder(); private ConfigProvider configProvider; public ConfigProvider getConfigProvider() { return configProvider; } public void setConfigProvider(ConfigProvider configProvider) { this.configProvider = configProvider; } public BundleContext getBundleContext() { return bundleContext; } public void setBundleContext(BundleContext bundleContext) { this.bundleContext = bundleContext; } private BundleContext bundleContext; public static DataHolder getInstance() { return instance; } } ================================================ FILE: analytics/msf4j-analytics/src/main/java/org/wso2/msf4j/analytics/internal/InterceptorCapabilityProvider.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.analytics.internal; import org.osgi.service.component.annotations.Component; import org.wso2.carbon.kernel.startupresolver.CapabilityProvider; /** * This class signals Startup Order Resolver module in kernel that this bundle provides * two services of type {@code Interceptor} */ @Component( name = "org.wso2.msf4j.analytics.internal.InterceptorCapabilityProvider", immediate = true, property = "capabilityName=org.wso2.msf4j.Interceptor" ) public class InterceptorCapabilityProvider implements CapabilityProvider { /** * Returns the count of {@code Interceptor} OSGi services registered by this bundle. *

* This bundle registers two Interceptors * 1. {@code HTTPMonitoringInterceptor} * 2. {@code MetricsInterceptor} * * But here we return count as one, due to a bug in the startup launcher in Kernel. * * @return count of {@code Interceptor} services registered by this bundle. */ @Override public int getCount() { return 2; } } ================================================ FILE: analytics/msf4j-analytics/src/main/java/org/wso2/msf4j/analytics/metrics/Metrics.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.analytics.metrics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.carbon.metrics.core.MetricManagementService; import org.wso2.carbon.metrics.core.MetricService; import org.wso2.msf4j.analytics.AnalyticUtils; /** * A utility class to keep Metric Services. */ public final class Metrics { private static final Logger logger = LoggerFactory.getLogger(Metrics.class); private volatile MetricService metricService; private volatile MetricManagementService metricManagementService; private Metrics() { } /** * Initializes the Metrics instance */ private static class MetricsHolder { private static final Metrics INSTANCE = new Metrics(); } /** * This returns the Metrics singleton instance. * * @return The Metrics instance */ public static Metrics getInstance() { return MetricsHolder.INSTANCE; } /** * Initialize metric services */ private void initializeServices() { if (logger.isInfoEnabled()) { logger.info("Initializing Metrics Services"); } org.wso2.carbon.metrics.core.Metrics metrics = new org.wso2.carbon.metrics.core.Metrics(AnalyticUtils.getConfigurationProvider()); // Activate metrics metrics.activate(); metricService = metrics.getMetricService(); metricManagementService = metrics.getMetricManagementService(); // Deactivate Metrics at shutdown Thread thread = new Thread(() -> metrics.deactivate()); Runtime.getRuntime().addShutdownHook(thread); } /** * Returns the {@link MetricService} * * @return The {@link MetricService} instance */ public MetricService getMetricService() { if (metricService == null) { synchronized (this) { if (metricService == null) { initializeServices(); } } } return metricService; } /** * Set the {@link MetricService} service * * @param metricService The {@link MetricService} reference */ void setMetricService(MetricService metricService) { this.metricService = metricService; } /** * Returns the {@link MetricManagementService} * * @return The {@link MetricManagementService} instance */ public MetricManagementService getMetricManagementService() { if (metricManagementService == null) { synchronized (this) { if (metricManagementService == null) { initializeServices(); } } } return metricManagementService; } /** * Set the {@link MetricManagementService} service * * @param metricManagementService The {@link MetricManagementService} reference */ void setMetricManagementService(MetricManagementService metricManagementService) { this.metricManagementService = metricManagementService; } } ================================================ FILE: analytics/msf4j-analytics/src/main/java/org/wso2/msf4j/analytics/metrics/MetricsComponent.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.analytics.metrics; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.ReferenceCardinality; import org.osgi.service.component.annotations.ReferencePolicy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.carbon.metrics.core.MetricManagementService; import org.wso2.carbon.metrics.core.MetricService; /** * Metrics OSGi Component to Initialize/Destroy Metrics. */ @Component(name = "org.wso2.msf4j.analytics.metrics.MetricsComponent") public class MetricsComponent { private static final Logger logger = LoggerFactory.getLogger(MetricsComponent.class); /** * This is the activation method of MetricsComponent. This will be called when its references are * satisfied. */ @Activate protected void activate() { if (logger.isInfoEnabled()) { logger.info("Metrics Component is activated"); } } /** * This is the deactivation method of MetricsComponent. */ @Deactivate protected void deactivate() { if (logger.isInfoEnabled()) { logger.info("Metrics Component is deactivated"); } } /** * This bind method will be called when {@link MetricService} is registered. * * @param metricService The {@link MetricService} instance registered as an OSGi service */ @Reference( name = "carbon.metrics.service", service = MetricService.class, cardinality = ReferenceCardinality.MANDATORY, policy = ReferencePolicy.DYNAMIC, unbind = "unsetMetricService" ) protected void setMetricService(MetricService metricService) { if (logger.isDebugEnabled()) { logger.debug("Setting MetricService reference"); } Metrics.getInstance().setMetricService(metricService); } /** * This is the unbind method which gets called at the un-registration of {@link MetricService} * * @param metricService The {@link MetricService} instance registered as an OSGi service */ protected void unsetMetricService(MetricService metricService) { Metrics.getInstance().setMetricService(null); } /** * This bind method will be called when {@link MetricManagementService} is registered. * * @param metricManagementService The {@link MetricManagementService} instance registered as an OSGi service */ @Reference( name = "carbon.metrics.management.service", service = MetricManagementService.class, cardinality = ReferenceCardinality.MANDATORY, policy = ReferencePolicy.DYNAMIC, unbind = "unsetMetricManagementService" ) protected void setMetricManagementService(MetricManagementService metricManagementService) { if (logger.isDebugEnabled()) { logger.debug("Setting MetricManagementService reference"); } Metrics.getInstance().setMetricManagementService(metricManagementService); } /** * This is the unbind method which gets called at the un-registration of {@link MetricManagementService} * * @param metricManagementService The {@link MetricManagementService} instance registered as an OSGi service */ protected void unsetMetricManagementService(MetricManagementService metricManagementService) { Metrics.getInstance().setMetricManagementService(null); } } ================================================ FILE: analytics/msf4j-analytics/src/main/java/org/wso2/msf4j/analytics/metrics/MetricsInterceptor.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.analytics.metrics; import org.osgi.service.component.annotations.Component; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.carbon.metrics.core.Counter; import org.wso2.carbon.metrics.core.Meter; import org.wso2.carbon.metrics.core.MetricAnnotation; import org.wso2.carbon.metrics.core.Timer; import org.wso2.carbon.metrics.core.annotation.Counted; import org.wso2.carbon.metrics.core.annotation.Metered; import org.wso2.carbon.metrics.core.annotation.Timed; import org.wso2.msf4j.Interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import org.wso2.msf4j.ServiceMethodInfo; import java.lang.reflect.Method; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; /** * Collecting Metrics via annotations. */ @Component( name = "org.wso2.msf4j.analytics.metrics.MetricsInterceptor", service = Interceptor.class ) public class MetricsInterceptor implements Interceptor { private static final Logger logger = LoggerFactory.getLogger(MetricsInterceptor.class); private Map map = new ConcurrentHashMap<>(); public MetricsInterceptor() { if (logger.isDebugEnabled()) { logger.debug("Creating Metrics Interceptor"); } } private Timed getTimedAnnotation(Method method) { Timed annotation = method.getAnnotation(Timed.class); if (annotation == null) { annotation = method.getDeclaringClass().getAnnotation(Timed.class); } return annotation; } private Metered getMeteredAnnotation(Method method) { Metered annotation = method.getAnnotation(Metered.class); if (annotation == null) { annotation = method.getDeclaringClass().getAnnotation(Metered.class); } return annotation; } private Counted getCountedAnnotation(Method method) { Counted annotation = method.getAnnotation(Counted.class); if (annotation == null) { annotation = method.getDeclaringClass().getAnnotation(Counted.class); } return annotation; } @Override public boolean preCall(Request request, Response responder, ServiceMethodInfo serviceMethodInfo) throws Exception { Method method = serviceMethodInfo.getMethod(); MethodInterceptors methodInterceptors = map.get(method); if (methodInterceptors == null || !methodInterceptors.annotationScanned) { List interceptors = new CopyOnWriteArrayList<>(); Timed timed = getTimedAnnotation(method); if (timed != null) { Timer timer = MetricAnnotation.timer(Metrics.getInstance().getMetricService(), timed, method); Interceptor interceptor = new TimerInterceptor(timer); interceptors.add(interceptor); } Metered metered = getMeteredAnnotation(method); if (metered != null) { Meter meter = MetricAnnotation.meter(Metrics.getInstance().getMetricService(), metered, method); Interceptor interceptor = new MeterInterceptor(meter); interceptors.add(interceptor); } Counted counted = getCountedAnnotation(method); if (counted != null) { Counter counter = MetricAnnotation.counter(Metrics.getInstance().getMetricService(), counted, method); Interceptor interceptor = new CounterInterceptor(counter, counted.monotonic()); interceptors.add(interceptor); } methodInterceptors = new MethodInterceptors(true, interceptors); map.put(method, methodInterceptors); } return methodInterceptors.preCall(request, responder, serviceMethodInfo); } @Override public void postCall(Request request, int status, ServiceMethodInfo serviceMethodInfo) throws Exception { Method method = serviceMethodInfo.getMethod(); MethodInterceptors methodInterceptors = map.get(method); if (methodInterceptors != null) { methodInterceptors.postCall(request, status, serviceMethodInfo); } } private static class MethodInterceptors implements Interceptor { private final boolean annotationScanned; private Interceptor[] interceptors; MethodInterceptors(boolean annotationScanned, List interceptors) { this.annotationScanned = annotationScanned; if (!interceptors.isEmpty()) { this.interceptors = interceptors.toArray(new Interceptor[interceptors.size()]); } } @Override public boolean preCall(Request request, Response responder, ServiceMethodInfo serviceMethodInfo) throws Exception { if (interceptors != null) { for (Interceptor interceptor : interceptors) { interceptor.preCall(request, responder, serviceMethodInfo); } } return true; } @Override public void postCall(Request request, int status, ServiceMethodInfo serviceMethodInfo) throws Exception { if (interceptors != null) { for (Interceptor interceptor : interceptors) { interceptor.postCall(request, status, serviceMethodInfo); } } } } private static class TimerInterceptor implements Interceptor { private final Timer timer; private static final String TIMER_CONTEXT = "TIMER_CONTEXT"; private TimerInterceptor(Timer timer) { this.timer = timer; } @Override public boolean preCall(Request request, Response responder, ServiceMethodInfo serviceMethodInfo) { Timer.Context context = timer.start(); serviceMethodInfo.setAttribute(TIMER_CONTEXT, context); return true; } @Override public void postCall(Request request, int status, ServiceMethodInfo serviceMethodInfo) { Timer.Context context = (Timer.Context) serviceMethodInfo.getAttribute(TIMER_CONTEXT); context.stop(); } } private static class MeterInterceptor implements Interceptor { private final Meter meter; private MeterInterceptor(Meter meter) { this.meter = meter; } @Override public boolean preCall(Request request, Response responder, ServiceMethodInfo serviceMethodInfo) { meter.mark(); return true; } @Override public void postCall(Request request, int status, ServiceMethodInfo serviceMethodInfo) { } } private static class CounterInterceptor implements Interceptor { private final Counter counter; private final boolean monotonic; private CounterInterceptor(Counter counter, boolean monotonic) { this.counter = counter; this.monotonic = monotonic; } @Override public boolean preCall(Request request, Response responder, ServiceMethodInfo serviceMethodInfo) { counter.inc(); return true; } @Override public void postCall(Request request, int status, ServiceMethodInfo serviceMethodInfo) { if (!monotonic) { counter.dec(); } } } } ================================================ FILE: analytics/msf4j-analytics/src/main/java/org/wso2/msf4j/analytics/tracing/MSF4JTracingInterceptor.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.analytics.tracing; import org.wso2.msf4j.Interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import org.wso2.msf4j.ServiceMethodInfo; import org.wso2.msf4j.analytics.common.tracing.TraceEvent; import org.wso2.msf4j.analytics.common.tracing.TracingConstants; import org.wso2.msf4j.analytics.common.tracing.TracingEventTracker; import org.wso2.msf4j.analytics.common.tracing.TracingUtil; import java.util.Date; /** * Interceptor for tracing server side request/response flows. */ // TODO: Write tests and add the OSGi mode support public class MSF4JTracingInterceptor implements Interceptor { private static final String RESPONDER_ATTRIBUTE = "responder-attribute"; private static final String TRACE_EVENT_ATTRIBUTE = "trace-event-attribute"; private String instanceId; private String instanceName; private String dasUrl; /** * Constructor of the MSF4JTracingInterceptor. * * @param microServiceName Name of the Microservice */ public MSF4JTracingInterceptor(String microServiceName) { this(microServiceName, TracingConstants.DAS_RECEIVER_URL); } /** * Constructor of the MSF4JTracingInterceptor. * * @param microServiceName Name of the Microservice * @param dasUrl URL of the receiver of DAS server */ public MSF4JTracingInterceptor(String microServiceName, String dasUrl) { this.instanceId = TracingUtil.generateUniqueId(); this.instanceName = microServiceName; this.dasUrl = dasUrl; } /** * Intercepts the server request flow and extract request information * to be published to the DAS for tracing. */ @Override public boolean preCall(Request request, Response responder, ServiceMethodInfo serviceMethodInfo) throws Exception { long time = new Date().getTime(); serviceMethodInfo.setAttribute(RESPONDER_ATTRIBUTE, responder); String traceOriginId = request.getHeader(TracingConstants.TRACE_ORIGIN_ID_HEADER); String serverTraceId; if (traceOriginId == null) { traceOriginId = TracingUtil.generateUniqueId(); serverTraceId = traceOriginId; } else { serverTraceId = TracingUtil.generateUniqueId(); } String traceParentId = request.getHeader(TracingConstants.TRACE_ID_HEADER); TraceEvent serverTraceEvent = new TraceEvent( TracingConstants.SERVER_TRACE_START, serverTraceId, traceOriginId, time ); serverTraceEvent.setInstanceId(instanceId); serverTraceEvent.setInstanceName(instanceName); serverTraceEvent.setParentId(traceParentId); serverTraceEvent.setHttpMethod(request.getHttpMethod()); serverTraceEvent.setUrl(request.getUri()); TracingEventTracker.setTraceEvent(serverTraceEvent); serviceMethodInfo.setAttribute(TRACE_EVENT_ATTRIBUTE, serverTraceEvent); TracingUtil.pushToDAS(serverTraceEvent, dasUrl); return true; } /** * Intercepts the server response flow and extract response information * to be published to the DAS for tracing. */ @Override public void postCall(Request request, int status, ServiceMethodInfo serviceMethodInfo) throws Exception { long time = new Date().getTime(); TraceEvent traceEvent = (TraceEvent) serviceMethodInfo.getAttribute(TRACE_EVENT_ATTRIBUTE); if (traceEvent != null) { TraceEvent endTraceEvent = new TraceEvent( TracingConstants.SERVER_TRACE_END, traceEvent.getTraceId(), traceEvent.getOriginId(), time ); Response responder = (Response) serviceMethodInfo.getAttribute(RESPONDER_ATTRIBUTE); endTraceEvent.setStatusCode(responder.getStatusCode()); TracingUtil.pushToDAS(endTraceEvent, dasUrl); } } } ================================================ FILE: analytics/msf4j-analytics/src/main/resources/http-monitoring.yml ================================================ # Copyright 2016 WSO2 Inc. (http://wso2.org) # # 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. # This is the main configuration file for HTTP Monitoring # Enable HTTP Monitoring enabled: true # Data Analytics Server (DAS) configuration das: # Data Receiver URL used by the Data Publisher receiverURL: tcp://localhost:7611 # Authentication URL for the Data Publisher # authURL: ssl://localhost:7711 # The type used with Data Publisher type: thrift username: admin password: admin # The path for Data Bridge Agent configuration dataAgentConfigPath: data-agent-config.xml ================================================ FILE: analytics/msf4j-analytics/src/test/java/org/wso2/msf4j/analytics/HTTPMonitoringConfigTest.java ================================================ /* * Copyright 2016 WSO2 Inc. (http://wso2.org) * * 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.wso2.msf4j.analytics; import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import org.wso2.msf4j.analytics.httpmonitoring.config.HTTPMonitoringConfigBuilder; import org.wso2.msf4j.analytics.httpmonitoring.config.model.DasConfig; import org.wso2.msf4j.analytics.httpmonitoring.config.model.HTTPMonitoringConfig; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; /** * Test Cases for {@link HTTPMonitoringConfig} */ public class HTTPMonitoringConfigTest { private static final String DEFAULT_CONFIG_PATH = "deployment.yaml"; private static HTTPMonitoringConfig httpMonitoringConfig; @BeforeClass private void load() { httpMonitoringConfig = HTTPMonitoringConfigBuilder.build(); } @AfterClass private void clear() throws IOException { Path configPath = Paths.get(DEFAULT_CONFIG_PATH); if (Files.exists(configPath)) { Files.delete(configPath); } } @Test public void testEnabled() { Assert.assertFalse(httpMonitoringConfig.isEnabled()); } @Test public void testDasConfigLoad() { DasConfig config = httpMonitoringConfig.getDas(); Assert.assertEquals(config.getReceiverURL(), "tcp://localhost:7611"); Assert.assertNull(config.getAuthURL()); Assert.assertEquals(config.getType(), "thrift"); Assert.assertEquals(config.getUsername(), "admin"); Assert.assertEquals(config.getPassword(), "admin"); Assert.assertEquals(config.getDataAgentConfigPath(), "data-agent-config.xml"); } } ================================================ FILE: analytics/msf4j-analytics/src/test/resources/deployment.yaml ================================================ # Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved # # 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. # MSF4J configuration wso2.msf4j.configuration: # No of worker pool threads to handle MSF4J requests threadCount: 100 threadPoolName: msf4j.executor.workerpool # MSF4J Analytics configuration wso2.msf4j.analytics.configuration: # Whether HTTP Monitoring is enables or not enabled: false # DAS configuration das: # The type used with Data Publisher type: thrift # Data Receiver URL used by the Data Publisher receiverURL: tcp://localhost:7611 username: admin password: admin # The path for Data Bridge Agent configuration dataAgentConfigPath: data-agent-config.xml ================================================ FILE: analytics/msf4j-analytics-common/pom.xml ================================================ 4.0.0 org.wso2.msf4j msf4j-parent 2.8.14-SNAPSHOT ../../poms/parent/pom.xml msf4j-analytics-common bundle WSO2 MSF4J Analytics Common WSO2 MSF4J Analytics Common https://github.com/wso2/msf4j org.wso2.msf4j msf4j-core org.testng testng test org.apache.maven.plugins maven-compiler-plugin org.apache.rat apache-rat-plugin org.apache.maven.plugins maven-checkstyle-plugin org.apache.maven.plugins maven-surefire-plugin org.apache.maven.plugins maven-deploy-plugin org.apache.felix maven-bundle-plugin coverage org.apache.maven.plugins maven-surefire-plugin ${argLine} -Xmx512m org.jacoco jacoco-maven-plugin prepare-agent prepare-agent report prepare-package report org.jacoco jacoco-maven-plugin release org.apache.maven.plugins maven-source-plugin true attach-sources package jar-no-fork org.apache.maven.plugins maven-javadoc-plugin true http://docs.oracle.com/javaee/6/api/ *.internal.* Licensed under the Apache License, Version 2.0]]> attach-javadoc package jar org.apache.maven.plugins maven-gpg-plugin ${gpg.passphrase} ${gpg.useagent} sign org.apache.maven.plugins maven-source-plugin org.apache.maven.plugins maven-javadoc-plugin org.apache.maven.plugins maven-gpg-plugin org.wso2.msf4j.analytics.*;version="${msf4j.version}" org.slf4j.*;version="${slf4j.version.range}", org.osgi.framework.*;version="${osgi.framework.import.version.range}", org.osgi.util.tracker; version="${osgi.service.tracker.import.version.range}", javax.ws.rs.*, osgi.service;objectClass="org.wso2.carbon.kernel.startupresolver.CapabilityProvider";capabilityName="org.wso2.msf4j.Interceptor" ================================================ FILE: analytics/msf4j-analytics-common/src/main/java/org/wso2/msf4j/analytics/common/tracing/MSF4JClientTracingFilter.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.analytics.common.tracing; import java.io.IOException; import java.util.Date; import javax.ws.rs.client.ClientRequestContext; import javax.ws.rs.client.ClientRequestFilter; import javax.ws.rs.client.ClientResponseContext; import javax.ws.rs.client.ClientResponseFilter; /** * Filter for tracing client side request/response flow. */ // TODO: Write tests and add the OSGi mode support public class MSF4JClientTracingFilter implements ClientRequestFilter, ClientResponseFilter { private static final String TRACE_EVENT_ATTRIBUTE = "trace-event-attribute"; private TraceEvent parentEvent; private String instanceId; private String instanceName; private String dasUrl; /** * Constructor of the MSF4JClientTracingFilter. * * @param clientName Name of the client */ public MSF4JClientTracingFilter(String clientName) { this(clientName, TracingConstants.DAS_RECEIVER_URL); } /** * Constructor of the MSF4JClientTracingFilter. * * @param clientName Name of the client * @param dasUrl URL of the receiver of DAS server */ public MSF4JClientTracingFilter(String clientName, String dasUrl) { this(clientName, null, dasUrl); } /** * Constructor of the MSF4JClientTracingFilter. * * @param clientName Name of the client * @param parentEvent TraceEvent of the caller * @param dasUrl URL of the receiver of DAS server */ public MSF4JClientTracingFilter(String clientName, TraceEvent parentEvent, String dasUrl) { this.instanceName = clientName; this.instanceId = TracingUtil.generateUniqueId(); this.dasUrl = dasUrl; if (parentEvent != null) { this.parentEvent = parentEvent; } else { this.parentEvent = TracingEventTracker.getTraceEvent(); } } /** * Intercepts the client request flow and extract request information * to be published to the DAS for tracing. */ @Override public void filter(ClientRequestContext requestContext) throws IOException { long time = new Date().getTime(); String clientTraceId; String traceOriginId; String traceParentId = null; if (this.parentEvent == null) { traceOriginId = TracingUtil.generateUniqueId(); clientTraceId = traceOriginId; } else { traceOriginId = parentEvent.getOriginId(); clientTraceId = TracingUtil.generateUniqueId(); traceParentId = parentEvent.getTraceId(); } TraceEvent clientTraceEvent = new TraceEvent( TracingConstants.CLIENT_TRACE_START, clientTraceId, traceOriginId, time ); clientTraceEvent.setInstanceId(instanceId); clientTraceEvent.setInstanceName(instanceName); clientTraceEvent.setParentId(traceParentId); clientTraceEvent.setHttpMethod(requestContext.getMethod()); clientTraceEvent.setUrl(requestContext.getUri().toString()); requestContext.setProperty(TRACE_EVENT_ATTRIBUTE, clientTraceEvent); requestContext.getHeaders().putSingle(TracingConstants.TRACE_ID_HEADER, clientTraceId); requestContext.getHeaders().putSingle(TracingConstants.TRACE_ORIGIN_ID_HEADER, traceOriginId); TracingUtil.pushToDAS(clientTraceEvent, dasUrl); } /** * Intercepts the client response flow and extract response information * to be published to the DAS server for tracing. */ @Override public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException { long time = new Date().getTime(); TraceEvent traceEvent = (TraceEvent) requestContext.getProperty(TRACE_EVENT_ATTRIBUTE); if (traceEvent != null) { TraceEvent endTraceEvent = new TraceEvent( TracingConstants.CLIENT_TRACE_END, traceEvent.getTraceId(), traceEvent.getOriginId(), time ); endTraceEvent.setStatusCode(responseContext.getStatus()); TracingUtil.pushToDAS(endTraceEvent, dasUrl); } } } ================================================ FILE: analytics/msf4j-analytics-common/src/main/java/org/wso2/msf4j/analytics/common/tracing/TraceEvent.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.analytics.common.tracing; /** * Class to hold tracing start event data. */ public class TraceEvent { private final String type; private final String traceId; private final String originId; private final long time; private int statusCode; private String httpMethod; private String instanceId; private String instanceName; private String parentId; private String url; public TraceEvent(String type, String traceId, String originId, long time) { this.type = type; this.traceId = traceId; this.originId = originId; this.time = time; } public String getType() { return type; } public String getTraceId() { return traceId; } public String getOriginId() { return originId; } public long getTime() { return time; } public String getInstanceId() { return instanceId; } public void setInstanceId(String instanceId) { this.instanceId = instanceId; } public String getInstanceName() { return instanceName; } public void setInstanceName(String instanceName) { this.instanceName = instanceName; } public String getParentId() { return parentId; } public void setParentId(String parentId) { this.parentId = parentId; } public int getStatusCode() { return statusCode; } public void setStatusCode(int statusCode) { this.statusCode = statusCode; } public String getHttpMethod() { return httpMethod; } public void setHttpMethod(String httpMethod) { this.httpMethod = httpMethod; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } @Override public String toString() { return "TraceEvent{" + "type='" + type + '\'' + ", traceId='" + traceId + '\'' + ", originId='" + originId + '\'' + ", time=" + time + ", statusCode=" + statusCode + ", httpMethod='" + httpMethod + '\'' + ", instanceId='" + instanceId + '\'' + ", instanceName='" + instanceName + '\'' + ", parentId='" + parentId + '\'' + ", url='" + url + '\'' + '}'; } } ================================================ FILE: analytics/msf4j-analytics-common/src/main/java/org/wso2/msf4j/analytics/common/tracing/TracingConstants.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.analytics.common.tracing; /** * Class to hold constants used in tracing. */ public class TracingConstants { public static final String CLIENT_TRACE_START = "CTS"; public static final String CLIENT_TRACE_END = "CTE"; public static final String SERVER_TRACE_START = "STS"; public static final String SERVER_TRACE_END = "STE"; public static final String TRACE_ID_HEADER = "X-msf4j-trace-id"; public static final String TRACE_ORIGIN_ID_HEADER = "X-msf4j-trace-origin-id"; public static final String DAS_RECEIVER_URL = "http://localhost:9763/endpoints/msf4jtracereceiver"; public static final String DEFAULT_ZIPKIN_URL = "http://0.0.0.0:9411"; /** * Tracing Types. */ public enum TracingType { DAS, ZIPKIN } } ================================================ FILE: analytics/msf4j-analytics-common/src/main/java/org/wso2/msf4j/analytics/common/tracing/TracingEventTracker.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.analytics.common.tracing; /** * Class for holding the thread local of tracing events. * This class help the Client interceptors to access the * request trace event of the containing service to keep * the correlation. */ public class TracingEventTracker { private static final ThreadLocal traceEventThreadLocal = new ThreadLocal<>(); public static void setTraceEvent(TraceEvent traceEvent) { traceEventThreadLocal.set(traceEvent); } public static TraceEvent getTraceEvent() { return traceEventThreadLocal.get(); } } ================================================ FILE: analytics/msf4j-analytics-common/src/main/java/org/wso2/msf4j/analytics/common/tracing/TracingUtil.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.analytics.common.tracing; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.Entity; import javax.ws.rs.core.Response; /** * Utility methods of for MSF4J tracing. */ public class TracingUtil { private static final Logger log = LoggerFactory.getLogger(TracingUtil.class); private static final ExecutorService executorService = Executors.newSingleThreadExecutor(); private static final Random random = new Random(); /** * Generate a random string unique identifier. */ public static String generateUniqueId() { // UUID.randomUUID().toString() is too slow // TODO: Test whether the ID is unique enough return System.currentTimeMillis() + "" + String.format("%08d", random.nextInt(100000000)); } /** * Publish trace event to DAS in the background. */ public static void pushToDAS(TraceEvent traceEvent, String dasUrl) { Future future = executorService.submit(() -> { log.debug("Publishing trace event " + traceEvent); if (ClientBuilder.newClient().target(dasUrl) .request().post(Entity.json(traceEvent)).getStatus() != Response.Status.OK.getStatusCode()) { log.error("Error while publishing trace event " + traceEvent); } }); future.isDone(); // Added to avoid findbugs warning } } ================================================ FILE: analytics/msf4j_http_monitoring_capp_source/build.xml ================================================ ================================================ FILE: analytics/msf4j_http_monitoring_capp_source/msf4j_http_monitoring_capp/artifacts.xml ================================================ ================================================ FILE: analytics/msf4j_http_monitoring_capp_source/msf4j_http_monitoring_capp/http_event_receiver_1.0.0/artifact.xml ================================================ http_event_receiver.xml ================================================ FILE: analytics/msf4j_http_monitoring_capp_source/msf4j_http_monitoring_capp/http_event_receiver_1.0.0/http_event_receiver.xml ================================================ ================================================ FILE: analytics/msf4j_http_monitoring_capp_source/msf4j_http_monitoring_capp/http_event_store_1.0.0/artifact.xml ================================================ org_wso2_msf4j_analytics_httpmonitoring.xml ================================================ FILE: analytics/msf4j_http_monitoring_capp_source/msf4j_http_monitoring_capp/http_event_store_1.0.0/org_wso2_msf4j_analytics_httpmonitoring.xml ================================================ meta_timestamp true false false LONG meta_server_address true false false STRING meta_server_name true false false STRING meta_application_type true false false STRING correlation_activity_id true false false FACET correlation_parent_request true false false STRING service_class true false false STRING service_name true false false STRING service_method true false false STRING request_uri false false false STRING service_context true false false STRING http_method false false false STRING content_type false false false STRING request_content_length false false false LONG referrer false false false STRING http_status_code true false false INTEGER response_time true false false LONG group_by true false false FACET org.wso2.msf4j.analytics.httpmonitoring:1.0.0 EVENT_STORE ================================================ FILE: analytics/msf4j_http_monitoring_capp_source/msf4j_http_monitoring_capp/http_event_stream_1.0.0/artifact.xml ================================================ org.wso2.msf4j.analytics.httpmonitoring_1.0.0.json ================================================ FILE: analytics/msf4j_http_monitoring_capp_source/msf4j_http_monitoring_capp/http_event_stream_1.0.0/org.wso2.msf4j.analytics.httpmonitoring_1.0.0.json ================================================ { "name": "org.wso2.msf4j.analytics.httpmonitoring", "version": "1.0.0", "nickName": "msf4j_http_monitoring", "description": "MSF4J HTTP Monitoring", "metaData": [ { "name": "timestamp", "type": "LONG" }, { "name": "server_address", "type": "STRING" }, { "name": "server_name", "type": "STRING" }, { "name": "application_type", "type": "STRING" } ], "correlationData": [ { "name": "activity_id", "type": "STRING" }, { "name": "parent_request", "type": "STRING" } ], "payloadData": [ { "name": "service_class", "type": "STRING" }, { "name": "service_name", "type": "STRING" }, { "name": "service_method", "type": "STRING" }, { "name": "request_uri", "type": "STRING" }, { "name": "service_context", "type": "STRING" }, { "name": "http_method", "type": "STRING" }, { "name": "content_type", "type": "STRING" }, { "name": "request_content_length", "type": "LONG" }, { "name": "referrer", "type": "STRING" }, { "name": "http_status_code", "type": "INT" }, { "name": "response_time", "type": "LONG" } ] } ================================================ FILE: analytics/msf4j_http_monitoring_capp_source/msf4j_http_monitoring_capp/spark_script_1.0.0/artifact.xml ================================================ http_event_script.xml ================================================ FILE: analytics/msf4j_http_monitoring_capp_source/msf4j_http_monitoring_capp/spark_script_1.0.0/http_event_script.xml ================================================ http_event_script 0 0/15 * 1/1 * ? * ================================================ FILE: analytics/wso2das-tracing-capp/capp-content/Dashboard_1.0.0/artifact.xml ================================================ msf4j-message-tracing.json ================================================ FILE: analytics/wso2das-tracing-capp/capp-content/Dashboard_1.0.0/msf4j-message-tracing.json ================================================ { "id": "msf4j-message-tracing", "title": "MSF4J Message Tracing", "description": "", "permissions": { "viewers": [ "Internal/everyone" ], "editors": [ "Internal/everyone" ] }, "pages": [ { "id": "landing", "title": "Welcome", "layout": { "content": { "loggedIn": { "blocks": [ { "id": "a", "col": 1, "row": 1, "size_x": 12, "size_y": 3, "banner": false } ] } }, "fluidLayout": false }, "isanon": false, "content": { "default": { "a": [ { "id": "n6ql6vcjn4hs1mikmu5trzfr", "content": { "id": "msf4j-message-tracing", "title": "MSF4J Message Tracing", "type": "gadget", "thumbnail": "store://gadget/msf4j-tracing/icon.png", "data": { "url": "store://gadget/msf4j-tracing/index.xml" }, "styles": { "title": "MSF4J Message Tracing", "borders": true }, "options": { "timeBack": { "type": "STRING", "title": "Trace Last Minutes", "value": "1440", "options": [], "required": false }, "dasHost": { "type": "STRING", "title": "DAS Host", "value": "https://localhost:9443", "options": [], "required": false }, "username": { "type": "STRING", "title": "Username", "value": "admin", "options": [], "required": false }, "password": { "type": "STRING", "title": "Password", "value": "admin", "options": [], "required": false } }, "locale_titles": {} } } ] }, "anon": {} } } ], "identityServerUrl": "", "accessTokenUrl": "", "apiKey": "", "apiSecret": "", "banner": { "globalBannerExists": null, "customBannerExists": null }, "landing": "landing", "isanon": false, "isUserCustom": false } ================================================ FILE: analytics/wso2das-tracing-capp/capp-content/Eventreceiver_1.0.0/artifact.xml ================================================ msf4jtracereceiver.xml ================================================ FILE: analytics/wso2das-tracing-capp/capp-content/Eventreceiver_1.0.0/msf4jtracereceiver.xml ================================================ http ================================================ FILE: analytics/wso2das-tracing-capp/capp-content/Eventstore_1.0.0/artifact.xml ================================================ msf4j-tracing.xml ================================================ FILE: analytics/wso2das-tracing-capp/capp-content/Eventstore_1.0.0/msf4j-tracing.xml ================================================ typefalsetruefalseSTRINGtimefalsefalsefalseLONGstatusCodefalsefalsefalseINTEGERhttpMethodfalsefalsefalseSTRINGinstanceIdfalsefalsefalseSTRINGinstanceNamefalsefalsefalseSTRINGparentIdfalsefalsefalseSTRINGtraceIdfalsetruefalseSTRINGoriginIdtruefalsefalseSTRINGurlfalsefalsefalseSTRINGmeta_traceIdfalsefalsefalseSTRINGcorrelation_originIdfalsefalsefalseINTEGERmsf4j-tracing:1.0.0falseEVENT_STORE ================================================ FILE: analytics/wso2das-tracing-capp/capp-content/Eventstream_1.0.0/artifact.xml ================================================ msf4j-tracing_1.0.0.json ================================================ FILE: analytics/wso2das-tracing-capp/capp-content/Eventstream_1.0.0/msf4j-tracing_1.0.0.json ================================================ { "name": "msf4j-tracing", "version": "1.0.0", "nickName": "", "description": "", "payloadData": [ { "name": "type", "type": "STRING" }, { "name": "time", "type": "LONG" }, { "name": "statusCode", "type": "INT" }, { "name": "httpMethod", "type": "STRING" }, { "name": "instanceId", "type": "STRING" }, { "name": "instanceName", "type": "STRING" }, { "name": "parentId", "type": "STRING" }, { "name": "traceId", "type": "STRING" }, { "name": "originId", "type": "STRING" }, { "name": "url", "type": "STRING" } ] } ================================================ FILE: analytics/wso2das-tracing-capp/capp-content/GadgetMSF4JTracing_1.0.0/artifact.xml ================================================ msf4j-tracing ================================================ FILE: analytics/wso2das-tracing-capp/capp-content/GadgetMSF4JTracing_1.0.0/msf4j-tracing/css/main.css ================================================ body { display: block; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; overflow: hidden; } #refresh-traces { padding: 5px 10px; background-color: #005FB0; color: #ffffff; display: inline-block; margin: 12px 32px; cursor: pointer; } #refresh-traces .pressed { background-color: #ffffff; } #trace-groups { position: relative; display: block; width: 100%; height: 100%; overflow-y: auto; } .trace-group { padding: 12px; cursor: default; border-bottom: 1px solid #dddddd; } .trace-group .inst-name { display: inline-block; margin-left: 20px; max-width: 120px; } .trace-group .endpoint { display: inline-block; margin-left: 20px; } .trace-group .method { font-weight: bold; display: inline-block; margin-left: 20px; } .trace-group .url { margin-left: 20px; display: inline-block; } .trace-group .time { display: inline-block; margin-left: 20px; min-width: 145px; } .trace-bar, .trace-bar * { position: relative; } .trace-bar .bar-row { border-bottom: 1px solid #dddddd; } .trace-bar .left-pane { position: absolute; float: left; left: 0; top: 0; width: 200px; line-height: 20px; padding: 10px 0; cursor: pointer; } .trace-bar .arrow { display: inline-block; width: 0; height: 0; border-left: 5px solid transparent; border-right: 5px solid transparent; border-top: 5px solid #000; } .trace-bar .arrow-down { display: inline-block; width: 0; height: 0; border-top: 5px solid transparent; border-bottom: 5px solid transparent; border-left: 5px solid #000; } .trace-bar .arrow-hide { margin-left: 5px; display: inline-block; } .trace-bar .right-pane { margin-left: 200px; border-left: 1px solid #dddddd; padding: 14px 0; } .trace-bar .bar-wrapper { padding: 0 5px; } .trace-bar .bar { position: relative; background-color: #005FB0; top: 0; height: 12px; cursor: pointer; border-radius: 2px; } .trace-bar .child-container { } #trace-tree-view { position: absolute; display: block; height: 100%; width: 100%; left: 0; top: 0; z-index: 100; background-color: #ffffff; padding-top: 40px; overflow-y: auto; } .bar-info { z-index: 100; margin: 5px auto; width: 600px; position: relative; } .bar-info .row { display: block; position: relative; } .bar-info .row.emp { font-weight: bold; } .bar-info .row.gap { margin-top: 15px; } .bar-info .left-ipane { position: relative; display: block; left: 0; width: 50%; text-align: right; } .bar-info .left-ipane .wrapper.emp, .bar-info .right-ipane .wrapper.emp { padding: 8px; } .bar-info .left-ipane .wrapper, .bar-info .right-ipane .wrapper { padding: 8px; } .bar-info .right-ipane { position: absolute; display: block; left: 50%; width: 50%; top: 0; text-align: left; } .bar-info .close { z-index: 100; } #trace-tree-view .container { border-top: 1px solid #dddddd; } .bar-info .cell { display: inline-block; } #trace-tree-view .close, .bar-info .close { cursor: pointer; position: absolute; display: block; left: 0; top: 0; margin-left: 10px; font-size: 18px; } #trace-tree-view .close .arrow { display: inline-block; width: 0; height: 0; border-right: 10px solid #666; border-top: 8px solid transparent; border-bottom: 8px solid transparent; margin-right: 5px; margin-bottom: -2px; } #trace-tree-view .time-line-pane { margin-left: 200px; } #trace-tree-view .time-line-wrapper { position: relative; display: block; } #time-line { width: 100%; height: 20px; } #time-line .unit { border-left: 1px solid #dddddd; color: #666666; min-height: 4px; font-size: 10px; padding-top: 3px; padding-left: 3px; display: inline-block; position: absolute; } ================================================ FILE: analytics/wso2das-tracing-capp/capp-content/GadgetMSF4JTracing_1.0.0/msf4j-tracing/gadget.json ================================================ { "id": "msf4j-message-tracing", "title": "MSF4J Message Tracing", "type": "gadget", "thumbnail": "store://gadget/msf4j-tracing/icon.png", "data": { "url": "store://gadget/msf4j-tracing/index.xml" } } ================================================ FILE: analytics/wso2das-tracing-capp/capp-content/GadgetMSF4JTracing_1.0.0/msf4j-tracing/index.xml ================================================

Refresh Traces
Back
]]> ================================================ FILE: analytics/wso2das-tracing-capp/capp-content/GadgetMSF4JTracing_1.0.0/msf4j-tracing/js/libs/jquery.base64.js ================================================ /*! * jquery.base64.js 0.1 - https://github.com/yckart/jquery.base64.js * Makes Base64 en & -decoding simpler as it is. * * Based upon: https://gist.github.com/Yaffle/1284012 * * Copyright (c) 2012 Yannick Albert (http://yckart.com) * Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php). * 2013/02/10 **/ ;(function($) { var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", a256 = '', r64 = [256], r256 = [256], i = 0; var UTF8 = { /** * Encode multi-byte Unicode string into utf-8 multiple single-byte characters * (BMP / basic multilingual plane only) * * Chars in range U+0080 - U+07FF are encoded in 2 chars, U+0800 - U+FFFF in 3 chars * * @param {String} strUni Unicode string to be encoded as UTF-8 * @returns {String} encoded string */ encode: function(strUni) { // use regular expressions & String.replace callback function for better efficiency // than procedural approaches var strUtf = strUni.replace(/[\u0080-\u07ff]/g, // U+0080 - U+07FF => 2 bytes 110yyyyy, 10zzzzzz function(c) { var cc = c.charCodeAt(0); return String.fromCharCode(0xc0 | cc >> 6, 0x80 | cc & 0x3f); }) .replace(/[\u0800-\uffff]/g, // U+0800 - U+FFFF => 3 bytes 1110xxxx, 10yyyyyy, 10zzzzzz function(c) { var cc = c.charCodeAt(0); return String.fromCharCode(0xe0 | cc >> 12, 0x80 | cc >> 6 & 0x3F, 0x80 | cc & 0x3f); }); return strUtf; }, /** * Decode utf-8 encoded string back into multi-byte Unicode characters * * @param {String} strUtf UTF-8 string to be decoded back to Unicode * @returns {String} decoded string */ decode: function(strUtf) { // note: decode 3-byte chars first as decoded 2-byte strings could appear to be 3-byte char! var strUni = strUtf.replace(/[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g, // 3-byte chars function(c) { // (note parentheses for precence) var cc = ((c.charCodeAt(0) & 0x0f) << 12) | ((c.charCodeAt(1) & 0x3f) << 6) | (c.charCodeAt(2) & 0x3f); return String.fromCharCode(cc); }) .replace(/[\u00c0-\u00df][\u0080-\u00bf]/g, // 2-byte chars function(c) { // (note parentheses for precence) var cc = (c.charCodeAt(0) & 0x1f) << 6 | c.charCodeAt(1) & 0x3f; return String.fromCharCode(cc); }); return strUni; } }; while(i < 256) { var c = String.fromCharCode(i); a256 += c; r256[i] = i; r64[i] = b64.indexOf(c); ++i; } function code(s, discard, alpha, beta, w1, w2) { s = String(s); var buffer = 0, i = 0, length = s.length, result = '', bitsInBuffer = 0; while(i < length) { var c = s.charCodeAt(i); c = c < 256 ? alpha[c] : -1; buffer = (buffer << w1) + c; bitsInBuffer += w1; while(bitsInBuffer >= w2) { bitsInBuffer -= w2; var tmp = buffer >> bitsInBuffer; result += beta.charAt(tmp); buffer ^= tmp << bitsInBuffer; } ++i; } if(!discard && bitsInBuffer > 0) result += beta.charAt(buffer << (w2 - bitsInBuffer)); return result; } var Plugin = $.base64 = function(dir, input, encode) { return input ? Plugin[dir](input, encode) : dir ? null : this; }; Plugin.btoa = Plugin.encode = function(plain, utf8encode) { plain = Plugin.raw === false || Plugin.utf8encode || utf8encode ? UTF8.encode(plain) : plain; plain = code(plain, false, r256, b64, 8, 6); return plain + '===='.slice((plain.length % 4) || 4); }; Plugin.atob = Plugin.decode = function(coded, utf8decode) { coded = String(coded).split('='); var i = coded.length; do {--i; coded[i] = code(coded[i], true, r64, a256, 6, 8); } while (i > 0); coded = coded.join(''); return Plugin.raw === false || Plugin.utf8decode || utf8decode ? UTF8.decode(coded) : coded; }; }(jQuery)); ================================================ FILE: analytics/wso2das-tracing-capp/capp-content/GadgetMSF4JTracing_1.0.0/msf4j-tracing/js/main.js ================================================ gadgets.util.registerOnLoadHandler(function () { gadgets.window.adjustHeight(); }); $(document).ready(function () { var prefs = new _IG_Prefs(); var traceGroupsElm = $('#trace-groups'); var traceTreeVwElm = $("#trace-tree-view"); var traceTreeVw = initTraceTreeView( traceTreeVwElm, traceTreeVwElm.find(".container").first(), traceTreeVwElm.find(".close").first() ); (function () { var refreshTracesElm = $("#refresh-traces"); var isLoading = false; load(); function load() { if (isLoading == false) { isLoading = true; loadAndRenderTraceGroups(prefs, traceGroupsElm, traceTreeVw, function () { isLoading = false; }); } } refreshTracesElm.click(function () { load(); }); })(); }); function loadAndRenderTraceGroups(prefs, traceGroupsElm, traceTreeVw, callback) { var analyticsPath = "/analytics/tables/MSF4J-TRACING"; var timeBack = prefs.getInt("timeBack"); var dasHost = prefs.getString("dasHost"); var username = prefs.getString("username"); var password = prefs.getString("password"); loadTraces((function (dasHost, analyticsPath, timeBack) { return dasHost + analyticsPath + ((dasHost.substr(dasHost.length - 1) == "/") ? "" : "/") + ((new Date()).getTime() - timeBack * 60 * 1000); })(dasHost, analyticsPath, timeBack), timeBack, username, password, function (data) { var traceGroups = groupTraceEvents(data); renderTraceGroups(traceGroups, traceGroupsElm, traceTreeVw); callback(); }, function (jqXHR) { callback(); var errorData = $.parseJSON(jqXHR.responseText); if (errorData) { alert(errorData.message); } else { alert("Failed to load traces"); } }); } function initBarInfoView(rootElm, traceData, closeCallBack) { var close = null; function buildBarInfoVwModel(traceData) { var vwModel = { chttpMethod: "-", shttpMethod: "-", clientPath: "-", serverPath: "-", cStatusCode: "-", sStatusCode: "-", clientStartTime: "-", serverStartTime: "-", serverEndTime: "-", clientEndTime: "-", serverInstName: "-", clientInstName: "-", serverInstId: "-", clientInstId: "-" }; if (traceData.type == "CTS") { vwModel.chttpMethod = traceData.httpMethod || "-"; vwModel.clientPath = traceData.url || "-"; vwModel.clientStartTime = traceData.timeStr || "-"; vwModel.clientEndTime = (traceData.end) ? traceData.end.timeStr || "-" : "-"; vwModel.clientInstName = traceData.clientName || "-"; vwModel.clientInstId = traceData.instanceId || "-"; vwModel.cStatusCode = (traceData.end) ? traceData.end.statusCode : "-"; if (traceData.service) { vwModel.shttpMethod = traceData.service.httpMethod || "-"; vwModel.serverPath = traceData.service.url || "-"; vwModel.serverStartTime = traceData.service.timeStr || "-"; vwModel.serverEndTime = (traceData.service.end) ? traceData.service.end.timeStr || "-" : "-"; vwModel.serverInstName = traceData.instanceName || "-"; vwModel.serverInstId = traceData.service.instanceId || "-"; vwModel.sStatusCode = (traceData.service.end) ? traceData.service.end.statusCode : "-"; } } else if (traceData.type == "STS") { vwModel.shttpMethod = traceData.httpMethod || "-"; vwModel.serverPath = traceData.url || "-"; vwModel.serverStartTime = traceData.timeStr || "-"; vwModel.serverEndTime = (traceData.end) ? traceData.end.timeStr || "-" : "-"; vwModel.serverInstName = traceData.instanceName || "-"; vwModel.serverInstId = traceData.instanceId || "-"; vwModel.sStatusCode = (traceData.end) ? traceData.end.statusCode : "-"; } return vwModel; } function show(traceData) { var barInfoElm = $( Mustache.render( $('#bar-info').html(), buildBarInfoVwModel(traceData) ) ); close = function () { barInfoElm.remove(); if (closeCallBack) { closeCallBack(); } }; // var closeBtnElm = barInfoElm.find(".close").first(); // closeBtnElm.click(close); rootElm.append(barInfoElm); } return { show: show, close: function () { if (close) { close(); } } }; } function initTraceTreeView(viewElm, containerElm, closeBtnElm) { hide(); closeBtnElm.click(function () { hide(); }); function clean() { containerElm.empty(); } function hide() { clean(); viewElm.hide(); } function renderTimeLine(start, end) { var timeLineElm = $("#time-line"); var timeGaps = [ { gap: 1, unit: "ms", unitMs: 1 }, { gap: 5, unit: "ms", unitMs: 1 }, { gap: 10, unit: "ms", unitMs: 1 }, { gap: 100, unit: "s", unitMs: 1000 }, { gap: 200, unit: "s", unitMs: 1000 }, { gap: 500, unit: "s", unitMs: 1000 }, { gap: 1000, unit: "s", unitMs: 1000 }, { gap: 1000 * 5, unit: "s", unitMs: 1000 }, { gap: 1000 * 10, unit: "s", unitMs: 1000 }, { gap: 1000 * 60, unit: "s", unitMs: 1000 * 60 }, { gap: 1000 * 60 * 5, unit: "m", unitMs: 1000 * 60 }, { gap: 1000 * 60 * 10, unit: "m", unitMs: 1000 * 60 }, { gap: 1000 * 60 * 60, unit: "h", unitMs: 1000 * 60 * 60 } ]; var timeDiff = end - start; function render() { var spaceDiff = timeLineElm.width(); var minGapWidth = 40; var timeGap = (function () { var timeGapsLen = timeGaps.length; for (var i = 0; i < timeGapsLen; i++) { var timeGap = timeGaps[i]; var gapCount = (timeDiff / timeGap.gap); var gapWidth = spaceDiff / gapCount; if (gapWidth >= minGapWidth) { return timeGap; } } return timeGaps[timeGapsLen - 1]; })(); var gapCount = (timeDiff / timeGap.gap); var unitGap = timeGap.gap / timeGap.unitMs; for (var i = 0; i < gapCount; i++) { var left = (i * timeGap.gap) * 100 / timeDiff; var unitElm = $(document.createElement("div")); unitElm.attr("class", "unit"); unitElm.css("left", left + "%"); unitElm.html((i * unitGap).toFixed(3) + timeGap.unit); timeLineElm.append(unitElm); } } timeLineElm.empty(); render(); timeLineElm.resize(function () { timeLineElm.empty(); render(); }); } function show(traceTree) { viewElm.show(); renderTraceTree(traceTree, buildOriginTraceBarVw(traceTree, traceTree.timeRange, 0)); renderTimeLine(traceTree.timeRange.start, traceTree.timeRange.end); } function buildOriginTraceBarVw(traceTree, timeRange, callDepth) { var originTraceBarVw = buildTraceBarView(traceTree, timeRange, callDepth); containerElm.append(originTraceBarVw.traceBarElm); return originTraceBarVw; } function renderTraceTree(traceTree, parentTraceBarVw) { var children = traceTree.children; var childrenLen = children.length; for (var i = 0; i < childrenLen; i++) { var child = children[i]; var childTraceBarVw = buildTraceBarView( child, parentTraceBarVw.timeRange, // Do not indent the service call of a client (child.type == "STS") ? parentTraceBarVw.callDepth : parentTraceBarVw.callDepth + 1 ); parentTraceBarVw.addChildTraceBar(childTraceBarVw); renderTraceTree(child, childTraceBarVw); } } return { show: function (traceTree) { show(traceTree); } }; } function buildTraceBarView(traceData, timeRange, callDepth) { var traceBarElm = $( Mustache.render( $('#trace-bar').html() ) ); var childContainerElm = traceBarElm.find(".child-container").first(); var leftPaneElm = traceBarElm.find(".left-pane").first(); var arrowElm = traceBarElm.find(".arrow").first(); var labelElm = traceBarElm.find(".label").first(); var barElm = traceBarElm.find(".bar").first(); var barInfoContElm = traceBarElm.find(".bar-info-container").first(); var childTraceBarVws = []; var collapseChildren = null; setName(traceData.instanceName); setTimeRange( traceData.time, // If end time is not available, draw the bar to touch right end (traceData.end) ? traceData.end.time : timeRange.end, timeRange.start, timeRange.end ); setCallDepth(callDepth); (function () { var inforBarVw = null; barElm.click(function () { if (!inforBarVw) { inforBarVw = initBarInfoView(barInfoContElm, traceData); inforBarVw.show(traceData); } else { inforBarVw.close(); inforBarVw = null; } }); })(); (function (childTraceBarVws) { var isHidden = false; collapseChildren = function () { if (traceData.children.length > 0) { childContainerElm.hide(); labelElm.css("font-weight", "bold"); arrowElm.attr("class", "arrow-down"); var childTraceBarVwsLen = childTraceBarVws.length; for (var i = 0; i < childTraceBarVwsLen; i++) { childTraceBarVw = childTraceBarVws[i]; childTraceBarVw.collapseChildren(); } isHidden = true; } }; if (traceData.children.length > 0) { leftPaneElm.click(function () { if (isHidden) { childContainerElm.show(); labelElm.css("font-weight", "normal"); arrowElm.attr("class", "arrow"); isHidden = false; } else { collapseChildren(); } }); } else { arrowElm.attr("class", "arrow-hide"); } })(childTraceBarVws); function setTimeRange(startTime, endTime, rootStart, rootEnd) { var left = 100 * (startTime - rootStart) / (rootEnd - rootStart); var width = 100 * (endTime - startTime) / (rootEnd - rootStart); barElm.css({ left: left + "%", width: width + "%", "background-color": (function shadeBlend(p, c0, c1) { var n = p < 0 ? p * -1 : p, u = Math.round, w = parseInt; if (c0.length > 7) { var f = c0.split(","), t = (c1 ? c1 : p < 0 ? "rgb(0,0,0)" : "rgb(255,255,255)").split(","), R = w(f[0].slice(4)), G = w(f[1]), B = w(f[2]); return "rgb(" + (u((w(t[0].slice(4)) - R) * n) + R) + "," + (u((w(t[1]) - G) * n) + G) + "," + (u((w(t[2]) - B) * n) + B) + ")" } else { var f = w(c0.slice(1), 16), t = w((c1 ? c1 : p < 0 ? "#000000" : "#FFFFFF").slice(1), 16), R1 = f >> 16, G1 = f >> 8 & 0x00FF, B1 = f & 0x0000FF; return "#" + (0x1000000 + (u(((t >> 16) - R1) * n) + R1) * 0x10000 + (u(((t >> 8 & 0x00FF) - G1) * n) + G1) * 0x100 + (u(((t & 0x0000FF) - B1) * n) + B1)).toString(16).slice(1) } })((function () { var p = callDepth * 0.1; return (p > 0.4) ? 0.4 : p; })(callDepth), "#005FB0") }); } function setName(name) { labelElm.html(name); } function addChildTraceBar(traceBarVw) { childTraceBarVws.push(traceBarVw); childContainerElm.append(traceBarVw.traceBarElm); } function setCallDepth(callDepth) { leftPaneElm.css("margin-left", callDepth * 15); } return { traceBarElm: traceBarElm, collapseChildren: collapseChildren, timeRange: timeRange, callDepth: callDepth, addChildTraceBar: function (traceBarVw) { addChildTraceBar(traceBarVw); } }; } function loadTraces(url, timeBack, username, password, callback, errorCallback) { $.ajax({ type: "GET", beforeSend: function (request) { if (!window.btoa) { window.btoa = $.base64.btoa; } request.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password)); }, url: url, data: "json", processData: false, success: callback, error: errorCallback }); } function renderTraceGroups(traceGroups, rootElm, traceTreeVw) { rootElm.empty(); itrValidTraceGroups(traceGroups, function (traceGroup) { var traceGroupElm = $( Mustache.render( $('#trace-group').html(), traceGroup ) ); traceGroupElm.click(function () { traceTreeVw.show(traceGroup.getTraceTree()); }); rootElm.prepend(traceGroupElm); }); } function groupTraceEvents(data) { var traceGroups = {}; var dataLen = data.length; for (var i = 0; i < dataLen; i++) { var event = data[i].values; if (!event || !event.originId) { // Ignore the event if it is null or does not contain the original event ID // Or the original event Id is invalid continue; } var eventDate = new Date(event.time); event.timeStr = eventDate.toTimeString() .replace(" GMT", ":" + ("00" + eventDate.getMilliseconds()).slice(-3) + " GMT"); var traceGroup = traceGroups[event.originId]; if (!traceGroup) { traceGroups[event.originId] = traceGroup = { isBuilt: false, timeRange: { // The time range that the trace spans // Required for drawing gantt bars to scale start: Number.MAX_VALUE, end: 0, addStart: function (startTime) { if (this.start > startTime) { this.start = startTime; } }, addEnd: function (endTime) { if (this.end < endTime) { this.end = endTime; } } }, origin: null, events: { start: {}, end: {} }, addEvent: function (event) { if (event.type == "STS" || event.type == "CTS") { if (event.traceId == event.originId) { // If this condition is met this is the first event of the trace if (validateOriginEvent(event)) { this.origin = event; this.timeRange.addStart(event.time); } } else { var children = this.events.start[event.parentId]; if (!children) { this.events.start[event.parentId] = children = []; } children.push(event); this.timeRange.addStart(event.time); } } else { this.events.end[event.traceId] = event; this.timeRange.addEnd(event.time); } }, getTraceTree: function () { if (this.isBuilt) { return this.origin; } else { var startEvents = this.events.start; var endEvents = this.events.end; var origin = this.origin; origin.timeRange = this.timeRange; var parents = []; parents.push(origin); while (Object.keys(startEvents).length > 0 || parents.length > 0) { var parent = parents.shift(); parent.end = endEvents[parent.traceId]; parent.children = startEvents[parent.traceId] || []; delete startEvents[parent.traceId]; var childrenLen = parent.children.length; if (parent.type == "CTS") { // Parent STS can have many children that are initiated multiple client calls // Parent CTS will have only one child which is the service call (Merge it to CTS itself) // In all iterations except the initial* iteration Parent will be CTS if (childrenLen == 1) { // The only child of CTS is a STS // Add children of STS to CTS var childSTS = parent.children[0]; childSTS.end = endEvents[childSTS.traceId]; parent.service = childSTS; parent.clientName = parent.instanceName; parent.instanceName = childSTS.instanceName; parent.children = startEvents[childSTS.traceId] || []; delete startEvents[childSTS.traceId]; } } parent.children.sort(function (a, b) { return a.time - b.time; }); childrenLen = parent.children.length; for (var i = 0; i < childrenLen; i++) { var childCTS = parent.children[i]; childCTS.end = endEvents[childCTS.traceId]; parents.push(childCTS); } } this.isBuilt = true; this.events = null; console.log("TraceTree:"); console.log(origin); if (origin.timeRange.end == 0) { // This condition will satisfy if no end events are available origin.timeRange.end = (new Date()).getTime(); } return origin; } } }; } traceGroup.addEvent(event); } console.log("Trace groups:"); console.log(traceGroups); return traceGroups; } function itrValidTraceGroups(traceGroups, callback) { for (var key in traceGroups) { if (!traceGroups.hasOwnProperty(key)) { continue; } var traceGroup = traceGroups[key]; if (traceGroup.origin && validateOriginEvent(traceGroup.origin)) { callback(traceGroup); } } } function validateOriginEvent(event) { return event.instanceName && event.time && event.traceId && event.url && event.httpMethod && !event.parentId; } ================================================ FILE: analytics/wso2das-tracing-capp/capp-content/artifacts.xml ================================================ ================================================ FILE: analytics/wso2das-tracing-capp/pom.xml ================================================ org.wso2.msf4j msf4j-parent 2.8.14-SNAPSHOT ../../poms/parent/pom.xml 4.0.0 wso2das-msf4j-tracing WSO2 DAS MSF4J message tracing C-App org.wso2.msf4j msf4j-analytics org.apache.maven.plugins maven-assembly-plugin zip.xml false ${project.artifactId}-${project.version} make-assembly package single org.apache.maven.plugins maven-antrun-plugin package run org.apache.maven.plugins maven-jar-plugin default-jar none org.wso2.msf4j.example.Application ================================================ FILE: analytics/wso2das-tracing-capp/zip.xml ================================================ bin / zip false capp-content/ / ================================================ FILE: analytics/zipkin-tracing/pom.xml ================================================ 4.0.0 org.wso2.msf4j msf4j-parent 2.8.14-SNAPSHOT ../../poms/parent/pom.xml zipkin-tracing jar Zipkin Tracing Module This module provides Zipkin tracing support for MSF4J server-less mode. https://github.com/wso2/msf4j org.wso2.msf4j msf4j-core io.zipkin.brave brave-spancollector-http io.zipkin.brave brave-http org.wso2.msf4j msf4j-analytics-common io.github.openfeign feign-core org.testng testng test org.apache.maven.plugins maven-compiler-plugin org.apache.rat apache-rat-plugin org.apache.maven.plugins maven-checkstyle-plugin org.apache.maven.plugins maven-surefire-plugin -Dmsf4jTestRunning src/test/resources/testng.xml org.apache.maven.plugins maven-deploy-plugin coverage org.apache.maven.plugins maven-surefire-plugin ${argLine} -Xmx512m org.jacoco jacoco-maven-plugin prepare-agent prepare-agent report prepare-package report org.jacoco jacoco-maven-plugin release org.apache.maven.plugins maven-source-plugin true attach-sources package jar-no-fork org.apache.maven.plugins maven-javadoc-plugin true http://docs.oracle.com/javaee/6/api/ *.internal.* Licensed under the Apache License, Version 2.0]]> attach-javadoc package jar org.apache.maven.plugins maven-gpg-plugin ${gpg.passphrase} ${gpg.useagent} sign org.apache.maven.plugins maven-source-plugin org.apache.maven.plugins maven-javadoc-plugin org.apache.maven.plugins maven-gpg-plugin ================================================ FILE: analytics/zipkin-tracing/src/main/java/org/wso2/msf4j/analytics/zipkintracing/MSF4JZipkinTracingInterceptor.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.analytics.zipkintracing; import com.github.kristofa.brave.Brave; import com.github.kristofa.brave.EmptySpanCollectorMetricsHandler; import com.github.kristofa.brave.ServerRequestInterceptor; import com.github.kristofa.brave.ServerResponseInterceptor; import com.github.kristofa.brave.http.DefaultSpanNameProvider; import com.github.kristofa.brave.http.HttpResponse; import com.github.kristofa.brave.http.HttpServerRequest; import com.github.kristofa.brave.http.HttpServerRequestAdapter; import com.github.kristofa.brave.http.HttpServerResponseAdapter; import com.github.kristofa.brave.http.HttpSpanCollector; import org.wso2.msf4j.Interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import org.wso2.msf4j.ServiceMethodInfo; import org.wso2.msf4j.analytics.common.tracing.TracingConstants; /** * Interceptor for tracing server side request/response flows to Zipkin. */ public class MSF4JZipkinTracingInterceptor implements Interceptor { private final ServerRequestInterceptor reqInterceptor; private final ServerResponseInterceptor respInterceptor; private static final String RESPONDER_ATTRIBUTE = "responder-attribute"; /** * Constructor of the MSF4JTracingInterceptor. * * @param microServiceName Name of the Microservice */ public MSF4JZipkinTracingInterceptor(String microServiceName) { this(microServiceName, TracingConstants.DEFAULT_ZIPKIN_URL); } /** * Constructor of the MSF4JTracingInterceptor. * * @param microServiceName Name of the Microservice * @param zipkinUrl Base URL of the Zipkin server */ public MSF4JZipkinTracingInterceptor(String microServiceName, String zipkinUrl) { Brave.Builder builder = new Brave.Builder(microServiceName); builder.spanCollector(HttpSpanCollector.create(zipkinUrl, new EmptySpanCollectorMetricsHandler())); Brave brave = builder.build(); reqInterceptor = brave.serverRequestInterceptor(); respInterceptor = brave.serverResponseInterceptor(); } /** * Intercepts the server request flow and extract request information * to be published to the Zipkin server for tracing. */ @Override public boolean preCall(Request request, Response responder, ServiceMethodInfo serviceMethodInfo) throws Exception { serviceMethodInfo.setAttribute(RESPONDER_ATTRIBUTE, responder); HttpServerRequest req = new TraceableHttpServerRequest(request); HttpServerRequestAdapter reqAdapter = new HttpServerRequestAdapter(req, new DefaultSpanNameProvider()); reqInterceptor.handle(reqAdapter); return true; } /** * Intercepts the server response flow and extract response information * to be published to the Zipkin server for tracing. */ @Override public void postCall(Request request, int status, ServiceMethodInfo serviceMethodInfo) throws Exception { HttpResponse httpResponse = new TraceableHttpServerResponse((Response) serviceMethodInfo .getAttribute(RESPONDER_ATTRIBUTE)); HttpServerResponseAdapter adapter = new HttpServerResponseAdapter(httpResponse); respInterceptor.handle(adapter); } } ================================================ FILE: analytics/zipkin-tracing/src/main/java/org/wso2/msf4j/analytics/zipkintracing/TraceableHttpClientRequest.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.analytics.zipkintracing; import com.github.kristofa.brave.http.HttpClientRequest; import feign.Request; import java.net.URI; import java.util.ArrayList; import java.util.Collection; /** * Adaptor class for client side request object tracing */ public class TraceableHttpClientRequest implements HttpClientRequest { private final Request request; public TraceableHttpClientRequest(Request request) { this.request = request; } @Override public void addHeader(String header, String value) { Collection existingValues = request.headers().get(header); if (existingValues == null) { existingValues = new ArrayList<>(); } existingValues.add(value); request.headers().put(header, existingValues); } @Override public URI getUri() { return URI.create(request.url()); } @Override public String getHttpMethod() { return request.method(); } } ================================================ FILE: analytics/zipkin-tracing/src/main/java/org/wso2/msf4j/analytics/zipkintracing/TraceableHttpClientResponse.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.analytics.zipkintracing; import com.github.kristofa.brave.http.HttpResponse; import feign.Response; /** * Adaptor class for client side response object tracing */ public class TraceableHttpClientResponse implements HttpResponse { private final Response response; public TraceableHttpClientResponse(Response response) { this.response = response; } @Override public int getHttpStatusCode() { return response.status(); } } ================================================ FILE: analytics/zipkin-tracing/src/main/java/org/wso2/msf4j/analytics/zipkintracing/TraceableHttpServerRequest.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.analytics.zipkintracing; import com.github.kristofa.brave.http.HttpServerRequest; import org.wso2.msf4j.Request; import java.net.URI; /** * Adaptor class for server side request object tracing */ public class TraceableHttpServerRequest implements HttpServerRequest { private final Request req; TraceableHttpServerRequest(Request req) { this.req = req; } @Override public String getHttpHeaderValue(String headerName) { return req.getHeader(headerName); } @Override public URI getUri() { return URI.create(req.getUri()); } @Override public String getHttpMethod() { return req.getHttpMethod(); } } ================================================ FILE: analytics/zipkin-tracing/src/main/java/org/wso2/msf4j/analytics/zipkintracing/TraceableHttpServerResponse.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.analytics.zipkintracing; import com.github.kristofa.brave.http.HttpResponse; import org.wso2.msf4j.Response; /** * Adaptor class for server side response object tracing */ public class TraceableHttpServerResponse implements HttpResponse { private Response response; TraceableHttpServerResponse(Response response) { this.response = response; } @Override public int getHttpStatusCode() { return response.getStatusCode(); } } ================================================ FILE: analytics/zipkin-tracing/src/test/java/org/wso2/msf4j/analytics/zipkintracing/TraceableHttpClientRequestTest.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.analytics.zipkintracing; import com.github.kristofa.brave.http.HttpClientRequest; import feign.Request; import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.net.MalformedURLException; import java.net.URI; import java.nio.charset.Charset; import java.util.HashMap; import javax.ws.rs.HttpMethod; /** * Class for testing TraceableHttpClient. */ public class TraceableHttpClientRequestTest extends Assert { private Request request; private HttpClientRequest httpRequest; @BeforeClass public void setUp() throws MalformedURLException { request = Request.create(HttpMethod.GET, URI.create("msf4j").toString(), new HashMap<>(), null, Charset.defaultCharset()); httpRequest = new TraceableHttpClientRequest(request); } @Test public void testAddHeader() { httpRequest.addHeader("testK", "testV"); assertTrue(request.headers().get("testK").contains("testV")); } @Test public void testGetUri() { assertEquals(httpRequest.getUri(), URI.create("msf4j")); } @Test public void testGetHttpMethod() { assertEquals(httpRequest.getHttpMethod(), HttpMethod.GET); } } ================================================ FILE: analytics/zipkin-tracing/src/test/java/org/wso2/msf4j/analytics/zipkintracing/TraceableHttpClientResponseTest.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.analytics.zipkintracing; import com.github.kristofa.brave.http.HttpResponse; import feign.Response; import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.io.IOException; import java.util.HashMap; /** * Class for testing TraceableHttpClientResponse. */ public class TraceableHttpClientResponseTest extends Assert { private Response response; private HttpResponse httpResponse; @BeforeClass public void setUp() throws IOException { response = Response.builder().status(200).headers(new HashMap<>()).build(); httpResponse = new TraceableHttpClientResponse(response); } @Test public void testGetStatusCode() { assertEquals(httpResponse.getHttpStatusCode(), response.status()); } } ================================================ FILE: analytics/zipkin-tracing/src/test/java/org/wso2/msf4j/analytics/zipkintracing/TraceableHttpServerRequestTest.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.analytics.zipkintracing; import com.github.kristofa.brave.http.HttpServerRequest; import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.HttpVersion; import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import org.wso2.msf4j.Request; import org.wso2.transport.http.netty.message.HttpCarbonMessage; import java.io.IOException; import java.net.URI; import javax.ws.rs.HttpMethod; /** * Class for testing TraceableHttpServerRequest. */ public class TraceableHttpServerRequestTest extends Assert { private Request request; private HttpServerRequest httpServerRequest; @BeforeClass public void setUp() throws IOException { HttpCarbonMessage httpCarbonMessage = new HttpCarbonMessage( new DefaultHttpRequest(HttpVersion.HTTP_1_1, io.netty.handler.codec.http.HttpMethod.GET, "msf4j")); httpCarbonMessage.setHeader("testK", "testV"); httpCarbonMessage.setHttpMethod(HttpMethod.GET); request = new Request(httpCarbonMessage); request.setProperty("TO", "msf4j"); httpServerRequest = new TraceableHttpServerRequest(request); } @Test public void testGetHeader() { assertEquals(httpServerRequest.getHttpHeaderValue("testK"), "testV"); } @Test public void testGetUrl() { assertEquals(httpServerRequest.getUri(), URI.create("msf4j")); } @Test public void testGetHttpMethod() { assertEquals(httpServerRequest.getHttpMethod(), HttpMethod.GET); } } ================================================ FILE: analytics/zipkin-tracing/src/test/java/org/wso2/msf4j/analytics/zipkintracing/TraceableHttpServerResponseTest.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.analytics.zipkintracing; import com.github.kristofa.brave.http.HttpResponse; import io.netty.handler.codec.http.DefaultHttpResponse; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import org.wso2.msf4j.Response; import org.wso2.transport.http.netty.message.HttpCarbonMessage; import java.io.IOException; /** * Class for testing TraceableHttpServerResponse. */ public class TraceableHttpServerResponseTest extends Assert { private Response response; private HttpResponse httpResponse; @BeforeClass public void setUp() throws IOException { HttpCarbonMessage httpCarbonMessage = new HttpCarbonMessage(new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK)); response = new Response(httpCarbonMessage); response.setStatus(200); httpResponse = new TraceableHttpServerResponse(response); } @Test public void testGetStatusCode() { assertEquals(httpResponse.getHttpStatusCode(), response.getStatusCode()); } } ================================================ FILE: analytics/zipkin-tracing/src/test/resources/testng.xml ================================================ ================================================ FILE: archetypes/README.md ================================================ #Creating a Microservice using the msf4j-microservice Maven archetype A Microservice based on WSO2 Microservices Framework for Java (MSF4J) can be created with single command using this Maven archetype. Here is an example; ``` mvn archetype:generate -DarchetypeGroupId=org.wso2.msf4j -DarchetypeArtifactId=msf4j-microservice -DarchetypeVersion=2.6.2 -DgroupId=org.example -DartifactId=myservice -Dversion=1.0.0-SNAPSHOT -Dpackage=org.example.service ``` The above command will create an MSF4J microservice project structure for you similar to the one shown below; ``` myservice ├── pom.xml └── src └── main └── java └── org └── example └── service ├── Application.java └── MyService.java ``` ##Properties The following table lists down the properties specific to the msf4j-microservice Maven archetype; | Property | Description | Mandatory/Optional | | ------------------- | ------------------------------- | ------------------ | | archetypeGroupId | The groupId of the archetype | Mandatory | | archetypeArtifactId | The artifactId of the archetype | Mandatory | | archetypeVersion | The version of the archetype | Optional | The table below lists down the properties which are specific to the project you create. | Property | Description | Default value | | --------------- | ------------------------------------ | --------------------------- | | groupId | The groupId of the project | org.example | | artifactId | The artifactId of the project | msf4j-service | | version | The version of the project | 1.0.0-SNAPSHOT | | package | The package hierarchy of the project | org.example.service | | serviceClass | The names of the microservice class | org.example.service | For more information about Maven archetypes, see [http://maven.apache.org/archetype/maven-archetype-plugin/generate-mojo.html](http://maven.apache.org/archetype/maven-archetype-plugin/generate-mojo.html) ================================================ FILE: archetypes/msf4j-microservice/pom.xml ================================================ org.wso2.msf4j msf4j-parent 2.8.14-SNAPSHOT ../../poms/parent/pom.xml 4.0.0 msf4j-microservice maven-archetype WSO2 MSF4J - Microservice Archetype This an archetype for WSO2 MSF4J microservice ${project.basedir}/src/main/resources true **/* org.apache.maven.plugins maven-resources-plugin ^*^ false ================================================ FILE: archetypes/msf4j-microservice/src/main/resources/META-INF/maven/archetype-metadata.xml ================================================ src/main/java **/*.java org.example msf4j-service 1.0.0-SNAPSHOT org.example.service MyService ================================================ FILE: archetypes/msf4j-microservice/src/main/resources/archetype-resources/pom.xml ================================================ org.wso2.msf4j msf4j-service ^project.version^ 4.0.0 ${groupId} ${artifactId} ${version} WSO2 MSF4J Microservice ${package}.Application ================================================ FILE: archetypes/msf4j-microservice/src/main/resources/archetype-resources/src/main/java/Application.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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 ${package}; import org.wso2.msf4j.MicroservicesRunner; /** * Application entry point. * * @since ${version} */ public class Application { public static void main(String[] args) { new MicroservicesRunner() .deploy(new ${serviceClass}()) .start(); } } ================================================ FILE: archetypes/msf4j-microservice/src/main/resources/archetype-resources/src/main/java/__serviceClass__.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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 ${package}; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; /** * This is the Microservice resource class. * See https://github.com/wso2/msf4j#getting-started * for the usage of annotations. * * @since ${version} */ @Path("/service") public class ${serviceClass} { @GET @Path("/") public String get() { // TODO: Implementation for HTTP GET request System.out.println("GET invoked"); return "Hello from WSO2 MSF4J"; } @POST @Path("/") public void post() { // TODO: Implementation for HTTP POST request System.out.println("POST invoked"); } @PUT @Path("/") public void put() { // TODO: Implementation for HTTP PUT request System.out.println("PUT invoked"); } @DELETE @Path("/") public void delete() { // TODO: Implementation for HTTP DELETE request System.out.println("DELETE invoked"); } } ================================================ FILE: client/README.md ================================================ # MSF4J Client MSF4J client which allow making HTTP calls to other microservices as well as external APIs. ================================================ FILE: client/pom.xml ================================================ 4.0.0 org.wso2.msf4j msf4j-parent 2.8.14-SNAPSHOT ../poms/parent/pom.xml msf4j-client bundle WSO2 MSF4J client WSO2 MSF4J client https://github.com/wso2/msf4j org.wso2.msf4j msf4j-analytics org.wso2.msf4j msf4j-core org.wso2.orbit.org.apache.httpcomponents httpclient io.github.openfeign feign-core io.github.openfeign feign-jackson com.fasterxml.jackson.core jackson-databind io.github.openfeign feign-gson io.github.openfeign feign-hystrix org.apache.httpcomponents httpclient-osgi org.apache.httpcomponents httpcore-osgi com.fasterxml.jackson.core jackson-core com.fasterxml.jackson.core jackson-databind com.fasterxml.jackson.core jackson-annotations io.zipkin.brave brave-spancollector-http io.zipkin.brave brave-http org.wso2.msf4j zipkin-tracing org.wso2.carbon.utils org.wso2.carbon.utils org.wso2.msf4j msf4j-core org.testng testng org.wso2.msf4j.internal.* !org.wso2.msf4j.client.internal.*, org.wso2.msf4j.client.*;version="${msf4j.version}" feign.*;version="${feign.version}", feign.gson.*;version="${feign.version}", com.google.gson.*;version="${gson.version.range}", com.google.common.*;version="${guava.version.range}", javax.annotation.*, javax.ws.rs.*, javax.xml.bind;resolution:=optional, javax.xml.bind.annotation;resolution:=optional, org.osgi.framework.*;version="${osgi.framework.import.version.range}", org.osgi.util.tracker; version="${osgi.service.tracker.import.version.range}", com.github.kristofa.brave.*;resolution:=optional, org.wso2.msf4j.analytics.*;version="${msf4j.version}";resolution:=optional, org.slf4j.*;version="${slf4j.version.range}";resolution:=optional, ================================================ FILE: client/src/main/java/org/wso2/msf4j/client/ApacheHttpClient.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ package org.wso2.msf4j.client; import feign.Client; import feign.Request; import feign.Response; import feign.Util; import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.StatusLine; import org.apache.http.client.CookieStore; import org.apache.http.client.HttpClient; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.methods.RequestBuilder; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.client.utils.URIBuilder; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.BasicCookieStore; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.HttpContext; import org.apache.http.util.EntityUtils; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import static feign.Util.UTF_8; /** * This module directs Feign's http requests to Apache's * HttpClient. Ex. *
 * GitHub github = Feign.builder().client(new ApacheHttpClient()).target(GitHub.class,
 * "https://api.github.com");
 *
 * Based on Square, Inc's Retrofit ApacheClient implementation
 */
public final class ApacheHttpClient implements Client {
    private static final String ACCEPT_HEADER_NAME = "Accept";
    private final ThreadLocal httpContextThreadLocal = new ThreadLocal<>();
    private final HttpClient client;

    public ApacheHttpClient() {
        this(HttpClientBuilder.create().build());
    }

    public ApacheHttpClient(HttpClient client) {
        this.client = client;
    }

    @Override
    public Response execute(Request request, Request.Options options) throws IOException {
        HttpUriRequest httpUriRequest;
        HttpContext httpContext = httpContextThreadLocal.get();
        if (httpContext == null) {
            CookieStore cookieStore = new BasicCookieStore();
            httpContext = new BasicHttpContext();
            httpContext.setAttribute(HttpClientContext.COOKIE_STORE, cookieStore);
            httpContextThreadLocal.set(httpContext);
        }

        try {
            httpUriRequest = toHttpUriRequest(request, options);
        } catch (URISyntaxException e) {
            throw new IOException("URL '" + request.url() + "' couldn't be parsed into a URI", e);
        }
        HttpResponse httpResponse = client.execute(httpUriRequest, httpContext);
        return toFeignResponse(httpResponse).toBuilder().request(request).build();
    }

    HttpUriRequest toHttpUriRequest(Request request, Request.Options options) throws
            UnsupportedEncodingException, MalformedURLException, URISyntaxException {
        RequestBuilder requestBuilder = RequestBuilder.create(request.method());

        //per request timeouts
        RequestConfig requestConfig = RequestConfig
                .custom()
                .setConnectTimeout(options.connectTimeoutMillis())
                .setSocketTimeout(options.readTimeoutMillis())
                .build();
        requestBuilder.setConfig(requestConfig);

        URI uri = new URIBuilder(request.url()).build();

        requestBuilder.setUri(uri.getScheme() + "://" + uri.getAuthority() + uri.getRawPath());

        //request query params
        List queryParams = URLEncodedUtils.parse(uri, requestBuilder.getCharset().name
                ());
        for (NameValuePair queryParam : queryParams) {
            requestBuilder.addParameter(queryParam);
        }

        //request headers
        boolean hasAcceptHeader = false;
        for (Map.Entry> headerEntry : request.headers().entrySet()) {
            String headerName = headerEntry.getKey();
            if (headerName.equalsIgnoreCase(ACCEPT_HEADER_NAME)) {
                hasAcceptHeader = true;
            }

            if (headerName.equalsIgnoreCase(Util.CONTENT_LENGTH)) {
                // The 'Content-Length' header is always set by the Apache client and it
                // doesn't like us to set it as well.
                continue;
            }

            for (String headerValue : headerEntry.getValue()) {
                requestBuilder.addHeader(headerName, headerValue);
            }
        }
        //some servers choke on the default accept string, so we'll set it to anything
        if (!hasAcceptHeader) {
            requestBuilder.addHeader(ACCEPT_HEADER_NAME, "*/*");
        }

        //request body
        if (request.body() != null) {
            HttpEntity entity = null;
            if (request.charset() != null) {
                ContentType contentType = getContentType(request);
                String content = new String(request.body(), request.charset());
                entity = new StringEntity(content, contentType);
            } else {
                entity = new ByteArrayEntity(request.body());
            }

            requestBuilder.setEntity(entity);
        }

        return requestBuilder.build();
    }

    private ContentType getContentType(Request request) {
        ContentType contentType = ContentType.DEFAULT_TEXT;
        for (Map.Entry> entry : request.headers().entrySet()) {
            if (entry.getKey().equalsIgnoreCase("Content-Type")) {
                Collection values = entry.getValue();
                if (values != null && !values.isEmpty()) {
                    contentType = ContentType.create(entry.getValue().iterator().next(), request.charset());
                    break;
                }
            }
        }
        return contentType;
    }

    Response toFeignResponse(HttpResponse httpResponse) throws IOException {
        StatusLine statusLine = httpResponse.getStatusLine();
        int statusCode = statusLine.getStatusCode();

        String reason = statusLine.getReasonPhrase();

        Map> headers = new HashMap>();
        for (Header header : httpResponse.getAllHeaders()) {
            String name = header.getName();
            String value = header.getValue();

            Collection headerValues = headers.get(name);
            if (headerValues == null) {
                headerValues = new ArrayList();
                headers.put(name, headerValues);
            }
            headerValues.add(value);
        }

        return Response.builder()
                .status(statusCode)
                .reason(reason)
                .headers(headers)
                .body(toFeignBody(httpResponse))
                .build();
    }

    Response.Body toFeignBody(HttpResponse httpResponse) throws IOException {
        final HttpEntity entity = httpResponse.getEntity();
        if (entity == null) {
            return null;
        }
        return new HttpEntityResponseBody(entity);
    }

    /**
     * Implements {@link feign.Response.Body} which represents HTTP response body
     */
    private static class HttpEntityResponseBody implements Response.Body {
        final HttpEntity entity;

        HttpEntityResponseBody(HttpEntity entity) {
            this.entity = entity;
        }

        @Override
        public Integer length() {
            return entity.getContentLength() >= 0 && entity.getContentLength() <= Integer.MAX_VALUE ?
                    (int) entity.getContentLength() : null;
        }

        @Override
        public boolean isRepeatable() {
            return entity.isRepeatable();
        }

        @Override
        public InputStream asInputStream() throws IOException {
            return entity.getContent();
        }

        @Override
        public Reader asReader() throws IOException {
            return new InputStreamReader(asInputStream(), UTF_8);
        }

        @Override
        public void close() throws IOException {
            EntityUtils.consume(entity);
        }
    }
}


================================================
FILE: client/src/main/java/org/wso2/msf4j/client/FeginZipkinTracingClient.java
================================================
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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.wso2.msf4j.client;

import com.github.kristofa.brave.Brave;
import com.github.kristofa.brave.ClientRequestAdapter;
import com.github.kristofa.brave.ClientRequestInterceptor;
import com.github.kristofa.brave.ClientResponseAdapter;
import com.github.kristofa.brave.ClientResponseInterceptor;
import com.github.kristofa.brave.EmptySpanCollectorMetricsHandler;
import com.github.kristofa.brave.http.DefaultSpanNameProvider;
import com.github.kristofa.brave.http.HttpClientRequest;
import com.github.kristofa.brave.http.HttpClientRequestAdapter;
import com.github.kristofa.brave.http.HttpClientResponseAdapter;
import com.github.kristofa.brave.http.HttpResponse;
import com.github.kristofa.brave.http.HttpSpanCollector;
import feign.Client;
import feign.Request;
import feign.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.msf4j.analytics.common.tracing.TracingConstants;
import org.wso2.msf4j.analytics.zipkintracing.TraceableHttpClientRequest;
import org.wso2.msf4j.analytics.zipkintracing.TraceableHttpClientResponse;

import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 *  Client for Zipkin Tracing.
 */
public class FeginZipkinTracingClient implements Client {
    private static final Logger log = LoggerFactory.getLogger(FeignTracingClient.class);
    private final Client clientDelegate;
    private final ClientRequestInterceptor requestInterceptor;
    private final ClientResponseInterceptor responseInterceptor;

    /**
     * Constructor of FeignTracingClient.
     */
    public FeginZipkinTracingClient(Client client, String instanceName) {
        this(client, instanceName, TracingConstants.DEFAULT_ZIPKIN_URL);
    }

    /**
     * Constructor of FeginZipkinTracingClient.
     *
     * @param client
     * @param instanceName
     * @param zipkinUrl URL of the receiver of DAS server.
     */
    public FeginZipkinTracingClient(Client client, String instanceName, String zipkinUrl) {
        this.clientDelegate = client;
        Brave.Builder builder = new Brave.Builder(instanceName);
        builder.spanCollector(HttpSpanCollector.create(zipkinUrl, new EmptySpanCollectorMetricsHandler()));
        Brave brave = builder.build();
        requestInterceptor = brave.clientRequestInterceptor();
        responseInterceptor = brave.clientResponseInterceptor();
    }

    @Override
    public Response execute(Request request, Request.Options options) throws IOException {
        Map> traceHeaders = new HashMap<>();
        traceHeaders.putAll(request.headers());
        Request wrappedRequest =
                Request.create(request.method(), request.url(), traceHeaders, request.body(), request.charset());
        HttpClientRequest httpClientRequest = new TraceableHttpClientRequest(wrappedRequest);
        ClientRequestAdapter adapter = new HttpClientRequestAdapter(httpClientRequest, new DefaultSpanNameProvider());
        requestInterceptor.handle(adapter);

        Response response = clientDelegate.execute(wrappedRequest, options);

        HttpResponse httpResponse = new TraceableHttpClientResponse(response);
        ClientResponseAdapter responseAdapter = new HttpClientResponseAdapter(httpResponse);
        responseInterceptor.handle(responseAdapter);
        return response;
    }
}



================================================
FILE: client/src/main/java/org/wso2/msf4j/client/FeignClientWrapper.java
================================================
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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.wso2.msf4j.client;

import feign.Client;
import feign.Request;
import feign.Response;

import java.io.IOException;

/**
 * Wrapper for #Feign.Client
 */
public class FeignClientWrapper implements Client {

    private final Client clientDelegate;

    public FeignClientWrapper(Client client) {
        this.clientDelegate = client;
    }

    @Override
    public Response execute(Request request, Request.Options options) throws IOException {
        return clientDelegate.execute(request, options);
    }
}


================================================
FILE: client/src/main/java/org/wso2/msf4j/client/FeignTracingClient.java
================================================
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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.wso2.msf4j.client;

import feign.Client;
import feign.Request;
import feign.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.msf4j.analytics.common.tracing.TraceEvent;
import org.wso2.msf4j.analytics.common.tracing.TracingConstants;
import org.wso2.msf4j.analytics.common.tracing.TracingEventTracker;
import org.wso2.msf4j.analytics.common.tracing.TracingUtil;

import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Supports tracing capabilities with WSO2DAS
 */
class FeignTracingClient implements Client {
    private static final Logger log = LoggerFactory.getLogger(FeignTracingClient.class);
    private final String instanceId;
    private final String instanceName;
    private final String dasUrl;
    private final Client clientDelegate;

    /**
     * Constructor of FeignTracingClient.
     */
    public FeignTracingClient(Client client, String instanceName) {
        this(client, instanceName, TracingConstants.DAS_RECEIVER_URL);
    }

    /**
     * Constructor of FeignTracingClient with custom SSL configuration
     *
     * @param dasReceiverUrl URL of the receiver of DAS server
     */
    public FeignTracingClient(Client client, String instanceName, String dasReceiverUrl) {
        this.instanceName = instanceName;
        this.dasUrl = dasReceiverUrl;
        this.instanceId = TracingUtil.generateUniqueId();
        this.clientDelegate = client;
    }

    @Override
    public Response execute(Request request, Request.Options options) throws IOException {
        TraceEvent clientStartTraceEvent = generateClientStartTraceEvent(request);
        Request traceableRequest = tracePreRequest(request, clientStartTraceEvent);
        Response response = clientDelegate.execute(traceableRequest, options);
        tracePostRequest(response, clientStartTraceEvent);
        return response;
    }

    private TraceEvent generateClientStartTraceEvent(Request request) {
        long time = new Date().getTime();
        String clientTraceId;
        String traceOriginId;
        String traceParentId = null;
        TraceEvent parentEvent = TracingEventTracker.getTraceEvent();
        if (parentEvent == null) {
            traceOriginId = TracingUtil.generateUniqueId();
            clientTraceId = traceOriginId;
        } else {
            traceOriginId = parentEvent.getOriginId();
            clientTraceId = TracingUtil.generateUniqueId();
            traceParentId = parentEvent.getTraceId();
        }
        TraceEvent clientStartTraceEvent = new TraceEvent(
                TracingConstants.CLIENT_TRACE_START,
                clientTraceId,
                traceOriginId,
                time
        );
        clientStartTraceEvent.setInstanceId(instanceId);
        clientStartTraceEvent.setInstanceName(instanceName);
        clientStartTraceEvent.setParentId(traceParentId);
        clientStartTraceEvent.setHttpMethod(request.method());
        clientStartTraceEvent.setUrl(request.url());
        if (log.isDebugEnabled()) {
            log.debug("clientStartTraceEvent: " + ModelUtils.toString(clientStartTraceEvent));
        }
        return clientStartTraceEvent;
    }

    private Request tracePreRequest(Request request, TraceEvent traceEvent) {
        // set tracing headers to HTTP request
        Map> traceHeaders = new HashMap<>();
        traceHeaders.putAll(request.headers());
        traceHeaders.put(TracingConstants.TRACE_ID_HEADER, Collections.singletonList(traceEvent.getTraceId()));
        traceHeaders.put(TracingConstants.TRACE_ORIGIN_ID_HEADER, Collections.singletonList(traceEvent.getOriginId()));

        // publish event to DAS
        TracingUtil.pushToDAS(traceEvent, dasUrl);
        return Request.create(request.method(), request.url(), traceHeaders, request.body(), request.charset());
    }

    /**
     * Publish client request information to DAS after response is received
     */
    private void tracePostRequest(Response response, TraceEvent traceEvent) {
        long time = new Date().getTime();
        if (traceEvent != null) {
            TraceEvent clientEndTraceEvent = new TraceEvent(
                    TracingConstants.CLIENT_TRACE_END,
                    traceEvent.getTraceId(),
                    traceEvent.getOriginId(),
                    time
            );
            clientEndTraceEvent.setStatusCode(response.status());
            if (log.isDebugEnabled()) {
                log.debug("ClientEndTraceEvent: " + ModelUtils.toString(clientEndTraceEvent));
            }
            TracingUtil.pushToDAS(clientEndTraceEvent, dasUrl);
        }
    }
}


================================================
FILE: client/src/main/java/org/wso2/msf4j/client/MSF4JClient.java
================================================
/*
 *  Copyright (c) 2016 WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */
package org.wso2.msf4j.client;

import feign.Client;
import feign.Feign;
import feign.RequestInterceptor;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.codec.ErrorDecoder;
import feign.gson.GsonDecoder;
import feign.gson.GsonEncoder;
import feign.hystrix.FallbackFactory;
import feign.hystrix.HystrixFeign;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.carbon.utils.StringUtils;
import org.wso2.msf4j.analytics.common.tracing.TracingConstants;
import org.wso2.msf4j.client.codec.DefaultErrorDecoder;
import org.wso2.msf4j.client.codec.RestErrorResponseMapper;
import org.wso2.msf4j.client.exception.RestServiceException;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;

/**
 * MSF4J client.
 *
 * @param  REST service API interface
 */
public class MSF4JClient {
    private static final Logger log = LoggerFactory.getLogger(MSF4JClient.class);
    private final T api;

    public MSF4JClient(T api) {
        this.api = api;
    }

    public T api() {
        return api;
    }

    /**
     * MSF4J client builder
     *
     * @param  API interface
     */
    public static class Builder {

        private final List requestInterceptors = new ArrayList();
        private final Map> errorCodeExceptionMap = new HashMap<>();
        private FallbackFactory fallbackFactory;
        private boolean enableCircuitBreaker;
        private boolean enableTracing;
        private String instanceName;
        private String analyticsEndpoint;
        private String serviceEndpoint;
        private SSLContext sslContext;
        private HostnameVerifier hostnameVerifier;
        private boolean decode404;
        private Class apiClass;
        private ErrorDecoder errorDecoder = new DefaultErrorDecoder(errorCodeExceptionMap);
        private Encoder encoder = new GsonEncoder(ModelUtils.GSON);
        private Decoder decoder = new GsonDecoder(ModelUtils.GSON);
        private TracingConstants.TracingType tracingType = TracingConstants.TracingType.DAS;

        public Feign.Builder newFeignClientBuilder() {
            return Feign.builder()
                    .encoder(encoder)
                    .decoder(decoder);
        }

        public HystrixFeign.Builder newHystrixFeignClientBuilder() {
            return HystrixFeign.builder()
                    .encoder(encoder)
                    .decoder(decoder);
        }

        /**
         * Adds a single request interceptor to the builder.
         */
        public MSF4JClient.Builder requestInterceptor(RequestInterceptor requestInterceptor) {
            this.requestInterceptors.add(requestInterceptor);
            return this;
        }

        /**
         * Sets the full set of request interceptors for the builder, overwriting any previous
         * interceptors.
         */
        public MSF4JClient.Builder requestInterceptors(Iterable requestInterceptors) {
            this.requestInterceptors.clear();
            for (RequestInterceptor requestInterceptor : requestInterceptors) {
                this.requestInterceptors.add(requestInterceptor);
            }
            return this;
        }

        /**
         * Sets the fallback factory for HystrixFeign client which supports circuit breaker
         */
        public MSF4JClient.Builder fallbackFactory(FallbackFactory fallbackFactory) {
            this.fallbackFactory = fallbackFactory;
            return this;
        }

        public MSF4JClient.Builder enableCircuitBreaker() {
            this.enableCircuitBreaker = true;
            return this;
        }

        public MSF4JClient.Builder enableTracing() {
            this.enableTracing = true;
            return this;
        }

        public MSF4JClient.Builder instanceName(String instanceName) {
            this.instanceName = instanceName;
            return this;
        }

        public MSF4JClient.Builder analyticsEndpoint(String analyticsEndpoint) {
            this.analyticsEndpoint = analyticsEndpoint;
            return this;
        }

        public MSF4JClient.Builder serviceEndpoint(String serviceEndpoint) {
            this.serviceEndpoint = serviceEndpoint;
            return this;
        }

        public MSF4JClient.Builder apiClass(Class apiClass) {
            this.apiClass = apiClass;
            return this;
        }

        public MSF4JClient.Builder decode404(boolean decode404) {
            this.decode404 = decode404;
            return this;
        }

        public MSF4JClient.Builder encoder(Encoder encoder) {
            this.encoder = encoder;
            return this;
        }

        public MSF4JClient.Builder decoder(Decoder decoder) {
            this.decoder = decoder;
            return this;
        }

        public MSF4JClient.Builder tracingType(TracingConstants.TracingType tracingType) {
            this.tracingType = tracingType;
            return this;
        }

        public MSF4JClient.Builder addErrorResponseMapper(RestErrorResponseMapper... responseMappers) {
            Arrays.stream(responseMappers).forEach(rm -> {
                Arrays.stream(rm.getClass().getMethods()).
                        filter(method -> !StringUtils.isNullOrEmptyAfterTrim(rm.getExceptionKey())).
                        findAny().
                        ifPresent(method -> {
                            if (errorCodeExceptionMap.containsKey(rm.getExceptionKey())) {
                                log.warn("RestErrorResponseMapper has already been added for the given exception key " +
                                        "'{}'", rm.getExceptionKey());
                            }
                            errorCodeExceptionMap.put(rm.getExceptionKey(), rm.getExceptionClass());
                        });
            });
            return this;
        }

        public MSF4JClient.Builder errorDecoder(ErrorDecoder errorDecoder) {
            this.errorDecoder = errorDecoder;
            return this;
        }

        public HostnameVerifier getHostnameVerifier() {
            return hostnameVerifier;
        }

        public void setHostnameVerifier(HostnameVerifier hostnameVerifier) {
            this.hostnameVerifier = hostnameVerifier;
        }

        public SSLContext getSslContext() {
            return sslContext;
        }

        public void setSslContext(SSLContext sslContext) {
            this.sslContext = sslContext;
        }

        public MSF4JClient build() {
            MSF4JClient msf4JClient;
            Client client;

            PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
            cm.setMaxTotal(200);
            cm.setDefaultMaxPerRoute(20);

            CloseableHttpClient apacheHttpClient = HttpClients.custom()
                                                              .setSSLContext(sslContext)
                                                              .setSSLHostnameVerifier(hostnameVerifier)
                                                              .setConnectionManager(cm)
                                                              .build();

            if (enableTracing) {
                if (tracingType == TracingConstants.TracingType.ZIPKIN) {
                    client = new FeignClientWrapper(
                            new FeginZipkinTracingClient(new ApacheHttpClient(apacheHttpClient), instanceName,
                                                         analyticsEndpoint));
                } else {
                    client = new FeignClientWrapper(
                            new FeignTracingClient(new ApacheHttpClient(apacheHttpClient), instanceName,
                                                   analyticsEndpoint));
                }
            } else {
                client = new FeignClientWrapper(new ApacheHttpClient(apacheHttpClient));
            }

            if (enableCircuitBreaker) {
                HystrixFeign.Builder builder = newHystrixFeignClientBuilder();
                builder.client(client);
                builder.requestInterceptors(requestInterceptors);
                builder.errorDecoder(errorDecoder);
                if (decode404) {
                    builder.decode404();
                }
                msf4JClient = new MSF4JClient(builder.target(apiClass, serviceEndpoint, fallbackFactory));
            } else {
                Feign.Builder builder = newFeignClientBuilder();
                builder.client(client);
                builder.requestInterceptors(requestInterceptors);
                builder.errorDecoder(errorDecoder);
                if (decode404) {
                    builder.decode404();
                }
                msf4JClient = new MSF4JClient(builder.target(apiClass, serviceEndpoint));
            }
            return msf4JClient;
        }
    }
}


================================================
FILE: client/src/main/java/org/wso2/msf4j/client/ModelUtils.java
================================================
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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.wso2.msf4j.client;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

/**
 * Converts Object to gson
 */
public class ModelUtils {
    public static final Gson GSON = new GsonBuilder().setPrettyPrinting()
            .disableHtmlEscaping().create();

    public static String toString(Object o) {
        return GSON.toJson(o);
    }
}


================================================
FILE: client/src/main/java/org/wso2/msf4j/client/codec/DefaultErrorDecoder.java
================================================
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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.wso2.msf4j.client.codec;

import feign.Response;
import feign.codec.Decoder;
import feign.codec.ErrorDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.msf4j.client.exception.RestServiceException;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * Feign error decoder which translates REST service error response to Java exception class
 * which inherits {@link RestServiceException}
 */
public class DefaultErrorDecoder implements ErrorDecoder {
    private static final Logger log = LoggerFactory.getLogger(DefaultErrorDecoder.class);
    private final Map> errorResponseMappers;
    private Decoder decoder = new MSF4JJacksonDecoder();
    private ErrorDecoder fallbackErrorDecoder = new ErrorDecoder.Default();

    public DefaultErrorDecoder(Map> errorResponseMappers) {
        this.errorResponseMappers = errorResponseMappers;
    }

    @Override
    public Exception decode(String methodKey, Response response) {
        try {
            DefaultRestErrorResponse apiErrorResponse = (DefaultRestErrorResponse) decoder.decode(response,
                    DefaultRestErrorResponse.class);
            if (apiErrorResponse != null && errorResponseMappers.containsKey(apiErrorResponse.getErrorCode())) {
                return getExceptionSupplierFromExceptionClass(
                        errorResponseMappers.get(apiErrorResponse.getErrorCode()), apiErrorResponse.getMessage());
            }
        } catch (IOException e) {
            log.error("Error decoding error response", e);
        } catch (Exception e) {
            log.error("Error instantiating the exception mapped for the REST service error response '{}'",
                    response, e);
        }
        return fallbackErrorDecoder.decode(methodKey, response);
    }

    private RestServiceException getExceptionSupplierFromExceptionClass(Class clazz,
                                                                        String message)
            throws IllegalAccessException, InvocationTargetException, InstantiationException {
        List supportedArguments = Collections.singletonList(message);
        for (Constructor constructor : clazz.getConstructors()) {
            Class[] parameters = constructor.getParameterTypes();
            List arguments = new ArrayList<>();
            for (Class parameter : parameters) {
                supportedArguments
                        .stream()
                        .filter(argumentInstance -> parameter.isAssignableFrom(argumentInstance.getClass()))
                        .findFirst()
                        .ifPresent(arguments::add);
            }
            if (arguments.size() == parameters.length) {
                return (RestServiceException) constructor.newInstance(arguments.toArray(new Object[0]));
            }
        }
        log.warn("Could not instantiate the exception '{}'", clazz.getName());
        return null;
    }
}


================================================
FILE: client/src/main/java/org/wso2/msf4j/client/codec/DefaultRestErrorResponse.java
================================================
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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.wso2.msf4j.client.codec;

import org.wso2.msf4j.client.ModelUtils;

/**
 * Represents a REST service error response. A service response is considered an error response when response HTTP
 * status code is not within 2xx range.
 */
public class DefaultRestErrorResponse {
    private String errorCode;
    private String message;

    /**
     * No arg constructor is required for marshalling
     */
    public DefaultRestErrorResponse() {
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public String getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(String errorCode) {
        this.errorCode = errorCode;
    }

    @Override
    public String toString() {
        return ModelUtils.toString(this);
    }
}


================================================
FILE: client/src/main/java/org/wso2/msf4j/client/codec/MSF4JDecoder.java
================================================
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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.wso2.msf4j.client.codec;

import feign.FeignException;
import feign.Response;
import feign.codec.Decoder;
import feign.codec.StringDecoder;
import feign.gson.GsonDecoder;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Collection;
import javax.ws.rs.core.MediaType;

/**
 * Decoder for fegin client.
 */
public class MSF4JDecoder implements Decoder {

    private GsonDecoder gsonDecoder = new GsonDecoder();
    private StringDecoder stringDecoder = new StringDecoder();
    private static final String CONTENT_TYPE = "Content-Type";

    @Override
    public Object decode(Response response, Type type) throws IOException, FeignException {
        Collection contentTypeHeaders = response.headers().get(CONTENT_TYPE);
        String responseContentType =
                contentTypeHeaders != null ? contentTypeHeaders.iterator().next() : MediaType.TEXT_PLAIN;

        if (responseContentType.equals(MediaType.APPLICATION_JSON)) {
            return gsonDecoder.decode(response, type);
        } else if (responseContentType.equals(MediaType.TEXT_PLAIN) || responseContentType.equals(MediaType.WILDCARD)) {
            return stringDecoder.decode(response, type);
        }
        throw new RuntimeException("Unsupported Content Type " + responseContentType);
    }
}


================================================
FILE: client/src/main/java/org/wso2/msf4j/client/codec/MSF4JJacksonDecoder.java
================================================
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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.wso2.msf4j.client.codec;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.RuntimeJsonMappingException;
import feign.FeignException;
import feign.Response;
import feign.jackson.JacksonDecoder;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.Type;
import java.util.Collections;

/**
 * Decoder class for decoding error response which also handles error responses with 404 HTTP status code
 */
public class MSF4JJacksonDecoder extends JacksonDecoder {

    private final ObjectMapper mapper;

    public MSF4JJacksonDecoder() {
        this(Collections.emptyList());
    }

    public MSF4JJacksonDecoder(Iterable modules) {
        this(new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                .registerModules(modules));
    }

    public MSF4JJacksonDecoder(ObjectMapper mapper) {
        this.mapper = mapper;
    }

    @Override
    public Object decode(Response response, Type type) throws IOException, FeignException {
        if (response.body() == null) {
            return null;
        }
        Reader reader = response.body().asReader();
        if (!reader.markSupported()) {
            reader = new BufferedReader(reader, 1);
        }
        try {
            // Read the first byte to see if we have any data
            reader.mark(1);
            if (reader.read() == -1) {
                return null; // Eagerly returning null avoids "No content to map due to end-of-input"
            }
            reader.reset();
            return mapper.readValue(reader, mapper.constructType(type));
        } catch (RuntimeJsonMappingException e) {
            Throwable cause = e.getCause();
            if (cause != null && cause instanceof IOException) {
                throw IOException.class.cast(cause);
            }
            throw e;
        }
    }
}


================================================
FILE: client/src/main/java/org/wso2/msf4j/client/codec/RestErrorResponseMapper.java
================================================
/*
 *  Copyright (c) 2005-2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */
package org.wso2.msf4j.client.codec;

import org.wso2.msf4j.client.exception.RestServiceException;

/**
 * Interface for returning the key of type {@link String} from REST service error response to instantiate the
 * corresponding interface
 *
 * @param  A Class representing the {@link Exception} which extends {@link RestServiceException} to be thrown for
 *           the given key
 */
public abstract class RestErrorResponseMapper {
    public abstract String getExceptionKey();

    public abstract Class getExceptionClass();
}


================================================
FILE: client/src/main/java/org/wso2/msf4j/client/exception/RestServiceException.java
================================================
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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.wso2.msf4j.client.exception;

/**
 * Base exception class for REST service error responses
 */
public class RestServiceException extends Exception {

    // Always throw the exception HTTP REST service error response
    public RestServiceException(String message) {
        super(message);
    }
}


================================================
FILE: client/test/java/org/wso2/msf4j/client/test/ClientTest.java
================================================
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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.wso2.msf4j.client.test;

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.google.gson.Gson;
import io.netty.handler.codec.http.HttpHeaderNames;
import org.apache.commons.io.IOUtils;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.wso2.msf4j.MicroservicesRunner;
import org.wso2.msf4j.client.codec.RestErrorResponseMapper;
import org.wso2.msf4j.client.exception.RestServiceException;
import org.wso2.msf4j.client.test.client.exception.InvoiceNotFoundResponseMapper;
import org.wso2.msf4j.client.test.client.exception.InvoiceNotFoundRestServiceException;
import org.wso2.msf4j.client.test.exception.CustomerNotFoundMapper;
import org.wso2.msf4j.client.test.exception.InvoiceNotFoundMapper;
import org.wso2.msf4j.client.test.service.CustomerService;
import org.wso2.msf4j.client.test.service.InvoiceService;
import org.wso2.msf4j.client.test.service.ReportService;
import org.wso2.msf4j.formparam.util.StreamUtil;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import javax.ws.rs.HttpMethod;

public class ClientTest {
    private static final String HEADER_VAL_CLOSE = "CLOSE";
    private static final String HOSTNAME = "localhost";
    private static final int PORT = 8090;
    private MicroservicesRunner microservicesRunner1;
    private MicroservicesRunner microservicesRunner2;
    private MicroservicesRunner microservicesRunner3;
    private URI baseURI;

    public static void main(String[] args) {
        MicroservicesRunner microservicesRunner2 = new MicroservicesRunner(8089);
        microservicesRunner2.addExceptionMapper(new InvoiceNotFoundMapper())
                            .deploy(new InvoiceService()).start();
        MicroservicesRunner microservicesRunner1 = new MicroservicesRunner(8088);
        microservicesRunner1.addExceptionMapper(new CustomerNotFoundMapper())
                            .deploy(new CustomerService()).start();


        MicroservicesRunner microservicesRunner3 = new MicroservicesRunner(PORT);
        microservicesRunner3.addExceptionMapper(new CustomerNotFoundMapper(), new InvoiceNotFoundMapper())
                            .deploy(new ReportService()).start();
    }

    @BeforeClass
    public void setup() throws Exception {
        baseURI = URI.create(String.format("http://%s:%d", HOSTNAME, PORT));
        microservicesRunner1 = new MicroservicesRunner(8088);
        microservicesRunner1.addExceptionMapper(new CustomerNotFoundMapper())
                            .deploy(new CustomerService()).start();

        microservicesRunner2 = new MicroservicesRunner(8089);
        microservicesRunner2.addExceptionMapper(new InvoiceNotFoundMapper())
                            .deploy(new InvoiceService()).start();

        microservicesRunner3 = new MicroservicesRunner(PORT);
        microservicesRunner3.addExceptionMapper(new CustomerNotFoundMapper(), new InvoiceNotFoundMapper())
                            .deploy(new ReportService()).start();
    }

    @AfterClass
    public void teardown() throws Exception {
        microservicesRunner1.stop();
        microservicesRunner2.stop();
        microservicesRunner3.stop();
    }

    @Test
    public void testClient() throws Exception {
        HttpURLConnection urlConn = request("/report/invoice/I001", HttpMethod.GET, false);
        InputStream inputStream = urlConn.getInputStream();
        String response = StreamUtil.asString(inputStream);
        IOUtils.closeQuietly(inputStream);
        urlConn.disconnect();
        Assert.assertEquals(response,
                            "{\"id\":\"I001\",\"customer\":{\"id\":\"C001\",\"firstName\":\"WSO2\",\"lastName\":" +
                            "\"Inc\",\"address\":\"Colombo\"},\"amount\":250.15}");

        urlConn = request("/report/invoice/I002", HttpMethod.GET, false);
        int responseCode = urlConn.getResponseCode();
        Assert.assertEquals(responseCode, 404);
        inputStream = urlConn.getErrorStream();
        response = StreamUtil.asString(inputStream);
        Gson gson = new Gson();
        InvoiceNotFoundResponseMapper invoiceNotFoundResponseMapper =
                gson.fromJson(response, InvoiceNotFoundResponseMapper.class);
        IOUtils.closeQuietly(inputStream);
        Assert.assertEquals(invoiceNotFoundResponseMapper.getExceptionKey(), "30002");
        Assert.assertEquals(invoiceNotFoundResponseMapper.getExceptionClass(),
                            InvoiceNotFoundRestServiceException.class);
        urlConn.disconnect();
    }

    protected HttpURLConnection request(String path, String method, boolean keepAlive) throws IOException {
        URL url = baseURI.resolve(path).toURL();
        HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
        if (method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT)) {
            urlConn.setDoOutput(true);
        }
        urlConn.setRequestMethod(method);
        if (!keepAlive) {
            urlConn.setRequestProperty(HttpHeaderNames.CONNECTION.toString(), HEADER_VAL_CLOSE);
        }

        return urlConn;
    }

}


================================================
FILE: client/test/java/org/wso2/msf4j/client/test/ModelUtils.java
================================================
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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.wso2.msf4j.client.test;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

/**
 * Converts Object to gson
 */
public class ModelUtils {
    public static final Gson GSON = new GsonBuilder().setPrettyPrinting()
            .disableHtmlEscaping().create();

    public static String toString(Object o) {
        return GSON.toJson(o);
    }
}


================================================
FILE: client/test/java/org/wso2/msf4j/client/test/client/api/CustomerServiceAPI.java
================================================
/*
 * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved.
 *
 * 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.wso2.msf4j.client.test.client.api;

import feign.Param;
import feign.RequestLine;
import org.wso2.msf4j.client.exception.RestServiceException;
import org.wso2.msf4j.client.test.client.exception.CustomerNotFoundRestServiceException;
import org.wso2.msf4j.client.test.model.Customer;

public interface CustomerServiceAPI {
    // Customer service
    @RequestLine("GET /customer/{id}")
    Customer getCustomer(@Param("id") String id) throws CustomerNotFoundRestServiceException, RestServiceException;
}

================================================
FILE: client/test/java/org/wso2/msf4j/client/test/client/api/InvoiceServiceAPI.java
================================================
/*
 * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved.
 *
 * 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.wso2.msf4j.example;

import feign.Param;
import feign.RequestLine;
import org.wso2.msf4j.client.test.client.exception.InvoiceNotFoundRestServiceException;
import org.wso2.msf4j.client.test.model.Invoice;

public interface InvoiceServiceAPI {
    // Invoice service
    @RequestLine("GET /invoice/{id}")
    Invoice getInvoice(@Param("id") String id) throws InvoiceNotFoundRestServiceException;
}


================================================
FILE: client/test/java/org/wso2/msf4j/client/test/client/exception/CustomerNotFoundResponseMapper.java
================================================
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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.wso2.msf4j.client.test.client.exception;

import org.wso2.msf4j.client.codec.RestErrorResponseMapper;

/**
 * RestErrorResponseMapper which maps REST service response error to CustomerNotFoundRestServiceException
 */
public class CustomerNotFoundResponseMapper extends RestErrorResponseMapper {

    public static final String ERROR_CODE = "30001";

    @Override
    public String getExceptionKey() {
        return ERROR_CODE;
    }

    @Override
    public Class getExceptionClass() {
        return CustomerNotFoundRestServiceException.class;
    }
}


================================================
FILE: client/test/java/org/wso2/msf4j/client/test/client/exception/CustomerNotFoundRestServiceException.java
================================================
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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.wso2.msf4j.client.test.client.exception;

import org.wso2.msf4j.client.exception.RestServiceException;

public class CustomerNotFoundRestServiceException extends RestServiceException {

    public CustomerNotFoundRestServiceException(String message) {
        super(message);
    }
}


================================================
FILE: client/test/java/org/wso2/msf4j/client/test/client/exception/InvoiceNotFoundResponseMapper.java
================================================
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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.wso2.msf4j.client.test.client.exception;

import org.wso2.msf4j.client.codec.RestErrorResponseMapper;

/**
 * RestErrorResponseMapper which maps REST service response error to CustomerNotFoundRestServiceException
 */
public class InvoiceNotFoundResponseMapper extends RestErrorResponseMapper {
    public static final String ERROR_CODE = "30002";

    @Override
    public String getExceptionKey() {
        return ERROR_CODE;
    }

    @Override
    public Class getExceptionClass() {
        return InvoiceNotFoundRestServiceException.class;
    }
}


================================================
FILE: client/test/java/org/wso2/msf4j/client/test/client/exception/InvoiceNotFoundRestServiceException.java
================================================
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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.wso2.msf4j.client.test.client.exception;

import org.wso2.msf4j.client.exception.RestServiceException;

public class InvoiceNotFoundRestServiceException extends RestServiceException {

    public InvoiceNotFoundRestServiceException(String message) {
        super(message);
    }
}


================================================
FILE: client/test/java/org/wso2/msf4j/client/test/exception/CustomerNotFoundException.java
================================================
/*
 *  Copyright (c) 2005-2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */
package org.wso2.msf4j.client.test.exception;

/**
 * Thrown when a Customer record is not found.
 */
public class CustomerNotFoundException extends EntityNotFoundException {
    public CustomerNotFoundException() {
        super();
    }

    public CustomerNotFoundException(String message) {
        super(message);
    }

    public CustomerNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }

    public CustomerNotFoundException(Throwable cause) {
        super(cause);
    }

    protected CustomerNotFoundException(String message, Throwable cause,
                                        boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}


================================================
FILE: client/test/java/org/wso2/msf4j/client/test/exception/CustomerNotFoundMapper.java
================================================
/*
 *  Copyright (c) 2005-2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */
package org.wso2.msf4j.client.test.exception;

import org.wso2.msf4j.client.test.model.ServiceErrorResponse;

import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;

/**
 * ExceptionMapper which handles CustomerNotFoundException.
 */
public class CustomerNotFoundMapper implements ExceptionMapper {

    private static final String ERROR_CODE = "30001";

    public Response toResponse(CustomerNotFoundException ex) {
        return Response.status(404)
                .entity(new ServiceErrorResponse(ERROR_CODE, ex.getMessage()))
                .type("text/plain")
                .build();
    }
}


================================================
FILE: client/test/java/org/wso2/msf4j/client/test/exception/EntityNotFoundException.java
================================================
/*
 *  Copyright (c) 2005-2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */
package org.wso2.msf4j.client.test.exception;

/**
 * EntityNotFoundException.
 */
public class EntityNotFoundException extends Exception {
    public EntityNotFoundException() {
        super();
    }

    public EntityNotFoundException(String message) {
        super(message);
    }

    public EntityNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }

    public EntityNotFoundException(Throwable cause) {
        super(cause);
    }

    protected EntityNotFoundException(String message, Throwable cause,
                                      boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}


================================================
FILE: client/test/java/org/wso2/msf4j/client/test/exception/EntityNotFoundMapper.java
================================================
/*
 *  Copyright (c) 2005-2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */
package org.wso2.msf4j.client.test.exception;

import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;

/**
 * EntityNotFoundMapper.
 */
public class EntityNotFoundMapper implements ExceptionMapper {
    @Override
    public Response toResponse(EntityNotFoundException ex) {
        return Response.status(404).
                entity(ex.getMessage() + " [from EntityNotFoundMapper]").
                type("text/plain").
                build();
    }
}


================================================
FILE: client/test/java/org/wso2/msf4j/client/test/exception/GenericServerErrorException.java
================================================
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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.wso2.msf4j.client.test.exception;

/**
 * Thrown when a generic exception occurs while handling a micro service request
 */
public class GenericServerErrorException extends Exception {
    public GenericServerErrorException() {
        super();
    }

    public GenericServerErrorException(String message) {
        super(message);
    }

    public GenericServerErrorException(String message, Throwable cause) {
        super(message, cause);
    }

    public GenericServerErrorException(Throwable cause) {
        super(cause);
    }

    protected GenericServerErrorException(String message, Throwable cause,
                                          boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}


================================================
FILE: client/test/java/org/wso2/msf4j/client/test/exception/GenericServerErrorMapper.java
================================================
/*
 *  Copyright (c) 2005-2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */
package org.wso2.msf4j.client.test.exception;

import org.wso2.msf4j.client.test.model.ServiceErrorResponse;

import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;

/**
 * ExceptionMapper which handled GenericServerErrorException.
 */
public class GenericServerErrorMapper implements ExceptionMapper {

    private static final String ERROR_CODE = "10000";

    @Override
    public Response toResponse(GenericServerErrorException ex) {
        return Response.status(500)
                .entity(new ServiceErrorResponse(ERROR_CODE, ex.getMessage()))
                .type("text/plain")
                .build();
    }
}


================================================
FILE: client/test/java/org/wso2/msf4j/client/test/exception/InvoiceNotFoundException.java
================================================
/*
 *  Copyright (c) 2005-2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */
package org.wso2.msf4j.client.test.exception;

/**
 * Thrown when a Invoice record is not found.
 */
public class InvoiceNotFoundException extends EntityNotFoundException {
    public InvoiceNotFoundException() {
        super();
    }

    public InvoiceNotFoundException(String message) {
        super(message);
    }

    public InvoiceNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }

    public InvoiceNotFoundException(Throwable cause) {
        super(cause);
    }

    protected InvoiceNotFoundException(String message, Throwable cause,
                                       boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}


================================================
FILE: client/test/java/org/wso2/msf4j/client/test/exception/InvoiceNotFoundMapper.java
================================================
/*
 *  Copyright (c) 2005-2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */
package org.wso2.msf4j.client.test.exception;

import org.wso2.msf4j.client.test.model.ServiceErrorResponse;

import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;

/**
 * ExceptionMapper which handles InvoiceNotFoundException.
 */
public class InvoiceNotFoundMapper implements ExceptionMapper {

    private static final String ERROR_CODE = "30002";

    public Response toResponse(InvoiceNotFoundException ex) {
        return Response.status(404)
                .entity(new ServiceErrorResponse(ERROR_CODE, ex.getMessage()))
                .type("text/plain")
                .build();
    }
}


================================================
FILE: client/test/java/org/wso2/msf4j/client/test/model/Customer.java
================================================
/*
 * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved.
 *
 * 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.wso2.msf4j.client.test.model;

import org.wso2.msf4j.client.test.ModelUtils;

/**
 * Represents a Customer item
 */
@SuppressWarnings("unused")
public class Customer {

    private String id;
    private String firstName;
    private String lastName;
    private String address;

    /**
     * No arg constructor is required for marshalling
     */
    public Customer() {
    }

    public Customer(String id, String firstName, String lastName, String address) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
        this.address = address;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return ModelUtils.toString(this);
    }
}


================================================
FILE: client/test/java/org/wso2/msf4j/client/test/model/Invoice.java
================================================
/*
 * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved.
 *
 * 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.wso2.msf4j.client.test.model;


import org.wso2.msf4j.client.test.ModelUtils;

import java.util.Date;

/**
 * Represents an Invoice item
 */
@SuppressWarnings("unused")
public class Invoice {

    private String id;
    private String customerId;
    private double amount;

    /**
     * No arg constructor is required for marshalling
     */
    public Invoice() {
    }

    public String getId() {
        return id;
    }

    public Invoice(String id, String customerId, double amount) {
        this.id = id;
        this.customerId = customerId;
        this.amount = amount;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getCustomerId() {
        return customerId;
    }

    public void setCustomerId(String customerId) {
        this.customerId = customerId;
    }

    public double getAmount() {
        return amount;
    }

    public void setAmount(double amount) {
        this.amount = amount;
    }

    @Override
    public String toString() {
        return ModelUtils.toString(this);
    }
}


================================================
FILE: client/test/java/org/wso2/msf4j/client/test/model/InvoiceReport.java
================================================
/*
 * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved.
 *
 * 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.wso2.msf4j.client.test.model;

import org.wso2.msf4j.client.test.ModelUtils;

import java.util.Date;

/**
 * Represents an Invoice Report item.
 */
@SuppressWarnings("unused")
public class InvoiceReport {

    private String id;
    private Customer customer;
    private double amount;

    /**
     * No arg constructor is required for marshalling
     */
    public InvoiceReport() {
    }

    public InvoiceReport(Invoice invoice, Customer customer) {
        this.id = invoice.getId();
        this.customer = customer;
        this.amount = invoice.getAmount();
    }

    public InvoiceReport(String id, Customer customer, double amount, Date date) {
        this.id = id;
        this.customer = customer;
        this.amount = amount;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public Customer getCustomer() {
        return customer;
    }

    public void setCustomer(Customer customer) {
        this.customer = customer;
    }

    public double getAmount() {
        return amount;
    }

    public void setAmount(double amount) {
        this.amount = amount;
    }

    @Override
    public String toString() {
        return ModelUtils.toString(this);
    }
}


================================================
FILE: client/test/java/org/wso2/msf4j/client/test/model/ServiceErrorResponse.java
================================================
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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.wso2.msf4j.client.test.model;

import org.wso2.msf4j.client.test.ModelUtils;

/**
 * Represents error response body to be sent back to the client
 */
public class ServiceErrorResponse {
    private String errorCode;
    private String message;

    /**
     * No arg constructor is required for marshalling
     */
    public ServiceErrorResponse() {
    }

    public ServiceErrorResponse(String errorCode, String message) {
        this.errorCode = errorCode;
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public String getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(String errorCode) {
        this.errorCode = errorCode;
    }

    @Override
    public String toString() {
        return ModelUtils.toString(this);
    }
}


================================================
FILE: client/test/java/org/wso2/msf4j/client/test/service/CustomerService.java
================================================
/*
 * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved.
 *
 * 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.wso2.msf4j.client.test.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.msf4j.client.test.model.Customer;
import org.wso2.msf4j.client.test.exception.CustomerNotFoundException;

import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;

/**
 * CustomerService resource class.
 */
@Path("/customer")
public class CustomerService {

    private static final Logger log = LoggerFactory.getLogger(CustomerService.class);
    private Map customerMap = new HashMap<>();

    public CustomerService() {
        customerMap.put("C001", new Customer("C001", "WSO2", "Inc", "Colombo"));
    }

    /**
     * Retrieves a customer record for a given customer ID.
     * http://localhost:8080/customer/C001
     *
     * @param id Customer ID will be taken from the path parameter.
     * @return
     */
    @GET
    @Path("/{id}")
    @Produces({"application/json"})
    public Response getCustomer(@PathParam("id") String id) throws CustomerNotFoundException {
        Customer customer = customerMap.get(id);
        if (customer == null) {
            log.info("Request for non-existing customer: " + id);
            throw new CustomerNotFoundException("Customer ID " + id + " not found");
        }
        return Response.status(Response.Status.OK).entity(customer).build();
    }
}


================================================
FILE: client/test/java/org/wso2/msf4j/client/test/service/InvoiceService.java
================================================
/*
 * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved.
 *
 * 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.wso2.msf4j.client.test.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.msf4j.client.test.exception.InvoiceNotFoundException;
import org.wso2.msf4j.client.test.model.Invoice;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * CustomerService resource class.
 */
@Path("/invoice")
public class InvoiceService {

    private static final Logger log = LoggerFactory.getLogger(InvoiceService.class);
    private Map invoiceMap = new HashMap<>();

    public InvoiceService() {
        invoiceMap.put("I001", new Invoice("I001", "C001", 250.15));
    }

    /**
     * Retrieves an invoice record for a given invoice ID.
     * http://localhost:8080/invoice/I001
     *
     * @param id Invoice ID will be taken from the path parameter.
     * @return
     */
    @GET
    @Path("/{id}")
    @Produces({"application/json"})
    public Response getCustomer(@PathParam("id") String id) throws InvoiceNotFoundException {
        Invoice invoice = invoiceMap.get(id);
        if (invoice == null) {
            log.info("Request for non-existing invoice: " + id);
            throw new InvoiceNotFoundException("Invoice ID " + id + " not found");
        }
        return Response.status(Response.Status.OK).entity(invoice).build();
    }
}


================================================
FILE: client/test/java/org/wso2/msf4j/client/test/service/ReportService.java
================================================
/*
 * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved.
 *
 * 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.wso2.msf4j.client.test.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.msf4j.client.MSF4JClient;
import org.wso2.msf4j.client.test.client.api.CustomerServiceAPI;
import org.wso2.msf4j.client.test.client.api.InvoiceServiceAPI;
import org.wso2.msf4j.client.test.client.exception.CustomerNotFoundRestServiceException;
import org.wso2.msf4j.client.test.client.exception.InvoiceNotFoundRestServiceException;
import org.wso2.msf4j.client.test.exception.InvoiceNotFoundException;
import org.wso2.msf4j.client.test.model.Customer;
import org.wso2.msf4j.client.test.client.exception.CustomerNotFoundResponseMapper;
import org.wso2.msf4j.client.test.client.exception.InvoiceNotFoundResponseMapper;
import org.wso2.msf4j.client.test.exception.CustomerNotFoundException;
import org.wso2.msf4j.client.test.exception.GenericServerErrorException;
import org.wso2.msf4j.client.test.model.Invoice;
import org.wso2.msf4j.client.test.model.InvoiceReport;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;

/**
 * ReportService resource class.
 */
@Path("/report")
public class ReportService {

    private static final Logger log = LoggerFactory.getLogger(ReportService.class);
    private static final String CUSTOMER_SERVICE_URL = "http://localhost:8088";
    private static final String INVOICE_SERVICE_URL = "http://localhost:8089";
    private final MSF4JClient customerServiceClient;
    private final MSF4JClient invoiceServiceClient;

    public ReportService() {
        customerServiceClient = new MSF4JClient.Builder()
                .apiClass(CustomerServiceAPI.class)
                .instanceName("CustomerServiceClient")
                .serviceEndpoint(CUSTOMER_SERVICE_URL)
                .addErrorResponseMapper(new CustomerNotFoundResponseMapper())
                .build();

        invoiceServiceClient = new MSF4JClient.Builder()
                .apiClass(InvoiceServiceAPI.class)
                .instanceName("InvoiceServiceClient")
                .serviceEndpoint(INVOICE_SERVICE_URL)
               // .addErrorResponseMapper(new InvoiceNotFoundResponseMapper())
                .build();
    }

    /**
     * Retrieves the invoice report for a given invoice ID.
     * http://localhost:8080/report/invoice/I001
     *
     * @param id Invoice ID will be taken from the path parameter.
     * @return
     */
    @GET
    @Path("/invoice/{id}")
    @Produces({"application/json"})
    public Response getInvoiceReport(@PathParam("id") String id) throws InvoiceNotFoundException,
                                                                        CustomerNotFoundException, GenericServerErrorException {
        InvoiceReport invoiceReport;
        Invoice invoice;
        try {
            invoice = invoiceServiceClient.api().getInvoice(id);
            if (log.isDebugEnabled()) {
                log.info("Invoice retrieved: " + invoice);
            }
        } catch (InvoiceNotFoundRestServiceException e) {
            throw new InvoiceNotFoundException(e);
        } catch (Exception e) {
            log.error("Generic exception encountered", e);
            throw new GenericServerErrorException("Server Error: Something went wrong!");
        }

        try {
            String customerId = invoice.getCustomerId();
            Customer customer = customerServiceClient.api().getCustomer(customerId);
            if (log.isDebugEnabled()) {
                log.debug("Customer retrieved: " + customer);
            }
            invoiceReport = new InvoiceReport(invoice, customer);
        } catch (CustomerNotFoundRestServiceException e) {
            throw new CustomerNotFoundException(e);
        } catch (Exception e) {
            log.error("Generic exception encountered", e);
            throw new GenericServerErrorException("Server Error: Something went wrong!");
        }

        return Response.status(Response.Status.OK).entity(invoiceReport).build();
    }
}


================================================
FILE: client/test/resources/testng.xml
================================================






    
        
            
        
    



================================================
FILE: core/deployment.yaml
================================================
#   Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved
#
#   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.

  # MSF4J configuration
wso2.msf4j.configuration:
    # No of worker pool threads to handle MSF4J requests
  threadCount: 100
  threadPoolName: msf4j.executor.workerpool



================================================
FILE: core/pom.xml
================================================




    4.0.0

    
        org.wso2.msf4j
        msf4j-parent
        2.8.14-SNAPSHOT
        ../poms/parent/pom.xml
    

    msf4j-core
    bundle

    WSO2 MSF4J core
    WSO2 MSF4J core
    https://github.com/wso2/msf4j

    
        
            org.wso2.carbon.messaging
            org.wso2.carbon.messaging
            
                
                    org.wso2.carbon
                    wso2carbon-kernel
                
                
                    org.wso2.carbon
                    org.wso2.carbon.server.feature
                
                
                    org.wso2.carbon
                    org.wso2.carbon.osgi.feature
                
            
        
        
            org.wso2.transport.http
            org.wso2.transport.http.netty
            
                
                    org.wso2.carbon.messaging
                    org.wso2.carbon.messaging
                
                
                    org.wso2.orbit.org.yaml
                    snakeyaml
                
            
        
        
            org.wso2.eclipse.osgi
            org.eclipse.osgi
        
        
            org.wso2.eclipse.osgi
            org.eclipse.osgi.services
        
        
            javax.ws.rs
            javax.ws.rs-api
        
        
            org.wso2.msf4j
            jaxrs-delegates
        
        
            org.slf4j
            slf4j-api
        
        
            org.slf4j
            slf4j-log4j12
        
        
            com.google.code.gson
            gson
        
        
            com.google.code.findbugs
            jsr305
        
        
            org.apache.servicemix.bundles
            org.apache.servicemix.bundles.commons-beanutils
        
        
            com.nimbusds
            nimbus-jose-jwt
        
        
            org.yaml
            snakeyaml
        
        
            org.wso2.carbon.config
            org.wso2.carbon.config
        
        
            javax.annotation
            javax.annotation-api
        
        
            commons-io.wso2
            commons-io
        

        
        
            org.testng
            testng
            test
        
        
            ch.qos.logback
            logback-core
            test
        
        
            ch.qos.logback
            logback-classic
            test
        
        
            org.apache.httpcomponents
            httpmime
            4.5.1
            test
        
        
            org.apache.maven.shared
            maven-invoker
            2.2
            test
        
        
            javax.websocket
            javax.websocket-api
            ${javax.websocket.version}
        
    

    
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
            
            
                org.apache.rat
                apache-rat-plugin
            
            
                org.apache.maven.plugins
                maven-checkstyle-plugin
            
            
                org.apache.maven.plugins
                maven-surefire-plugin
                
                    -Dmsf4jTestRunning
                    
                        src/test/resources/testng.xml
                    
                    
                        ${maven.home}
                        ${project.basedir}
                    
                
            
            
                org.apache.maven.plugins
                maven-deploy-plugin
            
            
                org.apache.felix
                maven-bundle-plugin
            
            
                org.apache.maven.plugins
                maven-jar-plugin
                2.6
                
                    
                        
                            test-jar
                        
                    
                
            
            
                org.wso2.carbon.config
                org.wso2.carbon.config.maven.plugin
            
        
    

    
        
        
            coverage
            
                
                    
                        
                            org.apache.maven.plugins
                            maven-surefire-plugin
                            
                                ${argLine} -Xmx512m
                            
                        
                        
                            org.jacoco
                            jacoco-maven-plugin
                            
                                
                                    prepare-agent
                                    
                                        prepare-agent
                                    
                                
                                
                                    report
                                    prepare-package
                                    
                                        report
                                    
                                
                            
                        
                    
                

                
                    
                        org.jacoco
                        jacoco-maven-plugin
                    
                
            
        

        
        
            release
            
                
                    
                        
                        
                            org.apache.maven.plugins
                            maven-source-plugin
                            
                                true
                            
                            
                                
                                    attach-sources
                                    package
                                    
                                        jar-no-fork
                                    
                                
                            
                        

                        
                        
                            org.apache.maven.plugins
                            maven-javadoc-plugin
                            
                                true
                                
                                    http://docs.oracle.com/javaee/6/api/
                                
                                *.internal.*
                                
                                    Licensed under the Apache License, Version 2.0]]>
                                
                            
                            
                                
                                    attach-javadoc
                                    package
                                    
                                        jar
                                    
                                
                            
                        

                        
                        
                            org.apache.maven.plugins
                            maven-gpg-plugin
                            
                                ${gpg.passphrase}
                                ${gpg.useagent}
                            
                            
                                
                                    
                                        sign
                                    
                                
                            
                        
                    
                

                
                    
                        org.apache.maven.plugins
                        maven-source-plugin
                    
                    
                        org.apache.maven.plugins
                        maven-javadoc-plugin
                    
                    
                        org.apache.maven.plugins
                        maven-gpg-plugin
                    
                
            
        
    

    
        org.wso2.msf4j.internal.MicroservicesServerActivator
        org.wso2.msf4j.internal.*
        
            !org.wso2.msf4j.internal.*,
            !org.wso2.msf4j.delegates.*,
            org.wso2.msf4j.*;version="${msf4j.version}"
        
        
            org.wso2.msf4j.delegates.*;version="${msf4j.version.range}",
            org.apache.commons.io.*;version="${commons-io.version.range}",
            com.google.gson.*;version="${gson.version.range}",
            javax.annotation.*,
            javax.ws.rs.*,
            javax.websocket.*;version="${javax.websocket.version}",
            javax.xml.bind;resolution:=optional,
            javax.xml.bind.annotation;resolution:=optional,
            org.slf4j.*;version="${slf4j.version.range}",
            org.apache.commons.beanutils.*;version="${beanutils.version.range}",
            org.osgi.framework.*;version="${osgi.framework.import.version.range}",
            org.osgi.util.tracker; version="${osgi.service.tracker.import.version.range}",
            org.wso2.carbon.kernel.startupresolver.*;version="${carbon.kernel.version.range}",
            org.wso2.carbon.kernel.config.model.*;version="${carbon.kernel.version.range}",
            org.wso2.carbon.config.*;version="${carbon.config.package.import.version.range}",
            org.wso2.transport.*;version="${org.wso2.transport.http.version.range}",
            io.netty.*;version="${netty.version.range}",
            org.yaml.snakeyaml.*;version="${org.snakeyaml.version}"
        
        
            startup.listener;componentName="wso2-microservices-server";requiredService="org.wso2.msf4j.Microservice,org.wso2.msf4j.Interceptor,
            org.wso2.msf4j.interceptor.OSGiInterceptorConfig,org.wso2.carbon.config.provider.ConfigProvider",
            startup.listener;componentName="wso2-websocket-server";requiredService="org.wso2.msf4j.WebSocketEndpoint",
            osgi.service;objectClass="org.wso2.msf4j.internal.MicroservicesServerSC",
            osgi.service;objectClass="org.wso2.msf4j.internal.websocket.WebSocketServerSC"
        
    



================================================
FILE: core/src/main/java/org/wso2/msf4j/AbstractSessionManager.java
================================================
/*
 *  Copyright (c) 2016 WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */
package org.wso2.msf4j;

import org.wso2.msf4j.internal.session.SessionIdGenerator;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * Abstract SessionManager implementation which leave the sessions persistence related method to be implemented.
 */
public abstract class AbstractSessionManager implements SessionManager {
    private boolean isStopped;

    /**
     * The default maximum inactive interval, in minutes, for Sessions created by
     * this Manager.
     */
    private static final int DEFAULT_MAX_INACTIVE_INTERVAL = 15;  // In minutes

    /**
     * Max number of sessions that can be active at a given time.
     */
    private static final int DEFAULT_MAX_ACTIVE_SESSIONS = 100_000;

    /**
     * The session id length of Sessions created by this Manager.
     */
    private static final int SESSION_ID_LENGTH = 16;

    private Map sessions = new ConcurrentHashMap<>();
    private SessionIdGenerator sessionIdGenerator = new SessionIdGenerator();

    private ScheduledExecutorService sessionExpiryChecker;

    public final void init() {
        sessionIdGenerator.setSessionIdLength(SESSION_ID_LENGTH);
        loadSessions(sessions);

        // Session expiry scheduled task
        sessionExpiryChecker = Executors.newScheduledThreadPool(1);
        sessionExpiryChecker.scheduleAtFixedRate(() ->
                        sessions.values().parallelStream()
                                .filter(session ->
                                        (System.currentTimeMillis() - session.getLastAccessedTime() >=
                                                (long) session.getMaxInactiveInterval() * 60 * 1000))
                                .forEach(Session::invalidate),
                30, 30, TimeUnit.SECONDS);
    }

    public final Session getSession(String sessionId) {
        checkValidity();
        Session session = sessions.get(sessionId);
        if (session == null) {
            session = readSession(sessionId);
        }
        if (session != null) {
            sessions.put(session.getId(), session);
            session.setNew(false);
        }
        return session;
    }

    public final Session createSession() {
        checkValidity();
        if (sessions.size() >= DEFAULT_MAX_ACTIVE_SESSIONS) {
            throw new IllegalStateException("Too many active sessions");
        }
        Session session = new Session(sessionIdGenerator.generateSessionId(""), DEFAULT_MAX_INACTIVE_INTERVAL);
        session.setManager(this);
        sessions.put(session.getId(), session);
        saveSession(session);
        return session;
    }

    public final void invalidateSession(Session session) {
        checkValidity();
        sessions.remove(session.getId());
        deleteSession(session);
    }

    @Override
    public final int getDefaultMaxInactiveInterval() {
        return DEFAULT_MAX_INACTIVE_INTERVAL;
    }

    @Override
    public final int getDefaultMaxActiveSessions() {
        return DEFAULT_MAX_ACTIVE_SESSIONS;
    }

    @Override
    public final int getSessionIdLength() {
        return SESSION_ID_LENGTH;
    }

    @Override
    public final void stop() {
        sessionExpiryChecker.shutdown();
        isStopped = true;
    }

    protected final void checkValidity() {
        if (isStopped) {
            throw new IllegalStateException("This SessionManager has been stopped");
        }
    }
}


================================================
FILE: core/src/main/java/org/wso2/msf4j/DefaultSessionManager.java
================================================
/*
 *  Copyright (c) 2016 WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */
package org.wso2.msf4j;

import java.util.Map;

/**
 * Default SessionManager to manage MSF4J transport sessions.
 */
public class DefaultSessionManager extends AbstractSessionManager {

    @Override
    public void loadSessions(Map sessions) {
        checkValidity();
        // Nothing to do because this is an in-memory implementation
    }

    @Override
    public void saveSession(Session session) {
        checkValidity();
        // Nothing to do because this is an in-memory implementation
    }

    @Override
    public Session readSession(String sessionId) {
        checkValidity();
        // Nothing to do because this is an in-memory implementation
        return null;
    }

    @Override
    public void deleteSession(Session session) {
        checkValidity();
        // Nothing to do because this is an in-memory implementation
    }

    @Override
    public void updateSession(Session session) {
        checkValidity();
        // Nothing to do because this is an in-memory implementation
    }
}


================================================
FILE: core/src/main/java/org/wso2/msf4j/HttpStreamHandler.java
================================================
/*
 * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved.
 *
 * 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.wso2.msf4j;

import java.nio.ByteBuffer;

/**
 * HttpHandler would extend this abstract class and implement methods to stream the body directly.
 * chunk method would receive the http-chunks of the body and finished would be called
 * on receipt of the last chunk.
 */
public interface HttpStreamHandler {

    /**
     * Initialize the stream handler.
     *
     * @param response response object that should be used to send response
     */
    void init(Response response);

    /**
     * Http request content will be streamed directly to this method.
     *
     * @param content content of chunks
     * @throws  Exception if error occurs while invoking streaming handlers
     */
    void chunk(ByteBuffer content) throws Exception;

    /**
     * This method will be called when all chunks
     * have been completely streamed.
     *
     * @throws Exception if error occurs while stopping streaming handlers
     */
    void end() throws Exception;

    /**
     * When there is exception on netty while streaming, it will be propagated to handler
     * so the handler can do the cleanup.
     *
     * @param cause Cause of the Exception
     */
    void error(Throwable cause);
}


================================================
FILE: core/src/main/java/org/wso2/msf4j/HttpStreamer.java
================================================
/*
 * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved.
 *
 * 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.wso2.msf4j;

/**
 * This class can be injected to a resource method
 * inorder to consume requests as streams.
 */
public class HttpStreamer {

    private HttpStreamHandler httpStreamHandler;

    /**
     * Register a streaming callback to handle body chunks.
     *
     * @param httpStreamHandler handler object
     */
    public void callback(HttpStreamHandler httpStreamHandler) {
        this.httpStreamHandler = httpStreamHandler;
    }

    public HttpStreamHandler getHttpStreamHandler() {
        return httpStreamHandler;
    }
}


================================================
FILE: core/src/main/java/org/wso2/msf4j/Interceptor.java
================================================
/*
 * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved.
 *
 * 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.wso2.msf4j;

import org.wso2.msf4j.interceptor.RequestInterceptor;
import org.wso2.msf4j.interceptor.ResponseInterceptor;
import org.wso2.msf4j.internal.MSF4JConstants;

import java.lang.reflect.Method;

/**
 * Interface that needs to be implemented to intercept handler method calls.
 *
 * @deprecated
 */
public interface Interceptor extends RequestInterceptor, ResponseInterceptor {

    /**
     * preCall is run before a handler method call is made. If any of the preCalls throw exception or return false then
     * no other subsequent preCalls will be called and the request processing will be terminated,
     * also no postCall interceptors will be called.
     *
     * @param request           HttpRequest being processed.
     * @param responder         HttpResponder to send response.
     * @param serviceMethodInfo Info on handler method that will be called.
     * @return true if the request processing can continue, otherwise the hook should send response and return false to
     * stop further processing.
     * @throws Exception if error occurs while executing the preCall
     * @deprecated
     */
    boolean preCall(Request request, Response responder, ServiceMethodInfo serviceMethodInfo) throws Exception;

    /**
     * postCall is run after a handler method call is made. If any of the postCalls throw and exception then the
     * remaining postCalls will still be called. If the handler method was not called then postCall interceptors will
     * not be called.
     *
     * @param request           HttpRequest being processed.
     * @param status            Http status returned to the client.
     * @param serviceMethodInfo Info on handler method that was called.
     * @throws Exception if error occurs while executing the postCall
     * @deprecated
     */
    void postCall(Request request, int status, ServiceMethodInfo serviceMethodInfo) throws Exception;

    @Override
    default boolean interceptRequest(Request request, Response response) throws Exception {
        Method method = (Method) request.getProperty(MSF4JConstants.METHOD_PROPERTY_NAME);
        ServiceMethodInfo serviceMethodInfo = new ServiceMethodInfo(method.getName(), method, request);
        request.getProperties().forEach(serviceMethodInfo::setAttribute);
        return preCall(request, response, serviceMethodInfo);
    }

    @Override
    default boolean interceptResponse(Request request, Response response) throws Exception {
        Method method = (Method) request.getProperty(MSF4JConstants.METHOD_PROPERTY_NAME);
        ServiceMethodInfo serviceMethodInfo = new ServiceMethodInfo(method.getName(), method, request);
        request.getProperties().forEach(serviceMethodInfo::setAttribute);
        postCall(request, response.getStatusCode(), serviceMethodInfo);
        return true;
    }

    /**
     * As of according to the deprecated interceptors, the post call is void and there was no control given to the user
     * to control the interceptor flow in the post call. True is returned to depict the same behaviour to ensure
     * proper backward compatibility.
     *
     * @param request  MSF4J request.
     * @param response MSF4J Response.
     * @return should interception flow proceed?
     */
    @Override
    default boolean onResponseInterceptionError(Request request, Response response, Exception e) {
        return true;
    }
}


================================================
FILE: core/src/main/java/org/wso2/msf4j/Microservice.java
================================================
/*
 * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved.
 *
 * 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.wso2.msf4j;

/**
 * Interface that needs to be implemented for handling HTTP methods.
 */
public interface Microservice {

}


================================================
FILE: core/src/main/java/org/wso2/msf4j/MicroservicesRegistry.java
================================================
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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.wso2.msf4j;

import org.wso2.msf4j.interceptor.RequestInterceptor;
import org.wso2.msf4j.interceptor.ResponseInterceptor;

import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.ws.rs.ext.ExceptionMapper;

/**
 * Interface that needs to be implemented for handle the registry.
 */
public interface MicroservicesRegistry {

    /**
     * Get the service object with given base path.
     *
     * @param path base path of the service
     * @return service obj for the given base path or Optional null if no service found for given path.
     */
    Optional> getServiceWithBasePath(String path);

    /**
     * Get all the available service.
     *
     * @return Set of all the available services
     */
    Set getHttpServices();

    /**
     * Register service instances.
     *
     * @param service instances
     */
    void addService(Object... service);

    /**
     * Register a service instance to given basepath. This will discard the class level Path annotation.
     *
     * @param basePath path string which services need to be registered.
     * @param service instance.
     */
    void addService(String basePath, Object service);

    /**
     * Remove the given service instance.
     *
     * @param service instance to be remove.
     */
    void removeService(Object service);

    /**
     * Register SessionManager.
     *
     * @param sessionManager SessionManager instance.
     */
    void setSessionManager(SessionManager sessionManager);

    /**
     * Register request interceptors.
     *
     * @param requestInterceptor interceptor instances.
     */
    void addGlobalRequestInterceptor(RequestInterceptor... requestInterceptor);

    /**
     * Remove msf4j request interceptor.
     *
     * @param requestInterceptor MSF4J interceptor instance.
     */
    void removeGlobalRequestInterceptor(RequestInterceptor requestInterceptor);

    /**
     * Register response interceptors.
     *
     * @param responseInterceptors interceptor instances.
     */
    void addGlobalResponseInterceptor(ResponseInterceptor... responseInterceptors);

    /**
     * Remove msf4j response interceptor.
     *
     * @param responseInterceptor MSF4J interceptor instance.
     */
    void removeGlobalResponseInterceptor(ResponseInterceptor responseInterceptor);

    /**
     * Add msf4j global request and response interceptor.
     *
     * @param interceptor instance.
     */
    @Deprecated
    void addInterceptor(Interceptor... interceptor);

    /**
     * Remove msf4j global request and response interceptor.
     *
     * @param interceptor instance.
     */
    @Deprecated
    void removeInterceptor(Interceptor interceptor);

    /**
     * Register exception mappers.
     *
     * @param mapper ExceptionMapper instances.
     */
    void addExceptionMapper(ExceptionMapper... mapper);

    /**
     * Remove exception mapper.
     *
     * @param em ExceptionMapper instance.
     */
    void removeExceptionMapper(ExceptionMapper em);

    /**
     * Invoke post construct life cycle state for all the service.
     */
    void initServices();

    /**
     * Invoke post construct life cycle state for given service.
     *
     * @param httpService service instance
     */
    void initService(Object httpService);

    /**
     * Invoke predestroy life cycle state for all the service.
     */
    void preDestroyServices();

    /**
     * Invoke predestroy life cycle state for given service.
     *
     * @param httpService service instance
     */
    void preDestroyService(Object httpService);
}


================================================
FILE: core/src/main/java/org/wso2/msf4j/MicroservicesRunner.java
================================================
/*
 * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved.
 *
 * 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.wso2.msf4j;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.carbon.config.ConfigProviderFactory;
import org.wso2.carbon.config.ConfigurationException;
import org.wso2.carbon.config.provider.ConfigProvider;
import org.wso2.msf4j.interceptor.RequestInterceptor;
import org.wso2.msf4j.interceptor.ResponseInterceptor;
import org.wso2.msf4j.internal.DataHolder;
import org.wso2.msf4j.internal.HttpConnectorPortBindingListener;
import org.wso2.msf4j.internal.MSF4JConstants;
import org.wso2.msf4j.internal.MSF4JHttpConnectorListener;
import org.wso2.msf4j.internal.MSF4JWSConnectorListener;
import org.wso2.msf4j.internal.MicroservicesRegistryImpl;
import org.wso2.msf4j.internal.websocket.EndpointsRegistryImpl;
import org.wso2.msf4j.util.Utils;
import org.wso2.transport.http.netty.contract.Constants;
import org.wso2.transport.http.netty.contract.HttpWsConnectorFactory;
import org.wso2.transport.http.netty.contract.ServerConnector;
import org.wso2.transport.http.netty.contract.ServerConnectorFuture;
import org.wso2.transport.http.netty.contract.config.ListenerConfiguration;
import org.wso2.transport.http.netty.contract.config.ServerBootstrapConfiguration;
import org.wso2.transport.http.netty.contract.config.TransportsConfiguration;
import org.wso2.transport.http.netty.contractimpl.DefaultHttpWsConnectorFactory;
import org.wso2.transport.http.netty.contractimpl.common.Util;
import org.wso2.transport.http.netty.message.HttpConnectorUtil;

import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.ws.rs.ext.ExceptionMapper;

import static org.wso2.msf4j.internal.MSF4JConstants.STREAMLINED_TRANSPORT_NAMESPACE;

/**
 * This runner initializes the microservices runtime, deploys the microservices and service interceptors,
 * and starts the relevant transports.
 */
public class MicroservicesRunner {

    private static final Logger log = LoggerFactory.getLogger(MicroservicesRunner.class);
    /**
     * Default host used when using microservice runner starts with {@link #MicroservicesRunner(int...)}.
     */
    private static final String DEFAULT_HOST = "0.0.0.0";
    /**
     * The environment variable which overrides the {@link #DEFAULT_HOST}.
     */
    private static final String MSF4J_HOST = "msf4j.host";
    /**
     * The environment variable which have netty transport configuration file path.
     */
    private static final String TRANSPORTS_NETTY_CONF = "transports.netty.conf";
    protected List serverConnectors = new ArrayList<>();
    private EndpointsRegistryImpl endpointsRegistry = EndpointsRegistryImpl.getInstance();
    private MicroservicesRegistryImpl msRegistry = new MicroservicesRegistryImpl();
    private long startTime = System.currentTimeMillis();
    private boolean isStarted;

    /**
     * Creates a MicroservicesRunner instance which will be used for deploying microservices. Allows specifying
     * ports on which the microservices in this MicroservicesRunner are deployed.
     *
     * @param ports The port on which the microservices are exposed
     */
    public MicroservicesRunner(int... ports) {
        configureTransport(ports);
    }

    /**
     * Default constructor which will take care of initializing Netty transports in the file pointed to by the
     * System property transports.netty.conf.
     * 

* If that System property is not specified, it will start a single Netty transport on port 8080. *

* {@link #MicroservicesRunner(int...)} */ public MicroservicesRunner() { configureTransport(); } /** * Creates a MicroservicesRunner instance which will be used for deploying microservices. Allows specifying * transport configuration on which the microservices in this MicroservicesRunner are deployed. * * @param transportsConfiguration The transport configuration on which the microservices are exposed */ public MicroservicesRunner(TransportsConfiguration transportsConfiguration) { configureTransport(transportsConfiguration); } /** * Deploy a microservice. * * @param microservice The microservice which is to be deployed * @return this MicroservicesRunner object */ public MicroservicesRunner deploy(Object... microservice) { checkState(); msRegistry.addService(microservice); return this; } /** * Deploy a microservice with dynamic path. * * @param microservice The microservice which is to be deployed * @param basePath The context path for the service * @return this MicroservicesRunner object */ public MicroservicesRunner deploy(String basePath, Object microservice) { msRegistry.addService(basePath, microservice); return this; } /** * Add WebSocket endpoint to the MicroserviceRunner * * @param webSocketEndpoint webSocketEndpoint endpoint which is to be added. * @return this MicroservicesRunner object. */ public MicroservicesRunner deployWebSocketEndpoint(Object webSocketEndpoint) { endpointsRegistry.addEndpoint(webSocketEndpoint); return this; } /** * Register a custom {@link SessionManager}. * * @param sessionManager The SessionManager instance to be registered. * @return this MicroservicesRunner object */ public MicroservicesRunner setSessionManager(SessionManager sessionManager) { msRegistry.setSessionManager(sessionManager); return this; } /** * Register request interceptors. * * @param requestInterceptor interceptor instances */ public MicroservicesRunner addGlobalRequestInterceptor(RequestInterceptor... requestInterceptor) { msRegistry.addGlobalRequestInterceptor(requestInterceptor); return this; } /** * Register response interceptors. * * @param responseInterceptor interceptor instances */ public MicroservicesRunner addGlobalResponseInterceptor(ResponseInterceptor... responseInterceptor) { msRegistry.addGlobalResponseInterceptor(responseInterceptor); return this; } /** * Add an interceptor which will get called before & after the deployed microservices are invoked. * Multiple interceptors can be added * * @param interceptor interceptor The interceptor to be added. * @return this MicroservicesRunner object * @deprecated */ public MicroservicesRunner addInterceptor(Interceptor... interceptor) { msRegistry.addGlobalRequestInterceptor(interceptor); msRegistry.addGlobalResponseInterceptor(interceptor); return this; } /** * Add javax.ws.rs.ext.ExceptionMapper objects. * * @param exceptionMapper The ExceptionMapper to be added * @return this MicroservicesRunner object */ public MicroservicesRunner addExceptionMapper(ExceptionMapper... exceptionMapper) { checkState(); msRegistry.addExceptionMapper(exceptionMapper); return this; } /** * Method to configure transports. * * @param ports The port on which the microservices are exposed */ protected void configureTransport(int... ports) { HttpWsConnectorFactory connectorFactory = new DefaultHttpWsConnectorFactory(); ServerBootstrapConfiguration bootstrapConfiguration = new ServerBootstrapConfiguration(new HashMap<>()); for (int port : ports) { ListenerConfiguration listenerConfiguration = new ListenerConfiguration("netty-" + port, System .getProperty(MSF4J_HOST, DEFAULT_HOST), port); DataHolder.getInstance().getMicroservicesRegistries() .put(Util.createServerConnectorID(listenerConfiguration.getHost(), listenerConfiguration.getPort()), msRegistry); ServerConnector serverConnector = connectorFactory.createServerConnector(bootstrapConfiguration, listenerConfiguration); serverConnectors.add(serverConnector); } } /** * Method to configure transports. */ protected void configureTransport() { String transportYaml = System.getProperty(TRANSPORTS_NETTY_CONF); if (transportYaml == null || transportYaml.isEmpty()) { HttpWsConnectorFactory connectorFactory = new DefaultHttpWsConnectorFactory(); ServerBootstrapConfiguration bootstrapConfiguration = new ServerBootstrapConfiguration(new HashMap<>()); ListenerConfiguration listenerConfiguration = new ListenerConfiguration(); ServerConnector serverConnector = connectorFactory.createServerConnector(bootstrapConfiguration, listenerConfiguration); DataHolder.getInstance().getMicroservicesRegistries() .put(Util.createServerConnectorID(listenerConfiguration.getHost(), listenerConfiguration.getPort()), msRegistry); serverConnectors.add(serverConnector); } else { try { ConfigProvider configProvider = ConfigProviderFactory.getConfigProvider(Paths.get(transportYaml), null); TransportsConfiguration transportsConfiguration; Object transportConf = configProvider.getConfigurationObject(STREAMLINED_TRANSPORT_NAMESPACE); if (transportConf != null) { transportsConfiguration = Utils.resolveTransportsNSConfiguration(transportConf); } else { transportsConfiguration = configProvider.getConfigurationObject (MSF4JConstants.WSO2_TRANSPORT_HTTP_CONFIG_NAMESPACE, TransportsConfiguration.class); } Map transportProperties = HttpConnectorUtil .getTransportProperties(transportsConfiguration); int bossGroup = transportProperties.get(Constants.SERVER_BOOTSTRAP_BOSS_GROUP_SIZE) != null ? (Integer) transportProperties.get(Constants.SERVER_BOOTSTRAP_BOSS_GROUP_SIZE) : Runtime.getRuntime() .availableProcessors(); int workerGroup = transportProperties.get(Constants.SERVER_BOOTSTRAP_WORKER_GROUP_SIZE) != null ? (Integer) transportProperties.get(Constants.SERVER_BOOTSTRAP_WORKER_GROUP_SIZE) : Runtime.getRuntime().availableProcessors() * 2; HttpWsConnectorFactory connectorFactory = new DefaultHttpWsConnectorFactory(bossGroup, workerGroup, workerGroup); ServerBootstrapConfiguration serverBootstrapConfiguration = HttpConnectorUtil .getServerBootstrapConfiguration(transportsConfiguration.getTransportProperties()); for (ListenerConfiguration listenerConfiguration : transportsConfiguration.getListenerConfigurations()) { ServerConnector serverConnector = connectorFactory.createServerConnector(serverBootstrapConfiguration, listenerConfiguration); DataHolder.getInstance().getMicroservicesRegistries() .put(Util.createServerConnectorID(listenerConfiguration.getHost(), listenerConfiguration.getPort()), msRegistry); serverConnectors.add(serverConnector); } } catch (ConfigurationException e) { throw new RuntimeException("Error loading yaml Configuration", e); } } } /** * Method to configure transports with external transport configuration * * @param transportsConfiguration the external transports configuration */ protected void configureTransport(TransportsConfiguration transportsConfiguration) { if (transportsConfiguration != null) { Map transportProperties = HttpConnectorUtil.getTransportProperties(transportsConfiguration); int bossGroup = transportProperties.get(Constants.SERVER_BOOTSTRAP_BOSS_GROUP_SIZE) != null ? (Integer) transportProperties.get(Constants.SERVER_BOOTSTRAP_BOSS_GROUP_SIZE) : Runtime.getRuntime() .availableProcessors(); int workerGroup = transportProperties.get(Constants.SERVER_BOOTSTRAP_WORKER_GROUP_SIZE) != null ? (Integer) transportProperties.get(Constants.SERVER_BOOTSTRAP_WORKER_GROUP_SIZE) : Runtime.getRuntime() .availableProcessors() * 2; HttpWsConnectorFactory connectorFactory = new DefaultHttpWsConnectorFactory(bossGroup, workerGroup, workerGroup); ServerBootstrapConfiguration serverBootstrapConfiguration = HttpConnectorUtil.getServerBootstrapConfiguration(transportsConfiguration.getTransportProperties()); for (ListenerConfiguration listenerConfiguration : transportsConfiguration.getListenerConfigurations()) { ServerConnector serverConnector = connectorFactory.createServerConnector(serverBootstrapConfiguration, listenerConfiguration); DataHolder.getInstance().getMicroservicesRegistries() .put(Util.createServerConnectorID(listenerConfiguration.getHost(), listenerConfiguration.getPort()), msRegistry); serverConnectors.add(serverConnector); } } else { configureTransport(); } } private void checkState() { if (isStarted) { throw new IllegalStateException("Microservices runner already started"); } } /** * Start this Microservices runner. This will startup all the HTTP transports. */ public void start() { msRegistry.getSessionManager().init(); handleServiceLifecycleMethods(); MSF4JHttpConnectorListener msf4JHttpConnectorListener = new MSF4JHttpConnectorListener(); MSF4JWSConnectorListener msf4JWSConnectorListener = new MSF4JWSConnectorListener(); serverConnectors.forEach(serverConnector -> { ServerConnectorFuture serverConnectorFuture = serverConnector.start(); serverConnectorFuture.setHttpConnectorListener(msf4JHttpConnectorListener); serverConnectorFuture.setWebSocketConnectorListener(msf4JWSConnectorListener); serverConnectorFuture.setPortBindingEventListener(new HttpConnectorPortBindingListener()); isStarted = true; log.info("Microservices server started in " + (System.currentTimeMillis() - startTime) + "ms"); }); } /** * Stop this Microservices runner. This will stop all the HTTP Transports. */ public void stop() { serverConnectors.forEach(ServerConnector::stop); log.info("Microservices server stopped"); } /** * Get the MicroservicesRegistry instance of this runner. * * @return MicroservicesRegistry instance of this runner */ public MicroservicesRegistryImpl getMsRegistry() { return msRegistry; } protected void handleServiceLifecycleMethods() { msRegistry.initServices(); Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { msRegistry.preDestroyServices(); } }); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/MicroservicesServer.java ================================================ /* * Copyright (c) 2017 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ package org.wso2.msf4j; import org.wso2.transport.http.netty.contract.config.ListenerConfiguration; import java.util.Map; /** * Interface to handle server details of Microservices server. transport details etc. *

* This interface handles server details of microservices server in OSGi environment. * * @since 4.5.0 */ public interface MicroservicesServer { /** * Provide Listener Configuration details with server connector id used in microservices server. * * @return ListenerConfigurationMap */ Map getListenerConfigurations(); } ================================================ FILE: core/src/main/java/org/wso2/msf4j/PersistentSessionManager.java ================================================ /* * Copyright (c) 2016 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ package org.wso2.msf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.nio.file.Paths; import java.util.Arrays; import java.util.Map; /** * This session manager persists sessions in the local file system. */ public class PersistentSessionManager extends AbstractSessionManager { private static final Logger log = LoggerFactory.getLogger(PersistentSessionManager.class); private static final String SESSION_DIR = ".sessions"; public PersistentSessionManager() { File dir = new File(SESSION_DIR); if (!dir.exists() && !dir.mkdirs()) { throw new IllegalStateException("Cannot create .tmp directory"); } } @Override public void loadSessions(Map sessions) { File dir = new File(SESSION_DIR); if (!dir.exists()) { return; } String path = Paths.get(SESSION_DIR).toString(); Arrays.stream(new File(path).listFiles()).parallel().forEach(file -> { Session session = readSession(file.getName()); // Delete expired session files if (System.currentTimeMillis() - session.getLastAccessedTime() >= (long) session.getMaxInactiveInterval() * 60 * 1000 && !file.delete()) { log.warn("Couldn't delete expired session file " + file.getAbsolutePath()); } else { sessions.put(session.getId(), session); } }); } @Override public Session readSession(String sessionId) { String path = Paths.get(SESSION_DIR, sessionId).toString(); if (!new File(path).exists()) { return null; } try (FileInputStream fis = new FileInputStream(path); ObjectInputStream ois = new ObjectInputStream(fis);) { Session session = (Session) ois.readObject(); session.setManager(this); return session; } catch (IOException | ClassNotFoundException e) { throw new RuntimeException("Cannot read session " + sessionId, e); } } @Override public void saveSession(Session session) { try (FileOutputStream fout = new FileOutputStream(Paths.get(SESSION_DIR, session.getId()).toString()); ObjectOutputStream oos = new ObjectOutputStream(fout)) { oos.writeObject(session); } catch (IOException e) { throw new RuntimeException("Cannot save session " + session.getId(), e); } } @Override public void deleteSession(Session session) { String pathname = Paths.get(SESSION_DIR, session.getId()).toString(); if (!new File(pathname).delete()) { throw new IllegalStateException("File " + pathname + " deletion failed"); } } @Override public void updateSession(Session session) { saveSession(session); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/Request.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j; import io.netty.buffer.ByteBuf; import org.wso2.msf4j.internal.HttpHeadersImpl; import org.wso2.msf4j.internal.MSF4JConstants; import org.wso2.transport.http.netty.contract.Constants; import org.wso2.transport.http.netty.contract.HttpResponseFuture; import org.wso2.transport.http.netty.contract.exceptions.ServerConnectorException; import org.wso2.transport.http.netty.message.HttpCarbonMessage; import org.wso2.transport.http.netty.message.HttpMessageDataStreamer; import java.io.InputStream; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import javax.ws.rs.core.HttpHeaders; /** * Class that represents an HTTP request in MSF4J level. */ public class Request { private final HttpCarbonMessage httpCarbonMessage; private List acceptTypes = null; private String contentType = null; private SessionManager sessionManager; private Session session; public Request(HttpCarbonMessage httpCarbonMessage) { this.httpCarbonMessage = httpCarbonMessage; // find accept types String acceptHeaderStr = httpCarbonMessage.getHeader(javax.ws.rs.core.HttpHeaders.ACCEPT); acceptTypes = (acceptHeaderStr != null) ? Arrays.stream(acceptHeaderStr.split("\\s*,\\s*")) .map(mediaType -> mediaType.split("\\s*;\\s*")[0]) .collect(Collectors.toList()) : null; //find content type String contentTypeHeaderStr = httpCarbonMessage.getHeader(javax.ws.rs.core.HttpHeaders.CONTENT_TYPE); //Trim specified charset since UTF-8 is assumed contentType = (contentTypeHeaderStr != null) ? contentTypeHeaderStr.split("\\s*;\\s*")[0] : null; } public void setSessionManager(SessionManager sessionManager) { this.sessionManager = sessionManager; } /** * @return true if the request does not have body content */ public boolean isEmpty() { return httpCarbonMessage.isEmpty(); } /** * @return next available message body chunk */ @Deprecated public ByteBuf getMessageBody() { return httpCarbonMessage.getMessageBody(); } /** * @return map of headers of the HTTP request */ public HttpHeaders getHeaders() { return new HttpHeadersImpl(httpCarbonMessage.getHeaders()); } /** * Get an HTTP header of the HTTP request. * * @param key name of the header * @return value of the header */ public String getHeader(String key) { return httpCarbonMessage.getHeader(key); } /** * Set a property in the underlining Carbon Message. * * @param key property key * @return value of the property key */ public Object getProperty(String key) { return httpCarbonMessage.getProperty(key); } /** * @return property map of the underlining CarbonMessage */ public Map getProperties() { return httpCarbonMessage.getProperties(); } /** * Set a property in the underlining Carbon Message. * * @param key property key * @param value property value */ public void setProperty(String key, Object value) { httpCarbonMessage.setProperty(key, value); } /** * Remove a property from the underlining CarbonMessage object. * * @param key property key */ public void removeProperty(String key) { httpCarbonMessage.removeProperty(key); } /** * @return URL of the request. */ public String getUri() { return (String) httpCarbonMessage.getProperty(Constants.TO); } /** * @return HTTP method of the request. */ public String getHttpMethod() { return httpCarbonMessage.getHttpMethod(); } /** * @return accept type of the request. */ public List getAcceptTypes() { return acceptTypes; } /** * @return request body content type. */ public String getContentType() { return contentType; } /** * Returns the current session associated with this request, or if the request does not have a session, * creates one. * * @return Session */ public Session getSession() { if (sessionManager == null) { throw new IllegalStateException("SessionManager has not been set"); } if (session != null) { return session.setAccessed(); } String cookieHeader = getHeader("Cookie"); if (cookieHeader != null) { session = Arrays.stream(cookieHeader.split(";")).map(String::trim) .filter(cookie -> cookie.startsWith(MSF4JConstants.SESSION_ID)) .findFirst() .map(jsession -> sessionManager.getSession(jsession.substring(MSF4JConstants.SESSION_ID.length()))) .orElseGet(sessionManager::createSession); return session.setAccessed(); } return session = sessionManager.createSession(); } /** * Returns the current HttpSession associated with this request or, if there is no current session and create is * true, returns a new session. * * @param create Create a new session or not * @return Session */ public Session getSession(boolean create) { if (sessionManager == null) { throw new IllegalStateException("SessionManager has not been set"); } if (session != null) { return session.setAccessed(); } String cookieHeader = getHeader("Cookie"); if (cookieHeader != null) { session = Arrays.stream(cookieHeader.split(";")).map(String::trim) .filter(cookie -> cookie.startsWith(MSF4JConstants.SESSION_ID)) .findFirst() .map(jsession -> sessionManager.getSession(jsession.substring(MSF4JConstants.SESSION_ID.length()))) .orElseGet(() -> { if (create) { return sessionManager.createSession(); } return null; }); return session.setAccessed(); } else if (create) { return session = sessionManager.createSession(); } return null; } Session getSessionInternal() { return session; } /** * Get underlying HttpCarbonMessage. * * @return HttpCarbonMessage instance of the Request */ HttpCarbonMessage getHttpCarbonMessage() { return httpCarbonMessage; } /** * Method use to send the response to the caller. * * @param carbonMessage Response message * @return true if no errors found, else otherwise * @throws ServerConnectorException server connector exception. */ public boolean respond(HttpCarbonMessage carbonMessage) throws ServerConnectorException { HttpResponseFuture statusFuture = httpCarbonMessage.respond(carbonMessage); return statusFuture.getStatus().getCause() == null; } /** * Returns InputStream of the ByteBuffers in message content. * * @return InputStream of the ByteBuffers */ public InputStream getMessageContentStream() { return new HttpMessageDataStreamer(httpCarbonMessage).getInputStream(); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/Response.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.handler.codec.http.DefaultLastHttpContent; import org.wso2.msf4j.delegates.CookieHeaderProvider; import org.wso2.msf4j.internal.HttpHeadersImpl; import org.wso2.msf4j.internal.MSF4JConstants; import org.wso2.msf4j.internal.entitywriter.EntityWriter; import org.wso2.msf4j.internal.entitywriter.EntityWriterRegistry; import org.wso2.transport.http.netty.contract.exceptions.ServerConnectorException; import org.wso2.transport.http.netty.message.HttpCarbonMessage; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.NewCookie; /** * Class that represents an HTTP response in MSF4J level. */ public class Response { private static final String COMMA_SEPARATOR = ", "; private static final int NULL_STATUS_CODE = -1; public static final int NO_CHUNK = 0; public static final int DEFAULT_CHUNK_SIZE = -1; private final HttpCarbonMessage httpCarbonMessage; private int statusCode = NULL_STATUS_CODE; private String mediaType = null; private Object entity; private int chunkSize = NO_CHUNK; private Request request; private javax.ws.rs.core.Response jaxrsResponse; public Response(HttpCarbonMessage responder) { this.httpCarbonMessage = responder; } public Response(Request request) { this(request.getHttpCarbonMessage().cloneCarbonMessageWithOutData()); this.httpCarbonMessage.getHeaders().clear(); this.request = request; } /** * @return returns true if the message body is empty */ public boolean isEmpty() { return httpCarbonMessage.isEmpty(); } /** * @return next available message body chunk */ @Deprecated public ByteBuf getMessageBody() { return httpCarbonMessage.getMessageBody(); } /** * @return map of headers in the response object */ public HttpHeaders getHeaders() { return new HttpHeadersImpl(httpCarbonMessage.getHeaders()); } /** * Get a header of the response. * * @param key header neame * @return value of the header */ public String getHeader(String key) { return httpCarbonMessage.getHeader(key); } /** * Set a header in the response. * * @param key header name * @param value value of the header * @return Response object */ public Response setHeader(String key, String value) { httpCarbonMessage.setHeader(key, value); return this; } /** * Add a set of headers to the response as a map. * * @param headerMap headers to be added to the response */ public void setHeaders(Map headerMap) { headerMap.forEach(httpCarbonMessage::setHeader); } /** * Get a property of the CarbonMessage. * * @param key Property key * @return property value */ public Object getProperty(String key) { return httpCarbonMessage.getProperty(key); } /** * @return map of properties in the CarbonMessage */ public Map getProperties() { return httpCarbonMessage.getProperties(); } /** * Set a property in the underlining CarbonMessage object. * * @param key property key * @param value property value */ public void setProperty(String key, Object value) { httpCarbonMessage.setProperty(key, value); } /** * @param key remove the header with this name */ public void removeHeader(String key) { httpCarbonMessage.removeHeader(key); } /** * Remove a property from the underlining CarbonMessage object. * * @param key property key */ public void removeProperty(String key) { httpCarbonMessage.removeProperty(key); } /** * @return the underlining CarbonMessage object */ HttpCarbonMessage getHttpCarbonMessage() { return httpCarbonMessage; } /** * Set the status code of the HTTP response. * * @param statusCode HTTP status code * @return Response object */ public Response setStatus(int statusCode) { this.statusCode = statusCode; return this; } /** * Get the status code of the HTTP response. * * @return status code value */ public int getStatusCode() { if (statusCode != NULL_STATUS_CODE) { return statusCode; } else if (entity != null) { return javax.ws.rs.core.Response.Status.OK.getStatusCode(); } else { return javax.ws.rs.core.Response.Status.NO_CONTENT.getStatusCode(); } } /** * Set HTTP media type of the response. * * @param mediaType HTTP media type string * @return Response object */ public Response setMediaType(String mediaType) { this.mediaType = mediaType; return this; } /** * Set http body for the HTTP response. * * @param entity object that should be set as the response body * @return Response object */ public Response setEntity(Object entity) { if (!httpCarbonMessage.isEmpty()) { throw new IllegalStateException("CarbonMessage should not contain a message body"); } if (entity instanceof javax.ws.rs.core.Response) { javax.ws.rs.core.Response response = (javax.ws.rs.core.Response) entity; this.jaxrsResponse = response; this.entity = response.getEntity(); //TODO: if you remove these lines, the tests fail. /*MultivaluedMap multivaluedMap = response.getStringHeaders(); if (multivaluedMap != null) { multivaluedMap.forEach((key, strings) -> setHeader(key, String.join(COMMA_SEPARATOR, strings))); }*/ setStatus(response.getStatus()); if (response.getMediaType() != null) { setMediaType(response.getMediaType().toString()); } } else { this.entity = entity; } return this; } /** * Specify the chunk size to send the response. * * @param chunkSize if 0 response will be sent without chunking * if -1 a default chunk size will be applied */ public void setChunkSize(int chunkSize) { this.chunkSize = chunkSize; } /** * Send the HTTP response using the content in this object. */ public void send() { httpCarbonMessage.setHttpStatusCode(getStatusCode()); List cookiesHeaderValue = new ArrayList<>(); if (jaxrsResponse != null) { MultivaluedMap multivaluedMap = jaxrsResponse.getStringHeaders(); if (multivaluedMap != null) { multivaluedMap.forEach((key, strings) -> setHeader(key, String.join(COMMA_SEPARATOR, strings))); } // String - cookie name Map cookies = jaxrsResponse.getCookies(); CookieHeaderProvider cookieProvider = new CookieHeaderProvider(); cookies.forEach((name, cookie) -> { cookiesHeaderValue.add(cookieProvider.toString(cookie)); }); } //Set-Cookie: session Session session = request.getSessionInternal(); if (session != null && session.isValid() && session.isNew()) { cookiesHeaderValue.add(MSF4JConstants.SESSION_ID + session.getId()); } for (String cookie : cookiesHeaderValue) { httpCarbonMessage.getHeaders().add("Set-Cookie", cookie); } processEntity(); } @SuppressWarnings("unchecked") private void processEntity() { if (entity != null) { EntityWriter entityWriter = EntityWriterRegistry.getEntityWriter(entity.getClass()); entityWriter.writeData(httpCarbonMessage, entity, mediaType, chunkSize, request.getHttpCarbonMessage()); } else { ByteBuffer byteBuffer = ByteBuffer.allocate(0); httpCarbonMessage.addHttpContent(new DefaultLastHttpContent(Unpooled.wrappedBuffer(byteBuffer))); try { request.getHttpCarbonMessage().respond(httpCarbonMessage); } catch (ServerConnectorException e) { throw new RuntimeException("Error while sending the response.", e); } } } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/ServiceMethodInfo.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j; import java.lang.reflect.Method; /** * Contains information about service method details. * @deprecated */ public class ServiceMethodInfo { private final String methodName; private final Method method; private final Request request; public ServiceMethodInfo(String methodName, Method method, Request request) { this.methodName = methodName; this.method = method; this.request = request; } public String getMethodName() { return methodName; } public Method getMethod() { return method; } /** * Returns the value of the named attribute as an Object, or null if no attribute of the given name exists. * * @param name a {@link String} specifying the name of the attribute * @return an {@link Object} containing the value of the attribute, or null if the attribute does not exist */ public Object getAttribute(String name) { return request.getProperty(name); } /** * Stores an attribute in this request. * * @param name a {@link String} specifying the name of the attribute * @param obj the {@link Object} to be stored */ public void setAttribute(String name, Object obj) { request.setProperty(name, obj); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/Session.java ================================================ /* * Copyright (c) 2016 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ package org.wso2.msf4j; import java.io.Serializable; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * Represents a transport session. */ public class Session implements Serializable { private static final long serialVersionUID = -3945729418329160933L; private transient SessionManager sessionManager; private String id; private long creationTime; private long lastAccessedTime; private int maxInactiveInterval; private boolean isValid = true; private boolean isNew = true; private Map attributes = new ConcurrentHashMap<>(); public Session() { } public Session(String id, int maxInactiveInterval) { this.id = id; this.maxInactiveInterval = maxInactiveInterval; creationTime = System.currentTimeMillis(); lastAccessedTime = creationTime; } long getCreationTime() { return creationTime; } String getId() { return id; } void setMaxInactiveInterval(int interval) { this.maxInactiveInterval = interval; } int getMaxInactiveInterval() { return maxInactiveInterval; } public Object getAttribute(String name) { checkValidity(); return attributes.get(name); } public Set getAttributeNames() { checkValidity(); return attributes.keySet(); } public void setAttribute(String name, Object value) { checkValidity(); attributes.put(name, value); sessionManager.updateSession(this); } public void removeAttribute(String name) { checkValidity(); sessionManager.updateSession(this); attributes.remove(name); } private void checkValidity() { if (!isValid) { throw new IllegalStateException("Session is invalid"); } } public void invalidate() { sessionManager.invalidateSession(this); attributes.clear(); isValid = false; } boolean isValid() { return isValid; } boolean isNew() { return isNew; } boolean getIsNew() { return isNew; } public void setNew(boolean isNew) { this.isNew = isNew; } Session setAccessed() { checkValidity(); lastAccessedTime = System.currentTimeMillis(); return this; } long getLastAccessedTime() { return lastAccessedTime; } public void setManager(SessionManager sessionManager) { this.sessionManager = sessionManager; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/SessionManager.java ================================================ /* * Copyright (c) 2016 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ package org.wso2.msf4j; import java.util.Map; /** * Manage transport sessions. */ public interface SessionManager { /** * Initialize the SessionManager. */ void init(); /** * Retrieve a session with ID. * * @param sessionId ID of the session * @return Session with id sessionId if it exists, and null otherwise. */ Session getSession(String sessionId); /** * Create a new session. * * @return the newly created session */ Session createSession(); /** * Invalidate a session. * * @param session The session to be invalidated. */ void invalidateSession(Session session); /** * The maximum time in minutes a session could be inactive before it is expired. * * @return max inactive interval */ int getDefaultMaxInactiveInterval(); /** * The maximum number of active sessions that can exist. * * @return max active sessions */ int getDefaultMaxActiveSessions(); /** * @return Length of the session ID in bytes. */ int getSessionIdLength(); /** * Load sessions from a persistent storage, for example, into memory. * * @param sessions The map into which the sessions are to be loaded. */ void loadSessions(Map sessions); /** * Read a session from a persistent storage, for example, into memory. * * @param sessionId ID of the session to be loaded or read. * @return The Session object. */ Session readSession(String sessionId); /** * Persist a new session. * * @param session the new session to be persisted. */ void saveSession(Session session); /** * Delete a session which has been persisted. * * @param session Session to be deleted */ void deleteSession(Session session); /** * Update a session in persistent storage. * * @param session Session to to be updated */ void updateSession(Session session); /** * Stop this SessionManager. */ void stop(); } ================================================ FILE: core/src/main/java/org/wso2/msf4j/SwaggerService.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j; /** * Interface need to implement for Swagger service. */ public interface SwaggerService { void init(MicroservicesRegistry serviceRegistry); } ================================================ FILE: core/src/main/java/org/wso2/msf4j/beanconversion/BeanConversionException.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.beanconversion; /** * Exception that is thrown when a bean conversion is failed. */ public class BeanConversionException extends RuntimeException { public BeanConversionException(String msg) { super(msg); } public BeanConversionException(String msg, Exception cause) { super(msg, cause); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/beanconversion/MediaTypeConverter.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.beanconversion; import java.io.InputStream; import java.lang.reflect.Type; import java.nio.ByteBuffer; /** * Interface of media type conversion classes. */ public abstract class MediaTypeConverter { protected static final String UTF_8_CHARSET = "UTF-8"; /** * Convert an object to a specific media type. * * @param object object that needs to be converted to a media content * @return converted media content */ public ByteBuffer convertToMedia(Object object) { if (object == null) { throw new BeanConversionException("Object cannot be null"); } return toMedia(object); } /** * Create an object from a specific content. * * @param content content that needs to be converted to an object * @param targetType media type of the content * @return created object * @throws BeanConversionException throws if object creation is failed */ public Object convertToObject(ByteBuffer content, Type targetType) throws BeanConversionException { if (content == null || targetType == null) { throw new BeanConversionException("Content or target type cannot be null"); } return toObject(content, targetType); } /** * Create an object from input stream.. * * @param inputStream stream that needs to be converted to an object * @param targetType media type of the content * @return created object * @throws BeanConversionException throws if object creation is failed */ public Object convertToObject(InputStream inputStream, Type targetType) throws BeanConversionException { if (inputStream == null || targetType == null) { throw new BeanConversionException("Content or target type cannot be null"); } return toObject(inputStream, targetType); } /** * Return an array of supported media types. * * @return String array of media types */ public abstract String[] getSupportedMediaTypes(); /** * Convert an object to a specific media type. * * @param object object that needs to be converted to a media content * @return converted media content * @throws BeanConversionException throws if conversion is failed */ protected abstract ByteBuffer toMedia(Object object) throws BeanConversionException; /** * Create an object from a specific content. * * @param content content that needs to be converted to an object * @param targetType media type of the content * @return created object * @throws BeanConversionException throws if object creation is failed */ protected abstract Object toObject(ByteBuffer content, Type targetType) throws BeanConversionException; /** * Create an object from input stream. * * @param inputStream input stream that needs to be converted to an object * @param targetType target object type * @return created object * @throws BeanConversionException throws if object creation is failed */ protected abstract Object toObject(InputStream inputStream, Type targetType) throws BeanConversionException; } ================================================ FILE: core/src/main/java/org/wso2/msf4j/config/MSF4JConfig.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.config; import org.wso2.carbon.config.annotation.Configuration; import org.wso2.carbon.config.annotation.Element; /** * Configuration class MSF4J. */ @Configuration(namespace = "wso2.msf4j.configuration", description = "MSF4J configuration") public class MSF4JConfig { @Element(description = "No of worker pool threads to handle MSF4J requests") private int threadCount = 100; @Element(description = "MSF4J thread pool name ") private String threadPoolName = "msf4j.executor.workerpool"; public int getThreadCount() { return threadCount; } public void setThreadCount(int threadCount) { this.threadCount = threadCount; } public String getThreadPoolName() { return threadPoolName; } public void setThreadPoolName(String threadPoolName) { this.threadPoolName = threadPoolName; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/exception/InterceptorException.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.exception; /** * Exception that is thrown when an interceptor fails. */ public class InterceptorException extends Exception { public InterceptorException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/exception/OSGiDeclarativeServiceException.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.exception; /** * Runtime exception when MicroServices Service Component fails to add or remove OSGi Declarative Services */ public class OSGiDeclarativeServiceException extends RuntimeException { public OSGiDeclarativeServiceException(String message) { super(message); } public OSGiDeclarativeServiceException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/formparam/FileInfo.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.formparam; /** *

* This class contains basic file information which can be used with * FormDataParam("file") InputStream. *

*/ public class FileInfo { private String fileName; private String contentType; public String getFileName() { return fileName; } public void setFileName(String fileName) { this.fileName = fileName; } public String getContentType() { return contentType; } public void setContentType(String contentType) { this.contentType = contentType; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/formparam/FormDataParam.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.formparam; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * This {@link java.lang.annotation.Annotation} provide supports for multipart/form-data with MSF4J. * */ @Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FormDataParam { /** * Defines the control name of a "multipart/form-data" body part whose * content will be used to initialize the value of the annotated method * argument. * * @return name of a "multipart/form-data" body part */ String value(); } ================================================ FILE: core/src/main/java/org/wso2/msf4j/formparam/FormItem.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.formparam; import org.wso2.msf4j.formparam.exception.FormUploadException; import org.wso2.msf4j.formparam.util.Closeable; import org.wso2.msf4j.formparam.util.FormItemHeader; import org.wso2.msf4j.formparam.util.StreamUtil; import java.io.IOException; import java.io.InputStream; /** *

This provides access to a file or form item that was * received within a multipart/form-data POST request. * The items contents are retrieved by calling {@link #openStream()}.

*

Instances of this class are created by accessing the * iterator, returned by * {@link FormParamIterator}.

*

Note: There is an interaction between the iterator and * its associated instances of {@link FormItem}: By invoking * @see java.util.Iterator#hasNext() on the iterator, you discard all data, * which hasn't been read so far from the previous data.

* */ public class FormItem { /** * The file items content type. */ private final String contentType; /** * The file items field name. */ private final String fieldName; /** * The file items file name. */ private final String name; /** * Whether the file item is a form field. */ private final boolean formField; /** * The file items input stream. */ private final InputStream stream; /** * The headers, if any. */ private FormItemHeader headers; /** * Creates a new instance. * * @param pName The items file name, or null. * @param pFieldName The items field name. * @param pContentType The items content type, or null. * @param pFormField Whether the item is a form field. * @param pContentLength The items content length, if known, or -1 * @throws IOException Creating the file item failed. */ FormItem(String pName, String pFieldName, String pContentType, boolean pFormField, long pContentLength, MultipartStream multi) { name = pName; fieldName = pFieldName; contentType = pContentType; formField = pFormField; stream = multi.newInputStream(); } /** * Returns the items content type, or null. * * @return Content type, if known, or null. */ public String getContentType() { return contentType; } /** * Returns the items field name. * * @return Field name. */ public String getFieldName() { return fieldName; } /** * Returns the items file name. * * @return File name, if known, or null. */ public String getName() { return StreamUtil.checkFileName(name); } /** * Returns, whether this is a form field. * * @return True, if the item is a form field, * otherwise false. */ public boolean isFormField() { return formField; } /** * Returns an input stream, which may be used to * read the items contents. * * @return Opened input stream. * @throws IOException An I/O error occurred. */ public InputStream openStream() throws IOException { if (((Closeable) stream).isClosed()) { throw new ItemSkippedException(); } return stream; } /** * Closes the file item. * */ public void close() { try { stream.close(); } catch (IOException e) { throw new FormUploadException("Error while closing the stream", e); } } /** * Returns the file item headers. * * @return The items header object */ public FormItemHeader getHeaders() { return headers; } /** * Sets the file item headers. * * @param pHeaders The items header object */ void setHeaders(FormItemHeader pHeaders) { headers = pHeaders; } static class ItemSkippedException extends RuntimeException { /** * The exceptions serial version UID, which is being used * when serializing an exception instance. */ private static final long serialVersionUID = -7280778431581963740L; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/formparam/FormParamIterator.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.formparam; import org.apache.commons.io.IOUtils; import org.wso2.msf4j.Request; import org.wso2.msf4j.formparam.exception.FormUploadException; import org.wso2.msf4j.formparam.exception.InvalidContentTypeException; import org.wso2.msf4j.formparam.util.FormItemHeader; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.util.Iterator; import java.util.Locale; import java.util.Map; import java.util.NoSuchElementException; import static java.lang.String.format; /** * */ public class FormParamIterator implements Iterator { /** * HTTP content type header name. */ private static final String CONTENT_TYPE = "Content-type"; /** * HTTP content disposition header name. */ private static final String CONTENT_DISPOSITION = "Content-disposition"; /** * HTTP content length header name. */ private static final String CONTENT_LENGTH = "Content-length"; /** * Content-disposition value for form data. */ private static final String FORM_DATA = "form-data"; /** * Content-disposition value for file attachment. */ private static final String ATTACHMENT = "attachment"; /** * Part of HTTP content type header. */ private static final String MULTIPART = "multipart/"; /** * HTTP content type header for multipart forms. */ private static final String MULTIPART_FORM_DATA = "multipart/form-data"; /** * HTTP content type header for multiple uploads. */ private static final String MULTIPART_MIXED = "multipart/mixed"; /** * The multi part stream to process. */ private final MultipartStream multi; /** * The boundary, which separates the various parts. */ private final byte[] boundary; /** * The item, which we currently process. */ private FormItem currentItem; /** * The current items field name. */ private String currentFieldName; /** * Whether we are currently skipping the preamble. */ private boolean skipPreamble; /** * Whether the current item may still be read. */ private boolean itemValid; /** * Whether we have seen the end of the file. */ private boolean eof; // ----------------------------------------------------------- Data members /** * Processes an RFC 1867 * compliant multipart/form-data stream. * * @param request The context for the request to be parsed. * @throws FormUploadException if there are problems reading/parsing * the request or storing files. * @throws IOException An I/O error occurred. This may be a network * error while communicating with the client or a problem while * storing the uploaded content. */ public FormParamIterator(Request request) throws FormUploadException, IOException { this(new RequestContext(request)); } // ------------------------------------------------------ Protected methods /** * Retrieves the boundary from the Content-type header. * * @param contentType The value of the content type header from which to * extract the boundary value. * @return The boundary, as a byte array. */ private byte[] getBoundary(String contentType) { ParameterParser parser = new ParameterParser(); parser.setLowerCaseNames(true); // Parameter parser can handle null input Map params = parser.parse(contentType, new char[] { ';', ',' }); String boundaryStr = params.get("boundary"); if (boundaryStr == null) { return new byte[] {}; } byte[] boundary; try { boundary = boundaryStr.getBytes("ISO-8859-1"); } catch (UnsupportedEncodingException e) { boundary = boundaryStr.getBytes(Charset.defaultCharset()); // Intentionally falls back to default charset } return boundary; } /** * Retrieves the file name from the Content-disposition * header. * * @param headers The HTTP headers object. * @return The file name for the current encapsulation. */ private String getFileName(FormItemHeader headers) { return getFileName(headers.getHeader(CONTENT_DISPOSITION)); } /** * Returns the given content-disposition headers file name. * * @param pContentDisposition The content-disposition headers value. * @return The file name */ private String getFileName(String pContentDisposition) { String fileName = null; if (pContentDisposition != null) { String cdl = pContentDisposition.toLowerCase(Locale.ENGLISH); if (cdl.startsWith(FORM_DATA) || cdl.startsWith(ATTACHMENT)) { ParameterParser parser = new ParameterParser(); parser.setLowerCaseNames(true); // Parameter parser can handle null input Map params = parser.parse(pContentDisposition, ';'); if (params.containsKey("filename")) { fileName = params.get("filename"); if (fileName != null) { fileName = fileName.trim(); } else { // Even if there is no value, the parameter is present, // so we return an empty file name rather than no file // name. fileName = ""; } } } } return fileName; } /** * Retrieves the field name from the Content-disposition * header. * * @param headers A Map containing the HTTP request headers. * @return The field name for the current encapsulation. */ private String getFieldName(FormItemHeader headers) { return getFieldName(headers.getHeader(CONTENT_DISPOSITION)); } /** * Returns the field name, which is given by the content-disposition * header. * * @param pContentDisposition The content-dispositions header value. * @return The field jake */ private String getFieldName(String pContentDisposition) { String fieldName = null; if (pContentDisposition != null && pContentDisposition.toLowerCase(Locale.ENGLISH).startsWith(FORM_DATA)) { ParameterParser parser = new ParameterParser(); parser.setLowerCaseNames(true); // Parameter parser can handle null input Map params = parser.parse(pContentDisposition, ';'); fieldName = params.get("name"); if (fieldName != null) { fieldName = fieldName.trim(); } } return fieldName; } /** *

Parses the header-part and returns as key/value * pairs. *

*

If there are multiple headers of the same names, the name * will map to a comma-separated list containing the values. * * @param headerPart The header-part of the current * encapsulation. * @return A Map containing the parsed HTTP request headers. */ private FormItemHeader getParsedHeaders(String headerPart) { final int len = headerPart.length(); FormItemHeader headers = newFileItemHeaders(); int start = 0; for (;;) { int end = parseEndOfLine(headerPart, start); if (start == end) { break; } StringBuilder header = new StringBuilder(headerPart.substring(start, end)); start = end + 2; while (start < len) { int nonWs = start; while (nonWs < len) { char c = headerPart.charAt(nonWs); if (c != ' ' && c != '\t') { break; } ++nonWs; } if (nonWs == start) { break; } // Continuation line found end = parseEndOfLine(headerPart, nonWs); header.append(" ").append(headerPart.substring(nonWs, end)); start = end + 2; } parseHeaderLine(headers, header.toString()); } return headers; } /** * Creates a new instance of {@link FormItemHeader}. * * @return The new instance. */ private FormItemHeader newFileItemHeaders() { return new FormItemHeader(); } /** * Skips bytes until the end of the current line. * * @param headerPart The headers, which are being parsed. * @param end Index of the last byte, which has yet been * processed. * @return Index of the \r\n sequence, which indicates * end of line. */ private int parseEndOfLine(String headerPart, int end) { int index = end; for (;;) { int offset = headerPart.indexOf('\r', index); if (offset == -1 || offset + 1 >= headerPart.length()) { throw new IllegalStateException("Expected headers to be terminated by an empty line."); } if (headerPart.charAt(offset + 1) == '\n') { return offset; } index = offset + 1; } } /** * Reads the next header line. * * @param headers String with all headers. * @param header Map where to store the current header. */ private void parseHeaderLine(FormItemHeader headers, String header) { final int colonOffset = header.indexOf(':'); if (colonOffset == -1) { // This header line is malformed, skip it. return; } String headerName = header.substring(0, colonOffset).trim(); String headerValue = header.substring(header.indexOf(':') + 1).trim(); headers.addHeader(headerName, headerValue); } /** * Creates a new instance. * * @param ctx The request context. * @throws FormUploadException An error occurred while * parsing the request. * @throws IOException An I/O error occurred. */ private FormParamIterator(RequestContext ctx) throws FormUploadException, IOException { if (ctx == null) { throw new NullPointerException("ctx parameter"); } String contentType = ctx.getContentType(); if ((null == contentType) || (!contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART))) { throw new InvalidContentTypeException( format("the request doesn't contain a %s or %s stream, content type header is %s", MULTIPART_FORM_DATA, MULTIPART_MIXED, contentType)); } InputStream input = ctx.getInputStream(); String charEncoding = ctx.getCharacterEncoding(); boundary = getBoundary(contentType); if (boundary.length == 0) { IOUtils.closeQuietly(input); // avoid possible resource leak throw new FormUploadException("the request was rejected because no multipart boundary was found"); } try { multi = new MultipartStream(input, boundary); } catch (IllegalArgumentException iae) { IOUtils.closeQuietly(input); // avoid possible resource leak throw new InvalidContentTypeException( format("The boundary specified in the %s header is too long", CONTENT_TYPE), iae); } multi.setHeaderEncoding(charEncoding); skipPreamble = true; findNextItem(); } /** * Called for finding the next item, if any. * * @return True, if an next item was found, otherwise false. * @throws IOException An I/O error occurred. */ private boolean findNextItem() { if (eof) { return false; } if (currentItem != null) { currentItem.close(); currentItem = null; } for (;;) { boolean nextPart; if (skipPreamble) { nextPart = multi.skipPreamble(); } else { nextPart = multi.readBoundary(); } if (!nextPart) { if (currentFieldName == null) { // Outer multipart terminated -> No more data eof = true; return false; } // Inner multipart terminated -> Return to parsing the outer multi.setBoundary(boundary); currentFieldName = null; continue; } FormItemHeader headers = getParsedHeaders(multi.readHeaders()); if (currentFieldName == null) { // We're parsing the outer multipart String fieldName = getFieldName(headers); if (fieldName != null) { String subContentType = headers.getHeader(CONTENT_TYPE); if (subContentType != null && subContentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART_MIXED)) { currentFieldName = fieldName; // Multiple files associated with this field name byte[] subBoundary = getBoundary(subContentType); multi.setBoundary(subBoundary); skipPreamble = true; continue; } String fileName = getFileName(headers); currentItem = new FormItem(fileName, fieldName, headers.getHeader(CONTENT_TYPE), fileName == null, getContentLength(headers), multi); currentItem.setHeaders(headers); itemValid = true; return true; } } else { String fileName = getFileName(headers); if (fileName != null) { currentItem = new FormItem(fileName, currentFieldName, headers.getHeader(CONTENT_TYPE), false, getContentLength(headers), multi); currentItem.setHeaders(headers); itemValid = true; return true; } } multi.discardBodyData(); } } private long getContentLength(FormItemHeader pHeaders) { try { return Long.parseLong(pHeaders.getHeader(CONTENT_LENGTH)); } catch (Exception e) { return -1; } } /** * Returns, whether another instance of {@link FormItem} * is available. * * @return True, if one or more additional file items * are available, otherwise false. */ public boolean hasNext() { if (eof) { return false; } return itemValid || findNextItem(); } /** * Returns the next available {@link FormItem}. * * @return FileItemStream instance, which provides * access to the next file item. */ public FormItem next() { if (eof || (!itemValid && !hasNext())) { throw new NoSuchElementException(); } itemValid = false; return currentItem; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/formparam/MultipartStream.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.formparam; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.msf4j.formparam.exception.FormUploadException; import org.wso2.msf4j.formparam.util.Closeable; import org.wso2.msf4j.formparam.util.StreamUtil; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import static java.lang.String.format; /** *

Low level API for processing file uploads.

* *

This class can be used to process data streams conforming to MIME * 'multipart' format as defined in * RFC 1867. Arbitrarily * large amounts of data in the stream can be processed under constant * memory usage. * *

The format of the stream is defined in the following way:
*

* * multipart-body := preamble 1*encapsulation close-delimiter epilogue
* encapsulation := delimiter body CRLF
* delimiter := "--" boundary CRLF
* close-delimiter := "--" boundary "--"
* preamble := <ignore>
* epilogue := <ignore>
* body := header-part CRLF body-part
* header-part := 1*header CRLF
* header := header-name ":" header-value
* header-name := <printable ascii characters except ":">
* header-value := <any ascii characters except CR & LF>
* body-data := <arbitrary data>
*
* *

Note that body-data can contain another mulipart entity. There * is limited support for single pass processing of such nested * streams. The nested stream is required to have a * boundary token of the same length as the parent stream (see {@link * #setBoundary(byte[])}). *

* *

Here is an example of usage of this class.
*

*
 *   try {
 *     MultipartStream multipartStream = new MultipartStream(input, boundary);
 *     boolean nextPart = multipartStream.skipPreamble();
 *     OutputStream output;
 *     while(nextPart) {
 *       String header = multipartStream.readHeaders();
 *       // process headers
 *       // create some output stream
 *       multipartStream.readBodyData(output);
 *       nextPart = multipartStream.readBoundary();
 *     }
 *   } catch(MultipartStream.MalformedStreamException e) {
 *     // the stream failed to follow required syntax
 *   } catch(IOException e) {
 *     // a read or write error occurred
 *   }
 * 
* */ public class MultipartStream { // ----------------------------------------------------- Manifest constants /** * The Carriage Return ASCII character value. */ private static final byte CR = 0x0D; /** * The Line Feed ASCII character value. */ private static final byte LF = 0x0A; /** * The dash (-) ASCII character value. */ private static final byte DASH = 0x2D; /** * The maximum length of header-part that will be * processed (10 kilobytes = 10240 bytes.). */ private static final int HEADER_PART_SIZE_MAX = 10240; /** * The default length of the buffer used for processing a request. */ private static final int DEFAULT_BUFSIZE = 4096; /** * A byte sequence that marks the end of header-part * (CRLFCRLF). */ private static final byte[] HEADER_SEPARATOR = { CR, LF, CR, LF }; /** * A byte sequence that that follows a delimiter that will be * followed by an encapsulation (CRLF). */ private static final byte[] FIELD_SEPARATOR = { CR, LF }; /** * A byte sequence that that follows a delimiter of the last * encapsulation in the stream (--). */ private static final byte[] STREAM_TERMINATOR = { DASH, DASH }; /** * A byte sequence that precedes a boundary (CRLF--). */ private static final byte[] BOUNDARY_PREFIX = { CR, LF, DASH, DASH }; // ----------------------------------------------------------- Data members /** * The input stream from which data is read. */ private final InputStream input; /** * The length of the boundary token plus the leading CRLF--. */ private int boundaryLength; /** * The amount of data, in bytes, that must be kept in the buffer in order * to detect delimiters reliably. */ private final int keepRegion; /** * The byte sequence that partitions the stream. */ private final byte[] boundary; /** * The length of the buffer used for processing the request. */ private final int bufSize; /** * The buffer used for processing the request. */ private final byte[] buffer; /** * The index of first valid character in the buffer. *
* 0 <= head < bufSize */ private int head; /** * The index of last valid character in the buffer + 1. *
* 0 <= tail <= bufSize */ private int tail; /** * The content encoding to use when reading headers. */ private String headerEncoding; private static final Logger log = LoggerFactory.getLogger(MultipartStream.class); // ----------------------------------------------------------- Constructors /** *

Constructs a MultipartStream with a custom size buffer. *

*

Note that the buffer must be at least big enough to contain the * boundary string, plus 4 characters for CR/LF and double dash, plus at * least one byte of data. Too small a buffer size setting will degrade * performance. *

* @param input The InputStream to serve as a data source. * @param boundary The token used for dividing the stream into * encapsulations. * @param bufSize The size of the buffer to be used, in bytes. */ public MultipartStream(InputStream input, byte[] boundary, int bufSize) { if (boundary == null) { throw new IllegalArgumentException("boundary may not be null"); } // We prepend CR/LF to the boundary to chop trailing CR/LF from // body-data tokens. this.boundaryLength = boundary.length + BOUNDARY_PREFIX.length; if (bufSize < this.boundaryLength + 1) { throw new IllegalArgumentException("The buffer size specified for the MultipartStream is too small"); } this.input = input; this.bufSize = Math.max(bufSize, boundaryLength * 2); this.buffer = new byte[this.bufSize]; this.boundary = new byte[this.boundaryLength]; this.keepRegion = this.boundary.length; System.arraycopy(BOUNDARY_PREFIX, 0, this.boundary, 0, BOUNDARY_PREFIX.length); System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length, boundary.length); head = 0; tail = 0; } /** *

Constructs a MultipartStream with a default size buffer.

* * @param input The InputStream to serve as a data source. * @param boundary The token used for dividing the stream into * encapsulations. * @see #MultipartStream(InputStream, byte[], int) */ MultipartStream(InputStream input, byte[] boundary) { this(input, boundary, DEFAULT_BUFSIZE); } // --------------------------------------------------------- Public methods /** * Retrieves the character encoding used when reading the headers of an * individual part. When not specified, or null, the platform * default encoding is used. * * @return The encoding used to read part headers. */ public String getHeaderEncoding() { return headerEncoding; } /** * Specifies the character encoding to be used when reading the headers of * individual parts. When not specified, or null, the platform * default encoding is used. * * @param encoding The encoding used to read part headers. */ public void setHeaderEncoding(String encoding) { headerEncoding = encoding; } /** * Reads a byte from the buffer, and refills it as * necessary. * * @return The next byte from the input stream. * @throws IOException if there is no more data available. */ public byte readByte() throws IOException { // Buffer depleted ? if (head == tail) { head = 0; // Refill. tail = input.read(buffer, head, bufSize); if (tail == -1) { // No more data available. throw new IOException("No more data is available"); } } return buffer[head++]; } /** * Skips a boundary token, and checks whether more * encapsulations are contained in the stream. * * @return true if there are more encapsulations in * this stream; false otherwise. */ public boolean readBoundary() { byte[] marker = new byte[2]; boolean nextChunk; head += boundaryLength; try { marker[0] = readByte(); if (marker[0] == LF) { // Work around IE5 Mac bug with input type=image. // Because the boundary delimiter, not including the trailing // CRLF, must not appear within any file (RFC 2046, section // 5.1.1), we know the missing CR is due to a buggy browser // rather than a file containing something similar to a // boundary. return true; } marker[1] = readByte(); if (arrayequals(marker, STREAM_TERMINATOR, 2)) { nextChunk = false; } else if (arrayequals(marker, FIELD_SEPARATOR, 2)) { nextChunk = true; } else { throw new MalformedStreamException("Unexpected characters follow a boundary"); } } catch (IOException e) { throw new MalformedStreamException("Stream ended unexpectedly"); } return nextChunk; } /** *

Changes the boundary token used for partitioning the stream. *

*

This method allows single pass processing of nested multipart * streams. *

*

The boundary token of the nested stream is required * to be of the same length as the boundary token in parent stream. *

*

Restoring the parent stream boundary token after processing of a * nested stream is left to the application. *

* @param boundary The boundary to be used for parsing of the nested * stream. */ public void setBoundary(byte[] boundary) { if (boundary.length != boundaryLength - BOUNDARY_PREFIX.length) { throw new IllegalBoundaryException("The length of a boundary token can not be changed"); } System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length, boundary.length); } /** *

Reads the header-part of the current * encapsulation. *

*

Headers are returned verbatim to the input stream, including the * trailing CRLF marker. Parsing is left to the * application. *

* * @return The header-part of the current encapsulation. */ public String readHeaders() { int i = 0; byte b; // to support multi-byte characters ByteArrayOutputStream baos = new ByteArrayOutputStream(); int size = 0; while (i < HEADER_SEPARATOR.length) { try { b = readByte(); } catch (IOException e) { throw new MalformedStreamException("Stream ended unexpectedly"); } if (++size > HEADER_PART_SIZE_MAX) { throw new MalformedStreamException( format("Header section has more than %s bytes (maybe it is not properly terminated)", Integer.valueOf(HEADER_PART_SIZE_MAX))); } if (b == HEADER_SEPARATOR[i]) { i++; } else { i = 0; } baos.write(b); } String headers = null; if (headerEncoding != null) { try { headers = baos.toString(headerEncoding); } catch (UnsupportedEncodingException e) { // Fall back to platform default if specified encoding is not // supported. if (!headerEncoding.equals(Charset.defaultCharset().displayName())) { try { headers = baos.toString(Charset.defaultCharset().displayName()); } catch (UnsupportedEncodingException e1) { throw new FormUploadException("Provided encoding doesn't support", e); } } } } else { try { headers = baos.toString(Charset.defaultCharset().displayName()); } catch (UnsupportedEncodingException e) { throw new FormUploadException("Provided encoding doesn't support", e); } } return headers; } /** *

Reads body-data from the current * encapsulation and writes its contents into the * output Stream. *

*

Arbitrary large amounts of data can be processed by this * method using a constant size buffer. (see {@link * #MultipartStream(InputStream, byte[], int) constructor}). *

* @param output The Stream to write data into. May * be null, in which case this method is equivalent * to {@link #discardBodyData()}. * @return the amount of data written. */ public int readBodyData(OutputStream output) { return (int) StreamUtil.copy(newInputStream(), output, false); // N.B. Streams.copy closes the input stream } /** * Creates a new {@link ItemInputStream}. * * @return A new instance of {@link ItemInputStream}. */ ItemInputStream newInputStream() { return new ItemInputStream(); } /** *

Reads body-data from the current * encapsulation and discards it. *

*

Use this method to skip encapsulations you don't need or don't * understand.

* * @return The amount of data discarded. */ public int discardBodyData() { return readBodyData(null); } /** * Finds the beginning of the first encapsulation. * * @return true if an encapsulation was found in * the stream. */ public boolean skipPreamble() { // First delimiter may be not preceeded with a CRLF. System.arraycopy(boundary, 2, boundary, 0, boundary.length - 2); boundaryLength = boundary.length - 2; try { // Discard all data up to the delimiter. discardBodyData(); // Read boundary - if succeeded, the stream contains an // encapsulation. return readBoundary(); } catch (MalformedStreamException e) { return false; } finally { // Restore delimiter. System.arraycopy(boundary, 0, boundary, 2, boundary.length - 2); boundaryLength = boundary.length; boundary[0] = CR; boundary[1] = LF; } } /** * Compares count first bytes in the arrays * a and b. * * @param a The first array to compare. * @param b The second array to compare. * @param count How many bytes should be compared. * @return true if count first bytes in arrays * a and b are equal. */ public static boolean arrayequals(byte[] a, byte[] b, int count) { for (int i = 0; i < count; i++) { if (a[i] != b[i]) { return false; } } return true; } /** * Searches for a byte of specified value in the buffer, * starting at the specified position. * * @param value The value to find. * @param pos The starting position for searching. * @return The position of byte found, counting from beginning of the * buffer, or -1 if not found. */ protected int findByte(byte value, int pos) { for (int i = pos; i < tail; i++) { if (buffer[i] == value) { return i; } } return -1; } /** * Searches for the boundary in the buffer * region delimited by head and tail. * * @return The position of the boundary found, counting from the * beginning of the buffer, or -1 if * not found. */ protected int findSeparator() { int first; int match = 0; int maxpos = tail - boundaryLength; for (first = head; first <= maxpos && match != boundaryLength; first++) { first = findByte(boundary[0], first); if (first == -1 || first > maxpos) { return -1; } for (match = 1; match < boundaryLength; match++) { if (buffer[first + match] != boundary[match]) { break; } } } if (match == boundaryLength) { return first - 1; } return -1; } /** * Thrown to indicate that the input stream fails to follow the * required syntax. */ public static class MalformedStreamException extends RuntimeException { /** * The UID to use when serializing this instance. */ private static final long serialVersionUID = 6466926458059796677L; /** * Constructs a MalformedStreamException with no * detail message. */ public MalformedStreamException() { super(); } /** * Constructs an MalformedStreamException with * the specified detail message. * * @param message The detail message. */ public MalformedStreamException(String message) { super(message); } } /** * Thrown upon attempt of setting an invalid boundary token. */ public static class IllegalBoundaryException extends RuntimeException { /** * The UID to use when serializing this instance. */ private static final long serialVersionUID = -161533165102632918L; /** * Constructs an IllegalBoundaryException with * the specified detail message. * * @param message The detail message. */ public IllegalBoundaryException(String message) { super(message); } } /** * An {@link InputStream} for reading an items contents. */ public class ItemInputStream extends InputStream implements Closeable { /** * The number of bytes, which have been read so far. */ private long total; /** * The number of bytes, which must be hold, because * they might be a part of the boundary. */ private int pad; /** * The current offset in the buffer. */ private int pos; /** * Whether the stream is already closed. */ private boolean closed; /** * Creates a new instance. */ ItemInputStream() { findSeparator(); } /** * Called for finding the separator. */ private void findSeparator() { pos = MultipartStream.this.findSeparator(); if (pos == -1) { if (tail - head > keepRegion) { pad = keepRegion; } else { pad = tail - head; } } } /** * Returns the number of bytes, which have been read * by the stream. * * @return Number of bytes, which have been read so far. */ public long getBytesRead() { return total; } /** * Returns the number of bytes, which are currently * available, without blocking. * * @return Number of bytes in the buffer. */ @Override public int available() { if (pos == -1) { return tail - head - pad; } return pos - head; } /** * Offset when converting negative bytes to integers. */ private static final int BYTE_POSITIVE_OFFSET = 256; /** * Returns the next byte in the stream. * * @return The next byte in the stream, as a non-negative * integer, or -1 for EOF. * @throws IOException An I/O error occurred. */ @Override public int read() throws IOException { if (closed) { throw new FormItem.ItemSkippedException(); } if (available() == 0 && makeAvailable() == 0) { return -1; } ++total; int b = buffer[head++]; if (b >= 0) { return b; } return b + BYTE_POSITIVE_OFFSET; } /** * Reads bytes into the given buffer. * * @param b The destination buffer, where to write to. * @param off Offset of the first byte in the buffer. * @param len Maximum number of bytes to read. * @return Number of bytes, which have been actually read, * or -1 for EOF. * @throws IOException An I/O error occurred. */ @Override public int read(byte[] b, int off, int len) throws IOException { if (closed) { throw new FormItem.ItemSkippedException(); } if (len == 0) { return 0; } int res = available(); if (res == 0) { res = makeAvailable(); if (res == 0) { return -1; } } res = Math.min(res, len); System.arraycopy(buffer, head, b, off, res); head += res; total += res; return res; } /** * Closes the input stream. * * @throws IOException An I/O error occurred. */ @Override public void close() throws IOException { close(false); } /** * Closes the input stream. * * @param pCloseUnderlying Whether to close the underlying stream * (hard close) * @throws IOException An I/O error occurred. */ public void close(boolean pCloseUnderlying) throws IOException { if (closed) { return; } if (pCloseUnderlying) { closed = true; input.close(); } else { for (;;) { int av = available(); if (av == 0) { av = makeAvailable(); if (av == 0) { break; } } long skip = skip(av); if (skip != av) { if (log.isDebugEnabled()) { log.debug(skip + " bytes been skipped."); } } } } closed = true; } /** * Skips the given number of bytes. * * @param bytes Number of bytes to skip. * @return The number of bytes, which have actually been * skipped. */ @Override public long skip(long bytes) { if (closed) { throw new FormItem.ItemSkippedException(); } int av = available(); if (av == 0) { av = makeAvailable(); if (av == 0) { return 0; } } long res = Math.min(av, bytes); head += res; return res; } /** * Attempts to read more data. * * @return Number of available bytes */ private int makeAvailable() { if (pos != -1) { return 0; } // Move the data to the beginning of the buffer. total += tail - head - pad; System.arraycopy(buffer, tail - pad, buffer, 0, pad); // Refill buffer with new data. head = 0; tail = pad; for (;;) { int bytesRead = 0; try { bytesRead = input.read(buffer, tail, bufSize - tail); } catch (IOException e) { throw new RuntimeException("Error while reading multipart stream"); } if (bytesRead == -1) { // The last pad amount is left in the buffer. // Boundary can't be in there so signal an error // condition. final String msg = "Stream ended unexpectedly"; throw new MalformedStreamException(msg); } tail += bytesRead; findSeparator(); int av = available(); if (av > 0 || pos != -1) { return av; } } } /** * Returns, whether the stream is closed. * * @return True, if the stream is closed, otherwise false. */ public boolean isClosed() { return closed; } } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/formparam/ParameterParser.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.formparam; import org.wso2.msf4j.formparam.util.mime.MimeUtility; import java.io.UnsupportedEncodingException; import java.util.Arrays; import java.util.HashMap; import java.util.Locale; import java.util.Map; /** * A simple parser intended to parse sequences of name/value pairs. *

* Parameter values are expected to be enclosed in quotes if they * contain unsafe characters, such as '=' characters or separators. * Parameter values are optional and can be omitted. * *

* param1 = value; param2 = "anything goes; really"; param3 *

* */ public class ParameterParser { /** * String to be parsed. */ private char[] chars = null; /** * Current position in the string. */ private int pos = 0; /** * Maximum position in the string. */ private int len = 0; /** * Start of a token. */ private int i1 = 0; /** * End of a token. */ private int i2 = 0; /** * Whether names stored in the map should be converted to lower case. */ private boolean lowerCaseNames = false; /** * Default ParameterParser constructor. */ public ParameterParser() { super(); } /** * Are there any characters left to parse? * * @return {@code true} if there are unparsed characters, * {@code false} otherwise. */ private boolean hasChar() { return this.pos < this.len; } /** * A helper method to process the parsed token. This method removes * leading and trailing blanks as well as enclosing quotation marks, * when necessary. * * @param quoted {@code true} if quotation marks are expected, * {@code false} otherwise. * @return the token */ private String getToken(boolean quoted) { // Trim leading white spaces while ((i1 < i2) && (Character.isWhitespace(chars[i1]))) { i1++; } // Trim trailing white spaces while ((i2 > i1) && (Character.isWhitespace(chars[i2 - 1]))) { i2--; } // Strip away quotation marks if necessary if (quoted && ((i2 - i1) >= 2) && (chars[i1] == '"') && (chars[i2 - 1] == '"')) { i1++; i2--; } String result = null; if (i2 > i1) { result = new String(chars, i1, i2 - i1); } return result; } /** * Tests if the given character is present in the array of characters. * * @param ch the character to test for presense in the array of characters * @param charray the array of characters to test against * @return {@code true} if the character is present in the array of * characters, {@code false} otherwise. */ private boolean isOneOf(char ch, final char[] charray) { boolean result = false; for (char element : charray) { if (ch == element) { result = true; break; } } return result; } /** * Parses out a token until any of the given terminators * is encountered. * * @param terminators the array of terminating characters. Any of these * characters when encountered signify the end of the token * @return the token */ private String parseToken(final char[] terminators) { char ch; i1 = pos; i2 = pos; while (hasChar()) { ch = chars[pos]; if (isOneOf(ch, terminators)) { break; } i2++; pos++; } return getToken(false); } /** * Parses out a token until any of the given terminators * is encountered outside the quotation marks. * * @param terminators the array of terminating characters. Any of these * characters when encountered outside the quotation marks signify the end * of the token * @return the token */ private String parseQuotedToken(final char[] terminators) { char ch; i1 = pos; i2 = pos; boolean quoted = false; boolean charEscaped = false; while (hasChar()) { ch = chars[pos]; if (!quoted && isOneOf(ch, terminators)) { break; } if (!charEscaped && ch == '"') { quoted = !quoted; } charEscaped = (!charEscaped && ch == '\\'); i2++; pos++; } return getToken(true); } /** * Returns {@code true} if parameter names are to be converted to lower * case when name/value pairs are parsed. * * @return {@code true} if parameter names are to be * converted to lower case when name/value pairs are parsed. * Otherwise returns {@code false} */ public boolean isLowerCaseNames() { return this.lowerCaseNames; } /** * Sets the flag if parameter names are to be converted to lower case when * name/value pairs are parsed. * * @param b {@code true} if parameter names are to be * converted to lower case when name/value pairs are parsed. * {@code false} otherwise. */ public void setLowerCaseNames(boolean b) { this.lowerCaseNames = b; } /** * Extracts a map of name/value pairs from the given string. Names are * expected to be unique. Multiple separators may be specified and * the earliest found in the input string is used. * * @param str the string that contains a sequence of name/value pairs * @param separators the name/value pairs separators * @return a map of name/value pairs */ public Map parse(final String str, char[] separators) { if (separators == null || separators.length == 0) { return new HashMap<>(); } char separator = separators[0]; if (str != null) { int idx = str.length(); for (char separator2 : separators) { int tmp = str.indexOf(separator2); if (tmp != -1 && tmp < idx) { idx = tmp; separator = separator2; } } } return parse(str, separator); } /** * Extracts a map of name/value pairs from the given string. Names are * expected to be unique. * * @param str the string that contains a sequence of name/value pairs * @param separator the name/value pairs separator * @return a map of name/value pairs */ public Map parse(final String str, char separator) { if (str == null) { return new HashMap<>(); } return parse(str.toCharArray(), separator); } /** * Extracts a map of name/value pairs from the given array of * characters. Names are expected to be unique. * * @param charArray the array of characters that contains a sequence of * name/value pairs * @param separator the name/value pairs separator * @return a map of name/value pairs */ public Map parse(final char[] charArray, char separator) { if (charArray == null) { return new HashMap<>(); } return parse(charArray, 0, charArray.length, separator); } /** * Extracts a map of name/value pairs from the given array of * characters. Names are expected to be unique. * * @param charArray - the array of characters that contains a sequence of name/value pairs * @param offset - the initial offset. * @param length - the length. * @param separator the name/value pairs separator * @return a map of name/value pairs */ public Map parse(final char[] charArray, int offset, int length, char separator) { if (charArray == null) { return new HashMap<>(); } HashMap params = new HashMap<>(); this.chars = Arrays.copyOf(charArray, charArray.length); this.pos = offset; this.len = length; String paramName; String paramValue; while (hasChar()) { paramName = parseToken(new char[] { '=', separator }); paramValue = null; if (hasChar() && (charArray[pos] == '=')) { pos++; // skip '=' paramValue = parseQuotedToken(new char[] { separator }); if (paramValue != null) { try { paramValue = MimeUtility.decodeText(paramValue); } catch (UnsupportedEncodingException e) { // let's keep the original value in this case } } } if (hasChar() && (charArray[pos] == separator)) { pos++; // skip separator } if ((paramName != null) && (paramName.length() > 0)) { if (this.lowerCaseNames) { paramName = paramName.toLowerCase(Locale.ENGLISH); } params.put(paramName, paramValue); } } return params; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/formparam/RequestContext.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.formparam; import org.wso2.msf4j.Request; import java.io.InputStream; import java.nio.charset.Charset; /** * Request information needed for file uploads. */ public class RequestContext { private Request request; public RequestContext(Request request) { this.request = request; } /** * Get request's character encoding. * * @return character encoding of the request */ public String getCharacterEncoding() { return Charset.defaultCharset().name(); } /** * Get the request's content type. * * @return String request's content type */ public String getContentType() { return this.request.getHeader("Content-Type"); } /** * Get the request's inputstream. * * @return InputStream request's inputstream */ public InputStream getInputStream() { return request.getMessageContentStream(); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/formparam/exception/FormUploadException.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.formparam.exception; import java.io.PrintStream; import java.io.PrintWriter; /** * Exception for errors encountered while processing the request. * */ public class FormUploadException extends RuntimeException { /** * Serial version UID, being used, if the exception * is serialized. */ private static final long serialVersionUID = 8881893724388807504L; /** * The exceptions cause. We overwrite the cause of * the super class, which isn't available in Java 1.3. */ private final Throwable cause; /** * Constructs a new FileUploadException without message. */ public FormUploadException() { this(null, null); } /** * Constructs a new FileUploadException with specified detail * message. * * @param msg the error message. */ public FormUploadException(final String msg) { this(msg, null); } /** * Creates a new FileUploadException with the given * detail message and cause. * * @param msg The exceptions detail message. * @param cause The exceptions cause. */ public FormUploadException(String msg, Throwable cause) { super(msg); this.cause = cause; } /** * Prints this throwable and its backtrace to the specified print stream. * * @param stream PrintStream to use for output */ @Override public void printStackTrace(PrintStream stream) { super.printStackTrace(stream); if (cause != null) { stream.println("Caused by:"); cause.printStackTrace(stream); } } /** * Prints this throwable and its backtrace to the specified * print writer. * * @param writer PrintWriter to use for output */ @Override public void printStackTrace(PrintWriter writer) { super.printStackTrace(writer); if (cause != null) { writer.println("Caused by:"); cause.printStackTrace(writer); } } /** * {@inheritDoc} */ @Override public Throwable getCause() { return cause; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/formparam/exception/InvalidContentTypeException.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.formparam.exception; /** * Thrown to indicate that the request is not a multipart request. */ public class InvalidContentTypeException extends FormUploadException { /** * The exceptions UID, for serializing an instance. */ private static final long serialVersionUID = -9073026332015646668L; /** * Constructs an InvalidContentTypeException with * the specified detail message. * * @param message The detail message. */ public InvalidContentTypeException(String message) { super(message); } /** * Constructs an InvalidContentTypeException with * the specified detail message and cause. * * @param msg The detail message. * @param cause the original cause */ public InvalidContentTypeException(String msg, Throwable cause) { super(msg, cause); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/formparam/exception/InvalidFileNameException.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.formparam.exception; /** * This exception is thrown in case of an invalid file name. * A file name is invalid, if it contains a NUL character. * Attackers might use this to circumvent security checks: * For example, a malicious user might upload a file with the name * "foo.exe\0.png". This file name might pass security checks (i.e. * checks for the extension ".png"), while, depending on the underlying * C library, it might create a file named "foo.exe", as the NUL * character is the string terminator in C. * */ public class InvalidFileNameException extends RuntimeException { /** * Serial version UID, being used, if the exception * is serialized. */ private static final long serialVersionUID = 7922042602454350470L; /** * The file name causing the exception. */ private final String name; /** * Creates a new instance. * * @param pName The file name causing the exception. * @param pMessage A human readable error message. */ public InvalidFileNameException(String pName, String pMessage) { super(pMessage); name = pName; } /** * Returns the invalid file name. * * @return the invalid file name. */ public String getName() { return name; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/formparam/util/Closeable.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.formparam.util; import java.io.IOException; /** * Interface of an object, which may be closed. * */ public interface Closeable { /** * Closes the object. * * @throws IOException An I/O error occurred. */ void close() throws IOException; /** * Returns, whether the object is already closed. * * @return True, if the object is closed, otherwise false. * @throws IOException An I/O error occurred. */ boolean isClosed() throws IOException; } ================================================ FILE: core/src/main/java/org/wso2/msf4j/formparam/util/FormItemHeader.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.formparam.util; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; /** *

This class provides support for accessing the headers for a file or form * item that was received within a multipart/form-data POST * request.

*/ public class FormItemHeader implements Serializable { /** * Serial version UID, being used, if serialized. */ private static final long serialVersionUID = -4455695752627032559L; /** * Map of String keys to a List of * String instances. */ private final Map> headerNameToValueListMap = new LinkedHashMap<>(); /** * {@inheritDoc} */ public String getHeader(String name) { String nameLower = name.toLowerCase(Locale.ENGLISH); List headerValueList = headerNameToValueListMap.get(nameLower); if (null == headerValueList) { return null; } return headerValueList.get(0); } /** * {@inheritDoc} */ public Iterator getHeaderNames() { return headerNameToValueListMap.keySet().iterator(); } /** * {@inheritDoc} */ public Iterator getHeaders(String name) { String nameLower = name.toLowerCase(Locale.ENGLISH); List headerValueList = headerNameToValueListMap.get(nameLower); if (null == headerValueList) { headerValueList = Collections.emptyList(); } return headerValueList.iterator(); } /** * Method to add header values to this instance. * * @param name name of this header * @param value value of this header */ public synchronized void addHeader(String name, String value) { String nameLower = name.toLowerCase(Locale.ENGLISH); List headerValueList = headerNameToValueListMap.get(nameLower); if (null == headerValueList) { headerValueList = new ArrayList<>(); headerNameToValueListMap.put(nameLower, headerValueList); } headerValueList.add(value); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/formparam/util/StreamUtil.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.formparam.util; import org.apache.commons.io.IOUtils; import org.wso2.msf4j.formparam.exception.FormUploadException; import org.wso2.msf4j.formparam.exception.InvalidFileNameException; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.Charset; /** * Utility class for working with streams. */ public final class StreamUtil { /** * Private constructor, to prevent instantiation. * This class has only static methods. */ private StreamUtil() { // Does nothing } /** * Default buffer size for use in * {@link #copy(InputStream, OutputStream, boolean)}. */ private static final int DEFAULT_BUFFER_SIZE = 8192; /** * Copies the contents of the given {@link InputStream} * to the given {@link OutputStream}. Shortcut for *
     *   copy(pInputStream, pOutputStream, new byte[8192]);
     * 
* * @param inputStream The input stream, which is being read. * It is guaranteed, that {@link InputStream#close()} is called * on the stream. * @param outputStream The output stream, to which data should * be written. May be null, in which case the input streams * contents are simply discarded. * @param closeOutputStream True guarantees, that {@link OutputStream#close()} * is called on the stream. False indicates, that only * {@link OutputStream#flush()} should be called finally. * @return Number of bytes, which have been copied. */ public static long copy(InputStream inputStream, OutputStream outputStream, boolean closeOutputStream) { return copy(inputStream, outputStream, closeOutputStream, new byte[DEFAULT_BUFFER_SIZE]); } /** * Copies the contents of the given {@link InputStream} * to the given {@link OutputStream}. * * @param inputStream The input stream, which is being read. * It is guaranteed, that {@link InputStream#close()} is called * on the stream. * @param outputStream The output stream, to which data should * be written. May be null, in which case the input streams * contents are simply discarded. * @param closeOutputStream True guarantees, that {@link OutputStream#close()} * is called on the stream. False indicates, that only * {@link OutputStream#flush()} should be called finally. * @param buffer Temporary buffer, which is to be used for * copying data. * @return Number of bytes, which have been copied. */ public static long copy(InputStream inputStream, OutputStream outputStream, boolean closeOutputStream, byte[] buffer) { OutputStream out = outputStream; InputStream in = inputStream; try { long total = 0; for (;;) { int res = in.read(buffer); if (res == -1) { break; } if (res > 0) { total += res; if (out != null) { out.write(buffer, 0, res); } } } if (out != null) { if (!closeOutputStream) { out.flush(); } } return total; } catch (IOException e) { throw new FormUploadException("Error while copying streams", e); } finally { IOUtils.closeQuietly(in); if (closeOutputStream) { IOUtils.closeQuietly(out); } } } /** * This convenience method allows to read a * {@link org.wso2.msf4j.formparam.FormItem}'s * content into a string. The platform's default character encoding * is used for converting bytes into characters. * * @param inputStream The input stream to read. * @return The streams contents, as a string. * @throws IOException An I/O error occurred. * @see #asString(InputStream, String) */ public static String asString(InputStream inputStream) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); copy(inputStream, baos, true); return baos.toString(Charset.defaultCharset().name()); } /** * This convenience method allows to read a * {@link org.wso2.msf4j.formparam.FormItem}'s * content into a string, using the given character encoding. * * @param inputStream The input stream to read. * @param encoding The character encoding, typically "UTF-8". * @return The streams contents, as a string. * @throws IOException An I/O error occurred. * @see #asString(InputStream) */ public static String asString(InputStream inputStream, String encoding) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); copy(inputStream, baos, true); return baos.toString(encoding); } /** * Checks, whether the given file name is valid in the sense, * that it doesn't contain any NUL characters. If the file name * is valid, it will be returned without any modifications. Otherwise, * an {@link InvalidFileNameException} is raised. * * @param fileName The file name to check * @return Unmodified file name, if valid. */ public static String checkFileName(String fileName) { if (fileName != null && fileName.indexOf('\u0000') != -1) { // pFileName.replace("\u0000", "\\0") final StringBuilder sb = new StringBuilder(); for (int i = 0; i < fileName.length(); i++) { char c = fileName.charAt(i); switch (c) { case 0: sb.append("\\0"); break; default: sb.append(c); break; } } throw new InvalidFileNameException(fileName, "Invalid file name: " + sb); } return fileName; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/formparam/util/mime/Base64Decoder.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.formparam.util.mime; import java.io.IOException; import java.io.OutputStream; /** * Simple Bas64 decoder. */ final class Base64Decoder { /** * Decoding table value for invalid bytes. */ private static final int INVALID_BYTE = -1; // must be outside range 0-63 /** * Decoding table value for padding bytes, so can detect PAD afer conversion. */ private static final int PAD_BYTE = -2; // must be outside range 0-63 /** * Mask to treat byte as unsigned integer. */ private static final int MASK_BYTE_UNSIGNED = 0xFF; /** * Number of bytes per encoded chunk - 4 6bit bytes produce 3 8bit bytes on output. */ private static final int INPUT_BYTES_PER_CHUNK = 4; /** * Set up the encoding table. */ private static final byte[] ENCODING_TABLE = { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) '+', (byte) '/' }; /** * The padding byte. */ private static final byte PADDING = (byte) '='; /** * Set up the decoding table; this is indexed by a byte converted to an unsigned int, * so must be at least as large as the number of different byte values, * positive and negative and zero. */ private static final byte[] DECODING_TABLE = new byte[Byte.MAX_VALUE - Byte.MIN_VALUE + 1]; static { // Initialise as all invalid characters for (int i = 0; i < DECODING_TABLE.length; i++) { DECODING_TABLE[i] = INVALID_BYTE; } // set up valid characters for (int i = 0; i < ENCODING_TABLE.length; i++) { DECODING_TABLE[ENCODING_TABLE[i]] = (byte) i; } // Allow pad byte to be easily detected after conversion DECODING_TABLE[PADDING] = PAD_BYTE; } /** * Hidden constructor, this class must not be instantiated. */ private Base64Decoder() { // do nothing } /** * Decode the base 64 encoded byte data writing it to the given output stream, * whitespace characters will be ignored. * * @param data the buffer containing the Base64-encoded data * @param out the output stream to hold the decoded bytes * @return the number of bytes produced. * @throws IOException thrown when the padding is incorrect or the input is truncated. */ public static int decode(byte[] data, OutputStream out) throws IOException { int outLen = 0; byte[] cache = new byte[INPUT_BYTES_PER_CHUNK]; int cachedBytes = 0; for (byte b : data) { final byte d = DECODING_TABLE[MASK_BYTE_UNSIGNED & b]; if (d == INVALID_BYTE) { continue; // Ignore invalid bytes } cache[cachedBytes++] = d; if (cachedBytes == INPUT_BYTES_PER_CHUNK) { // CHECKSTYLE IGNORE MagicNumber FOR NEXT 4 LINES final byte b1 = cache[0]; final byte b2 = cache[1]; final byte b3 = cache[2]; final byte b4 = cache[3]; if (b1 == PAD_BYTE || b2 == PAD_BYTE) { throw new IOException("Invalid Base64 input: incorrect padding, first two bytes cannot be padding"); } // Convert 4 6-bit bytes to 3 8-bit bytes // CHECKSTYLE IGNORE MagicNumber FOR NEXT 1 LINE out.write((b1 << 2) | (b2 >> 4)); // 6 bits of b1 plus 2 bits of b2 outLen++; if (b3 != PAD_BYTE) { // CHECKSTYLE IGNORE MagicNumber FOR NEXT 1 LINE out.write((b2 << 4) | (b3 >> 2)); // 4 bits of b2 plus 4 bits of b3 outLen++; if (b4 != PAD_BYTE) { // CHECKSTYLE IGNORE MagicNumber FOR NEXT 1 LINE out.write((b3 << 6) | b4); // 2 bits of b3 plus 6 bits of b4 outLen++; } } else if (b4 != PAD_BYTE) { // if byte 3 is pad, byte 4 must be pad too throw new // line wrap to avoid 120 char limit IOException( "Invalid Base64 input: incorrect padding, 4th byte must be padding if 3rd byte is"); } cachedBytes = 0; } } // Check for anything left over if (cachedBytes != 0) { throw new IOException("Invalid Base64 input: truncated"); } return outLen; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/formparam/util/mime/MimeUtility.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.formparam.util.mime; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.Locale; import java.util.Map; /** * Utility class to decode MIME texts. * */ public final class MimeUtility { /** * The {@code US-ASCII} charset identifier constant. */ private static final String US_ASCII_CHARSET = "US-ASCII"; /** * The marker to indicate text is encoded with BASE64 algorithm. */ private static final String BASE64_ENCODING_MARKER = "B"; /** * The marker to indicate text is encoded with QuotedPrintable algorithm. */ private static final String QUOTEDPRINTABLE_ENCODING_MARKER = "Q"; /** * If the text contains any encoded tokens, those tokens will be marked with "=?". */ private static final String ENCODED_TOKEN_MARKER = "=?"; /** * If the text contains any encoded tokens, those tokens will terminate with "=?". */ private static final String ENCODED_TOKEN_FINISHER = "?="; /** * The linear whitespace chars sequence. */ private static final String LINEAR_WHITESPACE = " \t\r\n"; /** * Mappings between MIME and Java charset. */ private static final Map MIME2JAVA = new HashMap<>(); static { MIME2JAVA.put("iso-2022-cn", "ISO2022CN"); MIME2JAVA.put("iso-2022-kr", "ISO2022KR"); MIME2JAVA.put("utf-8", "UTF8"); MIME2JAVA.put("utf8", "UTF8"); MIME2JAVA.put("ja_jp.iso2022-7", "ISO2022JP"); MIME2JAVA.put("ja_jp.eucjp", "EUCJIS"); MIME2JAVA.put("euc-kr", "KSC5601"); MIME2JAVA.put("euckr", "KSC5601"); MIME2JAVA.put("us-ascii", "ISO-8859-1"); MIME2JAVA.put("x-us-ascii", "ISO-8859-1"); } /** * Hidden constructor, this class must not be instantiated. */ private MimeUtility() { // do nothing } /** * Decode a string of text obtained from a mail header into * its proper form. The text generally will consist of a * string of tokens, some of which may be encoded using * base64 encoding. * * @param text The text to decode. * * @return The decoded text string. * @throws UnsupportedEncodingException if the detected encoding in the input text is not supported. */ public static String decodeText(String text) throws UnsupportedEncodingException { // if the text contains any encoded tokens, those tokens will be marked with "=?". If the // source string doesn't contain that sequent, no decoding is required. if (!text.contains(ENCODED_TOKEN_MARKER)) { return text; } int offset = 0; int endOffset = text.length(); int startWhiteSpace = -1; int endWhiteSpace = -1; StringBuilder decodedText = new StringBuilder(text.length()); boolean previousTokenEncoded = false; while (offset < endOffset) { char ch = text.charAt(offset); // is this a whitespace character? if (LINEAR_WHITESPACE.indexOf(ch) != -1) { // whitespace found startWhiteSpace = offset; while (offset < endOffset) { // step over the white space characters. ch = text.charAt(offset); if (LINEAR_WHITESPACE.indexOf(ch) != -1) { // whitespace found offset++; } else { // record the location of the first non lwsp and drop down to process the // token characters. endWhiteSpace = offset; break; } } } else { // we have a word token. We need to scan over the word and then try to parse it. int wordStart = offset; while (offset < endOffset) { // step over the non white space characters. ch = text.charAt(offset); if (LINEAR_WHITESPACE.indexOf(ch) == -1) { // not white space offset++; } else { break; } //NB: Trailing whitespace on these header strings will just be discarded. } // pull out the word token. String word = text.substring(wordStart, offset); // is the token encoded? decode the word if (word.startsWith(ENCODED_TOKEN_MARKER)) { try { // if this gives a parsing failure, treat it like a non-encoded word. String decodedWord = decodeWord(word); // are any whitespace characters significant? Append 'em if we've got 'em. if (!previousTokenEncoded && startWhiteSpace != -1) { decodedText.append(text.substring(startWhiteSpace, endWhiteSpace)); startWhiteSpace = -1; } // this is definitely a decoded token. previousTokenEncoded = true; // and add this to the text. decodedText.append(decodedWord); // we continue parsing from here...we allow parsing errors to fall through // and get handled as normal text. continue; } catch (ParseException e) { // just ignore it, skip to next word } } // this is a normal token, so it doesn't matter what the previous token was. Add the white space // if we have it. if (startWhiteSpace != -1) { decodedText.append(text.substring(startWhiteSpace, endWhiteSpace)); startWhiteSpace = -1; } // this is not a decoded token. previousTokenEncoded = false; decodedText.append(word); } } return decodedText.toString(); } /** * Parse a string using the RFC 2047 rules for an "encoded-word" * type. This encoding has the syntax: * * encoded-word = "=?" charset "?" encoding "?" encoded-text "?=" * * @param word The possibly encoded word value. * * @return The decoded word. * @throws ParseException * @throws UnsupportedEncodingException */ private static String decodeWord(String word) throws ParseException, UnsupportedEncodingException { // encoded words start with the characters "=?". If this not an encoded word, we throw a // ParseException for the caller. if (!word.startsWith(ENCODED_TOKEN_MARKER)) { throw new ParseException("Invalid RFC 2047 encoded-word: " + word); } int charsetPos = word.indexOf('?', 2); if (charsetPos == -1) { throw new ParseException("Missing charset in RFC 2047 encoded-word: " + word); } // pull out the character set information (this is the MIME name at this point). String charset = word.substring(2, charsetPos).toLowerCase(Locale.getDefault()); // now pull out the encoding token the same way. int encodingPos = word.indexOf('?', charsetPos + 1); if (encodingPos == -1) { throw new ParseException("Missing encoding in RFC 2047 encoded-word: " + word); } String encoding = word.substring(charsetPos + 1, encodingPos); // and finally the encoded text. int encodedTextPos = word.indexOf(ENCODED_TOKEN_FINISHER, encodingPos + 1); if (encodedTextPos == -1) { throw new ParseException("Missing encoded text in RFC 2047 encoded-word: " + word); } String encodedText = word.substring(encodingPos + 1, encodedTextPos); // seems a bit silly to encode a null string, but easy to deal with. if (encodedText.length() == 0) { return ""; } try { // the decoder writes directly to an output stream. ByteArrayOutputStream out = new ByteArrayOutputStream(encodedText.length()); byte[] encodedData = encodedText.getBytes(US_ASCII_CHARSET); // Base64 encoded? if (BASE64_ENCODING_MARKER.equals(encoding)) { Base64Decoder.decode(encodedData, out); } else if (QUOTEDPRINTABLE_ENCODING_MARKER.equals(encoding)) { // maybe quoted printable. QuotedPrintableDecoder.decode(encodedData, out); } else { throw new UnsupportedEncodingException("Unknown RFC 2047 encoding: " + encoding); } // get the decoded byte data and convert into a string. byte[] decodedData = out.toByteArray(); return new String(decodedData, javaCharset(charset)); } catch (IOException e) { throw new UnsupportedEncodingException("Invalid RFC 2047 encoding"); } } /** * Translate a MIME standard character set name into the Java * equivalent. * * @param charset The MIME standard name. * * @return The Java equivalent for this name. */ private static String javaCharset(String charset) { // nothing in, nothing out. if (charset == null) { return null; } String mappedCharset = MIME2JAVA.get(charset.toLowerCase(Locale.ENGLISH)); // if there is no mapping, then the original name is used. Many of the MIME character set // names map directly back into Java. The reverse isn't necessarily true. if (mappedCharset == null) { return charset; } return mappedCharset; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/formparam/util/mime/ParseException.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.formparam.util.mime; /** * Exception that will be thrown if any issues occur * when parsing the multipart/form-data request. * */ final class ParseException extends Exception { /** * The UID to use when serializing this instance. */ private static final long serialVersionUID = 5355281266579392077L; /** * Constructs a new exception with the specified detail message. * * @param message the detail message. */ public ParseException(String message) { super(message); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/formparam/util/mime/QuotedPrintableDecoder.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.formparam.util.mime; import java.io.IOException; import java.io.OutputStream; /** * Simple decoder for quoted values. */ final class QuotedPrintableDecoder { /** * The shift value required to create the upper nibble * from the first of 2 byte values converted from ascii hex. */ private static final int UPPER_NIBBLE_SHIFT = Byte.SIZE / 2; /** * Hidden constructor, this class must not be instantiated. */ private QuotedPrintableDecoder() { // do nothing } /** * Decode the encoded byte data writing it to the given output stream. * * @param data The array of byte data to decode. * @param out The output stream used to return the decoded data. * * @return the number of bytes produced. * @exception IOException */ public static int decode(byte[] data, OutputStream out) throws IOException { int off = 0; int length = data.length; int endOffset = off + length; int bytesWritten = 0; while (off < endOffset) { byte ch = data[off++]; // space characters were translated to '_' on encode, so we need to translate them back. if (ch == '_') { out.write(' '); } else if (ch == '=') { // we found an encoded character. Reduce the 3 char sequence to one. // but first, make sure we have two characters to work with. if (off + 1 >= endOffset) { throw new IOException("Invalid quoted printable encoding; truncated escape sequence"); } byte b1 = data[off++]; byte b2 = data[off++]; // we've found an encoded carriage return. The next char needs to be a newline if (b1 == '\r') { if (b2 != '\n') { throw new IOException("Invalid quoted printable encoding; CR must be followed by LF"); } // this was a soft linebreak inserted by the encoding. We just toss this away // on decode. } else { // this is a hex pair we need to convert back to a single byte. int c1 = hexToBinary(b1); int c2 = hexToBinary(b2); out.write((c1 << UPPER_NIBBLE_SHIFT) | c2); // 3 bytes in, one byte out bytesWritten++; } } else { // simple character, just write it out. out.write(ch); bytesWritten++; } } return bytesWritten; } /** * Convert a hex digit to the binary value it represents. * * @param b the ascii hex byte to convert (0-0, A-F, a-f) * @return the int value of the hex byte, 0-15 * @throws IOException if the byte is not a valid hex digit. */ private static int hexToBinary(final byte b) throws IOException { // CHECKSTYLE IGNORE MagicNumber FOR NEXT 1 LINE final int i = Character.digit((char) b, 16); if (i == -1) { throw new IOException("Invalid quoted printable encoding: not a valid hex digit: " + b); } return i; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/formparam/util/mime/package-info.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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. */ /** * MIME decoder implementation, imported and retailed from * Apache Geronimo. */ package org.wso2.msf4j.formparam.util.mime; ================================================ FILE: core/src/main/java/org/wso2/msf4j/formparam/util/package-info.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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. */ /** * This package contains various IO related utility classes * or methods, which are basically reusable and not necessarily * restricted to the scope of a file upload. */ package org.wso2.msf4j.formparam.util; ================================================ FILE: core/src/main/java/org/wso2/msf4j/interceptor/InterceptorExecutor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import org.wso2.msf4j.exception.InterceptorException; import org.wso2.msf4j.internal.MicroservicesRegistryImpl; import org.wso2.msf4j.util.ReflectionUtils; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; /** * Class for executing interceptors. */ public class InterceptorExecutor { private InterceptorExecutor() { } /** * Execute global request interceptors. * * @param microServicesRegistry current micro-service registry of {@link MicroservicesRegistryImpl} * @param request {@link Request} * @param response {@link Response} * @return is request interceptors successful */ public static boolean executeGlobalRequestInterceptors(MicroservicesRegistryImpl microServicesRegistry, Request request, Response response) { List globalRequestInterceptorList = microServicesRegistry.getGlobalRequestInterceptorList(); return executeGlobalRequestInterceptors(request, response, globalRequestInterceptorList); } /** * Execute request interceptors annotated in class. * * @param request {@link Request} * @param response {@link Response} * @param resourceClass method declaring class * @return is request interceptors successful * @throws InterceptorException {@link InterceptorException} on interception exception */ public static boolean executeClassLevelRequestInterceptors(Request request, Response response, Class resourceClass) throws InterceptorException { Collection> classRequestInterceptorClasses = resourceClass.isAnnotationPresent(org.wso2.msf4j.interceptor.annotation.RequestInterceptor.class) ? Arrays.asList(resourceClass .getAnnotation(org.wso2.msf4j.interceptor.annotation.RequestInterceptor.class).value()) : new ArrayList<>(); return executeNonGlobalRequestInterceptors(request, response, classRequestInterceptorClasses); } /** * Execute request interceptors annotated in method * * @param request {@link Request} * @param response {@link Response} * @param method method to be executed * @return is request interceptors successful * @throws InterceptorException {@link InterceptorException} on interception exception */ public static boolean executeMethodLevelRequestInterceptors(Request request, Response response, Method method) throws InterceptorException { Collection> methodRequestInterceptorClasses = method.isAnnotationPresent(org.wso2.msf4j.interceptor.annotation.RequestInterceptor.class) ? Arrays.asList(method .getAnnotation(org.wso2.msf4j.interceptor.annotation.RequestInterceptor.class).value()) : new ArrayList<>(); return executeNonGlobalRequestInterceptors(request, response, methodRequestInterceptorClasses); } /** * Execute global response interceptors. * * @param microServicesRegistry current micro-service registry of {@link MicroservicesRegistryImpl} * @param request {@link Request} * @param response {@link Response} * @return is request interceptors successful */ public static boolean executeGlobalResponseInterceptors(MicroservicesRegistryImpl microServicesRegistry, Request request, Response response) { List globalResponseInterceptorList = microServicesRegistry.getGlobalResponseInterceptorList(); return executeGlobalResponseInterceptors(request, response, globalResponseInterceptorList); } /** * Execute response interceptors annotated in class. * * @param request {@link Request} * @param response {@link Response} * @param resourceClass method declaring class * @return is request interceptors successful * @throws InterceptorException {@link InterceptorException} on interception exception */ public static boolean executeClassLevelResponseInterceptors(Request request, Response response, Class resourceClass) throws InterceptorException { List> classResponseInterceptorClasses = resourceClass.isAnnotationPresent(org.wso2.msf4j.interceptor.annotation.ResponseInterceptor.class) ? Arrays.asList(resourceClass .getAnnotation(org.wso2.msf4j.interceptor.annotation.ResponseInterceptor.class).value()) : new ArrayList<>(); return executeNonGlobalResponseInterceptors(request, response, classResponseInterceptorClasses); } /** * Execute response interceptors annotated in class for a list of classes. * * @param request {@link Request} * @param response {@link Response} * @param classes list of method declaring class * @return is request interceptors successful * @throws InterceptorException {@link InterceptorException} on interception exception */ public static boolean executeClassResponseInterceptorsForClasses(Request request, Response response, List> classes) throws InterceptorException { if (classes == null) { return true; } for (Class aClass : classes) { if (!(InterceptorExecutor.executeClassLevelResponseInterceptors(request, response, aClass))) { return false; } } return true; } /** * Execute response interceptors annotated in method * * @param request {@link Request} * @param response {@link Response} * @param method method to be executed * @return is request interceptors successful * @throws InterceptorException {@link InterceptorException} on interception exception */ public static boolean executeMethodLevelResponseInterceptors(Request request, Response response, Method method) throws InterceptorException { List> methodResponseInterceptorClasses = method.isAnnotationPresent(org.wso2.msf4j.interceptor.annotation.ResponseInterceptor.class) ? Arrays.asList(method .getAnnotation(org.wso2.msf4j.interceptor.annotation.ResponseInterceptor.class).value()) : new ArrayList<>(); return executeNonGlobalResponseInterceptors(request, response, methodResponseInterceptorClasses); } /** * Execute response interceptors annotated in method for a list of methods. * * @param request {@link Request} * @param response {@link Response} * @param methods list of methods to be executed * @return is request interceptors successful * @throws InterceptorException {@link InterceptorException} on interception exception */ public static boolean executeMethodResponseInterceptorsForMethods(Request request, Response response, List methods) throws InterceptorException { if (methods == null) { return true; } for (Method resourceMethod : methods) { if (!(InterceptorExecutor.executeMethodLevelResponseInterceptors(request, response, resourceMethod))) { return false; } } return true; } /** * Execute request interceptors. * * @param request {@link Request} * @param response {@link Response} * @param requestInterceptors request interceptor instances * @return is interception successful */ private static boolean executeGlobalRequestInterceptors(Request request, Response response, Collection requestInterceptors) { if (requestInterceptors == null) { return true; } for (RequestInterceptor interceptor : requestInterceptors) { if (!executeRequestInterceptor(interceptor, request, response)) { return false; } } return true; } /** * Execute response interceptors. * * @param request {@link Request} * @param response {@link Response} * @param responseInterceptors response interceptor instances * @return is interception successful */ private static boolean executeGlobalResponseInterceptors(Request request, Response response, Collection responseInterceptors) { if (responseInterceptors == null) { return true; } for (ResponseInterceptor interceptor : responseInterceptors) { if (!executeResponseInterceptor(interceptor, request, response)) { return false; } } return true; } /** * Execute request interceptors. * * @param request {@link Request} * @param response {@link Response} * @param classes request interceptor classes * @return is interception successful * @throws InterceptorException {@link InterceptorException} on interception exception */ private static boolean executeNonGlobalRequestInterceptors( Request request, Response response, Collection> classes) throws InterceptorException { Class[] parameterTypes = new Class[]{}; Object[] arguments = new Object[]{}; if (classes == null) { return true; } for (Class requestInterceptorClass : classes) { RequestInterceptor interceptor; try { interceptor = requestInterceptorClass.cast(ReflectionUtils .createInstanceFromClass(requestInterceptorClass, parameterTypes, arguments)); } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) { throw new InterceptorException("Error occurred when creating an instance type of the interceptor class " + requestInterceptorClass, e); } if (!executeRequestInterceptor(interceptor, request, response)) { return false; } } return true; } /** * Execute response interceptors. * * @param request {@link Request} * @param response {@link Response} * @param classes response interceptor classes * @return is interception successful * @throws InterceptorException {@link InterceptorException} on interception exception */ private static boolean executeNonGlobalResponseInterceptors( Request request, Response response, Collection> classes) throws InterceptorException { Class[] parameterTypes = new Class[]{}; Object[] arguments = new Object[]{}; if (classes == null) { return true; } for (Class responseInterceptorClass : classes) { ResponseInterceptor interceptor; try { interceptor = responseInterceptorClass.cast(ReflectionUtils .createInstanceFromClass(responseInterceptorClass, parameterTypes, arguments)); } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) { throw new InterceptorException("Error occurred when creating an instance type of the interceptor class " + responseInterceptorClass, e); } if (!executeResponseInterceptor(interceptor, request, response)) { return false; } } return true; } /** * Execute a single request interceptor. * * @param interceptor request interceptor * @param request request instance * @param response response instance * @return interceptor result */ private static boolean executeRequestInterceptor(RequestInterceptor interceptor, Request request, Response response) { try { return interceptor.interceptRequest(request, response); } catch (Exception e) { return interceptor.onRequestInterceptionError(request, response, e); } } /** * Execute a single response interceptor. * * @param interceptor response interceptor * @param request request instance * @param response response instance * @return interceptor result */ private static boolean executeResponseInterceptor(ResponseInterceptor interceptor, Request request, Response response) { try { return interceptor.interceptResponse(request, response); } catch (Exception e) { return interceptor.onResponseInterceptionError(request, response, e); } } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/interceptor/OSGiInterceptorConfig.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * OSGi interceptor registration component. */ public class OSGiInterceptorConfig { private final List globalRequestInterceptorList = new ArrayList<>(); private final List globalResponseInterceptorList = new ArrayList<>(); /** * Add global request interceptors. * Order in which the interceptors are added are the execution priority order if the interceptors * * @param globalRequestInterceptors {@link RequestInterceptor} */ protected final void addGlobalRequestInterceptors(RequestInterceptor... globalRequestInterceptors) { globalRequestInterceptorList.addAll(Arrays.asList(globalRequestInterceptors)); } /** * Add global response interceptors. * Order in which the interceptors are added are the execution priority order if the interceptors * * @param globalResponseInterceptors {@link ResponseInterceptor} */ protected final void addGlobalResponseInterceptors(ResponseInterceptor... globalResponseInterceptors) { globalResponseInterceptorList.addAll(Arrays.asList(globalResponseInterceptors)); } /** * Get global request interceptors added. * * @return {@link RequestInterceptor} */ public final RequestInterceptor[] getGlobalRequestInterceptorArray() { return globalRequestInterceptorList .toArray(new RequestInterceptor[globalRequestInterceptorList.size()]); } /** * Get global response interceptors added. * * @return {@link ResponseInterceptor} */ public final ResponseInterceptor[] getGlobalResponseInterceptorArray() { return globalResponseInterceptorList .toArray(new ResponseInterceptor[globalResponseInterceptorList.size()]); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/interceptor/RequestInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import javax.ws.rs.core.MediaType; /** * Interface that needs to be implemented to intercept request method calls *

* Priority of request interceptors will be * Global request interceptors -> Class level annotated request interceptors * -> Method level annotated interceptors -> Resource method execution * -> sub-resource method class level annotated interceptors -> sub-resource method method level annotated interceptors * -> sub-resource execution *

* Upon returning false from any interceptor will cause the above flow to break and send the response immediately *

* Upon exception in interceptors "onRequestInterceptionError" default method in the interface will be called. * Override this method for custom behaviour */ @FunctionalInterface public interface RequestInterceptor { /** * Globally, resource vise or sub-resource vise intercept requests * Please not that is you decided to return false ideally you should manually set the entity in response by using * method "setEntity". Otherwise the response will be a new Object() * * @param request MSF4J request. * @param response MSF4J response. * @return is interception successful * @throws Exception on any exception */ boolean interceptRequest(Request request, Response response) throws Exception; /** * This method will be called open request interception error (unhandled by end developer) * Override this method to manually handle exceptions when an unhandled error is thrown. * * @param request MSF4J request. * @param response MSF4J Response. * @return should interception flow proceed? */ default boolean onRequestInterceptionError(Request request, Response response, Exception e) { String message = "Exception while executing request interceptor " + this.getClass(); Logger log = LoggerFactory.getLogger(this.getClass()); log.error(message, e); response.setEntity(message) .setMediaType(MediaType.TEXT_PLAIN) .setStatus(javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()); return false; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/interceptor/ResponseInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import javax.ws.rs.core.MediaType; /** * Interface that needs to be implemented to intercept response method calls *

* Priority of response interceptors will be * sub-resource method method level annotated interceptors -> sub-resource method class level annotated interceptors * -> Method level annotated response interceptors -> Class level annotated response interceptors * -> Global response interceptors *

* Upon returning false from any interceptor will cause the above flow to break and send the response immediately *

* Upon exception in interceptors "onResponseInterceptionError" default method in the interface will be called. * Override this method for custom behaviour */ @FunctionalInterface public interface ResponseInterceptor { /** * Globally, resource vise or sub-resource vise intercept responses. * Please not that is you decided to return false ideally you should manually set the entity in response by using * method "setEntity". Otherwise the response will be a new Object() * * @param request MSF4J request. * @param response MSF4J response. * @return is interception successful * @throws Exception on any exception */ boolean interceptResponse(Request request, Response response) throws Exception; /** * This method will be called open response interception error (unhandled by end developer) * Override this method to manually handle exceptions when an unhandled error is thrown. * * @param request MSF4J request. * @param response MSF4J Response. * @return should interception flow proceed? */ default boolean onResponseInterceptionError(Request request, Response response, Exception e) { String message = "Exception while executing response interceptor " + this.getClass(); Logger log = LoggerFactory.getLogger(this.getClass()); log.error(message, e); response.setEntity(message) .setMediaType(MediaType.TEXT_PLAIN) .setStatus(javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()); return false; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/interceptor/annotation/RequestInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Annotation for intercepting a http request. */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface RequestInterceptor { /** * Request intercept class name. * * @return request intercept class names. */ Class[] value(); } ================================================ FILE: core/src/main/java/org/wso2/msf4j/interceptor/annotation/ResponseInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Annotation for intercepting a http response. */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface ResponseInterceptor { /** * Response intercept class name. * * @return response intercept class names. */ Class[] value(); } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/ClassComparator.java ================================================ /* * Copyright (c) 2005-2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ package org.wso2.msf4j.internal; import java.io.Serializable; import java.util.Comparator; /** * Compares classes to see if they are subclasses. */ public class ClassComparator implements Comparator, Serializable { private static final long serialVersionUID = 2101798650978464444L; @Override public int compare(Class class1, Class class2) { if (class1.equals(class2)) { return 0; } else if (class1.isAssignableFrom(class2)) { return 1; } else { return -1; } } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/DataHolder.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.internal; import org.osgi.framework.BundleContext; import org.wso2.carbon.config.provider.ConfigProvider; import java.util.HashMap; import java.util.Map; /** * DataHolder for MSF4J. */ public class DataHolder { private static final DataHolder instance = new DataHolder(); private BundleContext bundleContext; private Map microservicesRegistries = new HashMap<>(); private ConfigProvider configProvider; private DataHolder() { } public static DataHolder getInstance() { return instance; } public BundleContext getBundleContext() { return bundleContext; } public void setBundleContext(BundleContext bundleContext) { this.bundleContext = bundleContext; } public Map getMicroservicesRegistries() { return microservicesRegistries; } public void setConfigProvider(ConfigProvider configProvider) { this.configProvider = configProvider; } public ConfigProvider getConfigProvider() { return configProvider; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/HttpConnectorPortBindingListener.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.internal; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.transport.http.netty.contract.PortBindingEventListener; /** * An implementation of the LifeCycleEventListener. This can be used to listen to the HTTP connector life cycle events. * * @since 2.5.0 */ public class HttpConnectorPortBindingListener implements PortBindingEventListener { private static final Logger log = LoggerFactory.getLogger(HttpConnectorPortBindingListener.class); @Override public void onOpen(String serverConnectorId, boolean isHttps) { // Nothing to do when opening the connector port } @Override public void onClose(String serverConnectorId, boolean isHttps) { // Nothing to do when closing the connector port } @Override public void onError(Throwable throwable) { log.error("Error in http server connector", throwable); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/HttpHeadersImpl.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.internal; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Serializable; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.TimeZone; import javax.ws.rs.core.Cookie; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.MultivaluedMap; /** * This is JAX-RS HttpHeaders implementation for msf4j. * This also wraps netty httpHeaders. * * @since 2.5.0 */ public class HttpHeadersImpl implements HttpHeaders { private static final String DATE_FORMAT_PATTERN = "EEE, dd MMM yyyy HH:mm:ss zzz"; private static final String GMT_TIMEZONE = "GMT"; private final io.netty.handler.codec.http.HttpHeaders nettyHttpHeaders; private static final Logger log = LoggerFactory.getLogger(HttpHeadersImpl.class); public HttpHeadersImpl(io.netty.handler.codec.http.HttpHeaders httpHeaders) { nettyHttpHeaders = httpHeaders; } @Override public List getRequestHeader(String name) { return nettyHttpHeaders.getAll(name); } @Override public String getHeaderString(String name) { List headerValues = nettyHttpHeaders.getAll(name); if (headerValues == null) { return null; } StringBuilder sb = new StringBuilder(); for (int i = 0; i < headerValues.size(); i++) { String value = headerValues.get(i); if (value == null || value.isEmpty()) { continue; } sb.append(value); if (i + 1 < headerValues.size()) { sb.append(","); } } return sb.toString(); } @Override public MultivaluedMap getRequestHeaders() { MultivaluedHashMap newHeaders = new MultivaluedHashMap<>(); for (Map.Entry headerEntry : nettyHttpHeaders.entries()) { if (headerEntry != null) { newHeaders.add(headerEntry.getKey(), headerEntry.getValue()); } } return newHeaders; } @Override public List getAcceptableMediaTypes() { List values = nettyHttpHeaders.getAll(HttpHeaders.ACCEPT); if (values == null || values.isEmpty() || values.get(0) == null) { return Collections.singletonList(MediaType.WILDCARD_TYPE); } List mediaTypes = new LinkedList<>(); for (String value : values) { mediaTypes.add(MediaType.valueOf(value)); } return mediaTypes; } @Override public List getAcceptableLanguages() { // Accept-Language: da // Accept-Language: en-gb;q=0.8 List values = nettyHttpHeaders.getAll(HttpHeaders.ACCEPT_LANGUAGE); if (values.isEmpty()) { return Collections.singletonList(new Locale("*")); } List localeValues = new ArrayList<>(); Map prefs = new HashMap<>(); // derive preferences from Accept-Language and sort languages according to the preferences. for (String value : values) { String[] pair = value != null ? value.split(";") : new String[0]; Locale locale = new Locale(pair[0].trim()); localeValues.add(locale); if (pair.length > 1) { String[] pair2 = pair[1] != null ? pair[1].split("=") : new String[0]; if (pair2.length > 1) { prefs.put(locale, getLanguageQualityFactor(pair2[1].trim())); } else { prefs.put(locale, 1F); } } else { prefs.put(locale, 1F); } } if (localeValues.size() <= 1) { return localeValues; } localeValues.sort(new AcceptLanguageComparator(prefs)); return localeValues; } private float getLanguageQualityFactor(String q) { if (q == null) { return 1; } if (q.charAt(0) == '.') { q = '0' + q; } try { return Float.parseFloat(q); } catch (NumberFormatException ignored) { // ignored parsing exception and return default value. } return 1; } private static class AcceptLanguageComparator implements Comparator, Serializable { private Map preferences; private static final long serialVersionUID = 6006269076155338045L; AcceptLanguageComparator(Map prefs) { this.preferences = prefs; } public int compare(Locale lang1, Locale lang2) { float p1 = preferences.get(lang1); float p2 = preferences.get(lang2); return Float.compare(p1, p2) * -1; } } @Override public MediaType getMediaType() { String value = nettyHttpHeaders.get(HttpHeaders.CONTENT_TYPE); return value == null ? null : MediaType.valueOf(value); } @Override public Locale getLanguage() { String value = nettyHttpHeaders.get(HttpHeaders.CONTENT_LANGUAGE); return value == null ? null : new Locale(value.trim()); } @Override public Map getCookies() { List values = nettyHttpHeaders.getAll(HttpHeaders.COOKIE); if (values == null || values.isEmpty()) { return Collections.emptyMap(); } Map cookieMap = new HashMap<>(); for (String value : values) { if (value == null) { continue; } Cookie cookie = Cookie.valueOf(value); cookieMap.put(cookie.getName(), cookie); } return cookieMap; } @Override public Date getDate() { String value = nettyHttpHeaders.get(HttpHeaders.DATE); if (value == null || value.isEmpty()) { return null; } // Preferred date format in internet standard is Sun, 06 Nov 1994 08:49:37 GMT SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_PATTERN, Locale.ENGLISH); //All HTTP date/time stamps MUST be represented in Greenwich Mean Time (GMT) dateFormat.setTimeZone(TimeZone.getTimeZone(GMT_TIMEZONE)); try { return dateFormat.parse(value); } catch (ParseException e) { log.error("Error while parsing the Date value. Hence return null", e); return null; } } @Override public int getLength() { String value = nettyHttpHeaders.get(HttpHeaders.CONTENT_LENGTH); if (value == null || value.isEmpty()) { return -1; } int length = Integer.parseInt(value); return length >= 0 ? length : -1; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/MSF4JConstants.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.internal; /** * MSF4J Constants. */ public class MSF4JConstants { public static final String SESSION_ID = "JSESSIONID="; public static final String CHANNEL_ID = "listener.interface.id"; public static final String CONTEXT_PATH = "contextPath"; public static final String WSO2_TRANSPORT_HTTP_CONFIG_NAMESPACE = "wso2.transport.http"; public static final String STREAMLINED_TRANSPORT_NAMESPACE = "transports"; // Property constants public static final String METHOD_PROPERTY_NAME = "method"; @Deprecated public static final String PROTOCOL_NAME = "http"; public static final String DEPLOYMENT_YAML_SYS_PROPERTY = "msf4j.conf"; public static final String DEPLOYMENT_YAML_FILE = "deployment.yaml"; } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/MSF4JHttpConnectorListener.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.internal; import io.netty.handler.codec.http.DefaultLastHttpContent; import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.LastHttpContent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.carbon.config.ConfigProviderFactory; import org.wso2.carbon.config.ConfigurationException; import org.wso2.carbon.config.provider.ConfigProvider; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import org.wso2.msf4j.config.MSF4JConfig; import org.wso2.msf4j.delegates.MSF4JResponse; import org.wso2.msf4j.interceptor.InterceptorExecutor; import org.wso2.msf4j.internal.router.HandlerException; import org.wso2.msf4j.internal.router.HttpMethodInfo; import org.wso2.msf4j.internal.router.HttpMethodInfoBuilder; import org.wso2.msf4j.internal.router.HttpResourceModel; import org.wso2.msf4j.internal.router.PatternPathRouter; import org.wso2.msf4j.internal.router.Util; import org.wso2.msf4j.util.HttpUtil; import org.wso2.transport.http.netty.contract.Constants; import org.wso2.transport.http.netty.contract.HttpConnectorListener; import org.wso2.transport.http.netty.contract.exceptions.ServerConnectorException; import org.wso2.transport.http.netty.message.HttpCarbonMessage; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Locale; import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.ws.rs.ext.ExceptionMapper; /** * Process carbon messages for MSF4J. * * @since 2.5.0 */ public class MSF4JHttpConnectorListener implements HttpConnectorListener { private static final Logger log = LoggerFactory.getLogger(MSF4JHttpConnectorListener.class); private ExecutorService executorService; public MSF4JHttpConnectorListener() { ConfigProvider configProvider = DataHolder.getInstance().getConfigProvider(); MSF4JConfig msf4JConfig; if (configProvider == null) { if (DataHolder.getInstance().getBundleContext() != null) { throw new RuntimeException("Failed to populate MSF4J Configuration. Config Provider is Null."); } //Standalone mode String deploymentYamlPath = System.getProperty(MSF4JConstants.DEPLOYMENT_YAML_SYS_PROPERTY); try { if (deploymentYamlPath != null && Files.exists(Paths.get(deploymentYamlPath))) { configProvider = ConfigProviderFactory.getConfigProvider(Paths.get(deploymentYamlPath), null); DataHolder.getInstance().setConfigProvider(configProvider); } else { if (log.isDebugEnabled()) { log.debug("MSF4J Configuration file is not provided. either system property '" + MSF4JConstants .DEPLOYMENT_YAML_SYS_PROPERTY + "' is not set or provided file path not exist. Hence " + "using default configuration."); } } } catch (ConfigurationException e) { throw new RuntimeException("Error loading deployment.yaml Configuration", e); } } try { if (configProvider != null) { msf4JConfig = DataHolder.getInstance().getConfigProvider().getConfigurationObject(MSF4JConfig.class); } else { msf4JConfig = new MSF4JConfig(); } } catch (ConfigurationException e) { throw new RuntimeException("Error while loading " + MSF4JConfig.class.getName() + " from config provider", e); } executorService = Executors.newFixedThreadPool(msf4JConfig.getThreadCount(), new MSF4JThreadFactory( new ThreadGroup(msf4JConfig.getThreadPoolName()))); } public MSF4JHttpConnectorListener(String channelId, MicroservicesRegistryImpl microservicesRegistry) { DataHolder.getInstance().getMicroservicesRegistries().put(channelId, microservicesRegistry); } /** * Carbon message handler. */ @Override public void onMessage(HttpCarbonMessage httpCarbonMessage) { // If we are running on OSGi mode need to get the registry based on the channel_id. executorService.execute(() -> { //Identify the protocol name before doing the processing MicroservicesRegistryImpl currentMicroservicesRegistry = DataHolder.getInstance().getMicroservicesRegistries() .get(httpCarbonMessage.getProperty(MSF4JConstants.CHANNEL_ID)); Request request = new Request(httpCarbonMessage); request.setSessionManager(currentMicroservicesRegistry.getSessionManager()); setBaseUri(request); Response response = new Response(request); try { dispatchMethod(currentMicroservicesRegistry, request, response); } catch (HandlerException e) { handleHandlerException(e, request); } catch (InvocationTargetException e) { Throwable targetException = e.getTargetException(); if (targetException instanceof HandlerException) { handleHandlerException((HandlerException) targetException, request); } else { handleThrowable(currentMicroservicesRegistry, targetException, request); } } catch (Throwable t) { handleThrowable(currentMicroservicesRegistry, t, request); } finally { MSF4JResponse.clearBaseUri(); // Calling the release method to make sure that there won't be any memory leaks from netty if (!httpCarbonMessage.isEmpty()) { httpCarbonMessage.getHttpContent().release(); } } }); } private void setBaseUri(Request request) { StringBuilder builder = new StringBuilder(); builder.append(request.getProperty(Constants.PROTOCOL).toString().toLowerCase(Locale.US)).append("://") .append(request.getHeader(HttpHeaderNames.HOST.toString())); if (builder.charAt(builder.length() - 1) != '/') { builder.append("/"); } try { MSF4JResponse.setBaseUri(new URI(builder.toString())); } catch (URISyntaxException e) { log.error("Error while setting the Base URI. " + e.getMessage(), e); } } /** * Dispatch appropriate resource method. */ private void dispatchMethod(MicroservicesRegistryImpl registry, Request request, Response response) throws Exception { HttpUtil.setConnectionHeader(request, response); PatternPathRouter.RoutableDestination destination = registry.getMetadata() .getDestinationMethod( request.getUri(), request.getHttpMethod(), request.getContentType(), request.getAcceptTypes()); HttpResourceModel resourceModel = destination.getDestination(); response.setMediaType(Util.getResponseType(request.getAcceptTypes(), resourceModel.getProducesMediaTypes())); HttpMethodInfoBuilder httpMethodInfoBuilder = new HttpMethodInfoBuilder().httpResourceModel(resourceModel).httpRequest(request) .httpResponder(response).requestInfo(destination.getGroupNameValues()); HttpMethodInfo httpMethodInfo = httpMethodInfoBuilder.build(); if (httpMethodInfo.isStreamingSupported()) { Method method = resourceModel.getMethod(); Class clazz = method.getDeclaringClass(); // Execute request interceptors if (InterceptorExecutor.executeGlobalRequestInterceptors(registry, request, response) // Execute class level request interceptors && InterceptorExecutor.executeClassLevelRequestInterceptors(request, response, clazz) // Execute method level request interceptors && InterceptorExecutor.executeMethodLevelRequestInterceptors(request, response, method)) { HttpCarbonMessage carbonMessage = getHttpCarbonMessage(request); HttpContent httpContent = carbonMessage.getHttpContent(); while (true) { if (httpContent == null) { break; } httpMethodInfo.chunk(httpContent.content().nioBuffer()); httpContent.release(); // Exit the loop at the end of the content if (httpContent instanceof LastHttpContent) { break; } httpContent = carbonMessage.getHttpContent(); } boolean isResponseInterceptorsSuccessful = InterceptorExecutor.executeMethodLevelResponseInterceptors(request, response, method) // Execute class level interceptors (first in - last out order) && InterceptorExecutor.executeClassLevelResponseInterceptors(request, response, clazz) // Execute global interceptors && InterceptorExecutor.executeGlobalResponseInterceptors(registry, request, response); httpMethodInfo.end(isResponseInterceptorsSuccessful); } } else { httpMethodInfo.invoke(destination, request, httpMethodInfo, registry); } } private HttpCarbonMessage getHttpCarbonMessage(Request request) throws HandlerException { Class clazz = request.getClass(); try { Method retrieveCarbonMsg = clazz.getDeclaredMethod("getHttpCarbonMessage"); retrieveCarbonMsg.setAccessible(true); return (HttpCarbonMessage) retrieveCarbonMsg.invoke(request); } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { throw new HandlerException(javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR, String.format("Error in executing request: %s %s", request.getHttpMethod(), request.getUri()), e); } } private void handleThrowable(MicroservicesRegistryImpl currentMicroservicesRegistry, Throwable throwable, Request request) { Optional exceptionMapper = currentMicroservicesRegistry.getExceptionMapper(throwable); if (exceptionMapper.isPresent()) { org.wso2.msf4j.Response msf4jResponse = new org.wso2.msf4j.Response(request); msf4jResponse.setEntity(exceptionMapper.get().toResponse(throwable)); msf4jResponse.send(); } else { log.warn("Unmapped exception", throwable); try { HttpCarbonMessage response = HttpUtil.createTextResponse( javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), "Exception occurred :" + throwable.getMessage()); response.setHeader("Content-type", "text/plain"); response.addHttpContent(new DefaultLastHttpContent()); request.respond(response); } catch (ServerConnectorException e) { log.error("Error while sending the response.", e); } } } private void handleHandlerException(HandlerException e, Request request) { try { HttpCarbonMessage failureResponse = e.getFailureResponse(); failureResponse.addHttpContent(new DefaultLastHttpContent()); request.respond(failureResponse); } catch (ServerConnectorException e1) { log.error("Error while sending the response.", e); } } @Override public void onError(Throwable throwable) { // Adding stacktrace for debug level for better usability log.warn("Error in http connector listener : '" + throwable.getMessage() + "'"); log.debug("Error in http connector listener", throwable); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/MSF4JThreadFactory.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.internal; import java.util.concurrent.ThreadFactory; /** * A Thread Factory to be used when creating threads to handle MSF4J requests. */ public class MSF4JThreadFactory implements ThreadFactory { private ThreadGroup threadGroup; private int counter = 1; public MSF4JThreadFactory(ThreadGroup threadGroup) { this.threadGroup = threadGroup; } @Override public Thread newThread(Runnable r) { String name = threadGroup.getName() + "-" + counter; Thread thread = new Thread(threadGroup, r, name); counter++; return thread; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/MSF4JWSConnectorListener.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.internal; import org.osgi.service.component.annotations.Component; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.msf4j.internal.router.PatternPathRouter; import org.wso2.msf4j.internal.websocket.CloseCodeImpl; import org.wso2.msf4j.internal.websocket.EndpointDispatcher; import org.wso2.msf4j.internal.websocket.EndpointsRegistryImpl; import org.wso2.msf4j.internal.websocket.WebSocketPongMessage; import org.wso2.msf4j.websocket.exception.WebSocketEndpointMethodReturnTypeException; import org.wso2.transport.http.netty.contract.websocket.ServerHandshakeFuture; import org.wso2.transport.http.netty.contract.websocket.ServerHandshakeListener; import org.wso2.transport.http.netty.contract.websocket.WebSocketBinaryMessage; import org.wso2.transport.http.netty.contract.websocket.WebSocketCloseMessage; import org.wso2.transport.http.netty.contract.websocket.WebSocketConnection; import org.wso2.transport.http.netty.contract.websocket.WebSocketConnectorListener; import org.wso2.transport.http.netty.contract.websocket.WebSocketControlMessage; import org.wso2.transport.http.netty.contract.websocket.WebSocketHandshaker; import org.wso2.transport.http.netty.contract.websocket.WebSocketTextMessage; import org.wso2.transport.http.netty.message.HttpCarbonRequest; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; import javax.websocket.CloseReason; import javax.websocket.PongMessage; import javax.websocket.server.PathParam; /** * WebSocketConnectorListener implementation for MSF4J. * * This will process all the websocket messages which are coming to MSF4J. */ @Component( name = "org.wso2.msf4j.internal.MSF4JWSConnectorListener", immediate = true, service = WebSocketConnectorListener.class ) public class MSF4JWSConnectorListener implements WebSocketConnectorListener { private static final Logger log = LoggerFactory.getLogger(MSF4JWSConnectorListener.class); @Override public void onHandshake(WebSocketHandshaker webSocketHandshaker) { ServerHandshakeFuture handshakeFuture = webSocketHandshaker.handshake(); handshakeFuture.setHandshakeListener(new ServerHandshakeListener() { @Override public void onSuccess(WebSocketConnection webSocketConnection) { handleWebSocketHandshake(webSocketHandshaker, webSocketConnection); webSocketConnection.startReadingFrames(); } @Override public void onError(Throwable throwable) { log.error("Unexpected error occur while handshake.", throwable); } }); } @Override public void onMessage(WebSocketTextMessage webSocketTextMessage) { EndpointsRegistryImpl endpointsRegistry = EndpointsRegistryImpl.getInstance(); String uri = webSocketTextMessage.getTarget(); PatternPathRouter.RoutableDestination routableEndpoint = endpointsRegistry.getRoutableEndpoint(uri); handleTextWebSocketMessage(webSocketTextMessage, routableEndpoint, webSocketTextMessage .getWebSocketConnection()); } @Override public void onMessage(WebSocketBinaryMessage webSocketBinaryMessage) { EndpointsRegistryImpl endpointsRegistry = EndpointsRegistryImpl.getInstance(); String uri = webSocketBinaryMessage.getTarget(); PatternPathRouter.RoutableDestination routableEndpoint = endpointsRegistry.getRoutableEndpoint(uri); handleBinaryWebSocketMessage(webSocketBinaryMessage, routableEndpoint, webSocketBinaryMessage.getWebSocketConnection()); } @Override public void onMessage(WebSocketControlMessage webSocketControlMessage) { EndpointsRegistryImpl endpointsRegistry = EndpointsRegistryImpl.getInstance(); String uri = webSocketControlMessage.getTarget(); PatternPathRouter.RoutableDestination routableEndpoint = endpointsRegistry.getRoutableEndpoint(uri); handleControlCarbonMessage(webSocketControlMessage, routableEndpoint, webSocketControlMessage.getWebSocketConnection()); } @Override public void onMessage(WebSocketCloseMessage webSocketCloseMessage) { EndpointsRegistryImpl endpointsRegistry = EndpointsRegistryImpl.getInstance(); String uri = webSocketCloseMessage.getTarget(); PatternPathRouter.RoutableDestination routableEndpoint = endpointsRegistry.getRoutableEndpoint(uri); handleCloseWebSocketMessage(webSocketCloseMessage, routableEndpoint, webSocketCloseMessage .getWebSocketConnection()); } @Override public void onClose(WebSocketConnection webSocketConnection) { } @Override public void onError(WebSocketConnection webSocketConnection, Throwable throwable) { log.error("Unexpected error occur.", throwable); } @Override public void onIdleTimeout(WebSocketControlMessage webSocketControlMessage) { } private boolean handleWebSocketHandshake(WebSocketHandshaker carbonMessage, WebSocketConnection webSocketConnection) { EndpointsRegistryImpl endpointsRegistry = EndpointsRegistryImpl.getInstance(); String requestUri = carbonMessage.getTarget(); PatternPathRouter.RoutableDestination routableEndpoint = endpointsRegistry.getRoutableEndpoint(requestUri); if (routableEndpoint == null) { throw new RuntimeException("Error while connecting to server. Routable endpoint is not registered for the" + " request uri:" + requestUri); } Optional methodOptional = new EndpointDispatcher().getOnOpenMethod(routableEndpoint.getDestination()); Map paramValues = routableEndpoint.getGroupNameValues(); try { List parameterList = new LinkedList<>(); methodOptional.ifPresent(method -> { Arrays.stream(method.getParameters()).forEach(parameter -> { if (parameter.getType() == WebSocketConnection.class) { parameterList.add(webSocketConnection); } else if (parameter.getType() == String.class) { PathParam pathParam = parameter.getAnnotation(PathParam.class); if (pathParam != null) { parameterList.add(paramValues.get(pathParam.value())); } } else if (parameter.getType() == HttpCarbonRequest.class) { parameterList.add(carbonMessage.getHttpCarbonRequest()); } else { parameterList.add(null); } }); executeMethod(method, routableEndpoint.getDestination(), parameterList, webSocketConnection); }); return true; } catch (Throwable throwable) { handleError(throwable, routableEndpoint, webSocketConnection); return false; } } private void handleTextWebSocketMessage(WebSocketTextMessage textCarbonMessage, PatternPathRouter.RoutableDestination routableEndpoint, WebSocketConnection webSocketConnection) { if (routableEndpoint == null) { throw new RuntimeException("Error while handling the message. Routable endpoint is not registered for the" + " request uri:" + textCarbonMessage.getTarget()); } Object endpoint = routableEndpoint.getDestination(); Map paramValues = routableEndpoint.getGroupNameValues(); Optional methodOptional = new EndpointDispatcher().getOnStringMessageMethod(endpoint); try { methodOptional.ifPresent( method -> { List parameterList = new LinkedList<>(); Arrays.stream(method.getParameters()).forEach( parameter -> { if (parameter.getType() == String.class) { PathParam pathParam = parameter.getAnnotation(PathParam.class); if (pathParam == null) { parameterList.add(textCarbonMessage.getText()); } else { parameterList.add(paramValues.get(pathParam.value())); } } else if (parameter.getType() == WebSocketConnection.class) { parameterList.add(webSocketConnection); } else { parameterList.add(null); } } ); executeMethod(method, endpoint, parameterList, webSocketConnection); } ); } catch (Throwable throwable) { handleError(throwable, routableEndpoint, webSocketConnection); } } private void handleBinaryWebSocketMessage(WebSocketBinaryMessage binaryCarbonMessage, PatternPathRouter.RoutableDestination routableEndpoint, WebSocketConnection webSocketConnection) { if (routableEndpoint == null) { throw new RuntimeException("Error while handling the message. Routable endpoint is not registered for the" + " request uri:" + binaryCarbonMessage.getTarget()); } Object webSocketEndpoint = routableEndpoint.getDestination(); Map paramValues = routableEndpoint.getGroupNameValues(); Optional methodOptional = new EndpointDispatcher().getOnBinaryMessageMethod(webSocketEndpoint); try { methodOptional.ifPresent(method -> { List parameterList = new LinkedList<>(); Arrays.stream(method.getParameters()).forEach(parameter -> { if (parameter.getType() == ByteBuffer.class) { parameterList.add(binaryCarbonMessage.getByteBuffer()); } else if (parameter.getType() == byte[].class) { ByteBuffer buffer = binaryCarbonMessage.getByteBuffer(); byte[] bytes = new byte[buffer.capacity()]; for (int i = 0; i < buffer.capacity(); i++) { bytes[i] = buffer.get(); } parameterList.add(bytes); } else if (parameter.getType() == boolean.class) { parameterList.add(binaryCarbonMessage.isFinalFragment()); } else if (parameter.getType() == WebSocketConnection.class) { parameterList.add(webSocketConnection); } else if (parameter.getType() == String.class) { PathParam pathParam = parameter.getAnnotation(PathParam.class); if (pathParam != null) { parameterList.add(paramValues.get(pathParam.value())); } } else { parameterList.add(null); } }); executeMethod(method, webSocketEndpoint, parameterList, webSocketConnection); }); } catch (Throwable throwable) { handleError(throwable, routableEndpoint, webSocketConnection); } } private void handleCloseWebSocketMessage(WebSocketCloseMessage closeCarbonMessage, PatternPathRouter.RoutableDestination routableEndpoint, WebSocketConnection webSocketConnection) { if (routableEndpoint == null) { throw new RuntimeException("Error while handling the message. Routable endpoint is not registered for the" + " request uri:" + closeCarbonMessage.getTarget()); } Object webSocketEndpoint = routableEndpoint.getDestination(); Map paramValues = routableEndpoint.getGroupNameValues(); Optional methodOptional = new EndpointDispatcher().getOnCloseMethod(webSocketEndpoint); try { methodOptional.ifPresent(method -> { List parameterList = new LinkedList<>(); Arrays.stream(method.getParameters()).forEach(parameter -> { if (parameter.getType() == CloseReason.class) { CloseReason.CloseCode closeCode = new CloseCodeImpl(closeCarbonMessage.getCloseCode()); CloseReason closeReason = new CloseReason(closeCode, closeCarbonMessage.getCloseReason()); parameterList.add(closeReason); } else if (parameter.getType() == WebSocketConnection.class) { parameterList.add(webSocketConnection); } else if (parameter.getType() == String.class) { PathParam pathParam = parameter.getAnnotation(PathParam.class); if (pathParam != null) { parameterList.add(paramValues.get(pathParam.value())); } } else { parameterList.add(null); } }); executeMethod(method, webSocketEndpoint, parameterList, webSocketConnection); }); } catch (Throwable throwable) { handleError(throwable, routableEndpoint, webSocketConnection); } finally { // TODO: this is temp fix assuming websocket connection is blocking call. if (webSocketConnection.isOpen()) { webSocketConnection.terminateConnection(); } } } private void handleControlCarbonMessage(WebSocketControlMessage controlCarbonMessage, PatternPathRouter. RoutableDestination routableEndpoint, WebSocketConnection webSocketConnection) { if (routableEndpoint == null) { throw new RuntimeException("Error while handling the message. Routable endpoint is not registered for the" + " request uri:" + controlCarbonMessage.getTarget()); } Object webSocketEndpoint = routableEndpoint.getDestination(); Map paramValues = routableEndpoint.getGroupNameValues(); Optional methodOptional = new EndpointDispatcher().getOnPongMessageMethod(webSocketEndpoint); try { methodOptional.ifPresent(method -> { List parameterList = new LinkedList<>(); Arrays.stream(method.getParameters()).forEach(parameter -> { if (parameter.getType() == PongMessage.class) { parameterList.add(new WebSocketPongMessage(controlCarbonMessage.getByteBuffer())); } else if (parameter.getType() == WebSocketConnection.class) { parameterList.add(webSocketConnection); } else if (parameter.getType() == String.class) { PathParam pathParam = parameter.getAnnotation(PathParam.class); if (pathParam != null) { parameterList.add(paramValues.get(pathParam.value())); } } else { parameterList.add(null); } }); executeMethod(method, webSocketEndpoint, parameterList, webSocketConnection); }); } catch (Throwable throwable) { handleError(throwable, routableEndpoint, webSocketConnection); } } private void handleError(Throwable throwable, PatternPathRouter.RoutableDestination routableEndpoint, WebSocketConnection webSocketConnection) { Object webSocketEndpoint = routableEndpoint.getDestination(); Map paramValues = routableEndpoint.getGroupNameValues(); Optional methodOptional = new EndpointDispatcher().getOnErrorMethod(webSocketEndpoint); methodOptional.ifPresent(method -> { List parameterList = new LinkedList<>(); Arrays.stream(method.getParameters()).forEach(parameter -> { if (parameter.getType() == Throwable.class) { parameterList.add(throwable); } else if (parameter.getType() == WebSocketConnection.class) { parameterList.add(webSocketConnection); } else if (parameter.getType() == String.class) { PathParam pathParam = parameter.getAnnotation(PathParam.class); if (pathParam != null) { parameterList.add(paramValues.get(pathParam.value())); } } else { parameterList.add(null); } }); executeMethod(method, webSocketEndpoint, parameterList, webSocketConnection); }); } private void executeMethod(Method method, Object webSocketEndpoint, List parameterList, WebSocketConnection webSocketConnection) { try { if (method.getReturnType() == String.class) { String returnStr = (String) method.invoke(webSocketEndpoint, parameterList.toArray()); webSocketConnection.pushText(returnStr); } else if (method.getReturnType() == ByteBuffer.class) { ByteBuffer buffer = (ByteBuffer) method.invoke(webSocketEndpoint, parameterList.toArray()); webSocketConnection.pushBinary(buffer); } else if (method.getReturnType() == byte[].class) { byte[] bytes = (byte[]) method.invoke(webSocketEndpoint, parameterList.toArray()); webSocketConnection.pushBinary(ByteBuffer.wrap(bytes)); } else if (method.getReturnType() == void.class) { method.invoke(webSocketEndpoint, parameterList.toArray()); } else if (method.getReturnType() == PongMessage.class) { PongMessage pongMessage = (PongMessage) method.invoke(webSocketEndpoint, parameterList.toArray()); webSocketConnection.pong(pongMessage.getApplicationData()); } else { throw new WebSocketEndpointMethodReturnTypeException("Unknown return type."); } } catch (IllegalAccessException e) { log.error("Illegal access exception occurred: " + e.toString()); } catch (InvocationTargetException e) { log.error("Method invocation failed: " + e.toString()); } catch (WebSocketEndpointMethodReturnTypeException e) { log.error("WebSocket method return type exception occurred: " + e.toString()); } } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/MicroservicesLCException.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.internal; /** * This Exception can be used with Lifecycle methods. */ public class MicroservicesLCException extends RuntimeException { public MicroservicesLCException(String message, Throwable throwable) { super(message, throwable); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/MicroservicesRegistryImpl.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.internal; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.msf4j.DefaultSessionManager; import org.wso2.msf4j.Interceptor; import org.wso2.msf4j.MicroservicesRegistry; import org.wso2.msf4j.SessionManager; import org.wso2.msf4j.SwaggerService; import org.wso2.msf4j.interceptor.RequestInterceptor; import org.wso2.msf4j.interceptor.ResponseInterceptor; import org.wso2.msf4j.internal.router.MicroserviceMetadata; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.ServiceLoader; import java.util.Set; import java.util.TreeMap; import java.util.stream.Collectors; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.ws.rs.Path; import javax.ws.rs.ext.ExceptionMapper; /** * MicroservicesRegistry for the MSF4J component. */ public class MicroservicesRegistryImpl implements MicroservicesRegistry { private static final Logger log = LoggerFactory.getLogger(MicroservicesRegistryImpl.class); private final Map services = new HashMap<>(); private List globalRequestInterceptorList = new ArrayList<>(); private List globalResponseInterceptorList = new ArrayList<>(); private volatile MicroserviceMetadata metadata = new MicroserviceMetadata(Collections.emptyMap()); private Map exceptionMappers = new TreeMap<>(new ClassComparator()); private SessionManager sessionManager = new DefaultSessionManager(); public MicroservicesRegistryImpl() { /* In non OSGi mode, if we can find the SwaggerDefinitionService, Deploy the Swagger definition service which will return the Swagger definition.*/ if (DataHolder.getInstance().getBundleContext() == null) { ServiceLoader swaggerServices = ServiceLoader.load(SwaggerService.class); Iterator iterator = swaggerServices.iterator(); if (iterator.hasNext()) { SwaggerService swaggerService = iterator.next(); swaggerService.init(this); services.put("/swagger", swaggerService); } } } public void addService(Object... service) { for (Object svc : service) { services.put(svc.getClass().getAnnotation(Path.class).value(), svc); } updateMetadata(); if (log.isDebugEnabled()) { Arrays.stream(service).forEach(svc -> log.debug("Added microservice: {}.", svc)); } } public void addService(String basePath, Object service) { services.put(basePath, service); metadata.addMicroserviceMetadata(service, basePath); log.debug("Added microservice: {}.", service); } public Optional> getServiceWithBasePath(String path) { return services.entrySet().stream().filter(svc -> svc.getKey().equals(path)).findAny(); } public void removeService(Object service) { if (service == null) { log.error("Service cannot be null."); return; } Path path = service.getClass().getAnnotation(Path.class); if (path == null) { log.warn("Service removal failed. Microservice class '" + service.getClass().getName() + "' doesn't contain a root Path."); return; } services.remove(path.value()); updateMetadata(); } public void setSessionManager(SessionManager sessionManager) { if (sessionManager == null) { throw new IllegalArgumentException("SessionManager cannot be null"); } this.sessionManager = sessionManager; } public void addInterceptor(Interceptor... interceptor) { Collections.addAll(globalRequestInterceptorList, interceptor); Collections.addAll(globalResponseInterceptorList, interceptor); } public void removeInterceptor(Interceptor interceptor) { globalRequestInterceptorList.remove(interceptor); globalResponseInterceptorList.remove(interceptor); } public MicroserviceMetadata getMetadata() { return metadata; } public Set getHttpServices() { return Collections.unmodifiableSet(services.values().stream().collect(Collectors.toSet())); } /** * Register request interceptors. * * @param requestInterceptor interceptor instances. */ public void addGlobalRequestInterceptor(RequestInterceptor... requestInterceptor) { Collections.addAll(globalRequestInterceptorList, requestInterceptor); } /** * Register response interceptors. * * @param responseInterceptor interceptor instances. */ public void addGlobalResponseInterceptor(ResponseInterceptor... responseInterceptor) { Collections.addAll(globalResponseInterceptorList, responseInterceptor); } /** * Remove msf4j request interceptor. * * @param requestInterceptor MSF4J interceptor instance. */ public void removeGlobalRequestInterceptor(RequestInterceptor requestInterceptor) { globalRequestInterceptorList.remove(requestInterceptor); } /** * Remove msf4j response interceptor. * * @param responseInterceptor MSF4J interceptor instance. */ public void removeGlobalResponseInterceptor(ResponseInterceptor responseInterceptor) { globalResponseInterceptorList.remove(responseInterceptor); } /** * Get global request interceptor list. * * @return global request interceptor list */ public List getGlobalRequestInterceptorList() { return globalRequestInterceptorList; } /** * Get global response interceptor list. * * @return global response interceptor list */ public List getGlobalResponseInterceptorList() { return globalResponseInterceptorList; } public void addExceptionMapper(ExceptionMapper... mapper) { Arrays.stream(mapper).forEach(em -> { Arrays.stream(em.getClass().getMethods()). filter(method -> "toResponse".equals(method.getName()) && method.getParameterCount() == 1 && !Throwable.class.getName().equals(method.getParameterTypes()[0].getTypeName())). findAny(). ifPresent(method -> { try { exceptionMappers.put(Class.forName(method.getParameterTypes()[0].getTypeName(), false, em.getClass().getClassLoader()), em); } catch (ClassNotFoundException e) { log.error("Could not load class", e); } }); }); } Optional getExceptionMapper(Throwable throwable) { return exceptionMappers.entrySet(). stream(). filter(entry -> entry.getKey().isAssignableFrom(throwable.getClass())). findFirst(). flatMap(entry -> Optional.ofNullable(entry.getValue())); } public void removeExceptionMapper(ExceptionMapper em) { Arrays.stream(em.getClass().getMethods()). filter(method -> method.getName().equals("toResponse") && method.getParameterCount() == 1). findAny(). ifPresent(method -> { try { exceptionMappers.remove(Class.forName(method.getGenericParameterTypes()[0].getTypeName(), false, em.getClass().getClassLoader())); } catch (ClassNotFoundException e) { log.error("Could not load class", e); } }); } public int getServiceCount() { return services.size(); } private void updateMetadata() { metadata = new MicroserviceMetadata(Collections.unmodifiableMap(services)); } public void initServices() { invokeLifecycleMethods(PostConstruct.class); } public void initService(Object httpService) { invokeLifecycleMethod(httpService, PostConstruct.class); } public void preDestroyServices() { invokeLifecycleMethods(PreDestroy.class); } public void preDestroyService(Object httpService) { invokeLifecycleMethod(httpService, PreDestroy.class); } public SessionManager getSessionManager() { return sessionManager; } private void invokeLifecycleMethods(Class lcAnnotation) { services.values().stream().forEach(httpService -> invokeLifecycleMethod(httpService, lcAnnotation)); } private void invokeLifecycleMethod(Object httpService, Class lcAnnotation) { Optional lcMethod = Optional.ofNullable(getLifecycleMethod(httpService, lcAnnotation)); if (lcMethod.isPresent()) { try { lcMethod.get().invoke(httpService); } catch (IllegalAccessException | InvocationTargetException e) { throw new MicroservicesLCException("Exception occurs calling lifecycle method", e); } } } private Method getLifecycleMethod(Object httpService, Class lcAnnotation) { return Arrays.stream(httpService.getClass().getDeclaredMethods()).filter(m -> isValidLifecycleMethod (Optional.of(m), lcAnnotation)).findFirst().orElse(null); } private boolean isValidLifecycleMethod(Optional method, Class lcAnnotation) { return method.filter(m -> Modifier.isPublic(m.getModifiers()) && m.getAnnotation(lcAnnotation) != null).isPresent(); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/MicroservicesServerActivator.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.internal; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; /** * OSGi Bundle Activator of the MSF4J component. */ public class MicroservicesServerActivator implements BundleActivator { public void start(BundleContext bundleContext) throws Exception { DataHolder.getInstance().setBundleContext(bundleContext); } public void stop(BundleContext bundleContext) throws Exception { } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/MicroservicesServerImpl.java ================================================ /* * Copyright (c) 2017 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ package org.wso2.msf4j.internal; import org.wso2.msf4j.MicroservicesServer; import org.wso2.transport.http.netty.contract.config.ListenerConfiguration; import java.util.Collections; import java.util.Map; /** * Handles details relates microservices server. transport details etc. */ public class MicroservicesServerImpl implements MicroservicesServer { private final Map listenerConfigurationMap; public MicroservicesServerImpl(Map configurationMap) { listenerConfigurationMap = Collections.unmodifiableMap(configurationMap); } @Override public Map getListenerConfigurations() { return listenerConfigurationMap; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/MicroservicesServerSC.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.internal; import org.osgi.framework.BundleContext; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.ReferenceCardinality; import org.osgi.service.component.annotations.ReferencePolicy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.carbon.config.ConfigurationException; import org.wso2.carbon.config.provider.ConfigProvider; import org.wso2.carbon.kernel.config.model.CarbonConfiguration; import org.wso2.carbon.kernel.startupresolver.RequiredCapabilityListener; import org.wso2.carbon.kernel.startupresolver.StartupServiceUtils; import org.wso2.msf4j.DefaultSessionManager; import org.wso2.msf4j.Interceptor; import org.wso2.msf4j.Microservice; import org.wso2.msf4j.MicroservicesRegistry; import org.wso2.msf4j.MicroservicesServer; import org.wso2.msf4j.SessionManager; import org.wso2.msf4j.SwaggerService; import org.wso2.msf4j.exception.OSGiDeclarativeServiceException; import org.wso2.msf4j.interceptor.OSGiInterceptorConfig; import org.wso2.msf4j.util.RuntimeAnnotations; import org.wso2.msf4j.util.Utils; import org.wso2.transport.http.netty.contract.HttpConnectorListener; import org.wso2.transport.http.netty.contract.HttpWsConnectorFactory; import org.wso2.transport.http.netty.contract.ServerConnector; import org.wso2.transport.http.netty.contract.ServerConnectorFuture; import org.wso2.transport.http.netty.contract.config.ListenerConfiguration; import org.wso2.transport.http.netty.contract.config.ServerBootstrapConfiguration; import org.wso2.transport.http.netty.contract.config.TransportsConfiguration; import org.wso2.transport.http.netty.contractimpl.DefaultHttpWsConnectorFactory; import org.wso2.transport.http.netty.message.HttpConnectorUtil; import java.util.ArrayList; import java.util.Arrays; import java.util.Dictionary; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Set; import javax.ws.rs.Path; import javax.ws.rs.ext.ExceptionMapper; import static org.wso2.msf4j.internal.MSF4JConstants.STREAMLINED_TRANSPORT_NAMESPACE; /** * OSGi service component for MicroServicesServer. */ @Component( name = "org.wso2.msf4j.internal.MicroServicesServerSC", immediate = true, property = { "componentName=wso2-microservices-server" } ) @SuppressWarnings("unused") public class MicroservicesServerSC implements RequiredCapabilityListener { private static final Logger log = LoggerFactory.getLogger(MicroservicesServerSC.class); private boolean isAllRequiredCapabilitiesAvailable; private List serverConnectors = new ArrayList<>(); private MSF4JHttpConnectorListener msf4JHttpConnectorListener; private MSF4JWSConnectorListener msf4JWSConnectorListener; private Map listenerConfigurationMap = new HashMap<>(); @Activate protected void start(final BundleContext bundleContext) { } @Reference( name = "microservice", service = Microservice.class, cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC, unbind = "removeService" ) protected void addService(Microservice service, Map properties) { /* Some Microservices might get register even after #onAllRequiredCapabilitiesAvailable That is due to the UUF doesn't know the actual service count before hand. Therefore we need to handle those services separately. */ if (isAllRequiredCapabilitiesAvailable) { Object channelId = properties.get(MSF4JConstants.CHANNEL_ID); Object contextPath = properties.get(MSF4JConstants.CONTEXT_PATH); addMicroserviceToRegistry(service, channelId, contextPath); } StartupServiceUtils.updateServiceCache("wso2-microservices-server", Microservice.class); } protected void removeService(Microservice service, Map properties) { Object channelId = properties.get(MSF4JConstants.CHANNEL_ID); Map microservicesRegistries = DataHolder.getInstance().getMicroservicesRegistries(); if (channelId != null) { MicroservicesRegistryImpl microservicesRegistry = microservicesRegistries.get(channelId.toString()); if (microservicesRegistry != null) { microservicesRegistry.removeService(service); } } else { microservicesRegistries.values().forEach(registry -> registry.removeService(service)); } } @Reference( name = "swaggerservice", service = SwaggerService.class, cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC, unbind = "removeSwaggerService" ) protected void addSwaggerService(SwaggerService service, Map properties) { Object channelId = properties.get(MSF4JConstants.CHANNEL_ID); Map microservicesRegistries = DataHolder.getInstance().getMicroservicesRegistries(); if (channelId != null) { MicroservicesRegistryImpl microservicesRegistry = microservicesRegistries.get(channelId.toString()); if (microservicesRegistry == null) { throw new RuntimeException("Couldn't found the registry for channel ID " + channelId); } microservicesRegistry.addService(service); } else { microservicesRegistries.values().forEach(registry -> registry.addService(service)); } } protected void removeSwaggerService(SwaggerService service, Map properties) { Object channelId = properties.get(MSF4JConstants.CHANNEL_ID); Map microservicesRegistries = DataHolder.getInstance().getMicroservicesRegistries(); if (channelId != null) { MicroservicesRegistryImpl microservicesRegistry = microservicesRegistries.get(channelId.toString()); if (microservicesRegistry != null) { microservicesRegistry.removeService(service); } } } protected void removeCarbonTransport(ServerConnector serverConnector) { DataHolder.getInstance().getMicroservicesRegistries().remove(serverConnector.getConnectorID()); } @Reference( name = "carbon.config.provider", service = ConfigProvider.class, cardinality = ReferenceCardinality.MANDATORY, policy = ReferencePolicy.DYNAMIC, unbind = "unregisterConfigProvider" ) protected void registerConfigProvider(ConfigProvider configProvider) { DataHolder.getInstance().setConfigProvider(configProvider); try { final TransportsConfiguration transportsConfiguration; Object transportConf = configProvider.getConfigurationObject(STREAMLINED_TRANSPORT_NAMESPACE); if (transportConf != null) { transportsConfiguration = Utils.resolveTransportsNSConfiguration(transportConf); } else { transportsConfiguration = configProvider.getConfigurationObject (MSF4JConstants.WSO2_TRANSPORT_HTTP_CONFIG_NAMESPACE, TransportsConfiguration.class); } CarbonConfiguration carbonConfig = configProvider.getConfigurationObject(CarbonConfiguration.class); transportsConfiguration.getListenerConfigurations().forEach( listenerConfiguration -> listenerConfiguration.setPort( listenerConfiguration.getPort() + carbonConfig.getPortsConfig().getOffset())); Set listenerConfigurations = transportsConfiguration.getListenerConfigurations(); if (listenerConfigurations.isEmpty()) { listenerConfigurations = new HashSet<>(); listenerConfigurations.add(new ListenerConfiguration()); } ServerBootstrapConfiguration serverBootstrapConfiguration = HttpConnectorUtil.getServerBootstrapConfiguration(transportsConfiguration.getTransportProperties()); HttpWsConnectorFactory connectorFactory = new DefaultHttpWsConnectorFactory(); listenerConfigurations.forEach(listenerConfiguration -> { ServerConnector serverConnector = connectorFactory.createServerConnector(serverBootstrapConfiguration, listenerConfiguration); MicroservicesRegistryImpl microservicesRegistry = new MicroservicesRegistryImpl(); Map microservicesRegistries = DataHolder.getInstance().getMicroservicesRegistries(); Dictionary properties = new Hashtable<>(); properties.put(MSF4JConstants.CHANNEL_ID, serverConnector.getConnectorID()); microservicesRegistries.put(serverConnector.getConnectorID(), microservicesRegistry); DataHolder.getInstance().getBundleContext() .registerService(MicroservicesRegistry.class, microservicesRegistry, properties); listenerConfigurationMap.put(serverConnector.getConnectorID(), listenerConfiguration); serverConnectors.add(serverConnector); }); } catch (ConfigurationException e) { log.error("Error while loading TransportsConfiguration", e); throw new RuntimeException("Error while loading TransportsConfiguration", e); } StartupServiceUtils.updateServiceCache("wso2-microservices-server", ConfigProvider.class); } protected void unregisterConfigProvider(ConfigProvider configProvider) { DataHolder.getInstance().setConfigProvider(null); } @Reference( name = "interceptor-config", service = OSGiInterceptorConfig.class, cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC, unbind = "removeInterceptorConfig" ) protected void addInterceptorConfig(OSGiInterceptorConfig interceptorConfig, Map properties) { StartupServiceUtils.updateServiceCache("wso2-microservices-server", OSGiInterceptorConfig.class); } protected void removeInterceptorConfig(OSGiInterceptorConfig interceptorConfig, Map properties) { Object channelId = properties.get(MSF4JConstants.CHANNEL_ID); Map microservicesRegistries = DataHolder.getInstance().getMicroservicesRegistries(); if (channelId != null) { MicroservicesRegistryImpl microServicesRegistry = microservicesRegistries.get(channelId.toString()); Arrays.stream(interceptorConfig.getGlobalRequestInterceptorArray()).forEach( microServicesRegistry::removeGlobalRequestInterceptor); Arrays.stream(interceptorConfig.getGlobalResponseInterceptorArray()).forEach( microServicesRegistry::removeGlobalResponseInterceptor); } else { microservicesRegistries.values().forEach(registry -> { Arrays.stream(interceptorConfig.getGlobalRequestInterceptorArray()).forEach( registry::removeGlobalRequestInterceptor); Arrays.stream(interceptorConfig.getGlobalResponseInterceptorArray()).forEach( registry::removeGlobalResponseInterceptor); }); } } @Reference( name = "interceptor", service = Interceptor.class, cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC, unbind = "removeInterceptor" ) protected void addInterceptor(Interceptor interceptor, Map properties) { StartupServiceUtils.updateServiceCache("wso2-microservices-server", Interceptor.class); } /** * Remove interceptor. * * @param interceptor interceptor to be removed * @param properties map of interceptor component properties * @deprecated */ protected void removeInterceptor(Interceptor interceptor, Map properties) { Object channelId = properties.get(MSF4JConstants.CHANNEL_ID); Map microservicesRegistries = DataHolder.getInstance().getMicroservicesRegistries(); if (channelId != null) { microservicesRegistries.get(channelId.toString()).removeGlobalRequestInterceptor(interceptor); microservicesRegistries.get(channelId.toString()).removeGlobalResponseInterceptor(interceptor); } else { microservicesRegistries.values().forEach(registry -> { registry.removeGlobalRequestInterceptor(interceptor); registry.removeGlobalResponseInterceptor(interceptor); }); } } @Reference( name = "exception-mapper", service = ExceptionMapper.class, cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC, unbind = "removeExceptionMapper" ) protected void addExceptionMapper(ExceptionMapper exceptionMapper, Map properties) { } protected void removeExceptionMapper(ExceptionMapper exceptionMapper, Map properties) { Object channelId = properties.get(MSF4JConstants.CHANNEL_ID); Map microservicesRegistries = DataHolder.getInstance().getMicroservicesRegistries(); if (channelId != null) { microservicesRegistries.get(channelId.toString()).removeExceptionMapper(exceptionMapper); } else { microservicesRegistries.values().forEach(registry -> registry.removeExceptionMapper(exceptionMapper)); } } @Reference( name = "session-manager", service = SessionManager.class, cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC, unbind = "removeSessionManager" ) protected void addSessionManager(SessionManager sessionManager, Map properties) { } protected void removeSessionManager(SessionManager sessionManager, Map properties) { Object channelId = properties.get(MSF4JConstants.CHANNEL_ID); Map microservicesRegistries = DataHolder.getInstance().getMicroservicesRegistries(); sessionManager.stop(); DefaultSessionManager defaultSessionManager = new DefaultSessionManager(); defaultSessionManager.init(); if (channelId != null) { microservicesRegistries.get(channelId.toString()).setSessionManager(defaultSessionManager); } else { microservicesRegistries.values().forEach(registry -> registry.setSessionManager(defaultSessionManager)); } } @Override public void onAllRequiredCapabilitiesAvailable() { try { ServiceReference[] serviceReferences = DataHolder.getInstance().getBundleContext() .getServiceReferences(Microservice.class.getName(), null); if (serviceReferences != null && serviceReferences.length > 0) { Arrays.stream(serviceReferences).forEach(serviceReference -> { Microservice service = (Microservice) DataHolder.getInstance().getBundleContext().getService(serviceReference); Object channelId = serviceReference.getProperty(MSF4JConstants.CHANNEL_ID); Object contextPath = serviceReference.getProperty("contextPath"); addMicroserviceToRegistry(service, channelId, contextPath); }); } // Add request and response interceptors serviceReferences = DataHolder.getInstance().getBundleContext() .getServiceReferences(OSGiInterceptorConfig.class.getName(), null); if (serviceReferences != null && serviceReferences.length > 0) { Arrays.stream(serviceReferences).forEach(serviceReference -> { OSGiInterceptorConfig interceptorConfig = (OSGiInterceptorConfig) DataHolder.getInstance().getBundleContext() .getService(serviceReference); Object channelId = serviceReference.getProperty(MSF4JConstants.CHANNEL_ID); addRequestResponseInterceptorsToRegistry(interceptorConfig, channelId); }); } serviceReferences = DataHolder.getInstance().getBundleContext().getServiceReferences(Interceptor.class.getName(), null); if (serviceReferences != null && serviceReferences.length > 0) { Arrays.stream(serviceReferences).forEach(serviceReference -> { Interceptor interceptor = (Interceptor) DataHolder.getInstance().getBundleContext().getService(serviceReference); Object channelId = serviceReference.getProperty(MSF4JConstants.CHANNEL_ID); addInterceptorToRegistry(interceptor, channelId); }); } serviceReferences = DataHolder.getInstance().getBundleContext() .getServiceReferences(ExceptionMapper.class.getName(), null); if (serviceReferences != null && serviceReferences.length > 0) { Arrays.stream(serviceReferences).forEach(serviceReference -> { ExceptionMapper exceptionMapper = (ExceptionMapper) DataHolder.getInstance().getBundleContext().getService(serviceReference); Object channelId = serviceReference.getProperty(MSF4JConstants.CHANNEL_ID); addExceptionMapperToRegistry(exceptionMapper, channelId); }); } serviceReferences = DataHolder.getInstance().getBundleContext() .getServiceReferences(SessionManager.class.getName(), null); if (serviceReferences != null && serviceReferences.length > 0) { Arrays.stream(serviceReferences).forEach(serviceReference -> { SessionManager sessionManager = (SessionManager) DataHolder.getInstance().getBundleContext().getService(serviceReference); Object channelId = serviceReference.getProperty(MSF4JConstants.CHANNEL_ID); addSessionManagerToRegistry(sessionManager, channelId); }); } } catch (InvalidSyntaxException e) { log.error("Error while registering required capabilities. " + e.getMessage()); } finally { isAllRequiredCapabilitiesAvailable = true; } msf4JHttpConnectorListener = new MSF4JHttpConnectorListener(); msf4JWSConnectorListener = new MSF4JWSConnectorListener(); DataHolder.getInstance().getBundleContext() .registerService(HttpConnectorListener.class, msf4JHttpConnectorListener, null); DataHolder.getInstance().getBundleContext().registerService(MicroservicesServerSC.class, this, null); DataHolder.getInstance().getBundleContext().registerService(MicroservicesServer.class, new MicroservicesServerImpl(listenerConfigurationMap), null); log.info("All microservices are available"); serverConnectors.forEach(serverConnector -> { final ServerConnectorFuture serverConnectorFuture = serverConnector.start(); serverConnectorFuture.setHttpConnectorListener(msf4JHttpConnectorListener); serverConnectorFuture.setWebSocketConnectorListener(msf4JWSConnectorListener); }); } private void addMicroserviceToRegistry(Microservice service, Object channelId, Object contextPath) { if (contextPath != null) { Map valuesMap = new HashMap<>(); valuesMap.put("value", contextPath); RuntimeAnnotations.putAnnotation(service.getClass(), Path.class, valuesMap); } Map microservicesRegistries = DataHolder.getInstance().getMicroservicesRegistries(); if (channelId != null) { MicroservicesRegistryImpl microservicesRegistry = microservicesRegistries.get(channelId.toString()); if (microservicesRegistry == null) { throw new RuntimeException("Couldn't found the registry for channel ID " + channelId); } if (contextPath == null) { microservicesRegistry.addService(service); } else { microservicesRegistry.addService(contextPath.toString(), service); } } else { if (contextPath == null) { microservicesRegistries.values().forEach(registry -> registry.addService(service)); } else { microservicesRegistries.values() .forEach(registry -> registry.addService(contextPath.toString(), service)); } } } /** * Add request and response interceptors to registry. * * @param interceptorConfig interceptor configuration * @param channelId micro-service channel id */ private void addRequestResponseInterceptorsToRegistry(OSGiInterceptorConfig interceptorConfig, Object channelId) { Map microservicesRegistries = DataHolder.getInstance().getMicroservicesRegistries(); if (channelId != null) { MicroservicesRegistryImpl microservicesRegistry = microservicesRegistries.get(channelId.toString()); if (microservicesRegistry == null) { throw new OSGiDeclarativeServiceException("Couldn't find the registry for channel ID " + channelId); } microservicesRegistry.addGlobalRequestInterceptor(interceptorConfig.getGlobalRequestInterceptorArray()); microservicesRegistry.addGlobalResponseInterceptor(interceptorConfig.getGlobalResponseInterceptorArray()); } else { microservicesRegistries.values().forEach(registry -> { registry.addGlobalRequestInterceptor(interceptorConfig.getGlobalRequestInterceptorArray()); registry.addGlobalResponseInterceptor(interceptorConfig.getGlobalResponseInterceptorArray()); }); } } /** * Add interceptor to registry. * * @param interceptor interceptor * @param channelId micro-service channel it */ @Deprecated private void addInterceptorToRegistry(Interceptor interceptor, Object channelId) { Map microservicesRegistries = DataHolder.getInstance().getMicroservicesRegistries(); if (channelId != null) { MicroservicesRegistryImpl microservicesRegistry = microservicesRegistries.get(channelId.toString()); if (microservicesRegistry == null) { throw new OSGiDeclarativeServiceException("Couldn't found the registry for channel ID " + channelId); } microservicesRegistry.addGlobalRequestInterceptor(interceptor); microservicesRegistry.addGlobalResponseInterceptor(interceptor); } else { microservicesRegistries.values().forEach(registry -> { registry.addGlobalRequestInterceptor(interceptor); registry.addGlobalResponseInterceptor(interceptor); }); } } private void addExceptionMapperToRegistry(ExceptionMapper exceptionMapper, Object channelId) { Map microservicesRegistries = DataHolder.getInstance().getMicroservicesRegistries(); if (channelId != null) { MicroservicesRegistryImpl microservicesRegistry = microservicesRegistries.get(channelId.toString()); if (microservicesRegistry == null) { throw new RuntimeException("Couldn't found the registry for channel ID " + channelId); } microservicesRegistry.addExceptionMapper(exceptionMapper); } else { microservicesRegistries.values().forEach(registry -> registry.addExceptionMapper(exceptionMapper)); } } private void addSessionManagerToRegistry(SessionManager sessionManager, Object channelId) { Map microservicesRegistries = DataHolder.getInstance().getMicroservicesRegistries(); sessionManager.init(); if (channelId != null) { MicroservicesRegistryImpl microservicesRegistry = microservicesRegistries.get(channelId.toString()); if (microservicesRegistry == null) { throw new RuntimeException("Couldn't found the registry for channel ID " + channelId); } microservicesRegistry.setSessionManager(sessionManager); } else { microservicesRegistries.values().forEach(registry -> registry.setSessionManager(sessionManager)); } } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/beanconversion/BeanConverter.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.internal.beanconversion; import org.wso2.msf4j.beanconversion.MediaTypeConverter; import java.util.HashMap; import java.util.Locale; import java.util.Map; /** * Factory class for getting correct media type conversion * instance for a given mime type. */ public class BeanConverter { private static final MediaTypeConverter DEFAULT_CONVERTER = new TextPlainConverter(); private static final Map converterMap = new HashMap<>(); private BeanConverter() { } static { addMediaTypeConverter(new JsonConverter()); addMediaTypeConverter(new XmlConverter()); } /** * Get a media type converter for a given media type string. * * @param mediaType media type String * @return MediaTypeConverter */ public static MediaTypeConverter getConverter(String mediaType) { MediaTypeConverter mediaTypeConverter = converterMap.get(mediaType.toLowerCase(Locale.US)); if (mediaTypeConverter == null) { mediaTypeConverter = DEFAULT_CONVERTER; } return mediaTypeConverter; } /** * Register a media type converter. */ private static void addMediaTypeConverter(MediaTypeConverter mediaTypeConverter) { for (String mediaType : mediaTypeConverter.getSupportedMediaTypes()) { converterMap.put(mediaType.toLowerCase(Locale.US), mediaTypeConverter); } } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/beanconversion/JsonConverter.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.internal.beanconversion; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.gson.JsonSyntaxException; import org.wso2.msf4j.beanconversion.BeanConversionException; import org.wso2.msf4j.beanconversion.MediaTypeConverter; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.lang.reflect.Type; import java.nio.ByteBuffer; import java.nio.charset.Charset; import javax.ws.rs.core.MediaType; /** * Media type converter for text/json, * application/json mime types. */ public class JsonConverter extends MediaTypeConverter { private static final Gson gson = new Gson(); private static final String TEXT_JSON = "text/json"; /** * Provides the supported media types for bean conversions. */ @Override public String[] getSupportedMediaTypes() { return new String[]{MediaType.APPLICATION_JSON, TEXT_JSON}; } /** * Convert an Object to a Json encoded byte buffer. * * @param object object that needs to be converted to a media content * @return Json encoded byte buffer */ @Override public ByteBuffer toMedia(Object object) { String value = (object instanceof String || object instanceof JsonArray || object instanceof JsonObject) ? object.toString() : gson.toJson(object); return ByteBuffer.wrap(value.getBytes(Charset.defaultCharset())); } /** * Convert a Json ByteBuffer content to an object. * * @param content content that needs to be converted to an object * @param targetType media type of the content * @return Object that maps the Json data * @throws BeanConversionException if error occure while converting the content */ @Override public Object toObject(ByteBuffer content, Type targetType) throws BeanConversionException { try { String str = Charset.defaultCharset().decode(content).toString(); Object object = gson.fromJson(str, targetType); if (object == null) { throw new BeanConversionException("Unable to perform json to object conversion"); } return object; } catch (JsonSyntaxException ex) { throw new BeanConversionException("Unable to perform json to object conversion", ex); } } @Override protected Object toObject(InputStream inputStream, Type targetType) throws BeanConversionException { try { Reader reader = new InputStreamReader(inputStream, UTF_8_CHARSET); Object object = gson.fromJson(reader, targetType); if (object == null) { throw new BeanConversionException("Unable to perform json to object conversion"); } return object; } catch (UnsupportedEncodingException ex) { throw new BeanConversionException("Unable to perform json to object conversion", ex); } } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/beanconversion/TextPlainConverter.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.internal.beanconversion; import org.apache.commons.io.IOUtils; import org.wso2.msf4j.beanconversion.BeanConversionException; import org.wso2.msf4j.beanconversion.MediaTypeConverter; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Type; import java.nio.ByteBuffer; import java.nio.charset.Charset; /** * Media type converter for text/plain mime type. This will be * applied by default for unsupported media types. */ public class TextPlainConverter extends MediaTypeConverter { /** * Provides the supported media types for bean conversions. */ @Override public String[] getSupportedMediaTypes() { return new String[0]; } /** * Convert an Object to a text ByteBuffer. * * @param object object that needs to be converted to a media content * @return Byte buffer that contains the text */ @Override public ByteBuffer toMedia(Object object) { return ByteBuffer.wrap(object.toString().getBytes(Charset.defaultCharset())); } /** * Convert a text ByteBuffer content to an object. * * @param content content that needs to be converted to an object * @param targetType media type of the content * @return String object that contains the text data */ @Override public Object toObject(ByteBuffer content, Type targetType) { return Charset.defaultCharset().decode(content).toString(); } @Override protected Object toObject(InputStream inputStream, Type targetType) throws BeanConversionException { try { return IOUtils.toString(inputStream, UTF_8_CHARSET); } catch (IOException e) { throw new BeanConversionException("Unable to perform string conversion", e); } } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/beanconversion/XmlConverter.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.internal.beanconversion; import org.wso2.msf4j.beanconversion.BeanConversionException; import org.wso2.msf4j.beanconversion.MediaTypeConverter; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.lang.reflect.Type; import java.nio.ByteBuffer; import java.nio.charset.Charset; import javax.ws.rs.core.MediaType; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; /** * Media type converter for text/xml mime type. */ public class XmlConverter extends MediaTypeConverter { private static final String TEXT_XML = "text/xml"; /** * Provides the supported media types for bean conversions. */ @Override public String[] getSupportedMediaTypes() { return new String[]{MediaType.APPLICATION_XML, TEXT_XML}; } /** * Convert an Object to a xml encoded ByteBuffer. * * @param object object that needs to be converted to a media content * @return xml encoded byte buffer */ @Override public ByteBuffer toMedia(Object object) throws BeanConversionException { try { JAXBContext jaxbContext = JAXBContext.newInstance(object.getClass()); Marshaller marshaller = jaxbContext.createMarshaller(); StringWriter stringWriter = new StringWriter(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(object, stringWriter); return ByteBuffer.wrap(stringWriter.toString().getBytes(Charset.defaultCharset())); } catch (JAXBException e) { throw new BeanConversionException("Unable to perform object to xml conversion", e); } } /** * Convert a xml ByteBuffer content to an object. * * @param content content that needs to be converted to an object * @param targetType media type of the content * @return Object that maps the xml data */ @Override public Object toObject(ByteBuffer content, Type targetType) throws BeanConversionException { try { String str = Charset.defaultCharset().decode(content).toString(); JAXBContext jaxbContext = null; if (targetType instanceof Class) { jaxbContext = JAXBContext.newInstance((Class) targetType); return jaxbContext.createUnmarshaller().unmarshal(new StringReader(str)); } } catch (JAXBException e) { throw new BeanConversionException("Unable to perform xml to object conversion", e); } return null; } @Override protected Object toObject(InputStream inputStream, Type targetType) throws BeanConversionException { try { JAXBContext jaxbContext; Reader reader = new InputStreamReader(inputStream, UTF_8_CHARSET); if (targetType instanceof Class) { jaxbContext = JAXBContext.newInstance((Class) targetType); return jaxbContext.createUnmarshaller().unmarshal(reader); } } catch (JAXBException | UnsupportedEncodingException e) { throw new BeanConversionException("Unable to perform xml to object conversion", e); } return null; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/entitywriter/EntityWriter.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.internal.entitywriter; import org.wso2.transport.http.netty.message.HttpCarbonMessage; /** * Interface that provides the capability of writing an entity type to * a carbon message. * * @param type of the entity */ public interface EntityWriter { String CHUNKED = "chunked"; /** * Provide supported entity type. * * @return entity type */ Class getType(); /** * Write the entity object to the carbon message by considering the * provided chunk size and media type. * * @param carbonMessage response message * @param entity object * @param mediaType user defined media type * @param chunkSize user defined chunk size * 0 to signify none chunked response * -1 to signify default chunk size of the EntityWriter * @param responder HttpCarbonMessage that should be used to start sending the response payload */ void writeData(HttpCarbonMessage carbonMessage, T entity, String mediaType, int chunkSize, HttpCarbonMessage responder); } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/entitywriter/EntityWriterRegistry.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.internal.entitywriter; import org.wso2.msf4j.internal.ClassComparator; import java.util.Map; import java.util.Optional; import java.util.TreeMap; /** * Registry that stores entity writers for different entity types. */ public class EntityWriterRegistry { private static final EntityWriter DEFAULT_ENTITY_WRITER = new ObjectEntityWriter(); private static final Map writers = new TreeMap<>(new ClassComparator()); static { registerEntityWriter(new FileEntityWriter()); registerEntityWriter(new InputStreamEntityWriter()); registerEntityWriter(new StreamingOutputEntityWriter()); } private EntityWriterRegistry() { } /** * Register an entity writer. * * @param entityWriter entity writer for a specific entity type */ private static void registerEntityWriter(EntityWriter entityWriter) { writers.put(entityWriter.getType(), entityWriter); } /** * Get entity writer for a given type. * * @param type type of the entity to be written to a carbon message * @return entity writer */ public static EntityWriter getEntityWriter(Class type) { return writers.entrySet(). stream(). filter(entry -> entry.getKey().isAssignableFrom(type)). findFirst(). flatMap(entry -> Optional.ofNullable(entry.getValue())).orElse(DEFAULT_ENTITY_WRITER); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/entitywriter/FileEntityWriter.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.internal.entitywriter; import io.netty.buffer.Unpooled; import io.netty.handler.codec.http.DefaultHttpContent; import io.netty.handler.codec.http.DefaultLastHttpContent; import io.netty.handler.codec.http.HttpHeaderNames; import org.apache.commons.io.FilenameUtils; import org.wso2.msf4j.Response; import org.wso2.msf4j.internal.mime.MimeMapper; import org.wso2.msf4j.internal.mime.MimeMappingException; import org.wso2.transport.http.netty.contract.exceptions.ServerConnectorException; import org.wso2.transport.http.netty.message.HttpCarbonMessage; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import javax.ws.rs.core.MediaType; /** * EntityWriter for entity of type File. */ public class FileEntityWriter implements EntityWriter { public static final int DEFAULT_CHUNK_SIZE = 1024; /** * Supported entity type. */ @Override public Class getType() { return File.class; } /** * Write the entity to the carbon message. */ @Override public void writeData(HttpCarbonMessage httpCarbonMessage, File file, String mediaType, int chunkSize, HttpCarbonMessage responder) { if (mediaType == null || mediaType.equals(MediaType.WILDCARD)) { try { mediaType = MimeMapper.getMimeType(FilenameUtils.getExtension(file.getName())); } catch (MimeMappingException e) { mediaType = MediaType.WILDCARD; } } try { FileChannel fileChannel = new FileInputStream(file).getChannel(); if (chunkSize == Response.NO_CHUNK || chunkSize == Response.DEFAULT_CHUNK_SIZE) { chunkSize = DEFAULT_CHUNK_SIZE; } httpCarbonMessage.setHeader(HttpHeaderNames.TRANSFER_ENCODING.toString(), CHUNKED); httpCarbonMessage.setHeader(HttpHeaderNames.CONTENT_TYPE.toString(), mediaType); try { responder.respond(httpCarbonMessage); } catch (ServerConnectorException e) { throw new RuntimeException("Error while sending the response.", e); } ByteBuffer buffer = ByteBuffer.allocate(chunkSize); while (fileChannel.read(buffer) != -1) { buffer.flip(); httpCarbonMessage.addHttpContent(new DefaultHttpContent(Unpooled.wrappedBuffer(buffer))); buffer = ByteBuffer.allocate(chunkSize); } fileChannel.close(); httpCarbonMessage.addHttpContent(new DefaultLastHttpContent()); } catch (IOException e) { throw new RuntimeException("Error occurred while reading from file", e); } } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/entitywriter/InputStreamEntityWriter.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.internal.entitywriter; import io.netty.buffer.Unpooled; import io.netty.handler.codec.http.DefaultHttpContent; import io.netty.handler.codec.http.DefaultLastHttpContent; import io.netty.handler.codec.http.HttpHeaderNames; import org.wso2.msf4j.Response; import org.wso2.transport.http.netty.contract.exceptions.ServerConnectorException; import org.wso2.transport.http.netty.message.HttpCarbonMessage; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.util.concurrent.Executors; /** * EntityWriter for entity of type InputStream. */ public class InputStreamEntityWriter implements EntityWriter { public static final int DEFAULT_CHUNK_SIZE = 1024; /** * Supported entity type. */ @Override public Class getType() { return InputStream.class; } /** * Write the entity to the carbon message. */ @Override public void writeData(HttpCarbonMessage carbonMessage, InputStream ipStream, String mediaType, int chunkSize, HttpCarbonMessage responder) { try { if (chunkSize == Response.NO_CHUNK || chunkSize == Response.DEFAULT_CHUNK_SIZE) { chunkSize = DEFAULT_CHUNK_SIZE; } carbonMessage.setHeader(HttpHeaderNames.TRANSFER_ENCODING.toString(), CHUNKED); carbonMessage.setHeader(HttpHeaderNames.CONTENT_TYPE.toString(), mediaType); Executors.newSingleThreadExecutor().execute(() -> { try { responder.respond(carbonMessage); } catch (ServerConnectorException e) { throw new RuntimeException("Error while sending the response.", e); } }); byte[] data = new byte[chunkSize]; int len; while ((len = ipStream.read(data)) != -1) { carbonMessage.addHttpContent(new DefaultHttpContent(Unpooled.wrappedBuffer(ByteBuffer.wrap(data, 0, len)))); data = new byte[chunkSize]; } carbonMessage.addHttpContent(new DefaultLastHttpContent()); } catch (IOException e) { throw new RuntimeException("Error occurred while reading from InputStream", e); } finally { if (ipStream != null) { try { ipStream.close(); } catch (IOException ignore) { } } } } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/entitywriter/ObjectEntityWriter.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.internal.entitywriter; import io.netty.buffer.Unpooled; import io.netty.handler.codec.http.DefaultLastHttpContent; import io.netty.handler.codec.http.HttpHeaderNames; import org.wso2.msf4j.Response; import org.wso2.msf4j.internal.beanconversion.BeanConverter; import org.wso2.transport.http.netty.contract.exceptions.ServerConnectorException; import org.wso2.transport.http.netty.message.HttpCarbonMessage; import java.nio.ByteBuffer; import javax.ws.rs.core.MediaType; /** * EntityWriter for entity of type Object. */ public class ObjectEntityWriter implements EntityWriter { /** * Supported entity type. */ @Override public Class getType() { return Object.class; } /** * Write the entity to the carbon message. */ @Override public void writeData(HttpCarbonMessage carbonMessage, Object entity, String mediaType, int chunkSize, HttpCarbonMessage responder) { mediaType = (mediaType != null) ? mediaType : MediaType.WILDCARD; ByteBuffer byteBuffer = BeanConverter.getConverter(mediaType).convertToMedia(entity); carbonMessage.addHttpContent(new DefaultLastHttpContent(Unpooled.wrappedBuffer(byteBuffer))); if (chunkSize == Response.NO_CHUNK) { carbonMessage.setHeader(HttpHeaderNames.CONTENT_LENGTH.toString(), String.valueOf(byteBuffer.remaining())); } else { carbonMessage.setHeader(HttpHeaderNames.TRANSFER_ENCODING.toString(), CHUNKED); } carbonMessage.setHeader(HttpHeaderNames.CONTENT_TYPE.toString(), mediaType); try { responder.respond(carbonMessage); } catch (ServerConnectorException e) { throw new RuntimeException("Error while sending the response.", e); } } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/entitywriter/StreamingOutputEntityWriter.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.internal.entitywriter; import io.netty.handler.codec.http.HttpHeaderNames; import org.wso2.transport.http.netty.contract.exceptions.ServerConnectorException; import org.wso2.transport.http.netty.message.HttpCarbonMessage; import org.wso2.transport.http.netty.message.HttpMessageDataStreamer; import java.io.IOException; import java.io.OutputStream; import java.util.concurrent.Executors; import javax.ws.rs.core.StreamingOutput; /** * EntityWriter for entity of type {@link javax.ws.rs.core.StreamingOutput}. */ public class StreamingOutputEntityWriter implements EntityWriter { /** * Supported entity type. */ @Override public Class getType() { return StreamingOutput.class; } /** * Write the entity to the carbon message. */ @Override public void writeData(HttpCarbonMessage carbonMessage, StreamingOutput output, String mediaType, int chunkSize, HttpCarbonMessage responder) { try { carbonMessage.setHeader(HttpHeaderNames.CONTENT_TYPE.toString(), mediaType); carbonMessage.setHeader(HttpHeaderNames.TRANSFER_ENCODING.toString(), CHUNKED); Executors.newSingleThreadExecutor().execute(() -> { try { responder.respond(carbonMessage); } catch (ServerConnectorException e) { throw new RuntimeException("Error while sending the response.", e); } }); OutputStream outputStream = new HttpMessageDataStreamer(carbonMessage).getOutputStream(); output.write(outputStream); outputStream.close(); } catch (IOException e) { throw new RuntimeException("Error occurred while streaming output", e); } } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/mime/MimeMapper.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.internal.mime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStream; import java.util.Properties; /** * This class lazily loads the mime-map.properties file * and maps file extension to mime type using the file. */ public class MimeMapper { private static Properties mimeMap = null; private static final Logger log = LoggerFactory.getLogger(MimeMapper.class); private static void loadMimeMap() throws IOException { mimeMap = new Properties(); InputStream inputStream = MimeMapper.class.getClassLoader() .getResourceAsStream("mime-map.properties"); if (inputStream != null) { mimeMap.load(inputStream); try { inputStream.close(); } catch (IOException e) { log.warn("Could not close input stream", e); } } } public static String getMimeType(String extension) throws MimeMappingException { try { if (mimeMap == null) { loadMimeMap(); } } catch (IOException e) { throw new MimeMappingException("Could not load mime map", e); } String mimeType = mimeMap.getProperty(extension); if (mimeType == null) { throw new MimeMappingException("Could not find mime type"); } return mimeType; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/mime/MimeMappingException.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.internal.mime; /** * This exception will be thrown in case a * file extension cannot be mapped to a mime type. */ public class MimeMappingException extends RuntimeException { public MimeMappingException(String msg) { super(msg); } public MimeMappingException(Exception cause) { super(cause); } public MimeMappingException(String msg, Exception cause) { super(msg, cause); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/router/HandlerException.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.internal.router; import org.wso2.msf4j.util.HttpUtil; import org.wso2.transport.http.netty.message.HttpCarbonMessage; import javax.ws.rs.core.Response; /** * Creating Http Response for Exception messages. */ public class HandlerException extends Exception { private final Response.Status failureStatus; private final String message; public HandlerException(Response.Status failureStatus, String message) { super(message); this.failureStatus = failureStatus; this.message = message; } public HandlerException(Response.Status failureStatus, String message, Throwable cause) { super(message, cause); this.failureStatus = failureStatus; this.message = message; } public HttpCarbonMessage getFailureResponse() { return HttpUtil.createTextResponse(failureStatus.getStatusCode(), message); } public Response.Status getFailureStatus() { return failureStatus; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/router/HttpMethodInfo.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.internal.router; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.msf4j.HttpStreamHandler; import org.wso2.msf4j.HttpStreamer; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import org.wso2.msf4j.interceptor.InterceptorExecutor; import org.wso2.msf4j.internal.MSF4JConstants; import org.wso2.msf4j.internal.MicroservicesRegistryImpl; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import javax.ws.rs.Path; import javax.ws.rs.core.MultivaluedMap; import static org.wso2.msf4j.internal.router.Util.GROUP_PATTERN; import static org.wso2.msf4j.internal.router.Util.GROUP_PATTERN_REGEX; import static org.wso2.msf4j.internal.router.Util.WILD_CARD_PATTERN; /** * HttpMethodInfo is a helper class having state information about the http handler method to be invoked, the handler * and arguments required for invocation by the Dispatcher. RequestRouter populates this class and stores in its * context as attachment. */ public class HttpMethodInfo { private final Method method; private final Object handler; private final Object[] args; private MultivaluedMap formParameters = null; private Response responder; private HttpStreamHandler httpStreamHandler; private static final Logger log = LoggerFactory.getLogger(HttpMethodInfo.class); private static final String DECLARING_CLASS_LIST_CONSTANT = "declaringClassList"; private static final String RESOURCE_METHOD_LIST_CONSTANT = "resourceMethodList"; /** * Construct HttpMethodInfo object for a handler * method that does not support streaming. * * @param method handler method * @param handler object of the handler method * @param args method arguments array * @param responder responder object */ public HttpMethodInfo(Method method, Object handler, Object[] args, MultivaluedMap formParameters, Response responder) { this.method = method; this.handler = handler; this.args = Arrays.copyOf(args, args.length); this.formParameters = formParameters; this.responder = responder; } /** * Construct HttpMethodInfo object for a streaming * supported handler method. * * @param method handler method * @param handler object of the handler method * @param args method arguments array * @param responder responder object * @param httpStreamer streaming handler * @throws HandlerException throws when HttpMethodInfo construction is unsuccessful */ public HttpMethodInfo(Method method, Object handler, Object[] args, MultivaluedMap formParameters, Response responder, HttpStreamer httpStreamer) throws HandlerException { this(method, handler, args, formParameters, responder); if (!method.getReturnType().equals(Void.TYPE)) { throw new HandlerException(javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR, "Resource method should be void if it accepts chunked requests"); } try { method.invoke(handler, args); } catch (InvocationTargetException e) { throw new HandlerException(javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR, "Resource method invocation failed", e.getTargetException()); } catch (IllegalAccessException e) { throw new HandlerException(javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR, "Resource method invocation access failed", e); } httpStreamHandler = httpStreamer.getHttpStreamHandler(); if (httpStreamHandler == null) { throw new HandlerException(javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR, "Streaming unsupported"); } httpStreamHandler.init(this.responder); } /** * Calls the http resource method. * * @param destination matching Destinations for the route * @param request original request * @param httpMethodInfo http method information * @param microservicesRegistry micro-services registry * @throws Exception if error occurs while invoking the resource method */ public void invoke(PatternPathRouter.RoutableDestination destination, Request request, HttpMethodInfo httpMethodInfo, MicroservicesRegistryImpl microservicesRegistry) throws Exception { request.setProperty(DECLARING_CLASS_LIST_CONSTANT, new ArrayList>()); request.setProperty(RESOURCE_METHOD_LIST_CONSTANT, new ArrayList()); ImmutablePair returnVal = invokeResource(destination, httpMethodInfo, request, microservicesRegistry, false); // Execute method level interceptors of sub-resources and resources (first in - last out order) if (returnVal.getFirst() && InterceptorExecutor.executeMethodResponseInterceptorsForMethods(request, httpMethodInfo.responder, (ArrayList) request.getProperty(RESOURCE_METHOD_LIST_CONSTANT)) // Execute class level interceptors of sub-resources and resources (first in - last out order) && InterceptorExecutor.executeClassResponseInterceptorsForClasses(request, httpMethodInfo.responder, (ArrayList>) request.getProperty(DECLARING_CLASS_LIST_CONSTANT)) // Execute global interceptors && InterceptorExecutor.executeGlobalResponseInterceptors(microservicesRegistry, request, httpMethodInfo.responder)) { responder.setEntity(returnVal.getSecond()); } responder.send(); } /** * Execute http resource. * * @param destination matching Destinations for the route * @param httpMethodInfo http method information * @param request original request * @param microservicesRegistry micro-services registry * @return value returned from executing the sub-resource method * @throws Exception if error occurs while invoking the sub-resource method */ private ImmutablePair invokeResource( PatternPathRouter.RoutableDestination destination, HttpMethodInfo httpMethodInfo, Request request, MicroservicesRegistryImpl microservicesRegistry, boolean isSubResource) throws Exception { Class clazz = httpMethodInfo.method.getDeclaringClass(); request.setProperty(MSF4JConstants.METHOD_PROPERTY_NAME, httpMethodInfo.method); // Required for analytics // Execute global request interceptors if not a sub-resource (global interceptors will only be executed // at the parent resource if (!isSubResource && !InterceptorExecutor .executeGlobalRequestInterceptors(microservicesRegistry, request, httpMethodInfo.responder)) { return ImmutablePair.of(false, new Object()); } // Execute class level request interceptors if (InterceptorExecutor.executeClassLevelRequestInterceptors(request, httpMethodInfo.responder, clazz) // Execute method level request interceptors && InterceptorExecutor.executeMethodLevelRequestInterceptors(request, httpMethodInfo.responder, httpMethodInfo.method)) { Object returnedValue = httpMethodInfo.method.invoke(httpMethodInfo.handler, httpMethodInfo.args); // Class level response interceptors - add to the top of the execution stack ((ArrayList>) request.getProperty(DECLARING_CLASS_LIST_CONSTANT)).add(0, clazz); // Method level response interceptors - add to the top of the execution stack ((ArrayList) request.getProperty(RESOURCE_METHOD_LIST_CONSTANT)).add(0, httpMethodInfo.method); return httpMethodInfo .invokeSubResource(request, destination, returnedValue, microservicesRegistry); } return ImmutablePair.of(false, new Object()); } /** * Calls http sub-resource method. * * @param request original request * @param destination matching Destinations for the route * @param returnVal value returned from the method execution * @param microservicesRegistry micro-services registry * @return value returned from executing the sub-resource method * @throws Exception if error occurs while invoking the sub-resource method */ private ImmutablePair invokeSubResource( Request request, PatternPathRouter.RoutableDestination destination, Object returnVal, MicroservicesRegistryImpl microservicesRegistry) throws Exception { // If this is a sub resource locator need to find and invoke the correct method if (destination.getDestination().isSubResourceLocator()) { String requestPath = request.getUri(); if (requestPath.endsWith("/")) { requestPath = requestPath.substring(0, requestPath.length() - 1); } if (requestPath.contains("?")) { requestPath = requestPath.substring(0, requestPath.indexOf("?")); } if (!destination.getDestination().isSubResourceScanned()) { // Scan the return object class to search the methods for (Method method : returnVal.getClass().getMethods()) { if (Modifier.isPublic(method.getModifiers()) && Util.isHttpMethodAvailable(method)) { String relativePath = ""; if (method.getAnnotation(Path.class) != null) { relativePath = method.getAnnotation(Path.class).value(); } if (relativePath.startsWith("/")) { relativePath = relativePath.substring(1); } String absolutePath = relativePath.isEmpty() ? destination.getDestination().getPath() : String.format("%s/%s", destination.getDestination().getPath(), relativePath); HttpResourceModel resourceModel = new HttpResourceModel(absolutePath, method, returnVal, false); resourceModel.setParent(destination.getDestination()); SubresourceKey subResKey = new SubresourceKey(absolutePath, method.getDeclaringClass(), resourceModel.getHttpMethod()); destination.getDestination().addSubResources(subResKey, resourceModel); } else if (Modifier.isPublic(method.getModifiers()) && method.getAnnotation(Path.class) != null) { // Sub resource locator method String relativePath = method.getAnnotation(Path.class).value(); if (relativePath.startsWith("/")) { relativePath = relativePath.substring(1); } String absolutePath = relativePath.isEmpty() ? destination.getDestination().getPath() : String.format("%s/%s", destination.getDestination().getPath(), relativePath); HttpResourceModel resourceModel = new HttpResourceModel(absolutePath, method, returnVal, true); resourceModel.setParent(destination.getDestination()); SubresourceKey subResKey = new SubresourceKey(absolutePath, method.getDeclaringClass(), Collections.emptySet()); destination.getDestination().addSubResources(subResKey, resourceModel); } } destination.getDestination().setSubResourceScanned(true); } String finalRequestPath = requestPath; List> entries = destination.getDestination().getSubResources().entrySet().stream() .filter(e -> e.getValue().getHttpMethod().contains(request.getHttpMethod()) && finalRequestPath.matches(e.getKey().getPath().replaceAll(GROUP_PATTERN, GROUP_PATTERN_REGEX)) && returnVal.getClass().equals(e.getKey().getTypedClass())) .collect(Collectors.toList()); Optional> entry = entries.stream().filter( entryPair -> entryPair.getValue().matchConsumeMediaType(request.getContentType()) && entryPair.getValue().matchProduceMediaType(request.getAcceptTypes())) .findFirst(); HttpResourceModel resourceModel; if (entry.isPresent()) { resourceModel = entry.get().getValue(); } else { // Another sub-resource call String finalRequestPath1 = requestPath; entries = destination.getDestination().getSubResources().entrySet().stream() .filter(e -> finalRequestPath1 .matches(e.getKey().getPath() .replaceAll(GROUP_PATTERN, GROUP_PATTERN_REGEX).concat(".*")) && e.getValue().isSubResourceLocator() && returnVal.getClass().equals(e.getKey().getTypedClass())).collect(Collectors.toList()); entry = entries.stream().filter(entryPair -> entryPair .getValue() .matchConsumeMediaType( request.getContentType()) && entryPair .getValue() .matchProduceMediaType( request.getAcceptTypes())) .findFirst(); if (entry.isPresent()) { resourceModel = entry.get().getValue(); } else { throw new HandlerException(javax.ws.rs.core.Response.Status.NOT_FOUND, String.format("Problem accessing: %s. Reason: Not Found", requestPath)); } } // Process path to get the PathParam values Path declaredAnnotation = resourceModel.getMethod().getDeclaredAnnotation(Path.class); String[] parts = declaredAnnotation.value().split("/"); StringBuilder sb = new StringBuilder(); List groupNames = new ArrayList<>(); for (String part : parts) { Matcher groupMatcher = Pattern.compile(GROUP_PATTERN).matcher(part); if (groupMatcher.matches()) { groupNames.add(Util.stripBraces(part)); sb.append("([^/]+)"); } else if (WILD_CARD_PATTERN.matcher(part).matches()) { sb.append(".*?"); } else { sb.append(part); } sb.append("/"); } if (sb.length() > 0) { sb.setLength(sb.length() - 1); } Map groupNameValues = new HashMap<>(); destination.getGroupNameValues().entrySet().forEach(e -> groupNameValues.put(e.getKey(), e.getValue())); //Get the sub resource path String[] paths = requestPath.split(destination.getDestination().getPath().replaceAll(GROUP_PATTERN, "([^/]+)")); String subResPath = "/"; if (paths.length != 0) { subResPath = paths[1]; } Pattern pattern = Pattern.compile(sb.toString() + ".*"); Matcher matcher = pattern.matcher(subResPath); if (matcher.matches()) { for (int i = 1; i <= matcher.groupCount(); i++) { groupNameValues.putIfAbsent(groupNames.get(i - 1), matcher.group(i)); } } // Invoke the sub-resource method HttpResourceModelProcessor httpSubResourceModelProcessor = new HttpResourceModelProcessor(resourceModel); httpSubResourceModelProcessor.setFormParameters(formParameters); responder.setMediaType( Util.getResponseType(request.getAcceptTypes(), resourceModel.getProducesMediaTypes())); HttpMethodInfo httpMethodInfo = httpSubResourceModelProcessor .buildHttpMethodInfo(request, responder, groupNameValues); PatternPathRouter.RoutableDestination newDestination = new PatternPathRouter.RoutableDestination<>(resourceModel, groupNameValues); return invokeResource(newDestination, httpMethodInfo, request, microservicesRegistry, true); } return ImmutablePair.of(true, returnVal); } /** * If chunk handling is supported provide chunks directly. * * @param chunk chunk content * @throws Exception if error occurs while invoking streaming handlers */ public void chunk(ByteBuffer chunk) throws Exception { try { httpStreamHandler.chunk(chunk); } catch (Throwable e) { log.error("Exception while invoking streaming handlers", e); httpStreamHandler.error(e); throw e; } } /** * If chunk handling is supported end streaming chunks. * * @param isResponseInterceptorsSuccessful have the response interceptors successfully executed * @throws Exception if error occurs while stopping streaming handlers */ public void end(boolean isResponseInterceptorsSuccessful) throws Exception { try { httpStreamHandler.end(); } catch (Throwable e) { log.error("Exception while invoking streaming handlers", e); log.error("Response interceptor execute is successful : " + isResponseInterceptorsSuccessful, e); httpStreamHandler.error(e); throw e; } } /** * Return true if the handler method supports streaming. * * @return boolean true if streaming is supported */ public boolean isStreamingSupported() { return httpStreamHandler != null; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/router/HttpMethodInfoBuilder.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.internal.router; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import java.util.Map; /** * Builder for HttpMethodInfo that will hold HttpResourceModel and * request information in order to process the request when all * data is ready. */ public class HttpMethodInfoBuilder { private HttpResourceModel httpResourceModel; private Request request; private Response responder; private Map groupValues; private HttpMethodInfo httpMethodInfo; /** * Set the associated HttpResourceModel object. * * @param httpResourceModel resource model for the request * @return HttpMethodInfoBuilder object */ public HttpMethodInfoBuilder httpResourceModel(HttpResourceModel httpResourceModel) { this.httpResourceModel = httpResourceModel; return this; } /** * Set the associated Request object. * * @param request Request object * @return HttpMethodInfoBuilder object */ public HttpMethodInfoBuilder httpRequest(Request request) { this.request = request; return this; } /** * Set the associated Response object. * * @param responder Response object * @return HttpMethodInfoBuilder object */ public HttpMethodInfoBuilder httpResponder(Response responder) { this.responder = responder; return this; } /** * Set information of the request that were processed * when searching for the route. * * @param groupValues request info to be set * @return HttpMethodInfoBuilder object */ public HttpMethodInfoBuilder requestInfo(Map groupValues) { this.groupValues = groupValues; return this; } /** * Build HttpMethodInfo instance. * * @return HttpMethodInfo object * @throws HandlerException if error occurs while executing the request */ public HttpMethodInfo build() throws HandlerException { if (httpMethodInfo == null) { httpMethodInfo = (new HttpResourceModelProcessor(httpResourceModel)) .buildHttpMethodInfo(request, responder, groupValues); } return httpMethodInfo; } public HttpResourceModel getHttpResourceModel() { return httpResourceModel; } public Response getResponder() { return responder; } public Request getRequest() { return request; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/router/HttpResourceModel.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.internal.router; import org.wso2.msf4j.HttpStreamer; import org.wso2.msf4j.formparam.FormDataParam; import org.wso2.msf4j.util.Utils; 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.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Function; import javax.annotation.Nullable; import javax.ws.rs.Consumes; import javax.ws.rs.CookieParam; import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.HEAD; import javax.ws.rs.HeaderParam; import javax.ws.rs.HttpMethod; import javax.ws.rs.OPTIONS; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; /** * HttpResourceModel contains information needed to handle Http call for a given path. Used as a destination in * {@code PatternPathRouter} to route URI paths to right Http end points. */ public final class HttpResourceModel { private static final Set> SUPPORTED_PARAM_ANNOTATIONS; static { Set> supportedAnnotation = new HashSet<>(); supportedAnnotation.add(PathParam.class); supportedAnnotation.add(QueryParam.class); supportedAnnotation.add(HeaderParam.class); supportedAnnotation.add(Context.class); supportedAnnotation.add(FormParam.class); supportedAnnotation.add(FormDataParam.class); supportedAnnotation.add(CookieParam.class); SUPPORTED_PARAM_ANNOTATIONS = Collections.unmodifiableSet(supportedAnnotation); } private static final String[] ANY_MEDIA_TYPE = new String[]{"*/*"}; private static final int STREAMING_REQ_UNKNOWN = 0, STREAMING_REQ_SUPPORTED = 1, STREAMING_REQ_UNSUPPORTED = 2; private final Set httpMethods; private final String path; private final Method method; private final Object handler; private final List> paramInfoList; private List consumesMediaTypes; private List producesMediaTypes; private int isStreamingReqSupported = STREAMING_REQ_UNKNOWN; private Map subResources = new HashMap<>(); private boolean isSubResourceLocator; private boolean isSubResourceScanned; private HttpResourceModel parent; /** * If this is a subresource locator, get the parent of this Model. * @return HttpResourceModel parent model. */ public HttpResourceModel getParent() { return parent; } /** * If this is a subresource locator, set the parent Model. * @param parent HttpResourceModel of this. */ public void setParent(HttpResourceModel parent) { this.parent = parent; consumesMediaTypes = parseConsumesMediaTypes(); producesMediaTypes = parseProducesMediaTypes(); } /** * Get the sub resource locators of this model. * @return Map of subresource locators of this. */ public Map getSubResources() { return subResources; } /** * Set the sub resource locators of this model. * @param subResources Map of sub resource locators. */ public void setSubResources(Map subResources) { this.subResources = subResources; } public void addSubResources(SubresourceKey subresourceKey, HttpResourceModel httpResourceModel) { subResources.put(subresourceKey, httpResourceModel); } /** * Set if this model is already scanned for sub resource locators. * @param subResourceScanned boolean indicate whether this already scanned for sub resource locators. */ public void setSubResourceScanned(boolean subResourceScanned) { isSubResourceScanned = subResourceScanned; } /** * Check if this model is already scanned for sub resource locators. * @return boolean whether this model already scanned for sub resources. */ public boolean isSubResourceScanned() { return isSubResourceScanned; } /** * Construct a resource model with HttpMethod, method that handles httprequest, Object that contains the method. * * @param path path associated with this model. * @param method handler that handles the http request. * @param handler instance {@code HttpHandler}. * @param isSubResourceLocator indicate if this is a subresource locator method */ public HttpResourceModel(String path, Method method, Object handler, boolean isSubResourceLocator) { this.httpMethods = getHttpMethods(method); this.path = path; this.method = method; this.handler = handler; this.isSubResourceLocator = isSubResourceLocator; this.paramInfoList = makeParamInfoList(method); consumesMediaTypes = parseConsumesMediaTypes(); producesMediaTypes = parseProducesMediaTypes(); } private List parseConsumesMediaTypes() { String[] consumesMediaTypeArr = method.isAnnotationPresent(Consumes.class) ? method.getAnnotation(Consumes.class).value() : handler.getClass().isAnnotationPresent(Consumes.class) ? handler.getClass().getAnnotation(Consumes.class).value() : parent == null ? ANY_MEDIA_TYPE : new String[] {}; if (parent != null && consumesMediaTypeArr.length == 0) { HttpResourceModel tmpParent = parent; while (tmpParent.getConsumesMediaTypes().size() != 0 && tmpParent.getParent() != null) { tmpParent = parent.getParent(); } consumesMediaTypeArr = tmpParent.getMethod().isAnnotationPresent(Consumes.class) ? tmpParent.getMethod().getAnnotation(Consumes.class).value() : handler.getClass().isAnnotationPresent(Consumes.class) ? handler.getClass().getAnnotation(Consumes.class).value() : ANY_MEDIA_TYPE; } return Arrays.asList(consumesMediaTypeArr); } private List parseProducesMediaTypes() { String[] producesMediaTypeArr = method.isAnnotationPresent(Produces.class) ? method.getAnnotation(Produces.class).value() : handler.getClass().isAnnotationPresent(Produces.class) ? handler.getClass().getAnnotation(Produces.class).value() : parent == null ? ANY_MEDIA_TYPE : new String[] {}; if (parent != null && producesMediaTypeArr.length == 0) { HttpResourceModel tmpParent = parent; while (tmpParent.getProducesMediaTypes().size() != 0 && tmpParent.getParent() != null) { tmpParent = parent.getParent(); } producesMediaTypeArr = tmpParent.getMethod().isAnnotationPresent(Produces.class) ? tmpParent.getMethod().getAnnotation(Produces.class).value() : handler.getClass().isAnnotationPresent(Produces.class) ? handler.getClass().getAnnotation(Produces.class).value() : ANY_MEDIA_TYPE; } return Arrays.asList(producesMediaTypeArr); } public boolean matchConsumeMediaType(String consumesMediaType) { return consumesMediaType == null || consumesMediaType.isEmpty() || consumesMediaType.equals("*/*") || this.consumesMediaTypes.contains("*/*") || this.consumesMediaTypes.contains(consumesMediaType); } public boolean matchProduceMediaType(List producesMediaTypes) { return producesMediaTypes == null || producesMediaTypes.contains("*/*") || this.producesMediaTypes.contains("*/*") || this.producesMediaTypes .stream().filter (producesMediaTypes::contains).findAny().isPresent(); } /** * @return httpMethods. */ public Set getHttpMethod() { return httpMethods; } /** * @return path associated with this model. */ public String getPath() { return path; } /** * @return handler method that handles an http end-point. */ public Method getMethod() { return method; } /** * @return instance of {@code HttpHandler}. */ public Object getHttpHandler() { return handler; } /** * Indicate this method as subresource locator method. * @param subResourceLocator boolean value to set method */ public void setSubResourceLocator(boolean subResourceLocator) { isSubResourceLocator = subResourceLocator; } /** * Return true if this method is a subresource locator method. * * @return boolean true if this method is a subresource locator method. */ public boolean isSubResourceLocator() { return isSubResourceLocator; } @Override public String toString() { return Utils.toString(this, new String[] { "httpMethods", "path", "method", "handler" }); } /** * Fetches the HttpMethod from annotations and returns String representation of HttpMethod. * Return emptyString if not present. * * @param method Method handling the http request. * @return String representation of HttpMethod from annotations or emptyString as a default. */ private Set getHttpMethods(Method method) { Set httpMethods = new HashSet(); boolean isSubResourceLocator = true; if (method.isAnnotationPresent(GET.class)) { httpMethods.add(HttpMethod.GET); isSubResourceLocator = false; } if (method.isAnnotationPresent(PUT.class)) { httpMethods.add(HttpMethod.PUT); isSubResourceLocator = false; } if (method.isAnnotationPresent(POST.class)) { httpMethods.add(HttpMethod.POST); isSubResourceLocator = false; } if (method.isAnnotationPresent(DELETE.class)) { httpMethods.add(HttpMethod.DELETE); isSubResourceLocator = false; } if (method.isAnnotationPresent(HEAD.class)) { httpMethods.add(HttpMethod.HEAD); isSubResourceLocator = false; } if (method.isAnnotationPresent(OPTIONS.class)) { httpMethods.add(HttpMethod.OPTIONS); isSubResourceLocator = false; } // If this is a sub resource locator need to add all the method designator if (isSubResourceLocator) { httpMethods.add(HttpMethod.GET); httpMethods.add(HttpMethod.POST); httpMethods.add(HttpMethod.PUT); httpMethods.add(HttpMethod.DELETE); httpMethods.add(HttpMethod.HEAD); httpMethods.add(HttpMethod.OPTIONS); } return Collections.unmodifiableSet(httpMethods); } /** * Gathers all parameters' annotations for the given method, starting from the third parameter. */ private List> makeParamInfoList(Method method) { List> paramInfoList = new ArrayList<>(); Type[] paramTypes = method.getGenericParameterTypes(); Annotation[][] paramAnnotations = method.getParameterAnnotations(); for (int i = 0; i < paramAnnotations.length; i++) { Annotation[] annotations = paramAnnotations[i]; //Can have only one from @PathParam, @QueryParam, @HeaderParam or @Context. if (Utils.getIntersection(SUPPORTED_PARAM_ANNOTATIONS, Collections.unmodifiableSet(new HashSet(Arrays.asList(annotations)))) > 1) { throw new IllegalArgumentException( String.format("Must have exactly one annotation from %s for parameter %d in method %s", SUPPORTED_PARAM_ANNOTATIONS, i, method)); } Annotation annotation = null; Type parameterType = paramTypes[i]; Function converter = null; String defaultVal = null; for (Annotation annotation0 : annotations) { annotation = annotation0; Class annotationType = annotation.annotationType(); if (PathParam.class.isAssignableFrom(annotationType)) { converter = ParamConvertUtils.createPathParamConverter(parameterType); } else if (QueryParam.class.isAssignableFrom(annotationType)) { converter = ParamConvertUtils.createQueryParamConverter(parameterType); } else if (FormParam.class.isAssignableFrom(annotationType)) { converter = ParamConvertUtils.createFormParamConverter(parameterType); } else if (FormDataParam.class.isAssignableFrom(annotationType)) { converter = ParamConvertUtils.createFormDataParamConverter(parameterType); } else if (HeaderParam.class.isAssignableFrom(annotationType)) { converter = ParamConvertUtils.createHeaderParamConverter(parameterType); } else if (CookieParam.class.isAssignableFrom(annotationType)) { converter = ParamConvertUtils.createCookieParamConverter(parameterType); } else if (DefaultValue.class.isAssignableFrom(annotationType)) { defaultVal = ((DefaultValue) annotation).value(); } } ParameterInfo parameterInfo = ParameterInfo.create(parameterType, annotation, defaultVal, converter); paramInfoList.add(parameterInfo); } return Collections.unmodifiableList(paramInfoList); } public boolean isStreamingReqSupported() { if (isStreamingReqSupported == STREAMING_REQ_SUPPORTED) { return true; } else if (isStreamingReqSupported == STREAMING_REQ_UNSUPPORTED) { return false; } else if (paramInfoList.stream().filter(parameterInfo -> parameterInfo .getParameterType().equals(HttpStreamer.class)) .findAny().isPresent()) { isStreamingReqSupported = STREAMING_REQ_SUPPORTED; return true; } else { isStreamingReqSupported = STREAMING_REQ_UNSUPPORTED; return false; } } public List> getParamInfoList() { return paramInfoList; } public List getConsumesMediaTypes() { return consumesMediaTypes; } public List getProducesMediaTypes() { return producesMediaTypes; } /** * A container class to hold information about a handler method parameters. * @param type of parameter */ public static final class ParameterInfo { private final Annotation annotation; private final Function converter; private final Type parameterType; private final String defaultVal; private ParameterInfo(Type parameterType, Annotation annotation, String defaultVal, @Nullable Function converter) { this.parameterType = parameterType; this.annotation = annotation; this.defaultVal = defaultVal; this.converter = converter; } static ParameterInfo create(Type parameterType, Annotation annotation, String defaultVal, @Nullable Function converter) { return new ParameterInfo<>(parameterType, annotation, defaultVal, converter); } @SuppressWarnings("unchecked") V getAnnotation() { return (V) annotation; } public Type getParameterType() { return parameterType; } public String getDefaultVal() { return defaultVal; } Object convert(T input) { return (converter == null) ? null : converter.apply(input); } public Function getConverter() { return converter; } } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/router/HttpResourceModelProcessor.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.internal.router; import org.apache.commons.io.FileCleaningTracker; import org.apache.commons.io.FileDeleteStrategy; import org.wso2.msf4j.HttpStreamer; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import org.wso2.msf4j.beanconversion.MediaTypeConverter; import org.wso2.msf4j.formparam.FileInfo; import org.wso2.msf4j.formparam.FormDataParam; import org.wso2.msf4j.formparam.FormItem; import org.wso2.msf4j.formparam.FormParamIterator; import org.wso2.msf4j.formparam.exception.FormUploadException; import org.wso2.msf4j.formparam.util.StreamUtil; import org.wso2.msf4j.internal.beanconversion.BeanConverter; import org.wso2.msf4j.util.QueryStringDecoderUtil; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import javax.ws.rs.CookieParam; import javax.ws.rs.FormParam; import javax.ws.rs.HeaderParam; import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.MultivaluedMap; /** * This class is responsible for processing the HttpResourceModel * when a HTTP request arrives. */ public class HttpResourceModelProcessor { private final HttpResourceModel httpResourceModel; private HttpStreamer httpStreamer; private MultivaluedMap formParameters = null; private Map formParamContentType = new HashMap<>(); private static Path tempRepoPath = Paths.get(System.getProperty("java.io.tmpdir"), "msf4jtemp"); private Path tmpPathForRequest; // Temp File cleaning thread private static FileCleaningTracker fileCleaningTracker = new FileCleaningTracker(); private static final String FILEINFO_POSTFIX = "file.info"; public HttpResourceModelProcessor(HttpResourceModel httpResourceModel) { this.httpResourceModel = httpResourceModel; } /** * Build an HttpMethodInfo object to dispatch the request. * * @param request HttpRequest to be handled. * @param responder HttpResponder to write the response. * @param groupValues Values needed for the invocation. * @return HttpMethodInfo * @throws HandlerException If an error occurs */ @SuppressWarnings("unchecked") public HttpMethodInfo buildHttpMethodInfo(Request request, Response responder, Map groupValues) throws HandlerException { try { //Setup args for reflection call List> paramInfoList = httpResourceModel.getParamInfoList(); Object[] args = new Object[paramInfoList.size()]; int idx = 0; for (HttpResourceModel.ParameterInfo paramInfo : paramInfoList) { if (paramInfo.getAnnotation() != null) { Class annotationType = paramInfo.getAnnotation().annotationType(); if (PathParam.class.isAssignableFrom(annotationType)) { args[idx] = getPathParamValue((HttpResourceModel.ParameterInfo) paramInfo, groupValues); } else if (QueryParam.class.isAssignableFrom(annotationType)) { args[idx] = getQueryParamValue((HttpResourceModel.ParameterInfo>) paramInfo, request.getUri()); } else if (HeaderParam.class.isAssignableFrom(annotationType)) { args[idx] = getHeaderParamValue((HttpResourceModel.ParameterInfo>) paramInfo, request); } else if (CookieParam.class.isAssignableFrom(annotationType)) { args[idx] = getCookieParamValue((HttpResourceModel.ParameterInfo) paramInfo, request); } else if (Context.class.isAssignableFrom(annotationType)) { args[idx] = getContextParamValue((HttpResourceModel.ParameterInfo) paramInfo, request, responder); } else if (FormParam.class.isAssignableFrom(annotationType)) { args[idx] = getFormParamValue((HttpResourceModel.ParameterInfo>) paramInfo, request); } else if (FormDataParam.class.isAssignableFrom(annotationType)) { args[idx] = getFormDataParamValue((HttpResourceModel.ParameterInfo>) paramInfo, request); } else { createObject(request, args, idx, paramInfo); } } else { // If an annotation is not present the parameter is considered a // request body data parameter createObject(request, args, idx, paramInfo); } idx++; } if (httpStreamer == null) { return new HttpMethodInfo(httpResourceModel.getMethod(), httpResourceModel.getHttpHandler(), args, formParameters, responder); } else { return new HttpMethodInfo(httpResourceModel.getMethod(), httpResourceModel.getHttpHandler(), args, formParameters, responder, httpStreamer); } } catch (Throwable e) { throw new HandlerException(javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR, String.format("Error in executing request: %s %s", request.getHttpMethod(), request.getUri()), e); } } private void createObject(Request request, Object[] args, int idx, HttpResourceModel.ParameterInfo paramInfo) throws IOException { try (InputStream inputStream = request.getMessageContentStream()) { Type paramType = paramInfo.getParameterType(); args[idx] = BeanConverter.getConverter((request.getContentType() != null) ? request.getContentType() : MediaType.WILDCARD).convertToObject(inputStream, paramType); } } private Object getFormDataParamValue(HttpResourceModel.ParameterInfo> paramInfo, Request request) throws FormUploadException, IOException { Type paramType = paramInfo.getParameterType(); FormDataParam formDataParam = paramInfo.getAnnotation(); if (getFormParameters() == null) { setFormParameters(extractRequestFormParams(request, paramInfo, true)); } List parameter = getParameter(formDataParam.value()); boolean isNotNull = (parameter != null); if (paramInfo.getConverter() != null) { // We need to skip the conversion for java.io.File types and handle special cases if (paramType instanceof ParameterizedType && isNotNull && parameter.get(0).getClass().isAssignableFrom(File.class)) { return parameter; } else if (isNotNull && parameter.get(0).getClass().isAssignableFrom(File.class)) { return parameter.get(0); } else if (MediaType.TEXT_PLAIN.equalsIgnoreCase(formParamContentType.get(formDataParam.value()))) { return paramInfo.convert(parameter); } else if (MediaType.APPLICATION_FORM_URLENCODED.equals(request.getContentType())) { return paramInfo.convert(parameter); } // Beans with string constructor return createBean(parameter, formDataParam, paramType, isNotNull); } // We only support InputStream for a single file. Therefore only get first element from the list if (paramType == InputStream.class && isNotNull && parameter.get(0).getClass().isAssignableFrom(File.class)) { return new FileInputStream((File) parameter.get(0)); } else if (paramType == FileInfo.class) { List fileInfo = getParameter(formDataParam.value() + FILEINFO_POSTFIX); return fileInfo == null ? null : fileInfo.get(0); } // These are beans without having string constructor. Convert using existing BeanConverter return createBean(parameter, formDataParam, paramType, isNotNull); } /** * Extract the form items in the request. * * @param request Request which need to be processed * @param paramInfo of the method * @param addFileInfo if FileInfo object needed to be added to params. In a case of InputStream this should be true * @return MultivaluedMap of form items * @throws IOException if error occurs while processing the multipart/form-data request */ private MultivaluedMap extractRequestFormParams(Request request, HttpResourceModel.ParameterInfo paramInfo, boolean addFileInfo) throws IOException { MultivaluedMap parameters = new MultivaluedHashMap<>(); if (MediaType.MULTIPART_FORM_DATA.equals(request.getContentType())) { FormParamIterator formParamIterator = new FormParamIterator(request); while (formParamIterator.hasNext()) { FormItem item = formParamIterator.next(); String cType = item.getContentType(); if (cType != null && cType.contains(";")) { cType = cType.split(";")[0]; } if (cType == null) { cType = MediaType.TEXT_PLAIN; } boolean isFile = item.getHeaders().getHeader("content-disposition").contains("filename") || MediaType.APPLICATION_OCTET_STREAM.equals(item.getHeaders().getHeader("content-type")); formParamContentType.putIfAbsent(item.getFieldName(), cType); List existingValues = parameters.get(item.getFieldName()); if (existingValues == null) { parameters.put(item.getFieldName(), isFile ? new ArrayList<>(Collections.singletonList(createAndTrackTempFile(item))) : new ArrayList<>(Collections.singletonList(StreamUtil.asString(item.openStream())))); } else { existingValues.add(isFile ? createAndTrackTempFile(item) : StreamUtil.asString(item.openStream())); } if (addFileInfo && isFile) { //Create FileInfo bean to handle InputStream FileInfo fileInfo = new FileInfo(); fileInfo.setFileName(item.getName()); fileInfo.setContentType(item.getContentType()); parameters.putSingle(item.getFieldName() + FILEINFO_POSTFIX, fileInfo); } } } else if (MediaType.APPLICATION_FORM_URLENCODED.equals(request.getContentType())) { try (InputStream inputStream = request.getMessageContentStream()) { String bodyStr = BeanConverter .getConverter((request.getContentType() != null) ? request.getContentType() : MediaType .WILDCARD).convertToObject(inputStream, paramInfo.getParameterType()).toString(); QueryStringDecoderUtil queryStringDecoderUtil = new QueryStringDecoderUtil(bodyStr, false); queryStringDecoderUtil.parameters().entrySet(). forEach(entry -> parameters.put(entry.getKey(), new ArrayList<>(entry.getValue()))); } } return parameters; } private Object createBean(List parameter, FormDataParam formDataParam, Type paramType, boolean isNotNull) { if (isNotNull) { MediaTypeConverter converter = BeanConverter.getConverter(formParamContentType.get(formDataParam.value())); ByteBuffer value = ByteBuffer.wrap(parameter.get(0).toString().getBytes(Charset.defaultCharset())); return converter.convertToObject(value, paramType); } return null; } private File createAndTrackTempFile(FormItem item) throws IOException { if (tmpPathForRequest == null) { if (Files.notExists(tempRepoPath)) { Files.createDirectory(tempRepoPath); } tmpPathForRequest = Files.createTempDirectory(tempRepoPath, "tmp"); } Path path = Paths.get(tmpPathForRequest.toString(), item.getName()); File file = path.toFile(); StreamUtil.copy(item.openStream(), new FileOutputStream(file), true); fileCleaningTracker.track(file, file); fileCleaningTracker.track(tmpPathForRequest.toFile(), file, FileDeleteStrategy.FORCE); return file; } private Object getFormParamValue(HttpResourceModel.ParameterInfo> paramInfo, Request request) throws FormUploadException, IOException { FormParam formParam = paramInfo.getAnnotation(); if (getFormParameters() == null) { MultivaluedMap parameters = new MultivaluedHashMap<>(); if (MediaType.MULTIPART_FORM_DATA.equals(request.getContentType())) { FormParamIterator formParamIterator = new FormParamIterator(request); while (formParamIterator.hasNext()) { FormItem item = formParamIterator.next(); List existingValues = parameters.get(item.getFieldName()); if (existingValues == null) { parameters.put(item.getFieldName(), new ArrayList<>( Collections.singletonList(StreamUtil.asString(item.openStream())))); } else { existingValues.add(StreamUtil.asString(item.openStream())); } } } else if (MediaType.APPLICATION_FORM_URLENCODED.equals(request.getContentType())) { try (InputStream inputStream = request.getMessageContentStream()) { String bodyStr = BeanConverter.getConverter( (request.getContentType() != null) ? request.getContentType() : MediaType.WILDCARD) .convertToObject(inputStream, paramInfo.getParameterType()).toString(); QueryStringDecoderUtil queryStringDecoderUtil = new QueryStringDecoderUtil(bodyStr, false); queryStringDecoderUtil.parameters().entrySet(). forEach(entry -> parameters.put(entry.getKey(), new ArrayList<>(entry.getValue()))); } } setFormParameters(parameters); } List paramValue = getParameter(formParam.value()); if (paramValue == null) { String defaultVal = paramInfo.getDefaultVal(); if (defaultVal != null) { paramValue = Collections.singletonList(defaultVal); } } return paramInfo.convert(paramValue); } @SuppressWarnings("unchecked") private Object getContextParamValue(HttpResourceModel.ParameterInfo paramInfo, Request request, Response responder) throws FormUploadException, IOException { Type paramType = paramInfo.getParameterType(); Object value = null; if (((Class) paramType).isAssignableFrom(Request.class)) { value = request; } else if (((Class) paramType).isAssignableFrom(Response.class)) { value = responder; } else if (((Class) paramType).isAssignableFrom(HttpStreamer.class)) { if (httpStreamer == null) { httpStreamer = new HttpStreamer(); } value = httpStreamer; } else if (((Class) paramType).isAssignableFrom(FormParamIterator.class)) { value = new FormParamIterator(request); } else if (((Class) paramType).isAssignableFrom(MultivaluedMap.class)) { MultivaluedMap listMultivaluedMap = new MultivaluedHashMap<>(); if (MediaType.MULTIPART_FORM_DATA.equals(request.getContentType())) { listMultivaluedMap = extractRequestFormParams(request, paramInfo, false); } else if (MediaType.APPLICATION_FORM_URLENCODED.equals(request.getContentType())) { try (InputStream inputStream = request.getMessageContentStream()) { String bodyStr = BeanConverter.getConverter( (request.getContentType() != null) ? request.getContentType() : MediaType.WILDCARD) .convertToObject(inputStream, paramInfo.getParameterType()).toString(); QueryStringDecoderUtil queryStringDecoderUtil = new QueryStringDecoderUtil(bodyStr, false); MultivaluedMap finalListMultivaluedMap = listMultivaluedMap; queryStringDecoderUtil.parameters().entrySet().forEach(entry -> finalListMultivaluedMap.put(entry .getKey(), new ArrayList(entry.getValue()))); } } value = listMultivaluedMap; } Objects.requireNonNull(value, String.format("Could not resolve parameter %s", paramType.getTypeName())); return value; } @SuppressWarnings("unchecked") private Object getPathParamValue(HttpResourceModel.ParameterInfo info, Map groupValues) { PathParam pathParam = info.getAnnotation(); String value = groupValues.get(pathParam.value()); if (value == null) { String defaultVal = info.getDefaultVal(); if (defaultVal != null) { value = defaultVal; } } Objects.requireNonNull(value, String.format("Could not resolve value for parameter %s", pathParam.value())); return info.convert(value); } @SuppressWarnings("unchecked") private Object getQueryParamValue(HttpResourceModel.ParameterInfo> info, String uri) { QueryParam queryParam = info.getAnnotation(); List values = new QueryStringDecoderUtil(uri).parameters().get(queryParam.value()); if (values == null || values.isEmpty()) { String defaultVal = info.getDefaultVal(); if (defaultVal != null) { values = Collections.singletonList(defaultVal); } } return info.convert(values); } @SuppressWarnings("unchecked") private Object getHeaderParamValue(HttpResourceModel.ParameterInfo> info, Request request) { HeaderParam headerParam = info.getAnnotation(); String headerName = headerParam.value(); String header = request.getHeader(headerName); if (header == null || header.isEmpty()) { String defaultVal = info.getDefaultVal(); if (defaultVal != null) { header = defaultVal; } } return info.convert(Collections.singletonList(header)); } @SuppressWarnings("unchecked") private Object getCookieParamValue(HttpResourceModel.ParameterInfo info, Request request) { CookieParam cookieParam = info.getAnnotation(); String cookieName = cookieParam.value(); String cookieHeader = request.getHeader("Cookie"); if (cookieHeader != null) { String cookieValue = Arrays.stream(cookieHeader.split(";")) .filter(cookie -> cookie.startsWith(cookieName + "=")) .findFirst() .map(cookie -> cookie.substring((cookieName + "=").length())) .orElseGet(info::getDefaultVal); return info.convert(cookieValue); } return null; } /** * @param key parameter name. * @return parameter value of the given key. */ private List getParameter(String key) { return formParameters.get(key); } /** * @return Map of request formParameters */ public Map> getFormParameters() { return formParameters; } /** * Set the request formParameters. * * @param parameters request formParameters */ public void setFormParameters(MultivaluedMap parameters) { this.formParameters = parameters; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/router/ImmutablePair.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.internal.router; import org.wso2.msf4j.util.Utils; import java.util.Arrays; import java.util.Objects; /** * An {@link ImmutablePair} consists of two elements within. The elements once set * in the ImmutablePair cannot be modified. The class itself is final, so that it * cannot be subclassed. This is general norm for creating Immutable classes. * Please note that the {@link ImmutablePair} cannot be modified once set, but the * objects within them can be, so in general it means that if there are mutable objects * within the pair then the pair itself is effectively mutable. * *
 *   ImmutablePair tupleStreamPair= new
 *    ImmutablePair (tuple, identifier);
 *   ...
 *   ...
 *   Tuple t = tupleStreamPair.getFirst();
 *   TupleInputStreamIdentifier identifier = tupleStreamPair.getSecond();
 *   ...
 * 
* * @param type A * @param type B */ final class ImmutablePair { private final A first; private final B second; /** * Constructs a Immutable Pair. * * @param first object in pair * @param second object in pair */ private ImmutablePair(A first, B second) { this.first = first; this.second = second; } public static ImmutablePair of(A first, B second) { return new ImmutablePair<>(first, second); } /** * Returns first object from pair. * * @return first object from pair. */ public A getFirst() { return first; } /** * Return second object from pair. * * @return second object from pair. */ public B getSecond() { return second; } /** * Returns a string representation of {@link ImmutablePair} object. * * @return string representation of this object. */ @Override public String toString() { return Utils.toString(this, new String[] { "first", "second" }); } /** * Returns a hash code value for this object. * * @return hash code value of this object. */ @Override public int hashCode() { return Arrays.hashCode(new ImmutablePair[]{ (ImmutablePair) first, (ImmutablePair) second }); } /** * Returns whether some other object "is equal" to this object. * * @param o reference object with which to compare * @return true if object is the same as the obj argument; false otherwise. */ @Override public boolean equals(Object o) { if (o == null) { return false; } if (!(o instanceof ImmutablePair)) { return false; } ImmutablePair other = (ImmutablePair) o; return Objects.equals(first, other.first) && Objects.equals(second, other.second); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/router/MicroserviceMetadata.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.internal.router; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.msf4j.util.Utils; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.HEAD; import javax.ws.rs.OPTIONS; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.core.Response; /** * MicroserviceMetadata handles the http request. HttpResourceHandler looks up all Jax-rs annotations in classes * and dispatches to appropriate method on receiving requests. */ public final class MicroserviceMetadata { private static final Logger log = LoggerFactory.getLogger(MicroserviceMetadata.class); private final PatternPathRouter patternRouter = PatternPathRouter.create(); /** * Construct HttpResourceHandler. Reads all annotations from all the handler classes and methods passed in, * constructs patternPathRouter which is routable by path to {@code HttpResourceModel} as destination of the route. * * @param services Map of base paths and services. */ public MicroserviceMetadata(Map services) { //Store the services to call init and destroy on all services. services.forEach((basePath, service) -> { for (Method method : service.getClass().getMethods()) { if (method.isAnnotationPresent(PostConstruct.class) || method.isAnnotationPresent(PreDestroy.class)) { continue; } if (Modifier.isPublic(method.getModifiers()) && isHttpMethodAvailable(method)) { String relativePath = ""; if (method.getAnnotation(Path.class) != null) { relativePath = method.getAnnotation(Path.class).value(); } String absolutePath = String.format("%s/%s", basePath, relativePath); patternRouter.add(absolutePath, new HttpResourceModel(absolutePath, method, service, false)); } else if (Modifier.isPublic(method.getModifiers()) && method.getAnnotation(Path.class) != null) { // Sub resource locator method String relativePath = method.getAnnotation(Path.class).value(); if (relativePath.startsWith("/")) { relativePath = relativePath.substring(1); } String absolutePath = String.format("%s/%s", basePath, relativePath); patternRouter.add(absolutePath, new HttpResourceModel(absolutePath, method, service, true)); } else { log.trace("Not adding method {}({}) to path routing like. " + "HTTP calls will not be routed to this method", method.getName(), method.getParameterTypes()); } } }); } /** * Register given service object with the given base path. Path annotion of the service class will be ignore, * instead use the provided base path. * * @param service HttpHandler object * @param basePath Path the handler should be registered */ public void addMicroserviceMetadata(final Object service, String basePath) { //Store the services to call init and destroy on all services. for (Method method : service.getClass().getMethods()) { if (method.isAnnotationPresent(PostConstruct.class) || method.isAnnotationPresent(PreDestroy.class)) { continue; } if (Modifier.isPublic(method.getModifiers()) && isHttpMethodAvailable(method)) { String relativePath = ""; if (method.getAnnotation(Path.class) != null) { relativePath = method.getAnnotation(Path.class).value(); } String absolutePath = String.format("%s/%s", basePath, relativePath); patternRouter.add(absolutePath, new HttpResourceModel(absolutePath, method, service, false)); } else { log.trace("Not adding method {}({}) to path routing like. " + "HTTP calls will not be routed to this method", method.getName(), method.getParameterTypes()); } } } private boolean isHttpMethodAvailable(Method method) { return method.isAnnotationPresent(GET.class) || method.isAnnotationPresent(PUT.class) || method.isAnnotationPresent(POST.class) || method.isAnnotationPresent(DELETE.class) || method.isAnnotationPresent(HEAD.class) || method.isAnnotationPresent(OPTIONS.class); } /** * Get destination resource method to match the arrived request. * 404 if path is not found. 405 if httpMethod does not match what's configured. * 415 if mediatype does not match. * * @param uri request uri * @param httpMethod http method of the request * @param contentTypeHeader content type of the request * @param acceptHeader accept type of the request * @return matching resource method * @throws HandlerException if the method not found or content type mismatch */ public PatternPathRouter .RoutableDestination getDestinationMethod(String uri, String httpMethod, String contentTypeHeader, List acceptHeader) throws HandlerException { try { String path = URI.create(uri).normalize().getPath(); List> routableDestinations = patternRouter.getDestinations(path); List> matchedDestinations = getMatchedDestination(routableDestinations, httpMethod, path); if (!matchedDestinations.isEmpty()) { if (matchedDestinations.size() == 1) { return matchedDestinations.stream().filter(matchedDestination1 -> matchedDestination1.getDestination() .matchConsumeMediaType( contentTypeHeader) && matchedDestination1.getDestination() .matchProduceMediaType( acceptHeader)) .findFirst().get(); } else { return matchedDestinations.stream().filter(matchedDestination1 -> matchedDestination1.getDestination() .matchConsumeMediaType( contentTypeHeader) && matchedDestination1.getDestination() .matchProduceMediaType( acceptHeader)) .filter(destination -> destination.getDestination().getHttpHandler() .getClass() == destination.getDestination().getMethod() .getDeclaringClass()).findFirst().get(); } } else if (!routableDestinations.isEmpty()) { //Found a matching resource but could not find the right HttpMethod so return 405 throw new HandlerException(Response.Status.METHOD_NOT_ALLOWED, uri); } else { throw new HandlerException(Response.Status.NOT_FOUND, String.format("Problem accessing: %s. Reason: Not Found", uri)); } } catch (NoSuchElementException ex) { throw new HandlerException(Response.Status.UNSUPPORTED_MEDIA_TYPE, String.format("Problem accessing: %s. Reason: Unsupported Media Type", uri), ex); } catch (IllegalArgumentException ex) { throw new HandlerException(Response.Status.BAD_REQUEST, String.format("Problem accessing: %s. Reason: Bad Request", uri), ex); } } /** * Get HttpResourceModel which matches the HttpMethod of the request. * * @param routableDestinations List of ResourceModels. * @param targetHttpMethod HttpMethod. * @param requestUri request URI. * @return RoutableDestination that matches httpMethod that needs to be handled. null if there are no matches. */ private List> getMatchedDestination(List> routableDestinations, String targetHttpMethod, String requestUri) { Iterable requestUriParts = Collections.unmodifiableList(Utils.split(requestUri, "/", true)); List> matchedDestinations = new ArrayList<>(routableDestinations.size()); int maxExactMatch = 0; int maxGroupMatch = 0; int maxPatternLength = 0; for (PatternPathRouter.RoutableDestination destination : routableDestinations) { HttpResourceModel resourceModel = destination.getDestination(); int groupMatch = destination.getGroupNameValues().size(); for (String httpMethod : resourceModel.getHttpMethod()) { if (targetHttpMethod.equals(httpMethod)) { int exactMatch = getExactPrefixMatchCount(requestUriParts, Collections .unmodifiableList(Utils.split(resourceModel.getPath(), "/", true))); // When there are multiple matches present, the following precedence order is used - // 1. template path that has highest exact prefix match with the url is chosen. // 2. template path has the maximum groups is chosen. // 3. finally, template path that has the longest length is chosen. if (exactMatch > maxExactMatch) { maxExactMatch = exactMatch; maxGroupMatch = groupMatch; maxPatternLength = resourceModel.getPath().length(); matchedDestinations.clear(); matchedDestinations.add(destination); } else if (exactMatch == maxExactMatch && groupMatch >= maxGroupMatch) { if (groupMatch > maxGroupMatch || resourceModel.getPath().length() > maxPatternLength) { maxGroupMatch = groupMatch; maxPatternLength = resourceModel.getPath().length(); matchedDestinations.clear(); } matchedDestinations.add(destination); } } } } return matchedDestinations; } /** * @return the number of path components that match from left to right. */ private int getExactPrefixMatchCount(Iterable first, Iterable second) { int count = 0; for (Iterator fit = first.iterator(), sit = second.iterator(); fit.hasNext() && sit.hasNext(); ) { if (fit.next().equals(sit.next())) { ++count; } else { break; } } return count; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/router/ParamConvertUtils.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.internal.router; import com.google.gson.reflect.TypeToken; import org.apache.commons.beanutils.ConvertUtils; import org.wso2.msf4j.util.Defaults; import org.wso2.msf4j.util.Primitives; import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.function.Function; /** * Util class to convert request parameters. */ public final class ParamConvertUtils { private static final Map, Method> PRIMITIVES_PARSE_METHODS; // Setup methods for converting string into primitive/boxed types static { Map, Method> methods = new HashMap<>(); for (Class wrappedType : Primitives.allWrapperTypes()) { try { methods.put(wrappedType, wrappedType.getMethod("valueOf", String.class)); } catch (NoSuchMethodException e) { // Void and Character has no valueOf. It's ok to ignore them } } PRIMITIVES_PARSE_METHODS = methods; } private ParamConvertUtils() { } /** * Creates a converter function that converts a path segment into the given result type. * Current implementation doesn't follow the {@link javax.ws.rs.PathParam} specification to maintain backward * compatibility. * * @param resultType Result type * @return Function the function */ public static Function createPathParamConverter(final Type resultType) { if (!(resultType instanceof Class)) { throw new IllegalArgumentException("Unsupported @PathParam type " + resultType); } return value -> ConvertUtils.convert(value, (Class) resultType); } /** * Creates a converter function that converts header value into an object of the given result type. * It follows the supported types of {@link javax.ws.rs.HeaderParam} with the following exceptions: *
    *
  1. Does not support types registered with {@link javax.ws.rs.ext.ParamConverterProvider}
  2. *
* * @param resultType Result type * @return Function the function */ public static Function, Object> createHeaderParamConverter(Type resultType) { return createListConverter(resultType); } /** * Creates a converter function that converts cookie value into an object of the given result type. * It follows the supported types of {@link javax.ws.rs.CookieParam} with the following exceptions: *
    *
  1. Does not support types registered with {@link javax.ws.rs.ext.ParamConverterProvider}
  2. *
* * @param resultType Result type * @return Function the function */ public static Function createCookieParamConverter(Type resultType) { return value -> ConvertUtils.convert(value, (Class) resultType); } /** * Creates a converter function that converts query parameter into an object of the given result type. * It follows the supported types of {@link javax.ws.rs.QueryParam} with the following exceptions: *
    *
  1. Does not support types registered with {@link javax.ws.rs.ext.ParamConverterProvider}
  2. *
* * @param resultType Result type * @return Function the function */ public static Function, Object> createQueryParamConverter(Type resultType) { return createListConverter(resultType); } /** * Creates a converter function that converts form parameter into an object of the given result type. * It follows the supported types of {@link javax.ws.rs.FormParam} with the following exceptions: *
    *
  1. Does not support types registered with {@link javax.ws.rs.ext.ParamConverterProvider}
  2. *
* * @param resultType Result type * @return Function the function */ public static Function, Object> createFormParamConverter(Type resultType) { return createListConverter(resultType); } /** * Creates a converter function that converts form parameter into an object of the given result type. * It follows the supported types of {@link org.wso2.msf4j.formparam.FormDataParam} with the following exceptions: *
    *
  1. Does not support types registered with {@link javax.ws.rs.ext.ParamConverterProvider}
  2. *
* * @param resultType Result type * @return Function the function */ public static Function, Object> createFormDataParamConverter(Type resultType) { // For java.io.File we only support List ParameterizedType if (resultType instanceof ParameterizedType) { ParameterizedType type = (ParameterizedType) resultType; Type elementType = type.getActualTypeArguments()[0]; if (elementType == File.class && type.getRawType() != List.class) { throw new IllegalArgumentException("Unsupported type " + resultType); } } Function, Object> listConverter = null; try { listConverter = createListConverter(resultType); } catch (Throwable e) { // Ignore the exceptions since from the logic we will handle the beans and Files } return listConverter; } /** * Common helper method to convert value for {@link javax.ws.rs.HeaderParam} and {@link javax.ws.rs.QueryParam}. * * @param resultType Result type * @return Function the function * @see #createHeaderParamConverter(Type) * @see #createQueryParamConverter(Type) */ private static Function, Object> createListConverter(Type resultType) { TypeToken typeToken = TypeToken.get(resultType); // Use boxed type if raw type is primitive type. Otherwise the type won't change. Class resultClass = typeToken.getRawType(); // For string, just return the first value if (resultClass == String.class) { return new BasicConverter(Defaults.defaultValue(resultClass)) { @Override protected Object convert(String value) throws Exception { return value; } }; } // Creates converter based on the type // Primitive Function, Object> converter = createPrimitiveTypeConverter(resultClass); if (converter != null) { return converter; } // String constructor converter = createStringConstructorConverter(resultClass); if (converter != null) { return converter; } // Static string argument methods converter = createStringMethodConverter(resultClass); if (converter != null) { return converter; } // Collection converter = createCollectionConverter(typeToken); if (converter != null) { return converter; } throw new IllegalArgumentException("Unsupported type " + typeToken); } /** * Creates a converter function that converts value into primitive type. * * @param resultClass The result class * @return A converter function or {@code null} if the given type is not primitive type */ private static Function, Object> createPrimitiveTypeConverter(Class resultClass) { Object defaultValue = Defaults.defaultValue(resultClass); final Class boxedType = Primitives.wrap(resultClass); if (!Primitives.isWrapperType(boxedType)) { return null; } return new BasicConverter(defaultValue) { @Override protected Object convert(String value) throws Exception { Method method = PRIMITIVES_PARSE_METHODS.get(boxedType); if (method != null) { // It's primitive/wrapper type (except char) return method.invoke(null, value); } // One exception is char type if (boxedType == Character.class) { return value.charAt(0); } // Should not happen. return null; } }; } /** * Creates a converter function that converts value using a constructor that accepts a single String argument. * * @param resultClass Result class * @return A converter function or {@code null} if the given type doesn't have a public constructor that accepts * a single String argument. */ private static Function, Object> createStringConstructorConverter(Class resultClass) { try { final Constructor constructor = resultClass.getConstructor(String.class); return new BasicConverter(Defaults.defaultValue(resultClass)) { @Override protected Object convert(String value) throws Exception { return constructor.newInstance(value); } }; } catch (NoSuchMethodException e) { return null; } } /** * Creates a converter function that converts value using a public static method named * {@code valueOf} or {@code fromString} that accepts a single String argument. * * @param resultClass Result class * @return A converter function or {@code null} if the given type doesn't have a public static method * named {@code valueOf} or {@code fromString} that accepts a single String argument. */ private static Function, Object> createStringMethodConverter(Class resultClass) { Method method; try { method = resultClass.getMethod("valueOf", String.class); } catch (NoSuchMethodException e) { try { method = resultClass.getMethod("fromString", String.class); } catch (NoSuchMethodException ex) { return null; } } final Method convertMethod = method; return new BasicConverter(Defaults.defaultValue(resultClass)) { @Override protected Object convert(String value) throws Exception { return convertMethod.invoke(null, value); } }; } /** * Creates a converter function that converts value into a {@link List}, {@link Set} or {@link SortedSet}. * * @param resultType Result type * @return A converter function or {@code null} if the given type is not a {@link ParameterizedType} with raw type * as {@link List}, {@link Set} or {@link SortedSet}. Also, for {@link SortedSet} type, if the element type * doesn't implements {@link Comparable}, {@code null} is returned. */ @SuppressWarnings("unchecked") private static Function, Object> createCollectionConverter(TypeToken resultType) { final Class rawType = resultType.getRawType(); // Collection. It must be List or Set if (rawType != List.class && rawType != Set.class && rawType != SortedSet.class) { return null; } // Must be ParameterizedType if (!(resultType.getType() instanceof ParameterizedType)) { return null; } // Must have 1 type parameter ParameterizedType type = (ParameterizedType) resultType.getType(); if (type.getActualTypeArguments().length != 1) { return null; } // For SortedSet, the entry type must be Comparable. Type elementType = type.getActualTypeArguments()[0]; if (rawType == SortedSet.class && !Comparable.class.isAssignableFrom(TypeToken.get(elementType).getRawType())) { return null; } //We only support List type for java.io.File if (elementType == File.class && rawType != List.class) { throw new IllegalArgumentException("File doesn't support " + rawType); } // Get the converter for the collection element. final Function, Object> elementConverter = createQueryParamConverter(elementType); return new Function, Object>() { @Override public Object apply(List values) { Collection collection; if (rawType == List.class) { collection = new ArrayList<>(); } else if (rawType == Set.class) { collection = new HashSet<>(); } else { collection = new TreeSet<>(); } if (values != null) { for (String value : values) { add(collection, elementConverter.apply(Collections.singletonList(value))); } } return collection; } @SuppressWarnings("unchecked") private void add(Collection collection, Object element) { collection.add((T) element); } }; } /** * A converter that converts first String value from a List of String. */ private abstract static class BasicConverter implements Function, Object> { private final Object defaultValue; protected BasicConverter(Object defaultValue) { this.defaultValue = defaultValue; } @Override public final Object apply(List values) { if (values == null || values.isEmpty()) { return getDefaultValue(); } try { return convert(values.get(0)); } catch (Exception e) { Objects.requireNonNull(e); if (e instanceof RuntimeException) { throw (RuntimeException) e; } throw new RuntimeException(e); } } protected Object getDefaultValue() { return defaultValue; } protected abstract Object convert(String value) throws Exception; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/router/PatternPathRouter.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.internal.router; import org.wso2.msf4j.util.Utils; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Matches incoming un-matched paths to destinations. Designed to be used for routing URI paths to http resources. * Parameters within braces "{}" are treated as template parameter (a named wild-card pattern). * * @param represents the destination of the routes. */ public final class PatternPathRouter { //GROUP_PATTERN is used for named wild card pattern in paths which is specified within braces. //Example: {id} private static final Pattern GROUP_PATTERN = Pattern.compile("\\{(.*?)\\}"); // non-greedy wild card match. private static final Pattern WILD_CARD_PATTERN = Pattern.compile("\\*\\*"); private static final Pattern VARIABLE_PATTERN = Pattern.compile("(\\w[-\\w\\.]*[ ]*)(\\:(.+))?"); private static final String PATH_SLASH = "/"; private final List> patternRouteList; /** * Initialize PatternPathRouter. */ public PatternPathRouter() { this.patternRouteList = new ArrayList<>(); } public static PatternPathRouter create() { return new PatternPathRouter<>(); } /** * Add a source and destination. * * @param source Source path to be routed. Routed path can have named wild-card pattern with braces "{}". * @param destination Destination of the path. */ public void add(final String source, final T destination) { // replace multiple slashes with a single slash. String path = source.replaceAll("/+", PATH_SLASH); path = (path.endsWith(PATH_SLASH) && path.length() > 1) ? path.substring(0, path.length() - 1) : path; String[] parts = path.split(PATH_SLASH); StringBuilder sb = new StringBuilder(); List groupNames = new ArrayList<>(); for (String part : parts) { Matcher groupMatcher = GROUP_PATTERN.matcher(part); if (groupMatcher.matches()) { PathPart pathPart = createPathPart(groupMatcher.group(1)); groupNames.add(pathPart.getName()); if (pathPart.getPattern() != null) { sb.append('('); sb.append(pathPart.getPattern()); sb.append(')'); } else { sb.append("([^/]+?)"); } } else if (WILD_CARD_PATTERN.matcher(part).matches()) { sb.append(".*?"); } else { sb.append(part); } sb.append(PATH_SLASH); } //Ignore the last "/" if (sb.length() > 0) { sb.setLength(sb.length() - 1); } Pattern pattern = Pattern.compile(sb.toString()); patternRouteList.add(ImmutablePair.of(pattern, new RouteDestinationWithGroups(destination, groupNames))); } private static PathPart createPathPart(String uriChunk) { PathPart pathPart = new PathPart(); uriChunk = stripBraces(uriChunk).trim(); Matcher matcher = VARIABLE_PATTERN.matcher(uriChunk); if (matcher.matches()) { pathPart.setName(matcher.group(1).trim()); if (matcher.group(2) != null && matcher.group(3) != null) { pathPart.setPattern(matcher.group(3).trim()); } } else { pathPart.setName(uriChunk); } return pathPart; } private static String stripBraces(String token) { return token.charAt(0) == '{' && token.charAt(token.length() - 1) == '}' ? token.substring(1, token.length() - 1) : token; } /** * Represents Paths parts. */ private static class PathPart { private String name; private String pattern; public String getPattern() { return pattern; } public void setPattern(String pattern) { this.pattern = pattern; } public String getName() { return name; } public void setName(String name) { this.name = name; } } /** * Get a list of destinations and the values matching templated parameter for the given path. * Returns an empty list when there are no destinations that are matched. * * @param path path to be routed. * @return List of Destinations matching the given route. */ public List> getDestinations(String path) { String cleanPath = (path.endsWith(PATH_SLASH) && path.length() > 0) ? path.substring(0, path.length() - 1) : path; List> result = new ArrayList<>(); for (ImmutablePair patternRoute : patternRouteList) { Map groupNameValues = new HashMap<>(); Matcher matcher = patternRoute.getFirst().matcher(cleanPath); if (matcher.matches()) { int matchIndex = 1; for (String name : patternRoute.getSecond().getGroupNames()) { String value = matcher.group(matchIndex); groupNameValues.put(name, value); matchIndex++; } result.add(new RoutableDestination<>(patternRoute.getSecond().getDestination(), Collections.unmodifiableMap(groupNameValues))); } } //Check for sub-resource locator if (result.isEmpty()) { patternRouteList.stream() .filter(patternRoute -> patternRoute.getSecond().destination instanceof HttpResourceModel && ((HttpResourceModel) patternRoute.getSecond().destination) .isSubResourceLocator()).forEach(patternRoute -> { Map groupNameValues = new HashMap<>(); Pattern pattern = Pattern.compile(patternRoute.getFirst().pattern() + ".*"); Matcher matcher = pattern.matcher(cleanPath); if (matcher.matches()) { int matchIndex = 1; for (String name : patternRoute.getSecond().getGroupNames()) { String value = matcher.group(matchIndex); groupNameValues.put(name, value); matchIndex++; } result.add(new RoutableDestination<>(patternRoute.getSecond().getDestination(), Collections.unmodifiableMap(groupNameValues))); } }); } return result; } /** * Represents a matched destination. * * @param Type of destination. */ public static final class RoutableDestination { private final T destination; private final Map groupNameValues; /** * Construct the RouteableDestination with the given parameters. * * @param destination destination of the route. * @param groupNameValues parameters */ public RoutableDestination(T destination, Map groupNameValues) { this.destination = destination; this.groupNameValues = groupNameValues; } /** * @return destination of the route. */ public T getDestination() { return destination; } /** * @return Map of templated parameter and string representation group value matching the templated parameter as * the value. */ public Map getGroupNameValues() { return groupNameValues; } @Override public String toString() { return Utils.toString(this, new String[] { "destination", "groupNameValues" }); } } /** * Helper class to store the groupNames and Destination. */ private final class RouteDestinationWithGroups { private final T destination; private final List groupNames; public RouteDestinationWithGroups(T destination, List groupNames) { this.destination = destination; this.groupNames = groupNames; } public T getDestination() { return destination; } public List getGroupNames() { return groupNames; } } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/router/SubresourceKey.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.internal.router; import java.util.HashSet; import java.util.Set; /** * This class is used to track the sub-resources. * */ public class SubresourceKey { private Class typedClass; private String path; private Set httpMethods = new HashSet<>(); public SubresourceKey(String path, Class tClass, Set httpMethods) { this.path = path; typedClass = tClass; this.httpMethods = httpMethods; } /** * Get the sub-resource method type. * * @return Class get the type of the sub-resource */ public Class getTypedClass() { return typedClass; } /** * Get sub-resource absolute path. * * @return String absolute path of the sub-resource */ public String getPath() { return path; } /** * Get method's http verbs. * * @return available http methods */ public Set getHttpMethods() { return httpMethods; } @Override public boolean equals(Object o) { if (!(o instanceof SubresourceKey)) { return false; } SubresourceKey other = (SubresourceKey) o; return path.equals(other.path) && typedClass == other.typedClass && httpMethods.equals(((SubresourceKey) o).httpMethods); } @Override public int hashCode() { return typedClass.hashCode() + 37 * path.hashCode(); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/router/Util.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.internal.router; import java.lang.reflect.Method; import java.util.List; import java.util.regex.Pattern; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.HEAD; import javax.ws.rs.OPTIONS; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.core.MediaType; /** * Util class. */ public class Util { public static final String GROUP_PATTERN = "\\{(.*?)\\}"; public static final String GROUP_PATTERN_REGEX = "([^/]+?)"; public static final Pattern WILD_CARD_PATTERN = Pattern.compile("\\*\\*"); /** * Check if http verb is available for the method. * * @param method * @return */ public static boolean isHttpMethodAvailable(Method method) { return method.isAnnotationPresent(GET.class) || method.isAnnotationPresent(PUT.class) || method.isAnnotationPresent(POST.class) || method.isAnnotationPresent(DELETE.class) || method.isAnnotationPresent(HEAD.class) || method.isAnnotationPresent(OPTIONS.class); } /** * Remove the curly braces if the token is wrapped with curly braces. * * @param token * @return */ public static String stripBraces(String token) { return token.charAt(0) == '{' && token.charAt(token.length() - 1) == '}' ? token.substring(1, token.length() - 1) : token; } /** * Process accept type considering the produce type and the * accept types of the request header. * * @param acceptTypes accept types of the request. * @return processed accept type */ public static String getResponseType(List acceptTypes, List producesMediaTypes) { String responseType = MediaType.WILDCARD; if (!producesMediaTypes.contains(MediaType.WILDCARD) && acceptTypes != null) { responseType = (acceptTypes.contains(MediaType.WILDCARD)) ? producesMediaTypes.get(0) : producesMediaTypes.stream().filter(acceptTypes::contains).findFirst().get(); } else if (acceptTypes == null && !producesMediaTypes.isEmpty()) { responseType = producesMediaTypes.get(0); } return responseType; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/router/package-info.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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. */ /** * Service and components to build Netty based Http web service. * {@code NettyHttpService} sets up the necessary pipeline and manages starting, stopping, * state-management of the web service. * * * In-order to handle http requests, {@code HttpHandler} must be implemented. The methods * in the classes implemented from {@code HttpHandler} must be annotated with Jersey annotations to * specify http uri paths and http methods. * Note: Only supports the following annotations: * {@link javax.ws.rs.Path Path}, * {@link javax.ws.rs.PathParam PathParam}, * {@link javax.ws.rs.GET GET}, * {@link javax.ws.rs.PUT PUT}, * {@link javax.ws.rs.POST POST}, * {@link javax.ws.rs.DELETE DELETE}, * {@link javax.ws.rs.HEAD HEAD}, * {@link javax.ws.rs.OPTIONS OPTIONS}. * * Note: Doesn't support getting Annotations from base class if the HttpHandler implements also extends * a class with annotation. * * Sample usage Handlers and Netty service setup: * *
 * //Setup Handlers
 *
 * {@literal @}Path("/common/v1/")
 * public class ApiHandler implements HttpHandler {
 *
 *   {@literal @}Path("widgets")
 *   {@literal @}GET
 *   public void widgetHandler(HttpRequest request, HttpResponder responder) {
 *     responder.sendJson(HttpResponseStatus.OK, "{\"key\": \"value\"}");
 *   }
 *
 *   {@literal @}Override
 *   public void init(HandlerContext context) {
 *     //Perform bootstrap operations before any of the handlers in this class gets called.
 *   }
 *
 *   {@literal @}Override
 *   public void destroy(HandlerContext context) {
 *    //Perform teardown operations the server shuts down.
 *   }
 * }
 *
 * //Set up and start the http service
 * NettyHttpService service = NettyHttpService.builder()
 *                                            .addHttpHandlers(ImmutableList.of(new Handler())
 *                                            .setPort(8989)
 *                                            .build();
 * service.startAndWait();
 *
 * // ....
 *
 * //Stop the web-service
 * service.shutdown();
 *
 * 
*/ package org.wso2.msf4j.internal.router; ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/session/SessionIdGenerator.java ================================================ /* * Copyright (c) 2016 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ package org.wso2.msf4j.internal.session; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.SecureRandom; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; /** * SessionIdGenerator util class. Borrowed from Tomcat 8. */ @SuppressWarnings("unused") public class SessionIdGenerator { private static final Logger log = LoggerFactory.getLogger(SessionIdGenerator.class); private int sessionIdLength = 16; /** * Queue of random number generator objects to be used when creating session * identifiers. If the queue is empty when a random number generator is * required, a new random number generator object is created. This is * designed this way since random number generators use a sync to make them * thread-safe and the sync makes using a a single object slow(er). */ private final Queue randoms = new ConcurrentLinkedQueue<>(); private String secureRandomClass = null; private String secureRandomAlgorithm = "SHA1PRNG"; private String secureRandomProvider = null; /** * Node identifier when in a cluster. Defaults to the empty string. */ private String jvmRoute = ""; public void setSessionIdLength(int sessionIdLength) { this.sessionIdLength = sessionIdLength; } public void setSecureRandomClass(String secureRandomClass) { this.secureRandomClass = secureRandomClass; } public void setSecureRandomAlgorithm(String secureRandomAlgorithm) { this.secureRandomAlgorithm = secureRandomAlgorithm; } public void setSecureRandomProvider(String secureRandomProvider) { this.secureRandomProvider = secureRandomProvider; } public void setJvmRoute(String jvmRoute) { this.jvmRoute = jvmRoute; } public String generateSessionId(String route) { byte random[] = new byte[16]; // Render the result as a String of hexadecimal digits // Start with enough space for sessionIdLength and medium route size StringBuilder buffer = new StringBuilder(2 * sessionIdLength + 20); int resultLenBytes = 0; while (resultLenBytes < sessionIdLength) { getRandomBytes(random); for (int j = 0; j < random.length && resultLenBytes < sessionIdLength; j++) { byte b1 = (byte) ((random[j] & 0xf0) >> 4); byte b2 = (byte) (random[j] & 0x0f); if (b1 < 10) { buffer.append((char) ('0' + b1)); } else { buffer.append((char) ('A' + (b1 - 10))); } if (b2 < 10) { buffer.append((char) ('0' + b2)); } else { buffer.append((char) ('A' + (b2 - 10))); } resultLenBytes++; } } if (route != null && route.length() > 0) { buffer.append('.').append(route); } else { if (jvmRoute != null && jvmRoute.length() > 0) { buffer.append('.').append(jvmRoute); } } return buffer.toString(); } protected void getRandomBytes(byte bytes[]) { SecureRandom random = randoms.poll(); if (random == null) { random = createSecureRandom(); } random.nextBytes(bytes); randoms.add(random); } /** * Create a new random number generator instance we should use for * generating session identifiers. */ private SecureRandom createSecureRandom() { SecureRandom result = null; long t1 = System.currentTimeMillis(); if (secureRandomClass != null) { try { // Construct and seed a new random number generator Class clazz = Class.forName(secureRandomClass); result = (SecureRandom) clazz.newInstance(); } catch (Exception e) { log.error("Cannot load secureRandomClass: " + secureRandomClass, e); } } if (result == null) { // No secureRandomClass or creation failed. Use SecureRandom. try { if (secureRandomProvider != null && secureRandomProvider.length() > 0) { result = SecureRandom.getInstance(secureRandomAlgorithm, secureRandomProvider); } else if (secureRandomAlgorithm != null && secureRandomAlgorithm.length() > 0) { result = SecureRandom.getInstance(secureRandomAlgorithm); } } catch (NoSuchAlgorithmException e) { log.error("SecureRandomAlgorithm " + secureRandomAlgorithm + " not found", e); } catch (NoSuchProviderException e) { log.error("SecureRandomProvider not found " + secureRandomProvider, e); } } if (result == null) { // Invalid provider / algorithm try { result = SecureRandom.getInstance("SHA1PRNG"); } catch (NoSuchAlgorithmException e) { log.error("SecureRandomAlgorithm " + secureRandomAlgorithm + " not found", e); } } if (result == null) { // Nothing works - use platform default result = new SecureRandom(); } // Force seeding to take place result.nextInt(); long t2 = System.currentTimeMillis(); if ((t2 - t1) > 100) { log.warn("Session took more than 100ms to create! Time taken: " + (t2 - t1) + "ms"); } return result; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/websocket/CloseCodeImpl.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.internal.websocket; import javax.websocket.CloseReason; /** * {@link CloseReason.CloseCode} implementation for WebSocket in MSF4J */ public class CloseCodeImpl implements CloseReason.CloseCode { private final int closeCode; /** * @param closeCode close code for the reason of closure. */ public CloseCodeImpl(int closeCode) { this.closeCode = closeCode; } @Override public int getCode() { return this.closeCode; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/websocket/EndpointDispatcher.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.internal.websocket; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.List; import java.util.Optional; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.PongMessage; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; /** * Dispatch the registered endpoints. * This class will find the best matching resource of an endpoint for a given message type. */ public class EndpointDispatcher { /** * Validate the endpoint against the {@link ServerEndpoint} since without {@link ServerEndpoint} definition * there can't be a WebSocket endpoint. * @param websocketEndpoint endpoint which should be validated. */ public boolean validateEndpointUri(Object websocketEndpoint) { if (websocketEndpoint != null) { return websocketEndpoint.getClass().isAnnotationPresent(ServerEndpoint.class); } return false; } /** * Extract the URI from the endpoint. * Note that it is better use validateEndpointUri method to validate the endpoint uri * before getting it out if needed. Otherwise it will cause issues. Use this method only and only if * it is sure that endpoint contains {@link ServerEndpoint} defined. * * @param webSocketEndpoint WebSocket endpoint which the URI should be extracted. * @return the URI of the Endpoint as a String. */ public String getUri(Object webSocketEndpoint) { return webSocketEndpoint.getClass().getAnnotation(ServerEndpoint.class).value(); } /** * Extract OnOpen method from the endpoint if exists. * * @param webSocketEndpoint Endpoint to extract method. * @return method optional to handle new connection. */ public Optional getOnOpenMethod(Object webSocketEndpoint) { Method[] methods = webSocketEndpoint.getClass().getMethods(); Method returnMethod = null; for (Method method : methods) { if (method.isAnnotationPresent(OnOpen.class)) { returnMethod = method; break; } } return Optional.ofNullable(returnMethod); } /** * Extract OnClose method from the endpoint if exists. * * @param webSocketEndpoint Endpoint to extract method. * @return method optional to handle new connection. */ public Optional getOnCloseMethod(Object webSocketEndpoint) { Method[] methods = webSocketEndpoint.getClass().getMethods(); Method returnMethod = null; for (Method method : methods) { if (method.isAnnotationPresent(OnClose.class)) { returnMethod = method; break; } } return Optional.ofNullable(returnMethod); } /** * Extract OnError method from the endpoint if exists * * @param webSocketEndpoint Endpoint to extract method. * @return method optional to handle errors. */ public Optional getOnErrorMethod(Object webSocketEndpoint) { Method[] methods = webSocketEndpoint.getClass().getMethods(); Method returnMethod = null; for (Method method : methods) { if (method.isAnnotationPresent(OnError.class)) { returnMethod = method; } } return Optional.ofNullable(returnMethod); } /** * Extract OnMessage method for String from the endpoint if exists. * * @param webSocketEndpoint Endpoint to extract method. * @return method optional to handle String messages. */ public Optional getOnStringMessageMethod(Object webSocketEndpoint) { Method[] methods = webSocketEndpoint.getClass().getMethods(); Method returnMethod = null; for (Method method : methods) { if (method.isAnnotationPresent(OnMessage.class)) { Parameter[] parameters = method.getParameters(); for (Parameter parameter: parameters) { if (!parameter.isAnnotationPresent(PathParam.class) && parameter.getType() == String.class) { returnMethod = method; } } } } return Optional.ofNullable(returnMethod); } /** * Extract OnMessage method for Binary from the endpoint if exists. * * @param webSocketEndpoint Endpoint to extract method. * @return method optional to handle binary messages. */ public Optional getOnBinaryMessageMethod(Object webSocketEndpoint) { Method[] methods = webSocketEndpoint.getClass().getMethods(); Method returnMethod = null; for (Method method : methods) { if (method.isAnnotationPresent(OnMessage.class)) { //Adding OnMessage according to their types Class[] paraTypes = method.getParameterTypes(); List> paraList = Arrays.asList(paraTypes); if (paraList.contains(byte[].class) || paraList.contains(ByteBuffer.class)) { returnMethod = method; } } } return Optional.ofNullable(returnMethod); } /** * Extract OnMessage method for Pong from the endpoint if exists. * * @param webSocketEndpoint Endpoint to extract method. * @return method optional to handle pong messages. */ public Optional getOnPongMessageMethod(Object webSocketEndpoint) { Method[] methods = webSocketEndpoint.getClass().getMethods(); Method returnMethod = null; for (Method method : methods) { if (method.isAnnotationPresent(OnMessage.class)) { Class[] paraTypes = method.getParameterTypes(); List> paraList = Arrays.asList(paraTypes); if (paraList.contains(PongMessage.class)) { returnMethod = method; } } } return Optional.ofNullable(returnMethod); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/websocket/EndpointValidator.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.internal.websocket; import org.wso2.msf4j.websocket.exception.WebSocketEndpointAnnotationException; import org.wso2.msf4j.websocket.exception.WebSocketEndpointMethodReturnTypeException; import org.wso2.msf4j.websocket.exception.WebSocketMethodParameterException; import org.wso2.transport.http.netty.contract.websocket.WebSocketConnection; import org.wso2.transport.http.netty.message.HttpCarbonRequest; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.nio.ByteBuffer; import javax.websocket.CloseReason; import javax.websocket.PongMessage; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; /** * This validates all the methods which are relevant to WebSocket Server endpoint using JSR-356 specification. */ public class EndpointValidator { /** * Validate the whole WebSocket endpoint. * * @return true if validation is completed without any error. * @throws WebSocketEndpointAnnotationException if error on an annotation declaration occurred. * @throws WebSocketMethodParameterException if the method parameters are invalid for a given method according * to JSR-356 specification. */ public boolean validate(Object webSocketEndpoint) throws WebSocketEndpointAnnotationException, WebSocketMethodParameterException, WebSocketEndpointMethodReturnTypeException { if (webSocketEndpoint == null) { return false; } return validateURI(webSocketEndpoint) && validateOnStringMethod(webSocketEndpoint) && validateOnBinaryMethod(webSocketEndpoint) && validateOnPongMethod(webSocketEndpoint) && validateOnOpenMethod(webSocketEndpoint) && validateOnCloseMethod(webSocketEndpoint) && validateOnErrorMethod(webSocketEndpoint); } private boolean validateURI(Object webSocketEndpoint) throws WebSocketEndpointAnnotationException { if (webSocketEndpoint.getClass().isAnnotationPresent(ServerEndpoint.class)) { return true; } throw new WebSocketEndpointAnnotationException("Server Endpoint is not defined."); } private boolean validateOnStringMethod(Object webSocketEndpoint) throws WebSocketMethodParameterException, WebSocketEndpointMethodReturnTypeException { EndpointDispatcher dispatcher = new EndpointDispatcher(); Method method; if (dispatcher.getOnStringMessageMethod(webSocketEndpoint).isPresent()) { method = dispatcher.getOnStringMessageMethod(webSocketEndpoint).get(); } else { return true; } validateReturnType(method); boolean foundPrimaryString = false; for (Parameter parameter: method.getParameters()) { Class paraType = parameter.getType(); if (paraType == String.class) { if (parameter.getAnnotation(PathParam.class) == null) { if (foundPrimaryString) { throw new WebSocketMethodParameterException("Invalid parameter found on text message method: " + "More than one string parameter without " + "@PathParam annotation."); } foundPrimaryString = true; } } else if (paraType != WebSocketConnection.class) { throw new WebSocketMethodParameterException("Invalid parameter found on text message method: " + paraType); } } return foundPrimaryString; } private boolean validateOnBinaryMethod(Object webSocketEndpoint) throws WebSocketMethodParameterException, WebSocketEndpointMethodReturnTypeException { EndpointDispatcher dispatcher = new EndpointDispatcher(); Method method; if (dispatcher.getOnBinaryMessageMethod(webSocketEndpoint).isPresent()) { method = dispatcher.getOnBinaryMessageMethod(webSocketEndpoint).get(); } else { return true; } validateReturnType(method); boolean foundPrimaryBuffer = false; boolean foundIsFinal = false; for (Parameter parameter: method.getParameters()) { Class paraType = parameter.getType(); if (paraType == String.class) { if (parameter.getAnnotation(PathParam.class) == null) { throw new WebSocketMethodParameterException("Invalid parameter found on binary message method: " + "string parameter without " + "@PathParam annotation."); } } else if (paraType == ByteBuffer.class || paraType == byte[].class) { if (foundPrimaryBuffer) { throw new WebSocketMethodParameterException("Invalid parameter found on binary message method: " + "only one ByteBuffer/byte[] " + "should be declared."); } foundPrimaryBuffer = true; } else if (paraType == boolean.class) { if (foundIsFinal) { throw new WebSocketMethodParameterException("Invalid parameter found on binary message method: " + "only one boolean should be declared and " + "found more than one."); } foundIsFinal = true; } else if (paraType != WebSocketConnection.class) { throw new WebSocketMethodParameterException("Invalid parameter found on binary message method: " + paraType); } } return foundPrimaryBuffer; } private boolean validateOnPongMethod(Object webSocketEndpoint) throws WebSocketMethodParameterException, WebSocketEndpointMethodReturnTypeException { EndpointDispatcher dispatcher = new EndpointDispatcher(); Method method; if (dispatcher.getOnPongMessageMethod(webSocketEndpoint).isPresent()) { method = dispatcher.getOnPongMessageMethod(webSocketEndpoint).get(); } else { return true; } validateReturnType(method); boolean foundPrimaryPong = false; for (Parameter parameter: method.getParameters()) { Class paraType = parameter.getType(); if (paraType == String.class) { if (parameter.getAnnotation(PathParam.class) == null) { throw new WebSocketMethodParameterException("Invalid parameter found on pong message method: " + "string parameter without " + "@PathParam annotation."); } } else if (paraType == PongMessage.class) { if (foundPrimaryPong) { throw new WebSocketMethodParameterException("Invalid parameter found on pong message method: " + "only one PongMessage should be declared."); } foundPrimaryPong = true; } else if (paraType != WebSocketConnection.class) { throw new WebSocketMethodParameterException("Invalid parameter found on pong message method: " + paraType); } } return foundPrimaryPong; } private boolean validateOnOpenMethod(Object webSocketEndpoint) throws WebSocketMethodParameterException, WebSocketEndpointMethodReturnTypeException { EndpointDispatcher dispatcher = new EndpointDispatcher(); Method method; if (dispatcher.getOnOpenMethod(webSocketEndpoint).isPresent()) { method = dispatcher.getOnOpenMethod(webSocketEndpoint).get(); } else { return true; } validateReturnType(method); for (Parameter parameter: method.getParameters()) { Class paraType = parameter.getType(); if (paraType == String.class) { if (parameter.getAnnotation(PathParam.class) == null) { throw new WebSocketMethodParameterException("Invalid parameter found on open message method: " + "string parameter without " + "@PathParam annotation."); } } else if (paraType != WebSocketConnection.class && paraType != HttpCarbonRequest.class) { throw new WebSocketMethodParameterException("Invalid parameter found on open message method: " + paraType); } } return true; } private boolean validateOnCloseMethod(Object webSocketEndpoint) throws WebSocketMethodParameterException, WebSocketEndpointMethodReturnTypeException { EndpointDispatcher dispatcher = new EndpointDispatcher(); Method method; if (dispatcher.getOnCloseMethod(webSocketEndpoint).isPresent()) { method = dispatcher.getOnCloseMethod(webSocketEndpoint).get(); } else { return true; } validateReturnType(method); for (Parameter parameter: method.getParameters()) { Class paraType = parameter.getType(); if (paraType == String.class) { if (parameter.getAnnotation(PathParam.class) == null) { throw new WebSocketMethodParameterException("Invalid parameter found on close message method: " + "string parameter without " + "@PathParam annotation."); } } else if (paraType != CloseReason.class && paraType != WebSocketConnection.class) { throw new WebSocketMethodParameterException("Invalid parameter found on close message method: " + paraType); } } return true; } private boolean validateOnErrorMethod(Object webSocketEndpoint) throws WebSocketMethodParameterException, WebSocketEndpointMethodReturnTypeException { EndpointDispatcher dispatcher = new EndpointDispatcher(); Method method; if (dispatcher.getOnErrorMethod(webSocketEndpoint).isPresent()) { method = dispatcher.getOnErrorMethod(webSocketEndpoint).get(); } else { return true; } validateReturnType(method); boolean foundPrimaryThrowable = false; for (Parameter parameter: method.getParameters()) { Class paraType = parameter.getType(); if (paraType == String.class) { if (parameter.getAnnotation(PathParam.class) == null) { throw new WebSocketMethodParameterException("Invalid parameter found on error message method: " + "string parameter without " + "@PathParam annotation."); } } else if (paraType == Throwable.class) { if (foundPrimaryThrowable) { throw new WebSocketMethodParameterException("Invalid parameter found on pong message method: " + "only one Throwable should be declared."); } foundPrimaryThrowable = true; } else if (paraType != WebSocketConnection.class) { throw new WebSocketMethodParameterException("Invalid parameter found on error message method: " + paraType); } } if (!foundPrimaryThrowable) { throw new WebSocketMethodParameterException("Mandatory parameter for on error method " + Throwable.class + " not found."); } return foundPrimaryThrowable; } private boolean validateReturnType(Method method) throws WebSocketEndpointMethodReturnTypeException { Class returnType = method.getReturnType(); boolean foundCorrectReturnType = returnType == String.class || returnType == ByteBuffer.class || returnType == byte[].class || returnType == PongMessage.class || returnType == void.class; if (!foundCorrectReturnType) { throw new WebSocketEndpointMethodReturnTypeException("Unexpected method return type: " + returnType); } return foundCorrectReturnType; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/websocket/EndpointsRegistryImpl.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.internal.websocket; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.msf4j.internal.router.PatternPathRouter; import org.wso2.msf4j.websocket.WebSocketEndpointsRegistry; import org.wso2.msf4j.websocket.exception.WebSocketEndpointAnnotationException; import org.wso2.msf4j.websocket.exception.WebSocketEndpointMethodReturnTypeException; import org.wso2.msf4j.websocket.exception.WebSocketMethodParameterException; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; /** * Implementation for {@link WebSocketEndpointsRegistry}. * Endpoints are Dispatched and Stored in a {@link PatternPathRouter}. So when new request comes it will be routed to * best matching URI. */ public class EndpointsRegistryImpl implements WebSocketEndpointsRegistry { private static final Logger log = LoggerFactory.getLogger(EndpointsRegistryImpl.class); private static final EndpointsRegistryImpl webSocketEndpointsRegistry = new EndpointsRegistryImpl(); private final EndpointValidator validator = new EndpointValidator(); // Map private final Map webSocketEndpointMap = new ConcurrentHashMap<>(); // PatterPathRouter private PatternPathRouter endpointPatternPathRouter = PatternPathRouter.create(); // Makes this class singleton. private EndpointsRegistryImpl() { } /** * @return this singleton instance of {@link EndpointsRegistryImpl}. */ public static EndpointsRegistryImpl getInstance() { return webSocketEndpointsRegistry; } /** * Adding endpoints to the registry. * * @param webSocketEndpoints to add. * @return the endpoints which could not deploy due to validation errors. */ public List addEndpoint(Object... webSocketEndpoints) { List endpointsWithError = new LinkedList<>(); Arrays.stream(webSocketEndpoints).forEach( endpoint -> { EndpointDispatcher dispatcher = new EndpointDispatcher(); try { if (validator.validate(endpoint)) { webSocketEndpointMap.put(dispatcher.getUri(endpoint), endpoint); log.info("Endpoint Registered : " + dispatcher.getUri(endpoint)); } } catch (WebSocketEndpointAnnotationException e) { endpointsWithError.add(endpoint); log.error("Cannot deploy endpoint" + ": server endpoint not defined." + System.lineSeparator() + e.toString()); } catch (WebSocketMethodParameterException e) { endpointsWithError.add(endpoint); log.error("Cannot deploy endpoint " + endpoint.getClass().getName() + ": error method definition." + System.lineSeparator() + e.toString()); } catch (WebSocketEndpointMethodReturnTypeException e) { endpointsWithError.add(endpoint); log.error("Cannot deploy endpoint " + endpoint.getClass().getName() + ": invalid method return type." + System.lineSeparator() + e.toString()); } } ); updatePatternPathRouter(); return endpointsWithError; } /** * Remove WebSocket Endpoint from Registry. * * @param webSocketEndpoint which should be removed. */ public void removeEndpoint(Object webSocketEndpoint) { EndpointDispatcher dispatcher = new EndpointDispatcher(); webSocketEndpointMap.remove(dispatcher.getUri(webSocketEndpoint)); updatePatternPathRouter(); log.info("Removed endpoint : " + dispatcher.getUri(webSocketEndpoint)); } /** * Extract the best possible {@link org.wso2.msf4j.internal.router.PatternPathRouter.RoutableDestination}. * * @param uri String of the desired destination endpoint. * @return the best possible {@link org.wso2.msf4j.internal.router.PatternPathRouter.RoutableDestination}. */ public PatternPathRouter.RoutableDestination getRoutableEndpoint(String uri) { List> routableDestinations = endpointPatternPathRouter.getDestinations(uri); return getBestEndpoint(routableDestinations, uri); } @Override public Set getAllEndpoints() { return webSocketEndpointMap.entrySet().stream() .map(Map.Entry::getValue) .collect(Collectors.toSet()); } /** * Find the best matching RoutableDestination from the All matching RoutableDestinations. * * @param routableDestinationList routable destication list for a given uri. * @param requestUri uri which the best endpoint should be found for. * @return the best possible routable destination for the requested uri. */ private PatternPathRouter.RoutableDestination getBestEndpoint( List> routableDestinationList, String requestUri) { PatternPathRouter.RoutableDestination bestRoutableDestination = null; int currentBestHitCount = 0; for (PatternPathRouter.RoutableDestination currentRoutableDestination: routableDestinationList) { int tempCount = getHitCount(new EndpointDispatcher().getUri(currentRoutableDestination.getDestination()) .split("/"), requestUri.split("/")); if (tempCount > currentBestHitCount) { bestRoutableDestination = currentRoutableDestination; currentBestHitCount = tempCount; } } return bestRoutableDestination; } /** * Update the PatternPathRouter when adding and removing an endpoint. */ private void updatePatternPathRouter() { endpointPatternPathRouter = PatternPathRouter.create(); webSocketEndpointMap.entrySet().forEach( entry -> endpointPatternPathRouter.add(entry.getKey(), entry.getValue()) ); } // /** * Compare and find number of equalities of the Endpoint URI and Requested URI. * * @param destinationUriChunkArray chunk words of the endpoint uri. * @param requestUriChunkArray chunk words of the requested uri. * @return hit count - how many words are matched endpoint uri against requested uri. */ private int getHitCount(String[] destinationUriChunkArray, String[] requestUriChunkArray) { int count = 0; for (int i = 0; i < destinationUriChunkArray.length; i++) { if (destinationUriChunkArray[i].equals(requestUriChunkArray[i])) { count++; } } return count; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/websocket/WebSocketPongMessage.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.internal.websocket; import java.nio.ByteBuffer; import javax.websocket.PongMessage; /** * WebSocket pong message implementation of {@link PongMessage}. */ public class WebSocketPongMessage implements PongMessage { private final ByteBuffer applicationData; /** * @param byteBuffer application data of the {@link PongMessage} */ public WebSocketPongMessage(ByteBuffer byteBuffer) { applicationData = byteBuffer; } @Override public ByteBuffer getApplicationData() { return applicationData; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/internal/websocket/WebSocketServerSC.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.internal.websocket; import org.osgi.framework.BundleContext; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.ReferenceCardinality; import org.osgi.service.component.annotations.ReferencePolicy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.carbon.kernel.startupresolver.RequiredCapabilityListener; import org.wso2.msf4j.internal.DataHolder; import org.wso2.msf4j.websocket.WebSocketEndpoint; /** * OSGi Service component for WebSocket server. This will identify the endpoints which are trying to identify * and register them as WebSocket Server Endpoints */ @Component( name = "org.wso2.msf4j.internal.websocket.WebSocketServerSC", immediate = true, property = { "componentName=wso2-websocket-server" } ) public class WebSocketServerSC implements RequiredCapabilityListener { private static final Logger log = LoggerFactory.getLogger(WebSocketServerSC.class); private EndpointsRegistryImpl endpointsRegistry = EndpointsRegistryImpl.getInstance(); @Activate protected void start(final BundleContext bundleContext) { if (log.isDebugEnabled()) { log.debug("Endpoint Activated."); } } /** * Add endpoint to the endpoint registry. * * @param endpoint endpoint which should be added to the registry. */ @Reference( name = "websocketEndpoint", service = WebSocketEndpoint.class, cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC, unbind = "removeEndpoint" ) protected void addEndpoint(WebSocketEndpoint endpoint) { endpointsRegistry.addEndpoint(endpoint); } /** * Remove endpoint from the endpoint registry. * * @param endpoint endpoint which should be removed from the registry. */ protected void removeEndpoint(WebSocketEndpoint endpoint) { endpointsRegistry.removeEndpoint(endpoint); } @Override public void onAllRequiredCapabilitiesAvailable() { DataHolder.getInstance().getBundleContext().registerService(WebSocketServerSC.class, this, null); log.info("All required capabilities are available of WebSocket service component is available."); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/security/JWTSecurityInterceptor.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.security; import com.nimbusds.jose.JOSEException; import com.nimbusds.jose.JWSVerifier; import com.nimbusds.jose.crypto.RSASSAVerifier; import com.nimbusds.jwt.SignedJWT; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import org.wso2.msf4j.interceptor.RequestInterceptor; import org.wso2.msf4j.util.SystemVariableUtil; import java.io.IOException; import java.io.InputStream; import java.security.Key; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.security.interfaces.RSAPublicKey; import java.text.ParseException; import java.util.Date; /** * Verify the JWT header in request. */ public class JWTSecurityInterceptor implements RequestInterceptor { private static final Logger log = LoggerFactory.getLogger(JWTSecurityInterceptor.class); private static final String JWT_HEADER = "X-JWT-Assertion"; private static final String AUTH_TYPE_JWT = "JWT"; private static final String KEYSTORE = SystemVariableUtil.getValue("PETSTORE_KEYSTORE", "wso2carbon.jks"); private static final String ALIAS = SystemVariableUtil.getValue("PETSTORE_KEY_ALIAS", "wso2carbon"); private static final String KEYSTORE_PASSWORD = SystemVariableUtil.getValue("PETSTORE_KEYSTORE_PASS", "wso2carbon"); @Override public boolean interceptRequest(Request request, Response response) throws Exception { log.info("Authentication precall"); boolean isValidSignature; String jwtHeader = request.getHeader(JWT_HEADER); if (jwtHeader != null) { isValidSignature = verifySignature(jwtHeader); if (isValidSignature) { return true; } } response.setHeader(javax.ws.rs.core.HttpHeaders.WWW_AUTHENTICATE, AUTH_TYPE_JWT); response.setStatus(javax.ws.rs.core.Response.Status.UNAUTHORIZED.getStatusCode()); return false; } private boolean verifySignature(String jwt) { try { SignedJWT signedJWT = SignedJWT.parse(jwt); if (new Date().before(signedJWT.getJWTClaimsSet().getExpirationTime())) { JWSVerifier verifier = new RSASSAVerifier((RSAPublicKey) getPublicKey(KEYSTORE, KEYSTORE_PASSWORD, ALIAS)); return signedJWT.verify(verifier); } else { log.info("Token has expired"); } } catch (ParseException | IOException | KeyStoreException | CertificateException | NoSuchAlgorithmException | UnrecoverableKeyException | JOSEException e) { log.error("Error occurred while JWT signature verification. JWT=" + jwt, e); } return false; } private PublicKey getPublicKey(String keyStorePath, String keyStorePassword, String alias) throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException { try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(keyStorePath)) { KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); keystore.load(inputStream, keyStorePassword.toCharArray()); Key key = keystore.getKey(alias, keyStorePassword.toCharArray()); if (key instanceof PrivateKey) { // Get certificate of public key java.security.cert.Certificate cert = keystore.getCertificate(alias); // Get public key return cert.getPublicKey(); } } return null; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/security/MSF4JSecurityException.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.security; /** * The class {@code MSF4JSecurityException} and its subclasses are a form of * {@code Exception} that indicates security related error conditions. */ public class MSF4JSecurityException extends Exception { private SecurityErrorCode errorCode; /** * Constructs a new exception with the specified error code and the detail message. * * @param errorCode SecurityErrorCode * @param message error message */ public MSF4JSecurityException(SecurityErrorCode errorCode, String message) { super(message); this.errorCode = errorCode; } /** * Constructs a new exception with the specified detail message and the error code. * * @param errorCode SecurityErrorCode * @param message error message * @param cause cause the cause (which is saved for later retrieval by the * {@link #getCause()} method). (A null value is * permitted, and indicates that the cause is nonexistent or * unknown.) */ public MSF4JSecurityException(SecurityErrorCode errorCode, String message, Throwable cause) { super(message, cause); this.errorCode = errorCode; } /** * Returns the associate {@code SecurityErrorCode} value. * * @return an instances of the SecurityErrorCode */ public SecurityErrorCode getErrorCode() { return errorCode; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/security/SecurityErrorCode.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.security; /** * Enum class which represent security error codes. */ public enum SecurityErrorCode { INVALID_AUTHORIZATION_HEADER(1), AUTHENTICATION_FAILURE(1), AUTHORIZATION_FAILURE(2), GENERIC_ERROR(4); private final int code; SecurityErrorCode(int code) { this.code = code; } public int getCode() { return code; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/security/basic/AbstractBasicAuthSecurityInterceptor.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.security.basic; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import org.wso2.msf4j.interceptor.RequestInterceptor; import java.nio.charset.Charset; import java.util.Base64; /** * AbstractBasicAuthSecurityInterceptor hides Netty based header processing and provide authenticate() method to plug-in * custom authentication logic. * * @since 1.1.0 */ public abstract class AbstractBasicAuthSecurityInterceptor implements RequestInterceptor { private static final String AUTH_TYPE_BASIC = "Basic"; public static final String CHARSET_UTF_8 = "UTF-8"; private static final int AUTH_TYPE_BASIC_LENGTH = AUTH_TYPE_BASIC.length(); @Override public boolean interceptRequest(Request request, Response response) throws Exception { String authHeader = request.getHeader(javax.ws.rs.core.HttpHeaders.AUTHORIZATION); if (authHeader != null) { String authType = authHeader.substring(0, AUTH_TYPE_BASIC_LENGTH); String authEncoded = authHeader.substring(AUTH_TYPE_BASIC_LENGTH).trim(); if (AUTH_TYPE_BASIC.equals(authType) && !authEncoded.isEmpty()) { byte[] decodedByte = authEncoded.getBytes(Charset.forName(CHARSET_UTF_8)); String authDecoded = new String(Base64.getDecoder().decode(decodedByte), Charset.forName(CHARSET_UTF_8)); String[] authParts = authDecoded.split(":"); String username = authParts[0]; String password = authParts[1]; if (authenticate(username, password)) { return true; } } } response.setStatus(javax.ws.rs.core.Response.Status.UNAUTHORIZED.getStatusCode()); response.setHeader(javax.ws.rs.core.HttpHeaders.WWW_AUTHENTICATE, AUTH_TYPE_BASIC); return false; } protected abstract boolean authenticate(String username, String password); } ================================================ FILE: core/src/main/java/org/wso2/msf4j/security/oauth2/IntrospectionResponse.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.security.oauth2; /** * Introspection response bean class. */ public final class IntrospectionResponse { /* * REQUIRED. Boolean indicator of whether or not the presented token is currently active. The specifics of a token's * "active" state will vary depending on the implementation of the authorization server and the information it keeps * about its tokens, but a "true" value return for the "active" property will generally indicate that a given token * has been issued by this authorization server, has not been revoked by the resource owner, and is within its given * time window of validity (e.g., after its issuance time and before its expiration time). See Section 4 for * information on implementation of such checks. */ public static final String ACTIVE = "active"; /* * OPTIONAL. A JSON string containing a space-separated list of scopes associated with this token, in the format * described in Section 3.3 of OAuth 2.0 */ public static final String SCOPE = "scope"; /* * OPTIONAL. Client identifier for the OAuth 2.0 client that requested this token. */ public static final String CLIENT_ID = "client_id"; /* * OPTIONAL. Human-readable identifier for the resource owner who authorized this token. */ public static final String USERNAME = "username"; /* * OPTIONAL. Type of the token as defined in Section 5.1 of OAuth 2.0 */ public static final String TOKEN_TYPE = "token_type"; /* * OPTIONAL. Integer time-stamp, measured in the number of seconds since January 1 1970 UTC, indicating when this * token is not to be used before, as defined in JWT */ public static final String NBF = "nbf"; /* * OPTIONAL. Service-specific string identifier or list of string identifiers representing the intended audience for * this token, as defined in JWT */ public static final String AUD = "aud"; /* * OPTIONAL. String representing the issuer of this token, as defined in JWT */ public static final String ISS = "iss"; /* * OPTIONAL. String identifier for the token, as defined in JWT */ public static final String JTI = "jti"; /* * OPTIONAL. Subject of the token, as defined in JWT [RFC7519]. Usually a machine-readable identifier of the * resource owner who authorized this token. */ public static final String SUB = "sub"; /* * OPTIONAL. Integer time-stamp, measured in the number of seconds since January 1 1970 UTC, indicating when this * token will expire, as defined in JWT */ public static final String EXP = "exp"; /* * OPTIONAL. Integer time-stamp, measured in the number of seconds since January 1 1970 UTC, indicating when this * token was originally issued, as defined in JWT */ public static final String IAT = "iat"; /* * The request is missing a required parameter, includes an unsupported parameter value (other than grant type), * repeats a parameter, includes multiple credentials, utilizes more than one mechanism for authenticating the * client, or is otherwise malformed. */ public static final String INVALID_REQUEST = "invalid_request"; /* * A single ASCII [USASCII] error code */ public static final String ERROR = "error"; /* * Human-readable ASCII [USASCII] text providing additional information, used to assist the client developer in * understanding the error that occurred. */ public static final String ERROR_DESCRIPTION = "error_description"; } ================================================ FILE: core/src/main/java/org/wso2/msf4j/security/oauth2/OAuth2SecurityInterceptor.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.security.oauth2; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import org.apache.commons.io.Charsets; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import org.wso2.msf4j.interceptor.RequestInterceptor; import org.wso2.msf4j.security.MSF4JSecurityException; import org.wso2.msf4j.security.SecurityErrorCode; import org.wso2.msf4j.util.SystemVariableUtil; import java.lang.reflect.Type; import java.net.HttpURLConnection; import java.net.URL; import java.util.Locale; import java.util.Map; import javax.ws.rs.HttpMethod; import javax.ws.rs.core.HttpHeaders; /** * Act as a security gateway for resources secured with Oauth2. *

* Verify Oauth2 access token in Authorization Bearer HTTP header and allow access to the resource accordingly. * * @since 1.0.0 */ public class OAuth2SecurityInterceptor implements RequestInterceptor { private static final Logger log = LoggerFactory.getLogger(OAuth2SecurityInterceptor.class); private static final String AUTHORIZATION_HTTP_HEADER = "Authorization"; private static final String AUTH_TYPE_OAUTH2 = "OAuth2"; private static final String BEARER_PREFIX = "bearer"; private static final String AUTH_SERVER_URL_KEY = "AUTH_SERVER_URL"; private static final String AUTH_SERVER_URL; private static final String TRUST_STORE = "TRUST_STORE"; private static final String TRUST_STORE_PASSWORD = "TRUST_STORE_PASSWORD"; static { AUTH_SERVER_URL = SystemVariableUtil.getValue(AUTH_SERVER_URL_KEY, null); if (AUTH_SERVER_URL == null) { throw new RuntimeException(AUTH_SERVER_URL_KEY + " is not specified."); } String trustStore = SystemVariableUtil.getValue(TRUST_STORE, null); String trustStorePassword = SystemVariableUtil.getValue(TRUST_STORE_PASSWORD, null); if (trustStore != null && !trustStore.isEmpty() && trustStorePassword != null && !trustStorePassword.isEmpty()) { System.setProperty("javax.net.ssl.trustStore", trustStore); System.setProperty("javax.net.ssl.trustStorePassword", trustStorePassword); } } @Override public boolean interceptRequest(Request request, Response response) throws Exception { SecurityErrorCode errorCode; try { HttpHeaders headers = request.getHeaders(); String authHeader = headers.getHeaderString(AUTHORIZATION_HTTP_HEADER); if (authHeader != null && !authHeader.isEmpty()) { return validateToken(authHeader); } else { throw new MSF4JSecurityException(SecurityErrorCode.AUTHENTICATION_FAILURE, "Missing Authorization header is the request.`"); } } catch (MSF4JSecurityException e) { errorCode = e.getErrorCode(); log.error(e.getMessage() + " Requested Path: " + request.getUri()); } handleSecurityError(errorCode, response); return false; } /** * Extract the accessToken from the give Authorization header value and validates the accessToken * with an external key manager. * * @param authHeader Authorization Bearer header which contains the access token * @return true if the token is a valid token */ private boolean validateToken(String authHeader) throws MSF4JSecurityException { // 1. Check whether this token is bearer token, if not return false String accessToken = extractAccessToken(authHeader); // 2. Send a request to key server's introspect endpoint to validate this token String responseStr = getValidatedTokenResponse(accessToken); Map responseData = getResponseDataMap(responseStr); //TODO handle NPE // 3. Process the response and return true if the token is valid. if (!Boolean.parseBoolean(responseData.get(IntrospectionResponse.ACTIVE))) { throw new MSF4JSecurityException(SecurityErrorCode.AUTHENTICATION_FAILURE, "Invalid Access token."); } return true; } /** * @param authHeader Authorization Bearer header which contains the access token * @return access token */ private String extractAccessToken(String authHeader) throws MSF4JSecurityException { authHeader = authHeader.trim(); if (authHeader.toLowerCase(Locale.US).startsWith(BEARER_PREFIX)) { // Split the auth header to get the access token. // Value should be in this format ("Bearer" 1*SP b64token) String[] authHeaderParts = authHeader.split(" "); if (authHeaderParts.length == 2) { return authHeaderParts[1]; } } throw new MSF4JSecurityException(SecurityErrorCode.INVALID_AUTHORIZATION_HEADER, "Invalid Authorization header: " + authHeader); } /** * Validated the given accessToken with an external key server. * * @param accessToken AccessToken to be validated. * @return the response from the key manager server. */ private String getValidatedTokenResponse(String accessToken) throws MSF4JSecurityException { URL url; try { url = new URL(AUTH_SERVER_URL); HttpURLConnection urlConn = (HttpURLConnection) url.openConnection(); urlConn.setDoOutput(true); urlConn.setRequestMethod(HttpMethod.POST); urlConn.getOutputStream() .write(("token=" + accessToken + "&token_type_hint=" + BEARER_PREFIX).getBytes(Charsets.UTF_8)); return new String(IOUtils.toByteArray(urlConn.getInputStream()), Charsets.UTF_8); } catch (java.io.IOException e) { log.error("Error invoking Authorization Server", e); throw new MSF4JSecurityException(SecurityErrorCode.GENERIC_ERROR, "Error invoking Authorization Server", e); } } /** * @param responseStr validated token response string returned from the key server. * @return a Map of key, value pairs available the response String. */ private Map getResponseDataMap(String responseStr) { Gson gson = new Gson(); Type typeOfMapOfStrings = new ExtendedTypeToken>() { }.getType(); return gson.fromJson(responseStr, typeOfMapOfStrings); } /** * This class extends the {@link TypeToken}. * Created due to the findbug issue when passing anonymous inner class. * * @param Generic type */ private static class ExtendedTypeToken extends TypeToken { } /** * @param errorCode Security error code * @param responder HttpResponder instance which is used send error messages back to the client */ private void handleSecurityError(SecurityErrorCode errorCode, Response responder) throws Exception { if (errorCode == SecurityErrorCode.AUTHENTICATION_FAILURE || errorCode == SecurityErrorCode.INVALID_AUTHORIZATION_HEADER) { responder.setStatus(javax.ws.rs.core.Response.Status.UNAUTHORIZED.getStatusCode()); responder.setHeader(javax.ws.rs.core.HttpHeaders.WWW_AUTHENTICATE, AUTH_TYPE_OAUTH2); } else if (errorCode == SecurityErrorCode.AUTHORIZATION_FAILURE) { responder.setStatus(javax.ws.rs.core.Response.Status.FORBIDDEN.getStatusCode()); } else { responder.setStatus(javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()); } } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/template/RuntimeTemplateException.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.template; /** * This runtime exception will be thrown if a runtime * error is occurred while processing a template. */ public class RuntimeTemplateException extends RuntimeException { public RuntimeTemplateException() { super(); } public RuntimeTemplateException(Exception cause) { super(cause); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/template/TemplateEngine.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.template; /** * MSF4J template engines should implement this interface. */ public interface TemplateEngine { /** * Renders the given model using the given template. * * @param view name of the template file in resources/templates directory * @param model model to be rendered from the template * @return rendered template */ String render(String view, Object model); } ================================================ FILE: core/src/main/java/org/wso2/msf4j/util/BufferUtil.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.util; import java.nio.Buffer; import java.nio.ByteBuffer; import java.util.List; /** * Utility methods for buffer operations. */ public class BufferUtil { /** * Merge a list of buffers into a single buffer. * * @param byteBuffers list of ByteBuffer objects * @return merged ByteBuffer */ public static ByteBuffer merge(List byteBuffers) { if (byteBuffers == null || byteBuffers.size() == 0) { return ByteBuffer.allocate(0); } else if (byteBuffers.size() == 1) { return byteBuffers.get(0); } else { ByteBuffer fullContent = ByteBuffer.allocate( byteBuffers.stream() .mapToInt(Buffer::capacity) .sum() ); byteBuffers.forEach(fullContent::put); fullContent.flip(); return fullContent; } } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/util/Defaults.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.util; import java.util.HashMap; import java.util.Map; import java.util.Objects; import javax.annotation.Nullable; /** * This class represents default values of primitive data types. */ public class Defaults { private Defaults() { } private static final Map, Object> DEFAULTS = new HashMap<>(8); static { // Only add to this map via put(Map, Class, T) DEFAULTS.put(boolean.class, false); DEFAULTS.put(char.class, '\0'); DEFAULTS.put(byte.class, (byte) 0); DEFAULTS.put(short.class, (short) 0); DEFAULTS.put(int.class, 0); DEFAULTS.put(long.class, 0L); DEFAULTS.put(float.class, 0f); DEFAULTS.put(double.class, 0d); } /** * Returns the default value of {@code type} as defined by JLS --- {@code 0} for numbers, {@code * false} for {@code boolean} and {@code '\0'} for {@code char}. For non-primitive types and * {@code void}, {@code null} is returned. * * @param type class of the value. * @return default value of given class. */ @Nullable public static T defaultValue(Class type) { // Primitives.wrap(type).cast(...) would avoid the warning, but we can't use that from here @SuppressWarnings("unchecked") // the put method enforces this key-value relationship T t = (T) DEFAULTS.get(Objects.requireNonNull(type)); return t; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/util/HttpUtil.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.util; import io.netty.buffer.Unpooled; import io.netty.handler.codec.http.DefaultHttpResponse; import io.netty.handler.codec.http.DefaultLastHttpContent; import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import org.wso2.transport.http.netty.message.HttpCarbonMessage; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import javax.ws.rs.core.HttpHeaders; /** * Utility methods related to HTTP. */ public class HttpUtil { public static final String EMPTY_BODY = ""; public static final String CLOSE = "close"; public static final String KEEP_ALIVE = "keep-alive"; /** * Create a CarbonMessage for a specific status code. * * @param status HTTP status code * @param msg message text * @return CarbonMessage representing the status */ public static HttpCarbonMessage createTextResponse(int status, String msg) { HttpCarbonMessage response = new HttpCarbonMessage( new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(status))); response.setHttpStatusCode(status); if (msg != null) { response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(msg.length())); byte[] msgArray = null; try { msgArray = msg.getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException("Failed to get the byte array from responseValue", e); } ByteBuffer byteBuffer = ByteBuffer.allocate(msgArray.length); byteBuffer.put(msgArray); byteBuffer.flip(); response.addHttpContent(new DefaultLastHttpContent(Unpooled.wrappedBuffer(byteBuffer))); } else { response.setHeader(HttpHeaders.CONTENT_LENGTH, "0"); } return response; } /** * Set connection header of the response object according to the * connection header of the request. * * @param request HTTP request object * @param response HTTP response object */ public static void setConnectionHeader(Request request, Response response) { String connection = request.getHeader(HttpHeaderNames.CONNECTION.toString()); if (connection != null && CLOSE.equalsIgnoreCase(connection)) { response.setHeader(HttpHeaderNames.CONNECTION.toString(), CLOSE); } else { response.setHeader(HttpHeaderNames.CONNECTION.toString(), KEEP_ALIVE); } } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/util/Primitives.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.util; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Set; /** * Contains static utility methods pertaining to primitive types and their corresponding wrapper * types. * */ public final class Primitives { private Primitives() { } /** * A map from primitive types to their corresponding wrapper types. */ private static final Map, Class> PRIMITIVE_TO_WRAPPER_TYPE; /** * A map from wrapper types to their corresponding primitive types. */ private static final Map, Class> WRAPPER_TO_PRIMITIVE_TYPE; // Sad that we can't use a BiMap. :( static { Map, Class> primToWrap = new HashMap, Class>(16); Map, Class> wrapToPrim = new HashMap, Class>(16); add(primToWrap, wrapToPrim, boolean.class, Boolean.class); add(primToWrap, wrapToPrim, byte.class, Byte.class); add(primToWrap, wrapToPrim, char.class, Character.class); add(primToWrap, wrapToPrim, double.class, Double.class); add(primToWrap, wrapToPrim, float.class, Float.class); add(primToWrap, wrapToPrim, int.class, Integer.class); add(primToWrap, wrapToPrim, long.class, Long.class); add(primToWrap, wrapToPrim, short.class, Short.class); add(primToWrap, wrapToPrim, void.class, Void.class); PRIMITIVE_TO_WRAPPER_TYPE = Collections.unmodifiableMap(primToWrap); WRAPPER_TO_PRIMITIVE_TYPE = Collections.unmodifiableMap(wrapToPrim); } private static void add(Map, Class> forward, Map, Class> backward, Class key, Class value) { forward.put(key, value); backward.put(value, key); } /** * Returns an immutable set of all nine primitive types (including {@code * void}). Note that a simpler way to test whether a {@code Class} instance is a member of this * set is to call {@link Class#isPrimitive}. * */ public static Set> allPrimitiveTypes() { return PRIMITIVE_TO_WRAPPER_TYPE.keySet(); } /** * Returns an immutable set of all nine primitive-wrapper types (including {@link Void}). * */ public static Set> allWrapperTypes() { return WRAPPER_TO_PRIMITIVE_TYPE.keySet(); } /** * Returns {@code true} if {@code type} is one of the nine primitive-wrapper types, such as * {@link Integer}. * * @see Class#isPrimitive */ public static boolean isWrapperType(Class type) { Objects.requireNonNull(type); return WRAPPER_TO_PRIMITIVE_TYPE.containsKey(type); } /** * 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) { Objects.requireNonNull(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; } /** * Returns the corresponding primitive type of {@code type} if it is a wrapper type; otherwise * returns {@code type} itself. Idempotent. *

*

     *     unwrap(Integer.class) == int.class
     *     unwrap(int.class) == int.class
     *     unwrap(String.class) == String.class
     * 
*/ public static Class unwrap(Class type) { Objects.requireNonNull(type); // cast is safe: long.class and Long.class are both of type Class @SuppressWarnings("unchecked") Class unwrapped = (Class) WRAPPER_TO_PRIMITIVE_TYPE.get(type); return (unwrapped == null) ? type : unwrapped; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/util/QueryStringDecoderUtil.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.util; import java.net.URI; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** * Splits an HTTP query string into a path string and key-value parameter pairs. * This decoder is for one time use only. Create a new instance for each URI: *
 * {@link QueryStringDecoderUtil} decoder = new {@link QueryStringDecoderUtil}("/hello?recipient=world&x=1;y=2");
 * assert decoder.path().equals("/hello");
 * assert decoder.parameters().get("recipient").get(0).equals("world");
 * assert decoder.parameters().get("x").get(0).equals("1");
 * assert decoder.parameters().get("y").get(0).equals("2");
 * 
*

* This decoder can also decode the content of an HTTP POST request whose * content type is application/x-www-form-urlencoded: *

 * {@link QueryStringDecoderUtil} decoder = new {@link QueryStringDecoderUtil}("recipient=world&x=1;y=2", false);
 * ...
 * 
* *

HashDOS vulnerability fix

* * As a workaround to the HashDOS vulnerability, the decoder * limits the maximum number of decoded key-value parameter pairs, up to {@literal 1024} by * default, and you can configure it when you construct the decoder by passing an additional * integer parameter. */ public class QueryStringDecoderUtil { private static final int DEFAULT_MAX_PARAMS = 1024; private final Charset charset; private final String uri; private final boolean hasPath; private final int maxParams; private String path; private Map> params; private int nParams; /** * Creates a new decoder that decodes the specified URI. The decoder will * assume that the query string is encoded in UTF-8. * * @param uri object to be decoded */ public QueryStringDecoderUtil(String uri) { this(uri, Charset.defaultCharset()); } /** * Creates a new decoder that decodes the specified URI encoded in the * specified charset. * * @param uri object to be decoded * @param hasPath whether uri contains path */ public QueryStringDecoderUtil(String uri, boolean hasPath) { this(uri, Charset.defaultCharset(), hasPath); } /** * Creates a new decoder that decodes the specified URI encoded in the * specified charset. * * @param uri object to be decoded * @param charset encoded charset */ public QueryStringDecoderUtil(String uri, Charset charset) { this(uri, charset, true); } /** * Creates a new decoder that decodes the specified URI encoded in the * specified charset. * * @param uri object to be decoded * @param charset encoded charset * @param hasPath whether uri contains path */ public QueryStringDecoderUtil(String uri, Charset charset, boolean hasPath) { this(uri, charset, hasPath, DEFAULT_MAX_PARAMS); } /** * Creates a new decoder that decodes the specified URI encoded in the * specified charset. * * @param uri object to be decoded * @param charset encoded charset * @param hasPath whether uri contains path * @param maxParams maximum no of parameters to decode */ public QueryStringDecoderUtil(String uri, Charset charset, boolean hasPath, int maxParams) { if (uri == null) { throw new NullPointerException("getUri"); } if (charset == null) { throw new NullPointerException("charset"); } if (maxParams <= 0) { throw new IllegalArgumentException( "maxParams: " + maxParams + " (expected: a positive integer)"); } this.uri = uri; this.charset = charset; this.maxParams = maxParams; this.hasPath = hasPath; } /** * Creates a new decoder that decodes the specified URI. The decoder will * assume that the query string is encoded in UTF-8. * * @param uri object to be decoded */ public QueryStringDecoderUtil(URI uri) { this(uri, Charset.defaultCharset()); } /** * Creates a new decoder that decodes the specified URI encoded in the * specified charset. * * @param uri object to be decoded * @param charset encoded charset */ public QueryStringDecoderUtil(URI uri, Charset charset) { this(uri, charset, DEFAULT_MAX_PARAMS); } /** * Creates a new decoder that decodes the specified URI encoded in the * specified charset. * * @param uri object to be decoded * @param charset encoded charset * @param maxParams maximum no of parameters to decode */ public QueryStringDecoderUtil(URI uri, Charset charset, int maxParams) { if (uri == null) { throw new NullPointerException("getUri"); } if (charset == null) { throw new NullPointerException("charset"); } if (maxParams <= 0) { throw new IllegalArgumentException( "maxParams: " + maxParams + " (expected: a positive integer)"); } String rawPath = uri.getRawPath(); if (rawPath != null) { hasPath = true; } else { rawPath = ""; hasPath = false; } // Also take care of cut of things like "http://localhost" this.uri = rawPath + (uri.getRawQuery() == null ? "" : '?' + uri.getRawQuery()); this.charset = charset; this.maxParams = maxParams; } /** * Returns the uri used to initialize this {@link QueryStringDecoderUtil}. * * @return uri object that initialize decoder */ public String uri() { return uri; } /** * Returns the decoded path string of the URI. * * @return decoded path string */ public String path() { if (path == null) { if (!hasPath) { return path = ""; } int pathEndPos = uri.indexOf('?'); if (pathEndPos < 0) { path = uri; } else { return path = uri.substring(0, pathEndPos); } } return path; } /** * Returns the decoded key-value parameter pairs of the URI. * * @return the decoded key-value parameter pairs of the URI */ public Map> parameters() { if (params == null) { if (hasPath) { int pathLength = path().length(); if (uri.length() == pathLength) { return Collections.emptyMap(); } decodeParams(uri.substring(pathLength + 1)); } else { if (uri.isEmpty()) { return Collections.emptyMap(); } decodeParams(uri); } } return params; } private void decodeParams(String s) { Map> params = this.params = new LinkedHashMap<>(); nParams = 0; String name = null; int pos = 0; // Beginning of the unprocessed region int i; // End of the unprocessed region char c; // Current character for (i = 0; i < s.length(); i++) { c = s.charAt(i); if (c == '=' && name == null) { if (pos != i) { name = decodeComponent(s.substring(pos, i), charset); } pos = i + 1; // http://www.w3.org/TR/html401/appendix/notes.html#h-B.2.2 } else if (c == '&' || c == ';') { if (name == null && pos != i) { // We haven't seen an `=' so far but moved forward. // Must be a param of the form '&a&' so add it with // an empty value. if (!addParam(params, decodeComponent(s.substring(pos, i), charset), "")) { return; } } else if (name != null) { if (!addParam(params, name, decodeComponent(s.substring(pos, i), charset))) { return; } name = null; } pos = i + 1; } } if (pos != i) { // Are there characters we haven't dealt with? if (name == null) { // Yes and we haven't seen any `='. addParam(params, decodeComponent(s.substring(pos, i), charset), ""); } else { // Yes and this must be the last value. addParam(params, name, decodeComponent(s.substring(pos, i), charset)); } } else if (name != null) { // Have we seen a name without value? addParam(params, name, ""); } } private boolean addParam(Map> params, String name, String value) { if (nParams >= maxParams) { return false; } List values = params.get(name); if (values == null) { values = new ArrayList<>(1); // Often there's only 1 value. params.put(name, values); } values.add(value); nParams++; return true; } /** * Decodes a bit of an URL encoded by a browser. *

* This is equivalent to calling {@link #decodeComponent(String, Charset)} * with the UTF-8 charset (recommended to comply with RFC 3986, Section 2). * * @param s The string to decode (can be empty). * @return The decoded string, or {@code s} if there's nothing to decode. * If the string to decode is {@code null}, returns an empty string. * @throws IllegalArgumentException if the string contains a malformed * escape sequence. */ public static String decodeComponent(final String s) { return decodeComponent(s, Charset.forName("UTF-8")); } /** * Decodes a bit of an URL encoded by a browser. *

* The string is expected to be encoded as per RFC 3986, Section 2. * This is the encoding used by JavaScript functions {@code encodeURI} * and {@code encodeURIComponent}, but not {@code escape}. For example * in this encoding, é (in Unicode {@code U+00E9} or in UTF-8 * {@code 0xC3 0xA9}) is encoded as {@code %C3%A9} or {@code %c3%a9}. *

* This is essentially equivalent to calling * URLDecoder.decode(s, charset.name()) * except that it's over 2x faster and generates less garbage for the GC. * Actually this function doesn't allocate any memory if there's nothing * to decode, the argument itself is returned. * * @param s The string to decode (can be empty). * @param charset The charset to use to decode the string * @return The decoded string, or {@code s} if there's nothing to decode. * If the string to decode is {@code null}, returns an empty string. * @throws IllegalArgumentException if the string contains a malformed * escape sequence. */ public static String decodeComponent(final String s, final Charset charset) { if (s == null) { return ""; } final int size = s.length(); boolean modified = false; for (int i = 0; i < size; i++) { final char c = s.charAt(i); if (c == '%' || c == '+') { modified = true; break; } } if (!modified) { return s; } final byte[] buf = new byte[size]; int pos = 0; // position in `buf'. for (int i = 0; i < size; i++) { char c = s.charAt(i); switch (c) { case '+': buf[pos++] = ' '; // "+" -> " " break; case '%': if (i == size - 1) { throw new IllegalArgumentException("unterminated escape" + " sequence at end of string: " + s); } c = s.charAt(++i); if (c == '%') { buf[pos++] = '%'; // "%%" -> "%" break; } if (i == size - 1) { throw new IllegalArgumentException("partial escape" + " sequence at end of string: " + s); } c = decodeHexNibble(c); final char c2 = decodeHexNibble(s.charAt(++i)); if (c == Character.MAX_VALUE || c2 == Character.MAX_VALUE) { throw new IllegalArgumentException( "invalid escape sequence `%" + s.charAt(i - 1) + s.charAt(i) + "' at index " + (i - 2) + " of: " + s); } c = (char) (c * 16 + c2); // Fall through. default: buf[pos++] = (byte) c; break; } } return new String(buf, 0, pos, charset); } /** * Helper to decode half of a hexadecimal number from a string. * * @param c The ASCII character of the hexadecimal number to decode. * Must be in the range {@code [0-9a-fA-F]}. * @return The hexadecimal value represented in the ASCII character * given, or {@link Character#MAX_VALUE} if the character is invalid. */ private static char decodeHexNibble(final char c) { if ('0' <= c && c <= '9') { return (char) (c - '0'); } else if ('a' <= c && c <= 'f') { return (char) (c - 'a' + 10); } else if ('A' <= c && c <= 'F') { return (char) (c - 'A' + 10); } else { return Character.MAX_VALUE; } } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/util/ReflectionUtils.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.util; import org.osgi.framework.Bundle; import org.osgi.framework.FrameworkUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Optional; /** * Util class to do java reflection related activities. */ public class ReflectionUtils { private static final Logger log = LoggerFactory.getLogger(ReflectionUtils.class); private ReflectionUtils() { } /** * Create a new instance for a given type. * * @param clazz type of the expected instance * @param constructorParameterTypes the parameter array * @param constructorArguments array of objects to be passed as arguments to the constructor call * @param Type of the object to be created * @return Object of type T * @throws NoSuchMethodException when the specified constructor is not available * @throws IllegalAccessException when the constructor is not visible * @throws InvocationTargetException when invocation target exception * @throws InstantiationException when error on object creation */ public static T createInstanceFromClass(Class clazz, Class[] constructorParameterTypes, Object... constructorArguments) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Constructor constructor = clazz.getConstructor(constructorParameterTypes); return constructor.newInstance(constructorArguments); } /** * Load class from bundle (find class from the bundles). * If there is no bundle with the specific class or loading the class from bundle fails, this method * will return Optional.empty() * This method is only sensible to be used in the OSGi environment * * @param clazz class to load * @param type of the class * @return Optional loaded class */ @SuppressWarnings("unchecked") public static Optional> loadClassFromBundle(Class clazz) { String className = clazz.getName(); Bundle bundle = FrameworkUtil.getBundle(clazz); if (bundle != null) { try { return Optional.of((Class) bundle.loadClass(className)); } catch (ClassNotFoundException e) { log.error("Class " + className + " do not exist in any bundle", e); } } return Optional.empty(); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/util/RuntimeAnnotations.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.util; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; /** * Utility class used to change the class annotation at runtime. * This class will be removed in future release. Since some of the classes that being used here might cause * issues in different JDKs. */ @Deprecated public final class RuntimeAnnotations { private static final Constructor annotationInvocationHandlerConstructor; private static final Constructor annotationDataConstructor; private static final Method classAnnotationData; private static final Field classClassRedefinedCount; private static final Field annotationDataAnnotations; private static final Field annotationDataDeclaredAnotations; private static final Method atomicClassAnnotationData; private static final Class atomicClass; static { // static initialization of necessary reflection Objects try { Class annotationInvocationHandlerClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); annotationInvocationHandlerConstructor = annotationInvocationHandlerClass.getDeclaredConstructor(new Class[] { Class.class, Map.class }); annotationInvocationHandlerConstructor.setAccessible(true); atomicClass = Class.forName("java.lang.Class$Atomic"); Class annotationDataClass = Class.forName("java.lang.Class$AnnotationData"); annotationDataConstructor = annotationDataClass.getDeclaredConstructor(new Class[] { Map.class, Map.class, int.class }); annotationDataConstructor.setAccessible(true); classAnnotationData = Class.class.getDeclaredMethod("annotationData"); AccessController.doPrivileged(new PrivilegedAction() { @Override public Object run() { classAnnotationData.setAccessible(true); return null; } }); classClassRedefinedCount = Class.class.getDeclaredField("classRedefinedCount"); AccessController.doPrivileged(new PrivilegedAction() { @Override public Void run() { classClassRedefinedCount.setAccessible(true); return null; } }); annotationDataAnnotations = annotationDataClass.getDeclaredField("annotations"); AccessController.doPrivileged(new PrivilegedAction() { @Override public Object run() { annotationDataAnnotations.setAccessible(true); return null; } }); annotationDataDeclaredAnotations = annotationDataClass.getDeclaredField("declaredAnnotations"); AccessController.doPrivileged(new PrivilegedAction() { @Override public Object run() { annotationDataDeclaredAnotations.setAccessible(true); return null; } }); atomicClassAnnotationData = atomicClass .getDeclaredMethod("casAnnotationData", Class.class, annotationDataClass, annotationDataClass); AccessController.doPrivileged(new PrivilegedAction() { @Override public Object run() { atomicClassAnnotationData.setAccessible(true); return null; } }); } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | NoSuchFieldException e) { throw new IllegalStateException(e); } } /** * Change the given annotation of the given class. * * @param clazz Class need to be change * @param annotationClass Annotation class that need to change * @param valuesMap value map to set * @param Annotation that get change */ public static void putAnnotation(Class clazz, Class annotationClass, Map valuesMap) { putAnnotation(clazz, annotationClass, annotationForMap(annotationClass, valuesMap)); } @SuppressWarnings("unchecked") private static void putAnnotation(Class c, Class annotationClass, T annotation) { try { while (true) { // retry loop int classRedefinedCount = classClassRedefinedCount.getInt(c); Object annotationData = classAnnotationData.invoke(c); // null or stale annotationData -> optimistically create new instance Object newAnnotationData = createAnnotationData(c, annotationData, annotationClass, annotation, classRedefinedCount); // try to install it if ((boolean) atomicClassAnnotationData.invoke(atomicClass, c, annotationData, newAnnotationData)) { // successfully installed new AnnotationData break; } } } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException | InstantiationException e) { throw new IllegalStateException(e); } } @SuppressWarnings("unchecked") private static Object createAnnotationData(Class c, Object annotationData, Class annotationClass, T annotation, int classRedefinedCount) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { Map, Annotation> annotations = (Map, Annotation>) annotationDataAnnotations.get(annotationData); Map, Annotation> declaredAnnotations = (Map, Annotation>) annotationDataDeclaredAnotations.get(annotationData); Map, Annotation> newDeclaredAnnotations = new LinkedHashMap<>(annotations); newDeclaredAnnotations.put(annotationClass, annotation); Map, Annotation> newAnnotations; if (declaredAnnotations == annotations) { newAnnotations = newDeclaredAnnotations; } else { newAnnotations = new LinkedHashMap<>(annotations); newAnnotations.put(annotationClass, annotation); } return annotationDataConstructor.newInstance(newAnnotations, newDeclaredAnnotations, classRedefinedCount); } @SuppressWarnings("unchecked") private static T annotationForMap(final Class annotationClass, final Map valuesMap) { return (T) AccessController.doPrivileged(new PrivilegedAction() { public Annotation run() { InvocationHandler handler; try { handler = (InvocationHandler) annotationInvocationHandlerConstructor .newInstance(annotationClass, new HashMap<>(valuesMap)); } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new IllegalStateException(e); } return (Annotation) Proxy .newProxyInstance(annotationClass.getClassLoader(), new Class[] { annotationClass }, handler); } }); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/util/SystemVariableUtil.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.util; import java.util.Map; import java.util.Properties; import java.util.stream.Collectors; /** * A utility which allows reading variables from the environment or System properties. * If the variable in available in the environment as well as a System property, the System property takes * precedence. */ public class SystemVariableUtil { private static final String CUSTOM_VAR_PREFIX = "CUSTOM_"; public static String getValue(String variableName, String defaultValue) { String value; if (System.getProperty(variableName) != null) { value = System.getProperty(variableName); } else if (System.getenv(variableName) != null) { value = System.getenv(variableName); } else { value = defaultValue; } return value; } public static Map getArbitraryAttributes() { Map arbitraryAttributes; Map environmentVariables = System.getenv(); arbitraryAttributes = environmentVariables.entrySet() .stream() .filter(entry -> entry.getKey().startsWith(CUSTOM_VAR_PREFIX)) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); Properties properties = System.getProperties(); arbitraryAttributes.putAll( properties.entrySet() .stream() .filter(entry -> ((String) entry.getKey()).startsWith(CUSTOM_VAR_PREFIX)) .collect(Collectors.toMap(entry -> (String) entry.getKey(), entry -> (String) entry.getValue())) ); return arbitraryAttributes; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/util/Utils.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.util; import org.wso2.carbon.config.ConfigurationException; import org.wso2.transport.http.netty.contract.config.TransportsConfiguration; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.CustomClassLoaderConstructor; import org.yaml.snakeyaml.introspector.BeanAccess; import java.lang.reflect.Field; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; /** * Utility class. */ public class Utils { /** * Return the String representation of given object. * * @param object Object to be reflected. * @return String representation of given object. */ public static String toString(Object object) { Objects.requireNonNull(object); StringBuilder sb = new StringBuilder(); try { for (Field field : object.getClass().getFields()) { sb.append(field.getName()).append(":").append(field.get(object)).append("\n"); } } catch (IllegalAccessException e) { throw new RuntimeException("Error while executing " + object.getClass() + ".toString()", e); } return sb.toString(); } /** * Return the String representation of given object's given fields list. * * @param object Object to be reflected. * @return String representation of given object. */ public static String toString(Object object, String[] fields) { Objects.requireNonNull(object); StringBuilder sb = new StringBuilder(); AccessController.doPrivileged(new PrivilegedAction() { @Override public Object run() { try { for (String field : fields) { Objects.requireNonNull(field); Field declaredField = object.getClass().getDeclaredField(field); declaredField.setAccessible(true); sb.append(field).append(":").append(declaredField.get(object)).append("\n"); declaredField.setAccessible(false); } return sb; } catch (IllegalAccessException e) { throw new RuntimeException("Error while executing " + object.getClass() + ".toString()", e); } catch (NoSuchFieldException e) { throw new RuntimeException("Error while executing " + object.getClass() + ".toString()", e); } } }); return sb.toString(); } /** * Split the given sequence with the given delimiter and return list of values. * * @param sequence String need to be splitted. * @param delimiter String delimiter * @param omitEmpty boolean need to skip empty values. * @return List of values obtained by splitting. */ public static List split(String sequence, String delimiter, boolean omitEmpty) { Objects.requireNonNull(sequence); Objects.requireNonNull(delimiter); String[] splittedValues = sequence.split(delimiter); List values = Arrays.asList(splittedValues); return omitEmpty ? values.stream().filter(value -> !value.isEmpty()).collect(Collectors.toList()) : values; } /** * Get the remaining count of intersection between 2 sets. * * @param set1 First set * @param set2 Second set * @return remaining count of intersection between 2 sets */ public static int getIntersection(Set set1, Set set2) { HashSet cloneSet1 = new HashSet<>(set1); HashSet cloneSet2 = new HashSet<>(set2); if (cloneSet1.size() > cloneSet2.size()) { cloneSet1.retainAll(cloneSet2); return cloneSet1.size(); } cloneSet2.retainAll(cloneSet1); return cloneSet2.size(); } public static TransportsConfiguration resolveTransportsNSConfiguration(Object transportsConfig) throws ConfigurationException { TransportsConfiguration transportsConfiguration; if (transportsConfig instanceof Map) { LinkedHashMap httpConfig = ((LinkedHashMap) ((Map) transportsConfig).get("http")); if (httpConfig != null) { String configYaml = new Yaml().dump(httpConfig); Yaml yaml = new Yaml(new CustomClassLoaderConstructor(TransportsConfiguration.class, TransportsConfiguration.class.getClassLoader(), new LoaderOptions())); yaml.setBeanAccess(BeanAccess.FIELD); transportsConfiguration = yaml.loadAs(configYaml, TransportsConfiguration.class); } else { transportsConfiguration = new TransportsConfiguration(); } } else { throw new ConfigurationException("The first level config under 'transports' namespace should be " + "a map."); } return transportsConfiguration; } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/websocket/WebSocketEndpoint.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.websocket; /** * Maker Interface for developers who are developing endpoints in OSGi environment. * This should be implemented if developer are creating a WebSocket endpoint */ public interface WebSocketEndpoint { } ================================================ FILE: core/src/main/java/org/wso2/msf4j/websocket/WebSocketEndpointsRegistry.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.websocket; import java.util.Set; /** * Registry interface for WebSocket Endpoints */ public interface WebSocketEndpointsRegistry { Set getAllEndpoints(); } ================================================ FILE: core/src/main/java/org/wso2/msf4j/websocket/exception/WebSocketEndpointAnnotationException.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.websocket.exception; /** * Throws when an exception occurred in WebSocket Endpoint annotations. */ public class WebSocketEndpointAnnotationException extends Exception { /** * @param message the message which should be shown with the exception. */ public WebSocketEndpointAnnotationException(String message) { super(message); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/websocket/exception/WebSocketEndpointMethodReturnTypeException.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.websocket.exception; /** * This is thrown when an invalid return type is defined in method. */ public class WebSocketEndpointMethodReturnTypeException extends Exception { /** * @param message The message which should be shown when the exception is thrown. */ public WebSocketEndpointMethodReturnTypeException(String message) { super(message); } } ================================================ FILE: core/src/main/java/org/wso2/msf4j/websocket/exception/WebSocketMethodParameterException.java ================================================ /* * Copyright (c) ${date}, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.websocket.exception; /** * This is thrown when a issue found it WebSocket method parameters. */ public class WebSocketMethodParameterException extends Exception { public WebSocketMethodParameterException(String message) { super(message); } } ================================================ FILE: core/src/main/resources/deployment.yaml ================================================ # Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved # # 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. # MSF4J configuration wso2.msf4j.configuration: # No of worker pool threads to handle MSF4J requests threadCount: 100 threadPoolName: msf4j.executor.workerpool ================================================ FILE: core/src/main/resources/log4j.properties ================================================ # Root logger option log4j.rootLogger=INFO, stdout # Direct log messages to stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target=System.out log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n ================================================ FILE: core/src/main/resources/mime-map.properties ================================================ 123=application/vnd.lotus-1-2-3 3dml=text/vnd.in3d.3dml 3ds=image/x-3ds 3g2=video/3gpp2 3gp=video/3gpp 7z=application/x-7z-compressed aab=application/x-authorware-bin aac=audio/x-aac aam=application/x-authorware-map aas=application/x-authorware-seg abs=audio/x-mpeg abw=application/x-abiword ac=application/pkix-attr-cert acc=application/vnd.americandynamics.acc ace=application/x-ace-compressed acu=application/vnd.acucobol acutc=application/vnd.acucorp adp=audio/adpcm aep=application/vnd.audiograph afm=application/x-font-type1 afp=application/vnd.ibm.modcap ahead=application/vnd.ahead.space ai=application/postscript aif=audio/x-aiff aifc=audio/x-aiff aiff=audio/x-aiff aim=application/x-aim air=application/vnd.adobe.air-application-installer-package+zip ait=application/vnd.dvb.ait ami=application/vnd.amiga.ami anx=application/annodex apk=application/vnd.android.package-archive appcache=text/cache-manifest application=application/x-ms-application apr=application/vnd.lotus-approach arc=application/x-freearc art=image/x-jg asc=application/pgp-signature asf=video/x-ms-asf asm=text/x-asm aso=application/vnd.accpac.simply.aso asx=video/x-ms-asf atc=application/vnd.acucorp atom=application/atom+xml atomcat=application/atomcat+xml atomsvc=application/atomsvc+xml atx=application/vnd.antix.game-component au=audio/basic avi=video/x-msvideo avx=video/x-rad-screenplay aw=application/applixware axa=audio/annodex axv=video/annodex azf=application/vnd.airzip.filesecure.azf azs=application/vnd.airzip.filesecure.azs azw=application/vnd.amazon.ebook bat=application/x-msdownload bcpio=application/x-bcpio bdf=application/x-font-bdf bdm=application/vnd.syncml.dm+wbxml bed=application/vnd.realvnc.bed bh2=application/vnd.fujitsu.oasysprs bin=application/octet-stream blb=application/x-blorb blorb=application/x-blorb bmi=application/vnd.bmi bmp=image/bmp body=text/html book=application/vnd.framemaker box=application/vnd.previewsystems.box boz=application/x-bzip2 bpk=application/octet-stream btif=image/prs.btif bz=application/x-bzip bz2=application/x-bzip2 c=text/x-c c11amc=application/vnd.cluetrust.cartomobile-config c11amz=application/vnd.cluetrust.cartomobile-config-pkg c4d=application/vnd.clonk.c4group c4f=application/vnd.clonk.c4group c4g=application/vnd.clonk.c4group c4p=application/vnd.clonk.c4group c4u=application/vnd.clonk.c4group cab=application/vnd.ms-cab-compressed caf=audio/x-caf cap=application/vnd.tcpdump.pcap car=application/vnd.curl.car cat=application/vnd.ms-pki.seccat cb7=application/x-cbr cba=application/x-cbr cbr=application/x-cbr cbt=application/x-cbr cbz=application/x-cbr cc=text/x-c cct=application/x-director ccxml=application/ccxml+xml cdbcmsg=application/vnd.contact.cmsg cdf=application/x-cdf cdkey=application/vnd.mediastation.cdkey cdmia=application/cdmi-capability cdmic=application/cdmi-container cdmid=application/cdmi-domain cdmio=application/cdmi-object cdmiq=application/cdmi-queue cdx=chemical/x-cdx cdxml=application/vnd.chemdraw+xml cdy=application/vnd.cinderella cer=application/pkix-cert cfs=application/x-cfs-compressed cgm=image/cgm chat=application/x-chat chm=application/vnd.ms-htmlhelp chrt=application/vnd.kde.kchart cif=chemical/x-cif cii=application/vnd.anser-web-certificate-issue-initiation cil=application/vnd.ms-artgalry cla=application/vnd.claymore class=application/java clkk=application/vnd.crick.clicker.keyboard clkp=application/vnd.crick.clicker.palette clkt=application/vnd.crick.clicker.template clkw=application/vnd.crick.clicker.wordbank clkx=application/vnd.crick.clicker clp=application/x-msclip cmc=application/vnd.cosmocaller cmdf=chemical/x-cmdf cml=chemical/x-cml cmp=application/vnd.yellowriver-custom-menu cmx=image/x-cmx cod=application/vnd.rim.cod com=application/x-msdownload conf=text/plain cpio=application/x-cpio cpp=text/x-c cpt=application/mac-compactpro crd=application/x-mscardfile crl=application/pkix-crl crt=application/x-x509-ca-cert cryptonote=application/vnd.rig.cryptonote csh=application/x-csh csml=chemical/x-csml csp=application/vnd.commonspace css=text/css cst=application/x-director csv=text/csv cu=application/cu-seeme curl=text/vnd.curl cww=application/prs.cww cxt=application/x-director cxx=text/x-c dae=model/vnd.collada+xml daf=application/vnd.mobius.daf dart=application/vnd.dart dataless=application/vnd.fdsn.seed davmount=application/davmount+xml dbk=application/docbook+xml dcr=application/x-director dcurl=text/vnd.curl.dcurl dd2=application/vnd.oma.dd2+xml ddd=application/vnd.fujixerox.ddd deb=application/x-debian-package def=text/plain deploy=application/octet-stream der=application/x-x509-ca-cert dfac=application/vnd.dreamfactory dgc=application/x-dgc-compressed dib=image/bmp dic=text/x-c dir=application/x-director dis=application/vnd.mobius.dis dist=application/octet-stream distz=application/octet-stream djv=image/vnd.djvu djvu=image/vnd.djvu dll=application/x-msdownload dmg=application/x-apple-diskimage dmp=application/vnd.tcpdump.pcap dms=application/octet-stream dna=application/vnd.dna doc=application/msword docm=application/vnd.ms-word.document.macroenabled.12 docx=application/vnd.openxmlformats-officedocument.wordprocessingml.document dot=application/msword dotm=application/vnd.ms-word.template.macroenabled.12 dotx=application/vnd.openxmlformats-officedocument.wordprocessingml.template dp=application/vnd.osgi.dp dpg=application/vnd.dpgraph dra=audio/vnd.dra dsc=text/prs.lines.tag dssc=application/dssc+der dtb=application/x-dtbook+xml dtd=application/xml-dtd dts=audio/vnd.dts dtshd=audio/vnd.dts.hd dump=application/octet-stream dv=video/x-dv dvb=video/vnd.dvb.file dvi=application/x-dvi dwf=model/vnd.dwf dwg=image/vnd.dwg dxf=image/vnd.dxf dxp=application/vnd.spotfire.dxp dxr=application/x-director ecelp4800=audio/vnd.nuera.ecelp4800 ecelp7470=audio/vnd.nuera.ecelp7470 ecelp9600=audio/vnd.nuera.ecelp9600 ecma=application/ecmascript edm=application/vnd.novadigm.edm edx=application/vnd.novadigm.edx efif=application/vnd.picsel ei6=application/vnd.pg.osasli elc=application/octet-stream emf=application/x-msmetafile eml=message/rfc822 emma=application/emma+xml emz=application/x-msmetafile eol=audio/vnd.digital-winds eot=application/vnd.ms-fontobject eps=application/postscript epub=application/epub+zip es3=application/vnd.eszigno3+xml esa=application/vnd.osgi.subsystem esf=application/vnd.epson.esf et3=application/vnd.eszigno3+xml etx=text/x-setext eva=application/x-eva evy=application/x-envoy exe=application/octet-stream exi=application/exi ext=application/vnd.novadigm.ext ez=application/andrew-inset ez2=application/vnd.ezpix-album ez3=application/vnd.ezpix-package f=text/x-fortran f4v=video/x-f4v f77=text/x-fortran f90=text/x-fortran fbs=image/vnd.fastbidsheet fcdt=application/vnd.adobe.formscentral.fcdt fcs=application/vnd.isac.fcs fdf=application/vnd.fdf fe_launch=application/vnd.denovo.fcselayout-link fg5=application/vnd.fujitsu.oasysgp fgd=application/x-director fh=image/x-freehand fh4=image/x-freehand fh5=image/x-freehand fh7=image/x-freehand fhc=image/x-freehand fig=application/x-xfig flac=audio/flac fli=video/x-fli flo=application/vnd.micrografx.flo flv=video/x-flv flw=application/vnd.kde.kivio flx=text/vnd.fmi.flexstor fly=text/vnd.fly fm=application/vnd.framemaker fnc=application/vnd.frogans.fnc for=text/x-fortran fpx=image/vnd.fpx frame=application/vnd.framemaker fsc=application/vnd.fsc.weblaunch fst=image/vnd.fst ftc=application/vnd.fluxtime.clip fti=application/vnd.anser-web-funds-transfer-initiation fvt=video/vnd.fvt fxp=application/vnd.adobe.fxp fxpl=application/vnd.adobe.fxp fzs=application/vnd.fuzzysheet g2w=application/vnd.geoplan g3=image/g3fax g3w=application/vnd.geospace gac=application/vnd.groove-account gam=application/x-tads gbr=application/rpki-ghostbusters gca=application/x-gca-compressed gdl=model/vnd.gdl geo=application/vnd.dynageo gex=application/vnd.geometry-explorer ggb=application/vnd.geogebra.file ggt=application/vnd.geogebra.tool ghf=application/vnd.groove-help gif=image/gif gim=application/vnd.groove-identity-message gml=application/gml+xml gmx=application/vnd.gmx gnumeric=application/x-gnumeric gph=application/vnd.flographit gpx=application/gpx+xml gqf=application/vnd.grafeq gqs=application/vnd.grafeq gram=application/srgs gramps=application/x-gramps-xml gre=application/vnd.geometry-explorer grv=application/vnd.groove-injector grxml=application/srgs+xml gsf=application/x-font-ghostscript gtar=application/x-gtar gtm=application/vnd.groove-tool-message gtw=model/vnd.gtw gv=text/vnd.graphviz gxf=application/gxf gxt=application/vnd.geonext gz=application/x-gzip h=text/x-c h261=video/h261 h263=video/h263 h264=video/h264 hal=application/vnd.hal+xml hbci=application/vnd.hbci hdf=application/x-hdf hh=text/x-c hlp=application/winhlp hpgl=application/vnd.hp-hpgl hpid=application/vnd.hp-hpid hps=application/vnd.hp-hps hqx=application/mac-binhex40 htc=text/x-component htke=application/vnd.kenameaapp htm=text/html html=text/html hvd=application/vnd.yamaha.hv-dic hvp=application/vnd.yamaha.hv-voice hvs=application/vnd.yamaha.hv-script i2g=application/vnd.intergeo icc=application/vnd.iccprofile ice=x-conference/x-cooltalk icm=application/vnd.iccprofile ico=image/x-icon ics=text/calendar ief=image/ief ifb=text/calendar ifm=application/vnd.shana.informed.formdata iges=model/iges igl=application/vnd.igloader igm=application/vnd.insors.igm igs=model/iges igx=application/vnd.micrografx.igx iif=application/vnd.shana.informed.interchange imp=application/vnd.accpac.simply.imp ims=application/vnd.ms-ims in=text/plain ink=application/inkml+xml inkml=application/inkml+xml install=application/x-install-instructions iota=application/vnd.astraea-software.iota ipfix=application/ipfix ipk=application/vnd.shana.informed.package irm=application/vnd.ibm.rights-management irp=application/vnd.irepository.package+xml iso=application/x-iso9660-image itp=application/vnd.shana.informed.formtemplate ivp=application/vnd.immervision-ivp ivu=application/vnd.immervision-ivu jad=text/vnd.sun.j2me.app-descriptor jam=application/vnd.jam jar=application/java-archive java=text/x-java-source jisp=application/vnd.jisp jlt=application/vnd.hp-jlyt jnlp=application/x-java-jnlp-file joda=application/vnd.joost.joda-archive jpe=image/jpeg jpeg=image/jpeg jpg=image/jpeg jpgm=video/jpm jpgv=video/jpeg jpm=video/jpm js=application/javascript jsf=text/plain json=application/json jsonml=application/jsonml+json jspf=text/plain kar=audio/midi karbon=application/vnd.kde.karbon kfo=application/vnd.kde.kformula kia=application/vnd.kidspiration kml=application/vnd.google-earth.kml+xml kmz=application/vnd.google-earth.kmz kne=application/vnd.kinar knp=application/vnd.kinar kon=application/vnd.kde.kontour kpr=application/vnd.kde.kpresenter kpt=application/vnd.kde.kpresenter kpxx=application/vnd.ds-keypoint ksp=application/vnd.kde.kspread ktr=application/vnd.kahootz ktx=image/ktx ktz=application/vnd.kahootz kwd=application/vnd.kde.kword kwt=application/vnd.kde.kword lasxml=application/vnd.las.las+xml latex=application/x-latex lbd=application/vnd.llamagraphics.life-balance.desktop lbe=application/vnd.llamagraphics.life-balance.exchange+xml les=application/vnd.hhe.lesson-player lha=application/x-lzh-compressed link66=application/vnd.route66.link66+xml list=text/plain list3820=application/vnd.ibm.modcap listafp=application/vnd.ibm.modcap lnk=application/x-ms-shortcut log=text/plain lostxml=application/lost+xml lrf=application/octet-stream lrm=application/vnd.ms-lrm ltf=application/vnd.frogans.ltf lvp=audio/vnd.lucent.voice lwp=application/vnd.lotus-wordpro lzh=application/x-lzh-compressed m13=application/x-msmediaview m14=application/x-msmediaview m1v=video/mpeg m21=application/mp21 m2a=audio/mpeg m2v=video/mpeg m3a=audio/mpeg m3u=audio/x-mpegurl m3u8=application/vnd.apple.mpegurl m4a=audio/mp4 m4b=audio/mp4 m4r=audio/mp4 m4u=video/vnd.mpegurl m4v=video/mp4 ma=application/mathematica mac=image/x-macpaint mads=application/mads+xml mag=application/vnd.ecowin.chart maker=application/vnd.framemaker man=text/troff mar=application/octet-stream mathml=application/mathml+xml mb=application/mathematica mbk=application/vnd.mobius.mbk mbox=application/mbox mc1=application/vnd.medcalcdata mcd=application/vnd.mcd mcurl=text/vnd.curl.mcurl mdb=application/x-msaccess mdi=image/vnd.ms-modi me=text/troff mesh=model/mesh meta4=application/metalink4+xml metalink=application/metalink+xml mets=application/mets+xml mfm=application/vnd.mfmp mft=application/rpki-manifest mgp=application/vnd.osgeo.mapguide.package mgz=application/vnd.proteus.magazine mid=audio/midi midi=audio/midi mie=application/x-mie mif=application/x-mif mime=message/rfc822 mj2=video/mj2 mjp2=video/mj2 mk3d=video/x-matroska mka=audio/x-matroska mks=video/x-matroska mkv=video/x-matroska mlp=application/vnd.dolby.mlp mmd=application/vnd.chipnuts.karaoke-mmd mmf=application/vnd.smaf mmr=image/vnd.fujixerox.edmics-mmr mng=video/x-mng mny=application/x-msmoney mobi=application/x-mobipocket-ebook mods=application/mods+xml mov=video/quicktime movie=video/x-sgi-movie mp1=audio/mpeg mp2=audio/mpeg mp21=application/mp21 mp2a=audio/mpeg mp3=audio/mpeg mp4=video/mp4 mp4a=audio/mp4 mp4s=application/mp4 mp4v=video/mp4 mpa=audio/mpeg mpc=application/vnd.mophun.certificate mpe=video/mpeg mpeg=video/mpeg mpega=audio/x-mpeg mpg=video/mpeg mpg4=video/mp4 mpga=audio/mpeg mpkg=application/vnd.apple.installer+xml mpm=application/vnd.blueice.multipass mpn=application/vnd.mophun.application mpp=application/vnd.ms-project mpt=application/vnd.ms-project mpv2=video/mpeg2 mpy=application/vnd.ibm.minipay mqy=application/vnd.mobius.mqy mrc=application/marc mrcx=application/marcxml+xml ms=text/troff mscml=application/mediaservercontrol+xml mseed=application/vnd.fdsn.mseed mseq=application/vnd.mseq msf=application/vnd.epson.msf msh=model/mesh msi=application/x-msdownload msl=application/vnd.mobius.msl msty=application/vnd.muvee.style mts=model/vnd.mts mus=application/vnd.musician musicxml=application/vnd.recordare.musicxml+xml mvb=application/x-msmediaview mwf=application/vnd.mfer mxf=application/mxf mxl=application/vnd.recordare.musicxml mxml=application/xv+xml mxs=application/vnd.triscape.mxs mxu=video/vnd.mpegurl n-gage=application/vnd.nokia.n-gage.symbian.install n3=text/n3 nb=application/mathematica nbp=application/vnd.wolfram.player nc=application/x-netcdf ncx=application/x-dtbncx+xml nfo=text/x-nfo ngdat=application/vnd.nokia.n-gage.data nitf=application/vnd.nitf nlu=application/vnd.neurolanguage.nlu nml=application/vnd.enliven nnd=application/vnd.noblenet-directory nns=application/vnd.noblenet-sealer nnw=application/vnd.noblenet-web npx=image/vnd.net-fpx nsc=application/x-conference nsf=application/vnd.lotus-notes ntf=application/vnd.nitf nzb=application/x-nzb oa2=application/vnd.fujitsu.oasys2 oa3=application/vnd.fujitsu.oasys3 oas=application/vnd.fujitsu.oasys obd=application/x-msbinder obj=application/x-tgif oda=application/oda odb=application/vnd.oasis.opendocument.database odc=application/vnd.oasis.opendocument.chart odf=application/vnd.oasis.opendocument.formula odft=application/vnd.oasis.opendocument.formula-template odg=application/vnd.oasis.opendocument.graphics odi=application/vnd.oasis.opendocument.image odm=application/vnd.oasis.opendocument.text-master odp=application/vnd.oasis.opendocument.presentation ods=application/vnd.oasis.opendocument.spreadsheet odt=application/vnd.oasis.opendocument.text oga=audio/ogg ogg=audio/ogg ogv=video/ogg ogx=application/ogg omdoc=application/omdoc+xml onepkg=application/onenote onetmp=application/onenote onetoc=application/onenote onetoc2=application/onenote opf=application/oebps-package+xml opml=text/x-opml oprc=application/vnd.palm org=application/vnd.lotus-organizer osf=application/vnd.yamaha.openscoreformat osfpvg=application/vnd.yamaha.openscoreformat.osfpvg+xml otc=application/vnd.oasis.opendocument.chart-template otf=application/x-font-otf otg=application/vnd.oasis.opendocument.graphics-template oth=application/vnd.oasis.opendocument.text-web oti=application/vnd.oasis.opendocument.image-template otp=application/vnd.oasis.opendocument.presentation-template ots=application/vnd.oasis.opendocument.spreadsheet-template ott=application/vnd.oasis.opendocument.text-template oxps=application/oxps oxt=application/vnd.openofficeorg.extension p=text/x-pascal p10=application/pkcs10 p12=application/x-pkcs12 p7b=application/x-pkcs7-certificates p7c=application/pkcs7-mime p7m=application/pkcs7-mime p7r=application/x-pkcs7-certreqresp p7s=application/pkcs7-signature p8=application/pkcs8 pas=text/x-pascal paw=application/vnd.pawaafile pbd=application/vnd.powerbuilder6 pbm=image/x-portable-bitmap pcap=application/vnd.tcpdump.pcap pcf=application/x-font-pcf pcl=application/vnd.hp-pcl pclxl=application/vnd.hp-pclxl pct=image/pict pcurl=application/vnd.curl.pcurl pcx=image/x-pcx pdb=application/vnd.palm pdf=application/pdf pfa=application/x-font-type1 pfb=application/x-font-type1 pfm=application/x-font-type1 pfr=application/font-tdpfr pfx=application/x-pkcs12 pgm=image/x-portable-graymap pgn=application/x-chess-pgn pgp=application/pgp-encrypted pic=image/pict pict=image/pict pkg=application/octet-stream pki=application/pkixcmp pkipath=application/pkix-pkipath plb=application/vnd.3gpp.pic-bw-large plc=application/vnd.mobius.plc plf=application/vnd.pocketlearn pls=audio/x-scpls pml=application/vnd.ctc-posml png=image/png pnm=image/x-portable-anymap pnt=image/x-macpaint portpkg=application/vnd.macports.portpkg pot=application/vnd.ms-powerpoint potm=application/vnd.ms-powerpoint.template.macroenabled.12 potx=application/vnd.openxmlformats-officedocument.presentationml.template ppam=application/vnd.ms-powerpoint.addin.macroenabled.12 ppd=application/vnd.cups-ppd ppm=image/x-portable-pixmap pps=application/vnd.ms-powerpoint ppsm=application/vnd.ms-powerpoint.slideshow.macroenabled.12 ppsx=application/vnd.openxmlformats-officedocument.presentationml.slideshow ppt=application/vnd.ms-powerpoint pptm=application/vnd.ms-powerpoint.presentation.macroenabled.12 pptx=application/vnd.openxmlformats-officedocument.presentationml.presentation pqa=application/vnd.palm prc=application/x-mobipocket-ebook pre=application/vnd.lotus-freelance prf=application/pics-rules ps=application/postscript psb=application/vnd.3gpp.pic-bw-small psd=image/vnd.adobe.photoshop psf=application/x-font-linux-psf pskcxml=application/pskc+xml ptid=application/vnd.pvi.ptid1 pub=application/x-mspublisher pvb=application/vnd.3gpp.pic-bw-var pwn=application/vnd.3m.post-it-notes pya=audio/vnd.ms-playready.media.pya pyv=video/vnd.ms-playready.media.pyv qam=application/vnd.epson.quickanime qbo=application/vnd.intu.qbo qfx=application/vnd.intu.qfx qps=application/vnd.publishare-delta-tree qt=video/quicktime qti=image/x-quicktime qtif=image/x-quicktime qwd=application/vnd.quark.quarkxpress qwt=application/vnd.quark.quarkxpress qxb=application/vnd.quark.quarkxpress qxd=application/vnd.quark.quarkxpress qxl=application/vnd.quark.quarkxpress qxt=application/vnd.quark.quarkxpress ra=audio/x-pn-realaudio ram=audio/x-pn-realaudio rar=application/x-rar-compressed ras=image/x-cmu-raster rcprofile=application/vnd.ipunplugged.rcprofile rdf=application/rdf+xml rdz=application/vnd.data-vision.rdz rep=application/vnd.businessobjects res=application/x-dtbresource+xml rgb=image/x-rgb rif=application/reginfo+xml rip=audio/vnd.rip ris=application/x-research-info-systems rl=application/resource-lists+xml rlc=image/vnd.fujixerox.edmics-rlc rld=application/resource-lists-diff+xml rm=application/vnd.rn-realmedia rmi=audio/midi rmp=audio/x-pn-realaudio-plugin rms=application/vnd.jcp.javame.midlet-rms rmvb=application/vnd.rn-realmedia-vbr rnc=application/relax-ng-compact-syntax roa=application/rpki-roa roff=text/troff rp9=application/vnd.cloanto.rp9 rpss=application/vnd.nokia.radio-presets rpst=application/vnd.nokia.radio-preset rq=application/sparql-query rs=application/rls-services+xml rsd=application/rsd+xml rss=application/rss+xml rtf=application/rtf rtx=text/richtext s=text/x-asm s3m=audio/s3m saf=application/vnd.yamaha.smaf-audio sbml=application/sbml+xml sc=application/vnd.ibm.secure-container scd=application/x-msschedule scm=application/vnd.lotus-screencam scq=application/scvp-cv-request scs=application/scvp-cv-response scurl=text/vnd.curl.scurl sda=application/vnd.stardivision.draw sdc=application/vnd.stardivision.calc sdd=application/vnd.stardivision.impress sdkd=application/vnd.solent.sdkm+xml sdkm=application/vnd.solent.sdkm+xml sdp=application/sdp sdw=application/vnd.stardivision.writer see=application/vnd.seemail seed=application/vnd.fdsn.seed sema=application/vnd.sema semd=application/vnd.semd semf=application/vnd.semf ser=application/java-serialized-object setpay=application/set-payment-initiation setreg=application/set-registration-initiation sfd-hdstx=application/vnd.hydrostatix.sof-data sfs=application/vnd.spotfire.sfs sfv=text/x-sfv sgi=image/sgi sgl=application/vnd.stardivision.writer-global sgm=text/sgml sgml=text/sgml sh=application/x-sh shar=application/x-shar shf=application/shf+xml sid=image/x-mrsid-image sig=application/pgp-signature sil=audio/silk silo=model/mesh sis=application/vnd.symbian.install sisx=application/vnd.symbian.install sit=application/x-stuffit sitx=application/x-stuffitx skd=application/vnd.koan skm=application/vnd.koan skp=application/vnd.koan skt=application/vnd.koan sldm=application/vnd.ms-powerpoint.slide.macroenabled.12 sldx=application/vnd.openxmlformats-officedocument.presentationml.slide slt=application/vnd.epson.salt sm=application/vnd.stepmania.stepchart smf=application/vnd.stardivision.math smi=application/smil+xml smil=application/smil+xml smv=video/x-smv smzip=application/vnd.stepmania.package snd=audio/basic snf=application/x-font-snf so=application/octet-stream spc=application/x-pkcs7-certificates spf=application/vnd.yamaha.smaf-phrase spl=application/x-futuresplash spot=text/vnd.in3d.spot spp=application/scvp-vp-response spq=application/scvp-vp-request spx=audio/ogg sql=application/x-sql src=application/x-wais-source srt=application/x-subrip sru=application/sru+xml srx=application/sparql-results+xml ssdl=application/ssdl+xml sse=application/vnd.kodak-descriptor ssf=application/vnd.epson.ssf ssml=application/ssml+xml st=application/vnd.sailingtracker.track stc=application/vnd.sun.xml.calc.template std=application/vnd.sun.xml.draw.template stf=application/vnd.wt.stf sti=application/vnd.sun.xml.impress.template stk=application/hyperstudio stl=application/vnd.ms-pki.stl str=application/vnd.pg.format stw=application/vnd.sun.xml.writer.template sub=text/vnd.dvb.subtitle sus=application/vnd.sus-calendar susp=application/vnd.sus-calendar sv4cpio=application/x-sv4cpio sv4crc=application/x-sv4crc svc=application/vnd.dvb.service svd=application/vnd.svd svg=image/svg+xml svgz=image/svg+xml swa=application/x-director swf=application/x-shockwave-flash swi=application/vnd.aristanetworks.swi sxc=application/vnd.sun.xml.calc sxd=application/vnd.sun.xml.draw sxg=application/vnd.sun.xml.writer.global sxi=application/vnd.sun.xml.impress sxm=application/vnd.sun.xml.math sxw=application/vnd.sun.xml.writer t=text/troff t3=application/x-t3vm-image taglet=application/vnd.mynfc tao=application/vnd.tao.intent-module-archive tar=application/x-tar tcap=application/vnd.3gpp2.tcap tcl=application/x-tcl teacher=application/vnd.smart.teacher tei=application/tei+xml teicorpus=application/tei+xml tex=application/x-tex texi=application/x-texinfo texinfo=application/x-texinfo text=text/plain tfi=application/thraud+xml tfm=application/x-tex-tfm tga=image/x-tga thmx=application/vnd.ms-officetheme tif=image/tiff tiff=image/tiff tmo=application/vnd.tmobile-livetv torrent=application/x-bittorrent tpl=application/vnd.groove-tool-template tpt=application/vnd.trid.tpt tr=text/troff tra=application/vnd.trueapp trm=application/x-msterminal tsd=application/timestamped-data tsv=text/tab-separated-values ttc=application/x-font-ttf ttf=application/x-font-ttf ttl=text/turtle twd=application/vnd.simtech-mindmapper twds=application/vnd.simtech-mindmapper txd=application/vnd.genomatix.tuxedo txf=application/vnd.mobius.txf txt=text/plain u32=application/x-authorware-bin udeb=application/x-debian-package ufd=application/vnd.ufdl ufdl=application/vnd.ufdl ulw=audio/basic ulx=application/x-glulx umj=application/vnd.umajin unityweb=application/vnd.unity uoml=application/vnd.uoml+xml uri=text/uri-list uris=text/uri-list urls=text/uri-list ustar=application/x-ustar utz=application/vnd.uiq.theme uu=text/x-uuencode uva=audio/vnd.dece.audio uvd=application/vnd.dece.data uvf=application/vnd.dece.data uvg=image/vnd.dece.graphic uvh=video/vnd.dece.hd uvi=image/vnd.dece.graphic uvm=video/vnd.dece.mobile uvp=video/vnd.dece.pd uvs=video/vnd.dece.sd uvt=application/vnd.dece.ttml+xml uvu=video/vnd.uvvu.mp4 uvv=video/vnd.dece.video uvva=audio/vnd.dece.audio uvvd=application/vnd.dece.data uvvf=application/vnd.dece.data uvvg=image/vnd.dece.graphic uvvh=video/vnd.dece.hd uvvi=image/vnd.dece.graphic uvvm=video/vnd.dece.mobile uvvp=video/vnd.dece.pd uvvs=video/vnd.dece.sd uvvt=application/vnd.dece.ttml+xml uvvu=video/vnd.uvvu.mp4 uvvv=video/vnd.dece.video uvvx=application/vnd.dece.unspecified uvvz=application/vnd.dece.zip uvx=application/vnd.dece.unspecified uvz=application/vnd.dece.zip vcard=text/vcard vcd=application/x-cdlink vcf=text/x-vcard vcg=application/vnd.groove-vcard vcs=text/x-vcalendar vcx=application/vnd.vcx vis=application/vnd.visionary viv=video/vnd.vivo vob=video/x-ms-vob vor=application/vnd.stardivision.writer vox=application/x-authorware-bin vrml=model/vrml vsd=application/vnd.visio vsf=application/vnd.vsf vss=application/vnd.visio vst=application/vnd.visio vsw=application/vnd.visio vtu=model/vnd.vtu vxml=application/voicexml+xml w3d=application/x-director wad=application/x-doom wav=audio/x-wav wax=audio/x-ms-wax wbmp=image/vnd.wap.wbmp wbs=application/vnd.criticaltools.wbs+xml wbxml=application/vnd.wap.wbxml wcm=application/vnd.ms-works wdb=application/vnd.ms-works wdp=image/vnd.ms-photo weba=audio/webm webm=video/webm webp=image/webp wg=application/vnd.pmi.widget wgt=application/widget wks=application/vnd.ms-works wm=video/x-ms-wm wma=audio/x-ms-wma wmd=application/x-ms-wmd wmf=application/x-msmetafile wml=text/vnd.wap.wml wmlc=application/vnd.wap.wmlc wmls=text/vnd.wap.wmlscript wmlsc=application/vnd.wap.wmlscriptc wmv=video/x-ms-wmv wmx=video/x-ms-wmx wmz=application/x-msmetafile woff=application/x-font-woff wpd=application/vnd.wordperfect wpl=application/vnd.ms-wpl wps=application/vnd.ms-works wqd=application/vnd.wqd wri=application/x-mswrite wrl=model/vrml wsdl=application/wsdl+xml wspolicy=application/wspolicy+xml wtb=application/vnd.webturbo wvx=video/x-ms-wvx x32=application/x-authorware-bin x3d=model/x3d+xml x3db=model/x3d+binary x3dbz=model/x3d+binary x3dv=model/x3d+vrml x3dvz=model/x3d+vrml x3dz=model/x3d+xml xaml=application/xaml+xml xap=application/x-silverlight-app xar=application/vnd.xara xbap=application/x-ms-xbap xbd=application/vnd.fujixerox.docuworks.binder xbm=image/x-xbitmap xdf=application/xcap-diff+xml xdm=application/vnd.syncml.dm+xml xdp=application/vnd.adobe.xdp+xml xdssc=application/dssc+xml xdw=application/vnd.fujixerox.docuworks xenc=application/xenc+xml xer=application/patch-ops-error+xml xfdf=application/vnd.adobe.xfdf xfdl=application/vnd.xfdl xht=application/xhtml+xml xhtml=application/xhtml+xml xhvml=application/xv+xml xif=image/vnd.xiff xla=application/vnd.ms-excel xlam=application/vnd.ms-excel.addin.macroenabled.12 xlc=application/vnd.ms-excel xlf=application/x-xliff+xml xlm=application/vnd.ms-excel xls=application/vnd.ms-excel xlsb=application/vnd.ms-excel.sheet.binary.macroenabled.12 xlsm=application/vnd.ms-excel.sheet.macroenabled.12 xlsx=application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlt=application/vnd.ms-excel xltm=application/vnd.ms-excel.template.macroenabled.12 xltx=application/vnd.openxmlformats-officedocument.spreadsheetml.template xlw=application/vnd.ms-excel xm=audio/xm xml=application/xml xo=application/vnd.olpc-sugar xop=application/xop+xml xpi=application/x-xpinstall xpl=application/xproc+xml xpm=image/x-xpixmap xpr=application/vnd.is-xpr xps=application/vnd.ms-xpsdocument xpw=application/vnd.intercon.formnet xpx=application/vnd.intercon.formnet xsl=application/xml xslt=application/xslt+xml xsm=application/vnd.syncml+xml xspf=application/xspf+xml xul=application/vnd.mozilla.xul+xml xvm=application/xv+xml xvml=application/xv+xml xwd=image/x-xwindowdump xyz=chemical/x-xyz xz=application/x-xz yang=application/yang yin=application/yin+xml z=application/x-compress Z=application/x-compress z1=application/x-zmachine z2=application/x-zmachine z3=application/x-zmachine z4=application/x-zmachine z5=application/x-zmachine z6=application/x-zmachine z7=application/x-zmachine z8=application/x-zmachine zaz=application/vnd.zzazz.deck+xml zip=application/zip zir=application/vnd.zul zirz=application/vnd.zul zmm=application/vnd.handheld-entertainment+xml ================================================ FILE: core/src/test/java/org/wso2/msf4j/DeprecatedInterceptorTest.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.wso2.msf4j.conf.Constants; import org.wso2.msf4j.interceptor.TestInterceptor; import org.wso2.msf4j.service.TestMicroServiceWithDynamicPath; import org.wso2.msf4j.service.TestMicroservice; import java.net.URI; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import javax.ws.rs.core.Response; import static org.testng.AssertJUnit.assertEquals; /** * Tests handler interceptor. */ public class DeprecatedInterceptorTest extends InterceptorTestBase { private final TestInterceptor interceptor1 = new TestInterceptor(); private final TestInterceptor interceptor2 = new TestInterceptor(); private final TestMicroservice testMicroservice = new TestMicroservice(); private static final int port = Constants.PORT + 7; private MicroservicesRunner microservicesRunner; @BeforeClass public void setup() throws Exception { microservicesRunner = new MicroservicesRunner(port); microservicesRunner .deploy(testMicroservice) .addInterceptor(interceptor1, interceptor2) .start(); microservicesRunner.deploy("/DynamicPath", new TestMicroServiceWithDynamicPath()); microservicesRunner.deploy("/DynamicPath2", new TestMicroServiceWithDynamicPath()); baseURI = URI.create("http://" + Constants.HOSTNAME + ":" + port); Thread.sleep(100); } @AfterClass public void teardown() throws Exception { microservicesRunner.stop(); } @BeforeMethod public void reset() { interceptor1.reset(); interceptor2.reset(); } @Test public void testPreInterceptorReject() throws Exception { Map headers = new HashMap<>(); headers.put("X-Request-Type", "Reject"); int status = doGetAndGetStatusCode("/test/v1/resource", false, headers); assertEquals(Response.Status.NOT_ACCEPTABLE.getStatusCode(), status); // Wait for any post handlers to be called TimeUnit.MILLISECONDS.sleep(100); assertEquals(1, interceptor1.getNumPreCalls()); // The second pre-call should not have happened due to rejection by the first pre-call // None of the post calls should have happened. assertEquals(0, interceptor1.getNumPostCalls()); assertEquals(0, interceptor2.getNumPreCalls()); assertEquals(0, interceptor2.getNumPostCalls()); } @Test public void testPreException() throws Exception { Map headers = new HashMap<>(); headers.put("X-Request-Type", "PreException"); int status = doGetAndGetStatusCode("/test/v1/resource", false, headers); assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), status); // Wait for any post handlers to be called TimeUnit.MILLISECONDS.sleep(100); assertEquals(1, interceptor1.getNumPreCalls()); // The second pre-call should not have happened due to exception in the first pre-call // None of the post calls should have happened. assertEquals(0, interceptor1.getNumPostCalls()); assertEquals(0, interceptor2.getNumPreCalls()); assertEquals(0, interceptor2.getNumPostCalls()); } @Test public void testPostException() throws Exception { Map headers = new HashMap<>(); headers.put("X-Request-Type", "PostException"); int status = doGetAndGetStatusCode("/test/v1/resource", false, headers); assertEquals(Response.Status.OK.getStatusCode(), status); assertEquals(1, interceptor1.getNumPreCalls()); assertEquals(1, interceptor1.getNumPostCalls()); assertEquals(1, interceptor2.getNumPreCalls()); assertEquals(1, interceptor2.getNumPostCalls()); } @Test public void testUnknownPath() throws Exception { int status = doGetAndGetStatusCode("/unknown/path/test/v1/resource", false, Collections.unmodifiableMap(Collections.emptyMap())); assertEquals(Response.Status.NOT_FOUND.getStatusCode(), status); // Wait for any post handlers to be called TimeUnit.MILLISECONDS.sleep(100); assertEquals(0, interceptor1.getNumPreCalls()); assertEquals(0, interceptor1.getNumPostCalls()); assertEquals(0, interceptor2.getNumPreCalls()); assertEquals(0, interceptor2.getNumPostCalls()); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/ExtendedServiceTest.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import io.netty.handler.codec.http.HttpHeaderNames; import org.apache.commons.io.Charsets; import org.apache.commons.io.IOUtils; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import org.wso2.msf4j.conf.Constants; import org.wso2.msf4j.service.ExtendedTestMicroservice; import org.wso2.msf4j.service.TestMicroServiceWithDynamicPath; import org.wso2.msf4j.service.TestMicroservice; import java.io.IOException; import java.lang.reflect.Type; import java.net.HttpURLConnection; import java.net.URI; import java.net.URL; import java.util.Map; import javax.ws.rs.HttpMethod; import static org.testng.AssertJUnit.assertEquals; /** * Test for extended (inherited) microservices. */ public class ExtendedServiceTest { public static final String HEADER_VAL_CLOSE = "CLOSE"; protected static final Type STRING_MAP_TYPE = new TypeToken>() { }.getType(); protected static final Gson GSON = new Gson(); private final TestMicroservice testMicroservice = new ExtendedTestMicroservice(); private static final int port = Constants.PORT + 39; protected static URI baseURI; private MicroservicesRunner microservicesRunner; @BeforeClass public void setup() throws Exception { baseURI = URI.create(String.format("http://%s:%d", Constants.HOSTNAME, port)); microservicesRunner = new MicroservicesRunner(port); microservicesRunner .deploy(testMicroservice) .start(); microservicesRunner.deploy("/DynamicPath", new TestMicroServiceWithDynamicPath()); microservicesRunner.deploy("/DynamicPath2", new TestMicroServiceWithDynamicPath()); Thread.sleep(1000); } @AfterClass public void teardown() throws Exception { microservicesRunner.stop(); } /** * Tests that an overridden method gets invoked. * * @throws IOException */ @Test public void testValidEndPoints() throws IOException { HttpURLConnection urlConn = request("/ext-test/v1/resource?num=10", HttpMethod.GET); assertEquals(200, urlConn.getResponseCode()); String content = getContent(urlConn); Map map = GSON.fromJson(content, STRING_MAP_TYPE); assertEquals(1, map.size()); assertEquals("Handled extended get in resource end-point", map.get("status")); urlConn.disconnect(); } @Test public void testPutWithData() throws IOException { HttpURLConnection urlConn = request("/ext-test/v1/facebook/1/message", HttpMethod.PUT); writeContent(urlConn, "Hello, World"); assertEquals(200, urlConn.getResponseCode()); String content = getContent(urlConn); Map map = GSON.fromJson(content, STRING_MAP_TYPE); assertEquals(1, map.size()); assertEquals("Handled put in tweets end-point, id: 1. Content: Hello, World", map.get("result")); urlConn.disconnect(); } private HttpURLConnection request(String path, String method) throws IOException { return request(path, method, false); } private HttpURLConnection request(String path, String method, boolean keepAlive) throws IOException { URL url = baseURI.resolve(path).toURL(); HttpURLConnection urlConn = (HttpURLConnection) url.openConnection(); if (method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT)) { urlConn.setDoOutput(true); } urlConn.setRequestMethod(method); if (!keepAlive) { urlConn.setRequestProperty(HttpHeaderNames.CONNECTION.toString(), HEADER_VAL_CLOSE); } return urlConn; } private String getContent(HttpURLConnection urlConn) throws IOException { return new String(IOUtils.toByteArray(urlConn.getInputStream()), Charsets.UTF_8); } protected void writeContent(HttpURLConnection urlConn, String content) throws IOException { urlConn.getOutputStream().write(content.getBytes(Charsets.UTF_8)); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/HostBindingTest.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j; import org.testng.Assert; import org.testng.annotations.Test; import org.wso2.msf4j.service.TestMicroservice; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.NetworkInterface; import java.net.Socket; import java.net.SocketException; import java.util.Enumeration; /** * Testing the "msf4j.host" environment variable. */ public class HostBindingTest { private final TestMicroservice testMicroservice = new TestMicroservice(); /** * Testing happy path. */ @Test public void testHostForMicroserviceRunner() throws InterruptedException { MicroservicesRunner microservicesRunner = new MicroservicesRunner(3333); microservicesRunner.deploy(testMicroservice); microservicesRunner.start(); Thread.sleep(100); Assert.assertTrue(this.isHostPortAvailable("127.0.0.1", 3333), "Unable to connect to service started on 127.0.0.1"); Assert.assertTrue(this.isHostPortAvailable("localhost", 3333), "Unable to connect to service started on localhost"); microservicesRunner.stop(); } /** * Hosting a test microservice on 127.0.0.1 and check if it can be accessed with 127.0.0.1 and that it cannot be * accessed through any other network ip. */ @Test public void testDifferentHostForMicroserviceRunner() throws SocketException, InterruptedException { System.setProperty("msf4j.host", "127.0.0.1"); MicroservicesRunner microservicesRunner = new MicroservicesRunner(4444); microservicesRunner.deploy(testMicroservice); microservicesRunner.start(); Thread.sleep(100); Assert.assertTrue(this.isHostPortAvailable("127.0.0.1", 4444), "Unable to connect to service started on 127.0.0.1"); Assert.assertTrue(this.isHostPortAvailable("localhost", 4444), "Unable to connect to service started on localhost"); Enumeration e = NetworkInterface.getNetworkInterfaces(); while (e.hasMoreElements()) { NetworkInterface n = (NetworkInterface) e.nextElement(); Enumeration ee = n.getInetAddresses(); while (ee.hasMoreElements()) { InetAddress i = (InetAddress) ee.nextElement(); String networkHost = i.getHostAddress(); if (!networkHost.equals("127.0.0.1") && !networkHost.equals("localhost")) { Assert.assertFalse(this.isHostPortAvailable(networkHost, 4444), "Should not be able to connect on " + networkHost); } } } System.clearProperty("msf4j.host"); microservicesRunner.stop(); } /** * Check if a port is open with a given host. An SO timeout of 3000 milliseconds is used. * @param host The host to be checked. * @param port The port to be checked. * @return True if available, else false. */ private boolean isHostPortAvailable(String host, int port) { try { boolean isConnected; Socket client = new Socket(); client.connect(new InetSocketAddress(host, port), 3000); isConnected = client.isConnected(); client.close(); return isConnected; } catch (IOException ignored) { return false; } } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/HttpResourceModelTest.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j; import org.testng.annotations.Test; import org.wso2.msf4j.internal.router.HttpResourceModel; import static org.testng.AssertJUnit.assertTrue; /** * Tests functionality fo HttpResourceModel. */ public class HttpResourceModelTest { @Test public void testStreamingReqSupportedCheckWhenStreamingSupported() throws NoSuchMethodException { TestClass testObj = new TestClass(); HttpResourceModel httpResourceModel = new HttpResourceModel("", testObj.getClass() .getMethod("methodWithHttpStreaming", HttpStreamer.class), testObj, false); assertTrue(httpResourceModel.isStreamingReqSupported()); assertTrue(httpResourceModel.isStreamingReqSupported()); } @Test public void testStreamingReqSupportedCheckWhenStreamingUnsupported() throws NoSuchMethodException { TestClass testObj = new TestClass(); HttpResourceModel httpResourceModel = new HttpResourceModel("", testObj.getClass() .getMethod("methodWithNoHttpStreaming", Object.class), testObj, false); assertTrue(!httpResourceModel.isStreamingReqSupported()); assertTrue(!httpResourceModel.isStreamingReqSupported()); } /** * Test class used for testing HttpResourceMethod functionality. */ private static class TestClass { public void methodWithHttpStreaming(HttpStreamer httpStreamer) { } public void methodWithNoHttpStreaming(Object object) { } } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/HttpServerTest.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j; import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; import io.netty.handler.codec.http.HttpHeaderNames; import org.apache.commons.io.Charsets; import org.apache.commons.io.IOUtils; import org.apache.http.HttpEntity; import org.apache.http.entity.ContentType; import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.entity.mime.content.FileBody; import org.apache.http.entity.mime.content.StringBody; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import org.wso2.msf4j.beanconversion.BeanConversionException; import org.wso2.msf4j.conf.Constants; import org.wso2.msf4j.exception.TestExceptionMapper; import org.wso2.msf4j.exception.TestExceptionMapper2; import org.wso2.msf4j.formparam.util.StreamUtil; import org.wso2.msf4j.internal.beanconversion.BeanConverter; import org.wso2.msf4j.pojo.Category; import org.wso2.msf4j.pojo.Pet; import org.wso2.msf4j.pojo.TextBean; import org.wso2.msf4j.pojo.XmlBean; import org.wso2.msf4j.service.SecondService; import org.wso2.msf4j.service.TestMicroServiceWithDynamicPath; import org.wso2.msf4j.service.TestMicroservice; import org.wso2.msf4j.service.sub.Player; import org.wso2.msf4j.service.sub.Team; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; import java.io.RandomAccessFile; import java.lang.reflect.Type; import java.net.HttpURLConnection; import java.net.Socket; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.IntStream; import javax.ws.rs.HttpMethod; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertTrue; import static org.testng.AssertJUnit.fail; /** * Test the HttpServer. */ public class HttpServerTest { public static final String HEADER_VAL_CLOSE = "CLOSE"; protected static final Type STRING_MAP_TYPE = new TypeToken>() { }.getType(); protected static final Gson GSON = new Gson(); public static File tmpFolder; static { try { tmpFolder = Files.createTempDirectory("msf4j").toFile(); } catch (IOException e) { throw new RuntimeException("Error while creating tenp directory", e); } } private final TestMicroservice testMicroservice = new TestMicroservice(); private final SecondService secondService = new SecondService(); private static final int port = Constants.PORT + 1; protected static URI baseURI; private MicroservicesRunner microservicesRunner; private MicroservicesRunner secondMicroservicesRunner; @BeforeClass public void setup() throws Exception { baseURI = URI.create(String.format("http://%s:%d", Constants.HOSTNAME, port)); microservicesRunner = new MicroservicesRunner(port); microservicesRunner .addExceptionMapper(new TestExceptionMapper(), new TestExceptionMapper2()) .deploy(testMicroservice) .start(); microservicesRunner.deploy("/DynamicPath", new TestMicroServiceWithDynamicPath()); microservicesRunner.deploy("/DynamicPath2", new TestMicroServiceWithDynamicPath()); secondMicroservicesRunner = new MicroservicesRunner(port + 1); secondMicroservicesRunner.deploy(secondService).start(); } @AfterClass public void teardown() throws Exception { microservicesRunner.stop(); secondMicroservicesRunner.stop(); } @Test public void testMultipleMicroServiceRunners() throws IOException { HttpURLConnection urlConn = request("/SecondService/addNumbers/9/25", HttpMethod.GET, false, baseURI.getPort() + 1); assertEquals(200, urlConn.getResponseCode()); String content = getContent(urlConn); assertEquals(34, Integer.parseInt(content)); urlConn.disconnect(); } @Test public void testDynamicMicroserviceRegistration() throws IOException { HttpURLConnection urlConn = request("/DynamicPath/hello/MSF4J", HttpMethod.GET); assertEquals(200, urlConn.getResponseCode()); String content = getContent(urlConn); assertEquals("Hello MSF4J", content); urlConn.disconnect(); urlConn = request("/DynamicPath2/hello/MSF4J", HttpMethod.GET); assertEquals(200, urlConn.getResponseCode()); content = getContent(urlConn); assertEquals("Hello MSF4J", content); urlConn.disconnect(); } @Test public void testValidEndPoints() throws IOException { HttpURLConnection urlConn = request("/test/v1/resource?num=10", HttpMethod.GET); assertEquals(200, urlConn.getResponseCode()); String content = getContent(urlConn); Map map = GSON.fromJson(content, STRING_MAP_TYPE); assertEquals(1, map.size()); assertEquals("Handled get in resource end-point", map.get("status")); urlConn.disconnect(); urlConn = request("/test/v1/tweets/1", HttpMethod.GET); assertEquals(200, urlConn.getResponseCode()); content = getContent(urlConn); map = GSON.fromJson(content, STRING_MAP_TYPE); assertEquals(1, map.size()); assertEquals("Handled get in tweets end-point, id: 1", map.get("status")); urlConn.disconnect(); } @Test public void testSmallFileUpload() throws IOException { testStreamUpload(10, "testSmallFileUpload.txt"); } @Test public void testLargeFileUpload() throws IOException { testStreamUpload(1000000, "testLargeFileUpload.txt"); } protected void testStreamUpload(int size, String filename) throws IOException { //create a random file to be uploaded. File fname = new File(tmpFolder, filename); fname.createNewFile(); RandomAccessFile randf = new RandomAccessFile(fname, "rw"); String contentStr = IntStream.range(0, size) .mapToObj(value -> String.valueOf((int) (Math.random() * 1000))) .collect(Collectors.joining("")); randf.write(contentStr.getBytes(Charsets.UTF_8)); randf.close(); //test stream upload HttpURLConnection urlConn = request("/test/v1/stream/upload", HttpMethod.PUT); Files.copy(Paths.get(fname.toURI()), urlConn.getOutputStream()); assertEquals(200, urlConn.getResponseCode()); String contentFromServer = getContent(urlConn); assertEquals(contentStr, contentFromServer); urlConn.disconnect(); fname.delete(); } // @Test public void testStreamUploadFailure() throws IOException { //create a random file to be uploaded. int size = 20 * 1024; File fname = new File(tmpFolder, "testStreamUploadFailure.txt"); fname.createNewFile(); RandomAccessFile randf = new RandomAccessFile(fname, "rw"); randf.setLength(size); randf.close(); HttpURLConnection urlConn = request("/test/v1/stream/upload/fail", HttpMethod.PUT); Files.copy(Paths.get(fname.toURI()), urlConn.getOutputStream()); assertEquals(500, urlConn.getResponseCode()); urlConn.disconnect(); fname.delete(); } @Test public void testChunkAggregatedUpload() throws IOException { //create a random file to be uploaded. int size = 69 * 1024; File fname = new File(tmpFolder, "testChunkAggregatedUpload.txt"); fname.createNewFile(); RandomAccessFile randf = new RandomAccessFile(fname, "rw"); randf.setLength(size); randf.close(); //test chunked upload HttpURLConnection urlConn = request("/test/v1/aggregate/upload", HttpMethod.PUT); urlConn.setChunkedStreamingMode(1024); Files.copy(Paths.get(fname.toURI()), urlConn.getOutputStream()); assertEquals(200, urlConn.getResponseCode()); assertEquals(size, Integer.parseInt(getContent(urlConn).split(":")[1].trim())); urlConn.disconnect(); fname.delete(); } // @Test public void testChunkAggregatedUploadFailure() throws IOException { //create a random file to be uploaded. int size = 78 * 1024; File fname = new File(tmpFolder, "testChunkAggregatedUploadFailure.txt"); fname.createNewFile(); RandomAccessFile randf = new RandomAccessFile(fname, "rw"); randf.setLength(size); randf.close(); //test chunked upload HttpURLConnection urlConn = request("/test/v1/aggregate/upload", HttpMethod.PUT); urlConn.setChunkedStreamingMode(1024); Files.copy(Paths.get(fname.toURI()), urlConn.getOutputStream()); assertEquals(500, urlConn.getResponseCode()); urlConn.disconnect(); fname.delete(); } @Test public void testPathWithMultipleMethods() throws IOException { HttpURLConnection urlConn = request("/test/v1/tweets/1", HttpMethod.GET); assertEquals(200, urlConn.getResponseCode()); urlConn.disconnect(); urlConn = request("/test/v1/tweets/1", HttpMethod.PUT); writeContent(urlConn, "data"); assertEquals(200, urlConn.getResponseCode()); urlConn.disconnect(); } @Test public void testNonExistingEndPoints() throws IOException { HttpURLConnection urlConn = request("/test/v1/users", HttpMethod.POST); writeContent(urlConn, "data"); assertEquals(404, urlConn.getResponseCode()); urlConn.disconnect(); } @Test public void testPutWithData() throws IOException { HttpURLConnection urlConn = request("/test/v1/facebook/1/message", HttpMethod.PUT); writeContent(urlConn, "Hello, World"); assertEquals(200, urlConn.getResponseCode()); String content = getContent(urlConn); Map map = GSON.fromJson(content, STRING_MAP_TYPE); assertEquals(1, map.size()); assertEquals("Handled put in tweets end-point, id: 1. Content: Hello, World", map.get("result")); urlConn.disconnect(); } @Test public void testPostWithData() throws IOException { HttpURLConnection urlConn = request("/test/v1/facebook/1/message", HttpMethod.POST); writeContent(urlConn, "Hello, World"); assertEquals(200, urlConn.getResponseCode()); String content = getContent(urlConn); Map map = GSON.fromJson(content, STRING_MAP_TYPE); assertEquals(1, map.size()); assertEquals("Handled post in tweets end-point, id: 1. Content: Hello, World", map.get("result")); urlConn.disconnect(); } @Test public void testNonExistingMethods() throws IOException { HttpURLConnection urlConn = request("/test/v1/facebook/1/message", HttpMethod.GET); assertEquals(405, urlConn.getResponseCode()); urlConn.disconnect(); } @Test public void testMultiplePathParameters() throws IOException { HttpURLConnection urlConn = request("/test/v1/user/sree/message/12", HttpMethod.GET); assertEquals(200, urlConn.getResponseCode()); String content = getContent(urlConn); Map map = GSON.fromJson(content, STRING_MAP_TYPE); assertEquals(1, map.size()); assertEquals("Handled multiple path parameters sree 12", map.get("result")); urlConn.disconnect(); } //Test the end point where the parameter in path and order of declaration in method signature are different @Test public void testMultiplePathParametersWithParamterInDifferentOrder() throws IOException { HttpURLConnection urlConn = request("/test/v1/message/21/user/sree", HttpMethod.GET); assertEquals(200, urlConn.getResponseCode()); String content = getContent(urlConn); Map map = GSON.fromJson(content, STRING_MAP_TYPE); assertEquals(1, map.size()); assertEquals("Handled multiple path parameters sree 21", map.get("result")); urlConn.disconnect(); } @Test public void testNotRoutablePathParamMismatch() throws IOException { HttpURLConnection urlConn = request("/test/v1/NotRoutable/sree", HttpMethod.GET); assertEquals(500, urlConn.getResponseCode()); urlConn.disconnect(); } @Test public void testMultiMatchParamPut() throws Exception { HttpURLConnection urlConn = request("/test/v1/multi-match/bar", HttpMethod.PUT); assertEquals(405, urlConn.getResponseCode()); urlConn.disconnect(); } @Test public void testHandlerException() throws Exception { HttpURLConnection urlConn = request("/test/v1/uexception", HttpMethod.GET); assertEquals(500, urlConn.getResponseCode()); // assertEquals("Exception encountered while processing request : User Exception", // new String(ByteStreams.toByteArray(urlConn.getErrorStream()), Charsets.UTF_8)); urlConn.disconnect(); } /** * Test that the TestChannelHandler that was added using the builder adds the correct header field and value. * * @throws Exception */ /*@Test public void testChannelPipelineModification() throws Exception { HttpURLConnection urlConn = request("/test/v1/tweets/1", HttpMethod.GET); assertEquals(200, urlConn.getResponseCode()); assertEquals(urlConn.getHeaderField(TestChannelHandler.HEADER_FIELD), TestChannelHandler.HEADER_VALUE); }*/ @Test public void testMultiMatchFoo() throws Exception { testContent("/test/v1/multi-match/foo", "multi-match-get-actual-foo"); } @Test public void testMultiMatchAll() throws Exception { testContent("/test/v1/multi-match/foo/baz/id", "multi-match-*"); } @Test public void testMultiMatchParam() throws Exception { testContent("/test/v1/multi-match/bar", "multi-match-param-bar"); } @Test public void testMultiMatchParamBar() throws Exception { testContent("/test/v1/multi-match/id/bar", "multi-match-param-bar-id"); } @Test public void testMultiMatchFooParamBar() throws Exception { testContent("/test/v1/multi-match/foo/id/bar", "multi-match-foo-param-bar-id"); } @Test public void testMultiMatchFooBarParam() throws Exception { testContent("/test/v1/multi-match/foo/bar/id", "multi-match-foo-bar-param-id"); } @Test public void testMultiMatchFooBarParamId() throws Exception { testContent("/test/v1/multi-match/foo/bar/bar/bar", "multi-match-foo-bar-param-bar-id-bar"); } @Test public void testMultiMatchFooPut() throws Exception { testContent("/test/v1/multi-match/foo", "multi-match-put-actual-foo", HttpMethod.PUT); } //@Test public void testChunkResponse() throws IOException { HttpURLConnection urlConn = request("/test/v1/chunk", HttpMethod.POST); try { writeContent(urlConn, "Testing message"); String response = getContent(urlConn); assertEquals("Testing message", response); } finally { urlConn.disconnect(); } } @Test public void testStringQueryParam() throws IOException { // First send without query, for String type, should get defaulted to null. testContent("/test/v1/stringQueryParam/mypath", "mypath:null", HttpMethod.GET); // Then send with query, should response with the given name. testContent("/test/v1/stringQueryParam/mypath?name=netty", "mypath:netty", HttpMethod.GET); } @Test public void testPrimitiveQueryParam() throws IOException { // For primitive type, if missing parameter, should get defaulted to Java primitive default value. testContent("/test/v1/primitiveQueryParam", "0", HttpMethod.GET); testContent("/test/v1/primitiveQueryParam?age=20", "20", HttpMethod.GET); } @Test public void testSortedSetQueryParam() throws IOException { // For collection, if missing parameter, should get defaulted to empty collection testContent("/test/v1/sortedSetQueryParam", "", HttpMethod.GET); // Try different way of passing the ids, they should end up de-dup and sorted. testContent("/test/v1/sortedSetQueryParam?id=30&id=10&id=20&id=30", "10,20,30", HttpMethod.GET); testContent("/test/v1/sortedSetQueryParam?id=10&id=30&id=20&id=20", "10,20,30", HttpMethod.GET); testContent("/test/v1/sortedSetQueryParam?id=20&id=30&id=20&id=10", "10,20,30", HttpMethod.GET); } @Test public void testListHeaderParam() throws IOException { HttpURLConnection urlConn = request("/test/v1/listHeaderParam", HttpMethod.GET); urlConn.addRequestProperty("name", "name1,name3,name2,name1"); assertEquals(200, urlConn.getResponseCode()); assertEquals("name1,name3,name2,name1", getContent(urlConn)); urlConn.disconnect(); } @Test public void testHeaderResponse() throws IOException { HttpURLConnection urlConn = request("/test/v1/headerResponse", HttpMethod.GET); urlConn.addRequestProperty("name", "name1"); assertEquals(200, urlConn.getResponseCode()); assertEquals("name1", urlConn.getHeaderField("name")); urlConn.disconnect(); } @Test public void testDefaultQueryParam() throws IOException { // Submit with no parameters. Each should get the default values. HttpURLConnection urlConn = request("/test/v1/defaultValue", HttpMethod.GET); assertEquals(200, urlConn.getResponseCode()); JsonObject json = GSON.fromJson(getContent(urlConn), JsonObject.class); Type hobbyType = new TypeToken>() { }.getType(); assertEquals(30, json.get("age").getAsLong()); assertEquals("hello", json.get("name").getAsString()); assertEquals(Collections.singletonList("casking"), GSON.fromJson(json.get("hobby").getAsJsonArray(), hobbyType)); urlConn.disconnect(); } @Test(timeOut = 5000) public void testConnectionClose() throws Exception { URL url = baseURI.resolve("/test/v1/connectionClose").toURL(); // Fire http request using raw socket so that we can verify the connection get closed by the server // after the response. Socket socket = createRawSocket(url); try { PrintStream printer = new PrintStream(socket.getOutputStream(), false, "UTF-8"); printer.printf("GET %s HTTP/1.1\r\n", url.getPath()); printer.printf("Host: %s:%d\r\n", url.getHost(), url.getPort()); printer.printf ("%s: close\r\n", HttpHeaderNames.CONNECTION.toString()); printer.print("\r\n"); printer.flush(); // Just read everything from the response. Since the server will close the connection, the read loop should // end with an EOF. Otherwise there will be timeout of this test case String response = IOUtils.toString(new InputStreamReader(socket.getInputStream(), Charsets.UTF_8)); assertTrue(response.startsWith("HTTP/1.1 200 OK")); } finally { socket.close(); } } @Test public void testUploadReject() throws Exception { HttpURLConnection urlConn = request("/test/v1/uploadReject", HttpMethod.POST, true); try { urlConn.setChunkedStreamingMode(1024); urlConn.getOutputStream().write("Rejected Content".getBytes(Charsets.UTF_8)); try { urlConn.getInputStream(); fail(); } catch (IOException e) { // Expect to get exception since server response with 400. Just drain the error stream. IOUtils.toByteArray(urlConn.getErrorStream()); assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), urlConn.getResponseCode()); } } finally { urlConn.disconnect(); } } @Test public void testNoPathGetMethod() throws Exception { HttpURLConnection urlConn = request("/test/v1", HttpMethod.GET); assertEquals("no-@Path-GET", getContent(urlConn)); urlConn.disconnect(); } @Test public void testNoPathPostMethod() throws Exception { HttpURLConnection urlConn = request("/test/v1", HttpMethod.POST); assertEquals("no-@Path-POST", getContent(urlConn)); urlConn.disconnect(); } @Test public void testNoPathPutMethod() throws Exception { HttpURLConnection urlConn = request("/test/v1", HttpMethod.PUT); assertEquals("no-@Path-PUT", getContent(urlConn)); urlConn.disconnect(); } @Test public void testNoPathDeleteMethod() throws Exception { HttpURLConnection urlConn = request("/test/v1", HttpMethod.DELETE); assertEquals("no-@Path-DELETE", getContent(urlConn)); urlConn.disconnect(); } @Test public void testSleep() throws Exception { HttpURLConnection urlConn = request("/test/v1/sleep/10", HttpMethod.GET); assertEquals(200, urlConn.getResponseCode()); urlConn.disconnect(); } @Test public void testWrongMethod() throws IOException { HttpURLConnection urlConn = request("/test/v1/customException", HttpMethod.GET); assertEquals(Response.Status.METHOD_NOT_ALLOWED.getStatusCode(), urlConn.getResponseCode()); urlConn.disconnect(); } @Test public void testExceptionHandler() throws IOException { HttpURLConnection urlConn = request("/test/v1/customException", HttpMethod.POST); assertEquals(TestMicroservice.CustomException.HTTP_RESPONSE_STATUS, urlConn.getResponseCode()); urlConn.disconnect(); } @Test public void testConsumeJsonProduceString() throws IOException { HttpURLConnection urlConn = request("/test/v1/jsonConsumeStringProduce", HttpMethod.POST); urlConn.setRequestProperty(HttpHeaders.CONTENT_TYPE, "text/json"); Gson gson = new Gson(); Pet pet = petInstance(); writeContent(urlConn, gson.toJson(pet)); assertEquals(Response.Status.OK.getStatusCode(), urlConn.getResponseCode()); assertEquals(pet.getDetails(), getContent(urlConn)); urlConn.disconnect(); } @Test public void testConsumeStringProduceJson() throws IOException { HttpURLConnection urlConn = request("/test/v1/textConsumeJsonProduce", HttpMethod.POST); urlConn.setRequestProperty(HttpHeaders.CONTENT_TYPE, "text/plain"); String str = "send-something"; writeContent(urlConn, str); assertEquals(Response.Status.OK.getStatusCode(), urlConn.getResponseCode()); Gson gson = new Gson(); String content = getContent(urlConn); TextBean textBean = gson.fromJson(content, TextBean.class); assertEquals(str, textBean.getText()); urlConn.disconnect(); } @Test public void testConsumeStringProduceString() throws IOException { HttpURLConnection urlConn = request("/test/v1/textConsumeTextProduce", HttpMethod.POST); urlConn.setRequestProperty(HttpHeaders.CONTENT_TYPE, "text/plain"); String str = "send-something"; writeContent(urlConn, str); assertEquals(Response.Status.OK.getStatusCode(), urlConn.getResponseCode()); assertEquals(str + "-processed", getContent(urlConn)); urlConn.disconnect(); } @Test public void testConsumeXmlProduceXml() throws IOException, BeanConversionException { HttpURLConnection urlConn = request("/test/v1/textConsumeTextProduceXml", HttpMethod.POST); urlConn.setRequestProperty(HttpHeaders.CONTENT_TYPE, "text/xml"); XmlBean xmlBean = new XmlBean(); xmlBean.setName("send-something"); xmlBean.setId(10); xmlBean.setValue(15); writeContent(urlConn, Charset.defaultCharset() .decode(BeanConverter.getConverter("text/xml").convertToMedia(xmlBean)).toString()); assertEquals(Response.Status.OK.getStatusCode(), urlConn.getResponseCode()); String respBody = getContent(urlConn); XmlBean xmlBean2 = (XmlBean) BeanConverter.getConverter("text/xml").convertToObject( ByteBuffer.wrap(respBody.getBytes(Charset.defaultCharset())), XmlBean.class); assertEquals(xmlBean.getName(), xmlBean2.getName()); assertEquals(xmlBean.getId(), xmlBean2.getId()); assertEquals(xmlBean.getValue(), xmlBean2.getValue()); urlConn.disconnect(); } @Test public void testDownloadPngFile() throws Exception { HttpURLConnection urlConn = request("/test/v1/fileserver/png", HttpMethod.GET); assertEquals(Response.Status.OK.getStatusCode(), urlConn.getResponseCode()); String contentType = urlConn.getHeaderField(HttpHeaders.CONTENT_TYPE); assertTrue("image/png".equalsIgnoreCase(contentType)); InputStream downStream = urlConn.getInputStream(); File file = new File(Thread.currentThread().getContextClassLoader().getResource("testPngFile.png").toURI()); assertTrue(isStreamEqual(downStream, new FileInputStream(file))); } @Test public void testDownloadPngFileFromInputStream() throws Exception { HttpURLConnection urlConn = request("/test/v1/fileserver/ip/png", HttpMethod.GET); assertEquals(Response.Status.OK.getStatusCode(), urlConn.getResponseCode()); String contentType = urlConn.getHeaderField(HttpHeaders.CONTENT_TYPE); assertTrue("image/png".equalsIgnoreCase(contentType)); InputStream downStream = urlConn.getInputStream(); File file = new File(Thread.currentThread().getContextClassLoader().getResource("testPngFile.png").toURI()); assertTrue(isStreamEqual(downStream, new FileInputStream(file))); } @Test public void testDownloadJpgFile() throws Exception { HttpURLConnection urlConn = request("/test/v1/fileserver/jpg", HttpMethod.GET); assertEquals(Response.Status.OK.getStatusCode(), urlConn.getResponseCode()); assertEquals("wso2", urlConn.getHeaderField("X-Custom-Header")); String contentType = urlConn.getHeaderField(HttpHeaders.CONTENT_TYPE); assertTrue("image/jpeg".equalsIgnoreCase(contentType)); InputStream downStream = urlConn.getInputStream(); File file = new File(Thread.currentThread().getContextClassLoader().getResource("testJpgFile.jpg").toURI()); assertTrue(isStreamEqual(downStream, new FileInputStream(file))); } @Test public void testDownloadJpgFileFromInputStream() throws Exception { HttpURLConnection urlConn = request("/test/v1/fileserver/ip/jpg", HttpMethod.GET); assertEquals(Response.Status.OK.getStatusCode(), urlConn.getResponseCode()); assertEquals("wso2", urlConn.getHeaderField("X-Custom-Header")); String contentType = urlConn.getHeaderField(HttpHeaders.CONTENT_TYPE); assertTrue("image/jpeg".equalsIgnoreCase(contentType)); InputStream downStream = urlConn.getInputStream(); File file = new File(Thread.currentThread().getContextClassLoader().getResource("testJpgFile.jpg").toURI()); assertTrue(isStreamEqual(downStream, new FileInputStream(file))); } @Test public void testDownloadTxtFile() throws Exception { HttpURLConnection urlConn = request("/test/v1/fileserver/txt", HttpMethod.GET); assertEquals(Response.Status.OK.getStatusCode(), urlConn.getResponseCode()); String contentType = urlConn.getHeaderField(HttpHeaders.CONTENT_TYPE); assertTrue("text/plain".equalsIgnoreCase(contentType)); InputStream downStream = urlConn.getInputStream(); File file = new File(Thread.currentThread().getContextClassLoader().getResource("testTxtFile.txt").toURI()); assertTrue(isStreamEqual(downStream, new FileInputStream(file))); } @Test public void testDownloadTxtFileFromInputStream() throws Exception { HttpURLConnection urlConn = request("/test/v1/fileserver/ip/txt", HttpMethod.GET); assertEquals(Response.Status.OK.getStatusCode(), urlConn.getResponseCode()); String contentType = urlConn.getHeaderField(HttpHeaders.CONTENT_TYPE); assertTrue("text/plain".equalsIgnoreCase(contentType)); InputStream downStream = urlConn.getInputStream(); File file = new File(Thread.currentThread().getContextClassLoader().getResource("testTxtFile.txt").toURI()); assertTrue(isStreamEqual(downStream, new FileInputStream(file))); } @Test public void testGzipCompressionWithNoGzipAccept() throws Exception { HttpURLConnection urlConn = request("/test/v1/gzipfile", HttpMethod.GET); assertEquals(Response.Status.OK.getStatusCode(), urlConn.getResponseCode()); String contentEncoding = urlConn.getHeaderField(HttpHeaders.CONTENT_ENCODING); assertTrue(contentEncoding == null || !contentEncoding.contains("gzip")); InputStream downStream = urlConn.getInputStream(); assertTrue(IOUtils.toByteArray(downStream).length == IOUtils.toByteArray( Thread.currentThread().getContextClassLoader().getResource("testJpgFile.jpg").openStream()).length); } @Test public void testGzipCompressionWithGzipAccept() throws Exception { HttpURLConnection urlConn = request("/test/v1/gzipfile", HttpMethod.GET); urlConn.addRequestProperty(HttpHeaders.ACCEPT_ENCODING, "gzip"); assertEquals(Response.Status.OK.getStatusCode(), urlConn.getResponseCode()); String contentEncoding = urlConn.getHeaderField(HttpHeaders.CONTENT_ENCODING); assertTrue("gzip".equalsIgnoreCase(contentEncoding)); InputStream downStream = urlConn.getInputStream(); assertTrue(IOUtils.toByteArray(downStream).length < IOUtils.toByteArray( Thread.currentThread().getContextClassLoader().getResource("testJpgFile.jpg").openStream()).length); } @Test public void testContentTypeSetting0() throws Exception { HttpURLConnection urlConn = request("/test/v1/response/typehtml", HttpMethod.GET); assertEquals(Response.Status.OK.getStatusCode(), urlConn.getResponseCode()); String contentType = urlConn.getHeaderField(HttpHeaders.CONTENT_TYPE); assertTrue(contentType.equalsIgnoreCase(MediaType.TEXT_HTML)); String content = getContent(urlConn); assertEquals("Hello", content); urlConn.disconnect(); } @Test public void testContentTypeSetting1() throws Exception { HttpURLConnection urlConn = request("/test/v1/response/typehtml/str", HttpMethod.GET); assertEquals(Response.Status.OK.getStatusCode(), urlConn.getResponseCode()); String contentType = urlConn.getHeaderField(HttpHeaders.CONTENT_TYPE); assertTrue(contentType.equalsIgnoreCase(MediaType.TEXT_HTML)); String content = getContent(urlConn); assertEquals("Hello", content); urlConn.disconnect(); } @Test public void testExceptionMapper() throws Exception { HttpURLConnection urlConn = request("/test/v1/mappedException", HttpMethod.GET); assertEquals(Response.Status.NOT_FOUND.getStatusCode(), urlConn.getResponseCode()); assertEquals(MediaType.TEXT_PLAIN, urlConn.getHeaderField(HttpHeaders.CONTENT_TYPE)); urlConn.disconnect(); } @Test public void testExceptionMapper2() throws Exception { HttpURLConnection urlConn = request("/test/v1/mappedException2", HttpMethod.GET); assertEquals(Response.Status.EXPECTATION_FAILED.getStatusCode(), urlConn.getResponseCode()); urlConn.disconnect(); } @Test public void tesFormParamWithURLEncoded() throws IOException { HttpURLConnection connection = request("/test/v1/formParam", HttpMethod.POST); String rawData = "name=wso2&age=10"; ByteBuffer encodedData = Charset.defaultCharset().encode(rawData); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-Type", MediaType.APPLICATION_FORM_URLENCODED); connection.setRequestProperty("Content-Length", String.valueOf(encodedData.array().length)); try (OutputStream os = connection.getOutputStream()) { os.write(Arrays.copyOf(encodedData.array(), encodedData.limit())); } InputStream inputStream = connection.getInputStream(); String response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); connection.disconnect(); assertEquals(response, "wso2:10"); } @Test public void testFormParamWithMultipart() throws IOException, URISyntaxException { HttpURLConnection connection = request("/test/v1/formParam", HttpMethod.POST); MultipartEntityBuilder builder = MultipartEntityBuilder.create(); builder.addPart("name", new StringBody("wso2", ContentType.TEXT_PLAIN)); builder.addPart("age", new StringBody("10", ContentType.TEXT_PLAIN)); HttpEntity build = builder.build(); connection.setRequestProperty("Content-Type", build.getContentType().getValue()); try (OutputStream out = connection.getOutputStream()) { build.writeTo(out); } InputStream inputStream = connection.getInputStream(); String response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); connection.disconnect(); assertEquals(response, "wso2:10"); } @Test public void testFormDataParamWithSimpleRequest() throws IOException, URISyntaxException { // Send x-form-url-encoded request HttpURLConnection connection = request("/test/v1/formDataParam", HttpMethod.POST); String rawData = "name=wso2&age=10"; ByteBuffer encodedData = Charset.defaultCharset().encode(rawData); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-Type", MediaType.APPLICATION_FORM_URLENCODED); connection.setRequestProperty("Content-Length", String.valueOf(encodedData.array().length)); try (OutputStream os = connection.getOutputStream()) { os.write(Arrays.copyOf(encodedData.array(), encodedData.limit())); } InputStream inputStream = connection.getInputStream(); String response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); connection.disconnect(); assertEquals(response, "wso2:10"); // Send multipart/form-data request connection = request("/test/v1/formDataParam", HttpMethod.POST); MultipartEntityBuilder builder = MultipartEntityBuilder.create(); builder.addPart("name", new StringBody("wso2", ContentType.TEXT_PLAIN)); builder.addPart("age", new StringBody("10", ContentType.TEXT_PLAIN)); HttpEntity build = builder.build(); connection.setRequestProperty("Content-Type", build.getContentType().getValue()); try (OutputStream out = connection.getOutputStream()) { build.writeTo(out); } inputStream = connection.getInputStream(); response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); connection.disconnect(); assertEquals(response, "wso2:10"); } @Test public void tesFormParamWithCollection() throws IOException { // Send x-form-url-encoded request HttpURLConnection connection = request("/test/v1/formParamWithList", HttpMethod.POST); String rawData = "names=WSO2&names=IBM"; ByteBuffer encodedData = Charset.defaultCharset().encode(rawData); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-Type", MediaType.APPLICATION_FORM_URLENCODED); connection.setRequestProperty("Content-Length", String.valueOf(encodedData.array().length)); try (OutputStream os = connection.getOutputStream()) { os.write(Arrays.copyOf(encodedData.array(), encodedData.limit())); } InputStream inputStream = connection.getInputStream(); String response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); connection.disconnect(); assertEquals(response, "2"); // Send multipart/form-data request connection = request("/test/v1/formParamWithList", HttpMethod.POST); MultipartEntityBuilder builder = MultipartEntityBuilder.create(); builder.addPart("names", new StringBody("WSO2", ContentType.TEXT_PLAIN)); builder.addPart("names", new StringBody("IBM", ContentType.TEXT_PLAIN)); builder.addPart("names", new StringBody("Oracle", ContentType.TEXT_PLAIN)); HttpEntity build = builder.build(); connection.setRequestProperty("Content-Type", build.getContentType().getValue()); try (OutputStream out = connection.getOutputStream()) { build.writeTo(out); } inputStream = connection.getInputStream(); response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); connection.disconnect(); assertEquals(response, "3"); // Send x-form-url-encoded request connection = request("/test/v1/formParamWithSet", HttpMethod.POST); rawData = "names=WSO2&names=IBM&names=IBM"; encodedData = Charset.defaultCharset().encode(rawData); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-Type", MediaType.APPLICATION_FORM_URLENCODED); connection.setRequestProperty("Content-Length", String.valueOf(encodedData.array().length)); try (OutputStream os = connection.getOutputStream()) { os.write(Arrays.copyOf(encodedData.array(), encodedData.limit())); } inputStream = connection.getInputStream(); response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); connection.disconnect(); assertEquals(response, "2"); // Send multipart/form-data request connection = request("/test/v1/formParamWithSet", HttpMethod.POST); builder = MultipartEntityBuilder.create(); builder.addPart("names", new StringBody("WSO2", ContentType.TEXT_PLAIN)); builder.addPart("names", new StringBody("IBM", ContentType.TEXT_PLAIN)); builder.addPart("names", new StringBody("IBM", ContentType.TEXT_PLAIN)); builder.addPart("names", new StringBody("Oracle", ContentType.TEXT_PLAIN)); build = builder.build(); connection.setRequestProperty("Content-Type", build.getContentType().getValue()); try (OutputStream out = connection.getOutputStream()) { build.writeTo(out); } inputStream = connection.getInputStream(); response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); connection.disconnect(); assertEquals(response, "3"); } @Test public void testFormParamWithFile() throws IOException, URISyntaxException { HttpURLConnection connection = request("/test/v1/testFormParamWithFile", HttpMethod.POST); File file = new File(Thread.currentThread().getContextClassLoader().getResource("testJpgFile.jpg").toURI()); MultipartEntityBuilder builder = MultipartEntityBuilder.create(); FileBody fileBody = new FileBody(file, ContentType.DEFAULT_BINARY); builder.addPart("form", fileBody); HttpEntity build = builder.build(); connection.setRequestProperty("Content-Type", build.getContentType().getValue()); try (OutputStream out = connection.getOutputStream()) { build.writeTo(out); } InputStream inputStream = connection.getInputStream(); String response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); connection.disconnect(); assertEquals(response, file.getName()); } @Test public void testFormDataParamWithComplexForm() throws IOException, URISyntaxException { HttpURLConnection connection = request("/test/v1/complexForm", HttpMethod.POST); StringBody companyText = new StringBody("{\"type\": \"Open Source\"}", ContentType.APPLICATION_JSON); StringBody personList = new StringBody( "[{\"name\":\"Richard Stallman\",\"age\":63}, {\"name\":\"Linus Torvalds\",\"age\":46}]", ContentType.APPLICATION_JSON); HttpEntity reqEntity = MultipartEntityBuilder.create() .addTextBody("id", "1") .addPart("company", companyText) .addPart("people", personList) .addBinaryBody("file", new File(Thread.currentThread().getContextClassLoader().getResource("testTxtFile.txt").toURI()), ContentType.DEFAULT_BINARY, "testTxtFile.txt").build(); connection.setRequestProperty("Content-Type", reqEntity.getContentType().getValue()); try (OutputStream out = connection.getOutputStream()) { reqEntity.writeTo(out); } InputStream inputStream = connection.getInputStream(); String response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); connection.disconnect(); assertEquals(response, "testTxtFile.txt:1:2:Open Source"); } @Test public void testFormDataParamWithMultipleFiles() throws IOException, URISyntaxException { HttpURLConnection connection = request("/test/v1/multipleFiles", HttpMethod.POST); File file1 = new File(Thread.currentThread().getContextClassLoader().getResource("testTxtFile.txt").toURI()); File file2 = new File(Thread.currentThread().getContextClassLoader().getResource("testPngFile.png").toURI()); HttpEntity reqEntity = MultipartEntityBuilder.create(). addBinaryBody("files", file1, ContentType.DEFAULT_BINARY, file1.getName()) .addBinaryBody("files", file2, ContentType.DEFAULT_BINARY, file2.getName()).build(); connection.setRequestProperty("Content-Type", reqEntity.getContentType().getValue()); try (OutputStream out = connection.getOutputStream()) { reqEntity.writeTo(out); } InputStream inputStream = connection.getInputStream(); String response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); connection.disconnect(); assertEquals(response, "2"); } @Test public void testFormDataParamWithFileStream() throws IOException, URISyntaxException { HttpURLConnection connection = request("/test/v1/streamFile", HttpMethod.POST); File file = new File(Thread.currentThread().getContextClassLoader().getResource("testTxtFile.txt").toURI()); HttpEntity reqEntity = MultipartEntityBuilder.create(). addBinaryBody("file", file, ContentType.DEFAULT_BINARY, file.getName()).build(); connection.setRequestProperty("Content-Type", reqEntity.getContentType().getValue()); try (OutputStream out = connection.getOutputStream()) { reqEntity.writeTo(out); } InputStream inputStream = connection.getInputStream(); String response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); connection.disconnect(); StringBuilder stringBuilder = new StringBuilder(); try (BufferedReader bufferedReader = new BufferedReader(new FileReader(file))) { while (bufferedReader.ready()) { stringBuilder.append(bufferedReader.readLine()); } } assertEquals(response, stringBuilder.toString() + "-" + file.getName()); } @Test public void testGetAllFormItemsWithURLEncoded() throws IOException, URISyntaxException { HttpURLConnection connection = request("/test/v1/getAllFormItemsURLEncoded", HttpMethod.POST); String rawData = "names=WSO2&names=IBM&age=10&type=Software"; ByteBuffer encodedData = Charset.defaultCharset().encode(rawData); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-Type", MediaType.APPLICATION_FORM_URLENCODED); connection.setRequestProperty("Content-Length", String.valueOf(encodedData.array().length)); try (OutputStream os = connection.getOutputStream()) { os.write(Arrays.copyOf(encodedData.array(), encodedData.limit())); } InputStream inputStream = connection.getInputStream(); String response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); connection.disconnect(); assertEquals("No of Companies-2 type-Software", response); } @Test public void getAllFormItemsMultipart() throws IOException, URISyntaxException { HttpURLConnection connection = request("/test/v1/getAllFormItemsMultipart", HttpMethod.POST); StringBody companyText = new StringBody("{\"type\": \"Open Source\"}", ContentType.APPLICATION_JSON); StringBody personList = new StringBody( "[{\"name\":\"Richard Stallman\",\"age\":63}, {\"name\":\"Linus Torvalds\",\"age\":46}]", ContentType.APPLICATION_JSON); HttpEntity reqEntity = MultipartEntityBuilder.create() .addTextBody("id", "1") .addPart("company", companyText) .addPart("people", personList) .addBinaryBody("file", new File(Thread.currentThread().getContextClassLoader().getResource("testTxtFile.txt").toURI()), ContentType.DEFAULT_BINARY, "testTxtFile.txt") .addBinaryBody("file", new File(Thread.currentThread().getContextClassLoader().getResource("testPngFile.png").toURI()), ContentType.DEFAULT_BINARY, "testPngFile.png") .build(); connection.setRequestProperty("Content-Type", reqEntity.getContentType().getValue()); try (OutputStream out = connection.getOutputStream()) { reqEntity.writeTo(out); } InputStream inputStream = connection.getInputStream(); String response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); connection.disconnect(); assertEquals("FileCount-2 SecondFileName-testPngFile.png FirstPerson-Richard Stallman", response); connection = request("/test/v1/getAllFormItemsXFormUrlEncoded", HttpMethod.POST); String rawData = "names=WSO2&names=IBM&type=Software"; ByteBuffer encodedData = Charset.defaultCharset().encode(rawData); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-Type", MediaType.APPLICATION_FORM_URLENCODED); connection.setRequestProperty("Content-Length", String.valueOf(encodedData.array().length)); try (OutputStream os = connection.getOutputStream()) { os.write(Arrays.copyOf(encodedData.array(), encodedData.limit())); } inputStream = connection.getInputStream(); response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); connection.disconnect(); assertEquals("Type = Software No of names = 2 First name = IBM", response); } @Test public void testPathParamWithRegexOne() throws Exception { HttpURLConnection urlConn = request("/test/v1/WSDL/12/states", HttpMethod.GET); InputStream inputStream = urlConn.getInputStream(); String response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); urlConn.disconnect(); assertEquals(response, "Asset Type = WSDL, Asset Id = 12"); } @Test public void testPathParamWithRegexTwo() throws Exception { HttpURLConnection urlConn = request("/test/v1/endpoints/WADL/10", HttpMethod.GET); InputStream inputStream = urlConn.getInputStream(); String response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); urlConn.disconnect(); assertEquals(response, "Asset Type = WADL, Asset Id = 10"); } @Test public void testDualInvocation() throws Exception { HttpURLConnection urlConn = request("/test/v1/testDualInvocation1", HttpMethod.GET); InputStream inputStream = urlConn.getInputStream(); String response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); urlConn.disconnect(); assertEquals("1", response); urlConn = request("/test/v1/testDualInvocation2", HttpMethod.GET); inputStream = urlConn.getInputStream(); response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); urlConn.disconnect(); assertEquals("2", response); } @Test public void testJsonProduceWithStringJsonArrayAndJsonObject() throws Exception { HttpURLConnection urlConn = request("/test/v1/testJsonProduceWithString", HttpMethod.GET); InputStream inputStream = urlConn.getInputStream(); String response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); urlConn.disconnect(); assertEquals("{\"abc\":[{\"name\":\"Richard Stallman\",\"age\":63}, {\"name\":\"Linus Torvalds\",\"age\":46}]}", response); urlConn = request("/test/v1/testJsonProduceWithJsonArray", HttpMethod.GET); inputStream = urlConn.getInputStream(); response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); urlConn.disconnect(); assertEquals("[\"12\",\"15\",\"15\"]", response); urlConn = request("/test/v1/testJsonProduceWithJsonObject", HttpMethod.GET); inputStream = urlConn.getInputStream(); response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); urlConn.disconnect(); assertEquals("{\"name\":\"WSO2\",\"products\":[\"APIM\",\"IS\",\"MSF4J\"]}", response); } @Test public void testSetAndGetFromSession() throws Exception { long value = System.currentTimeMillis(); // Request to first operation HttpURLConnection urlConn = request("/test/v1/set-session/" + value, HttpMethod.GET); assertEquals(204, urlConn.getResponseCode()); String setCookieHeader = urlConn.getHeaderField("Set-Cookie"); assertNotNull(setCookieHeader); urlConn.disconnect(); // Request to 2nd operation urlConn = request("/test/v1/get-session/", HttpMethod.GET); urlConn.setRequestProperty("Cookie", setCookieHeader); assertEquals(200, urlConn.getResponseCode()); setCookieHeader = urlConn.getHeaderField("Set-Cookie"); assertNull(setCookieHeader); String content = getContent(urlConn); // content retrieved & returned from session assertEquals(String.valueOf(value), content); urlConn.disconnect(); } @Test public void testSetAndGetFromSession2() throws Exception { long value = System.currentTimeMillis(); // Request to first operation HttpURLConnection urlConn = request("/test/v1/set-session2/" + value, HttpMethod.GET); assertEquals(200, urlConn.getResponseCode()); String setCookieHeader = urlConn.getHeaderField("Set-Cookie"); assertNotNull(setCookieHeader); String content = getContent(urlConn); assertEquals(String.valueOf(value), content); urlConn.disconnect(); // Request to 2nd operation urlConn = request("/test/v1/get-session/", HttpMethod.GET); urlConn.setRequestProperty("Cookie", setCookieHeader); assertEquals(200, urlConn.getResponseCode()); setCookieHeader = urlConn.getHeaderField("Set-Cookie"); assertNull(setCookieHeader); content = getContent(urlConn); // content retrieved & returned from session assertEquals(String.valueOf(value), content); urlConn.disconnect(); } @Test public void testSessionExpiry() throws Exception { long value = System.currentTimeMillis(); // Request to first operation HttpURLConnection urlConn = request("/test/v1/set-session2/" + value, HttpMethod.GET); assertEquals(200, urlConn.getResponseCode()); String setCookieHeader = urlConn.getHeaderField("Set-Cookie"); assertNotNull(setCookieHeader); String content = getContent(urlConn); assertEquals(String.valueOf(value), content); urlConn.disconnect(); // Request to 2nd operation urlConn = request("/test/v1/get-session/", HttpMethod.GET); urlConn.setRequestProperty("Cookie", setCookieHeader); assertEquals(200, urlConn.getResponseCode()); setCookieHeader = urlConn.getHeaderField("Set-Cookie"); assertNull(setCookieHeader); content = getContent(urlConn); // content retrieved & returned from session assertEquals(String.valueOf(value), content); urlConn.disconnect(); // Expire the session urlConn = request("/test/v1/expire-session/", HttpMethod.GET); urlConn.setRequestProperty("Cookie", setCookieHeader); assertEquals(204, urlConn.getResponseCode()); setCookieHeader = urlConn.getHeaderField("Set-Cookie"); assertNull(setCookieHeader); urlConn.disconnect(); // Try to retrieve the object stored in the expired session urlConn = request("/test/v1/get-session/", HttpMethod.GET); urlConn.setRequestProperty("Cookie", setCookieHeader); assertEquals(204, urlConn.getResponseCode()); setCookieHeader = urlConn.getHeaderField("Set-Cookie"); assertNotNull(setCookieHeader); content = getContent(urlConn); // content retrieved & returned from session assertEquals("", content); urlConn.disconnect(); } @Test public void testCookieParam() throws Exception { String value = "wso2"; HttpURLConnection urlConn = request("/test/v1/cookie/", HttpMethod.GET); urlConn.setRequestProperty("Cookie", "name=" + value); assertEquals(200, urlConn.getResponseCode()); String content = getContent(urlConn); assertEquals(value, content); String cookie = urlConn.getHeaderField("Set-Cookie"); assertNotNull(cookie); assertEquals("test-cookie=" + value + ";Path=/cookie;Domain=wso2.com;MaxAge=10;Comment=Cookie Test;" + "Expires=Sat, 31 Dec 2016 18:30:00 GMT;Secure;HttpOnly", cookie); urlConn.disconnect(); value = "Apache"; urlConn = request("/test/v1/cookie/", HttpMethod.GET); urlConn.setRequestProperty("Cookie", "name=" + value); assertEquals(200, urlConn.getResponseCode()); content = getContent(urlConn); assertEquals(value, content); cookie = urlConn.getHeaderField("Set-Cookie"); assertNotNull(cookie); assertEquals("test-cookie=" + value, cookie); urlConn.disconnect(); } @Test public void testSubResources() throws Exception { HttpURLConnection urlConn = request("/test/v1/SL/team", HttpMethod.GET); InputStream inputStream = urlConn.getInputStream(); String response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); urlConn.disconnect(); Team team = GSON.fromJson(response, Team.class); assertEquals("Cricket", team.getTeamType()); assertEquals("SL", team.getCountryId()); urlConn = request("/test/v1/SL/team", HttpMethod.POST); String rawData = "countryName=SriLanka"; ByteBuffer encodedData = Charset.defaultCharset().encode(rawData); urlConn.setRequestMethod("POST"); urlConn.setRequestProperty("Content-Type", MediaType.APPLICATION_FORM_URLENCODED); urlConn.setRequestProperty("Content-Length", String.valueOf(encodedData.array().length)); try (OutputStream os = urlConn.getOutputStream()) { os.write(Arrays.copyOf(encodedData.array(), encodedData.limit())); } inputStream = urlConn.getInputStream(); response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); urlConn.disconnect(); team = GSON.fromJson(response, Team.class); assertEquals("Cricket", team.getTeamType()); assertEquals("SL", team.getCountryId()); assertEquals("SriLanka", team.getCountryName()); urlConn = request("/test/v1/SL/team/123", HttpMethod.POST); rawData = "countryName=SriLanka&type=Batsman"; encodedData = Charset.defaultCharset().encode(rawData); urlConn.setRequestMethod("POST"); urlConn.setRequestProperty("Content-Type", MediaType.APPLICATION_FORM_URLENCODED); urlConn.setRequestProperty("Content-Length", String.valueOf(encodedData.array().length)); try (OutputStream os = urlConn.getOutputStream()) { os.write(Arrays.copyOf(encodedData.array(), encodedData.limit())); } inputStream = urlConn.getInputStream(); response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); urlConn.disconnect(); Player player = GSON.fromJson(response, Player.class); assertEquals("player_1", player.getName()); assertEquals(123, player.getPlayerId()); assertEquals("SL", player.getCountryId()); assertEquals("SriLanka", player.getCountryName()); assertEquals(30, player.getAge()); assertEquals("Batsman", player.getType()); urlConn = request("/test/v1/SL/team/123/details/name", HttpMethod.POST); rawData = "countryName=SriLanka&type=Batsman"; encodedData = Charset.defaultCharset().encode(rawData); urlConn.setRequestMethod("POST"); urlConn.setRequestProperty("Content-Type", MediaType.APPLICATION_FORM_URLENCODED); urlConn.setRequestProperty("Content-Length", String.valueOf(encodedData.array().length)); try (OutputStream os = urlConn.getOutputStream()) { os.write(Arrays.copyOf(encodedData.array(), encodedData.limit())); } inputStream = urlConn.getInputStream(); response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); urlConn.disconnect(); assertEquals("SL_123_name_Batsman_SriLanka", response); } @Test public void testLocation() throws Exception { HttpURLConnection urlConn = request("/test/v1/locationRealtiveUriTest", HttpMethod.GET); assertEquals(201, urlConn.getResponseCode()); String location = urlConn.getHeaderField("Location"); assertEquals("http://" + baseURI.getHost() + ":" + baseURI.getPort() + "/entity/1", location); urlConn.disconnect(); urlConn = request("/test/v1/locationAbsoluteUriTest", HttpMethod.GET); assertEquals(201, urlConn.getResponseCode()); location = urlConn.getHeaderField("Location"); assertEquals("http://localhost:8080/products/entity/2", location); urlConn.disconnect(); } protected Socket createRawSocket(URL url) throws IOException { return new Socket(url.getHost(), url.getPort()); } protected void testContent(String path, String content) throws IOException { testContent(path, content, HttpMethod.GET); } protected void testContent(String path, String content, String method) throws IOException { HttpURLConnection urlConn = request(path, method); assertEquals(200, urlConn.getResponseCode()); assertEquals(content, getContent(urlConn)); urlConn.disconnect(); } protected HttpURLConnection request(String path, String method) throws IOException { return request(path, method, false); } protected HttpURLConnection request(String path, String method, boolean keepAlive) throws IOException { URL url = baseURI.resolve(path).toURL(); HttpURLConnection urlConn = (HttpURLConnection) url.openConnection(); if (method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT)) { urlConn.setDoOutput(true); } urlConn.setRequestMethod(method); if (!keepAlive) { urlConn.setRequestProperty(HttpHeaderNames.CONNECTION.toString(), HEADER_VAL_CLOSE); } return urlConn; } protected HttpURLConnection request(String path, String method, boolean keepAlive, int port) throws IOException { URL url = URI.create(String.format("http://%s:%d", Constants.HOSTNAME, port)).resolve(path).toURL(); HttpURLConnection urlConn = (HttpURLConnection) url.openConnection(); if (method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT)) { urlConn.setDoOutput(true); } urlConn.setRequestMethod(method); if (!keepAlive) { urlConn.setRequestProperty(HttpHeaderNames.CONNECTION.toString(), HEADER_VAL_CLOSE); } return urlConn; } protected String getContent(HttpURLConnection urlConn) throws IOException { return new String(IOUtils.toByteArray(urlConn.getInputStream()), Charsets.UTF_8); } protected void writeContent(HttpURLConnection urlConn, String content) throws IOException { urlConn.getOutputStream().write(content.getBytes(Charsets.UTF_8)); } protected boolean isStreamEqual(InputStream input1, InputStream input2) throws IOException { if (!(input1 instanceof BufferedInputStream)) { input1 = new BufferedInputStream(input1); } if (!(input2 instanceof BufferedInputStream)) { input2 = new BufferedInputStream(input2); } int ch = input1.read(); while (-1 != ch) { int ch2 = input2.read(); if (ch != ch2) { return false; } ch = input1.read(); } int ch2 = input2.read(); return (ch2 == -1); } protected Pet petInstance() { Pet pet = new Pet(); pet.setCategory(new Category("dog")); pet.setAgeMonths(3); pet.setDetails("small-cat"); pet.setPrice(10.5f); pet.setImage("cat.png"); return pet; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/HttpsServerTest.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j; import io.netty.handler.codec.http.HttpHeaderNames; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.wso2.msf4j.conf.Constants; import org.wso2.msf4j.conf.SSLClientContext; import org.wso2.msf4j.exception.TestExceptionMapper; import org.wso2.msf4j.exception.TestExceptionMapper2; import org.wso2.msf4j.service.SecondService; import org.wso2.msf4j.service.TestMicroServiceWithDynamicPath; import org.wso2.msf4j.service.TestMicroservice; import java.io.IOException; import java.net.HttpURLConnection; import java.net.Socket; import java.net.URI; import java.net.URL; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.ws.rs.HttpMethod; /** * Test the HttpsServer. */ public class HttpsServerTest extends HttpServerTest { private static SSLClientContext sslClientContext; private final TestMicroservice testMicroservice = new TestMicroservice(); private final SecondService secondService = new SecondService(); private MicroservicesRunner microservicesRunner; private MicroservicesRunner secondMicroservicesRunner; private static final int port = Constants.PORT + 4; @BeforeClass public void setup() throws Exception { baseURI = URI.create(String.format("https://%s:%d", Constants.HOSTNAME, port)); System.setProperty("transports.netty.conf", Thread.currentThread().getContextClassLoader().getResource("netty-transports-1.yaml") .getPath()); microservicesRunner = new MicroservicesRunner(); sslClientContext = new SSLClientContext(); microservicesRunner .addExceptionMapper(new TestExceptionMapper(), new TestExceptionMapper2()) .deploy(testMicroservice) .start(); secondMicroservicesRunner = new MicroservicesRunner(port + 1); secondMicroservicesRunner.deploy(secondService).start(); microservicesRunner.deploy("/DynamicPath", new TestMicroServiceWithDynamicPath()); microservicesRunner.deploy("/DynamicPath2", new TestMicroServiceWithDynamicPath()); } @AfterClass public void teardown() throws Exception { microservicesRunner.stop(); secondMicroservicesRunner.stop(); } @Override protected HttpURLConnection request(String path, String method, boolean keepAlive) throws IOException { URL url = baseURI.resolve(path).toURL(); HttpsURLConnection.setDefaultSSLSocketFactory(sslClientContext.getClientContext().getSocketFactory()); HostnameVerifier allHostsValid = (hostname1, session) -> true; // Install the all-trusting host verifier HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid); HttpURLConnection urlConn = (HttpsURLConnection) url.openConnection(); if (method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT)) { urlConn.setDoOutput(true); } urlConn.setRequestMethod(method); if (!keepAlive) { urlConn.setRequestProperty(HttpHeaderNames.CONNECTION.toString(), HEADER_VAL_CLOSE); } return urlConn; } @Override protected Socket createRawSocket(URL url) throws IOException { return sslClientContext.getClientContext().getSocketFactory().createSocket(url.getHost(), url.getPort()); } static void setSslClientContext(SSLClientContext sslClientContext) { HttpsServerTest.sslClientContext = sslClientContext; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/InterceptorTest.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j; import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.wso2.msf4j.conf.Constants; import org.wso2.msf4j.interceptor.HighPriorityGlobalRequestInterceptor; import org.wso2.msf4j.interceptor.HighPriorityGlobalResponseInterceptor; import org.wso2.msf4j.interceptor.LowPriorityGlobalRequestInterceptor; import org.wso2.msf4j.interceptor.LowPriorityGlobalResponseInterceptor; import org.wso2.msf4j.interceptor.MediumPriorityGlobalRequestInterceptor; import org.wso2.msf4j.interceptor.MediumPriorityGlobalResponseInterceptor; import org.wso2.msf4j.interceptor.PriorityDataHolder; import org.wso2.msf4j.interceptor.TestBreakRequestInterceptor; import org.wso2.msf4j.interceptor.TestBreakResponseInterceptor; import org.wso2.msf4j.interceptor.TestExceptionBreakRequestInterceptor; import org.wso2.msf4j.interceptor.TestInterceptorDeprecated; import org.wso2.msf4j.interceptor.TestRequestInterceptor; import org.wso2.msf4j.interceptor.TestResponseInterceptor; import org.wso2.msf4j.service.InterceptorTestMicroService; import org.wso2.msf4j.service.PriorityInterceptorTestMicroService; import org.wso2.msf4j.service.sub.Player; import org.wso2.msf4j.service.sub.Team; import java.net.URI; import java.net.URL; import java.util.Collections; import static org.testng.AssertJUnit.assertEquals; /** * Class for testing interceptors. *

* Please note that the deprecated interceptors are also tested along the way. */ public class InterceptorTest extends InterceptorTestBase { private static final int port = Constants.PORT + 6; private final String microServiceBaseUrl = "/test/interceptorTest/"; private MicroservicesRunner microservicesRunner; private final InterceptorTestMicroService interceptorTestMicroService = new InterceptorTestMicroService(); private final PriorityInterceptorTestMicroService priorityInterceptorTestMicroService = new PriorityInterceptorTestMicroService(); @BeforeClass public void setup() throws Exception { URL resource = Thread.currentThread().getContextClassLoader().getResource("netty-transports-3.yaml"); if (resource == null) { Assert.fail("netty-transports-3.yaml not found"); } System.setProperty("transports.netty.conf", resource.getPath()); microservicesRunner = new MicroservicesRunner(port); // Global request interceptors are registered in the priority order of new interceptors -> old interceptors // Global response interceptors are registered in the priority order of old interceptors -> new interceptors microservicesRunner .deploy(interceptorTestMicroService) .deploy(priorityInterceptorTestMicroService) .addGlobalRequestInterceptor(new HighPriorityGlobalRequestInterceptor(), new MediumPriorityGlobalRequestInterceptor(), new LowPriorityGlobalRequestInterceptor()) .addInterceptor(new TestInterceptorDeprecated()) .addGlobalResponseInterceptor(new HighPriorityGlobalResponseInterceptor(), new MediumPriorityGlobalResponseInterceptor(), new LowPriorityGlobalResponseInterceptor()) .start(); baseURI = URI.create("http://" + Constants.HOSTNAME + ":" + port); Thread.sleep(1000); } @AfterClass public void tearDown() throws Exception { microservicesRunner.stop(); } @BeforeMethod public void reset() { PriorityDataHolder.setPriorityOrder(""); // Reset request interceptors TestRequestInterceptor.reset(); // Reset response interceptors TestResponseInterceptor.reset(); // Reset global request interceptors HighPriorityGlobalRequestInterceptor.reset(); MediumPriorityGlobalRequestInterceptor.reset(); LowPriorityGlobalRequestInterceptor.reset(); // Reset global response interceptors HighPriorityGlobalResponseInterceptor.reset(); MediumPriorityGlobalResponseInterceptor.reset(); LowPriorityGlobalResponseInterceptor.reset(); // Reset deprecated interceptor TestInterceptorDeprecated.reset(); } /** * Test whether resource interceptors are called. * * @throws Exception on any exception */ @Test public void interceptionTest() throws Exception { // No sub resource Team team = doGetAndGetResponseObject(microServiceBaseUrl + "subResourceLocatorTest/SL/", false, Team.class, Collections.unmodifiableMap(Collections.emptyMap())); // Assert response assertEquals("Cricket", team.getTeamType()); assertEquals("SL", team.getCountryId()); // Assert interceptor calls // Request interceptors assertEquals(1, HighPriorityGlobalRequestInterceptor.getFilterCalls()); // Global interceptors assertEquals(1, MediumPriorityGlobalRequestInterceptor.getFilterCalls()); // Global interceptors assertEquals(1, LowPriorityGlobalRequestInterceptor.getFilterCalls()); // Global interceptors assertEquals(1, TestInterceptorDeprecated.getPreCallInterceptorCallsCount()); // Global interceptors assertEquals(2, TestRequestInterceptor.getFilterCalls()); // Resource level interceptor // Response interceptors assertEquals(1, HighPriorityGlobalResponseInterceptor.getFilterCalls()); // Global interceptors assertEquals(1, MediumPriorityGlobalResponseInterceptor.getFilterCalls()); // Global interceptors assertEquals(1, LowPriorityGlobalResponseInterceptor.getFilterCalls()); // Global interceptors assertEquals(1, TestInterceptorDeprecated.getPostCallInterceptorCallsCount()); // Global interceptors assertEquals(2, TestResponseInterceptor.getFilterCalls()); // Resource level interceptor } /** * Test whether sub-resource interceptors are called. * * @throws Exception on any exception */ @Test public void subResourceInterceptionTest() throws Exception { // 1 sub resource String rawData = "countryName=SriLanka&type=Batsman"; Player player = doPostAndGetResponseObject(microServiceBaseUrl + "subResourceLocatorTest/SL/" + "interceptorTest/99/", rawData, false, Player.class, Collections.unmodifiableMap(Collections.emptyMap())); // Assert response assertEquals("player_1", player.getName()); assertEquals(99, player.getPlayerId()); assertEquals("SriLanka", player.getCountryName()); assertEquals(30, player.getAge()); assertEquals("Batsman", player.getType()); // Assert interceptor calls // Request interceptors assertEquals(1, HighPriorityGlobalRequestInterceptor.getFilterCalls()); // Global interceptors assertEquals(1, MediumPriorityGlobalRequestInterceptor.getFilterCalls()); // Global interceptors assertEquals(1, LowPriorityGlobalRequestInterceptor.getFilterCalls()); // Global interceptors assertEquals(1, TestInterceptorDeprecated.getPreCallInterceptorCallsCount()); // Global interceptors assertEquals(3, TestRequestInterceptor.getFilterCalls()); // Resource level interceptor // Response interceptors assertEquals(1, HighPriorityGlobalResponseInterceptor.getFilterCalls()); // Global interceptors assertEquals(1, MediumPriorityGlobalResponseInterceptor.getFilterCalls()); // Global interceptors assertEquals(1, LowPriorityGlobalResponseInterceptor.getFilterCalls()); // Global interceptors assertEquals(1, TestInterceptorDeprecated.getPostCallInterceptorCallsCount()); // Global interceptors assertEquals(3, TestResponseInterceptor.getFilterCalls()); // Resource level interceptor String executionOrderString = "HighPriorityGlobalRequestInterceptor" + "MediumPriorityGlobalRequestInterceptor" + "LowPriorityGlobalRequestInterceptor" + "TestInterceptorDeprecated - [PRE CALL]" + "TestRequestInterceptor" + "TestRequestInterceptor" + "[HTTP RESOURCE METHOD]" + "TestRequestInterceptor" + "[HTTP SUB RESOURCE METHOD]" + "TestResponseInterceptor" + "TestResponseInterceptor" + "TestResponseInterceptor" + "TestInterceptorDeprecated - [POST CALL]" + "HighPriorityGlobalResponseInterceptor" + "MediumPriorityGlobalResponseInterceptor" + "LowPriorityGlobalResponseInterceptor"; assertEquals(executionOrderString, PriorityDataHolder.getPriorityOrder()); } /** * Test the order of the filters. * Order of the execution should be *

* 1) Global request interceptors according to the priority order (way developer writes them) * HighPriorityGlobalRequestInterceptor -> MediumPriorityGlobalRequestInterceptor -> * LowPriorityGlobalRequestInterceptor -> TestInterceptorDeprecated - [PRE CALL] *

* 2) Resource level request interceptors according to the priority order (way developer writes them) * HighPriorityRRequestInterceptor -> MediumPriorityRRequestInterceptor -> LowPriorityRRequestInterceptor *

* 3) Sub-resource level request interceptors according to the priority order (way developer writes them) * HighPrioritySRRequestInterceptor -> MediumPrioritySRRequestInterceptor -> LowPrioritySRRequestInterceptor *

* 4) HTTP method *

* 5) Sub-resource level response interceptors according to the priority order (way developer writes them) * HighPrioritySRResponseInterceptor -> MediumPrioritySRResponseInterceptor -> LowPrioritySRResponseInterceptor *

* 6) Resource level response interceptors according to the priority order (way developer writes them) * HighPriorityRResponseInterceptor -> MediumPriorityRResponseInterceptor -> LowPriorityRResponseInterceptor *

* 7) Global response interceptors according to the priority order (way developer writes them) * TestInterceptorDeprecated - [POST CALL] -> HighPriorityGlobalResponseInterceptor -> * MediumPriorityGlobalResponseInterceptor -> LowPriorityGlobalResponseInterceptor * * @throws Exception on any exception */ @Test public void priorityTest() throws Exception { String response = doGetAndGetResponseString("/test/priorityInterceptorTest/priorityTest", false, Collections.unmodifiableMap(Collections.emptyMap())); assertEquals("Priority interceptor test", response); String executionOrderString = "HighPriorityGlobalRequestInterceptor" + "MediumPriorityGlobalRequestInterceptor" + "LowPriorityGlobalRequestInterceptor" + "TestInterceptorDeprecated - [PRE CALL]" + "HighPriorityClassRequestInterceptor" + "MediumPriorityClassRequestInterceptor" + "LowPriorityClassRequestInterceptor" + "HighPriorityMethodRequestInterceptor" + "MediumPriorityMethodRequestInterceptor" + "LowPriorityMethodRequestInterceptor" + "[HTTP METHOD]" + "HighPriorityMethodResponseInterceptor" + "MediumPriorityMethodResponseInterceptor" + "LowPriorityMethodResponseInterceptor" + "HighPriorityClassResponseInterceptor" + "MediumPriorityClassResponseInterceptor" + "LowPriorityClassResponseInterceptor" + "TestInterceptorDeprecated - [POST CALL]" + "HighPriorityGlobalResponseInterceptor" + "MediumPriorityGlobalResponseInterceptor" + "LowPriorityGlobalResponseInterceptor"; assertEquals(executionOrderString, PriorityDataHolder.getPriorityOrder()); } /** * Test for interception break on exception. */ @Test public void interceptorFlowBreakOnExceptionTest() { try { doGetAndGetResponseString(microServiceBaseUrl + "interceptorBreakOnExceptionTest", false, Collections.unmodifiableMap(Collections.emptyMap())); Assert.fail(); // Fail test if exception is not thrown } catch (Exception e) { assertEquals(e.getClass().getName(), "java.io.IOException"); assertEquals(e.getMessage(), "Server returned HTTP response code: 500 for URL: " + "http://localhost:8096/test/interceptorTest/interceptorBreakOnExceptionTest"); assertEquals(1, HighPriorityGlobalRequestInterceptor.getFilterCalls()); // Global interceptors assertEquals(1, MediumPriorityGlobalRequestInterceptor.getFilterCalls()); // Global interceptors assertEquals(1, LowPriorityGlobalRequestInterceptor.getFilterCalls()); // Global interceptors assertEquals(1, TestInterceptorDeprecated.getPreCallInterceptorCallsCount()); // Global interceptors assertEquals(1, TestRequestInterceptor.getFilterCalls()); // Resource and sub-resource interceptors assertEquals(1, TestExceptionBreakRequestInterceptor.getFilterCalls()); // Faulty interceptor // Response interceptors assertEquals(0, TestResponseInterceptor.getFilterCalls()); // Resource and sub-resource interceptors assertEquals(0, TestInterceptorDeprecated.getPostCallInterceptorCallsCount()); // Global interceptors assertEquals(0, HighPriorityGlobalResponseInterceptor.getFilterCalls()); // Global interceptors assertEquals(0, MediumPriorityGlobalResponseInterceptor.getFilterCalls()); // Global interceptors assertEquals(0, LowPriorityGlobalResponseInterceptor.getFilterCalls()); // Global interceptors } } /** * Test for interception break manually by user. */ @Test public void requestInterceptorFlowBreakByUserTest() throws Exception { String response = doGetAndGetResponseString(microServiceBaseUrl + "requestInterceptorBreakByUserTest", false, Collections.unmodifiableMap(Collections.emptyMap())); assertEquals(response, ""); // Request interceptors assertEquals(1, HighPriorityGlobalRequestInterceptor.getFilterCalls()); // Global interceptors assertEquals(1, MediumPriorityGlobalRequestInterceptor.getFilterCalls()); // Global interceptors assertEquals(1, LowPriorityGlobalRequestInterceptor.getFilterCalls()); // Global interceptors assertEquals(1, TestInterceptorDeprecated.getPreCallInterceptorCallsCount()); // Global interceptors assertEquals(1, TestRequestInterceptor.getFilterCalls()); // Resource and sub-resource interceptors assertEquals(1, TestBreakRequestInterceptor.getFilterCalls()); // Resource and sub-resource interceptors // Response interceptors assertEquals(0, TestResponseInterceptor.getFilterCalls()); // Resource and sub-resource interceptors assertEquals(0, TestInterceptorDeprecated.getPostCallInterceptorCallsCount()); // Global interceptors assertEquals(0, HighPriorityGlobalResponseInterceptor.getFilterCalls()); // Global interceptors assertEquals(0, MediumPriorityGlobalResponseInterceptor.getFilterCalls()); // Global interceptors assertEquals(0, LowPriorityGlobalResponseInterceptor.getFilterCalls()); // Global interceptors } /** * Test for interception break manually by user. * * @throws Exception on any exception */ @Test public void responseInterceptorFlowBreakByUserTest() throws Exception { String response = doGetAndGetResponseString(microServiceBaseUrl + "responseInterceptorBreakByUserTest", false, Collections.unmodifiableMap(Collections.emptyMap())); assertEquals(response, ""); // Request interceptors assertEquals(1, HighPriorityGlobalRequestInterceptor.getFilterCalls()); // Global interceptors assertEquals(1, MediumPriorityGlobalRequestInterceptor.getFilterCalls()); // Global interceptors assertEquals(1, LowPriorityGlobalRequestInterceptor.getFilterCalls()); // Global interceptors assertEquals(1, TestInterceptorDeprecated.getPreCallInterceptorCallsCount()); // Global interceptors assertEquals(2, TestRequestInterceptor.getFilterCalls()); // Resource and sub-resource interceptors // Response interceptors assertEquals(1, TestBreakResponseInterceptor.getFilterCalls()); // Break interception flow interceptor assertEquals(0, TestResponseInterceptor.getFilterCalls()); // Resource and sub-resource interceptors assertEquals(0, TestInterceptorDeprecated.getPostCallInterceptorCallsCount()); // Global interceptors assertEquals(0, HighPriorityGlobalResponseInterceptor.getFilterCalls()); // Global interceptors assertEquals(0, MediumPriorityGlobalResponseInterceptor.getFilterCalls()); // Global interceptors assertEquals(0, LowPriorityGlobalResponseInterceptor.getFilterCalls()); // Global interceptors } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/InterceptorTestBase.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j; import com.google.gson.Gson; import io.netty.handler.codec.http.HttpHeaderNames; import org.apache.commons.io.IOUtils; import org.wso2.msf4j.formparam.util.StreamUtil; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URI; import java.net.URL; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.Arrays; import java.util.Map; import javax.ws.rs.HttpMethod; import javax.ws.rs.core.MediaType; /** * Base class for testing interceptors. */ public abstract class InterceptorTestBase { private final Gson gson = new Gson(); protected static URI baseURI; /** * @param path http path * @param keepAlive should the connection be kept alive * @param headers createHttpUrlConnection headers * @return object from response * @throws IOException on any exception */ protected int doGetAndGetStatusCode(String path, boolean keepAlive, Map headers) throws IOException { HttpURLConnection urlConn = createHttpUrlConnection(path, HttpMethod.GET, keepAlive, headers); return urlConn.getResponseCode(); } /** * @param path http path * @param keepAlive should the connection be kept alive * @param tClass type of the expected object * @param headers createHttpUrlConnection headers * @param type of the expected object * @return object from response * @throws IOException on any exception */ protected T doGetAndGetResponseObject(String path, boolean keepAlive, Class tClass, Map headers) throws IOException { HttpURLConnection urlConn = createHttpUrlConnection(path, HttpMethod.GET, keepAlive, headers); return getResponseObject(urlConn, tClass); } /** * @param path http path * @param keepAlive should the connection be kept alive * @param headers createHttpUrlConnection headers * @return object from response * @throws IOException on any exception */ protected String doGetAndGetResponseString(String path, boolean keepAlive, Map headers) throws IOException { HttpURLConnection urlConn = createHttpUrlConnection(path, HttpMethod.GET, keepAlive, headers); return getResponseString(urlConn); } /** * @param path http path * @param rawData data to post * @param keepAlive should the connection be kept alive * @param tClass type of the expected object * @param headers createHttpUrlConnection headers * @param type of the expected object * @return object from response * @throws IOException on any exception */ protected T doPostAndGetResponseObject(String path, String rawData, boolean keepAlive, Class tClass, Map headers) throws IOException { HttpURLConnection urlConn = createHttpUrlConnection(path, HttpMethod.POST, keepAlive, headers); ByteBuffer encodedData = Charset.defaultCharset().encode(rawData); urlConn.setRequestMethod("POST"); urlConn.setRequestProperty("Content-Type", MediaType.APPLICATION_FORM_URLENCODED); urlConn.setRequestProperty("Content-Length", String.valueOf(encodedData.array().length)); try (OutputStream os = urlConn.getOutputStream()) { os.write(Arrays.copyOf(encodedData.array(), encodedData.limit())); } return getResponseObject(urlConn, tClass); } /** * Get java object from http url connection. * * @param urlConn http url connection * @param tClass type of the expected object * @param type of the expected object * @return object from response * @throws IOException on any exception */ protected T getResponseObject(HttpURLConnection urlConn, Class tClass) throws IOException { return gson.fromJson(getResponseString(urlConn), tClass); } /** * Get java object from http url connection. * * @param urlConn http url connection * @return string from response * @throws IOException on any exception */ protected String getResponseString(HttpURLConnection urlConn) throws IOException { InputStream inputStream = urlConn.getInputStream(); String response = StreamUtil.asString(inputStream); IOUtils.closeQuietly(inputStream); return response; } /** * Get http url connection for the path and method specified. * * @param path http path * @param method http method * @param keepAlive should the connection be kept alive * @param headers createHttpUrlConnection headers * @return http url connection instance * @throws IOException on error creating http url connection */ protected HttpURLConnection createHttpUrlConnection(String path, String method, boolean keepAlive, Map headers) throws IOException { URL url = baseURI.resolve(path).toURL(); HttpURLConnection urlConn = (HttpURLConnection) url.openConnection(); if (headers != null) { headers.entrySet().forEach(header -> urlConn.setRequestProperty(header.getKey(), header.getValue())); } if (method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT)) { urlConn.setDoOutput(true); } urlConn.setRequestMethod(method); if (!keepAlive) { urlConn.setRequestProperty(HttpHeaderNames.CONNECTION.toString(), "CLOSE"); } return urlConn; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/MSF4JResponseTest.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j; import org.testng.annotations.Test; import java.util.Arrays; import javax.ws.rs.core.Response; import static org.testng.AssertJUnit.assertEquals; /** * Test MSF4JResponse and MSF4J ResponseBuilder. */ public class MSF4JResponseTest { @Test public void testStatusOk() { Response response = Response .status(Response.Status.OK.getStatusCode()) .build(); assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); } @Test public void testStatusNotFound() { Response response = Response .status(Response.Status.NOT_FOUND.getStatusCode()) .build(); assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus()); } @Test public void testEntity() { Response response = Response .status(Response.Status.OK.getStatusCode()) .entity("Entity") .build(); assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); assertEquals(response.getEntity(), "Entity"); } @Test public void testSingleHeaderSingleVal() { Response response = Response .status(Response.Status.OK.getStatusCode()) .header("key1", "val1") .build(); assertEquals("val1", response.getStringHeaders().getFirst("key1")); } @Test public void testMultipleHeaderSingleVal() { Response response = Response .status(Response.Status.OK.getStatusCode()) .header("key1", "val1") .header("key2", "val2") .build(); assertEquals("val1", response.getStringHeaders().getFirst("key1")); assertEquals("val2", response.getStringHeaders().getFirst("key2")); } @Test public void testSingleHeaderRepeatedSingleVal() { Response response = Response .status(Response.Status.OK.getStatusCode()) .header("key1", "val1") .header("key1", "val2") .build(); assertEquals("val1", response.getStringHeaders().get("key1").get(0)); assertEquals("val2", response.getStringHeaders().get("key1").get(1)); } @Test public void testSingleHeaderListVal() { Response response = Response. status(Response.Status.OK.getStatusCode()). header("key1", Arrays.asList("val1", "val2")).build(); assertEquals("val1", response.getStringHeaders().get("key1").get(0)); assertEquals("val2", response.getStringHeaders().get("key1").get(1)); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/MimeMapperTest.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j; import org.testng.annotations.Test; import org.wso2.msf4j.internal.mime.MimeMapper; import org.wso2.msf4j.internal.mime.MimeMappingException; import static org.testng.AssertJUnit.assertEquals; /** * Test the functionality of MimeMapper. */ public class MimeMapperTest { @Test public void testMimeMappingForKnownExtension() throws MimeMappingException { String mimeType = MimeMapper.getMimeType("png"); assertEquals("image/png", mimeType); } @Test(expectedExceptions = MimeMappingException.class) public void testMimeMappingForUnknownExtension() throws MimeMappingException { MimeMapper.getMimeType("unknownext"); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/MutualAuthServerTest.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.wso2.msf4j.conf.Constants; import org.wso2.msf4j.conf.SSLClientContext; import org.wso2.msf4j.exception.TestExceptionMapper; import org.wso2.msf4j.exception.TestExceptionMapper2; import org.wso2.msf4j.service.SecondService; import org.wso2.msf4j.service.TestMicroServiceWithDynamicPath; import org.wso2.msf4j.service.TestMicroservice; import java.io.File; import java.net.URI; import java.nio.file.Files; import java.nio.file.StandardCopyOption; /** * Test the HttpsServer with mutual authentication. */ public class MutualAuthServerTest extends HttpsServerTest { private final TestMicroservice testMicroservice = new TestMicroservice(); private final SecondService secondService = new SecondService(); private MicroservicesRunner microservicesRunner; private MicroservicesRunner secondMicroservicesRunner; private static String hostname = Constants.HOSTNAME; private static final int port = Constants.PORT + 5; private static File trustKeyStore; @BeforeClass public void setup() throws Exception { baseURI = URI.create(String.format("https://%s:%d", hostname, port)); trustKeyStore = new File(tmpFolder, "MutualAuthServerTest.jks"); trustKeyStore.createNewFile(); Files.copy(Thread.currentThread().getContextClassLoader().getResource("client.jks").openStream(), trustKeyStore.toPath(), StandardCopyOption.REPLACE_EXISTING); String trustKeyStorePassword = "password"; setSslClientContext(new SSLClientContext(trustKeyStore, trustKeyStorePassword)); System.setProperty("transports.netty.conf", Thread.currentThread().getContextClassLoader().getResource("netty-transports-2.yaml") .getPath()); microservicesRunner = new MicroservicesRunner(); microservicesRunner.addExceptionMapper(new TestExceptionMapper(), new TestExceptionMapper2()) .deploy(testMicroservice).start(); secondMicroservicesRunner = new MicroservicesRunner(port + 1); secondMicroservicesRunner.deploy(secondService).start(); microservicesRunner.deploy("/DynamicPath", new TestMicroServiceWithDynamicPath()); microservicesRunner.deploy("/DynamicPath2", new TestMicroServiceWithDynamicPath()); } @AfterClass public void teardown() throws Exception { microservicesRunner.stop(); secondMicroservicesRunner.stop(); trustKeyStore.delete(); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/PathRouterTest.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j; import org.testng.annotations.Test; import org.wso2.msf4j.internal.router.PatternPathRouter; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; /** * Test the routing logic using String as the destination. */ public class PathRouterTest { @Test public void testPathRoutings() { PatternPathRouter pathRouter = PatternPathRouter.create(); pathRouter.add("/", "root"); pathRouter.add("/foo/{baz}/b", "foobarb"); pathRouter.add("/foo/bar/baz", "foobarbaz"); pathRouter.add("/baz/bar", "bazbar"); pathRouter.add("/bar", "bar"); pathRouter.add("/foo/bar", "foobar"); pathRouter.add("//multiple/slash//route", "multipleslashroute"); pathRouter.add("/multi/match/**", "multi-match-*"); pathRouter.add("/multi/match/def", "multi-match-def"); pathRouter.add("/multi/maxmatch/**", "multi-max-match-*"); pathRouter.add("/multi/maxmatch/{id}", "multi-max-match-id"); pathRouter.add("/multi/maxmatch/foo", "multi-max-match-foo"); pathRouter.add("**/wildcard/{id}", "wildcard-id"); pathRouter.add("/**/wildcard/{id}", "slash-wildcard-id"); pathRouter.add("**/wildcard/**/foo/{id}", "wildcard-foo-id"); pathRouter.add("/**/wildcard/**/foo/{id}", "slash-wildcard-foo-id"); pathRouter.add("**/wildcard/**/foo/{id}/**", "wildcard-foo-id-2"); pathRouter.add("/**/wildcard/**/foo/{id}/**", "slash-wildcard-foo-id-2"); List> routes; routes = pathRouter.getDestinations(""); assertEquals(1, routes.size()); assertEquals("root", routes.get(0).getDestination()); assertTrue(routes.get(0).getGroupNameValues().isEmpty()); routes = pathRouter.getDestinations("/"); assertEquals(1, routes.size()); assertEquals("root", routes.get(0).getDestination()); assertTrue(routes.get(0).getGroupNameValues().isEmpty()); routes = pathRouter.getDestinations("/foo/bar/baz"); assertEquals(1, routes.size()); assertEquals("foobarbaz", routes.get(0).getDestination()); assertTrue(routes.get(0).getGroupNameValues().isEmpty()); routes = pathRouter.getDestinations("/baz/bar"); assertEquals(1, routes.size()); assertEquals("bazbar", routes.get(0).getDestination()); assertTrue(routes.get(0).getGroupNameValues().isEmpty()); routes = pathRouter.getDestinations("/foo/bar/baz/moo"); assertTrue(routes.isEmpty()); routes = pathRouter.getDestinations("/bar/121"); assertTrue(routes.isEmpty()); routes = pathRouter.getDestinations("/foo/bar/b"); assertEquals(1, routes.size()); assertEquals("foobarb", routes.get(0).getDestination()); assertEquals(1, routes.get(0).getGroupNameValues().size()); assertEquals("bar", routes.get(0).getGroupNameValues().get("baz")); routes = pathRouter.getDestinations("/foo/bar"); assertEquals(1, routes.size()); assertEquals("foobar", routes.get(0).getDestination()); assertTrue(routes.get(0).getGroupNameValues().isEmpty()); routes = pathRouter.getDestinations("/multiple/slash/route"); assertEquals(1, routes.size()); assertEquals("multipleslashroute", routes.get(0).getDestination()); assertTrue(routes.get(0).getGroupNameValues().isEmpty()); routes = pathRouter.getDestinations("/foo/bar/bazooka"); assertTrue(routes.isEmpty()); routes = pathRouter.getDestinations("/multi/match/def"); assertEquals(2, routes.size()); Set set1 = new HashSet<>(); set1.add("multi-match-def"); set1.add("multi-match-*"); Set set2 = new HashSet<>(); set2.add(routes.get(0).getDestination()); set2.add(routes.get(1).getDestination()); assertEquals(set1, set2); assertTrue(routes.get(0).getGroupNameValues().isEmpty()); assertTrue(routes.get(1).getGroupNameValues().isEmpty()); routes = pathRouter.getDestinations("/multi/match/ghi"); assertEquals(1, routes.size()); assertEquals("multi-match-*", routes.get(0).getDestination()); assertTrue(routes.get(0).getGroupNameValues().isEmpty()); routes = pathRouter.getDestinations("/multi/maxmatch/id1"); assertEquals(2, routes.size()); set1.clear(); set1.add("multi-max-match-id"); set1.add("multi-max-match-*"); set2.clear(); set2.add(routes.get(0).getDestination()); set2.add(routes.get(1).getDestination()); assertEquals(set1, set2); //noinspection assertEqualsBetweenInconvertibleTypes Set> set11 = new HashSet<>(); Set> set12 = new HashSet<>(); set11.add(Collections.singletonMap("id", "id1")); set11.add(Collections.emptyMap()); set12.add(routes.get(0).getGroupNameValues()); set12.add(routes.get(1).getGroupNameValues()); assertEquals(set11, set12); routes = pathRouter.getDestinations("/multi/maxmatch/foo"); assertEquals(3, routes.size()); set1.clear(); set1.add("multi-max-match-id"); set1.add("multi-max-match-*"); set1.add("multi-max-match-foo"); set2.clear(); set2.add(routes.get(0).getDestination()); set2.add(routes.get(1).getDestination()); set2.add(routes.get(2).getDestination()); assertEquals(set1, set2); //noinspection assertEqualsBetweenInconvertibleTypes set11.clear(); set12.clear(); set11.add(Collections.singletonMap("id", "foo")); set11.add(Collections.emptyMap()); set12.add(routes.get(0).getGroupNameValues()); set12.add(routes.get(1).getGroupNameValues()); assertEquals(set11, set12); routes = pathRouter.getDestinations("/foo/bar/wildcard/id1"); assertEquals(2, routes.size()); set1.clear(); set1.add("wildcard-id"); set1.add("slash-wildcard-id"); set2.clear(); set2.add(routes.get(0).getDestination()); set2.add(routes.get(1).getDestination()); assertEquals(set1, set2); //noinspection assertEqualsBetweenInconvertibleTypes set11.clear(); set12.clear(); set11.add(Collections.singletonMap("id", "id1")); set11.add(Collections.singletonMap("id", "id1")); set12.add(routes.get(0).getGroupNameValues()); set12.add(routes.get(1).getGroupNameValues()); assertEquals(set11, set12); routes = pathRouter.getDestinations("/wildcard/id1"); assertEquals(1, routes.size()); assertEquals("wildcard-id", routes.get(0).getDestination()); set11.clear(); set11.add(Collections.singletonMap("id", "id1")); assertEquals(Collections.singletonMap("id", "id1"), routes.get(0).getGroupNameValues()); routes = pathRouter.getDestinations("/foo/bar/wildcard/bar/foo/id1"); assertEquals(2, routes.size()); set1.clear(); set1.add("wildcard-foo-id"); set1.add("slash-wildcard-foo-id"); set2.clear(); set2.add(routes.get(0).getDestination()); set2.add(routes.get(1).getDestination()); assertEquals(set1, set2); //noinspection assertEqualsBetweenInconvertibleTypes set11.clear(); set12.clear(); set11.add(Collections.singletonMap("id", "id1")); set11.add(Collections.singletonMap("id", "id1")); set12.add(routes.get(0).getGroupNameValues()); set12.add(routes.get(1).getGroupNameValues()); assertEquals(set11, set12); routes = pathRouter.getDestinations("/foo/bar/wildcard/bar/foo/id1/baz/bar"); assertEquals(2, routes.size()); set1.clear(); set1.add("wildcard-foo-id-2"); set1.add("slash-wildcard-foo-id-2"); set2.clear(); set2.add(routes.get(0).getDestination()); set2.add(routes.get(1).getDestination()); assertEquals(set1, set2); //noinspection assertEqualsBetweenInconvertibleTypes set11.clear(); set12.clear(); set11.add(Collections.singletonMap("id", "id1")); set11.add(Collections.singletonMap("id", "id1")); set12.add(routes.get(0).getGroupNameValues()); set12.add(routes.get(1).getGroupNameValues()); assertEquals(set11, set12); routes = pathRouter.getDestinations("/wildcard/bar/foo/id1/baz/bar"); assertEquals(1, routes.size()); assertEquals("wildcard-foo-id-2", routes.get(0).getDestination()); set11.clear(); set11.add(Collections.singletonMap("id", "id1")); assertEquals(Collections.singletonMap("id", "id1"), routes.get(0).getGroupNameValues()); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/SSLKeyStoreTest.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import org.wso2.msf4j.conf.SSLConfig; import org.wso2.msf4j.conf.SSLHandlerFactory; import java.io.File; import java.nio.file.Files; import java.nio.file.StandardCopyOption; /** * Tests SSL KeyStore behaviour. */ public class SSLKeyStoreTest { public static File tmpFolder; private static File keyStore; @BeforeClass public static void setup() throws Exception { keyStore = new File(tmpFolder, "KeyStore.jks"); keyStore.createNewFile(); Files.copy(Thread.currentThread().getContextClassLoader().getResource("cert.jks").openStream(), keyStore.toPath(), StandardCopyOption.REPLACE_EXISTING); } @AfterClass public static void cleanup() { keyStore.delete(); } @Test(expectedExceptions = IllegalArgumentException.class) public void testSslCertPathConfiguration1() throws IllegalArgumentException { //Bad Certificate Path new SSLHandlerFactory(SSLConfig.builder(new File("badCertificate"), "secret").setCertificatePassword("secret") .build()); } @Test(expectedExceptions = IllegalArgumentException.class) public void testSslCertPathConfiguration2() throws IllegalArgumentException { //Null Certificate Path new SSLHandlerFactory(SSLConfig.builder(null, "secret").setCertificatePassword("secret").build()); } @Test(expectedExceptions = IllegalArgumentException.class) public void testSslKeyStorePassConfiguration2() throws IllegalArgumentException { //Missing Key Pass new SSLHandlerFactory(SSLConfig.builder(keyStore, null).setCertificatePassword("secret").build()); } @Test public void testSslCertPassConfiguration() throws IllegalArgumentException { //Bad Cert Pass new SSLHandlerFactory(SSLConfig.builder(keyStore, "secret").build()); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/TransportConfigurationTest.java ================================================ /* * Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j; import io.netty.handler.codec.http.HttpHeaderNames; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.wso2.carbon.config.ConfigProviderFactory; import org.wso2.carbon.config.ConfigurationException; import org.wso2.msf4j.conf.Constants; import org.wso2.msf4j.conf.SSLClientContext; import org.wso2.msf4j.exception.TestExceptionMapper; import org.wso2.msf4j.exception.TestExceptionMapper2; import org.wso2.msf4j.service.SecondService; import org.wso2.msf4j.service.TestMicroServiceWithDynamicPath; import org.wso2.msf4j.service.TestMicroservice; import org.wso2.transport.http.netty.contract.config.TransportsConfiguration; import java.io.File; import java.io.IOException; import java.net.HttpURLConnection; import java.net.Socket; import java.net.URI; import java.net.URL; import java.nio.file.Paths; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.ws.rs.HttpMethod; import static org.wso2.msf4j.internal.MSF4JConstants.WSO2_TRANSPORT_HTTP_CONFIG_NAMESPACE; /** * Test the HttpsServer. */ public class TransportConfigurationTest extends HttpServerTest { private static SSLClientContext sslClientContext; private final TestMicroservice testMicroservice = new TestMicroservice(); private final SecondService secondService = new SecondService(); private MicroservicesRunner microservicesRunner; private MicroservicesRunner secondMicroservicesRunner; private static final int port = Constants.PORT + 4; @BeforeClass public void setup() throws Exception { baseURI = URI.create(String.format("https://%s:%d", Constants.HOSTNAME, port)); TransportsConfiguration transportsConfiguration = getConfiguration(Thread.currentThread(). getContextClassLoader().getResource("netty-transports-1.yaml").getPath()); microservicesRunner = new MicroservicesRunner(transportsConfiguration); sslClientContext = new SSLClientContext(); microservicesRunner .addExceptionMapper(new TestExceptionMapper(), new TestExceptionMapper2()) .deploy(testMicroservice) .start(); secondMicroservicesRunner = new MicroservicesRunner(port + 1); secondMicroservicesRunner.deploy(secondService).start(); microservicesRunner.deploy("/DynamicPath", new TestMicroServiceWithDynamicPath()); microservicesRunner.deploy("/DynamicPath2", new TestMicroServiceWithDynamicPath()); } /** * Get the {@code TransportsConfiguration} represented by a particular configuration file * * @param configFileLocation configuration file location * @return TransportsConfiguration represented by a particular configuration file */ public TransportsConfiguration getConfiguration(String configFileLocation) { TransportsConfiguration transportsConfiguration; File file = new File(configFileLocation); if (file.exists()) { try { transportsConfiguration = ConfigProviderFactory.getConfigProvider(Paths.get(configFileLocation), null) .getConfigurationObject(WSO2_TRANSPORT_HTTP_CONFIG_NAMESPACE, TransportsConfiguration.class); } catch (ConfigurationException e) { throw new RuntimeException( "Error while loading " + configFileLocation + " configuration file", e); } } else { // return a default config transportsConfiguration = new TransportsConfiguration(); } return transportsConfiguration; } @AfterClass public void teardown() throws Exception { microservicesRunner.stop(); secondMicroservicesRunner.stop(); } @Override protected HttpURLConnection request(String path, String method, boolean keepAlive) throws IOException { URL url = baseURI.resolve(path).toURL(); HttpsURLConnection.setDefaultSSLSocketFactory(sslClientContext.getClientContext().getSocketFactory()); HostnameVerifier allHostsValid = (hostname1, session) -> true; // Install the all-trusting host verifier HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid); HttpURLConnection urlConn = (HttpsURLConnection) url.openConnection(); if (method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT)) { urlConn.setDoOutput(true); } urlConn.setRequestMethod(method); if (!keepAlive) { urlConn.setRequestProperty(HttpHeaderNames.CONNECTION.toString(), HEADER_VAL_CLOSE); } return urlConn; } @Override protected Socket createRawSocket(URL url) throws IOException { return sslClientContext.getClientContext().getSocketFactory().createSocket(url.getHost(), url.getPort()); } static void setSslClientContext(SSLClientContext sslClientContext) { TransportConfigurationTest.sslClientContext = sslClientContext; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/TransportConfigurationTest2.java ================================================ /* * Copyright (c) 2019, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j; import io.netty.handler.codec.http.HttpHeaderNames; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.wso2.msf4j.conf.Constants; import org.wso2.msf4j.conf.SSLClientContext; import org.wso2.msf4j.exception.TestExceptionMapper; import org.wso2.msf4j.exception.TestExceptionMapper2; import org.wso2.msf4j.service.SecondService; import org.wso2.msf4j.service.TestMicroServiceWithDynamicPath; import org.wso2.msf4j.service.TestMicroservice; import java.io.IOException; import java.net.HttpURLConnection; import java.net.Socket; import java.net.URI; import java.net.URL; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.ws.rs.HttpMethod; /** * Test the HttpsServer. */ public class TransportConfigurationTest2 extends HttpServerTest { private static SSLClientContext sslClientContext; private final TestMicroservice testMicroservice = new TestMicroservice(); private final SecondService secondService = new SecondService(); private MicroservicesRunner microservicesRunner; private MicroservicesRunner secondMicroservicesRunner; private static final int port = Constants.PORT + 4; @BeforeClass public void setup() throws Exception { baseURI = URI.create(String.format("https://%s:%d", Constants.HOSTNAME, port)); System.setProperty("transports.netty.conf", Thread.currentThread(). getContextClassLoader().getResource("netty-transports-4.yaml").getPath()); microservicesRunner = new MicroservicesRunner(); sslClientContext = new SSLClientContext(); microservicesRunner .addExceptionMapper(new TestExceptionMapper(), new TestExceptionMapper2()) .deploy(testMicroservice) .start(); secondMicroservicesRunner = new MicroservicesRunner(port + 1); secondMicroservicesRunner.deploy(secondService).start(); microservicesRunner.deploy("/DynamicPath", new TestMicroServiceWithDynamicPath()); microservicesRunner.deploy("/DynamicPath2", new TestMicroServiceWithDynamicPath()); } @AfterClass public void teardown() throws Exception { microservicesRunner.stop(); secondMicroservicesRunner.stop(); } @Override protected HttpURLConnection request(String path, String method, boolean keepAlive) throws IOException { URL url = baseURI.resolve(path).toURL(); HttpsURLConnection.setDefaultSSLSocketFactory(sslClientContext.getClientContext().getSocketFactory()); HostnameVerifier allHostsValid = (hostname1, session) -> true; // Install the all-trusting host verifier HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid); HttpURLConnection urlConn = (HttpsURLConnection) url.openConnection(); if (method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT)) { urlConn.setDoOutput(true); } urlConn.setRequestMethod(method); if (!keepAlive) { urlConn.setRequestProperty(HttpHeaderNames.CONNECTION.toString(), HEADER_VAL_CLOSE); } return urlConn; } @Override protected Socket createRawSocket(URL url) throws IOException { return sslClientContext.getClientContext().getSocketFactory().createSocket(url.getHost(), url.getPort()); } static void setSslClientContext(SSLClientContext sslClientContext) { TransportConfigurationTest2.sslClientContext = sslClientContext; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/beanconversion/BeanConverterTest.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.beanconversion; import org.testng.annotations.Test; import org.wso2.msf4j.internal.beanconversion.BeanConverter; import org.wso2.msf4j.pojo.Category; import org.wso2.msf4j.pojo.Pet; import org.wso2.msf4j.pojo.XmlBean; import java.nio.ByteBuffer; import static org.testng.AssertJUnit.assertEquals; /** * Tests the functionality of BeanConverter. */ public class BeanConverterTest { @Test public void testJsonBeanConversionTextJson() throws BeanConversionException { Pet pet = makePet(); ByteBuffer json = BeanConverter.getConverter("text/json").toMedia(pet); Pet pet1 = (Pet) BeanConverter.getConverter("text/json").toObject(json, Pet.class); assertEquals(pet.getId(), pet1.getId()); assertEquals(pet.getDetails(), pet1.getDetails()); assertEquals(pet.getImage(), pet1.getImage()); assertEquals(pet.getCategory().getName(), pet1.getCategory().getName()); assertEquals(pet.getAgeMonths(), pet1.getAgeMonths()); assertEquals(pet.getPrice(), pet1.getPrice(), 0); assertEquals(pet.getDateAdded(), pet1.getDateAdded()); } @Test public void testJsonBeanConversionApplicationJson() throws BeanConversionException { Pet original = makePet(); ByteBuffer json = BeanConverter.getConverter("application/json").toMedia(original); Pet result = (Pet) BeanConverter.getConverter("application/json").toObject(json, Pet.class); assertEquals(original.getId(), result.getId()); assertEquals(original.getDetails(), result.getDetails()); assertEquals(original.getImage(), result.getImage()); assertEquals(original.getCategory().getName(), result.getCategory().getName()); assertEquals(original.getAgeMonths(), result.getAgeMonths()); assertEquals(original.getPrice(), result.getPrice(), 0); assertEquals(original.getDateAdded(), result.getDateAdded()); } @Test public void testTextPlainBeanConversion() throws BeanConversionException { String val = "Test_String"; ByteBuffer media = BeanConverter.getConverter("text/plain").toMedia(val); Object obj1 = BeanConverter.getConverter("text/plain").toObject(media, null); assertEquals(obj1, val); } @Test public void testAnyBeanConversion() throws BeanConversionException { String original = "Test_String"; ByteBuffer media = BeanConverter.getConverter("*/*").toMedia(original); Object result = BeanConverter.getConverter("*/*").toObject(media, null); assertEquals(original, result); } @Test public void testXmlBeanConversion() throws BeanConversionException { XmlBean original = makeXmlBan(); ByteBuffer xml = BeanConverter.getConverter("text/xml").toMedia(original); XmlBean result = (XmlBean) BeanConverter.getConverter("text/xml").toObject(xml, XmlBean.class); assertEquals(original.getName(), result.getName()); assertEquals(original.getId(), result.getId()); assertEquals(original.getValue(), result.getValue()); } private XmlBean makeXmlBan() { XmlBean xmlBean = new XmlBean(); xmlBean.setId(12); xmlBean.setName("xml-bean-name"); xmlBean.setValue(457); return xmlBean; } private Pet makePet() { Pet pet = new Pet(); pet.setCategory(new Category("dog")); pet.setAgeMonths(3); pet.setDetails("small-cat"); pet.setPrice(10.5f); pet.setImage("cat.png"); return pet; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/conf/Constants.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.conf; /** * Constants related to tests. */ public class Constants { public static final String HOSTNAME = "localhost"; public static final int PORT = 8090; } ================================================ FILE: core/src/test/java/org/wso2/msf4j/conf/SSLClientContext.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.conf; import org.apache.commons.io.IOUtils; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.security.KeyStore; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; /** * Provides Client Context. * Used by HttpsServerTest */ public class SSLClientContext { private SSLContext clientContext; private String protocol = "TLS"; public SSLClientContext() { this(null, null); } public SSLClientContext(File keyStore, String keyStorePassword) { try { KeyManagerFactory kmf = null; if (keyStore != null && keyStorePassword != null) { KeyStore ks = getKeyStore(keyStore, keyStorePassword); kmf = KeyManagerFactory.getInstance("SunX509"); kmf.init(ks, keyStorePassword.toCharArray()); } clientContext = SSLContext.getInstance(protocol); clientContext.init(kmf == null ? null : kmf.getKeyManagers(), TrustManagerFactory.getTrustManagers(), null); } catch (Exception e) { throw new RuntimeException("Failed to initialize the client-side SSLContext", e); } } private static KeyStore getKeyStore(File keyStore, String keyStorePassword) throws IOException { KeyStore ks = null; InputStream is = new FileInputStream(keyStore); try { ks = KeyStore.getInstance("JKS"); ks.load(is, keyStorePassword.toCharArray()); } catch (Exception ex) { if (ex instanceof RuntimeException) { throw ((RuntimeException) ex); } throw new IOException(ex); } finally { IOUtils.closeQuietly(is); } return ks; } public SSLContext getClientContext() { return clientContext; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/conf/SSLConfig.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.conf; import java.io.File; /** * A class that encapsulates SSLContext configuration. */ public class SSLConfig { private final File keyStore; private final String keyStorePassword; private final String certificatePassword; private final File trustKeyStore; private final String trustKeyStorePassword; private SSLConfig(File keyStore, String keyStorePassword, String certificatePassword, File trustKeyStore, String trustKeyStorePassword) { this.keyStore = keyStore; this.keyStorePassword = keyStorePassword; this.certificatePassword = certificatePassword; this.trustKeyStore = trustKeyStore; this.trustKeyStorePassword = trustKeyStorePassword; } /** * * @param keyStore Key store * @param keyStorePassword password for accessing the key store * @return instance of {@code Builder} */ public static Builder builder(File keyStore, String keyStorePassword) { return new Builder(keyStore, keyStorePassword); } /** * @return KeyStore file */ public File getKeyStore() { return keyStore; } /** * @return KeyStore password. */ public String getKeyStorePassword() { return keyStorePassword; } /** * @return certificate password */ public String getCertificatePassword() { return certificatePassword; } /** * @return trust KeyStore file */ public File getTrustKeyStore() { return trustKeyStore; } /** * @return trust KeyStore password. */ public String getTrustKeyStorePassword() { return trustKeyStorePassword; } /** * Builder to help create the SSLConfig. */ public static class Builder { private final File keyStore; private final String keyStorePassword; private String certificatePassword; private File trustKeyStore; private String trustKeyStorePassword; private Builder(File keyStore, String keyStorePassword) { this.keyStore = keyStore; this.keyStorePassword = keyStorePassword; } /** * Set the certificate password for KeyStore. * * @param certificatePassword certificate password * @return instance of {@code Builder}. */ public Builder setCertificatePassword(String certificatePassword) { this.certificatePassword = certificatePassword; return this; } /** * Set trust KeyStore file. * * @param trustKeyStore trust KeyStore file. * @return an instance of {@code Builder}. */ public Builder setTrustKeyStore(File trustKeyStore) { this.trustKeyStore = trustKeyStore; return this; } /** * Set trust KeyStore password. * * @param trustKeyStorePassword trust KeyStore password. * @return an instance of {@code Builder}. */ public Builder setTrustKeyStorePassword(String trustKeyStorePassword) { if (trustKeyStorePassword == null) { throw new IllegalArgumentException("KeyStore Password Not Configured"); } this.trustKeyStorePassword = trustKeyStorePassword; return this; } /** * @return instance of {@code SSLConfig} */ public SSLConfig build() { if (keyStore == null) { throw new IllegalArgumentException("Certificate File Not Configured"); } if (keyStorePassword == null) { throw new IllegalArgumentException("KeyStore Password Not Configured"); } return new SSLConfig(keyStore, keyStorePassword, certificatePassword, trustKeyStore, trustKeyStorePassword); } } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/conf/SSLHandlerFactory.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.conf; import org.apache.commons.io.IOUtils; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.Security; import java.security.UnrecoverableKeyException; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; /** * A class that encapsulates SSL Certificate Information. */ public class SSLHandlerFactory { private static final String protocol = "TLS"; private final SSLContext serverContext; private boolean needClientAuth; public SSLHandlerFactory(SSLConfig sslConfig) { String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm"); if (algorithm == null) { algorithm = "SunX509"; } try { KeyStore ks = getKeyStore(sslConfig.getKeyStore(), sslConfig.getKeyStorePassword()); // Set up key manager factory to use our key store KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm); kmf.init(ks, sslConfig.getCertificatePassword() != null ? sslConfig.getCertificatePassword().toCharArray() : sslConfig.getKeyStorePassword().toCharArray()); KeyManager[] keyManagers = kmf.getKeyManagers(); TrustManager[] trustManagers = null; if (sslConfig.getTrustKeyStore() != null) { this.needClientAuth = true; KeyStore tks = getKeyStore(sslConfig.getTrustKeyStore(), sslConfig.getTrustKeyStorePassword()); TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm); tmf.init(tks); trustManagers = tmf.getTrustManagers(); } serverContext = SSLContext.getInstance(protocol); serverContext.init(keyManagers, trustManagers, null); } catch (UnrecoverableKeyException | KeyManagementException | NoSuchAlgorithmException | KeyStoreException | IOException e) { throw new IllegalArgumentException("Failed to initialize the server-side SSLContext", e); } } private static KeyStore getKeyStore(File keyStore, String keyStorePassword) throws IOException { KeyStore ks = null; InputStream is = new FileInputStream(keyStore); try { ks = KeyStore.getInstance("JKS"); ks.load(is, keyStorePassword.toCharArray()); } catch (Exception ex) { if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } throw new IOException(ex); } finally { IOUtils.closeQuietly(is); } return ks; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/conf/TrustManagerFactory.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.conf; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.security.InvalidAlgorithmParameterException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.cert.X509Certificate; import javax.net.ssl.ManagerFactoryParameters; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactorySpi; import javax.net.ssl.X509TrustManager; /** * Dummy TrustManager {@link TrustManagerFactorySpi} that accepts any certificate. */ public class TrustManagerFactory extends TrustManagerFactorySpi { private static final Logger log = LoggerFactory.getLogger(TrustManagerFactory.class); private static final TrustManager DUMMY_TRUST_MANAGER = new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } public void checkClientTrusted(X509Certificate[] chain, String authType) { // This example always trusts the incoming certificate. // Perform certificate inspection and invalid certificate check here log.error("UNKNOWN CLIENT CERTIFICATE: " + chain[0].getSubjectDN()); } public void checkServerTrusted(X509Certificate[] chain, String authType) { // Always trust - it is an example. // You should do something in the real world. log.error("UNKNOWN SERVER CERTIFICATE: " + chain[0].getSubjectDN()); } }; public static TrustManager[] getTrustManagers() { return new TrustManager[]{DUMMY_TRUST_MANAGER}; } @Override protected TrustManager[] engineGetTrustManagers() { return getTrustManagers(); } @Override protected void engineInit(KeyStore keystore) throws KeyStoreException { // Unused } @Override protected void engineInit(ManagerFactoryParameters managerFactoryParameters) throws InvalidAlgorithmParameterException { // Unused } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/exception/MappedException.java ================================================ /* * Copyright (c) 2005-2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ package org.wso2.msf4j.exception; /** * Test exception for testing ExceptionMapper. */ public class MappedException extends Exception { public MappedException() { super(); } public MappedException(String message) { super(message); } public MappedException(String message, Throwable cause) { super(message, cause); } public MappedException(Throwable cause) { super(cause); } protected MappedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/exception/MappedException2.java ================================================ /* * Copyright (c) 2005-2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ package org.wso2.msf4j.exception; /** * Test exception for testing ExceptionMapper. */ public class MappedException2 extends Exception { public MappedException2() { super(); } public MappedException2(String message) { super(message); } public MappedException2(String message, Throwable cause) { super(message, cause); } public MappedException2(Throwable cause) { super(cause); } protected MappedException2(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/exception/TestExceptionMapper.java ================================================ /* * Copyright (c) 2005-2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ package org.wso2.msf4j.exception; import javax.ws.rs.core.Response; import javax.ws.rs.ext.ExceptionMapper; /** * Test exception mapper used for testing ExceptionMapper feature. */ public class TestExceptionMapper implements ExceptionMapper { @Override public Response toResponse(MappedException e) { return Response.status(404). entity(e.getMessage()). type("text/plain"). build(); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/exception/TestExceptionMapper2.java ================================================ /* * Copyright (c) 2005-2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ package org.wso2.msf4j.exception; import javax.ws.rs.core.Response; import javax.ws.rs.ext.ExceptionMapper; /** * Test exception mapper used for testing ExceptionMapper feature. */ public class TestExceptionMapper2 implements ExceptionMapper { @Override public Response toResponse(MappedException2 e) { return Response.status(Response.Status.EXPECTATION_FAILED). entity(e.getMessage()). type("text/plain"). build(); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/interceptor/HighPriorityClassRequestInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; /** * High priority request interceptor. */ public class HighPriorityClassRequestInterceptor implements RequestInterceptor { @Override public boolean interceptRequest(Request request, Response response) throws Exception { PriorityDataHolder.setPriorityOrder(PriorityDataHolder.getPriorityOrder() + this.getClass().getSimpleName()); return true; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/interceptor/HighPriorityClassResponseInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; /** * High priority response interceptor. */ public class HighPriorityClassResponseInterceptor implements ResponseInterceptor { @Override public boolean interceptResponse(Request request, Response response) throws Exception { PriorityDataHolder.setPriorityOrder(PriorityDataHolder.getPriorityOrder() + this.getClass().getSimpleName()); return true; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/interceptor/HighPriorityGlobalRequestInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import java.util.concurrent.atomic.AtomicInteger; /** * High priority global request interceptor. */ public class HighPriorityGlobalRequestInterceptor implements RequestInterceptor { private static AtomicInteger filterCalls = new AtomicInteger(0); /** * Reset interceptor call count. */ public static void reset() { filterCalls.set(0); } /** * Get the number of interceptor calls. * * @return number of calls */ public static int getFilterCalls() { return filterCalls.get(); } @Override public boolean interceptRequest(Request request, Response response) throws Exception { filterCalls.incrementAndGet(); PriorityDataHolder.setPriorityOrder(PriorityDataHolder.getPriorityOrder() + this.getClass().getSimpleName()); return true; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/interceptor/HighPriorityGlobalResponseInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import java.util.concurrent.atomic.AtomicInteger; /** * High priority global response interceptor. */ public class HighPriorityGlobalResponseInterceptor implements ResponseInterceptor { private static AtomicInteger filterCalls = new AtomicInteger(0); /** * Reset interceptor call count. */ public static void reset() { filterCalls.set(0); } /** * Get the number of interceptor calls. * * @return number of calls */ public static int getFilterCalls() { return filterCalls.get(); } @Override public boolean interceptResponse(Request request, Response response) throws Exception { filterCalls.incrementAndGet(); PriorityDataHolder.setPriorityOrder(PriorityDataHolder.getPriorityOrder() + this.getClass().getSimpleName()); return true; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/interceptor/HighPriorityMethodRequestInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; /** * High priority request interceptor. */ public class HighPriorityMethodRequestInterceptor implements RequestInterceptor { @Override public boolean interceptRequest(Request request, Response response) throws Exception { PriorityDataHolder.setPriorityOrder(PriorityDataHolder.getPriorityOrder() + this.getClass().getSimpleName()); return true; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/interceptor/HighPriorityMethodResponseInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; /** * High priority response interceptor. */ public class HighPriorityMethodResponseInterceptor implements ResponseInterceptor { @Override public boolean interceptResponse(Request request, Response response) throws Exception { PriorityDataHolder.setPriorityOrder(PriorityDataHolder.getPriorityOrder() + this.getClass().getSimpleName()); return true; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/interceptor/LowPriorityClassRequestInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; /** * Low priority request interceptor. */ public class LowPriorityClassRequestInterceptor implements RequestInterceptor { @Override public boolean interceptRequest(Request request, Response response) throws Exception { PriorityDataHolder.setPriorityOrder(PriorityDataHolder.getPriorityOrder() + this.getClass().getSimpleName()); return true; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/interceptor/LowPriorityClassResponseInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; /** * Low priority response interceptor. */ public class LowPriorityClassResponseInterceptor implements ResponseInterceptor { @Override public boolean interceptResponse(Request request, Response response) throws Exception { PriorityDataHolder.setPriorityOrder(PriorityDataHolder.getPriorityOrder() + this.getClass().getSimpleName()); return true; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/interceptor/LowPriorityGlobalRequestInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import java.util.concurrent.atomic.AtomicInteger; /** * Low priority global request interceptor. */ public class LowPriorityGlobalRequestInterceptor implements RequestInterceptor { private static AtomicInteger filterCalls = new AtomicInteger(0); /** * Reset interceptor call count. */ public static void reset() { filterCalls.set(0); } /** * Get the number of interceptor calls. * * @return number of calls */ public static int getFilterCalls() { return filterCalls.get(); } @Override public boolean interceptRequest(Request request, Response response) throws Exception { filterCalls.incrementAndGet(); PriorityDataHolder.setPriorityOrder(PriorityDataHolder.getPriorityOrder() + this.getClass().getSimpleName()); return true; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/interceptor/LowPriorityGlobalResponseInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import java.util.concurrent.atomic.AtomicInteger; /** * Low priority global request interceptor. */ public class LowPriorityGlobalResponseInterceptor implements ResponseInterceptor { private static AtomicInteger filterCalls = new AtomicInteger(0); /** * Reset interceptor call count. */ public static void reset() { filterCalls.set(0); } /** * Get the number of interceptor calls. * * @return number of calls */ public static int getFilterCalls() { return filterCalls.get(); } @Override public boolean interceptResponse(Request request, Response response) throws Exception { filterCalls.incrementAndGet(); PriorityDataHolder.setPriorityOrder(PriorityDataHolder.getPriorityOrder() + this.getClass().getSimpleName()); return true; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/interceptor/LowPriorityMethodRequestInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; /** * Low priority request interceptor. */ public class LowPriorityMethodRequestInterceptor implements RequestInterceptor { @Override public boolean interceptRequest(Request request, Response response) throws Exception { PriorityDataHolder.setPriorityOrder(PriorityDataHolder.getPriorityOrder() + this.getClass().getSimpleName()); return true; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/interceptor/LowPriorityMethodResponseInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; /** * Low priority response interceptor. */ public class LowPriorityMethodResponseInterceptor implements ResponseInterceptor { @Override public boolean interceptResponse(Request request, Response response) throws Exception { PriorityDataHolder.setPriorityOrder(PriorityDataHolder.getPriorityOrder() + this.getClass().getSimpleName()); return true; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/interceptor/MediumPriorityClassRequestInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; /** * Medium priority request interceptor. */ public class MediumPriorityClassRequestInterceptor implements RequestInterceptor { @Override public boolean interceptRequest(Request request, Response response) throws Exception { PriorityDataHolder.setPriorityOrder(PriorityDataHolder.getPriorityOrder() + this.getClass().getSimpleName()); return true; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/interceptor/MediumPriorityClassResponseInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; /** * Medium priority response interceptor. */ public class MediumPriorityClassResponseInterceptor implements ResponseInterceptor { @Override public boolean interceptResponse(Request request, Response response) throws Exception { PriorityDataHolder.setPriorityOrder(PriorityDataHolder.getPriorityOrder() + this.getClass().getSimpleName()); return true; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/interceptor/MediumPriorityGlobalRequestInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import java.util.concurrent.atomic.AtomicInteger; /** * Medium priority global request interceptor. */ public class MediumPriorityGlobalRequestInterceptor implements RequestInterceptor { private static AtomicInteger filterCalls = new AtomicInteger(0); /** * Reset interceptor call count. */ public static void reset() { filterCalls.set(0); } /** * Get the number of interceptor calls. * * @return number of calls */ public static int getFilterCalls() { return filterCalls.get(); } @Override public boolean interceptRequest(Request request, Response response) throws Exception { filterCalls.incrementAndGet(); PriorityDataHolder.setPriorityOrder(PriorityDataHolder.getPriorityOrder() + this.getClass().getSimpleName()); return true; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/interceptor/MediumPriorityGlobalResponseInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import java.util.concurrent.atomic.AtomicInteger; /** * Medium priority global request interceptor. */ public class MediumPriorityGlobalResponseInterceptor implements ResponseInterceptor { private static AtomicInteger filterCalls = new AtomicInteger(0); /** * Reset interceptor call count. */ public static void reset() { filterCalls.set(0); } /** * Get the number of interceptor calls. * * @return number of calls */ public static int getFilterCalls() { return filterCalls.get(); } @Override public boolean interceptResponse(Request request, Response response) throws Exception { filterCalls.incrementAndGet(); PriorityDataHolder.setPriorityOrder(PriorityDataHolder.getPriorityOrder() + this.getClass().getSimpleName()); return true; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/interceptor/MediumPriorityMethodRequestInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; /** * Medium priority request interceptor. */ public class MediumPriorityMethodRequestInterceptor implements RequestInterceptor { @Override public boolean interceptRequest(Request request, Response response) throws Exception { PriorityDataHolder.setPriorityOrder(PriorityDataHolder.getPriorityOrder() + this.getClass().getSimpleName()); return true; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/interceptor/MediumPriorityMethodResponseInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; /** * Medium priority response interceptor. */ public class MediumPriorityMethodResponseInterceptor implements ResponseInterceptor { @Override public boolean interceptResponse(Request request, Response response) throws Exception { PriorityDataHolder.setPriorityOrder(PriorityDataHolder.getPriorityOrder() + this.getClass().getSimpleName()); return true; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/interceptor/PriorityDataHolder.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; /** * Note down the interceptor execution order. */ public class PriorityDataHolder { private static String priorityOrder = ""; /** * Get the executed priority order. * * @return executed priority order. */ public static String getPriorityOrder() { return priorityOrder; } /** * Set the executed priority order. * * @param priorityOrder executed priority order. */ public static void setPriorityOrder(String priorityOrder) { PriorityDataHolder.priorityOrder = priorityOrder; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/interceptor/TestBreakRequestInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import java.util.concurrent.atomic.AtomicInteger; /** * Test request interceptor. */ public class TestBreakRequestInterceptor implements RequestInterceptor { private static AtomicInteger filterCalls = new AtomicInteger(0); /** * Reset interceptor call count. */ public static void reset() { filterCalls.set(0); } /** * Get the number of interceptor calls. * * @return number of calls */ public static int getFilterCalls() { return filterCalls.get(); } @Override public boolean interceptRequest(Request request, Response response) throws Exception { filterCalls.incrementAndGet(); return false; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/interceptor/TestBreakResponseInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import java.util.concurrent.atomic.AtomicInteger; /** * Test response interceptor for fat jar mode. */ public class TestBreakResponseInterceptor implements ResponseInterceptor { private static AtomicInteger filterCalls = new AtomicInteger(0); /** * Reset interceptor call count. */ public static void reset() { filterCalls.set(0); } /** * Get the number of interceptor calls. * * @return number of calls */ public static int getFilterCalls() { return filterCalls.get(); } @Override public boolean interceptResponse(Request request, Response response) throws Exception { filterCalls.incrementAndGet(); return false; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/interceptor/TestExceptionBreakRequestInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import java.util.concurrent.atomic.AtomicInteger; /** * Test request interceptor. */ public class TestExceptionBreakRequestInterceptor implements RequestInterceptor { private static AtomicInteger filterCalls = new AtomicInteger(0); /** * Reset interceptor call count. */ public static void reset() { filterCalls.set(0); } /** * Get the number of interceptor calls. * * @return number of calls */ public static int getFilterCalls() { return filterCalls.get(); } @Override public boolean interceptRequest(Request request, Response response) throws Exception { filterCalls.incrementAndGet(); throw new Exception("Test exception"); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/interceptor/TestInterceptor.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.interceptor; import org.wso2.msf4j.Interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import org.wso2.msf4j.ServiceMethodInfo; import java.util.concurrent.atomic.AtomicInteger; /** * Interceptor used in test. */ public class TestInterceptor implements Interceptor { private volatile AtomicInteger numPreCalls = new AtomicInteger(0); private volatile AtomicInteger numPostCalls = new AtomicInteger(0); public int getNumPreCalls() { return numPreCalls.get(); } public int getNumPostCalls() { return numPostCalls.get(); } public void reset() { numPreCalls.set(0); numPostCalls.set(0); } @Override public boolean preCall(Request request, Response responder, ServiceMethodInfo serviceMethodInfo) throws Exception { numPreCalls.incrementAndGet(); String header = request.getHeader("X-Request-Type"); if (header != null && header.equals("Reject")) { responder.setStatus(javax.ws.rs.core.Response.Status.NOT_ACCEPTABLE.getStatusCode()); responder.send(); return false; } if (header != null && header.equals("PreException")) { throw new IllegalArgumentException("PreException"); } return true; } @Override public void postCall(Request request, int status, ServiceMethodInfo serviceMethodInfo) { numPostCalls.incrementAndGet(); String header = request.getHeader("X-Request-Type"); if (header != null && header.equals("PostException")) { throw new IllegalArgumentException("PostException"); } } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/interceptor/TestInterceptorDeprecated.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.wso2.msf4j.Interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import org.wso2.msf4j.ServiceMethodInfo; import java.util.concurrent.atomic.AtomicInteger; /** * Interceptor class using deprecated interceptor. */ public class TestInterceptorDeprecated implements Interceptor { private static AtomicInteger preCallInterceptorCalls = new AtomicInteger(0); private static AtomicInteger postCallInterceptorCalls = new AtomicInteger(0); /** * Reset interceptor call count. */ public static void reset() { preCallInterceptorCalls.set(0); postCallInterceptorCalls.set(0); } /** * Get the number of interceptor pre calls. * * @return number of pre calls */ public static int getPreCallInterceptorCallsCount() { return preCallInterceptorCalls.get(); } /** * Get the number of interceptor post calls. * * @return number of post calls */ public static int getPostCallInterceptorCallsCount() { return postCallInterceptorCalls.get(); } @Override public boolean preCall(Request request, Response responder, ServiceMethodInfo serviceMethodInfo) throws Exception { preCallInterceptorCalls.incrementAndGet(); PriorityDataHolder.setPriorityOrder(PriorityDataHolder.getPriorityOrder() + this.getClass().getSimpleName() + " - [PRE CALL]"); return true; } @Override public void postCall(Request request, int status, ServiceMethodInfo serviceMethodInfo) throws Exception { postCallInterceptorCalls.incrementAndGet(); PriorityDataHolder.setPriorityOrder(PriorityDataHolder.getPriorityOrder() + this.getClass().getSimpleName() + " - [POST CALL]"); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/interceptor/TestRequestInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import java.util.concurrent.atomic.AtomicInteger; /** * Test request interceptor. */ public class TestRequestInterceptor implements RequestInterceptor { private static AtomicInteger filterCalls = new AtomicInteger(0); /** * Reset interceptor call count. */ public static void reset() { filterCalls.set(0); } /** * Get the number of interceptor calls. * * @return number of calls */ public static int getFilterCalls() { return filterCalls.get(); } @Override public boolean interceptRequest(Request request, Response response) throws Exception { filterCalls.incrementAndGet(); PriorityDataHolder.setPriorityOrder(PriorityDataHolder.getPriorityOrder() + this.getClass().getSimpleName()); return true; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/interceptor/TestResponseInterceptor.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.interceptor; import org.wso2.msf4j.Request; import org.wso2.msf4j.Response; import java.util.concurrent.atomic.AtomicInteger; /** * Test response interceptor for fat jar mode. */ public class TestResponseInterceptor implements ResponseInterceptor { private static AtomicInteger filterCalls = new AtomicInteger(0); /** * Reset interceptor call count. */ public static void reset() { filterCalls.set(0); } /** * Get the number of interceptor calls. * * @return number of calls */ public static int getFilterCalls() { return filterCalls.get(); } @Override public boolean interceptResponse(Request request, Response response) throws Exception { filterCalls.incrementAndGet(); PriorityDataHolder.setPriorityOrder(PriorityDataHolder.getPriorityOrder() + this.getClass().getSimpleName()); return true; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/internal/HttpHeadersImplTest.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.internal; import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.HttpVersion; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.wso2.transport.http.netty.message.HttpCarbonMessage; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Map; import javax.ws.rs.core.Cookie; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; public class HttpHeadersImplTest { private javax.ws.rs.core.HttpHeaders httpHeaders1; private javax.ws.rs.core.HttpHeaders httpHeaders2; @BeforeMethod public void setUp() throws Exception { HttpCarbonMessage httpCarbonMessage = new HttpCarbonMessage( new DefaultHttpRequest(HttpVersion.HTTP_1_1, io.netty.handler.codec.http.HttpMethod.GET, "msf4j")); httpCarbonMessage.getHeaders().add("testA", "test1"); httpCarbonMessage.getHeaders().add("testA", "test2"); httpCarbonMessage.setHeader("Accept", "application/json"); httpCarbonMessage.setHeader("Content-Type", "text/html"); httpCarbonMessage.setHeader("Content-Language", "en"); httpCarbonMessage.setHeader("Content-Length", "1024"); httpCarbonMessage.setHeader("Date", "Sun, 06 Nov 1994 08:49:37 GMT"); httpCarbonMessage.getHeaders().add("Accept-Language", "da"); httpCarbonMessage.getHeaders().add("Accept-Language", "en-gb;q=0.8"); httpCarbonMessage.getHeaders().add("Accept-Language", "en;q=0.7"); httpCarbonMessage.getHeaders().add("Cookie", "JSESSIONID=3508015E4EF0ECA8C4B761FCC4BC1718"); httpHeaders1 = new HttpHeadersImpl(httpCarbonMessage.getHeaders()); HttpCarbonMessage httpCarbonMessage2 = new HttpCarbonMessage( new DefaultHttpRequest(HttpVersion.HTTP_1_1, io.netty.handler.codec.http.HttpMethod.GET, "msf4j")); httpHeaders2 = new HttpHeadersImpl(httpCarbonMessage2.getHeaders()); } @Test public void testGetRequestHeader() throws Exception { List headerValues = httpHeaders1.getRequestHeader("testA"); assertEquals(headerValues.size(), 2); assertTrue(headerValues.contains("test1")); assertTrue(headerValues.contains("test2")); } @Test public void testGetHeaderString() throws Exception { String headerValue = httpHeaders1.getHeaderString("testA"); assertEquals(headerValue, "test1,test2"); headerValue = httpHeaders1.getHeaderString("Accept"); assertEquals(headerValue, "application/json"); } @Test public void testGetRequestHeaders() throws Exception { MultivaluedMap headersMap = httpHeaders1.getRequestHeaders(); List headerValues = headersMap.get("testA"); assertEquals(headerValues.size(), 2); assertTrue(headerValues.contains("test1")); assertTrue(headerValues.contains("test2")); headerValues = headersMap.get("Accept"); assertEquals(headerValues.size(), 1); assertTrue(headerValues.contains("application/json")); } @Test public void testGetAcceptableMediaTypes() throws Exception { List mediaTypes = httpHeaders1.getAcceptableMediaTypes(); assertEquals(mediaTypes.size(), 1); assertEquals(mediaTypes.get(0).getType(), "application"); assertEquals(mediaTypes.get(0).getSubtype(), "json"); assertEquals(mediaTypes.get(0).toString(), "application/json"); mediaTypes = httpHeaders2.getAcceptableMediaTypes(); assertEquals(mediaTypes.size(), 1); assertEquals(mediaTypes.get(0).getType(), "*"); assertEquals(mediaTypes.get(0).getSubtype(), "*"); assertEquals(mediaTypes.get(0).toString(), "*/*"); } @Test public void testGetAcceptableLanguages() throws Exception { List locales = httpHeaders1.getAcceptableLanguages(); assertEquals(locales.size(), 3); assertEquals(locales.get(0).getLanguage(), "da"); assertEquals(locales.get(1).getLanguage(), "en-gb"); assertEquals(locales.get(2).getLanguage(), "en"); locales = httpHeaders2.getAcceptableLanguages(); assertEquals(locales.size(), 1); assertEquals(locales.get(0).getLanguage(), "*"); } @Test public void testGetMediaType() throws Exception { MediaType mediaType = httpHeaders1.getMediaType(); assertNotNull(mediaType); assertEquals(mediaType.getType(), "text"); assertEquals(mediaType.getSubtype(), "html"); assertEquals(mediaType.toString(), "text/html"); MediaType mediaType2 = httpHeaders2.getMediaType(); assertNull(mediaType2); } @Test public void testGetLanguage() throws Exception { Locale locale = httpHeaders1.getLanguage(); assertNotNull(locale); assertEquals(locale.getLanguage(), "en"); Locale locale2 = httpHeaders2.getLanguage(); assertNull(locale2); } @Test public void testGetCookies() throws Exception { Map cookieMap = httpHeaders1.getCookies(); assertNotNull(cookieMap); assertEquals(cookieMap.size(), 1); assertTrue(cookieMap.containsKey("JSESSIONID")); assertEquals(cookieMap.get("JSESSIONID").getValue(), "3508015E4EF0ECA8C4B761FCC4BC1718"); Map cookieMap2 = httpHeaders2.getCookies(); assertEquals(cookieMap2.size(), 0); } @Test public void testGetDate() throws Exception { Date date = httpHeaders1.getDate(); assertNotNull(date); assertEquals(date.getTime(), 784111777000L); Date date2 = httpHeaders2.getDate(); assertNull(date2); } @Test public void testGetLength() throws Exception { int length = httpHeaders1.getLength(); assertEquals(length, 1024); int length2 = httpHeaders2.getLength(); assertEquals(length2, -1); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/internal/MicroservicesRegistryTest.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.internal; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import org.wso2.msf4j.MicroservicesRunner; import org.wso2.msf4j.exception.MappedException; import org.wso2.msf4j.exception.MappedException2; import org.wso2.msf4j.exception.TestExceptionMapper; import org.wso2.msf4j.exception.TestExceptionMapper2; import org.wso2.msf4j.interceptor.RequestInterceptor; import org.wso2.msf4j.interceptor.ResponseInterceptor; import org.wso2.msf4j.interceptor.TestInterceptor; import org.wso2.msf4j.interceptor.TestInterceptorDeprecated; import org.wso2.msf4j.service.SecondService; import java.util.List; import java.util.Map; import java.util.Optional; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; /** * Test class to validate the MicroservicesRegistry. */ public class MicroservicesRegistryTest { private MicroservicesRunner microservicesRunner = new MicroservicesRunner(); private SecondService service = new SecondService(); private TestExceptionMapper exceptionMapper1 = new TestExceptionMapper(); private TestExceptionMapper2 exceptionMapper2 = new TestExceptionMapper2(); private TestInterceptor interceptor1 = new TestInterceptor(); private TestInterceptorDeprecated interceptor2 = new TestInterceptorDeprecated(); @BeforeClass public void setup() { microservicesRunner.getMsRegistry().addService(service); microservicesRunner.getMsRegistry().addInterceptor(interceptor1, interceptor2); microservicesRunner.getMsRegistry().addExceptionMapper(exceptionMapper1, exceptionMapper2); } /** * Test the basic functionality of MicroservicesRegistry. */ @Test public void testMicroservicesRegistry() { MicroservicesRegistryImpl msRegistry = microservicesRunner.getMsRegistry(); Optional> serviceWithBasePath = msRegistry.getServiceWithBasePath("/SecondService"); assertTrue(serviceWithBasePath.isPresent()); assertEquals(msRegistry.getHttpServices().size(), 1); assertEquals(msRegistry.getServiceCount(), 1); assertTrue(msRegistry.getHttpServices().contains(service)); msRegistry.removeService(service); serviceWithBasePath = msRegistry.getServiceWithBasePath("/SecondService"); assertFalse(serviceWithBasePath.isPresent()); List globalRequestInterceptorList = msRegistry.getGlobalRequestInterceptorList(); assertEquals(globalRequestInterceptorList.size(), 2); assertEquals(globalRequestInterceptorList.get(0), interceptor1); List globalResponseInterceptorList = msRegistry.getGlobalResponseInterceptorList(); assertEquals(globalResponseInterceptorList.size(), 2); assertEquals(globalResponseInterceptorList.get(0), interceptor1); msRegistry.removeInterceptor(interceptor1); assertEquals(msRegistry.getGlobalRequestInterceptorList().size(), 1); assertEquals(msRegistry.getGlobalResponseInterceptorList().size(), 1); msRegistry.removeGlobalRequestInterceptor(interceptor2); assertEquals(msRegistry.getGlobalRequestInterceptorList().size(), 0); assertEquals(msRegistry.getGlobalResponseInterceptorList().size(), 1); msRegistry.removeGlobalResponseInterceptor(interceptor2); assertEquals(msRegistry.getGlobalRequestInterceptorList().size(), 0); assertEquals(msRegistry.getGlobalResponseInterceptorList().size(), 0); assertTrue(msRegistry.getExceptionMapper(new MappedException()).isPresent()); assertTrue(msRegistry.getExceptionMapper(new MappedException2()).isPresent()); msRegistry.removeExceptionMapper(exceptionMapper1); //TODO this assertion get fail when enable coverage. Need to check why assertFalse(msRegistry.getExceptionMapper(new MappedException()).isPresent()); assertTrue(msRegistry.getExceptionMapper(new MappedException2()).isPresent()); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/pojo/Category.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.pojo; /** * The pet category; such as dog, cat, bird, reptile, fish etc. */ public class Category { private String name; public Category(String name) { this.name = name; } public String getName() { return name; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/pojo/Company.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.pojo; /** * Simple bean class for a company. */ public class Company { private String type; public String getType() { return type; } public void setType(String type) { this.type = type; } @Override public String toString() { return "Company {" + "type='" + type + '\'' + '}'; } /*public Company(String type) { this.type = type; }*/ } ================================================ FILE: core/src/test/java/org/wso2/msf4j/pojo/Person.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.pojo; /** * Simple Bean class for a Person. */ public class Person { private String name; private int age; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } /*public Person(String name) { this.name = name; }*/ } ================================================ FILE: core/src/test/java/org/wso2/msf4j/pojo/Pet.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.pojo; import java.util.UUID; /** * Bean class used for testing JAX-RS bean conversions. */ public class Pet { private String id = UUID.randomUUID().toString(); private Category category; private int ageMonths; private String details; private float price; private long dateAdded = System.currentTimeMillis(); private String image; public Pet() { } public int getAgeMonths() { return ageMonths; } public void setAgeMonths(int ageMonths) { this.ageMonths = ageMonths; } public Category getCategory() { return category; } public void setCategory(Category category) { this.category = category; } public long getDateAdded() { return dateAdded; } public String getDetails() { return details; } public void setDetails(String details) { this.details = details; } public String getId() { return id; } public String getImage() { return image; } public void setImage(String image) { this.image = image; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/pojo/TextBean.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.pojo; /** * Bean class used for testing JAX-RS bean conversions. */ public class TextBean { private String text; public String getText() { return text; } public void setText(String text) { this.text = text; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/pojo/XmlBean.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.pojo; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; /** * Xml bean conversion test bean. */ @XmlRootElement public class XmlBean { String name; int value; int id; public String getName() { return name; } @XmlElement public void setName(String name) { this.name = name; } public int getValue() { return value; } @XmlElement public void setValue(int value) { this.value = value; } public int getId() { return id; } @XmlAttribute public void setId(int id) { this.id = id; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/service/ExtendedTestMicroservice.java ================================================ /* * Copyright (c) 2005-2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ package org.wso2.msf4j.service; import com.google.gson.JsonObject; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.core.Response; /** * Service for testing inheritance. */ @Path("/ext-test/v1") public class ExtendedTestMicroservice extends TestMicroservice { @Path("resource") @GET public Response testExtendendGet() { JsonObject object = new JsonObject(); object.addProperty("status", "Handled extended get in resource end-point"); return Response.status(Response.Status.OK).entity(object).build(); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/service/InterceptorTestMicroService.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.service; import org.wso2.msf4j.Microservice; import org.wso2.msf4j.interceptor.PriorityDataHolder; import org.wso2.msf4j.interceptor.TestBreakRequestInterceptor; import org.wso2.msf4j.interceptor.TestBreakResponseInterceptor; import org.wso2.msf4j.interceptor.TestExceptionBreakRequestInterceptor; import org.wso2.msf4j.interceptor.TestRequestInterceptor; import org.wso2.msf4j.interceptor.TestResponseInterceptor; import org.wso2.msf4j.interceptor.annotation.RequestInterceptor; import org.wso2.msf4j.interceptor.annotation.ResponseInterceptor; import org.wso2.msf4j.service.sub.Team; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; /** * Micro Service to test interceptors. */ @Path("/test/interceptorTest") @RequestInterceptor(TestRequestInterceptor.class) @ResponseInterceptor(TestResponseInterceptor.class) public class InterceptorTestMicroService implements Microservice { @GET @Path("/interceptorBreakOnExceptionTest") @RequestInterceptor({TestExceptionBreakRequestInterceptor.class, TestRequestInterceptor.class}) public String interceptorBreakOnExceptionTest() { return "Exception break interceptor test"; } @GET @Path("/requestInterceptorBreakByUserTest") @RequestInterceptor({TestBreakRequestInterceptor.class, TestRequestInterceptor.class}) public String requestInterceptorBreakByUserTest() { return "Manual break request interceptor test"; } @GET @Path("/responseInterceptorBreakByUserTest") @RequestInterceptor(TestRequestInterceptor.class) @ResponseInterceptor({TestBreakResponseInterceptor.class, TestResponseInterceptor.class}) public String responseInterceptorBreakByUserTest() { return "Manual break response interceptor test"; } @Produces(MediaType.APPLICATION_JSON) @Path("/subResourceLocatorTest/{countryId}") @RequestInterceptor(TestRequestInterceptor.class) @ResponseInterceptor(TestResponseInterceptor.class) public Team subResourceLocatorTest(@PathParam("countryId") String countryId) { PriorityDataHolder.setPriorityOrder(PriorityDataHolder.getPriorityOrder() + "[HTTP RESOURCE METHOD]"); return new Team(countryId); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/service/PriorityInterceptorTestMicroService.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.service; import org.wso2.msf4j.Microservice; import org.wso2.msf4j.interceptor.HighPriorityClassRequestInterceptor; import org.wso2.msf4j.interceptor.HighPriorityClassResponseInterceptor; import org.wso2.msf4j.interceptor.HighPriorityMethodRequestInterceptor; import org.wso2.msf4j.interceptor.HighPriorityMethodResponseInterceptor; import org.wso2.msf4j.interceptor.LowPriorityClassRequestInterceptor; import org.wso2.msf4j.interceptor.LowPriorityClassResponseInterceptor; import org.wso2.msf4j.interceptor.LowPriorityMethodRequestInterceptor; import org.wso2.msf4j.interceptor.LowPriorityMethodResponseInterceptor; import org.wso2.msf4j.interceptor.MediumPriorityClassRequestInterceptor; import org.wso2.msf4j.interceptor.MediumPriorityClassResponseInterceptor; import org.wso2.msf4j.interceptor.MediumPriorityMethodRequestInterceptor; import org.wso2.msf4j.interceptor.MediumPriorityMethodResponseInterceptor; import org.wso2.msf4j.interceptor.PriorityDataHolder; import org.wso2.msf4j.interceptor.annotation.RequestInterceptor; import org.wso2.msf4j.interceptor.annotation.ResponseInterceptor; import javax.ws.rs.GET; import javax.ws.rs.Path; /** * Micro Service to test priority of interceptors. */ @Path("/test/priorityInterceptorTest") @RequestInterceptor({ HighPriorityClassRequestInterceptor.class, MediumPriorityClassRequestInterceptor.class, LowPriorityClassRequestInterceptor.class }) @ResponseInterceptor({ HighPriorityClassResponseInterceptor.class, MediumPriorityClassResponseInterceptor.class, LowPriorityClassResponseInterceptor.class }) public class PriorityInterceptorTestMicroService implements Microservice { @GET @Path("/priorityTest") @RequestInterceptor({ HighPriorityMethodRequestInterceptor.class, MediumPriorityMethodRequestInterceptor.class, LowPriorityMethodRequestInterceptor.class }) @ResponseInterceptor({ HighPriorityMethodResponseInterceptor.class, MediumPriorityMethodResponseInterceptor.class, LowPriorityMethodResponseInterceptor.class }) public String priorityInterceptorTest() { PriorityDataHolder.setPriorityOrder(PriorityDataHolder.getPriorityOrder() + "[HTTP METHOD]"); return "Priority interceptor test"; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/service/SecondService.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.service; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; /** * Simple RESTful web service. */ @Path("/SecondService") public class SecondService { @GET @Path("/addNumbers/{no1}/{no2}") public int add(@PathParam("no1") int no1, @PathParam("no2") int no2) { return no1 + no2; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/service/TestMicroServiceWithDynamicPath.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.service; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; public class TestMicroServiceWithDynamicPath { @GET @Path("/hello/{name}") public String sayHello(@PathParam("name") String name) { return "Hello " + name; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/service/TestMicroservice.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.service; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; import com.google.gson.reflect.TypeToken; import org.apache.commons.io.IOUtils; import org.wso2.msf4j.HttpStreamHandler; import org.wso2.msf4j.HttpStreamer; import org.wso2.msf4j.Microservice; import org.wso2.msf4j.Request; import org.wso2.msf4j.exception.MappedException; import org.wso2.msf4j.exception.MappedException2; import org.wso2.msf4j.formparam.FileInfo; import org.wso2.msf4j.formparam.FormDataParam; import org.wso2.msf4j.formparam.FormItem; import org.wso2.msf4j.formparam.FormParamIterator; import org.wso2.msf4j.formparam.exception.FormUploadException; import org.wso2.msf4j.pojo.Company; import org.wso2.msf4j.pojo.Person; import org.wso2.msf4j.pojo.Pet; import org.wso2.msf4j.pojo.TextBean; import org.wso2.msf4j.pojo.XmlBean; import org.wso2.msf4j.service.sub.Team; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URI; import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import java.util.Set; import java.util.SortedSet; import java.util.TimeZone; import java.util.concurrent.TimeUnit; import javax.ws.rs.Consumes; import javax.ws.rs.CookieParam; import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.HeaderParam; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.NewCookie; import javax.ws.rs.core.Response; import static org.testng.AssertJUnit.fail; /** * Test service. */ @SuppressWarnings("UnusedParameters") @Path("/test/v1") public class TestMicroservice implements Microservice { private static final String SAMPLE_STRING = "foo"; private static final Gson GSON = new Gson(); @GET public String noMethodPathGet() { return "no-@Path-GET"; } @POST public String noMethodPathPost() { return "no-@Path-POST"; } @PUT public String noMethodPathPut() { return "no-@Path-PUT"; } @DELETE public String noMethodPathDelete() { return "no-@Path-DELETE"; } @Path("jsonConsumeStringProduce") @POST @Consumes("text/json") @Produces("text/plain") public String jsonConsume01(Pet input) { return input.getDetails(); } @Path("textConsumeJsonProduce") @POST @Produces("text/json") @Consumes("text/plain") public TextBean textConsume01(String input) { TextBean textBean = new TextBean(); textBean.setText(input); return textBean; } @Path("textConsumeTextProduce") @POST @Consumes("text/plain") @Produces("text/plain") public String textConsume02(String input) { return input + "-processed"; } @Path("textConsumeTextProduceXml") @POST @Consumes("text/xml") @Produces("text/xml") public XmlBean textConsume03(XmlBean input) { return input; } @Path("sleep/{seconds}") @GET public Response testSleep(@PathParam("seconds") int seconds) { try { TimeUnit.SECONDS.sleep(seconds); return Response.status(Response.Status.OK).entity("slept: " + seconds + "s").build(); } catch (InterruptedException e) { return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); } } @Path("response/typehtml") @GET public Response produceHtmlContent0() { return Response.ok() .type(MediaType.TEXT_HTML_TYPE) .entity("Hello") .build(); } @Path("response/typehtml/str") @GET public Response produceHtmlContent1() { return Response.ok() .type(MediaType.TEXT_HTML) .entity("Hello") .build(); } @Path("resource") @GET public Response testGet() { JsonObject object = new JsonObject(); object.addProperty("status", "Handled get in resource end-point"); return Response.status(Response.Status.OK).entity(object).build(); } @Path("tweets/{id}") @GET public Response testGetTweet(@PathParam("id") String id) { JsonObject object = new JsonObject(); object.addProperty("status", String.format("Handled get in tweets end-point, id: %s", id)); return Response.status(Response.Status.OK).entity(object).build(); } @Path("tweets/{id}") @PUT public Response testPutTweet(@PathParam("id") String id) { JsonObject object = new JsonObject(); object.addProperty("status", String.format("Handled put in tweets end-point, id: %s", id)); return Response.status(Response.Status.OK).entity(object).build(); } @Path("facebook/{id}/message") @DELETE public void testNoMethodRoute(@PathParam("id") String id) { } @Path("facebook/{id}/message") @PUT public Response testPutMessage(@PathParam("id") String id, @Context Request request) { String message = String.format("Handled put in tweets end-point, id: %s. ", id); try { String data = getStringContent(request); message = message.concat(String.format("Content: %s", data)); } catch (IOException e) { //This condition should never occur fail(); } JsonObject object = new JsonObject(); object.addProperty("result", message); return Response.status(Response.Status.OK).entity(object).build(); } @Path("facebook/{id}/message") @POST public Response testPostMessage(@PathParam("id") String id, @Context Request request) { String message = String.format("Handled post in tweets end-point, id: %s. ", id); try { String data = getStringContent(request); message = message.concat(String.format("Content: %s", data)); } catch (IOException e) { //This condition should never occur fail(); } JsonObject object = new JsonObject(); object.addProperty("result", message); return Response.status(Response.Status.OK).entity(object).build(); } @Path("/user/{userId}/message/{messageId}") @GET public Response testMultipleParametersInPath(@PathParam("userId") String userId, @PathParam("messageId") int messageId) { JsonObject object = new JsonObject(); object.addProperty("result", String.format("Handled multiple path parameters %s %d", userId, messageId)); return Response.status(Response.Status.OK).entity(object).build(); } @Path("/message/{messageId}/user/{userId}") @GET public Response testMultipleParametersInDifferentParameterDeclarationOrder(@PathParam("userId") String userId, @PathParam("messageId") int messageId) { JsonObject object = new JsonObject(); object.addProperty("result", String.format("Handled multiple path parameters %s %d", userId, messageId)); return Response.status(Response.Status.OK).entity(object).build(); } @Path("/NotRoutable/{id}") @GET public Response notRoutableParameterMismatch(@PathParam("userid") String userId) { JsonObject object = new JsonObject(); object.addProperty("result", String.format("Handled Not routable path %s ", userId)); return Response.status(Response.Status.OK).entity(object).build(); } @Path("/exception") @GET public void exception() { throw new IllegalArgumentException("Illegal argument"); } private String getStringContent(Request request) throws IOException { return getStringFromInputStream(request.getMessageContentStream()); } /** * Convert input stream to String. * * @param in Input stream to be converted to string * @return Converted string */ private static String getStringFromInputStream(InputStream in) { BufferedInputStream bis = new BufferedInputStream(in); ByteArrayOutputStream bos = new ByteArrayOutputStream(); String result; try { int data; while ((data = bis.read()) != -1) { bos.write(data); } result = bos.toString(); } catch (IOException ioe) { return ""; } finally { try { bos.close(); } catch (IOException ignored) { } } return result; } @Path("/multi-match/**") @GET public String multiMatchAll() { return "multi-match-*"; } @Path("/multi-match/{param}") @GET public String multiMatchParam(@PathParam("param") String param) { return "multi-match-param-" + param; } @Path("/multi-match/foo") @GET public String multiMatchFoo() { return "multi-match-get-actual-foo"; } @Path("/multi-match/foo") @PUT public String multiMatchParamPut() { return "multi-match-put-actual-foo"; } @Path("/multi-match/{param}/bar") @GET public String multiMatchParamBar(@PathParam("param") String param) { return "multi-match-param-bar-" + param; } @Path("/multi-match/foo/{param}") @GET public String multiMatchFooParam(@PathParam("param") String param) { return "multi-match-get-foo-param-" + param; } @Path("/multi-match/foo/{param}/bar") @GET public String multiMatchFooParamBar(@PathParam("param") String param) { return "multi-match-foo-param-bar-" + param; } @Path("/multi-match/foo/bar/{param}") @GET public String multiMatchFooBarParam(@PathParam("param") String param) { return "multi-match-foo-bar-param-" + param; } @Path("/multi-match/foo/{param}/bar/baz") @GET public String multiMatchFooParamBarBaz(@PathParam("param") String param) { return "multi-match-foo-param-bar-baz-" + param; } @Path("/multi-match/foo/bar/{param}/{id}") @GET public String multiMatchFooBarParamId(@PathParam("param") String param, @PathParam("id") String id) { return "multi-match-foo-bar-param-" + param + "-id-" + id; } @Path("/fileserver/{fileType}") @GET public Response serveFile(@PathParam("fileType") String fileType) throws Exception { File file; if ("png".equals(fileType)) { file = new File(Thread.currentThread().getContextClassLoader().getResource("testPngFile.png").toURI()); return Response.ok(file).build(); } else if ("jpg".equals(fileType)) { file = new File(Thread.currentThread().getContextClassLoader().getResource("testJpgFile.jpg").toURI()); return Response.ok(file).header("X-Custom-Header", "wso2").build(); } else if ("txt".equals(fileType)) { file = new File(Thread.currentThread().getContextClassLoader().getResource("testTxtFile.txt").toURI()); return Response.ok(file).build(); } return Response.noContent().build(); } @Path("/fileserver/ip/{fileType}") @GET public Response serveInputStream(@PathParam("fileType") String fileType) throws Exception { if ("png".equals(fileType)) { InputStream ipStream = new FileInputStream( new File(Thread.currentThread().getContextClassLoader().getResource("testPngFile.png").toURI())); return Response.ok(ipStream).type("image/png").build(); } else if ("jpg".equals(fileType)) { InputStream ipStream = new FileInputStream( new File(Thread.currentThread().getContextClassLoader().getResource("testJpgFile.jpg").toURI())); return Response.ok(ipStream).type("image/jpeg").header("X-Custom-Header", "wso2").build(); } else if ("txt".equals(fileType)) { InputStream ipStream = new FileInputStream( new File(Thread.currentThread().getContextClassLoader().getResource("testTxtFile.txt").toURI())); return Response.ok(ipStream).type("text/plain").build(); } return Response.noContent().build(); } @Path("/stream/upload") @PUT public void streamUpload(@Context HttpStreamer httpStreamer) throws Exception { final StringBuffer sb = new StringBuffer(); httpStreamer.callback(new HttpStreamHandler() { private org.wso2.msf4j.Response response; @Override public void init(org.wso2.msf4j.Response response) { this.response = response; } @Override public void chunk(ByteBuffer content) throws Exception { sb.append(Charset.defaultCharset().decode(content).toString()); } @Override public void end() throws Exception { response.setStatus(Response.Status.OK.getStatusCode()); response.setEntity(sb.toString()); response.send(); } @Override public void error(Throwable cause) { sb.delete(0, sb.length()); } }); } @Path("/stream/upload/fail") @PUT public HttpStreamHandler streamUploadFailure() { final int fileSize = 30 * 1024 * 1024; return new HttpStreamHandler() { private org.wso2.msf4j.Response response; ByteBuffer offHeapBuffer = ByteBuffer.allocateDirect(fileSize); @Override public void init(org.wso2.msf4j.Response response) { this.response = response; } @Override public void chunk(ByteBuffer content) throws Exception { offHeapBuffer.put(content.array()); } @Override public void end() throws Exception { int bytesUploaded = offHeapBuffer.position(); response.setStatus(Response.Status.OK.getStatusCode()); response.setEntity("Uploaded:" + bytesUploaded); response.send(); } @Override public void error(Throwable cause) { offHeapBuffer = null; } }; } @Path("/aggregate/upload") @PUT public String aggregatedUpload(@Context Request request) { ByteBuffer content = ByteBuffer.wrap(getStringFromInputStream(request.getMessageContentStream()). getBytes(Charset.defaultCharset())); int bytesUploaded = content.capacity(); return "Uploaded:" + bytesUploaded; } @Path("/gzipfile") @GET public Response gzipFile() throws IOException, URISyntaxException { File file = new File(Thread.currentThread().getContextClassLoader().getResource("testJpgFile.jpg").toURI()); return Response.ok().entity(file).build(); } @Path("/uexception") @GET public void testException() { throw new RuntimeException("User Exception"); } @Path("/noresponse") @GET public void testNoResponse() { } @Path("/stringQueryParam/{path}") @GET public String testStringQueryParam(@PathParam("path") String path, @QueryParam("name") String name) { return path + ":" + name; } @Path("/primitiveQueryParam") @GET public String testPrimitiveQueryParam(@QueryParam("age") int age) { return Integer.toString(age); } @Path("/sortedSetQueryParam") @GET public String testSortedSetQueryParam(@QueryParam("id") SortedSet ids) { StringBuilder response = new StringBuilder(); ids.forEach(id -> response.append(id).append(",")); if (response.length() > 0) { response.setLength(response.length() - 1); } return response.toString(); } @Path("/listHeaderParam") @GET public String testListHeaderParam(@HeaderParam("name") List names) { StringBuilder response = new StringBuilder(); names.forEach(name -> response.append(name).append(",")); response.setLength(response.length() - 1); return response.toString(); } @Path("/headerResponse") @GET public Response testHeaderResponse(@HeaderParam("name") String name) { return Response.status(Response.Status.OK.getStatusCode()).entity("Entity").header("name", name).build(); } @Path("/defaultValue") @GET public Object testDefaultValue(@DefaultValue("30") @QueryParam("age") Integer age, @DefaultValue("hello") @QueryParam("name") String name, @DefaultValue("casking") @HeaderParam("hobby") List hobbies) { JsonObject response = new JsonObject(); response.addProperty("age", age); response.addProperty("name", name); response.add("hobby", GSON.toJsonTree(hobbies, new TypeToken>() { }.getType())); return response; } @Path("/connectionClose") @GET public Response testConnectionClose() { return Response.status(Response.Status.OK).entity("Close connection").header("Connection", "close").build(); } @Path("/uploadReject") @POST public Response testUploadReject() { return Response.status(Response.Status.BAD_REQUEST).entity("Rejected").header("Connection", "close").build(); } @Path("/customException") @POST public void testCustomException() throws CustomException { throw new CustomException(); } @Path("/mappedException") @GET public void testExceptionMapping() throws MappedException { throw new MappedException("Mapped exception thrown"); } @Path("/mappedException2") @GET public void testExceptionMapping2() throws MappedException2 { throw new MappedException2("Mapped exception 2 thrown"); } @Path("/formParam") @POST @Consumes({MediaType.APPLICATION_FORM_URLENCODED, MediaType.MULTIPART_FORM_DATA}) public Response tesFormParamWithURLEncoded(@FormParam("name") String name, @FormParam("age") int age) { return Response.ok().entity(name + ":" + age).build(); } @Path("/formDataParam") @POST @Consumes({MediaType.APPLICATION_FORM_URLENCODED, MediaType.MULTIPART_FORM_DATA}) public Response tesFormDataParam(@FormDataParam("name") String name, @FormDataParam("age") int age) { return Response.ok().entity(name + ":" + age).build(); } @Path("/formParamWithList") @POST @Consumes({MediaType.APPLICATION_FORM_URLENCODED, MediaType.MULTIPART_FORM_DATA}) public Response tesFormParamList(@FormParam("names") List names) { return Response.ok().entity(names.size()).build(); } @Path("/formParamWithSet") @POST @Consumes({MediaType.APPLICATION_FORM_URLENCODED, MediaType.MULTIPART_FORM_DATA}) public Response tesFormParamSet(@FormParam("names") Set names) { return Response.ok().entity(names.size()).build(); } @POST @Path("/testFormParamWithFile") public Response testFormParamWithFile(@Context FormParamIterator formParamIterator) { String response = ""; try { while (formParamIterator.hasNext()) { FormItem item = formParamIterator.next(); response = item.getName(); } } catch (FormUploadException e) { response = e.getMessage(); } return Response.ok().entity(response).build(); } @POST @Path("/complexForm") @Consumes(MediaType.MULTIPART_FORM_DATA) public Response complexForm(@FormDataParam("file") File file, @FormDataParam("id") int id, @FormDataParam("people") List personList, @FormDataParam("company") Company company) { return Response.ok().entity(file.getName() + ":" + id + ":" + personList.size() + ":" + company.getType()) .build(); } @POST @Path("/multipleFiles") @Consumes(MediaType.MULTIPART_FORM_DATA) public Response multipleFiles(@FormDataParam("files") List files) { return Response.ok().entity(files.size()).build(); } @POST @Path("/streamFile") @Consumes(MediaType.MULTIPART_FORM_DATA) public Response multipleFiles(@FormDataParam("file") FileInfo fileInfo, @FormDataParam("file") InputStream inputStream) { StringBuilder stringBuilder = new StringBuilder(); try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) { while (bufferedReader.ready()) { stringBuilder.append(bufferedReader.readLine()); } } catch (IOException e) { return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); } finally { IOUtils.closeQuietly(inputStream); } return Response.ok().entity(stringBuilder.toString() + "-" + fileInfo.getFileName()).build(); } @POST @Path("/getAllFormItemsURLEncoded") @Consumes(MediaType.APPLICATION_FORM_URLENCODED) public Response getAllFormItemsURLEncoded(@Context MultivaluedMap formItemMultivaluedMap) { int noOfCompanies = ((ArrayList) formItemMultivaluedMap.get("names")).size(); String type = formItemMultivaluedMap.getFirst("type").toString(); String response = "No of Companies-" + noOfCompanies + " type-" + type; return Response.ok().entity(response).build(); } @POST @Path("/getAllFormItemsMultipart") @Consumes(MediaType.MULTIPART_FORM_DATA) public Response getAllFormItemsMultipart(@Context MultivaluedMap formItemMultivaluedMap) { ArrayList files = (ArrayList) formItemMultivaluedMap.get("file"); String person = formItemMultivaluedMap.getFirst("people").toString(); JsonParser parser = new JsonParser(); String name = parser.parse(person).getAsJsonArray().get(0).getAsJsonObject().get("name").getAsString(); String response = "FileCount-" + files.size() + " SecondFileName-" + ((File) files.get(1)).getName() + " FirstPerson-" + name; return Response.ok().entity(response).build(); } @POST @Path("/getAllFormItemsXFormUrlEncoded") @Consumes(MediaType.APPLICATION_FORM_URLENCODED) public Response getAllFormItemsXFormUrlEncoded(@Context MultivaluedMap formItemMultivaluedMap) { ArrayList names = (ArrayList) formItemMultivaluedMap.get("names"); String type = formItemMultivaluedMap.getFirst("type").toString(); String response = "Type = " + type + " No of names = " + names.size() + " First name = " + names.get(1); return Response.ok().entity(response).build(); } @GET @Path("{assetType : [a-zA-Z][a-zA-Z_0-9]*}/{id}/states") public Response testPathParamWithRegexOne(@PathParam("assetType") String assetType, @PathParam("id") String id) { StringBuilder sb = new StringBuilder(); sb.append("Asset Type = ").append(assetType).append(", Asset Id = ").append(id); return Response.ok().entity(sb.toString()).build(); } @GET @Path("/endpoints/{assetType : [a-zA-Z][a-zA-Z_0-9]*}/{id}") public Response testPathParamWithRegexTwo(@PathParam("assetType") String assetType, @PathParam("id") String id) { StringBuilder sb = new StringBuilder(); sb.append("Asset Type = ").append(assetType).append(", Asset Id = ").append(id); return Response.ok().entity(sb.toString()).build(); } private static int initialValue = 0; @GET @Path("/testDualInvocation1") public Response testDualInvocation1() { initialValue++; return Response.ok().entity(initialValue).build(); } @GET @Path("/testDualInvocation2") public Response testDualInvocation2() { int returnVal = initialValue + 1; initialValue = 0; return Response.ok().entity(returnVal).build(); } @GET @Path("/testJsonProduceWithString") @Produces(MediaType.APPLICATION_JSON) public Response testJsonProduceWithString() { String res = "{\"abc\":[{\"name\":\"Richard Stallman\",\"age\":63}, {\"name\":\"Linus Torvalds\",\"age\":46}]}"; return Response.ok().entity(res).build(); } @GET @Path("/testJsonProduceWithJsonArray") @Produces(MediaType.APPLICATION_JSON) public Response testJsonProduceWithJsonArray() { JsonArray jsonArray = new JsonArray(); jsonArray.add(new JsonPrimitive("12")); jsonArray.add(new JsonPrimitive("15")); jsonArray.add(new JsonPrimitive("15")); return Response.ok().entity(jsonArray).build(); } @GET @Path("/testJsonProduceWithJsonObject") @Produces(MediaType.APPLICATION_JSON) public Response testJsonProduceWithJJsonObject() { JsonObject jsonObject = new JsonObject(); jsonObject.add("name", new JsonPrimitive("WSO2")); JsonArray jsonArray = new JsonArray(); jsonArray.add(new JsonPrimitive("APIM")); jsonArray.add(new JsonPrimitive("IS")); jsonArray.add(new JsonPrimitive("MSF4J")); jsonObject.add("products", jsonArray); return Response.ok().entity(jsonObject).build(); } /** * Operation with no content in the response and sets a value in the session. */ @GET @Path("/set-session/{value}") public void setObjectInSession(@Context Request request, @PathParam("value") String value) { request.getSession().setAttribute(SAMPLE_STRING, value); } /** * Operation which returns content in the response and sets a value in the session. */ @GET @Path("/set-session2/{value}") public String setObjectInSession2(@Context Request request, @PathParam("value") String value) { request.getSession().setAttribute(SAMPLE_STRING, value); return value; } /** * Operation which retrieves value set in the session in the {@link #setObjectInSession} & * {@link #setObjectInSession2} methods. */ @GET @Path("/get-session") public String getObjectFromSession(@Context Request request) { return (String) request.getSession().getAttribute(SAMPLE_STRING); } @GET @Path("/expire-session") public void expireSession(@Context Request request) { request.getSession().invalidate(); } @GET @Path("/cookie") public Response echoCookieValue(@CookieParam("name") String name) { NewCookie newCookie; if ("wso2".equalsIgnoreCase(name)) { TimeZone.setDefault(TimeZone.getTimeZone("IST")); Calendar instance = Calendar.getInstance(); instance.set(2017, 0, 1, 0, 0, 0); newCookie = new NewCookie("test-cookie", name, "/cookie", "wso2.com", 1, "Cookie Test", 10, instance.getTime(), true, true); } else { newCookie = new NewCookie("test-cookie", name); } return Response.ok().entity(name).cookie(newCookie).build(); } @Produces(MediaType.APPLICATION_JSON) @Path("/{countryId}/team") public Team getCountryTeam(@PathParam("countryId") String countryId) { return new Team(countryId); } @GET @Path("/locationRealtiveUriTest") public Response locationRealtiveUriTest() { return Response.status(Response.Status.CREATED).location(URI.create("/entity/1")).build(); } @GET @Path("/locationAbsoluteUriTest") public Response locationAbsoluteUriTest() { return Response.status(Response.Status.CREATED).location(URI.create("http://localhost:8080/products/entity/2")) .build(); } /** * Custom exception class for testing exception handler. */ public static final class CustomException extends Exception { public static final int HTTP_RESPONSE_STATUS = Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/service/sub/Player.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.service.sub; import javax.ws.rs.FormParam; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; /** * Simple RESTful web service for Player. */ @Path("/") public class Player { private String name; private int playerId; private String countryId; private String countryName; private byte age; private String type; public Player(String countryId, int playerId) { this.countryId = countryId; this.playerId = playerId; age = 30; name = "player_1"; } public Player() { name = "Sanath Jayasuriya"; age = 27; } @POST @Path("/details/{filed}") public String getPlayerProfileFiled(@PathParam("countryId") String countryId, @PathParam("playerId") int playerId, @PathParam("filed") String field, @FormParam("type") String type, @FormParam("countryName") String countryName) { return countryId + "_" + playerId + "_" + field + "_" + type + "_" + countryName; } @POST @Path("") public Player getPlayerProfile(@PathParam("countryId") String countryId, @PathParam("playerId") int playerId, @FormParam("type") String type, @FormParam("countryName") String countryName) { Player player = new Player(countryId, playerId); player.setType(type); player.setCountryName(countryName); return player; } public void setType(String type) { this.type = type; } public void setAge(byte age) { this.age = age; } public void setCountryId(String countryId) { this.countryId = countryId; } public void setPlayerId(int playerId) { this.playerId = playerId; } public void setName(String name) { this.name = name; } public void setCountryName(String countryName) { this.countryName = countryName; } public String getName() { return name; } public int getPlayerId() { return playerId; } public String getCountryId() { return countryId; } public String getCountryName() { return countryName; } public byte getAge() { return age; } public String getType() { return type; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/service/sub/Team.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.service.sub; import org.wso2.msf4j.interceptor.PriorityDataHolder; import org.wso2.msf4j.interceptor.TestRequestInterceptor; import org.wso2.msf4j.interceptor.TestResponseInterceptor; import org.wso2.msf4j.interceptor.annotation.RequestInterceptor; import org.wso2.msf4j.interceptor.annotation.ResponseInterceptor; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; /** * Simple RESTful web service for Team. */ @Path("/") public class Team { private String teamType; private String countryName; public String getCountryId() { return countryId; } public String getCountryName() { return countryName; } public void setCountryName(String countryName) { this.countryName = countryName; } public String getTeamType() { return teamType; } public void setTeamType(String teamType) { this.teamType = teamType; } private String countryId; public void setCountryId(String countryId) { this.countryId = countryId; } public Team(String countryId) { this.countryId = countryId; teamType = "Cricket"; } public Team(String countryId, String countryName) { this.countryName = countryName; this.countryId = countryId; teamType = "Cricket"; } @Path("/{playerId}") public Player getPlayerObj(@PathParam("countryId") String countryId, @PathParam("playerId") int playerId) { return new Player(countryId, playerId); } @Path("/interceptorTest/{playerId}") @RequestInterceptor(TestRequestInterceptor.class) @ResponseInterceptor(TestResponseInterceptor.class) public Player getPlayer(@PathParam("playerId") int playerId) { PriorityDataHolder.setPriorityOrder(PriorityDataHolder.getPriorityOrder() + "[HTTP SUB RESOURCE METHOD]"); return new Player(countryId, playerId); } @GET @Path("") public Team getCountryTeam(@PathParam("countryId") String countryId) { return new Team(countryId); } @POST @Path("") public Team getCountryTeamFromPost(@PathParam("countryId") String countryId, @FormParam("countryName") String countryName) { return new Team(countryId, countryName); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/session/SessionIdGeneratorTest.java ================================================ /* * Copyright (c) 2016 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ package org.wso2.msf4j.session; import org.testng.annotations.Test; import org.wso2.msf4j.internal.session.SessionIdGenerator; import java.util.ArrayList; import java.util.List; import static org.testng.Assert.fail; /** * Tests SessionIdGenerator. */ public class SessionIdGeneratorTest { @Test public void test() { List sessions = new ArrayList<>(); for (int i = 0; i < 10000; i++) { String id = new SessionIdGenerator().generateSessionId("foo:"); if (sessions.contains(id)) { fail("Duplicate session found " + id); } sessions.add(id); } } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/util/QueryStringDecoderUtilTest.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.util; import org.testng.annotations.Test; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.Charset; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertTrue; /** * Tests for QueryStringDecoderUtil. */ public class QueryStringDecoderUtilTest { @Test public void testBasicUris() throws URISyntaxException { QueryStringDecoderUtil d = new QueryStringDecoderUtil(new URI("http://localhost/path")); assertEquals(0, d.parameters().size()); } @Test public void testBasic() throws Exception { QueryStringDecoderUtil d; d = new QueryStringDecoderUtil("/foo?a=b=c"); assertEquals("/foo", d.path()); assertEquals(1, d.parameters().size()); assertEquals(1, d.parameters().get("a").size()); assertEquals("b=c", d.parameters().get("a").get(0)); d = new QueryStringDecoderUtil("/foo?a=1&a=2"); assertEquals("/foo", d.path()); assertEquals(1, d.parameters().size()); assertEquals(2, d.parameters().get("a").size()); assertEquals("1", d.parameters().get("a").get(0)); assertEquals("2", d.parameters().get("a").get(1)); d = new QueryStringDecoderUtil("/foo?a=&a=2"); assertEquals("/foo", d.path()); assertEquals(1, d.parameters().size()); assertEquals(2, d.parameters().get("a").size()); assertEquals("", d.parameters().get("a").get(0)); assertEquals("2", d.parameters().get("a").get(1)); d = new QueryStringDecoderUtil("/foo?a=1&a="); assertEquals("/foo", d.path()); assertEquals(1, d.parameters().size()); assertEquals(2, d.parameters().get("a").size()); assertEquals("1", d.parameters().get("a").get(0)); assertEquals("", d.parameters().get("a").get(1)); d = new QueryStringDecoderUtil("/foo?a=1&a=&a="); assertEquals(d.path(), "/foo"); assertEquals(d.parameters().size(), 1); assertEquals(3, d.parameters().get("a").size()); assertEquals("1", d.parameters().get("a").get(0)); assertEquals("", d.parameters().get("a").get(1)); assertEquals("", d.parameters().get("a").get(2)); d = new QueryStringDecoderUtil("/foo?a=1=&a==2"); assertEquals("/foo", d.path()); assertEquals(1, d.parameters().size()); assertEquals(2, d.parameters().get("a").size()); assertEquals("1=", d.parameters().get("a").get(0)); assertEquals("=2", d.parameters().get("a").get(1)); } @Test public void testExotic() throws Exception { assertQueryString("", ""); assertQueryString("foo", "foo"); assertQueryString("/foo", "/foo"); assertQueryString("?a=", "?a"); assertQueryString("foo?a=", "foo?a"); assertQueryString("/foo?a=", "/foo?a"); assertQueryString("/foo?a=", "/foo?a&"); assertQueryString("/foo?a=", "/foo?&a"); assertQueryString("/foo?a=", "/foo?&a&"); assertQueryString("/foo?a=", "/foo?&=a"); assertQueryString("/foo?a=", "/foo?=a&"); assertQueryString("/foo?a=", "/foo?a=&"); assertQueryString("/foo?a=b&c=d", "/foo?a=b&&c=d"); assertQueryString("/foo?a=b&c=d", "/foo?a=b&=&c=d"); assertQueryString("/foo?a=b&c=d", "/foo?a=b&==&c=d"); assertQueryString("/foo?a=b&c=&x=y", "/foo?a=b&c&x=y"); assertQueryString("/foo?a=", "/foo?a="); assertQueryString("/foo?a=", "/foo?&a="); assertQueryString("/foo?a=b&c=d", "/foo?a=b&c=d"); assertQueryString("/foo?a=1&a=&a=", "/foo?a=1&a&a="); } @Test public void testHashDos() throws Exception { StringBuilder buf = new StringBuilder(); buf.append('?'); for (int i = 0; i < 65536; i++) { buf.append('k'); buf.append(i); buf.append("=v"); buf.append(i); buf.append('&'); } assertEquals(1024, new QueryStringDecoderUtil(buf.toString()).parameters().size()); } @Test public void testHasPath() throws Exception { QueryStringDecoderUtil decoder = new QueryStringDecoderUtil("1=2", false); assertEquals("", decoder.path()); Map> params = decoder.parameters(); assertEquals(1, params.size()); assertTrue(params.containsKey("1")); List param = params.get("1"); assertNotNull(param); assertEquals(1, param.size()); assertEquals("2", param.get(0)); } @Test public void testUrlDecoding() throws Exception { final String caffe = new String( // "Caffé" but instead of putting the literal E-acute in the // source file, we directly use the UTF-8 encoding so as to // not rely on the platform's default encoding (not portable). new byte[]{'C', 'a', 'f', 'f', (byte) 0xC3, (byte) 0xA9}, "UTF-8"); final String[] tests = { // Encoded -> Decoded or error message substring "", "", "foo", "foo", "f%%b", "f%b", "f+o", "f o", "f++", "f ", "fo%", "unterminated escape sequence", "%42", "B", "%5f", "_", "f%4", "partial escape sequence", "%x2", "invalid escape sequence `%x2' at index 0 of: %x2", "%4x", "invalid escape sequence `%4x' at index 0 of: %4x", "Caff%C3%A9", caffe, }; for (int i = 0; i < tests.length; i += 2) { final String encoded = tests[i]; final String expected = tests[i + 1]; try { final String decoded = QueryStringDecoderUtil.decodeComponent(encoded); assertEquals(expected, decoded); } catch (IllegalArgumentException e) { assertTrue("String " + e.getMessage() + "\" does not contain \"" + expected + '"', e.getMessage().contains(expected)); } } } private static void assertQueryString(String expected, String actual) { QueryStringDecoderUtil ed = new QueryStringDecoderUtil(expected, Charset.defaultCharset()); QueryStringDecoderUtil ad = new QueryStringDecoderUtil(actual, Charset.defaultCharset()); assertEquals(ed.path(), ad.path()); assertEquals(ed.parameters(), ad.parameters()); } // See #189 @Test public void testURI() { URI uri = URI.create("http://localhost:8080/foo?param1=value1¶m2=value2¶m3=value3"); QueryStringDecoderUtil decoder = new QueryStringDecoderUtil(uri); assertEquals("/foo", decoder.path()); Map> params = decoder.parameters(); assertEquals(3, params.size()); Iterator>> entries = params.entrySet().iterator(); Entry> entry = entries.next(); assertEquals("param1", entry.getKey()); assertEquals(1, entry.getValue().size()); assertEquals("value1", entry.getValue().get(0)); entry = entries.next(); assertEquals("param2", entry.getKey()); assertEquals(1, entry.getValue().size()); assertEquals("value2", entry.getValue().get(0)); entry = entries.next(); assertEquals("param3", entry.getKey()); assertEquals(1, entry.getValue().size()); assertEquals("value3", entry.getValue().get(0)); assertFalse(entries.hasNext()); } // See #189 @Test public void testURISlashPath() { URI uri = URI.create("http://localhost:8080/?param1=value1¶m2=value2¶m3=value3"); QueryStringDecoderUtil decoder = new QueryStringDecoderUtil(uri); assertEquals("/", decoder.path()); Map> params = decoder.parameters(); assertEquals(3, params.size()); Iterator>> entries = params.entrySet().iterator(); Entry> entry = entries.next(); assertEquals("param1", entry.getKey()); assertEquals(1, entry.getValue().size()); assertEquals("value1", entry.getValue().get(0)); entry = entries.next(); assertEquals("param2", entry.getKey()); assertEquals(1, entry.getValue().size()); assertEquals("value2", entry.getValue().get(0)); entry = entries.next(); assertEquals("param3", entry.getKey()); assertEquals(1, entry.getValue().size()); assertEquals("value3", entry.getValue().get(0)); assertFalse(entries.hasNext()); } // See #189 @Test public void testURINoPath() { URI uri = URI.create("http://localhost:8080?param1=value1¶m2=value2¶m3=value3"); QueryStringDecoderUtil decoder = new QueryStringDecoderUtil(uri); assertEquals("", decoder.path()); Map> params = decoder.parameters(); assertEquals(3, params.size()); Iterator>> entries = params.entrySet().iterator(); Entry> entry = entries.next(); assertEquals("param1", entry.getKey()); assertEquals(1, entry.getValue().size()); assertEquals("value1", entry.getValue().get(0)); entry = entries.next(); assertEquals("param2", entry.getKey()); assertEquals(1, entry.getValue().size()); assertEquals("value2", entry.getValue().get(0)); entry = entries.next(); assertEquals("param3", entry.getKey()); assertEquals(1, entry.getValue().size()); assertEquals("value3", entry.getValue().get(0)); assertFalse(entries.hasNext()); } // See https://github.com/netty/netty/issues/1833 @Test public void testURI2() { URI uri = URI.create("http://foo.com/images;num=10?query=name;value=123"); QueryStringDecoderUtil decoder = new QueryStringDecoderUtil(uri); assertEquals("/images;num=10", decoder.path()); Map> params = decoder.parameters(); assertEquals(2, params.size()); Iterator>> entries = params.entrySet().iterator(); Entry> entry = entries.next(); assertEquals("query", entry.getKey()); assertEquals(1, entry.getValue().size()); assertEquals("name", entry.getValue().get(0)); entry = entries.next(); assertEquals("value", entry.getKey()); assertEquals(1, entry.getValue().size()); assertEquals("123", entry.getValue().get(0)); assertFalse(entries.hasNext()); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/util/client/websocket/WebSocketClient.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ package org.wso2.msf4j.util.client.websocket; import io.netty.bootstrap.Bootstrap; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.http.DefaultHttpHeaders; import io.netty.handler.codec.http.HttpClientCodec; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; import io.netty.handler.codec.http.websocketx.PingWebSocketFrame; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory; import io.netty.handler.codec.http.websocketx.WebSocketVersion; import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; import javax.net.ssl.SSLException; /** * WebSocket client class for test */ public class WebSocketClient { private static final Logger logger = LoggerFactory.getLogger(WebSocketClient.class); private final String url; private final String subProtocol; private Map customHeaders = new HashMap<>(); private Channel channel = null; private WebSocketClientHandler handler; private EventLoopGroup group; private boolean isHandshakeSuccessful = false; public WebSocketClient(String url) { this.subProtocol = null; this.url = url; } public WebSocketClient(String url, String subProtocol, Map customHeaders) { this.url = url; this.subProtocol = subProtocol; if (customHeaders != null) { this.customHeaders = customHeaders; } } /** * @return true if the handshake is done properly. * @throws URISyntaxException throws if there is an error in the URI syntax. * @throws InterruptedException throws if the connecting the server is interrupted. */ public boolean handhshake() throws InterruptedException, URISyntaxException, SSLException { boolean isDone; URI uri = new URI(url); String scheme = uri.getScheme() == null ? "ws" : uri.getScheme(); final String host = uri.getHost() == null ? "127.0.0.1" : uri.getHost(); final int port; if (uri.getPort() == -1) { if ("ws".equalsIgnoreCase(scheme)) { port = 80; } else if ("wss".equalsIgnoreCase(scheme)) { port = 443; } else { port = -1; } } else { port = uri.getPort(); } if (!"ws".equalsIgnoreCase(scheme) && !"wss".equalsIgnoreCase(scheme)) { logger.error("Only WS(S) is supported."); return false; } final boolean ssl = "wss".equalsIgnoreCase(scheme); final SslContext sslCtx; if (ssl) { sslCtx = SslContextBuilder.forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE).build(); } else { sslCtx = null; } group = new NioEventLoopGroup(); HttpHeaders headers = new DefaultHttpHeaders(); customHeaders.entrySet().forEach( header -> headers.add(header.getKey(), header.getValue()) ); try { // Connect with V13 (RFC 6455 aka HyBi-17). You can change it to V08 or V00. // If you change it to V00, ping is not supported and remember to change // HttpResponseDecoder to WebSocketHttpResponseDecoder in the pipeline. handler = new WebSocketClientHandler( WebSocketClientHandshakerFactory.newHandshaker( uri, WebSocketVersion.V13, subProtocol, true, headers)); Bootstrap b = new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer() { @Override protected void initChannel(SocketChannel ch) { ChannelPipeline p = ch.pipeline(); if (sslCtx != null) { p.addLast(sslCtx.newHandler(ch.alloc(), host, port)); } p.addLast( new HttpClientCodec(), new HttpObjectAggregator(8192), WebSocketClientCompressionHandler.INSTANCE, handler); } }); channel = b.connect(uri.getHost(), port).sync().channel(); isDone = handler.handshakeFuture().sync().isSuccess(); logger.debug("WebSocket Handshake successful : " + isDone); return isDone; } catch (Exception e) { logger.error("Handshake unsuccessful : " + e.getMessage(), e); return false; } } /** * Send text to the server. * @param text text need to be sent. */ public void sendText(String text) throws InterruptedException { if (channel == null) { logger.error("Channel is null. Cannot send text."); throw new IllegalArgumentException("Cannot find the channel to write"); } channel.writeAndFlush(new TextWebSocketFrame(text)).sync(); } /** * Send binary data to server. * @param buf buffer containing the data need to be sent. */ public void sendBinary(ByteBuffer buf) throws IOException, InterruptedException { if (channel == null) { logger.error("Channel is null. Cannot send text."); throw new IllegalArgumentException("Cannot find the channel to write"); } channel.writeAndFlush(new BinaryWebSocketFrame(Unpooled.wrappedBuffer(buf))).sync(); } /** * Send a ping message to the server. * @param buf content of the ping message to be sent. */ public void sendPing(ByteBuffer buf) throws IOException, InterruptedException { if (channel == null) { logger.error("Channel is null. Cannot send text."); throw new IllegalArgumentException("Cannot find the channel to write"); } channel.writeAndFlush(new PingWebSocketFrame(Unpooled.wrappedBuffer(buf))).sync(); } /** * @return the text received from the server. */ public String getTextReceived() { return handler.getTextReceived(); } /** * @return the binary data received from the server. */ public ByteBuffer getBufferReceived() { return handler.getBufferReceived(); } /** * Shutdown the WebSocket Client. */ public void shutDown() throws InterruptedException { handler.shutDown(); group.shutdownGracefully(); } /** * Check whether the handshake is successful of not. * * @return true if the handshake is successful. */ public boolean isHandshakeSuccessful() { return isHandshakeSuccessful; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/util/client/websocket/WebSocketClientHandler.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.util.client.websocket; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; import io.netty.handler.codec.http.websocketx.PongWebSocketFrame; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker; import io.netty.handler.codec.http.websocketx.WebSocketFrame; import io.netty.util.CharsetUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.nio.ByteBuffer; /** * WebSocket Client Handler for Testing. */ public class WebSocketClientHandler extends SimpleChannelInboundHandler { private static final Logger logger = LoggerFactory.getLogger(WebSocketClient.class); private final WebSocketClientHandshaker handshaker; private ChannelPromise handshakeFuture; private ChannelHandlerContext ctx; private String textReceived = ""; private ByteBuffer bufferReceived = null; private boolean isOpen = false; public WebSocketClientHandler(WebSocketClientHandshaker handshaker) { this.handshaker = handshaker; } public ChannelFuture handshakeFuture() { return handshakeFuture; } @Override public void handlerAdded(ChannelHandlerContext ctx) { handshakeFuture = ctx.newPromise(); this.ctx = ctx; } @Override public void channelActive(ChannelHandlerContext ctx) { handshaker.handshake(ctx.channel()); isOpen = true; } @Override public void channelInactive(ChannelHandlerContext ctx) throws InterruptedException { logger.debug("WebSocket Client disconnected!"); ctx.channel().close().sync(); isOpen = false; } @Override public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { Channel ch = ctx.channel(); if (!handshaker.isHandshakeComplete()) { handshaker.finishHandshake(ch, (FullHttpResponse) msg); logger.debug("WebSocket Client connected!"); handshakeFuture.setSuccess(); return; } if (msg instanceof FullHttpResponse) { FullHttpResponse response = (FullHttpResponse) msg; throw new IllegalStateException( "Unexpected FullHttpResponse (getStatus=" + response.status() + ", content=" + response.content().toString(CharsetUtil.UTF_8) + ')'); } WebSocketFrame frame = (WebSocketFrame) msg; if (frame instanceof TextWebSocketFrame) { TextWebSocketFrame textFrame = (TextWebSocketFrame) frame; logger.debug("WebSocket Client received text message: " + textFrame.text()); textReceived = textFrame.text(); } else if (frame instanceof BinaryWebSocketFrame) { BinaryWebSocketFrame binaryFrame = (BinaryWebSocketFrame) frame; bufferReceived = binaryFrame.content().nioBuffer(); logger.debug("WebSocket Client received binary message: " + bufferReceived.toString()); } else if (frame instanceof PongWebSocketFrame) { logger.debug("WebSocket Client received pong"); PongWebSocketFrame pongFrame = (PongWebSocketFrame) frame; bufferReceived = pongFrame.content().nioBuffer(); } else if (frame instanceof CloseWebSocketFrame) { logger.debug("WebSocket Client received closing"); ch.close(); } } /** * @return the text received from the server. */ public String getTextReceived() { return textReceived; } /** * @return the binary data received from the server. */ public ByteBuffer getBufferReceived() { return bufferReceived; } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { if (!handshakeFuture.isDone()) { logger.error("Handshake failed : " + cause.getMessage(), cause); handshakeFuture.setFailure(cause); } ctx.close(); } public void shutDown() throws InterruptedException { ctx.channel().writeAndFlush(new CloseWebSocketFrame()); ctx.channel().closeFuture().sync(); } public boolean isOpen() { return isOpen; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/websocket/DeploymentTest.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.websocket; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import org.wso2.msf4j.MicroservicesRunner; import org.wso2.msf4j.util.client.websocket.WebSocketClient; import org.wso2.msf4j.websocket.endpoint.ChatAppEndpoint; import org.wso2.msf4j.websocket.endpoint.EchoEndpoint; import org.wso2.msf4j.websocket.exception.WebSocketEndpointAnnotationException; import java.io.IOException; import java.net.URISyntaxException; import java.nio.ByteBuffer; import javax.net.ssl.SSLException; /** * Test the WebSocket endpoint deployment. * Check all the methods of the endpoint. */ public class DeploymentTest { private static final Logger log = LoggerFactory.getLogger(DeploymentTest.class); private final String host = "localhost"; private final String port = "9090"; private final int sleepTime = 1000; private String echoUrl = "ws://" + host + ":" + port + "/echo"; private String chatUrl = "ws://" + host + ":" + port + "/chat/"; private MicroservicesRunner microservicesRunner = new MicroservicesRunner(); @BeforeClass public void setup() throws WebSocketEndpointAnnotationException { log.info(System.lineSeparator() + "--------------------------------WebSocket Deployment Test--------------------------------"); microservicesRunner.deployWebSocketEndpoint(new EchoEndpoint()); microservicesRunner.deployWebSocketEndpoint(new ChatAppEndpoint()); microservicesRunner.start(); } @Test(description = "Testing the echoing the message sent by client for text, binary and pong messages.") public void testReply() throws InterruptedException, IOException, URISyntaxException { WebSocketClient echoClient = new WebSocketClient(echoUrl); //Test handshake Assert.assertTrue(echoClient.handhshake()); //Test Echo String String textSent = "test"; echoClient.sendText(textSent); Thread.sleep(sleepTime); String textReceived = echoClient.getTextReceived(); Assert.assertEquals(textReceived, textSent); //Test echo binaryMessage byte[] bytes = {1, 2, 3, 4, 5}; ByteBuffer bufferSent = ByteBuffer.wrap(bytes); echoClient.sendBinary(bufferSent); Thread.sleep(sleepTime); ByteBuffer bufferReceived = echoClient.getBufferReceived(); Assert.assertEquals(bufferReceived, bufferSent); //Test the Pong Message byte[] pingBytes = {6, 7, 8, 9, 10}; ByteBuffer pingBufferSent = ByteBuffer.wrap(pingBytes); echoClient.sendPing(pingBufferSent); Thread.sleep(sleepTime); ByteBuffer pongBufferReceived = echoClient.getBufferReceived(); Assert.assertEquals(pongBufferReceived, pingBufferSent); //Closing the connection echoClient.shutDown(); } @Test(description = "Testing broadcasting messages for text, binary and pong using two clients.") public void testBroadcast() throws InterruptedException, SSLException, URISyntaxException { //Initializing local variables String textReceived; String client1Name = "abc"; String client2Name = "xyz"; WebSocketClient chatClient1 = new WebSocketClient(chatUrl + client1Name); Assert.assertTrue(chatClient1.handhshake()); Thread.sleep(sleepTime); WebSocketClient chatClient2 = new WebSocketClient(chatUrl + client2Name); Assert.assertTrue(chatClient2.handhshake()); Thread.sleep(sleepTime); textReceived = chatClient1.getTextReceived(); Assert.assertEquals(textReceived, client2Name + " connected to chat"); //Check the broadcast text String textSent = "test"; chatClient1.sendText(textSent); Thread.sleep(sleepTime); Assert.assertEquals(chatClient1.getTextReceived(), client1Name + ":" + textSent); Assert.assertEquals(chatClient2.getTextReceived(), client1Name + ":" + textSent); //Check close connection chatClient2.shutDown(); Thread.sleep(sleepTime); Assert.assertEquals(chatClient1.getTextReceived(), client2Name + " left the chat"); chatClient1.shutDown(); } @AfterClass public void cleanUp() { microservicesRunner.stop(); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/websocket/EndpointRegistryTest.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.websocket; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import org.wso2.msf4j.internal.router.PatternPathRouter; import org.wso2.msf4j.internal.websocket.EndpointDispatcher; import org.wso2.msf4j.internal.websocket.EndpointsRegistryImpl; import org.wso2.msf4j.websocket.endpoint.TestEndpoint; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URISyntaxException; import java.util.LinkedList; import java.util.List; import java.util.Optional; /** * Test Class for WebSocket Endpoint Registry */ public class EndpointRegistryTest { private static final Logger log = LoggerFactory.getLogger(EndpointRegistryTest.class); private final String testText = "test"; private TestEndpoint testEndpoint = new TestEndpoint(); private EndpointsRegistryImpl endpointsRegistry = EndpointsRegistryImpl.getInstance(); private final String uri = "/test"; public EndpointRegistryTest() { } @BeforeClass public void onRegister() throws URISyntaxException { log.info(System.lineSeparator() + "--------------------------------WebSocket Registry Test--------------------------------"); } @Test(description = "Testing the adding a correct endpoint to the registry.") public void addEndpoint() throws InvocationTargetException, IllegalAccessException, URISyntaxException { log.info("Testing the adding a correct endpoint to the registry."); endpointsRegistry.addEndpoint(testEndpoint); PatternPathRouter.RoutableDestination routableEndpoint = endpointsRegistry.getRoutableEndpoint(uri); Object webSocketEndpoint = routableEndpoint.getDestination(); List paralist = new LinkedList<>(); paralist.add(testText); paralist.add(null); Optional methodOptional = new EndpointDispatcher().getOnStringMessageMethod(webSocketEndpoint); if (methodOptional.isPresent()) { String returnValue = (String) methodOptional.get().invoke(webSocketEndpoint, paralist.toArray()); Assert.assertEquals(returnValue, testText); } else { Assert.assertTrue(false); } } @Test(description = "Testing the removing of an endpoint from the registry.") public void removeEndpoint() { log.info("Testing the removing of an endpoint from the registry."); endpointsRegistry.removeEndpoint(testEndpoint); PatternPathRouter.RoutableDestination endPoint = endpointsRegistry. getRoutableEndpoint(uri); Assert.assertTrue(endPoint == null); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/websocket/ValidatorTest.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.websocket; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import org.wso2.msf4j.internal.websocket.EndpointValidator; import org.wso2.msf4j.websocket.endpoint.TestEndpointWithAllCorrect; import org.wso2.msf4j.websocket.endpoint.TestEndpointWithMandatoryParameters; import org.wso2.msf4j.websocket.endpoint.error.TestEndpoinWithOnTextError; import org.wso2.msf4j.websocket.endpoint.error.TestEndpointWithMandatoryParametersMissing; import org.wso2.msf4j.websocket.endpoint.error.TestEndpointWithOnBinaryError; import org.wso2.msf4j.websocket.endpoint.error.TestEndpointWithOnCloseError; import org.wso2.msf4j.websocket.endpoint.error.TestEndpointWithOnError; import org.wso2.msf4j.websocket.endpoint.error.TestEndpointWithOnOpenError; import org.wso2.msf4j.websocket.endpoint.error.TestEndpointWithOnPongError; import org.wso2.msf4j.websocket.endpoint.error.TestEndpointWithReturnTypeError; import org.wso2.msf4j.websocket.endpoint.error.TestEndpointWithServerEndpointError; import org.wso2.msf4j.websocket.exception.WebSocketEndpointAnnotationException; import org.wso2.msf4j.websocket.exception.WebSocketEndpointMethodReturnTypeException; import org.wso2.msf4j.websocket.exception.WebSocketMethodParameterException; /** * Test the Exceptions which can be occurred when deploying and running WebSocket. */ public class ValidatorTest { private static final Logger log = LoggerFactory.getLogger(ValidatorTest.class); private EndpointValidator validator = new EndpointValidator(); @BeforeClass public void setup() { log.info(System.lineSeparator() + "--------------------------------WebSocket Validator Test--------------------------------"); } @Test(description = "Test endpoint with all necessary methods and all correct parameters") public void testCorretEndpoint() throws WebSocketMethodParameterException, WebSocketEndpointAnnotationException, WebSocketEndpointMethodReturnTypeException { log.info("Test endpoint with all necessary methods and all correct parameters"); Assert.assertTrue(validator.validate(new TestEndpointWithAllCorrect())); } @Test(description = "Test endpoint with all necessary methods with mandatory parameters") public void testMandatoryParametersEndpoint() throws WebSocketMethodParameterException, WebSocketEndpointAnnotationException, WebSocketEndpointMethodReturnTypeException { log.info("Test endpoint with all necessary methods with mandatory parameters"); Assert.assertTrue(validator.validate(new TestEndpointWithMandatoryParameters())); } @Test(description = "Check the mandatory parameters missing", expectedExceptions = WebSocketMethodParameterException.class) public void testMandatoryParameterMissing() throws WebSocketMethodParameterException, WebSocketEndpointAnnotationException, WebSocketEndpointMethodReturnTypeException { log.info("Check the mandatory parameters missing"); validator.validate(new TestEndpointWithMandatoryParametersMissing()); } @Test(description = "Test the expected exceptions for not defining server endpoint", expectedExceptions = WebSocketEndpointAnnotationException.class) public void testerverEndpoint() throws WebSocketMethodParameterException, WebSocketEndpointAnnotationException, WebSocketEndpointMethodReturnTypeException { log.info("Test the expected exceptions for not defining server endpoint"); validator.validate(new TestEndpointWithServerEndpointError()); } @Test(description = "Test the expected exceptions for onOpen", expectedExceptions = WebSocketMethodParameterException.class) public void testOnOpen() throws WebSocketMethodParameterException, WebSocketEndpointAnnotationException, WebSocketEndpointMethodReturnTypeException { log.info("Test the expected exceptions for onOpen"); validator.validate(new TestEndpointWithOnOpenError()); } @Test(description = "Test the expected exceptions for onClose", expectedExceptions = WebSocketMethodParameterException.class) public void testOnClose() throws WebSocketMethodParameterException, WebSocketEndpointAnnotationException, WebSocketEndpointMethodReturnTypeException { log.info("Test the expected exceptions for onClose"); validator.validate(new TestEndpointWithOnCloseError()); } @Test(description = "Test the expected exceptions for onTextMessage", expectedExceptions = WebSocketMethodParameterException.class) public void testOnTextMessage() throws WebSocketMethodParameterException, WebSocketEndpointAnnotationException, WebSocketEndpointMethodReturnTypeException { log.info("Test the expected exceptions for onTextMessage"); validator.validate(new TestEndpoinWithOnTextError()); } @Test(description = "Test the expected exceptions for onBinaryMessage", expectedExceptions = WebSocketMethodParameterException.class) public void testOnBinaryMessage() throws WebSocketMethodParameterException, WebSocketEndpointAnnotationException, WebSocketEndpointMethodReturnTypeException { log.info("Test the expected exceptions for onBinaryMessage"); validator.validate(new TestEndpointWithOnBinaryError()); } @Test(description = "Test the expected exceptions for onPongMessage", expectedExceptions = WebSocketMethodParameterException.class) public void testOnPongMessage() throws WebSocketMethodParameterException, WebSocketEndpointAnnotationException, WebSocketEndpointMethodReturnTypeException { log.info("Test the expected exceptions for onPongMessage"); validator.validate(new TestEndpointWithOnPongError()); } @Test(description = "Test the expected exceptions for onError", expectedExceptions = WebSocketMethodParameterException.class) public void testOnError() throws WebSocketMethodParameterException, WebSocketEndpointAnnotationException, WebSocketEndpointMethodReturnTypeException { log.info("Test the expected exceptions for onError"); validator.validate(new TestEndpointWithOnError()); } @Test(description = "Test the expected exceptions for invalid return type", expectedExceptions = WebSocketEndpointMethodReturnTypeException.class) public void testReturnTypeError() throws WebSocketMethodParameterException, WebSocketEndpointAnnotationException, WebSocketEndpointMethodReturnTypeException { log.info("Test the expected exceptions for invalid return type"); validator.validate(new TestEndpointWithReturnTypeError()); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/websocket/endpoint/ChatAppEndpoint.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.websocket.endpoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.transport.http.netty.contract.websocket.WebSocketConnection; import java.io.IOException; import java.nio.ByteBuffer; import java.util.LinkedList; import java.util.List; import javax.websocket.CloseReason; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; /** * This is a Sample class for WebSocket. * This provides a chat with multiple users. */ @SuppressWarnings("ALL") @ServerEndpoint(value = "/chat/{name}") public class ChatAppEndpoint { private static final Logger log = LoggerFactory.getLogger(ChatAppEndpoint.class); private List webSocketConnections = new LinkedList<>(); @OnOpen public void onOpen(@PathParam("name") String name, WebSocketConnection webSocketConnection) { String msg = name + " connected to chat"; log.info(msg); sendMessageToAll(msg); webSocketConnections.add(webSocketConnection); } @OnMessage public void onTextMessage(@PathParam("name") String name, String text, WebSocketConnection webSocketConnection) throws IOException { String msg = name + ":" + text; log.info("Received Text: " + text + " from " + name + " " + webSocketConnection.getChannelId()); sendMessageToAll(msg); } @OnMessage public void onBinaryMessage(ByteBuffer buffer, boolean isFinal, WebSocketConnection webSocketConnection) { } @OnClose public void onClose(@PathParam("name") String name, CloseReason closeReason, WebSocketConnection webSocketConnection) { log.info("Connection is closed with status code: " + closeReason.getCloseCode().getCode() + " On reason " + closeReason.getReasonPhrase()); webSocketConnections.remove(webSocketConnection); String msg = name + " left the chat"; sendMessageToAll(msg); } @OnError public void onError(Throwable throwable, WebSocketConnection webSocketConnection) { log.error("Error found in method: " + throwable.toString()); } private void sendMessageToAll(String message) { webSocketConnections.forEach( webSocketConnection -> { log.info("Send message '" + message + "' to id '" + webSocketConnection.getChannelId() + "'."); webSocketConnection.pushText(message); } ); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/websocket/endpoint/EchoEndpoint.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.websocket.endpoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.msf4j.websocket.WebSocketEndpoint; import org.wso2.transport.http.netty.contract.websocket.WebSocketConnection; import java.io.IOException; import javax.websocket.CloseReason; import javax.websocket.OnClose; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.PongMessage; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; /** * This is a Sample class for WebSocket. */ @ServerEndpoint("/echo") public class EchoEndpoint implements WebSocketEndpoint { private static final Logger log = LoggerFactory.getLogger(EchoEndpoint.class); @OnOpen public void onOpen(WebSocketConnection webSocketConnection) { log.info(webSocketConnection.getChannelId() + " connected to repeat-app"); } /** * Echo the same {@link String} to user. */ @OnMessage public String onTextMessage(@PathParam("name") String name, String text, WebSocketConnection webSocketConnection) throws IOException { log.info("Received Text : " + text + " from " + webSocketConnection.getChannelId()); return text; } /** * Echo the same ByteBuffer to user. */ @OnMessage public byte[] onBinaryMessage(byte[] buffer, WebSocketConnection webSocketConnection) { String values = ""; int bufferLength = buffer.length; byte[] bytes = new byte[buffer.length]; for (int i = 0; i < buffer.length; i++) { byte b = buffer[i]; values = values.concat(" " + b); } log.info("Binary message values received from " + webSocketConnection.getChannelId() + System.lineSeparator() + "buffer length: " + bufferLength + System.lineSeparator() + "values : " + values); return buffer; } @OnMessage public PongMessage onPongMessage(PongMessage pongMessage, WebSocketConnection webSocketConnection) { log.info("Received a pong message."); return pongMessage; } @OnClose public void onClose(CloseReason closeReason, WebSocketConnection webSocketConnection) { log.info("Connection is closed with status code: " + closeReason.getCloseCode().getCode() + " On reason " + closeReason.getReasonPhrase()); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/websocket/endpoint/TestEndpoint.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.websocket.endpoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.transport.http.netty.contract.websocket.WebSocketConnection; import javax.websocket.CloseReason; import javax.websocket.OnClose; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.server.ServerEndpoint; @ServerEndpoint("/test") public class TestEndpoint { Logger log = LoggerFactory.getLogger(TestEndpoint.class); @OnOpen public void onOpen(WebSocketConnection webSocketConnection) { log.info(webSocketConnection.getChannelId()); } @OnMessage public String onStringMessage(String str, WebSocketConnection webSocketConnection) { log.info("Test str is : " + str); return str; } @OnClose public void onClose(CloseReason closeReason, WebSocketConnection webSocketConnection) { } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/websocket/endpoint/TestEndpointWithAllCorrect.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.websocket.endpoint; import org.wso2.transport.http.netty.contract.websocket.WebSocketConnection; import java.nio.ByteBuffer; import javax.websocket.CloseReason; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.PongMessage; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; /** * This endpoint include all necessary methods and all the acceptable parameters of those methods according to * JSR-356 specification */ @ServerEndpoint("/allCorrect/{param1}/{param2}") public class TestEndpointWithAllCorrect { @OnOpen public void onOpen(@PathParam("param1") String param1, @PathParam("param2") String param2, WebSocketConnection webSocketConnection) { } @OnMessage public byte[] onString(@PathParam("param1") String param1, @PathParam("param2") String param2, String text, WebSocketConnection webSocketConnection) { return new byte[4]; } @OnMessage public String onBinary(@PathParam("param1") String param1, @PathParam("param2") String param2, ByteBuffer buffer, WebSocketConnection webSocketConnection) { return "test"; } @OnMessage public PongMessage onPong(@PathParam("param1") String param1, @PathParam("param2") String param2, PongMessage pongMessage, WebSocketConnection webSocketConnection) { return pongMessage; } @OnClose public void onClose(@PathParam("param1") String param1, @PathParam("param2") String param2, CloseReason closeReason, WebSocketConnection webSocketConnection) { } @OnError public void onError(@PathParam("param1") String param1, @PathParam("param2") String param2, Throwable throwable, WebSocketConnection webSocketConnection) { } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/websocket/endpoint/TestEndpointWithMandatoryParameters.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.websocket.endpoint; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.PongMessage; import javax.websocket.server.ServerEndpoint; /** *This test class include all the methods of WebSocket with only mandatory parameters according to JSR-356 * specification */ @ServerEndpoint("/test-with-mandatory-params") public class TestEndpointWithMandatoryParameters { @OnOpen public void onOpen() { } @OnMessage public byte[] onString(String text) { return new byte[4]; } @OnMessage public String onBinary(byte[] bytes) { return "test"; } @OnMessage public PongMessage onPong(PongMessage pongMessage) { return pongMessage; } @OnClose public void onClose() { } @OnError public void onError(Throwable throwable) { } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/websocket/endpoint/error/TestEndpoinWithOnTextError.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.websocket.endpoint.error; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.transport.http.netty.contract.websocket.WebSocketConnection; import java.io.IOException; import java.nio.ByteBuffer; import java.util.LinkedList; import java.util.List; import javax.websocket.CloseReason; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; /** * This is a Sample class for WebSocket. * This provides a chat with multiple users. */ @ServerEndpoint(value = "/chat/{name}") public class TestEndpoinWithOnTextError { private static final Logger LOGGER = LoggerFactory.getLogger(TestEndpoinWithOnTextError.class); private List webSocketConnections = new LinkedList<>(); @OnOpen public void onOpen(@PathParam("name") String name, WebSocketConnection webSocketConnection) { webSocketConnections.add(webSocketConnection); String msg = name + " connected to chat"; LOGGER.info(msg); sendMessageToAll(msg); } @OnMessage public void onTextMessage(@PathParam("name") String name, String text, WebSocketConnection webSocketConnection, ByteBuffer buffer) throws IOException { String msg = name + ":" + text + buffer; LOGGER.info("Received Text: " + text + " from " + name + webSocketConnection.getChannelId()); sendMessageToAll(msg); } @OnClose public void onClose(@PathParam("name") String name, CloseReason closeReason, WebSocketConnection webSocketConnection) { LOGGER.info("Connection is closed with status code: " + closeReason.getCloseCode().getCode() + " On reason " + closeReason.getReasonPhrase()); webSocketConnections.remove(webSocketConnection); String msg = name + " left the chat"; sendMessageToAll(msg); } @OnError public void onError(Throwable throwable, WebSocketConnection webSocketConnection) { LOGGER.error("Error found in method: " + throwable.toString()); } private void sendMessageToAll(String message) { webSocketConnections.forEach( webSocketConnection -> webSocketConnection.pushText(message) ); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/websocket/endpoint/error/TestEndpointWithMandatoryParametersMissing.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.websocket.endpoint.error; import javax.websocket.OnError; import javax.websocket.server.ServerEndpoint; /** * Test class with mandatory parameters for a given javax.websocket annotation is missing according to JSR-356 * specification. */ @ServerEndpoint("/test-mandatory-parameters-missing") public class TestEndpointWithMandatoryParametersMissing { @OnError public void onError() { } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/websocket/endpoint/error/TestEndpointWithOnBinaryError.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.websocket.endpoint.error; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.transport.http.netty.contract.websocket.WebSocketConnection; import java.io.IOException; import java.nio.ByteBuffer; import java.util.LinkedList; import java.util.List; import javax.websocket.CloseReason; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; /** * This is a Sample class for WebSocket. * This provides a chat with multiple users. */ @ServerEndpoint(value = "/chat/{name}") public class TestEndpointWithOnBinaryError { private static final Logger LOGGER = LoggerFactory.getLogger(TestEndpointWithOnBinaryError.class); private List webSocketConnections = new LinkedList<>(); @OnOpen public void onOpen(@PathParam("name") String name, WebSocketConnection webSocketConnection) { webSocketConnections.add(webSocketConnection); String msg = name + " connected to chat"; LOGGER.info(msg); sendMessageToAll(msg); } @OnMessage public void onTextMessage(@PathParam("name") String name, String text, WebSocketConnection webSocketConnection) throws IOException { String msg = name + ":" + text; LOGGER.info("Received Text: " + text + " from " + name + webSocketConnection.getChannelId()); sendMessageToAll(msg); } @OnMessage public void onBinaryMessage(@PathParam("name") String name, ByteBuffer buffer, String text, WebSocketConnection webSocketConnection) throws IOException { String msg = name + ":" + text + buffer; LOGGER.info("Received Text: " + text + " from " + name + webSocketConnection.getChannelId()); sendMessageToAll(msg); } @OnClose public void onClose(@PathParam("name") String name, CloseReason closeReason, WebSocketConnection webSocketConnection) { LOGGER.info("Connection is closed with status code: " + closeReason.getCloseCode().getCode() + " On reason " + closeReason.getReasonPhrase()); webSocketConnections.remove(webSocketConnection); String msg = name + " left the chat"; sendMessageToAll(msg); } @OnError public void onError(Throwable throwable, WebSocketConnection webSocketConnection) { LOGGER.error("Error found in method: " + throwable.toString()); } private void sendMessageToAll(String message) { webSocketConnections.forEach( webSocketConnection -> webSocketConnection.pushText(message) ); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/websocket/endpoint/error/TestEndpointWithOnCloseError.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.websocket.endpoint.error; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.transport.http.netty.contract.websocket.WebSocketConnection; import java.io.IOException; import java.util.LinkedList; import java.util.List; import javax.websocket.CloseReason; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; /** * This is a Sample class for WebSocket. * This provides a chat with multiple users. */ @ServerEndpoint(value = "/chat/{name}") public class TestEndpointWithOnCloseError { private static final Logger LOGGER = LoggerFactory.getLogger(TestEndpointWithOnCloseError.class); private List webSocketConnections = new LinkedList<>(); @OnOpen public void onOpen(@PathParam("name") String name, WebSocketConnection webSocketConnection) { webSocketConnections.add(webSocketConnection); String msg = name + " connected to chat"; LOGGER.info(msg); sendMessageToAll(msg); } @OnMessage public void onTextMessage(@PathParam("name") String name, String text, WebSocketConnection webSocketConnection) throws IOException { String msg = name + ":" + text; LOGGER.info("Received Text: " + text + " from " + name + webSocketConnection.getChannelId()); sendMessageToAll(msg); } @OnClose public void onClose(@PathParam("name") String name, CloseReason closeReason, WebSocketConnection webSocketConnection, String errorValue) { webSocketConnections.remove(webSocketConnection); String msg = name + errorValue; sendMessageToAll(msg); } @OnError public void onError(Throwable throwable, WebSocketConnection webSocketConnection) { LOGGER.error("Error found in method: " + throwable.toString()); } private void sendMessageToAll(String message) { webSocketConnections.forEach( webSocketConnection -> webSocketConnection.pushText(message) ); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/websocket/endpoint/error/TestEndpointWithOnError.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.websocket.endpoint.error; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.transport.http.netty.contract.websocket.WebSocketConnection; import java.io.IOException; import java.util.LinkedList; import java.util.List; import javax.websocket.CloseReason; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; /** * This is a Sample class for WebSocket. * This provides a chat with multiple users. */ @ServerEndpoint(value = "/chat/{name}") public class TestEndpointWithOnError { private static final Logger LOGGER = LoggerFactory.getLogger(TestEndpointWithOnError.class); private List webSocketConnections = new LinkedList<>(); @OnOpen public void onOpen(@PathParam("name") String name, WebSocketConnection webSocketConnection) { webSocketConnections.add(webSocketConnection); String msg = name + " connected to chat"; LOGGER.info(msg); sendMessageToAll(msg); } @OnMessage public void onTextMessage(@PathParam("name") String name, String text, WebSocketConnection webSocketConnection) throws IOException { String msg = name + ":" + text; LOGGER.info("Received Text: " + text + " from " + name + webSocketConnection.getChannelId()); sendMessageToAll(msg); } @OnClose public void onClose(@PathParam("name") String name, CloseReason closeReason, WebSocketConnection webSocketConnection) { LOGGER.info("Connection is closed with status code: " + closeReason.getCloseCode().getCode() + " On reason " + closeReason.getReasonPhrase()); webSocketConnections.remove(webSocketConnection); String msg = name + " left the chat"; sendMessageToAll(msg); } @OnError public void onError(Throwable throwable, WebSocketConnection webSocketConnection, String errorValue) { LOGGER.error("Error found in method: " + throwable.toString()); } private void sendMessageToAll(String message) { webSocketConnections.forEach( webSocketConnection -> webSocketConnection.pushText(message) ); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/websocket/endpoint/error/TestEndpointWithOnOpenError.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.websocket.endpoint.error; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.transport.http.netty.contract.websocket.WebSocketConnection; import java.io.IOException; import java.util.LinkedList; import java.util.List; import javax.websocket.CloseReason; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; /** * This is a Sample class for WebSocket. * This provides a chat with multiple users. */ @ServerEndpoint(value = "/test-error") public class TestEndpointWithOnOpenError { private static final Logger LOGGER = LoggerFactory.getLogger(TestEndpointWithOnOpenError.class); private List webSocketConnections = new LinkedList<>(); @OnOpen public void onOpen(@PathParam("name") String name, WebSocketConnection webSocketConnection, String errorValue) { webSocketConnections.add(webSocketConnection); String msg = name + errorValue; LOGGER.info(msg); sendMessageToAll(msg); } @OnMessage public void onTextMessage(@PathParam("name") String name, String text, WebSocketConnection webSocketConnection) throws IOException { String msg = name + ":" + text; LOGGER.info("Received Text: " + text + " from " + name + webSocketConnection.getChannelId()); sendMessageToAll(msg); } @OnClose public void onClose(@PathParam("name") String name, CloseReason closeReason, WebSocketConnection webSocketConnection) { LOGGER.info("Connection is closed with status code: " + closeReason.getCloseCode().getCode() + " On reason " + closeReason.getReasonPhrase()); webSocketConnections.remove(webSocketConnection); String msg = name + " left the chat"; sendMessageToAll(msg); } @OnError public void onError(Throwable throwable, WebSocketConnection webSocketConnection) { LOGGER.error("Error found in method: " + throwable.toString()); } private void sendMessageToAll(String message) { webSocketConnections.forEach( webSocketConnection -> webSocketConnection.pushText(message) ); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/websocket/endpoint/error/TestEndpointWithOnPongError.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.websocket.endpoint.error; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.transport.http.netty.contract.websocket.WebSocketConnection; import java.io.IOException; import java.util.LinkedList; import java.util.List; import javax.websocket.CloseReason; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.PongMessage; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; /** * This is a Sample class for WebSocket. * This provides a chat with multiple users. */ @ServerEndpoint(value = "/chat/{name}") public class TestEndpointWithOnPongError { private static final Logger LOGGER = LoggerFactory.getLogger(TestEndpointWithOnPongError.class); private List webSocketConnections = new LinkedList<>(); @OnOpen public void onOpen(@PathParam("name") String name, WebSocketConnection webSocketConnection) { webSocketConnections.add(webSocketConnection); String msg = name + " connected to chat"; LOGGER.info(msg); sendMessageToAll(msg); } @OnMessage public void onTextMessage(@PathParam("name") String name, String text, WebSocketConnection webSocketConnection) throws IOException { String msg = name + ":" + text; LOGGER.info("Received Text: " + text + " from " + name + webSocketConnection.getChannelId()); sendMessageToAll(msg); } @OnMessage public void onTextMessage(PongMessage pongMessage, WebSocketConnection webSocketConnection, String errorText) throws IOException { } @OnClose public void onClose(@PathParam("name") String name, CloseReason closeReason, WebSocketConnection webSocketConnection) { LOGGER.info("Connection is closed with status code: " + closeReason.getCloseCode().getCode() + " On reason " + closeReason.getReasonPhrase()); webSocketConnections.remove(webSocketConnection); String msg = name + " left the chat"; sendMessageToAll(msg); } @OnError public void onError(Throwable throwable, WebSocketConnection webSocketConnection) { LOGGER.error("Error found in method: " + throwable.toString()); } private void sendMessageToAll(String message) { webSocketConnections.forEach( webSocketConnection -> webSocketConnection.pushText(message) ); } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/websocket/endpoint/error/TestEndpointWithReturnTypeError.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.websocket.endpoint.error; import org.wso2.transport.http.netty.contract.websocket.WebSocketConnection; import javax.websocket.OnMessage; import javax.websocket.server.ServerEndpoint; /** * Test class which include method with wrong return type. */ @ServerEndpoint("/test-with-false-return") public class TestEndpointWithReturnTypeError { @OnMessage public int onText(String text, WebSocketConnection webSocketConnection) { return 0; } } ================================================ FILE: core/src/test/java/org/wso2/msf4j/websocket/endpoint/error/TestEndpointWithServerEndpointError.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.msf4j.websocket.endpoint.error; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.msf4j.websocket.WebSocketEndpoint; import org.wso2.transport.http.netty.contract.websocket.WebSocketConnection; import javax.websocket.OnClose; import javax.websocket.OnMessage; import javax.websocket.OnOpen; public class TestEndpointWithServerEndpointError implements WebSocketEndpoint { Logger log = LoggerFactory.getLogger(TestEndpointWithServerEndpointError.class); @OnOpen public void onOpen(WebSocketConnection webSocketConnection) { log.info(webSocketConnection.getChannelId()); } @OnMessage public String onStringMessage(String str, WebSocketConnection webSocketConnection) { log.info("Test str is: " + str); return str; } @OnClose public void onClose(String reasonText, int statusCode) { } } ================================================ FILE: core/src/test/resources/deployment.yaml ================================================ # Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved # # 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. # MSF4J configuration wso2.msf4j.configuration: # No of worker pool threads to handle MSF4J requests threadCount: 100 threadPoolName: msf4j.executor.workerpool ================================================ FILE: core/src/test/resources/netty-transports-1.yaml ================================================ ################################################################################ # Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. # # 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. ################################################################################ wso2.transport.http: transportProperties: - name: "latency.metrics.enabled" value: true - name: "server.bootstrap.boss.group.size" value: 4 - name: "server.bootstrap.worker.group.size" value: 8 listenerConfigurations: - id: "msf4j-http" host: "127.0.0.1" port: 8080 - id: "jaxrs-http" host: "0.0.0.0" port: 8094 scheme: https sslConfig: keyStore: "src/test/resources/cert.jks" keyStorePass: secret senderConfigurations: - id: "netty-gw" ================================================ FILE: core/src/test/resources/netty-transports-2.yaml ================================================ ################################################################################ # Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. # # 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. ################################################################################ wso2.transport.http: transportProperties: - name: "latency.metrics.enabled" value: true - name: "server.bootstrap.boss.group.size" value: 4 - name: "server.bootstrap.worker.group.size" value: 8 listenerConfigurations: - id: "jaxrs-http" host: "0.0.0.0" port: 8095 scheme: https sslConfig: keyStore: "src/test/resources/cert.jks" keyStorePass: secret senderConfigurations: - id: "netty-gw" ================================================ FILE: core/src/test/resources/netty-transports-3.yaml ================================================ ################################################################################ # Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. # # 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. ################################################################################ wso2.transport.http: listenerConfigurations: - certPass: secret host: "0.0.0.0" id: jaxrs-http sslConfig: keyStore: "src/test/resources/cert.jks" keyStorePass: secret port: 8096 scheme: https senderConfigurations: - id: netty-gw transportProperties: - name: latency.metrics.enabled value: true - name: server.bootstrap.boss.group.size value: 4 - name: server.bootstrap.worker.group.size value: 8 ================================================ FILE: core/src/test/resources/netty-transports-4.yaml ================================================ ################################################################################ # Copyright (c) 2019, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. # # 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. ################################################################################ transports: http: transportProperties: - name: "latency.metrics.enabled" value: true - name: "server.bootstrap.boss.group.size" value: 4 - name: "server.bootstrap.worker.group.size" value: 8 listenerConfigurations: - id: "msf4j-http" host: "127.0.0.1" port: 8080 - id: "jaxrs-http" host: "0.0.0.0" port: 8094 scheme: https sslConfig: keyStore: "src/test/resources/cert.jks" keyStorePass: secret senderConfigurations: - id: "netty-gw" ================================================ FILE: core/src/test/resources/testTxtFile.txt ================================================ 9X54cMfpjnTiMTkIwlO/l8wxEn8GhoGbPxmwpEAfCaRDqqc/x8IhihSpapPLVeXZXjCTHmtzPh2e XkQqxZ9+WbOYJ8fJBMqdsOY6/yL/2a3k1gZxxItKfsztQnVd+kVnjL2Vb5oxZiueogAAO12Op4yt 9yykdeJruOE5JnVUWWnG+DA4z/km883uLrO3y0Pgp9X3FuPJvFENE0uUIZresMBeHR6FbhpSNAbj OH1p6PCgRfEBrHWEp6oVAjvUpESvquxSouebvUok8FnjabrBgTazxCSMnsZqOYOs76h47hEJdjGq dtnLs3FTJ1ibdGqK5Af9U4wJV6uA/O62flu9/Fe6QJnok3gmF86SFRYuL2LpatmfbT1tQt8Q9A2C 7cmZPSv8lEs9UrweHlaWfrTZbfBfpGiPBgv963tY9ov6nxAgdJQIEL2pGhJhysg/cDKYR2CGaeVd +nZ1+ByhHLQgmy/jitLYCXXNKYuoJ9ITd851EjDad1gY/oB7qzlZCEt+CZBf6T5SjWVwAW1JOTNS rLEkmb4UWYPucm9Mjhdv7KaoJGBRH1Y4pgyojab0B9duC3rQziyH58iC1tW9Utw8EhzwVg7PgtEc gEsLUFT4kN9T9H/n1LM2zpoItXoSAKwFOJcuuROF7ufohVEIrjRpRMjDGPwg40Y3a8BUsVtaGnoo ffkNOwCVekm9tFjbA+2hqPvZTgZvBuQx2vnUUkZKxX97aIaBclKiAvGCs4Qo2RZVf5WIVRTcTi3m 8Drfp0Dq7BCOlnk+vyKhMZ/K2C61DaNUpvV/u3rG3Aq9Q1oqPAdqzFmmTspAByLmqXitLwLPDyjd Bf03cKKSusE21i7+HGhe2p7MLMbRL/a6z/frNHLP0lpxTri9dVhxdeSSIi9odMnVsS1JdTY+nseN dKopMsQu9Z+mSx7SjORj8BzFOF+bwAqyECmuJ0v+91kRM9/kVkPMQDdmxVA4l9vDxh8CES+LIKlx ================================================ FILE: core/src/test/resources/testng.xml ================================================ ================================================ FILE: deployer/pom.xml ================================================ org.wso2.msf4j msf4j-parent 2.8.14-SNAPSHOT ../poms/parent/pom.xml 4.0.0 msf4j-deployer bundle WSO2 MSF4J Deployer WSO2 MSF4J Deployer https://github.com/wso2/msf4j org.wso2.eclipse.osgi org.eclipse.osgi org.wso2.eclipse.osgi org.eclipse.osgi.services org.wso2.carbon.deployment org.wso2.carbon.deployment.engine org.wso2.msf4j msf4j-core org.wso2.msf4j msf4j-spring org.testng testng test org.apache.maven.shared maven-invoker 2.2 test org.apache.maven.plugins maven-compiler-plugin org.apache.rat apache-rat-plugin org.apache.maven.plugins maven-checkstyle-plugin org.apache.maven.plugins maven-surefire-plugin -Dmsf4jTestRunning src/test/resources/testng.xml ${maven.home} ${project.basedir} org.apache.maven.plugins maven-deploy-plugin org.apache.felix maven-bundle-plugin org.wso2.msf4j.deployer.internal !org.wso2.msf4j.deployer.internal, org.wso2.msf4j.deployer;version="${msf4j.version}" org.wso2.msf4j.*;version="${msf4j.version}", javax.annotation.*, javax.ws.rs.*, org.slf4j.*;version="${slf4j.version.range}", org.osgi.framework.*;version="${osgi.framework.import.version.range}", org.wso2.carbon.kernel.startupresolver.*;version="${carbon.kernel.version.range}", org.wso2.carbon.deployment.engine.*;version="${carbon.deployment.version.range}", startup.listener;componentName="wso2-microservices-deployer";requiredService="org.wso2.msf4j.MicroservicesRegistry", osgi.service; objectClass="org.wso2.carbon.deployment.engine.Deployer" * ================================================ FILE: deployer/src/main/java/org/wso2/msf4j/deployer/MicroserviceDeploymentException.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.deployer; /** * Exception that is thrown when processing jar files * by MicroserviceProcessor. */ public class MicroserviceDeploymentException extends Exception { public MicroserviceDeploymentException(String message) { super(message); } public MicroserviceDeploymentException(String message, Exception e) { super(message, e); } } ================================================ FILE: deployer/src/main/java/org/wso2/msf4j/deployer/MicroserviceDeploymentUtils.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.deployer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.List; import java.util.jar.Attributes; import java.util.jar.JarFile; import java.util.jar.Manifest; /** * Dynamically initialising a set of objects from a given jar * according to the manifest entry 'microservices'. */ public class MicroserviceDeploymentUtils { private static final Logger logger = LoggerFactory.getLogger(MicroserviceDeploymentUtils.class); private static final String MICROSERVICES_MANIFEST_KEY = "Microservices"; /** * Initialize a list of objects from the jar for the specified * classes in the jar's manifest file under 'microservices' key. * * @return micro services object list * @throws MicroserviceDeploymentException if an error occurs while processing the jar file */ public static List getRourceInstances(File artifactFile) throws MicroserviceDeploymentException { String jarPath = artifactFile.getAbsolutePath(); final String[] serviceClassNames = readManifestEntry(jarPath); List resourceInstances = new ArrayList<>(); //Parent class loader is required to provide classes that are outside of the jar try { AccessController.doPrivileged(new PrivilegedExceptionAction() { public Void run() throws MicroserviceDeploymentException { try { URLClassLoader classLoader = new URLClassLoader(new URL[]{artifactFile.toURI().toURL()}, this.getClass().getClassLoader()); for (String className : serviceClassNames) { try { Class classToLoad = classLoader.loadClass(className); resourceInstances.add(classToLoad.newInstance()); } catch (ClassNotFoundException e) { throw new MicroserviceDeploymentException("Class: " + className + " not found", e); } catch (InstantiationException e) { throw new MicroserviceDeploymentException("Failed to initialize class: " + className, e); } catch (IllegalAccessException e) { throw new MicroserviceDeploymentException("Failed to access class: " + className, e); } } } catch (MalformedURLException e) { throw new MicroserviceDeploymentException("Path to jar is invalid", e); } return null; } }); } catch (PrivilegedActionException e) { //This assignment is required to fix unchecked/unconfirmed cast findbugs issue Exception e1 = e.getException(); if (e1 instanceof MicroserviceDeploymentException) { throw (MicroserviceDeploymentException) e1; } } return resourceInstances; } /** * Extracts the comma separated list of fully qualified class * names of the 'microservices' key of the jar's maifest file. * * @param jarPath absolute path to the jar file * @return String array of fully qualified class names */ private static String[] readManifestEntry(String jarPath) throws MicroserviceDeploymentException { try (JarFile jarFile = new JarFile(jarPath)) { Manifest manifest = jarFile.getManifest(); if (manifest == null) { throw new MicroserviceDeploymentException("Error retrieving manifest: " + jarPath); } Attributes mainAttributes = manifest.getMainAttributes(); String serviceEntry = mainAttributes.getValue(MICROSERVICES_MANIFEST_KEY); if (serviceEntry == null) { throw new MicroserviceDeploymentException("Manifest entry 'microservices' not found: " + jarPath); } return serviceEntry.split("\\s*,\\s*"); } catch (IOException e) { throw new MicroserviceDeploymentException("Error retrieving manifest: " + jarPath, e); } } } ================================================ FILE: deployer/src/main/java/org/wso2/msf4j/deployer/internal/DataHolder.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.deployer.internal; import org.wso2.msf4j.MicroservicesRegistry; import java.util.HashMap; import java.util.Map; /** * DataHolder for Swagger component. * * @since 2.4.0 */ public class DataHolder { private static final DataHolder instance = new DataHolder(); private Map microserviceRegistries = new HashMap<>(); private DataHolder() { } /** * Get DataHolder object. * * @return DataHolder instance. */ public static DataHolder getInstance() { return instance; } /** * Get available MicroservicesRegistries. * * @return Map of available MicroservicesRegistries. */ public Map getMicroservicesRegistries() { return microserviceRegistries; } /** * Get available MicroservicesRegistry for the gievn registry Id. * * @param registryId of the registry need to be pick * @return MicroservicesRegistry object if the given Id. */ public MicroservicesRegistry getMicroserviceRegistry(String registryId) { return microserviceRegistries.get(registryId); } /** * Add MicroservicesRegistry with the given registry Id. * * @param registryId id of the registry. * @param microservicesRegistry instance. */ public void addMicroserviceRegistry(String registryId, MicroservicesRegistry microservicesRegistry) { microserviceRegistries.put(registryId, microservicesRegistry); } /** * Get all the MicroservicesRegistries * @return Map of MicroservicesRegistries and there IDs * */ public Map getMicroserviceRegistries() { return microserviceRegistries; } } ================================================ FILE: deployer/src/main/java/org/wso2/msf4j/deployer/internal/MSF4JDeployerSC.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * 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.wso2.msf4j.deployer.internal; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.ReferenceCardinality; import org.osgi.service.component.annotations.ReferencePolicy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.carbon.deployment.engine.Deployer; import org.wso2.carbon.kernel.startupresolver.RequiredCapabilityListener; import org.wso2.carbon.kernel.startupresolver.StartupServiceUtils; import org.wso2.msf4j.MicroservicesRegistry; import java.util.Map; /** * Service component for Microservices Deployer. * * @since 2.4.0 */ @Component( name = "org.wso2.msf4j.deployer.internal.MSF4JDeployerSC", immediate = true, property = { "componentName=wso2-microservices-deployer" } ) public class MSF4JDeployerSC implements RequiredCapabilityListener { private static final Logger log = LoggerFactory.getLogger(MSF4JDeployerSC.class); private static final String CHANNEL_ID = "LISTENER_INTERFACE_ID"; @Activate protected void start(BundleContext bundleContext) { } @Deactivate protected void stop() { } @Override public void onAllRequiredCapabilitiesAvailable() { FrameworkUtil.getBundle(MSF4JDeployerSC.class).getBundleContext() .registerService(Deployer.class, new MicroservicesDeployer(), null); log.debug("MicroservicesDeployer service is available"); } /** * To deploy micro services, we need to have at least one micro service registry in the system. * Since microservice registry is added when microservice server is active, added the reference to wait till * microservices server is active. * Otherwise if deployer registered before at least one registry is added, Deployer will start to deploy services * and will fail deployment. */ @Reference( name = "microservices-regitry", service = MicroservicesRegistry.class, cardinality = ReferenceCardinality.AT_LEAST_ONE, policy = ReferencePolicy.DYNAMIC, unbind = "removeMicroservicesRegistry" ) protected void addMicroservicesRegitry(MicroservicesRegistry registry, Map properties) { log.debug("MicroservicesRegistry get registered successfully."); DataHolder.getInstance().addMicroserviceRegistry(properties.get(CHANNEL_ID).toString(), registry); StartupServiceUtils.updateServiceCache("wso2-microservices-deployer", MicroservicesRegistry.class); } protected void removeMicroservicesRegistry(MicroservicesRegistry microservicesRegistry, Map properties) { log.debug("MicroservicesRegistry get unregistered successfully."); DataHolder.getInstance().getMicroserviceRegistries().remove(properties.get(CHANNEL_ID).toString()); } } ================================================ FILE: deployer/src/main/java/org/wso2/msf4j/deployer/internal/MicroservicesDeployer.java ================================================ /* * Copyright (c) 2016, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.deployer.internal; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.carbon.deployment.engine.Artifact; import org.wso2.carbon.deployment.engine.ArtifactType; import org.wso2.carbon.deployment.engine.Deployer; import org.wso2.carbon.deployment.engine.exception.CarbonDeploymentException; import org.wso2.msf4j.Microservice; import org.wso2.msf4j.MicroservicesRegistry; import org.wso2.msf4j.deployer.MicroserviceDeploymentException; import org.wso2.msf4j.deployer.MicroserviceDeploymentUtils; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Implementation of the micro service deployer. * This will be picked by the DeploymentEngine service according to the whiteboard pattern to deploy * micro service POJO artifacts. */ public class MicroservicesDeployer implements Deployer { private static final Logger log = LoggerFactory.getLogger(MicroservicesDeployer.class); private static final String DEPLOYMENT_PATH = "file:microservices"; private static final String SUPPORTED_EXTENSIONS[] = {"jar", "zip"}; private static final String MICROSERVICE_ARTIFACT_TYPE = "microservices"; private URL deploymentLocation; private ArtifactType artifactType; private Map> deployedArtifacts = new HashMap<>(); public void init() { if (log.isDebugEnabled()) { log.debug("microservice deployer initializing"); } artifactType = new ArtifactType<>(MICROSERVICE_ARTIFACT_TYPE); try { deploymentLocation = new URL(DEPLOYMENT_PATH); } catch (MalformedURLException e) { log.error("microservices deployer location error | location: " + DEPLOYMENT_PATH , e); } } /** * Deploy and artifact in the netty-http service. * * @param artifact the artifact to be deployed * @return A key to identify the deployed artifact * @throws CarbonDeploymentException If deployment fails */ public Object deploy(Artifact artifact) throws CarbonDeploymentException { if (artifact == null || artifact.getFile() == null) { throw new CarbonDeploymentException("Deployment artifact cannot be null"); } File artifactFile = artifact.getFile(); String artifactPath = artifactFile.getAbsolutePath(); if (isSupportedFile(artifactFile)) { log.info("Deploying microservice artifact: {}", artifactPath); List resourcesList; try { resourcesList = MicroserviceDeploymentUtils.getRourceInstances(artifactFile); } catch (MicroserviceDeploymentException e) { throw new CarbonDeploymentException("Error while processing the artifact: " + artifactPath, e); } if (resourcesList.size() == 0) { throw new CarbonDeploymentException("No classes to initialize in artifact: " + artifactPath); } boolean deployed = resourcesList.stream() .filter(resource -> resource instanceof Microservice) .map(resource -> addService((Microservice) resource)) .anyMatch(Boolean.FALSE::equals); if (!deployed) { // If one service not deployed correctly, process should retry later. artifact.setKey(artifactPath); deployedArtifacts.put(artifactPath, resourcesList); return artifactPath; } } return null; } /** * Undeploy the artifact with the key from the netty-http service. * * @param key Key to identify the artifact * @throws CarbonDeploymentException If an error occurs while undeploying */ public void undeploy(Object key) throws CarbonDeploymentException { log.info("Undeploying artifact: {}", key); List resourcesList = deployedArtifacts.get(key); if (resourcesList != null) { resourcesList.forEach(resource -> { if (resource instanceof Microservice) { removeService((Microservice) resource); } }); } } /** * Update the artifact from the netty-http service. * @param artifact the artifact to be deployed * @return A key to identify the deployed artifact * @throws CarbonDeploymentException If update fails */ public Object update(Artifact artifact) throws CarbonDeploymentException { File artifactFile = artifact.getFile(); String artifactPath = artifactFile.getAbsolutePath(); if (isSupportedFile(artifactFile)) { log.info("Updating artifact: {}", artifactPath); undeploy(artifact.getKey()); deploy(artifact); } return artifactPath; } /** * @return Artifact deployment location relative to the * server dir in repository */ public URL getLocation() { return deploymentLocation; } /** * @return Artifact deployment location */ public ArtifactType getArtifactType() { return artifactType; } /** * Checks whether the artifact file type is supported in Microservices Deployer. * @param file artifact file * @return true, if file type is supported, false, if not */ private boolean isSupportedFile(File file) { return Arrays .stream(SUPPORTED_EXTENSIONS) .filter( s -> s.equalsIgnoreCase(getFileExtension(file)) ).findAny().isPresent(); } /** * Returns the extension of the artifact file. * @param file artifact file * @return file extension */ private String getFileExtension(File file) { String extension = ""; if (file == null) { return extension; } String fileName = file.getName(); if (file.isFile()) { int i = fileName.lastIndexOf('.'); if (i > 0) { extension = fileName.substring(i + 1); } } return extension; } /** * Add micro services instance to micro services registry. * @param service object to be added to registry */ private boolean addService(Microservice service) { Map microservicesRegistries = DataHolder.getInstance().getMicroserviceRegistries(); if (microservicesRegistries.isEmpty()) { log.error("Microservice deployment failed. Microservices Registry doesn't exist to register microservice."); return false; } microservicesRegistries.values().forEach(registry -> registry.addService(service)); log.info("Microservice {} deployed successfully", service.getClass().getName()); return true; } /** * Remove micro services instance to micro services registry. * @param service object to be removed from registry, */ private boolean removeService(Microservice service) { Map microservicesRegistries = DataHolder.getInstance().getMicroserviceRegistries(); if (microservicesRegistries.isEmpty()) { log.error("Microservice removal failed. Microservices Registry doesn't exist to register microservice."); return false; } microservicesRegistries.values().forEach(registry -> { registry.removeService(service); registry.preDestroyService(service); }); log.info("Microservice {} undeployed successfully", service.getClass().getName()); return true; } } ================================================ FILE: deployer/src/test/java/org/wso2/msf4j/deployer/MSF4JDeployerTest.java ================================================ /* * Copyright (c) 2017, WSO2 Inc. (http://wso2.com) All Rights Reserved. * * 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.wso2.msf4j.deployer; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import io.netty.handler.codec.http.HttpHeaderNames; import org.apache.commons.io.Charsets; import org.apache.commons.io.IOUtils; import org.apache.maven.shared.invoker.DefaultInvocationRequest; import org.apache.maven.shared.invoker.DefaultInvoker; import org.apache.maven.shared.invoker.InvocationRequest; import org.apache.maven.shared.invoker.Invoker; import org.apache.maven.shared.invoker.MavenInvocationException; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import org.wso2.carbon.deployment.engine.Artifact; import org.wso2.carbon.deployment.engine.exception.CarbonDeploymentException; import org.wso2.msf4j.MicroservicesRegistry; import org.wso2.msf4j.MicroservicesRunner; import org.wso2.msf4j.deployer.internal.DataHolder; import org.wso2.msf4j.deployer.internal.MicroservicesDeployer; import org.wso2.msf4j.internal.MicroservicesRegistryImpl; import java.io.File; import java.io.IOException; import java.lang.reflect.Type; import java.net.HttpURLConnection; import java.net.URI; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collections; import java.util.Map; import java.util.Optional; import java.util.stream.Stream; import javax.ws.rs.HttpMethod; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; /** * Tests microservice deployer. */ public class MSF4JDeployerTest { private static final String HOSTNAME = "localhost"; private static final int PORT = 8090; private MicroservicesDeployer deployer; private static final String HEADER_VAL_CLOSE = "CLOSE"; private static final Gson GSON = new Gson(); private static final Type STRING_MAP_TYPE = new TypeToken>() { }.getType(); protected static URI baseURI; private String stockqoutesSamplesFile; @BeforeClass public void setup() throws Exception { MicroservicesRunner microservicesRunner = new MicroservicesRunner(PORT); microservicesRunner.start(); Map microservicesRegistries = DataHolder.getInstance().getMicroservicesRegistries(); microservicesRegistries.put("test", microservicesRunner.getMsRegistry()); deployer = new MicroservicesDeployer(); baseURI = URI.create(String.format("http://%s:%d", HOSTNAME, 8090)); // get the stockquote sample project path. sample.filepath property is added to surefire plugins configuration String rootDirectory = Paths.get(System.getProperty("project.filepath")).getParent().toString(); stockqoutesSamplesFile = Paths.get(rootDirectory, "samples", "stockquote").toString(); } @AfterClass public void teardown() throws Exception { Map microservicesRegistries = org.wso2.msf4j.internal.DataHolder.getInstance().getMicroservicesRegistries(); microservicesRegistries.remove("test"); } @Test public void testJarArtifactDeployment() throws Exception { // compile the stockqoute deployable-jar sample compileTestSamples(Paths.get(stockqoutesSamplesFile, "deployable-jar", "pom.xml").toFile()); // get the jar file path Optional path = getSampleJarFile(Paths.get(stockqoutesSamplesFile, "deployable-jar", "target")); assertTrue("Sample artifact doesn't found in output directory : " + Paths.get(stockqoutesSamplesFile, "deployable-jar", "target"), path.isPresent()); File file = path.get().toFile(); Artifact artifact = new Artifact(file); deployer.deploy(artifact); HttpURLConnection urlConn = request("/stockquote/IBM", HttpMethod.GET); assertEquals(HttpURLConnection.HTTP_OK, urlConn.getResponseCode()); String content = getContent(urlConn); Map map = GSON.fromJson(content, STRING_MAP_TYPE); assertEquals(5, map.size()); assertEquals("IBM", map.get("symbol")); assertEquals("International Business Machines", map.get("name")); urlConn.disconnect(); } @Test(dependsOnMethods = "testJarArtifactDeployment") public void testJarArtifactUndeployment() throws Exception { Optional path = getSampleJarFile(Paths.get(stockqoutesSamplesFile, "deployable-jar", "target")); assertTrue("Sample artifact doesn't found in output directory : " + Paths.get(stockqoutesSamplesFile, "deployable-jar", "target"), path.isPresent()); File file = path.get().toFile(); deployer.undeploy(file.getAbsolutePath()); HttpURLConnection urlConn = request("/stockquote/IBM", HttpMethod.GET); assertEquals(HttpURLConnection.HTTP_NOT_FOUND, urlConn.getResponseCode()); } @Test(expectedExceptions = CarbonDeploymentException.class, expectedExceptionsMessageRegExp = "Error while processing the artifact.*") public void testFatJarArtifactDeployment() throws Exception { // compile the stockqoute fatjar sample compileTestSamples(Paths.get(stockqoutesSamplesFile, "fatjar", "pom.xml").toFile()); Optional path = getSampleJarFile(Paths.get(stockqoutesSamplesFile, "fatjar", "target")); assertTrue("Sample artifact doesn't found in output directory : " + Paths.get(stockqoutesSamplesFile, "fatjar", "target"), path.isPresent()); File file = path.get().toFile(); Artifact artifact = new Artifact(file); deployer.deploy(artifact); } @Test(expectedExceptions = CarbonDeploymentException.class, expectedExceptionsMessageRegExp = "Error while processing the artifact.*") public void testBundleArtifactDeployment() throws Exception { // compile the stockqoute bundle sample compileTestSamples(Paths.get(stockqoutesSamplesFile, "bundle", "pom.xml").toFile()); Optional path = getSampleJarFile(Paths.get(stockqoutesSamplesFile, "bundle", "target")); assertTrue("Sample artifact doesn't found in output directory : " + Paths.get(stockqoutesSamplesFile, "bundle", "target"), path.isPresent()); File file = path.get().toFile(); Artifact artifact = new Artifact(file); deployer.deploy(artifact); } private HttpURLConnection request(String path, String method) throws IOException { return request(path, method, false); } private HttpURLConnection request(String path, String method, boolean keepAlive) throws IOException { URL url = baseURI.resolve(path).toURL(); HttpURLConnection urlConn = (HttpURLConnection) url.openConnection(); if (method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT)) { urlConn.setDoOutput(true); } urlConn.setRequestMethod(method); if (!keepAlive) { urlConn.setRequestProperty(HttpHeaderNames.CONNECTION.toString(), HEADER_VAL_CLOSE); } return urlConn; } private String getContent(HttpURLConnection urlConn) throws IOException { return new String(IOUtils.toByteArray(urlConn.getInputStream()), Charsets.UTF_8); } /** * compile the sample project in the given filepath. * @param projectFile sample project pom file location * @throws MavenInvocationException */ private void compileTestSamples(File projectFile) throws MavenInvocationException { InvocationRequest request = new DefaultInvocationRequest(); request.setPomFile(projectFile); request.setGoals(Collections.singletonList("install")); Invoker invoker = new DefaultInvoker(); String mavenLocalRepo = System.getProperty("maven.repo.local"); if (mavenLocalRepo != null && !mavenLocalRepo.isEmpty()) { invoker.setLocalRepositoryDirectory(new File(mavenLocalRepo)); } invoker.execute(request); } /** * Returns the jar file in the given project target directory location. * @param targetDirectory target file path * @return * @throws IOException */ private Optional getSampleJarFile(Path targetDirectory) throws IOException { try (Stream paths = Files.walk(targetDirectory)) { return paths.filter(filePath -> Files.isRegularFile(filePath) && filePath.toString().endsWith(".jar")) .findFirst(); } } } ================================================ FILE: deployer/src/test/resources/testng.xml ================================================ ================================================ FILE: distribution/binary/LICENSE.txt ================================================ This product is licensed by WSO2 Inc. under Apache License 2.0. The license can be downloaded from the following locations: http://www.apache.org/licenses/LICENSE-2.0.html http://www.apache.org/licenses/LICENSE-2.0.txt This product also contains software under different licenses. This table below all the contained libraries (jar files) and the license under which they are provided to you. At the bottom of this file is a table that shows what each license indicated below is and where the actual text of the license can be found. Name Type License --------------------------------------------------------------------------------------------------------- guava_20.0.jar bundle apache2 commons-io_2.4.0.wso2v1.jar bundle apache2 commons-pool_1.5.6.wso2v1.jar bundle apache2 metrics-core_3.2.4.jar bundle apache2 metrics-jvm_3.2.4.jar bundle apache2 swagger-annotations_1.5.16.jar bundle apache2 swagger-core_1.5.16.jar bundle apache2 swagger-jaxrs_1.5.16.jar bundle apache2 swagger-models_1.5.16.jar bundle apache2 validation-api_1.1.0.Final.jar jar apache2 libthrift_0.9.2.wso2v1.jar bundle apache2 javassist_3.19.0-GA.jar bundle mpl+lgpl+apache2 json_20140107.jar jar apache2 json_3.0.0.wso2v1.jar bundle apache2 reflections_0.9.11.jar jar wtfpl+bsd disruptor_3.3.2.wso2v2.jar bundle httpclient_4.3.1.wso2v2.jar bundle apache2 snakeyaml_1.17.jar bundle apache2 log4j_1.2.17.wso2v1.jar jar apache2 jackson-annotations_2.8.9.jar bundle apache2 jackson-core_2.8.9.jar bundle apache2 jackson-databind_2.8.9.jar bundle apache2 jackson-dataformat-yaml_2.8.9.jar bundle apache2 jackson-jaxrs-base_2.4.5.jar bundle apache2 jackson-jaxrs-json-provider_2.4.5.jar bundle apache2 jackson-module-jaxb-annotations_2.4.5.jar bundle apache2 gson_2.2.4.jar jar apache2 javax.websocket-api_1.1.jar bundle javax.ws.rs-api_2.0.jar jar org.osgi.core_6.0.0.jar jar apache2 slf4j-api_1.7.5.jar jar mit slf4j-log4j12_1.6.0.jar jar mit commons-codec_1.9.jar jar apache2 commons-logging_1.1.1.jar jar apache2 commons-pool_1.5.6.jar jar apache2 commons-lang3_3.4.jar jar apache2 httpclient_4.5.2.jar jar apache2 httpcore_4.4.4.jar jar apache2 org.apache.servicemix.bundles.commons-beanutils_1.8.3_2.jar bundle apache2 netty-buffer_4.1.16.Final.jar jar apache2 netty-codec_4.1.16.Final.jar jar apache2 netty-codec-http_4.1.16.Final.jar jar apache2 netty-codec-http2_4.1.16.Final.jar jar apache2 netty-common_4.1.16.Final.jar jar apache2 netty-handler_4.1.16.Final.jar jar apache2 netty-resolver_4.1.16.Final.jar jar apache2 netty-transport_4.1.16.Final.jar jar apache2 org.wso2.carbon.config_2.1.5.jar bundle apache2 org.wso2.carbon.core_5.2.0.jar bundle apache2 org.wso2.carbon.launcher_5.2.0.jar jar apache2 org.wso2.carbon.databridge.agent_6.0.0.jar bundle apache2 org.wso2.carbon.databridge.commons_6.0.0.jar bundle apache2 org.wso2.carbon.databridge.commons.binary_6.0.0.jar bundle apache2 org.wso2.carbon.databridge.commons.thrift_6.0.0.jar bundle apache2 org.wso2.carbon.deployment.engine_5.1.9.jar bundle apache2 org.wso2.carbon.messaging_3.0.2.jar bundle apache2 org.wso2.carbon.metrics.core_2.3.2.jar bundle apache2 org.wso2.carbon.metrics.das.core_2.3.2.jar bundle apache2 org.wso2.carbon.metrics.das.reporter_2.3.2.jar bundle apache2 org.wso2.carbon.metrics.jdbc.core_2.3.2.jar bundle apache2 org.wso2.carbon.metrics.jdbc.reporter_2.3.2.jar bundle apache2 org.wso2.carbon.secvault_5.0.8.jar bundle apache2 org.wso2.transport.http.netty_6.0.163.jar bundle apache2 org.wso2.carbon.utils_2.0.2.jar bundle apache2 jaxrs-delegates_2.6.1.jar bundle apache2 msf4j-analytics_2.6.1.jar bundle apache2 msf4j-analytics-common_2.6.1.jar bundle apache2 msf4j-core_2.6.1.jar bundle apache2 msf4j-swagger_2.6.1.jar bundle apache2 HikariCP_2.6.3.jar bundle apache2 The license types used by the above libraries and their information is given below: apache2 Apache License Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.html cddl+gpl CDDL + GPLv2 https://glassfish.dev.java.net/public/CDDL+GPL.html epl1 Eclipse Public License http://www.eclipse.org/legal/epl-v10.html ================================================ FILE: distribution/binary/README.md ================================================ # WSO2 Microservice Framework for Java (MSF4J) Distribution WSO2 MSF4J is a lightweight high performance framework for implementing microservices in Java. MSF4J distribution contains the [msf4j-all.jar](../msf4j-all) file and the samples. ## Running & Installation Prerequisites: JDK 1.8 and Maven 3.1.x. You can simply add the msf4j-all-*.jar file in this distribution to your classpath and start writing your microservice. If you have Maven installed, you could navigate to the relevant samples in this distribution and run 'mvn clean package' to build the samples. Please follow the instructions in the README files for detailed instructions about the samples. We also have a Maven archetype called msf4j-microservice, and you can create your project using that archetype. Please see https://github.com/wso2/msf4j/tree/master/archetypes for more details. For key concepts, see https://docs.wso2.com/display/MSF4J100/Key+Concepts#KeyConcepts ## Support WSO2 Inc. offers a variety of development and production support programs, ranging from Web-based support up through normal business hours, to premium 24x7 phone support. For additional support information please refer to http://wso2.com/support For more information on WSO2 Microservices Framework for Java (MSF4J), visit project Home Page (http://wso2.com/products/microservices-framework-for-java/) ## Contact Us WSO2 MSF4J developers can be contacted via the following mailing lists. - Development: dev@wso2.org - Architecture: architecture@wso2.org Alternatively, questions can also be raised in the stackoverflow forum http://stackoverflow.com/questions/tagged/wso2 ## How To Contribute You can find comprehensive instructions on how to contribute from the Getting Involved guide http://docs.wso2.com/microservices-framework-for-java/Get+Involved ## For Further Details - http://docs.wso2.org/microservices-framework-for-java - https://github.com/wso2/msf4j --------------------------------------------------------------------------- (c) Copyright 2017 WSO2 Inc. ================================================ FILE: distribution/binary/bin.xml ================================================ true wso2msf4j-dist-${project.version} msf4j zip . org.wso2.msf4j:msf4j-all:jar conf . 644 ../../samples/ samples **/target/** **/.vagrant.d/** **/.vagrant/** **/kubernetes-vagrant-coreos-cluster/docker/** **/VirtualBox VMs/** **/dependency-reduced-pom.xml **/*.iml **/*.ipr **/*.iwr **/*.eclipse ../../analytics/das-setup analytics/das-setup **/target/** **/*.iml **/*.ipr **/*.iwr **/*.eclipse LICENSE.txt . 644 false README.md . 644 false ================================================ FILE: distribution/binary/conf/netty-transports.yml ================================================ ################################################################################ # Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. # # 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. ################################################################################ transportProperties: - name: "latency.metrics.enabled" value: true - name: "server.bootstrap.boss.group.size" value: 4 - name: "server.bootstrap.worker.group.size" value: 8 listenerConfigurations: - id: "msf4j-http" host: "0.0.0.0" port: 8080 - id: "msf4j-https" host: "0.0.0.0" port: 8443 scheme: https sslConfig: keyStore: "src/test/resources/cert.jks" keyStorePass: secret senderConfigurations: - id: "netty-gw" ================================================ FILE: distribution/binary/pom.xml ================================================ org.wso2.msf4j msf4j-parent 2.8.14-SNAPSHOT ../../poms/parent/pom.xml 4.0.0 wso2msf4j-dist pom WSO2 MSF4J Server - Distribution Pack WSO2 MSF4J Server - Distribution Pack http://wso2.org org.wso2.msf4j msf4j-all org.apache.maven.plugins maven-assembly-plugin distribution package single ${basedir}/bin.xml false ================================================ FILE: distribution/msf4j-all/README.md ================================================ # WSO2 Microservice Framework for Java (MSF4J) - All in one jar file A single jar file which packs MSF4J and all its dependencies. ================================================ FILE: distribution/msf4j-all/pom.xml ================================================ 4.0.0 org.wso2.msf4j msf4j-parent 2.8.14-SNAPSHOT ../../poms/parent/pom.xml msf4j-all jar MSF4J - All in one jar file org.wso2.msf4j msf4j-core com.nimbusds nimbus-jose-jwt org.wso2.eclipse.osgi org.eclipse.osgi org.wso2.eclipse.osgi org.eclipse.osgi.services com.google.code.findbugs jsr305 org.wso2.msf4j msf4j-swagger org.wso2.msf4j msf4j-analytics org.apache.maven.plugins maven-shade-plugin package shade *:* META-INF/*.SF META-INF/*.DSA META-INF/*.RSA ================================================ FILE: features/etc/feature.properties ================================================ ################################################################################ # Copyright (c) WSO2 Inc. (http://www.wso2.org) All Rights Reserved. # # WSO2 Inc. licenses this file to you under the Apache License, # Version 2.0 (the "License"); you may not use this file except # in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # ################################################################################ providerName=WSO2 Inc. ########################## license properties ################################## licenseURL=http://www.apache.org/licenses/LICENSE-2.0 license=\ Apache License\n\ Version 2.0, January 2004\n\ http://www.apache.org/licenses/\n\ \n\ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\ \n\ 1. Definitions.\n\ \n\ "License" shall mean the terms and conditions for use, reproduction,\n\ and distribution as defined by Sections 1 through 9 of this document.\n\ \n\ "Licensor" shall mean the copyright owner or entity authorized by\n\ the copyright owner that is granting the License.\n\ \n\ "Legal Entity" shall mean the union of the acting entity and all\n\ other entities that control, are controlled by, or are under common\n\ control with that entity. For the purposes of this definition,\n\ "control" means (i) the power, direct or indirect, to cause the\n\ direction or management of such entity, whether by contract or\n\ otherwise, or (ii) ownership of fifty percent (50%) or more of the\n\ outstanding shares, or (iii) beneficial ownership of such entity.\n\ \n\ "You" (or "Your") shall mean an individual or Legal Entity\n\ exercising permissions granted by this License.\n\ \n\ "Source" form shall mean the preferred form for making modifications,\n\ including but not limited to software source code, documentation\n\ source, and configuration files.\n\ \n\ "Object" form shall mean any form resulting from mechanical\n\ transformation or translation of a Source form, including but\n\ not limited to compiled object code, generated documentation,\n\ and conversions to other media types.\n\ \n\ "Work" shall mean the work of authorship, whether in Source or\n\ Object form, made available under the License, as indicated by a\n\ copyright notice that is included in or attached to the work\n\ (an example is provided in the Appendix below).\n\ \n\ "Derivative Works" shall mean any work, whether in Source or Object\n\ form, that is based on (or derived from) the Work and for which the\n\ editorial revisions, annotations, elaborations, or other modifications\n\ represent, as a whole, an original work of authorship. For the purposes\n\ of this License, Derivative Works shall not include works that remain\n\ separable from, or merely link (or bind by name) to the interfaces of,\n\ the Work and Derivative Works thereof.\n\ \n\ "Contribution" shall mean any work of authorship, including\n\ the original version of the Work and any modifications or additions\n\ to that Work or Derivative Works thereof, that is intentionally\n\ submitted to Licensor for inclusion in the Work by the copyright owner\n\ or by an individual or Legal Entity authorized to submit on behalf of\n\ the copyright owner. For the purposes of this definition, "submitted"\n\ means any form of electronic, verbal, or written communication sent\n\ to the Licensor or its representatives, including but not limited to\n\ communication on electronic mailing lists, source code control systems,\n\ and issue tracking systems that are managed by, or on behalf of, the\n\ Licensor for the purpose of discussing and improving the Work, but\n\ excluding communication that is conspicuously marked or otherwise\n\ designated in writing by the copyright owner as "Not a Contribution."\n\ \n\ "Contributor" shall mean Licensor and any individual or Legal Entity\n\ on behalf of whom a Contribution has been received by Licensor and\n\ subsequently incorporated within the Work.\n\ \n\ 2. Grant of Copyright License. Subject to the terms and conditions of\n\ this License, each Contributor hereby grants to You a perpetual,\n\ worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n\ copyright license to reproduce, prepare Derivative Works of,\n\ publicly display, publicly perform, sublicense, and distribute the\n\ Work and such Derivative Works in Source or Object form.\n\ \n\ 3. Grant of Patent License. Subject to the terms and conditions of\n\ this License, each Contributor hereby grants to You a perpetual,\n\ worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n\ (except as stated in this section) patent license to make, have made,\n\ use, offer to sell, sell, import, and otherwise transfer the Work,\n\ where such license applies only to those patent claims licensable\n\ by such Contributor that are necessarily infringed by their\n\ Contribution(s) alone or by combination of their Contribution(s)\n\ with the Work to which such Contribution(s) was submitted. If You\n\ institute patent litigation against any entity (including a\n\ cross-claim or counterclaim in a lawsuit) alleging that the Work\n\ or a Contribution incorporated within the Work constitutes direct\n\ or contributory patent infringement, then any patent licenses\n\ granted to You under this License for that Work shall terminate\n\ as of the date such litigation is filed.\n\ \n\ 4. Redistribution. You may reproduce and distribute copies of the\n\ Work or Derivative Works thereof in any medium, with or without\n\ modifications, and in Source or Object form, provided that You\n\ meet the following conditions:\n\ \n\ (a) You must give any other recipients of the Work or\n\ Derivative Works a copy of this License; and\n\ \n\ (b) You must cause any modified files to carry prominent notices\n\ stating that You changed the files; and\n\ \n\ (c) You must retain, in the Source form of any Derivative Works\n\ that You distribute, all copyright, patent, trademark, and\n\ attribution notices from the Source form of the Work,\n\ excluding those notices that do not pertain to any part of\n\ the Derivative Works; and\n\ \n\ (d) If the Work includes a "NOTICE" text file as part of its\n\ distribution, then any Derivative Works that You distribute must\n\ include a readable copy of the attribution notices contained\n\ within such NOTICE file, excluding those notices that do not\n\ pertain to any part of the Derivative Works, in at least one\n\ of the following places: within a NOTICE text file distributed\n\ as part of the Derivative Works; within the Source form or\n\ documentation, if provided along with the Derivative Works; or,\n\ within a display generated by the Derivative Works, if and\n\ wherever such third-party notices normally appear. The contents\n\ of the NOTICE file are for informational purposes only and\n\ do not modify the License. You may add Your own attribution\n\ notices within Derivative Works that You distribute, alongside\n\ or as an addendum to the NOTICE text from the Work, provided\n\ that such additional attribution notices cannot be construed\n\ as modifying the License.\n\ \n\ You may add Your own copyright statement to Your modifications and\n\ may provide additional or different license terms and conditions\n\ for use, reproduction, or distribution of Your modifications, or\n\ for any such Derivative Works as a whole, provided Your use,\n\ reproduction, and distribution of the Work otherwise complies with\n\ the conditions stated in this License.\n\ \n\ 5. Submission of Contributions. Unless You explicitly state otherwise,\n\ any Contribution intentionally submitted for inclusion in the Work\n\ by You to the Licensor shall be under the terms and conditions of\n\ this License, without any additional terms or conditions.\n\ Notwithstanding the above, nothing herein shall supersede or modify\n\ the terms of any separate license agreement you may have executed\n\ with Licensor regarding such Contributions.\n\ \n\ 6. Trademarks. This License does not grant permission to use the trade\n\ names, trademarks, service marks, or product names of the Licensor,\n\ except as required for reasonable and customary use in describing the\n\ origin of the Work and reproducing the content of the NOTICE file.\n\ \n\ 7. Disclaimer of Warranty. Unless required by applicable law or\n\ agreed to in writing, Licensor provides the Work (and each\n\ Contributor provides its Contributions) on an "AS IS" BASIS,\n\ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n\ implied, including, without limitation, any warranties or conditions\n\ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n\ PARTICULAR PURPOSE. You are solely responsible for determining the\n\ appropriateness of using or redistributing the Work and assume any\n\ risks associated with Your exercise of permissions under this License.\n\ \n\ 8. Limitation of Liability. In no event and under no legal theory,\n\ whether in tort (including negligence), contract, or otherwise,\n\ unless required by applicable law (such as deliberate and grossly\n\ negligent acts) or agreed to in writing, shall any Contributor be\n\ liable to You for damages, including any direct, indirect, special,\n\ incidental, or consequential damages of any character arising as a\n\ result of this License or out of the use or inability to use the\n\ Work (including but not limited to damages for loss of goodwill,\n\ work stoppage, computer failure or malfunction, or any and all\n\ other commercial damages or losses), even if such Contributor\n\ has been advised of the possibility of such damages.\n\ \n\ 9. Accepting Warranty or Additional Liability. While redistributing\n\ the Work or Derivative Works thereof, You may choose to offer,\n\ and charge a fee for, acceptance of support, warranty, indemnity,\n\ or other liability obligations and/or rights consistent with this\n\ License. However, in accepting such obligations, You may act only\n\ on Your own behalf and on Your sole responsibility, not on behalf\n\ of any other Contributor, and only if You agree to indemnify,\n\ defend, and hold each Contributor harmless for any liability\n\ incurred by, or claims asserted against, such Contributor by reason\n\ of your accepting any such warranty or additional liability.\n\ \n\ END OF TERMS AND CONDITIONS\n\ ================================================ FILE: features/feature-test/pom.xml ================================================ 4.0.0 org.wso2.msf4j msf4j-parent 2.3.0-SNAPSHOT ../../poms/parent/pom.xml msf4j-osgi-test jar WSO2 MSF4J OSGi tests WSO2 MSF4J core https://github.com/wso2/msf4j org.wso2.msf4j msf4j-core org.testng testng test org.ops4j.pax.exam pax-exam test org.ops4j.pax.exam pax-exam-inject test org.ops4j.pax.exam pax-exam-extender-service test org.osgi org.osgi.compendium test org.ops4j.base ops4j-base test org.apache.geronimo.specs geronimo-atinject_1.0_spec test org.ops4j.pax.exam pax-exam-container-native test org.ops4j.pax.exam pax-exam-testng test org.ops4j.pax.exam pax-exam-link-mvn test org.ops4j.pax.url pax-url-aether test org.slf4j slf4j-api test org.ow2.spec.ee ow2-jta-1.1-spec test org.jacoco org.jacoco.ant test org.apache.maven.plugins maven-compiler-plugin org.apache.rat apache-rat-plugin org.apache.maven.plugins maven-checkstyle-plugin org.apache.maven.plugins maven-surefire-plugin src/test/resources/testng.xml org.apache.maven.plugins maven-deploy-plugin org.apache.felix maven-bundle-plugin with-tests !maven.test.skip maven-resources-plugin copy-resources generate-resources copy-resources ${project.build.directory}/carbon-home true src/test/resources/carbon-home org.apache.maven.plugins maven-dependency-plugin copy-jacoco-dependencies compile copy-dependencies ${project.build.directory} jar org.jacoco.ant org.apache.maven.plugins maven-surefire-plugin ${settings.localRepository} ${project.build.directory}/jacoco.exec default carbon.home ${basedir}/target/carbon-home src/test/resources/testng.xml org.jacoco jacoco-maven-plugin ${jacoco.version} default-prepare-agent prepare-agent org.wso2.carbon* default-check check org.apache.maven.plugins maven-antrun-plugin prepare-package //this task is to provide class/src files locations for jacoco report generation run org.jacoco org.jacoco.ant ${org.jacoco.ant.version} org.ops4j.pax.exam maven-paxexam-plugin ${maven.paxexam.plugin.version} generate-config generate-depends-file ================================================ FILE: features/feature-test/src/test/java/org/wso2/msf4j/osgi/test/MSF4JStartupTest.java ================================================ /* * Copyright (c) 2005-2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ package org.wso2.msf4j.osgi.test; import org.ops4j.pax.exam.Configuration; import org.ops4j.pax.exam.Option; import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; import org.ops4j.pax.exam.spi.reactors.PerClass; import org.ops4j.pax.exam.testng.listener.PaxExam; import org.osgi.framework.BundleContext; import org.testng.Assert; import org.testng.annotations.Listeners; import org.testng.annotations.Test; import org.wso2.carbon.kernel.CarbonServerInfo; import org.wso2.carbon.osgi.test.util.CarbonSysPropConfiguration; import org.wso2.carbon.osgi.test.util.OSGiTestConfigurationUtils; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; /** * OSGi tests class to test MSF4J startup. * * @since 5.0.0 */ @Listeners(PaxExam.class) @ExamReactorStrategy(PerClass.class) public class MSF4JStartupTest { private static final String TRANSPORT_ID = "DummyTransport"; // @Inject // private TransportManager transportManager; // @Inject // private CommandProvider transportCommandProvider; @Inject private CarbonServerInfo carbonServerInfo; @Inject BundleContext bundleContext; @Configuration public Option[] createConfiguration() { List