├── .gitignore ├── .travis.yml ├── Makefile ├── OSSMETADATA ├── README.md ├── buildViaTravis.sh ├── project ├── AwsGenerate.scala ├── Bintray.scala ├── Build.scala ├── BuildSettings.scala ├── Dependencies.scala ├── GitVersion.scala ├── License.scala ├── build.properties ├── lib │ └── aws-java-sdk-1.9.33-sources.jar ├── plugins.sbt ├── sbt └── sbt-launch-0.13.8.jar ├── rx-aws-java-sdk-autoscaling └── src │ └── test │ ├── resources │ └── log4j.properties │ └── scala │ └── AmazonAutoScalingSuite.scala ├── rx-aws-java-sdk-cloudwatch └── src │ └── test │ ├── resources │ └── log4j.properties │ └── scala │ └── AmazonCloudWatchSuite.scala ├── rx-aws-java-sdk-core └── src │ └── main │ └── java │ └── com │ └── amazonaws │ ├── auth │ ├── RxAWS4Signer.java │ └── RxAbstractAWSSigner.java │ ├── http │ ├── AmazonRxNettyHttpClient.java │ ├── JsonRxNettyErrorResponseHandler.java │ ├── JsonRxNettyErrorResponseHandlerV2.java │ ├── JsonRxNettyResponseHandler.java │ ├── RxNettyResponseHandler.java │ ├── StaxRxNettyResponseHandler.java │ └── XmlRxNettyErrorResponseHandler.java │ ├── services │ ├── NamedServiceResult.java │ ├── PaginatedServiceResult.java │ └── ServiceResult.java │ ├── transform │ └── JsonRxNettyUnmarshallerContextImpl.java │ └── util │ ├── EventLoopsScheduler.java │ ├── RxSchedulers.java │ ├── RxSdkHttpUtils.java │ └── RxURLEncodedUtils.java ├── rx-aws-java-sdk-dynamodb └── src │ └── test │ ├── resources │ └── log4j.properties │ └── scala │ └── AmazonDynamoDBSuite.scala ├── rx-aws-java-sdk-ec2 └── src │ └── test │ ├── resources │ └── log4j.properties │ └── scala │ └── AmazonEC2Suite.scala ├── rx-aws-java-sdk-route53 └── src │ └── test │ ├── resources │ └── log4j.properties │ └── scala │ └── AmazonCloudWatchSuite.scala └── rx-aws-java-sdk-sqs └── src └── test ├── resources └── log4j.properties └── scala └── AmazonSQSSuite.scala /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Tmp files created by vim 3 | *.swp 4 | 5 | # Compiled sources 6 | *.class 7 | 8 | # Anything in target dirs 9 | build 10 | gh-pages-build 11 | target 12 | 13 | # Ivy caches 14 | .gradle 15 | .ivy2 16 | .ivy2.cache 17 | .sbt 18 | 19 | # Intellij 20 | *.iml 21 | *.ipr 22 | *.iws 23 | 24 | # anything in .idea dirs 25 | .idea 26 | .idea_modules 27 | out 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: scala 2 | jdk: 3 | - oraclejdk8 4 | scala: 5 | - 2.11.7 6 | script: 7 | - ./buildViaTravis.sh 8 | env: 9 | global: 10 | - secure: fhK26i5sn+KBeZoItCVIP9B3qmPfxTBrFkgVthks+j9Lim5KRDCp7kT5S2EfNGonBNcn2Z3Vxz8cxjGFz0EI15Trqr2WqmktcffAErsbWMB6h8XkUXu7wXECx7LRIKH464rF5EaF2s0rnyeBRw2rYiN9J3rS3p+2tm0AgRHAylvdLnNY4sR4AY+bxfxbk05p3wMnN5xuQFuKr0jBP7TtvEx1e/fn3Ooj5+Xu/+DP917Ksi5oxnvS+qMSct1Pth8O0i6xdraM4/PKZccM0cUonVZ+ZZPs92IBoNaRGF8AbfzKX65geLyebjFSnKbdQlJoQEnJXt5vFq3ozhz+IIEjHa+kYTGzXOcEuwf0rF6M25ZMHoP8Q1AJmWJDZ1FUTEFM2V3VUDaVC7iEXajhY8Q+vM7nlgotIlDqiSlT6f58buenfqEwscqrTmVxZBm+kp8N6lKdV/jJWa05JSAKe/3Bia4CJhzoE1tLXbeUsuhvG5G/R+1J4kdRzF3KoU3w/6w0UF/V21XyytHeYCtRba0Z7Z/omy0fm6ghpInBZGfwQijBIDdPTxJ3lvDMQGGqTzAAtW6xrXnKua4Yiy8VjjAgZBAKaxcZEfpUMN5b5tTlmU9ex+hfrDNuWDlKBxQrmykTp4vFr5/9fob4uwy4uN+Q5JDmqQHwprFc+/+D2YCh9d0= 11 | - secure: Q9+RbMWeJfSl9Mp76BG+YLCbxKdYNnj0vDhhw5CXAEAUaD7vHC1ndrBc+vnC3BZDNm9L/K8zg6jTMXziAA/7wQyWAM5e5MTW50TZC0n7amYfgtZAu1eNtNJN9ghfG9GoW1Ih3V1qxoIIgAAEEElAkFG3Wh2uO8qlxhTlL7iLTPtIEooxHKE4FHGECLjD+HZabtle7aioNMiwyiYSIORH+qeDJwKXGcs8qMdaffbt7MyOJqG6JN1h8juIJJrUP+E7b/R+finUUapjPcyOXDsXd1K/0EGapnYNGZFNQE9Hg2j5/8+t+XZa15A2sWCPcOaONI2YsWQrvPHd0ZivCy5Kukcag995+RUFchmphzMxCNKstqvYkUZFyKuvZxj/hlKPnUkpD4SqLU3CYYGfpFjPs4+8YV8O6z0K00waADaXjoSeepY/v370ciqL2Y/nn55+5z4DS1bKv7zPKBgAhsOuyNBwP8mkPzhPdCmmYF5W93VsAhXPG9HodoybURXtl1jQjvHFbTXzFtBTh9GkHnaNifZV9ShhTtMLc7HIcxUsTUqhO0kxjLFhH+RwhZUNkhU067WoCVlMXZXeZcquE+uPfQvwXdXfXNnzgZEwvCsNdYybOmWrkbVqm69YjYLnl++weB2cHOy4JxcZP7lr4AXehhaE076X7IWYWcLkTFceUlU= 12 | - secure: XhY4RlUXCU7HzERUcq/k4Vhq4oEUVLU5Ap3/PTOXUMrc02BKgnLeKFEkrEcVVGY/e32CrNy7td2e8wPmti74wkTvz9vY6Ocjz0h52rbPwWDF5fFMepRaJXqzXXdj9fMx/oXAetUGdXM2cVhbLmQIgjGTJ2Ei9EcJ0XbqpUVDryIy3yv3NEsQPhvTKWprbLNUp+dvr5fd+vSmKvpCWqbhuUQV87KD1lVkZaWvhMs1nW/xHZAAdF3l2m/lQ7XRUA8Ua6hLrYBBcbEhkBcslPZS59hbfBysuSdgO7HAqvkPOVo8lSoM/aXtm4gtALMCQ/a+3OyiiwPNeBQSx+aQKOGjO3SzxiLNr057gJ89e1qBcNUEoR+YDpJs8D+8nNYDBqgXEddpRqEMCPp6Fsq4ziZMifenI92245uDox78HdCr1tNxAczfD7CqfxZCMhcoSofwQxyljtlvwORxjYMxhY/S42AdcqN3N/tISAJR5wyR6v0cfJ7SMxobJUAtBFIjzkC3hOH0KpdUel64c9CC2G/qkhsWnBsqocdEYUGY6rbeZzjCaiVkWNE0ug1roIhNlJX0zH0FtVlvQE2yy+Wfp6HNLuxSArM8S8sy8RtPhXg5W/ZW52/EWbdoBdD3+5dd7AOjkzgYmKtGOFhVWid0bSUntH1qcZWTBAx1LRxQl0oiU60= 13 | - secure: nfJGNPKAsBk6S39mrIxZNTF210jax3HjmM9NV0CkBEFWhwBiU99FCHxfI8W7m1JMyS0iJzH7i6/QVeU5I6avFsBlA6coGkaD5XN1gQ0MWlfQmUxqcqcuoq5gI6f7VZ7Y8EPdpS3qNvcSCljs9Oj6V5tJMR4ACxp04uXwHWk8eGAWuzeOnoenWMNN0D1qoUkLjRtsbsnxF1NO4CyMV7+bjEXMU++w2xN/ypsWr6ENtrZHP7G7TEIb6CntWPVnz3gTKE8mK5vEY9R6863n21dG9o48tOAayNmX3FiZJfL2H0wEjFgfqUABVcQf7jYsXKF6GA54XJ5nKAdOYXMJT86LlnysOEHCwOEPyrO6PY4eTTSuT9AkfnF24M9A44sI63NAZfti7YaPwYSTlgdtR91hxS4AlnpZKnh1p96MYRP6s250MgBAr2lh0g5NwBdbX2d1JlrGmYWv8TupJ47XSXj4RhVAlvc2lOx/4jbDSWs9i+w1qMXENAMiVV1C4c8cQUlf8+6+lUopsVeENQVy5SBxeXwfNZk9Obyp8Ex2LbsX9BRxlwd6aE502TyB3hnseg1Pc9nV1S+L5w7BURd+YffD9joEUXteEUM/yCRJ3P/tjcYHHBwLDkKt5aqV4Uv4JzgfZLLMSwu8e4+gArm13JCZ2dV28ZO6Yafr75BpxTwR14M= 14 | cache: 15 | directories: 16 | - $HOME/.sbt 17 | - $HOME/.ivy2 18 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Map stdin to /dev/null to avoid interactive prompts if there is some failure related to the 2 | # build script. 3 | SBT := cat /dev/null | project/sbt 4 | 5 | .PHONY: build snapshot release coverage license 6 | 7 | build: 8 | echo "Starting build" 9 | $(SBT) clean test checkLicenseHeaders 10 | 11 | snapshot: 12 | echo "Starting snapshot build" 13 | git fetch --unshallow 14 | $(SBT) clean test checkLicenseHeaders storeBintrayCredentials publish 15 | 16 | release: 17 | # Storing the bintray credentials needs to be done as a separate command so they will 18 | # be available early enough for the publish task. 19 | # 20 | # The storeBintrayCredentials still needs to be on the subsequent command or we get: 21 | # [error] (iep-service/*:bintrayEnsureCredentials) java.util.NoSuchElementException: None.get 22 | echo "Starting release build" 23 | git fetch --unshallow 24 | $(SBT) storeBintrayCredentials 25 | $(SBT) clean test checkLicenseHeaders storeBintrayCredentials publish bintrayRelease 26 | 27 | coverage: 28 | $(SBT) clean coverage test coverageReport 29 | $(SBT) coverageAggregate 30 | 31 | license: 32 | $(SBT) formatLicenseHeaders 33 | 34 | -------------------------------------------------------------------------------- /OSSMETADATA: -------------------------------------------------------------------------------- 1 | osslifecycle=archived 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATED 2 | 3 | This project is no longer maintained or in use at Netflix. 4 | 5 | # AWS java sdk using rxjava 6 | 7 | Generation of aws client classes exposing an RX api. 8 | -------------------------------------------------------------------------------- /buildViaTravis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This script will build the project. 3 | 4 | if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then 5 | echo -e "Build Pull Request #$TRAVIS_PULL_REQUEST => Branch [$TRAVIS_BRANCH]" 6 | make build 7 | elif [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_TAG" == "" ]; then 8 | echo -e 'Build Branch with Snapshot => Branch ['$TRAVIS_BRANCH']' 9 | make snapshot 10 | elif [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_TAG" != "" ]; then 11 | echo -e 'Build Branch for Release => Branch ['$TRAVIS_BRANCH'] Tag ['$TRAVIS_TAG']' 12 | make release 13 | else 14 | echo -e 'WARN: Should not be here => Branch ['$TRAVIS_BRANCH'] Tag ['$TRAVIS_TAG'] Pull Request ['$TRAVIS_PULL_REQUEST']' 15 | make build 16 | fi 17 | -------------------------------------------------------------------------------- /project/AwsGenerate.scala: -------------------------------------------------------------------------------- 1 | import scala.collection.JavaConversions._ 2 | import scala.util.matching.Regex 3 | 4 | import java.lang.reflect._ 5 | import java.io.File 6 | import java.nio.file.{Paths, Files} 7 | import java.nio.charset.StandardCharsets 8 | 9 | import com.google.common.reflect._ 10 | 11 | case class ClientInfo( 12 | name: String, 13 | cinfo: ClassPath.ClassInfo, 14 | exceptionUnmarshallers: List[ClassPath.ClassInfo], 15 | format: String, 16 | exceptions: List[ClassPath.ClassInfo] 17 | ) { 18 | lazy val endpoint: Option[String] = AwsGenerate.endpoints.get(name.toLowerCase) 19 | lazy val className: String = s"Amazon${name}RxNettyClient" 20 | lazy val interfaceName: String = s"Amazon${name}RxNetty" 21 | lazy val isStax: Boolean = format == "stax" 22 | lazy val isJsonV1: Boolean = format == "jsonV1" 23 | lazy val isJsonV2: Boolean = format == "jsonV2" 24 | lazy val isJson: Boolean = isJsonV1 || isJsonV2 25 | } 26 | 27 | object Pagination { 28 | val tokenRegex = """^(get|set)(?:Next)?Token$""".r 29 | val keyRegex = """^(get|set)(?:LastEvaluated|ExclusiveStart)Key$""".r 30 | val markerRegex = """^(get|set)(?:Next)?Marker$""".r 31 | val recordNameRegex = """^(get|set)(?:Start|Next)?RecordName$""".r 32 | val recordTypeRegex = """^(get|set)(?:Start|Next)?RecordType$""".r 33 | val recordIdentifierRegex = """^(get|set)(?:Start|Next)?RecordIdentifier$""".r 34 | 35 | 36 | def apply(request: Class[_], result: Class[_]): List[Pagination] = List( 37 | mkPagination(tokenRegex, request, result), 38 | mkPagination(keyRegex, request, result), 39 | mkPagination(markerRegex, request, result), 40 | mkPagination(recordNameRegex, request, result), 41 | mkPagination(recordTypeRegex, request, result), 42 | mkPagination(recordIdentifierRegex, request, result) 43 | ).flatten 44 | 45 | def mkPagination( 46 | regex: Regex, 47 | request: Class[_], 48 | result: Class[_] 49 | ): Option[Pagination] = { 50 | val requestGetter = request.getDeclaredMethods.toList.filter(_.getName match { 51 | case regex("get") => true 52 | case _ => false 53 | }).filter(_.getParameterTypes.size == 0) 54 | val requestSetter = request.getDeclaredMethods.toList.filter(_.getName match { 55 | case regex("set") => true 56 | case _ => false 57 | }).filter(_.getParameterTypes.size == 1) 58 | val resultGetter = result.getDeclaredMethods.toList.filter(_.getName match { 59 | case regex("get") => true 60 | case _ => false 61 | }).filter(_.getParameterTypes.size == 0) 62 | if (requestGetter.isEmpty || requestSetter.isEmpty || resultGetter.isEmpty) None 63 | //else if (getter.isEmpty || setter.isEmpty) 64 | // throw new IllegalStateException(s"${getter} || ${setter}") 65 | else Some( 66 | Pagination( 67 | requestGetter.head.getReturnType.getSimpleName, 68 | requestGetter.head.getName, 69 | requestSetter.head.getName, 70 | resultGetter.head.getName, 71 | requestSetter.size > 1 72 | ) 73 | ) 74 | } 75 | } 76 | 77 | case class Pagination( 78 | objType: String, 79 | requestGetterName: String, 80 | requestSetterName: String, 81 | resultGetterName: String, 82 | multipleSetters: Boolean 83 | ) { 84 | def strCast: String = { 85 | if (multipleSetters) s"(${objType})" 86 | else "" 87 | } 88 | } 89 | 90 | object AwsGenerate { 91 | 92 | val clientPattern = """^Amazon([A-Za-z0-9]+?(? { 102 | println(c.cinfo) 103 | val iFile = new File(dir, s"${c.interfaceName}.java") 104 | val file = new File(dir, s"${c.className}.java") 105 | val (iContent, content) = mkContent(c) 106 | Files.write(iFile.toPath, iContent.getBytes(StandardCharsets.UTF_8)) 107 | Files.write(file.toPath, content.getBytes(StandardCharsets.UTF_8)) 108 | List(iFile, file) 109 | }) 110 | } 111 | 112 | def clientClasses(pkgSuffixes: List[String]): List[ClientInfo] = { 113 | val cl = classOf[com.amazonaws.services.ec2.AmazonEC2].getClassLoader 114 | pkgSuffixes.flatMap(pkgSuffix => { 115 | val pkg = s"com.amazonaws.services.${pkgSuffix}" 116 | ClassPath.from(cl).getTopLevelClassesRecursive(pkg).flatMap(cinfo => { 117 | cinfo.getSimpleName match { 118 | case clientPattern(prefix) => { 119 | val exceptionUnmarshallers = ClassPath.from(cl) 120 | .getTopLevelClassesRecursive(cinfo.getPackageName).flatMap(c2 => { 121 | c2.getSimpleName match { 122 | case exceptionUnmarshaller(n) => { 123 | Some(c2) 124 | } 125 | case _ => None 126 | } 127 | }).toList.sortBy(_.toString) 128 | val hasJson = ClassPath.from(cl) 129 | .getTopLevelClassesRecursive(cinfo.getPackageName).exists(c2 => { 130 | c2.getSimpleName.endsWith("JsonUnmarshaller") 131 | }) 132 | val format = exceptionUnmarshallers match { 133 | case v if (v.size == 0 && hasJson) => "jsonV2" 134 | case v if (v.forall(_.load.getSuperclass.getSimpleName == "StandardErrorUnmarshaller")) => "stax" 135 | case _ => "jsonV1" 136 | } 137 | val exceptions = ClassPath.from(cl) 138 | .getTopLevelClasses(s"${cinfo.getPackageName}.model").flatMap(c2 => { 139 | c2.getSimpleName match { 140 | case exception(n) => { 141 | Some(c2) 142 | } 143 | case _ => None 144 | } 145 | }).toList.sortBy(_.toString) 146 | Some(ClientInfo(prefix, cinfo, exceptionUnmarshallers, format, exceptions)) 147 | } 148 | case _ => None 149 | } 150 | }) 151 | }).toList.sortBy(_.toString) 152 | } 153 | def mkContent(c: ClientInfo): (String, String) = ( 154 | mkIHeader(c) + mkBody(c, true).mkString("\n") + mkIExtraMethods(c) + mkIFooter(c), 155 | mkHeader(c) + mkBody(c).mkString("\n") + mkExtraMethods(c) + mkFooter(c) 156 | ) 157 | 158 | def mkIHeader(c: ClientInfo): String = { 159 | iHeaderTemplate 160 | .replaceAll("<>", c.cinfo.getPackageName) 161 | .replaceAll("<>", c.interfaceName) 162 | } 163 | 164 | def mkHeader(c: ClientInfo, isInterface: Boolean = false): String = { 165 | val (imp, field, init) = mkExceptionUnmarshaller(c) 166 | headerTemplate 167 | .replaceAll("<>", c.cinfo.getPackageName) 168 | .replaceAll("<>", c.cinfo.getPackageName.replaceAll("\\.", "/")) 169 | .replaceAll("<>", c.className) 170 | .replaceAll("<>", c.interfaceName) 171 | .replaceAll("<>", c.endpoint.get) 172 | .replaceAll("<>", imp) 173 | .replaceAll("<>", field) 174 | .replaceAll("<>", init) 175 | } 176 | 177 | def mkBody(c: ClientInfo, isInterface: Boolean = false): Seq[String] = { 178 | val cls = c.cinfo.load 179 | cls.getDeclaredMethods 180 | .filter(_.getModifiers == 1) 181 | .filterNot(m => ignore.contains(m.getName)) 182 | .filter(m => { 183 | (m.getParameterTypes.size == 0 && m.getReturnType.getSimpleName.endsWith("Result")) || 184 | (m.getParameterTypes.size == 1 && m.getParameterTypes.head.getSimpleName.endsWith("Request")) 185 | }) 186 | .groupBy(_.getName) 187 | .toList 188 | .filter(_._2.map(_.getParameterTypes.size).sum > 0) 189 | .sortBy(_._1) 190 | .flatMap({ case (n, ms) => mkMethod(c, ms.toList, isInterface) }) 191 | } 192 | 193 | def mkMethod(c: ClientInfo, methods: List[Method], isInterface: Boolean): Option[String] = { 194 | val method :: remainder = methods.sortBy(_.getParameterTypes.size).reverse 195 | val methodName = method.getName 196 | val requestType = method.getParameterTypes.head 197 | val strRequestType = requestType.getSimpleName 198 | val resultType = method.getReturnType 199 | val strResultType = resultType.getSimpleName match { 200 | case "void" => "Void" 201 | case s => s 202 | } 203 | 204 | val pagination = Pagination(requestType, resultType) 205 | 206 | val content = { 207 | if (pagination.size > 0) { 208 | if (isInterface) 209 | remainder.headOption.map(_ => paginatedITemplateNoArgs).getOrElse("") + paginatedITemplate 210 | else 211 | remainder.headOption.map(_ => paginatedTemplateNoArgs).getOrElse("") + paginatedTemplate 212 | } 213 | else if (strResultType == "Void") { 214 | if (isInterface) 215 | remainder.headOption.map(_ => voidITemplateNoArgs).getOrElse("") + voidITemplate 216 | else 217 | remainder.headOption.map(_ => voidTemplateNoArgs).getOrElse("") + voidTemplate 218 | } 219 | else { 220 | if (isInterface) 221 | remainder.headOption.map(_ => simpleITemplateNoArgs).getOrElse("") + simpleITemplate 222 | else 223 | remainder.headOption.map(_ => simpleTemplateNoArgs).getOrElse("") + simpleTemplate 224 | } 225 | } 226 | 227 | Some( 228 | content 229 | .replaceAll("<>", strResultType) 230 | .replaceAll("<>", methodName) 231 | .replaceAll("<>", strRequestType) 232 | .replaceAll("<>", mkInitPagination(pagination)) 233 | .replaceAll("<>", mkTokenParameters(pagination)) 234 | .replaceAll("<>", mkUpdatePagination(pagination)) 235 | .replaceAll("<>", { if (c.isStax) "Stax" else "Json" }) 236 | .replaceAll("<>", { if (c.isStax) "Stax" else if (c.isJsonV1) "Json" else "JsonV2" }) 237 | ) 238 | } 239 | 240 | def mkIFooter(c: ClientInfo): String = iFooterTemplate 241 | def mkFooter(c: ClientInfo): String = footerTemplate 242 | 243 | def mkExceptionUnmarshaller(c: ClientInfo): (String, String, String) = { 244 | if (c.isStax) ( 245 | "import org.w3c.dom.Node;", 246 | "protected List> exceptionUnmarshallers;", 247 | ( 248 | List( 249 | "exceptionUnmarshallers = new ArrayList>();" 250 | ) ++ 251 | c.exceptionUnmarshallers.map(c2 => { 252 | s"exceptionUnmarshallers.add(new ${c2.getSimpleName}());" 253 | }) ++ 254 | List( 255 | "exceptionUnmarshallers.add(new StandardErrorUnmarshaller());", 256 | "exceptionUnmarshallers.add(new LegacyErrorUnmarshaller());" 257 | ) 258 | ).mkString("\n ") 259 | ) 260 | else if (c.isJsonV1) ( 261 | "", 262 | "protected List exceptionUnmarshallers;", 263 | ( 264 | List( 265 | "exceptionUnmarshallers = new ArrayList();" 266 | ) ++ 267 | c.exceptionUnmarshallers.map(c2 => { 268 | s"exceptionUnmarshallers.add(new ${c2.getSimpleName}());" 269 | }) ++ 270 | List("exceptionUnmarshallers.add(new JsonErrorUnmarshaller());") 271 | ).mkString("\n ") 272 | ) 273 | else ( 274 | "", 275 | "protected List exceptionUnmarshallers;", 276 | ( 277 | List( 278 | "exceptionUnmarshallers = new ArrayList();" 279 | ) ++ 280 | c.exceptions.map(c2 => { 281 | val ex = c2.getSimpleName 282 | val n = { if (ex.endsWith("ErrorException")) ex.substring(0, ex.size - 9) else ex } 283 | s"""exceptionUnmarshallers.add(new JsonErrorUnmarshallerV2(${c2.getName}.class, \"${n}\"));""" 284 | }) ++ 285 | List("exceptionUnmarshallers.add(JsonErrorUnmarshallerV2.DEFAULT_UNMARSHALLER);") 286 | ).mkString("\n ") 287 | ) 288 | } 289 | 290 | val ignore = List("dryRun", "getCachedResponseMetadata") 291 | 292 | val endpoints = Map( 293 | "autoscaling" -> "autoscaling.us-east-1.amazonaws.com", 294 | "cloudformation" -> "cloudformation.us-east-1.amazonaws.com", 295 | "cloudfront" -> "cloudfront.amazonaws.com", 296 | "cloudsearch" -> "cloudsearch.us-east-1.amazonaws.com", 297 | "cloudsearchdomain" -> "cloudsearchdomain.us-east-1.amazonaws.com", 298 | "cloudwatch" -> "monitoring.us-east-1.amazonaws.com", 299 | "codedeploy" -> "codedeploy.us-east-1.amazonaws.com", 300 | "cognitoidentity" -> "cognito-identity.us-east-1.amazonaws.com", 301 | "cognitosync" -> "cognito-sync.us-east-1.amazonaws.com", 302 | "config" -> "config.us-east-1.amazonaws.com", 303 | "directconnect" -> "directconnect.us-east-1.amazonaws.com", 304 | "dynamodb" -> "dynamodb.us-east-1.amazonaws.com", 305 | "ec2" -> "ec2.us-east-1.amazonaws.com", 306 | "ecs" -> "ecs.us-east-1.amazonaws.com", 307 | "elasticache" -> "elasticache.us-east-1.amazonaws.com", 308 | "elasticloadbalancing" -> "elasticloadbalancing.us-east-1.amazonaws.com", 309 | "elasticmapreduce" -> "elasticmapreduce.us-east-1.amazonaws.com", 310 | "elastictranscoder" -> "elastictranscoder.us-east-1.amazonaws.com", 311 | "glacier" -> "glacier.us-east-1.amazonaws.com", 312 | "identitymanagement" -> "iam.amazonaws.com", 313 | "importexport" -> "importexport.amazonaws.com", 314 | "kinesis" -> "kinesis.us-east-1.amazonaws.com", 315 | "rds" -> "rds.us-east-1.amazonaws.com", 316 | "redshift" -> "redshift.us-east-1.amazonaws.com", 317 | "route53" -> "route53.amazonaws.com", 318 | "route53domains" -> "route53domains.us-east-1.amazonaws.com", 319 | //"s3" -> "s3.amazonaws.com", // No unmarshaller for setting up the request 320 | //"s3encryption" -> "s3.amazonaws.com", 321 | //"simpledb" -> "sdb.amazonaws.com", // Legacy error handling 322 | "simpleemailservice" -> "email.us-east-1.amazonaws.com", 323 | "simpleworkflow" -> "swf.us-east-1.amazonaws.com", 324 | "sns" -> "sns.us-east-1.amazonaws.com", 325 | "sqs" -> "sqs.us-east-1.amazonaws.com" 326 | ) 327 | 328 | val iHeaderTemplate = """ 329 | package <>; 330 | 331 | import com.amazonaws.services.*; 332 | import <>.model.*; 333 | import rx.Observable; 334 | 335 | public interface <> { 336 | 337 | public void setEndpoint(String endpoint); 338 | """ 339 | 340 | val headerTemplate = """ 341 | package <>; 342 | 343 | import java.net.URL; 344 | import java.util.ArrayList; 345 | import java.util.List; 346 | import java.util.concurrent.atomic.AtomicInteger; 347 | 348 | <> 349 | 350 | import com.amazonaws.*; 351 | import com.amazonaws.auth.*; 352 | import com.amazonaws.handlers.*; 353 | import com.amazonaws.http.*; 354 | import com.amazonaws.internal.*; 355 | import com.amazonaws.metrics.*; 356 | import com.amazonaws.regions.*; 357 | import com.amazonaws.transform.*; 358 | import com.amazonaws.util.*; 359 | import com.amazonaws.util.AWSRequestMetrics.Field; 360 | 361 | import com.amazonaws.services.*; 362 | import <>.model.*; 363 | import <>.model.transform.*; 364 | 365 | import rx.Observable; 366 | 367 | public class <> extends AmazonRxNettyHttpClient implements <> { 368 | 369 | public <>() { 370 | super(); 371 | } 372 | 373 | public <>(AWSCredentialsProvider credProvider) { 374 | super(credProvider); 375 | } 376 | 377 | public <>(ClientConfiguration config) { 378 | super(config); 379 | } 380 | 381 | public <>(AWSCredentialsProvider credProvider, ClientConfiguration config) { 382 | super(credProvider, config); 383 | } 384 | 385 | <> 386 | 387 | @Override 388 | protected void init() { 389 | setEndpoint("<>"); 390 | <> 391 | HandlerChainFactory chainFactory = new HandlerChainFactory(); 392 | requestHandler2s.addAll(chainFactory.newRequestHandlerChain( 393 | "/<>/request.handlers")); 394 | requestHandler2s.addAll(chainFactory.newRequestHandler2Chain( 395 | "/<>/request.handler2s")); 396 | } 397 | """ 398 | 399 | def mkInitPagination(pagination: List[Pagination]): String = { 400 | pagination.map(p => s"request.${p.requestSetterName}(${p.strCast}null);").mkString("\n ") 401 | } 402 | def mkTokenParameters(pagination: List[Pagination]): String = { 403 | pagination.map(p => s"""((request.${p.requestGetterName}() == null || request.${p.requestGetterName}().equals(\"\")) ? null : request.${p.requestGetterName}().toString())""").mkString(", ") 404 | } 405 | def mkUpdatePagination(pagination: List[Pagination]): String = { 406 | pagination.map(p => s" request.${p.requestSetterName}(result.${p.resultGetterName}());").mkString("\n") 407 | } 408 | 409 | val paginatedITemplateNoArgs = """ 410 | public Observable>>> <>(); 411 | """ 412 | 413 | val paginatedITemplate = """ 414 | public Observable>>> <>( 415 | <> request 416 | ); 417 | """ 418 | 419 | val paginatedTemplateNoArgs = """ 420 | @Override 421 | public Observable>>> <>() { 422 | return <>(new <>()); 423 | } 424 | """ 425 | 426 | val paginatedTemplate = """ 427 | @Override 428 | public Observable>>> <>( 429 | final <> request 430 | ) { 431 | final AtomicInteger cntRef = new AtomicInteger(0); 432 | <> 433 | return Observable.using( 434 | () -> { return null; }, 435 | (create) -> { 436 | long startTime = System.currentTimeMillis(); 437 | String token = mkToken(<>); 438 | return Observable.just(request) 439 | .observeOn(RxSchedulers.computation()) 440 | .flatMap(r -> { 441 | if (token == null && cntRef.get() > 0) return Observable.just(null); 442 | else { 443 | ExecutionContext executionContext = createExecutionContext(r); 444 | AWSRequestMetrics awsRequestMetrics = executionContext.getAwsRequestMetrics(); 445 | Request<<>> mReq = new <>Marshaller().marshall(beforeMarshalling(r)); 446 | mReq.setAWSRequestMetrics(awsRequestMetrics); 447 | <><>Unmarshaller unmarshaller = <><>Unmarshaller.getInstance(); 448 | return invoke<>(mReq, unmarshaller, exceptionUnmarshallers, executionContext) 449 | .doOnNext(result -> { 450 | <> 451 | }) 452 | .map(result -> { 453 | return new PaginatedServiceResult<<>>(startTime, token, result); 454 | }); 455 | } 456 | }); 457 | }, 458 | (dispose) -> { 459 | cntRef.incrementAndGet(); 460 | } 461 | ) 462 | .repeat() 463 | .takeWhile(result -> { 464 | return result != null; 465 | }); 466 | } 467 | """ 468 | 469 | val simpleITemplateNoArgs = """ 470 | public Observable>>> <>(); 471 | """ 472 | 473 | val simpleITemplate = """ 474 | public Observable>>> <>( 475 | <> request 476 | ); 477 | """ 478 | 479 | val simpleTemplateNoArgs = """ 480 | @Override 481 | public Observable>>> <>() { 482 | return <>(new <>()); 483 | } 484 | """ 485 | 486 | val simpleTemplate = """ 487 | @Override 488 | public Observable>>> <>( 489 | final <> request 490 | ) { 491 | return Observable.just(request) 492 | .observeOn(RxSchedulers.computation()) 493 | .flatMap(r -> { 494 | long startTime = System.currentTimeMillis(); 495 | ExecutionContext executionContext = createExecutionContext(r); 496 | AWSRequestMetrics awsRequestMetrics = executionContext.getAwsRequestMetrics(); 497 | Request<<>> mReq = new <>Marshaller().marshall(r); 498 | mReq.setAWSRequestMetrics(awsRequestMetrics); 499 | <><>Unmarshaller unmarshaller = <><>Unmarshaller.getInstance(); 500 | return invoke<>(mReq, unmarshaller, exceptionUnmarshallers, executionContext) 501 | .map(result -> { 502 | return new ServiceResult<<>>(startTime, result); 503 | }); 504 | }); 505 | } 506 | """ 507 | 508 | val voidITemplateNoArgs = """ 509 | public Observable>>> <>(); 510 | """ 511 | 512 | val voidITemplate = """ 513 | public Observable>>> <>( 514 | <> request 515 | ); 516 | """ 517 | 518 | val voidTemplateNoArgs = """ 519 | | @Override 520 | | public Observable>>> <>() 521 | | return <>(new <>()); 522 | | } 523 | |""".stripMargin 524 | 525 | val voidTemplate = """ 526 | | @Override 527 | | public Observable>>> <>( 528 | | final <> request 529 | | ) { 530 | | return Observable.just(request) 531 | | .observeOn(RxSchedulers.computation()) 532 | | .flatMap(r -> { 533 | | long startTime = System.currentTimeMillis(); 534 | | ExecutionContext executionContext = createExecutionContext(r); 535 | | AWSRequestMetrics awsRequestMetrics = executionContext.getAwsRequestMetrics(); 536 | | Request<<>> mReq = new <>Marshaller().marshall(r); 537 | | mReq.setAWSRequestMetrics(awsRequestMetrics); 538 | | Unmarshaller>UnmarshallerContext> unmarshaller = (Unmarshaller>UnmarshallerContext>) null; 539 | | return invoke<>(mReq, unmarshaller, exceptionUnmarshallers, executionContext) 540 | | .map(result -> { 541 | | return new ServiceResult<<>>(startTime, result); 542 | | }); 543 | | }); 544 | | } 545 | |""".stripMargin 546 | 547 | val iFooterTemplate = """ 548 | |} 549 | |""".stripMargin 550 | 551 | val footerTemplate = """ 552 | |} 553 | |""".stripMargin 554 | 555 | def mkIExtraMethods(c: ClientInfo): String = { 556 | extraMethods.get(c.name.toLowerCase).map(_.head).getOrElse("") 557 | } 558 | 559 | def mkExtraMethods(c: ClientInfo): String = { 560 | extraMethods.get(c.name.toLowerCase).map(_.last).getOrElse("") 561 | } 562 | 563 | val extraMethods = Map( 564 | "elasticloadbalancing" -> List( 565 | """ 566 | | public Observable> describeInstanceHealth(); 567 | | 568 | | public Observable> describeLoadBalancerAttributes(); 569 | |""".stripMargin, 570 | """ 571 | | public Observable> describeInstanceHealth() { 572 | | return describeLoadBalancers().flatMap(r0 -> { 573 | | return Observable.from(r0.result.getLoadBalancerDescriptions()); 574 | | }).flatMap(elb -> { 575 | | String elbName = elb.getLoadBalancerName(); 576 | | return describeInstanceHealth( 577 | | new DescribeInstanceHealthRequest().withLoadBalancerName(elbName) 578 | | ).map(r1 -> { 579 | | return new NamedServiceResult(r1.startTime, elbName, r1.result); 580 | | }); 581 | | }); 582 | | } 583 | | 584 | | public Observable> describeLoadBalancerAttributes() { 585 | | return describeLoadBalancers().flatMap(r0 -> { 586 | | return Observable.from(r0.result.getLoadBalancerDescriptions()); 587 | | }).flatMap(elb -> { 588 | | String elbName = elb.getLoadBalancerName(); 589 | | return describeLoadBalancerAttributes( 590 | | new DescribeLoadBalancerAttributesRequest().withLoadBalancerName(elbName) 591 | | ).map(r1 -> { 592 | | return new NamedServiceResult(r1.startTime, elbName, r1.result); 593 | | }); 594 | | }); 595 | | } 596 | |""".stripMargin 597 | ), 598 | "route53" -> List( 599 | """ 600 | | public Observable> listResourceRecordSets(); 601 | |""".stripMargin, 602 | """ 603 | | public Observable> listResourceRecordSets() { 604 | | return listHostedZones().reduce(new ArrayList(), (acc, r) -> { 605 | | r.result.getHostedZones().stream().forEach(h -> acc.add(h.getId())); 606 | | return acc; 607 | | }) 608 | | .flatMap(list -> { 609 | | if (list.size() == 0) { 610 | | return Observable.just(new PaginatedServiceResult( 611 | | System.currentTimeMillis(), null, new ListResourceRecordSetsResult() 612 | | )); 613 | | } 614 | | else { 615 | | return Observable.from(list) 616 | | .flatMap(id -> { 617 | | return listResourceRecordSets(new ListResourceRecordSetsRequest().withHostedZoneId(id)); 618 | | }); 619 | | } 620 | | }); 621 | | } 622 | |""".stripMargin 623 | ) 624 | ) 625 | } 626 | -------------------------------------------------------------------------------- /project/Bintray.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import sbt.Keys._ 3 | import bintray.BintrayPlugin._ 4 | import bintray.BintrayCredentials.api 5 | import bintray.BintrayKeys._ 6 | 7 | object Bintray { 8 | 9 | lazy val now = System.currentTimeMillis 10 | lazy val isPullRequest = sys.env.getOrElse("TRAVIS_PULL_REQUEST", "false") != "false" 11 | 12 | private def get(k: String): String = { 13 | if (isPullRequest) s"dummy$k" else sys.env.getOrElse(s"bintray$k", s"missing$k") 14 | } 15 | 16 | lazy val user = get("User") 17 | lazy val pass = get("Key") 18 | 19 | lazy val storeBintrayCredentials = taskKey[Unit]("store bintray credentials") 20 | 21 | lazy val settings: Seq[Def.Setting[_]] = bintraySettings ++ Seq( 22 | bintrayRepository := "maven", 23 | bintrayPackage := "rx-aws-java-sdk", 24 | bintrayOrganization := Some("netflixoss"), 25 | bintrayReleaseOnPublish := false, 26 | licenses += ("Apache-2.0", url("https://www.apache.org/licenses/LICENSE-2.0.txt")), 27 | credentials += Credentials("Artifactory Realm", "oss.jfrog.org", user, pass), 28 | 29 | publishTo := { 30 | if (isSnapshot.value) 31 | Some("OJO" at s"https://oss.jfrog.org/oss-snapshot-local;build.timestamp=${now}/") 32 | else 33 | publishTo in bintray value 34 | }, 35 | 36 | storeBintrayCredentials := { 37 | IO.write(bintrayCredentialsFile.value, api.template(user, pass)) 38 | }, 39 | 40 | pomExtra := ( 41 | https://github.com/netflix/rx-aws-java-sdk/wiki 42 | 43 | git@github.com:netflix/rx-aws-java-sdk.git 44 | scm:git:git@github.com:netflix/rx-aws-java-sdk.git 45 | 46 | 47 | 48 | bpitman 49 | Brent Pitman 50 | bpitman@netflix.com 51 | 52 | 53 | ) 54 | ) 55 | } 56 | -------------------------------------------------------------------------------- /project/Build.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import sbt.Keys._ 3 | 4 | object MainBuild extends Build { 5 | 6 | lazy val baseSettings = 7 | sbtrelease.ReleasePlugin.releaseSettings ++ 8 | GitVersion.settings ++ 9 | Bintray.settings ++ 10 | net.virtualvoid.sbt.graph.Plugin.graphSettings ++ 11 | scoverage.ScoverageSbtPlugin.projectSettings 12 | 13 | lazy val buildSettings = baseSettings ++ Seq( 14 | organization := BuildSettings.organization, 15 | scalaVersion := Dependencies.Versions.scala, 16 | scalacOptions ++= BuildSettings.compilerFlags, 17 | javacOptions ++= BuildSettings.javaCompilerFlags, 18 | javacOptions in doc := BuildSettings.javadocFlags, 19 | crossPaths := false, 20 | sourcesInBase := false, 21 | fork in Test := true, 22 | autoScalaLibrary := false, 23 | externalResolvers := BuildSettings.resolvers, 24 | checkLicenseHeaders := License.checkLicenseHeaders(streams.value.log, sourceDirectory.value), 25 | formatLicenseHeaders := License.formatLicenseHeaders(streams.value.log, sourceDirectory.value) 26 | ) 27 | 28 | lazy val root = project.in(file(".")) 29 | .aggregate( 30 | `rx-aws-java-sdk-core`, 31 | `rx-aws-java-sdk-autoscaling`, 32 | `rx-aws-java-sdk-cloudformation`, 33 | `rx-aws-java-sdk-cloudfront`, 34 | `rx-aws-java-sdk-cloudsearch`, 35 | // `rx-aws-java-sdk-cloudsearchdomain`, 36 | `rx-aws-java-sdk-cloudwatch`, 37 | `rx-aws-java-sdk-codedeploy`, 38 | `rx-aws-java-sdk-cognitoidentity`, 39 | `rx-aws-java-sdk-cognitosync`, 40 | `rx-aws-java-sdk-config`, 41 | `rx-aws-java-sdk-directconnect`, 42 | `rx-aws-java-sdk-dynamodb`, 43 | `rx-aws-java-sdk-ec2`, 44 | `rx-aws-java-sdk-ecs`, 45 | `rx-aws-java-sdk-elasticache`, 46 | `rx-aws-java-sdk-elasticloadbalancing`, 47 | `rx-aws-java-sdk-emr`, 48 | `rx-aws-java-sdk-elastictranscoder`, 49 | `rx-aws-java-sdk-glacier`, 50 | `rx-aws-java-sdk-iam`, 51 | `rx-aws-java-sdk-importexport`, 52 | `rx-aws-java-sdk-kinesis`, 53 | `rx-aws-java-sdk-rds`, 54 | `rx-aws-java-sdk-redshift`, 55 | `rx-aws-java-sdk-route53`, 56 | // `rx-aws-java-sdk-route53domains`, 57 | //`rx-aws-java-sdk-s3`, 58 | //`rx-aws-java-sdk-s3encryption`, 59 | //`rx-aws-java-sdk-simpledb`, 60 | `rx-aws-java-sdk-ses`, 61 | `rx-aws-java-sdk-simpleworkflow`, 62 | `rx-aws-java-sdk-sns`, 63 | `rx-aws-java-sdk-sqs`, 64 | `rx-aws-java-sdk-sts`, 65 | `rx-aws-java-sdk` 66 | ) 67 | .settings(buildSettings: _*) 68 | .settings(BuildSettings.noPackaging: _*) 69 | 70 | lazy val `rx-aws-java-sdk-core` = project 71 | .settings(buildSettings: _*) 72 | .settings(libraryDependencies ++= commonDeps) 73 | .settings(libraryDependencies ++= Seq( 74 | Dependencies.awsCore, 75 | Dependencies.guava, 76 | Dependencies.jzlib, 77 | Dependencies.rxjava, 78 | Dependencies.rxnettyCore, 79 | Dependencies.rxnettyCtxts, 80 | Dependencies.spectatorApi, 81 | Dependencies.slf4jApi, 82 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 83 | )) 84 | 85 | lazy val `rx-aws-java-sdk-autoscaling` = project 86 | .dependsOn(`rx-aws-java-sdk-core`) 87 | .settings(buildSettings: _*) 88 | .settings(libraryDependencies ++= commonDeps) 89 | .settings(libraryDependencies ++= Seq( 90 | Dependencies.awsAutoScaling, 91 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 92 | )) 93 | .settings( 94 | sourceGenerators in Compile <+= Def.task { 95 | AwsGenerate.generate((sourceManaged in Compile).value, List("autoscaling")) 96 | } 97 | ) 98 | 99 | lazy val `rx-aws-java-sdk-cloudformation` = project 100 | .dependsOn(`rx-aws-java-sdk-core`) 101 | .settings(buildSettings: _*) 102 | .settings(libraryDependencies ++= commonDeps) 103 | .settings(libraryDependencies ++= Seq( 104 | Dependencies.awsCloudFormation, 105 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 106 | )) 107 | .settings( 108 | sourceGenerators in Compile <+= Def.task { 109 | AwsGenerate.generate((sourceManaged in Compile).value, List("cloudformation")) 110 | } 111 | ) 112 | 113 | lazy val `rx-aws-java-sdk-cloudfront` = project 114 | .dependsOn(`rx-aws-java-sdk-core`) 115 | .settings(buildSettings: _*) 116 | .settings(libraryDependencies ++= commonDeps) 117 | .settings(libraryDependencies ++= Seq( 118 | Dependencies.awsCloudFront, 119 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 120 | )) 121 | .settings( 122 | sourceGenerators in Compile <+= Def.task { 123 | AwsGenerate.generate((sourceManaged in Compile).value, List("cloudfront")) 124 | } 125 | ) 126 | 127 | lazy val `rx-aws-java-sdk-cloudsearch` = project 128 | .dependsOn(`rx-aws-java-sdk-core`) 129 | .settings(buildSettings: _*) 130 | .settings(libraryDependencies ++= commonDeps) 131 | .settings(libraryDependencies ++= Seq( 132 | Dependencies.awsCloudSearch, 133 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 134 | )) 135 | .settings( 136 | sourceGenerators in Compile <+= Def.task { 137 | AwsGenerate.generate((sourceManaged in Compile).value, List("cloudsearch", "cloudsearchdomain")) 138 | } 139 | ) 140 | 141 | /** 142 | lazy val `rx-aws-java-sdk-cloudsearchdomain` = project 143 | .dependsOn(`rx-aws-java-sdk-core`) 144 | .settings(buildSettings: _*) 145 | .settings(libraryDependencies ++= commonDeps) 146 | .settings(libraryDependencies ++= Seq( 147 | Dependencies.awsCloudSearchDomain, 148 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 149 | )) 150 | .settings( 151 | sourceGenerators in Compile <+= Def.task { 152 | AwsGenerate.generate((sourceManaged in Compile).value, List("cloudsearchdomain")) 153 | } 154 | ) 155 | */ 156 | 157 | lazy val `rx-aws-java-sdk-cloudwatch` = project 158 | .dependsOn(`rx-aws-java-sdk-core`) 159 | .settings(buildSettings: _*) 160 | .settings(libraryDependencies ++= commonDeps) 161 | .settings(libraryDependencies ++= Seq( 162 | Dependencies.awsCloudWatch, 163 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 164 | )) 165 | .settings( 166 | sourceGenerators in Compile <+= Def.task { 167 | AwsGenerate.generate((sourceManaged in Compile).value, List("cloudwatch")) 168 | } 169 | ) 170 | 171 | lazy val `rx-aws-java-sdk-codedeploy` = project 172 | .dependsOn(`rx-aws-java-sdk-core`) 173 | .settings(buildSettings: _*) 174 | .settings(libraryDependencies ++= commonDeps) 175 | .settings(libraryDependencies ++= Seq( 176 | Dependencies.awsCodeDeploy, 177 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 178 | )) 179 | .settings( 180 | sourceGenerators in Compile <+= Def.task { 181 | AwsGenerate.generate((sourceManaged in Compile).value, List("codedeploy")) 182 | } 183 | ) 184 | 185 | lazy val `rx-aws-java-sdk-cognitoidentity` = project 186 | .dependsOn(`rx-aws-java-sdk-core`) 187 | .settings(buildSettings: _*) 188 | .settings(libraryDependencies ++= commonDeps) 189 | .settings(libraryDependencies ++= Seq( 190 | Dependencies.awsCognitoIdentity, 191 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 192 | )) 193 | .settings( 194 | sourceGenerators in Compile <+= Def.task { 195 | AwsGenerate.generate((sourceManaged in Compile).value, List("cognitoidentity")) 196 | } 197 | ) 198 | 199 | lazy val `rx-aws-java-sdk-cognitosync` = project 200 | .dependsOn(`rx-aws-java-sdk-core`) 201 | .settings(buildSettings: _*) 202 | .settings(libraryDependencies ++= commonDeps) 203 | .settings(libraryDependencies ++= Seq( 204 | Dependencies.awsCognitoSync, 205 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 206 | )) 207 | .settings( 208 | sourceGenerators in Compile <+= Def.task { 209 | AwsGenerate.generate((sourceManaged in Compile).value, List("cognitosync")) 210 | } 211 | ) 212 | 213 | lazy val `rx-aws-java-sdk-config` = project 214 | .dependsOn(`rx-aws-java-sdk-core`) 215 | .settings(buildSettings: _*) 216 | .settings(libraryDependencies ++= commonDeps) 217 | .settings(libraryDependencies ++= Seq( 218 | Dependencies.awsConfig, 219 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 220 | )) 221 | .settings( 222 | sourceGenerators in Compile <+= Def.task { 223 | AwsGenerate.generate((sourceManaged in Compile).value, List("config")) 224 | } 225 | ) 226 | 227 | lazy val `rx-aws-java-sdk-directconnect` = project 228 | .dependsOn(`rx-aws-java-sdk-core`) 229 | .settings(buildSettings: _*) 230 | .settings(libraryDependencies ++= commonDeps) 231 | .settings(libraryDependencies ++= Seq( 232 | Dependencies.awsDirectConnect, 233 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 234 | )) 235 | .settings( 236 | sourceGenerators in Compile <+= Def.task { 237 | AwsGenerate.generate((sourceManaged in Compile).value, List("directconnect")) 238 | } 239 | ) 240 | 241 | lazy val `rx-aws-java-sdk-dynamodb` = project 242 | .dependsOn(`rx-aws-java-sdk-core`) 243 | .settings(buildSettings: _*) 244 | .settings(libraryDependencies ++= commonDeps) 245 | .settings(libraryDependencies ++= Seq( 246 | Dependencies.awsDynamoDB, 247 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 248 | )) 249 | .settings( 250 | sourceGenerators in Compile <+= Def.task { 251 | AwsGenerate.generate((sourceManaged in Compile).value, List("dynamodb", "dynamodbv2")) 252 | } 253 | ) 254 | 255 | lazy val `rx-aws-java-sdk-ec2` = project 256 | .dependsOn(`rx-aws-java-sdk-core`) 257 | .settings(buildSettings: _*) 258 | .settings(libraryDependencies ++= commonDeps) 259 | .settings(libraryDependencies ++= Seq( 260 | Dependencies.awsEC2, 261 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 262 | )) 263 | .settings( 264 | sourceGenerators in Compile <+= Def.task { 265 | AwsGenerate.generate((sourceManaged in Compile).value, List("ec2")) 266 | } 267 | ) 268 | 269 | lazy val `rx-aws-java-sdk-ecs` = project 270 | .dependsOn(`rx-aws-java-sdk-core`) 271 | .settings(buildSettings: _*) 272 | .settings(libraryDependencies ++= commonDeps) 273 | .settings(libraryDependencies ++= Seq( 274 | Dependencies.awsECS, 275 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 276 | )) 277 | .settings( 278 | sourceGenerators in Compile <+= Def.task { 279 | AwsGenerate.generate((sourceManaged in Compile).value, List("ecs")) 280 | } 281 | ) 282 | 283 | lazy val `rx-aws-java-sdk-elasticache` = project 284 | .dependsOn(`rx-aws-java-sdk-core`) 285 | .settings(buildSettings: _*) 286 | .settings(libraryDependencies ++= commonDeps) 287 | .settings(libraryDependencies ++= Seq( 288 | Dependencies.awsElasticache, 289 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 290 | )) 291 | .settings( 292 | sourceGenerators in Compile <+= Def.task { 293 | AwsGenerate.generate((sourceManaged in Compile).value, List("elasticache")) 294 | } 295 | ) 296 | 297 | lazy val `rx-aws-java-sdk-elasticloadbalancing` = project 298 | .dependsOn(`rx-aws-java-sdk-core`) 299 | .settings(buildSettings: _*) 300 | .settings(libraryDependencies ++= commonDeps) 301 | .settings(libraryDependencies ++= Seq( 302 | Dependencies.awsElasticLoadBalancing, 303 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 304 | )) 305 | .settings( 306 | sourceGenerators in Compile <+= Def.task { 307 | AwsGenerate.generate((sourceManaged in Compile).value, List("elasticloadbalancing")) 308 | } 309 | ) 310 | 311 | lazy val `rx-aws-java-sdk-emr` = project 312 | .dependsOn(`rx-aws-java-sdk-core`) 313 | .settings(buildSettings: _*) 314 | .settings(libraryDependencies ++= commonDeps) 315 | .settings(libraryDependencies ++= Seq( 316 | Dependencies.awsElasticMapReduce, 317 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 318 | )) 319 | .settings( 320 | sourceGenerators in Compile <+= Def.task { 321 | AwsGenerate.generate((sourceManaged in Compile).value, List("elasticmapreduce")) 322 | } 323 | ) 324 | 325 | lazy val `rx-aws-java-sdk-elastictranscoder` = project 326 | .dependsOn(`rx-aws-java-sdk-core`) 327 | .settings(buildSettings: _*) 328 | .settings(libraryDependencies ++= commonDeps) 329 | .settings(libraryDependencies ++= Seq( 330 | Dependencies.awsElasticTranscoder, 331 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 332 | )) 333 | .settings( 334 | sourceGenerators in Compile <+= Def.task { 335 | AwsGenerate.generate((sourceManaged in Compile).value, List("elastictranscoder")) 336 | } 337 | ) 338 | 339 | lazy val `rx-aws-java-sdk-glacier` = project 340 | .dependsOn(`rx-aws-java-sdk-core`) 341 | .settings(buildSettings: _*) 342 | .settings(libraryDependencies ++= commonDeps) 343 | .settings(libraryDependencies ++= Seq( 344 | Dependencies.awsGlacier, 345 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 346 | )) 347 | .settings( 348 | sourceGenerators in Compile <+= Def.task { 349 | AwsGenerate.generate((sourceManaged in Compile).value, List("glacier")) 350 | } 351 | ) 352 | 353 | lazy val `rx-aws-java-sdk-iam` = project 354 | .dependsOn(`rx-aws-java-sdk-core`) 355 | .settings(buildSettings: _*) 356 | .settings(libraryDependencies ++= commonDeps) 357 | .settings(libraryDependencies ++= Seq( 358 | Dependencies.awsIdentityManagement, 359 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 360 | )) 361 | .settings( 362 | sourceGenerators in Compile <+= Def.task { 363 | AwsGenerate.generate((sourceManaged in Compile).value, List("identitymanagement")) 364 | } 365 | ) 366 | 367 | lazy val `rx-aws-java-sdk-importexport` = project 368 | .dependsOn(`rx-aws-java-sdk-core`) 369 | .settings(buildSettings: _*) 370 | .settings(libraryDependencies ++= commonDeps) 371 | .settings(libraryDependencies ++= Seq( 372 | Dependencies.awsImportExport, 373 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 374 | )) 375 | .settings( 376 | sourceGenerators in Compile <+= Def.task { 377 | AwsGenerate.generate((sourceManaged in Compile).value, List("importexport")) 378 | } 379 | ) 380 | 381 | lazy val `rx-aws-java-sdk-kinesis` = project 382 | .dependsOn(`rx-aws-java-sdk-core`) 383 | .settings(buildSettings: _*) 384 | .settings(libraryDependencies ++= commonDeps) 385 | .settings(libraryDependencies ++= Seq( 386 | Dependencies.awsKinesis, 387 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 388 | )) 389 | .settings( 390 | sourceGenerators in Compile <+= Def.task { 391 | AwsGenerate.generate((sourceManaged in Compile).value, List("kinesis")) 392 | } 393 | ) 394 | 395 | lazy val `rx-aws-java-sdk-rds` = project 396 | .dependsOn(`rx-aws-java-sdk-core`) 397 | .settings(buildSettings: _*) 398 | .settings(libraryDependencies ++= commonDeps) 399 | .settings(libraryDependencies ++= Seq( 400 | Dependencies.awsRDS, 401 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 402 | )) 403 | .settings( 404 | sourceGenerators in Compile <+= Def.task { 405 | AwsGenerate.generate((sourceManaged in Compile).value, List("rds")) 406 | } 407 | ) 408 | 409 | lazy val `rx-aws-java-sdk-redshift` = project 410 | .dependsOn(`rx-aws-java-sdk-core`) 411 | .settings(buildSettings: _*) 412 | .settings(libraryDependencies ++= commonDeps) 413 | .settings(libraryDependencies ++= Seq( 414 | Dependencies.awsRedShift, 415 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 416 | )) 417 | .settings( 418 | sourceGenerators in Compile <+= Def.task { 419 | AwsGenerate.generate((sourceManaged in Compile).value, List("redshift")) 420 | } 421 | ) 422 | 423 | lazy val `rx-aws-java-sdk-route53` = project 424 | .dependsOn(`rx-aws-java-sdk-core`) 425 | .settings(buildSettings: _*) 426 | .settings(libraryDependencies ++= commonDeps) 427 | .settings(libraryDependencies ++= Seq( 428 | Dependencies.awsRoute53, 429 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 430 | )) 431 | .settings( 432 | sourceGenerators in Compile <+= Def.task { 433 | AwsGenerate.generate((sourceManaged in Compile).value, List("route53", "route53domains")) 434 | } 435 | ) 436 | 437 | /** 438 | lazy val `rx-aws-java-sdk-route53domains` = project 439 | .dependsOn(`rx-aws-java-sdk-core`) 440 | .settings(buildSettings: _*) 441 | .settings(libraryDependencies ++= commonDeps) 442 | .settings(libraryDependencies ++= Seq( 443 | Dependencies.awsRoute53Domains, 444 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 445 | )) 446 | .settings( 447 | sourceGenerators in Compile <+= Def.task { 448 | AwsGenerate.generate((sourceManaged in Compile).value, List("route53domains")) 449 | } 450 | ) 451 | */ 452 | 453 | lazy val `rx-aws-java-sdk-s3` = project 454 | .dependsOn(`rx-aws-java-sdk-core`) 455 | .settings(buildSettings: _*) 456 | .settings(libraryDependencies ++= commonDeps) 457 | .settings(libraryDependencies ++= Seq( 458 | Dependencies.awsS3, 459 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 460 | )) 461 | .settings( 462 | sourceGenerators in Compile <+= Def.task { 463 | AwsGenerate.generate((sourceManaged in Compile).value, List("s3")) 464 | } 465 | ) 466 | 467 | lazy val `rx-aws-java-sdk-s3encryption` = project 468 | .dependsOn(`rx-aws-java-sdk-core`) 469 | .settings(buildSettings: _*) 470 | .settings(libraryDependencies ++= commonDeps) 471 | .settings(libraryDependencies ++= Seq( 472 | Dependencies.awsS3Encryption, 473 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 474 | )) 475 | .settings( 476 | sourceGenerators in Compile <+= Def.task { 477 | AwsGenerate.generate((sourceManaged in Compile).value, List("s3encryption")) 478 | } 479 | ) 480 | 481 | lazy val `rx-aws-java-sdk-simpledb` = project 482 | .dependsOn(`rx-aws-java-sdk-core`) 483 | .settings(buildSettings: _*) 484 | .settings(libraryDependencies ++= commonDeps) 485 | .settings(libraryDependencies ++= Seq( 486 | Dependencies.awsSimpleDB, 487 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 488 | )) 489 | .settings( 490 | sourceGenerators in Compile <+= Def.task { 491 | AwsGenerate.generate((sourceManaged in Compile).value, List("simpledb")) 492 | } 493 | ) 494 | 495 | lazy val `rx-aws-java-sdk-ses` = project 496 | .dependsOn(`rx-aws-java-sdk-core`) 497 | .settings(buildSettings: _*) 498 | .settings(libraryDependencies ++= commonDeps) 499 | .settings(libraryDependencies ++= Seq( 500 | Dependencies.awsSimpleEmailService, 501 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 502 | )) 503 | .settings( 504 | sourceGenerators in Compile <+= Def.task { 505 | AwsGenerate.generate((sourceManaged in Compile).value, List("simpleemail")) 506 | } 507 | ) 508 | 509 | lazy val `rx-aws-java-sdk-simpleworkflow` = project 510 | .dependsOn(`rx-aws-java-sdk-core`) 511 | .settings(buildSettings: _*) 512 | .settings(libraryDependencies ++= commonDeps) 513 | .settings(libraryDependencies ++= Seq( 514 | Dependencies.awsSimpleWorkflow, 515 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 516 | )) 517 | .settings( 518 | sourceGenerators in Compile <+= Def.task { 519 | AwsGenerate.generate((sourceManaged in Compile).value, List("simpleworkflow")) 520 | } 521 | ) 522 | 523 | lazy val `rx-aws-java-sdk-sns` = project 524 | .dependsOn(`rx-aws-java-sdk-core`) 525 | .settings(buildSettings: _*) 526 | .settings(libraryDependencies ++= commonDeps) 527 | .settings(libraryDependencies ++= Seq( 528 | Dependencies.awsSNS, 529 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 530 | )) 531 | .settings( 532 | sourceGenerators in Compile <+= Def.task { 533 | AwsGenerate.generate((sourceManaged in Compile).value, List("sns")) 534 | } 535 | ) 536 | 537 | lazy val `rx-aws-java-sdk-sqs` = project 538 | .dependsOn(`rx-aws-java-sdk-core`) 539 | .settings(buildSettings: _*) 540 | .settings(libraryDependencies ++= commonDeps) 541 | .settings(libraryDependencies ++= Seq( 542 | Dependencies.awsSQS, 543 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 544 | )) 545 | .settings( 546 | sourceGenerators in Compile <+= Def.task { 547 | AwsGenerate.generate((sourceManaged in Compile).value, List("sqs")) 548 | } 549 | ) 550 | 551 | lazy val `rx-aws-java-sdk-sts` = project 552 | .dependsOn(`rx-aws-java-sdk-core`) 553 | .settings(buildSettings: _*) 554 | .settings(libraryDependencies ++= commonDeps) 555 | .settings(libraryDependencies ++= Seq( 556 | Dependencies.awsSTS, 557 | "io.reactivex" %% "rxscala" % "0.25.0" % "test" 558 | )) 559 | .settings( 560 | sourceGenerators in Compile <+= Def.task { 561 | AwsGenerate.generate((sourceManaged in Compile).value, List("sts")) 562 | } 563 | ) 564 | 565 | lazy val `rx-aws-java-sdk` = project 566 | .dependsOn(`rx-aws-java-sdk-autoscaling`) 567 | .dependsOn(`rx-aws-java-sdk-cloudformation`) 568 | .dependsOn(`rx-aws-java-sdk-cloudfront`) 569 | .dependsOn(`rx-aws-java-sdk-cloudsearch`) 570 | // .dependsOn(`rx-aws-java-sdk-cloudsearchdomain`) 571 | .dependsOn(`rx-aws-java-sdk-cloudwatch`) 572 | .dependsOn(`rx-aws-java-sdk-codedeploy`) 573 | .dependsOn(`rx-aws-java-sdk-cognitoidentity`) 574 | .dependsOn(`rx-aws-java-sdk-cognitosync`) 575 | .dependsOn(`rx-aws-java-sdk-config`) 576 | .dependsOn(`rx-aws-java-sdk-directconnect`) 577 | .dependsOn(`rx-aws-java-sdk-dynamodb`) 578 | .dependsOn(`rx-aws-java-sdk-ec2`) 579 | .dependsOn(`rx-aws-java-sdk-ecs`) 580 | .dependsOn(`rx-aws-java-sdk-elasticache`) 581 | .dependsOn(`rx-aws-java-sdk-elasticloadbalancing`) 582 | .dependsOn(`rx-aws-java-sdk-emr`) 583 | .dependsOn(`rx-aws-java-sdk-elastictranscoder`) 584 | .dependsOn(`rx-aws-java-sdk-glacier`) 585 | .dependsOn(`rx-aws-java-sdk-iam`) 586 | .dependsOn(`rx-aws-java-sdk-importexport`) 587 | .dependsOn(`rx-aws-java-sdk-kinesis`) 588 | .dependsOn(`rx-aws-java-sdk-rds`) 589 | .dependsOn(`rx-aws-java-sdk-redshift`) 590 | .dependsOn(`rx-aws-java-sdk-route53`) 591 | // .dependsOn(`rx-aws-java-sdk-route53domains`) 592 | //.dependsOn(`rx-aws-java-sdk-s3`) 593 | //.dependsOn(`rx-aws-java-sdk-s3encryption`) 594 | //.dependsOn(`rx-aws-java-sdk-simpledb`) 595 | .dependsOn(`rx-aws-java-sdk-ses`) 596 | .dependsOn(`rx-aws-java-sdk-simpleworkflow`) 597 | .dependsOn(`rx-aws-java-sdk-sns`) 598 | .dependsOn(`rx-aws-java-sdk-sqs`) 599 | .dependsOn(`rx-aws-java-sdk-sts`) 600 | .settings(buildSettings: _*) 601 | .settings(libraryDependencies ++= commonDeps) 602 | 603 | lazy val commonDeps = Seq( 604 | Dependencies.junitInterface % "test", 605 | Dependencies.scalatest % "test", 606 | Dependencies.slf4jBinding % "test" 607 | ) 608 | 609 | lazy val checkLicenseHeaders = taskKey[Unit]("Check the license headers for all source files.") 610 | lazy val formatLicenseHeaders = taskKey[Unit]("Fix the license headers for all source files.") 611 | } 612 | -------------------------------------------------------------------------------- /project/BuildSettings.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import sbt.Keys._ 3 | 4 | object BuildSettings { 5 | val organization = "com.netflix.rx-aws-java-sdk" 6 | 7 | val javaCompilerFlags = Seq( 8 | "-source", "1.8", 9 | "-target", "1.8") 10 | 11 | val javadocFlags = Seq("-Xdoclint:none") 12 | 13 | val compilerFlags = Seq( 14 | "-deprecation", 15 | "-unchecked", 16 | "-Xexperimental", 17 | "-Xlint:_,-infer-any", 18 | "-feature", 19 | "-target:jvm-1.8") 20 | 21 | val resolvers = Seq( 22 | Resolver.mavenLocal, 23 | Resolver.jcenterRepo, 24 | "jfrog" at "http://oss.jfrog.org/oss-snapshot-local") 25 | 26 | // Don't create root.jar, from: 27 | // http://stackoverflow.com/questions/20747296/producing-no-artifact-for-root-project-with-package-under-multi-project-build-in 28 | lazy val noPackaging = Seq( 29 | Keys.`package` := file(""), 30 | packageBin in Global := file(""), 31 | packagedArtifacts := Map() 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /project/Dependencies.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | 3 | object Dependencies { 4 | object Versions { 5 | val aws = "1.10.27" 6 | val awsMapper = "1.10.27.0" 7 | val rxj = "1.2.1" 8 | val rxnetty = "0.4.18.28" 9 | val scala = "2.11.8" 10 | val slf4j = "1.7.21" 11 | val spectator = "0.42.0" 12 | } 13 | 14 | import Versions._ 15 | 16 | val awsCore = "com.amazonaws" % "aws-java-sdk-core" % aws 17 | val awsAutoScaling = "com.amazonaws" % "aws-java-sdk-autoscaling" % aws 18 | val awsCloudFormation = "com.amazonaws" % "aws-java-sdk-cloudformation" % aws 19 | val awsCloudFront = "com.amazonaws" % "aws-java-sdk-cloudfront" % aws 20 | val awsCloudSearch = "com.amazonaws" % "aws-java-sdk-cloudsearch" % aws 21 | //val awsCloudSearchDomain = "com.amazonaws" % "aws-java-sdk-cloudsearchdomain" % aws 22 | val awsCloudWatch = "com.amazonaws" % "aws-java-sdk-cloudwatch" % aws 23 | val awsCodeDeploy = "com.amazonaws" % "aws-java-sdk-codedeploy" % aws 24 | val awsCognitoIdentity = "com.amazonaws" % "aws-java-sdk-cognitoidentity" % aws 25 | val awsCognitoSync = "com.amazonaws" % "aws-java-sdk-cognitosync" % aws 26 | val awsConfig = "com.amazonaws" % "aws-java-sdk-config" % aws 27 | val awsDirectConnect = "com.amazonaws" % "aws-java-sdk-directconnect" % aws 28 | val awsDynamoDB = "com.amazonaws" % "aws-java-sdk-dynamodb" % aws 29 | val awsEC2 = "com.amazonaws" % "aws-java-sdk-ec2" % aws 30 | val awsECS = "com.amazonaws" % "aws-java-sdk-ecs" % aws 31 | val awsElasticache = "com.amazonaws" % "aws-java-sdk-elasticache" % aws 32 | val awsElasticLoadBalancing = "com.amazonaws" % "aws-java-sdk-elasticloadbalancing" % aws 33 | val awsElasticMapReduce = "com.amazonaws" % "aws-java-sdk-emr" % aws 34 | val awsElasticTranscoder = "com.amazonaws" % "aws-java-sdk-elastictranscoder" % aws 35 | val awsGlacier = "com.amazonaws" % "aws-java-sdk-glacier" % aws 36 | val awsIdentityManagement = "com.amazonaws" % "aws-java-sdk-iam" % aws 37 | val awsImportExport = "com.amazonaws" % "aws-java-sdk-importexport" % aws 38 | val awsKinesis = "com.amazonaws" % "aws-java-sdk-kinesis" % aws 39 | val awsRDS = "com.amazonaws" % "aws-java-sdk-rds" % aws 40 | val awsRedShift = "com.amazonaws" % "aws-java-sdk-redshift" % aws 41 | val awsRoute53 = "com.amazonaws" % "aws-java-sdk-route53" % aws 42 | //val awsRoute53Domains = "com.amazonaws" % "aws-java-sdk-route53domains" % aws 43 | val awsS3 = "com.amazonaws" % "aws-java-sdk-s3" % aws 44 | val awsS3Encryption = "com.amazonaws" % "aws-java-sdk-s3encryption" % aws 45 | val awsSimpleDB = "com.amazonaws" % "aws-java-sdk-simpledb" % aws 46 | val awsSimpleEmailService = "com.amazonaws" % "aws-java-sdk-ses" % aws 47 | val awsSimpleWorkflow = "com.amazonaws" % "aws-java-sdk-simpleworkflow" % aws 48 | val awsSNS = "com.amazonaws" % "aws-java-sdk-sns" % aws 49 | val awsSQS = "com.amazonaws" % "aws-java-sdk-sqs" % aws 50 | val awsSTS = "com.amazonaws" % "aws-java-sdk-sts" % aws 51 | 52 | val awsObjectMapper = "com.netflix.awsobjectmapper" % "awsobjectmapper" % awsMapper 53 | val guava = "com.google.guava" % "guava" % "18.0" 54 | val inject = "javax.inject" % "javax.inject" % "1" 55 | val junit = "junit" % "junit" % "4.10" 56 | val junitInterface = "com.novocode" % "junit-interface" % "0.11" 57 | val jzlib = "com.jcraft" % "jzlib" % "1.1.3" 58 | val rxjava = "io.reactivex" % "rxjava" % rxj 59 | val rxnettyCore = "com.netflix.iep-shadow" % "iepshadow-rxnetty" % rxnetty 60 | val rxnettyCtxts = "com.netflix.iep-shadow" % "iepshadow-rxnetty-contexts" % rxnetty 61 | val rxnettySpectator= "com.netflix.iep-shadow" % "iepshadow-rxnetty-spectator" % rxnetty 62 | val scalaLibrary = "org.scala-lang" % "scala-library" % scala 63 | val scalaLibraryAll = "org.scala-lang" % "scala-library-all" % scala 64 | val scalaLogging = "com.typesafe.scala-logging" % "scala-logging_2.11" % "3.1.0" 65 | val scalaReflect = "org.scala-lang" % "scala-reflect" % scala 66 | val scalatest = "org.scalatest" % "scalatest_2.11" % "2.2.1" 67 | val slf4jApi = "org.slf4j" % "slf4j-api" % slf4j 68 | val slf4jBinding = "org.slf4j" % "slf4j-log4j12" % slf4j 69 | val spectatorApi = "com.netflix.spectator" % "spectator-api" % spectator 70 | val spectatorSandbox= "com.netflix.spectator" % "spectator-ext-sandbox" % spectator 71 | } 72 | -------------------------------------------------------------------------------- /project/GitVersion.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import sbt.Keys._ 3 | import sbtrelease.Version 4 | import com.typesafe.sbt.SbtGit._ 5 | 6 | object GitVersion { 7 | 8 | // Base version for master branch 9 | private val baseVersion = "v0.1.x" 10 | 11 | // 0.1.x 12 | private val versionBranch = """v?([0-9\.]+)(?:\.x)?""".r 13 | 14 | // v0.1.47-31-g230560c 15 | // v0.1.47-20150807.161518-9 16 | private val snapshotVersion = """v?([0-9\.]+)-(?:\d+)-(?:[0-9a-z]+)""".r 17 | 18 | // 1.5.0-rc.1-123-gcbfe51a 19 | private val candidateVersion = """v?([0-9\.]+)(?:-rc\.\d+)?-(?:\d+)-(?:[0-9a-z]+)""".r 20 | 21 | // v0.1.47 22 | // 1.5.0-rc.1 23 | private val releaseVersion = """v?([0-9\.]+(?:-rc\.\d+)?)""".r 24 | 25 | /** 26 | * Needs to check for "false", don't assume it will ever be set to "true". 27 | * http://docs.travis-ci.com/user/environment-variables/#Default-Environment-Variables 28 | */ 29 | private def isPullRequest: Boolean = sys.env.getOrElse("TRAVIS_PULL_REQUEST", "false") != "false" 30 | 31 | /** 32 | * Bump the last git described version to use for the current snapshot. If it is a version branch 33 | * and the prefix doesn't match, then it is the first snapshot for the branch so use the branch 34 | * version to start with. 35 | */ 36 | private def toSnapshotVersion(branch: String, v: String): String = { 37 | val v2 = Version(v).map(_.bump.string).getOrElse(v) 38 | val suffix = "-SNAPSHOT" 39 | branch match { 40 | case versionBranch(b) if !v2.startsWith(b) => 41 | s"${Version(s"$b.0").map(_.string).getOrElse(v2)}$suffix" 42 | case _ => 43 | s"$v2$suffix" 44 | } 45 | } 46 | 47 | lazy val settings: Seq[Def.Setting[_]] = Seq( 48 | version in ThisBuild := { 49 | val branch = sys.env.getOrElse("TRAVIS_BRANCH", git.gitCurrentBranch.value) 50 | val branchVersion = if (branch == "master") baseVersion else branch 51 | git.gitDescribedVersion.value getOrElse "0.1-SNAPSHOT" match { 52 | case _ if isPullRequest => s"0.0.0-PULLREQUEST" 53 | case snapshotVersion(v) => toSnapshotVersion(branchVersion, v) 54 | case candidateVersion(v) => s"$v-SNAPSHOT" 55 | case releaseVersion(v) => v 56 | case v => v 57 | } 58 | } 59 | ) 60 | } 61 | -------------------------------------------------------------------------------- /project/License.scala: -------------------------------------------------------------------------------- 1 | import java.io.File 2 | import java.io.PrintStream 3 | import java.util.Calendar 4 | import java.util.TimeZone 5 | import scala.io.Source 6 | import sbt._ 7 | 8 | /** 9 | * Loosely based on: https://github.com/Banno/sbt-license-plugin 10 | * 11 | * Main changes in functionality: 12 | * - remove spurious whitespace on empty lines for license 13 | * - supports both test and main source files 14 | * - add target to check which can fail the build 15 | */ 16 | object License { 17 | private val lineSeparator = System.getProperty("line.separator") 18 | 19 | def year = Calendar.getInstance(TimeZone.getTimeZone("UTC")).get(Calendar.YEAR) 20 | 21 | val apache2 = s""" 22 | |/* 23 | | * Copyright 2014-$year Netflix, Inc. 24 | | * 25 | | * Licensed under the Apache License, Version 2.0 (the "License"); 26 | | * you may not use this file except in compliance with the License. 27 | | * You may obtain a copy of the License at 28 | | * 29 | | * http://www.apache.org/licenses/LICENSE-2.0 30 | | * 31 | | * Unless required by applicable law or agreed to in writing, software 32 | | * distributed under the License is distributed on an "AS IS" BASIS, 33 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 34 | | * See the License for the specific language governing permissions and 35 | | * limitations under the License. 36 | | */ 37 | """.stripMargin.trim 38 | 39 | def findFiles(dir: File): Seq[File] = { 40 | (dir ** "*.scala").get ++ (dir ** "*.java").get 41 | } 42 | 43 | def checkLicenseHeaders(log: Logger, srcDir: File): Unit = { 44 | val badFiles = findFiles(srcDir).filterNot(checkLicenseHeader) 45 | if (badFiles.nonEmpty) { 46 | badFiles.foreach { f => log.error(s"bad license header: $f") } 47 | error(s"${badFiles.size} files with incorrect header, run formatLicenseHeaders to fix") 48 | } else { 49 | log.info("all files have correct license header") 50 | } 51 | } 52 | 53 | def checkLicenseHeader(file: File): Boolean = { 54 | val lines = Source.fromFile(file, "UTF-8").getLines().toList 55 | checkLicenseHeader(lines) 56 | } 57 | 58 | def checkLicenseHeader(lines: List[String]): Boolean = { 59 | val header = lines.takeWhile(!_.startsWith("package ")).mkString(lineSeparator) 60 | header == apache2 61 | } 62 | 63 | def formatLicenseHeaders(log: Logger, srcDir: File): Unit = { 64 | findFiles(srcDir).foreach { f => formatLicenseHeader(log, f) } 65 | } 66 | 67 | def formatLicenseHeader(log: Logger, file: File): Unit = { 68 | val lines = Source.fromFile(file, "UTF-8").getLines().toList 69 | if (!checkLicenseHeader(lines)) { 70 | log.info(s"fixing license header: $file") 71 | writeLines(file, apache2 :: removeExistingHeader(lines)) 72 | } 73 | } 74 | 75 | def removeExistingHeader(lines: List[String]): List[String] = { 76 | val res = lines.dropWhile(!_.startsWith("package ")) 77 | if (res.isEmpty) lines else res 78 | } 79 | 80 | def writeLines(file: File, lines: List[String]): Unit = { 81 | val out = new PrintStream(file) 82 | try lines.foreach(out.println) finally out.close() 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.9 2 | -------------------------------------------------------------------------------- /project/lib/aws-java-sdk-1.9.33-sources.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/rx-aws-java-sdk/0cc14ddc3d9bdb299f5b5769d53cc83507a22d79/project/lib/aws-java-sdk-1.9.33-sources.jar -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | 2 | libraryDependencies += "com.google.guava" % "guava" % "18.0" 3 | 4 | libraryDependencies += "com.github.javaparser" % "javaparser-core" % "2.0.0" 5 | 6 | libraryDependencies += "com.amazonaws" % "aws-java-sdk" % "1.10.27" //withSources() 7 | 8 | addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.7.4") 9 | 10 | addSbtPlugin("org.scoverage" %% "sbt-scoverage" % "1.3.3") 11 | 12 | addSbtPlugin("com.sksamuel.sbt-versions" % "sbt-versions" % "0.2.0") 13 | 14 | //addSbtPlugin("com.sksamuel.scapegoat" %% "sbt-scapegoat" % "0.94.6") 15 | 16 | addSbtPlugin("me.lessis" % "bintray-sbt" % "0.3.0") 17 | 18 | addSbtPlugin("com.github.gseitz" % "sbt-release" % "0.8.5") 19 | 20 | addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.2.5") 21 | 22 | addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.8.4") 23 | -------------------------------------------------------------------------------- /project/sbt: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script is used to launch sbt on developer workstations. 4 | 5 | # Additional options to add in when calling java 6 | OPTIONS="" 7 | 8 | # On jenkins the colors should be disabled for the console log 9 | [ -n "$BUILD_ID" ] && OPTIONS="$OPTIONS -Dsbt.log.noformat=true" 10 | 11 | java \ 12 | -XX:+UseG1GC \ 13 | -Xms2g \ 14 | -Xmx2g \ 15 | -XX:ReservedCodeCacheSize=128m \ 16 | -XX:+UseCodeCacheFlushing \ 17 | -Dsbt.boot.directory=${WORKSPACE:-$HOME}/.sbt \ 18 | -Dsbt.ivy.home=${WORKSPACE:-$HOME}/.ivy2 \ 19 | $OPTIONS \ 20 | -jar `dirname $0`/sbt-launch-0.13.8.jar "$@" 21 | -------------------------------------------------------------------------------- /project/sbt-launch-0.13.8.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Netflix/rx-aws-java-sdk/0cc14ddc3d9bdb299f5b5769d53cc83507a22d79/project/sbt-launch-0.13.8.jar -------------------------------------------------------------------------------- /rx-aws-java-sdk-autoscaling/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # Root logger option 2 | log4j.rootLogger=INFO,console,file 3 | 4 | log4j.appender.console=org.apache.log4j.ConsoleAppender 5 | log4j.appender.console.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p %c{1}:%L - %m%n 7 | 8 | # Direct log messages to a log file 9 | log4j.appender.file=org.apache.log4j.RollingFileAppender 10 | log4j.appender.file.File=sbt.log 11 | log4j.appender.file.MaxFileSize=1MB 12 | log4j.appender.file.MaxBackupIndex=1 13 | log4j.appender.file.layout=org.apache.log4j.PatternLayout 14 | log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p %c{1}:%L - %m%n 15 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-autoscaling/src/test/scala/AmazonAutoScalingSuite.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2017 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.amazonaws.services.autoscaling 17 | 18 | import org.scalatest.{ FunSuite, BeforeAndAfterAll } 19 | 20 | import java.net.InetAddress 21 | 22 | import org.apache.commons.codec.binary.Base64 23 | 24 | import rx.lang.scala.JavaConversions._ 25 | 26 | import com.amazonaws.auth.EnvironmentVariableCredentialsProvider 27 | import model._ 28 | 29 | class AmazonAutoScalingSuite extends FunSuite with BeforeAndAfterAll { 30 | 31 | val creds = new EnvironmentVariableCredentialsProvider 32 | val client = new AmazonAutoScalingRxNettyClient(creds) 33 | 34 | override def beforeAll() { 35 | } 36 | 37 | override def afterAll() { 38 | } 39 | 40 | val lcName = s"regression-${System.currentTimeMillis}-${InetAddress.getLocalHost.getHostName}" 41 | 42 | ignore("createLaunchConfiguration") { 43 | val r = toScalaObservable(client.createLaunchConfiguration( 44 | new CreateLaunchConfigurationRequest() 45 | .withLaunchConfigurationName(lcName) 46 | .withImageId("ami-19fa4172") 47 | .withInstanceType("c3.large") 48 | .withUserData(new String(Base64.encodeBase64("test".getBytes))) 49 | )).toBlocking.toList 50 | assert(r.size == 1) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-cloudwatch/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # Root logger option 2 | log4j.rootLogger=INFO,console,file 3 | 4 | log4j.appender.console=org.apache.log4j.ConsoleAppender 5 | log4j.appender.console.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p %c{1}:%L - %m%n 7 | 8 | # Direct log messages to a log file 9 | log4j.appender.file=org.apache.log4j.RollingFileAppender 10 | log4j.appender.file.File=sbt.log 11 | log4j.appender.file.MaxFileSize=1MB 12 | log4j.appender.file.MaxBackupIndex=1 13 | log4j.appender.file.layout=org.apache.log4j.PatternLayout 14 | log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p %c{1}:%L - %m%n 15 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-cloudwatch/src/test/scala/AmazonCloudWatchSuite.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2017 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.amazonaws.services.cloudwatch 17 | 18 | import org.scalatest.{ FunSuite, BeforeAndAfterAll } 19 | 20 | import rx.lang.scala.JavaConversions._ 21 | 22 | import com.amazonaws.auth.EnvironmentVariableCredentialsProvider 23 | import model._ 24 | 25 | class AmazonCloudWatchSuite extends FunSuite with BeforeAndAfterAll { 26 | 27 | val creds = new EnvironmentVariableCredentialsProvider 28 | val client = new AmazonCloudWatchRxNettyClient(creds) 29 | 30 | override def beforeAll() { 31 | } 32 | 33 | override def afterAll() { 34 | } 35 | 36 | ignore("describeAlarms") { 37 | val r = toScalaObservable(client.describeAlarms).toBlocking.toList 38 | assert(r.size == 1) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-core/src/main/java/com/amazonaws/auth/RxAWS4Signer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2017 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.amazonaws.auth; 17 | 18 | import static com.amazonaws.auth.internal.SignerConstants.AUTHORIZATION; 19 | import static com.amazonaws.auth.internal.SignerConstants.AWS4_SIGNING_ALGORITHM; 20 | import static com.amazonaws.auth.internal.SignerConstants.AWS4_TERMINATOR; 21 | import static com.amazonaws.auth.internal.SignerConstants.HOST; 22 | import static com.amazonaws.auth.internal.SignerConstants.LINE_SEPARATOR; 23 | import static com.amazonaws.auth.internal.SignerConstants.PRESIGN_URL_MAX_EXPIRATION_SECONDS; 24 | import static com.amazonaws.auth.internal.SignerConstants.X_AMZ_ALGORITHM; 25 | import static com.amazonaws.auth.internal.SignerConstants.X_AMZ_CONTENT_SHA256; 26 | import static com.amazonaws.auth.internal.SignerConstants.X_AMZ_CREDENTIAL; 27 | import static com.amazonaws.auth.internal.SignerConstants.X_AMZ_DATE; 28 | import static com.amazonaws.auth.internal.SignerConstants.X_AMZ_EXPIRES; 29 | import static com.amazonaws.auth.internal.SignerConstants.X_AMZ_SECURITY_TOKEN; 30 | import static com.amazonaws.auth.internal.SignerConstants.X_AMZ_SIGNATURE; 31 | import static com.amazonaws.auth.internal.SignerConstants.X_AMZ_SIGNED_HEADER; 32 | 33 | import java.io.IOException; 34 | import java.io.InputStream; 35 | import java.net.URI; 36 | import java.nio.charset.Charset; 37 | import java.util.ArrayList; 38 | import java.util.Collections; 39 | import java.util.Date; 40 | import java.util.List; 41 | import java.util.Map; 42 | import java.util.concurrent.TimeUnit; 43 | 44 | import com.amazonaws.AmazonClientException; 45 | import com.amazonaws.ReadLimitInfo; 46 | import com.amazonaws.SignableRequest; 47 | import com.amazonaws.auth.internal.AWS4SignerRequestParams; 48 | import com.amazonaws.auth.internal.AWS4SignerUtils; 49 | import com.amazonaws.auth.internal.SignerKey; 50 | import com.amazonaws.internal.FIFOCache; 51 | import com.amazonaws.log.InternalLogApi; 52 | import com.amazonaws.log.InternalLogFactory; 53 | import com.amazonaws.util.BinaryUtils; 54 | import com.amazonaws.util.DateUtils; 55 | import com.amazonaws.util.RxSdkHttpUtils; 56 | 57 | public class RxAWS4Signer extends RxAbstractAWSSigner implements 58 | ServiceAwareSigner, RegionAwareSigner, Presigner { 59 | 60 | protected static final InternalLogApi log = InternalLogFactory.getLog(RxAWS4Signer.class); 61 | private static final int SIGNER_CACHE_MAX_SIZE = 300; 62 | private static final FIFOCache signerCache = new FIFOCache(SIGNER_CACHE_MAX_SIZE); 63 | 64 | protected String serviceName; 65 | 66 | protected String regionName; 67 | 68 | protected Date overriddenDate; 69 | 70 | protected boolean doubleUrlEncode; 71 | 72 | public RxAWS4Signer() { 73 | this(true); 74 | } 75 | 76 | public RxAWS4Signer(boolean doubleUrlEncoding) { 77 | this.doubleUrlEncode = doubleUrlEncoding; 78 | } 79 | 80 | @Override 81 | public void setServiceName(String serviceName) { 82 | this.serviceName = serviceName; 83 | } 84 | 85 | @Override 86 | public void setRegionName(String regionName) { 87 | this.regionName = regionName; 88 | } 89 | 90 | void setOverrideDate(Date overriddenDate) { 91 | this.overriddenDate = overriddenDate; 92 | } 93 | 94 | public String getRegionName() { 95 | return regionName; 96 | } 97 | 98 | public String getServiceName() { 99 | return serviceName; 100 | } 101 | 102 | public Date getOverriddenDate() { 103 | return overriddenDate == null ? null : new Date( 104 | overriddenDate.getTime()); 105 | } 106 | 107 | @Override 108 | public void sign(SignableRequest request, AWSCredentials credentials) { 109 | if (isAnonymous(credentials)) { 110 | return; 111 | } 112 | 113 | AWSCredentials sanitizedCredentials = sanitizeCredentials(credentials); 114 | if (sanitizedCredentials instanceof AWSSessionCredentials) { 115 | addSessionCredentials(request, 116 | (AWSSessionCredentials) sanitizedCredentials); 117 | } 118 | 119 | final AWS4SignerRequestParams signerParams = new AWS4SignerRequestParams( 120 | request, overriddenDate, regionName, serviceName, 121 | AWS4_SIGNING_ALGORITHM); 122 | 123 | addHostHeader(request); 124 | request.addHeader(X_AMZ_DATE, 125 | signerParams.getFormattedSigningDateTime()); 126 | 127 | String contentSha256 = calculateContentHash(request); 128 | 129 | if ("required".equals(request.getHeaders().get(X_AMZ_CONTENT_SHA256))) { 130 | request.addHeader(X_AMZ_CONTENT_SHA256, contentSha256); 131 | } 132 | 133 | final String canonicalRequest = createCanonicalRequest(request, 134 | contentSha256); 135 | 136 | final String stringToSign = createStringToSign(canonicalRequest, 137 | signerParams); 138 | 139 | final byte[] signingKey = deriveSigningKey(sanitizedCredentials, 140 | signerParams); 141 | 142 | final byte[] signature = computeSignature(stringToSign, signingKey, 143 | signerParams); 144 | 145 | request.addHeader( 146 | AUTHORIZATION, 147 | buildAuthorizationHeader(request, signature, 148 | sanitizedCredentials, signerParams)); 149 | 150 | processRequestPayload(request, signature, signingKey, 151 | signerParams); 152 | } 153 | 154 | @Override 155 | public void presignRequest(SignableRequest request, AWSCredentials credentials, 156 | Date userSpecifiedExpirationDate) { 157 | 158 | if (isAnonymous(credentials)) { 159 | return; 160 | } 161 | 162 | long expirationInSeconds = generateExpirationDate(userSpecifiedExpirationDate); 163 | 164 | addHostHeader(request); 165 | 166 | AWSCredentials sanitizedCredentials = sanitizeCredentials(credentials); 167 | if (sanitizedCredentials instanceof AWSSessionCredentials) { 168 | request.addParameter(X_AMZ_SECURITY_TOKEN, 169 | ((AWSSessionCredentials) sanitizedCredentials) 170 | .getSessionToken()); 171 | } 172 | 173 | final AWS4SignerRequestParams signerRequestParams = new AWS4SignerRequestParams( 174 | request, overriddenDate, regionName, serviceName, 175 | AWS4_SIGNING_ALGORITHM); 176 | 177 | final String timeStamp = AWS4SignerUtils.formatTimestamp(System 178 | .currentTimeMillis()); 179 | 180 | addPreSignInformationToRequest(request, sanitizedCredentials, 181 | signerRequestParams, timeStamp, expirationInSeconds); 182 | 183 | final String contentSha256 = calculateContentHashPresign(request); 184 | 185 | final String canonicalRequest = createCanonicalRequest(request, 186 | contentSha256); 187 | 188 | final String stringToSign = createStringToSign(canonicalRequest, 189 | signerRequestParams); 190 | 191 | final byte[] signingKey = deriveSigningKey(sanitizedCredentials, 192 | signerRequestParams); 193 | 194 | final byte[] signature = computeSignature(stringToSign, signingKey, 195 | signerRequestParams); 196 | 197 | request.addParameter(X_AMZ_SIGNATURE, BinaryUtils.toHex(signature)); 198 | } 199 | 200 | protected String createCanonicalRequest(SignableRequest request, 201 | String contentSha256) { 202 | final String path = RxSdkHttpUtils.appendUri( 203 | request.getEndpoint().getPath(), request.getResourcePath()); 204 | 205 | final StringBuilder canonicalRequestBuilder = new StringBuilder(request 206 | .getHttpMethod().toString()); 207 | 208 | canonicalRequestBuilder.append(LINE_SEPARATOR) 209 | .append(getCanonicalizedResourcePath(path, doubleUrlEncode)) 210 | .append(LINE_SEPARATOR) 211 | .append(getCanonicalizedQueryString(request)) 212 | .append(LINE_SEPARATOR) 213 | .append(getCanonicalizedHeaderString(request)) 214 | .append(LINE_SEPARATOR) 215 | .append(getSignedHeadersString(request)).append(LINE_SEPARATOR) 216 | .append(contentSha256); 217 | 218 | final String canonicalRequest = canonicalRequestBuilder.toString(); 219 | 220 | if (log.isDebugEnabled()) 221 | log.debug("AWS4 Canonical Request: '\"" + canonicalRequest + "\""); 222 | 223 | return canonicalRequest; 224 | } 225 | 226 | protected String createStringToSign(String canonicalRequest, 227 | AWS4SignerRequestParams signerParams) { 228 | 229 | final StringBuilder stringToSignBuilder = new StringBuilder( 230 | signerParams.getSigningAlgorithm()); 231 | stringToSignBuilder.append(LINE_SEPARATOR) 232 | .append(signerParams.getFormattedSigningDateTime()) 233 | .append(LINE_SEPARATOR) 234 | .append(signerParams.getScope()) 235 | .append(LINE_SEPARATOR) 236 | .append(BinaryUtils.toHex(hash(canonicalRequest))); 237 | 238 | final String stringToSign = stringToSignBuilder.toString(); 239 | 240 | if (log.isDebugEnabled()) 241 | log.debug("AWS4 String to Sign: '\"" + stringToSign + "\""); 242 | 243 | return stringToSign; 244 | } 245 | 246 | private final byte[] deriveSigningKey(AWSCredentials credentials, 247 | AWS4SignerRequestParams signerRequestParams) { 248 | 249 | final String cacheKey = computeSigningCacheKeyName(credentials, 250 | signerRequestParams); 251 | final long daysSinceEpochSigningDate = DateUtils 252 | .numberOfDaysSinceEpoch(signerRequestParams 253 | .getSigningDateTimeMilli()); 254 | 255 | SignerKey signerKey = signerCache.get(cacheKey); 256 | 257 | if (signerKey != null) { 258 | if (daysSinceEpochSigningDate == signerKey 259 | .getNumberOfDaysSinceEpoch()) { 260 | return signerKey.getSigningKey(); 261 | } 262 | } 263 | if (log.isDebugEnabled()) { 264 | log.debug("Generating a new signing key as the signing key not available in the cache for the date " 265 | + TimeUnit.DAYS.toMillis(daysSinceEpochSigningDate)); 266 | } 267 | byte[] signingKey = newSigningKey(credentials, 268 | signerRequestParams.getFormattedSigningDate(), 269 | signerRequestParams.getRegionName(), 270 | signerRequestParams.getServiceName()); 271 | signerCache.add(cacheKey, new SignerKey( 272 | daysSinceEpochSigningDate, signingKey)); 273 | return signingKey; 274 | } 275 | 276 | private final String computeSigningCacheKeyName(AWSCredentials credentials, 277 | AWS4SignerRequestParams signerRequestParams) { 278 | final StringBuilder hashKeyBuilder = new StringBuilder( 279 | credentials.getAWSSecretKey()); 280 | 281 | return hashKeyBuilder.append("-") 282 | .append(signerRequestParams.getRegionName()) 283 | .append("-") 284 | .append(signerRequestParams.getServiceName()).toString(); 285 | } 286 | 287 | protected final byte[] computeSignature(String stringToSign, 288 | byte[] signingKey, AWS4SignerRequestParams signerRequestParams) { 289 | return sign(stringToSign.getBytes(Charset.forName("UTF-8")), signingKey, 290 | SigningAlgorithm.HmacSHA256); 291 | } 292 | 293 | private String buildAuthorizationHeader(SignableRequest request, 294 | byte[] signature, AWSCredentials credentials, 295 | AWS4SignerRequestParams signerParams) { 296 | final String signingCredentials = credentials.getAWSAccessKeyId() + "/" 297 | + signerParams.getScope(); 298 | 299 | final String credential = "Credential=" 300 | + signingCredentials; 301 | final String signerHeaders = "SignedHeaders=" 302 | + getSignedHeadersString(request); 303 | final String signatureHeader = "Signature=" 304 | + BinaryUtils.toHex(signature); 305 | 306 | final StringBuilder authHeaderBuilder = new StringBuilder(); 307 | 308 | authHeaderBuilder.append(AWS4_SIGNING_ALGORITHM) 309 | .append(" ") 310 | .append(credential) 311 | .append(", ") 312 | .append(signerHeaders) 313 | .append(", ") 314 | .append(signatureHeader); 315 | 316 | return authHeaderBuilder.toString(); 317 | } 318 | 319 | private void addPreSignInformationToRequest(SignableRequest request, 320 | AWSCredentials credentials, AWS4SignerRequestParams signerParams, 321 | String timeStamp, long expirationInSeconds) { 322 | 323 | String signingCredentials = credentials.getAWSAccessKeyId() + "/" 324 | + signerParams.getScope(); 325 | 326 | request.addParameter(X_AMZ_ALGORITHM, AWS4_SIGNING_ALGORITHM); 327 | request.addParameter(X_AMZ_DATE, timeStamp); 328 | request.addParameter(X_AMZ_SIGNED_HEADER, 329 | getSignedHeadersString(request)); 330 | request.addParameter(X_AMZ_EXPIRES, 331 | Long.toString(expirationInSeconds)); 332 | request.addParameter(X_AMZ_CREDENTIAL, signingCredentials); 333 | } 334 | 335 | @Override 336 | protected void addSessionCredentials(SignableRequest request, 337 | AWSSessionCredentials credentials) { 338 | request.addHeader(X_AMZ_SECURITY_TOKEN, credentials.getSessionToken()); 339 | } 340 | 341 | protected String getCanonicalizedHeaderString(SignableRequest request) { 342 | final List sortedHeaders = new ArrayList(request.getHeaders() 343 | .keySet()); 344 | Collections.sort(sortedHeaders, String.CASE_INSENSITIVE_ORDER); 345 | 346 | final Map requestHeaders = request.getHeaders(); 347 | StringBuilder buffer = new StringBuilder(); 348 | for (String header : sortedHeaders) { 349 | String key = header.toLowerCase().replaceAll("\\s+", " "); 350 | String value = requestHeaders.get(header); 351 | 352 | buffer.append(key).append(":"); 353 | if (value != null) { 354 | buffer.append(value.replaceAll("\\s+", " ")); 355 | } 356 | 357 | buffer.append("\n"); 358 | } 359 | 360 | return buffer.toString(); 361 | } 362 | 363 | protected String getSignedHeadersString(SignableRequest request) { 364 | final List sortedHeaders = new ArrayList(request 365 | .getHeaders().keySet()); 366 | Collections.sort(sortedHeaders, String.CASE_INSENSITIVE_ORDER); 367 | 368 | StringBuilder buffer = new StringBuilder(); 369 | for (String header : sortedHeaders) { 370 | if (buffer.length() > 0) 371 | buffer.append(";"); 372 | buffer.append(header.toLowerCase()); 373 | } 374 | 375 | return buffer.toString(); 376 | } 377 | 378 | protected void addHostHeader(SignableRequest request) { 379 | 380 | final URI endpoint = request.getEndpoint(); 381 | final StringBuilder hostHeaderBuilder = new StringBuilder( 382 | endpoint.getHost()); 383 | if (RxSdkHttpUtils.isUsingNonDefaultPort(endpoint)) { 384 | hostHeaderBuilder.append(":").append(endpoint.getPort()); 385 | } 386 | 387 | request.addHeader(HOST, hostHeaderBuilder.toString()); 388 | } 389 | 390 | protected String calculateContentHash(SignableRequest request) { 391 | InputStream payloadStream = getBinaryRequestPayloadStream(request); 392 | ReadLimitInfo info = request.getReadLimitInfo(); 393 | payloadStream.mark(info == null ? -1 : info.getReadLimit()); 394 | String contentSha256 = BinaryUtils.toHex(hash(payloadStream)); 395 | try { 396 | payloadStream.reset(); 397 | } catch (IOException e) { 398 | throw new AmazonClientException( 399 | "Unable to reset stream after calculating AWS4 signature", 400 | e); 401 | } 402 | return contentSha256; 403 | } 404 | 405 | protected void processRequestPayload(SignableRequest request, byte[] signature, 406 | byte[] signingKey, AWS4SignerRequestParams signerRequestParams) { 407 | return; 408 | } 409 | 410 | protected String calculateContentHashPresign(SignableRequest request) { 411 | return calculateContentHash(request); 412 | } 413 | 414 | private boolean isAnonymous(AWSCredentials credentials) { 415 | return credentials instanceof AnonymousAWSCredentials; 416 | } 417 | 418 | private long generateExpirationDate(Date expirationDate) { 419 | 420 | long expirationInSeconds = expirationDate != null ? ((expirationDate 421 | .getTime() - System.currentTimeMillis()) / 1000L) 422 | : PRESIGN_URL_MAX_EXPIRATION_SECONDS; 423 | 424 | if (expirationInSeconds > PRESIGN_URL_MAX_EXPIRATION_SECONDS) { 425 | throw new AmazonClientException( 426 | "Requests that are pre-signed by SigV4 algorithm are valid for at most 7 days. " 427 | + "The expiration date set on the current request [" 428 | + AWS4SignerUtils.formatTimestamp(expirationDate 429 | .getTime()) + "] has exceeded this limit."); 430 | } 431 | return expirationInSeconds; 432 | } 433 | 434 | private byte[] newSigningKey(AWSCredentials credentials, 435 | String dateStamp, String regionName, String serviceName) { 436 | byte[] kSecret = ("AWS4" + credentials.getAWSSecretKey()) 437 | .getBytes(Charset.forName("UTF-8")); 438 | byte[] kDate = sign(dateStamp, kSecret, SigningAlgorithm.HmacSHA256); 439 | byte[] kRegion = sign(regionName, kDate, SigningAlgorithm.HmacSHA256); 440 | byte[] kService = sign(serviceName, kRegion, 441 | SigningAlgorithm.HmacSHA256); 442 | return sign(AWS4_TERMINATOR, kService, SigningAlgorithm.HmacSHA256); 443 | } 444 | } 445 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-core/src/main/java/com/amazonaws/auth/RxAbstractAWSSigner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2017 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.amazonaws.auth; 17 | 18 | import static com.amazonaws.util.StringUtils.UTF8; 19 | 20 | import java.io.ByteArrayInputStream; 21 | import java.io.ByteArrayOutputStream; 22 | import java.io.InputStream; 23 | import java.net.URI; 24 | import java.security.DigestInputStream; 25 | import java.security.MessageDigest; 26 | import java.util.ArrayList; 27 | import java.util.Collections; 28 | import java.util.Date; 29 | import java.util.Iterator; 30 | import java.util.List; 31 | import java.util.Map; 32 | import java.util.SortedMap; 33 | import java.util.TreeMap; 34 | 35 | import javax.crypto.Mac; 36 | import javax.crypto.spec.SecretKeySpec; 37 | 38 | import com.amazonaws.AmazonClientException; 39 | import com.amazonaws.ReadLimitInfo; 40 | import com.amazonaws.SDKGlobalTime; 41 | import com.amazonaws.SignableRequest; 42 | import com.amazonaws.internal.SdkDigestInputStream; 43 | import com.amazonaws.util.Base64; 44 | import com.amazonaws.util.BinaryUtils; 45 | import com.amazonaws.util.RxSdkHttpUtils; 46 | 47 | public abstract class RxAbstractAWSSigner implements Signer { 48 | public static final String EMPTY_STRING_SHA256_HEX; 49 | 50 | static { 51 | EMPTY_STRING_SHA256_HEX = BinaryUtils.toHex(doHash("")); 52 | } 53 | 54 | protected String signAndBase64Encode(String data, String key, 55 | SigningAlgorithm algorithm) throws AmazonClientException { 56 | return signAndBase64Encode(data.getBytes(UTF8), key, algorithm); 57 | } 58 | 59 | protected String signAndBase64Encode(byte[] data, String key, 60 | SigningAlgorithm algorithm) throws AmazonClientException { 61 | try { 62 | byte[] signature = sign(data, key.getBytes(UTF8), algorithm); 63 | return Base64.encodeAsString(signature); 64 | } catch (Exception e) { 65 | throw new AmazonClientException( 66 | "Unable to calculate a request signature: " 67 | + e.getMessage(), e); 68 | } 69 | } 70 | 71 | public byte[] sign(String stringData, byte[] key, 72 | SigningAlgorithm algorithm) throws AmazonClientException { 73 | try { 74 | byte[] data = stringData.getBytes(UTF8); 75 | return sign(data, key, algorithm); 76 | } catch (Exception e) { 77 | throw new AmazonClientException( 78 | "Unable to calculate a request signature: " 79 | + e.getMessage(), e); 80 | } 81 | } 82 | 83 | public byte[] signWithMac(String stringData, Mac mac) { 84 | try { 85 | return mac.doFinal(stringData.getBytes(UTF8)); 86 | } catch (Exception e) { 87 | throw new AmazonClientException( 88 | "Unable to calculate a request signature: " 89 | + e.getMessage(), e); 90 | } 91 | } 92 | 93 | protected byte[] sign(byte[] data, byte[] key, 94 | SigningAlgorithm algorithm) throws AmazonClientException { 95 | try { 96 | Mac mac = Mac.getInstance(algorithm.toString()); 97 | mac.init(new SecretKeySpec(key, algorithm.toString())); 98 | return mac.doFinal(data); 99 | } catch (Exception e) { 100 | throw new AmazonClientException( 101 | "Unable to calculate a request signature: " 102 | + e.getMessage(), e); 103 | } 104 | } 105 | 106 | public byte[] hash(String text) throws AmazonClientException { 107 | return RxAbstractAWSSigner.doHash(text); 108 | } 109 | 110 | private static byte[] doHash(String text) throws AmazonClientException { 111 | try { 112 | MessageDigest md = MessageDigest.getInstance("SHA-256"); 113 | md.update(text.getBytes(UTF8)); 114 | return md.digest(); 115 | } catch (Exception e) { 116 | throw new AmazonClientException( 117 | "Unable to compute hash while signing request: " 118 | + e.getMessage(), e); 119 | } 120 | } 121 | 122 | protected byte[] hash(InputStream input) throws AmazonClientException { 123 | try { 124 | MessageDigest md = MessageDigest.getInstance("SHA-256"); 125 | @SuppressWarnings("resource") 126 | DigestInputStream digestInputStream = new SdkDigestInputStream( 127 | input, md); 128 | byte[] buffer = new byte[1024]; 129 | while (digestInputStream.read(buffer) > -1) 130 | ; 131 | return digestInputStream.getMessageDigest().digest(); 132 | } catch (Exception e) { 133 | throw new AmazonClientException( 134 | "Unable to compute hash while signing request: " 135 | + e.getMessage(), e); 136 | } 137 | } 138 | 139 | public byte[] hash(byte[] data) throws AmazonClientException { 140 | try { 141 | MessageDigest md = MessageDigest.getInstance("SHA-256"); 142 | md.update(data); 143 | return md.digest(); 144 | } catch (Exception e) { 145 | throw new AmazonClientException( 146 | "Unable to compute hash while signing request: " 147 | + e.getMessage(), e); 148 | } 149 | } 150 | protected String getCanonicalizedQueryString(Map> parameters) { 151 | 152 | final SortedMap> sorted = new TreeMap>(); 153 | 154 | for (Map.Entry> entry : parameters.entrySet()) { 155 | final String encodedParamName = RxSdkHttpUtils.urlEncode( 156 | entry.getKey(), false); 157 | final List paramValues = entry.getValue(); 158 | final List encodedValues = new ArrayList( 159 | paramValues.size()); 160 | for (String value : paramValues) { 161 | encodedValues.add(RxSdkHttpUtils.urlEncode(value, false)); 162 | } 163 | Collections.sort(encodedValues); 164 | sorted.put(encodedParamName, encodedValues); 165 | 166 | } 167 | 168 | final StringBuilder result = new StringBuilder(); 169 | for(Map.Entry> entry : sorted.entrySet()) { 170 | for(String value : entry.getValue()) { 171 | if (result.length() > 0) { 172 | result.append("&"); 173 | } 174 | result.append(entry.getKey()) 175 | .append("=") 176 | .append(value); 177 | } 178 | } 179 | 180 | return result.toString(); 181 | } 182 | 183 | protected String getCanonicalizedQueryString(SignableRequest request) { 184 | if (RxSdkHttpUtils.usePayloadForQueryParameters(request)) 185 | return ""; 186 | return this.getCanonicalizedQueryString(request.getParameters()); 187 | } 188 | 189 | protected byte[] getBinaryRequestPayload(SignableRequest request) { 190 | if (RxSdkHttpUtils.usePayloadForQueryParameters(request)) { 191 | String encodedParameters = RxSdkHttpUtils.encodeParameters(request); 192 | if (encodedParameters == null) 193 | return new byte[0]; 194 | 195 | return encodedParameters.getBytes(UTF8); 196 | } 197 | 198 | return getBinaryRequestPayloadWithoutQueryParams(request); 199 | } 200 | 201 | protected String getRequestPayload(SignableRequest request) { 202 | return newString(getBinaryRequestPayload(request)); 203 | } 204 | 205 | protected String getRequestPayloadWithoutQueryParams(SignableRequest request) { 206 | return newString(getBinaryRequestPayloadWithoutQueryParams(request)); 207 | } 208 | 209 | protected byte[] getBinaryRequestPayloadWithoutQueryParams(SignableRequest request) { 210 | InputStream content = getBinaryRequestPayloadStreamWithoutQueryParams(request); 211 | 212 | try { 213 | ReadLimitInfo info = request.getReadLimitInfo(); 214 | content.mark(info == null ? -1 : info.getReadLimit()); 215 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 216 | byte[] buffer = new byte[1024 * 5]; 217 | while (true) { 218 | int bytesRead = content.read(buffer); 219 | if (bytesRead == -1) break; 220 | 221 | byteArrayOutputStream.write(buffer, 0, bytesRead); 222 | } 223 | 224 | byteArrayOutputStream.close(); 225 | content.reset(); 226 | 227 | return byteArrayOutputStream.toByteArray(); 228 | } catch (Exception e) { 229 | throw new AmazonClientException("Unable to read request payload to sign request: " + e.getMessage(), e); 230 | } 231 | } 232 | 233 | protected InputStream getBinaryRequestPayloadStream(SignableRequest request) { 234 | if (RxSdkHttpUtils.usePayloadForQueryParameters(request)) { 235 | String encodedParameters = RxSdkHttpUtils.encodeParameters(request); 236 | if (encodedParameters == null) 237 | return new ByteArrayInputStream(new byte[0]); 238 | 239 | return new ByteArrayInputStream( 240 | encodedParameters.getBytes(UTF8)); 241 | } 242 | 243 | return getBinaryRequestPayloadStreamWithoutQueryParams(request); 244 | } 245 | 246 | protected InputStream getBinaryRequestPayloadStreamWithoutQueryParams(SignableRequest request) { 247 | try { 248 | InputStream is = request.getContentUnwrapped(); 249 | if (is == null) 250 | return new ByteArrayInputStream(new byte[0]); 251 | if (!is.markSupported()) 252 | throw new AmazonClientException("Unable to read request payload to sign request."); 253 | return is; 254 | } catch (AmazonClientException e) { 255 | throw e; 256 | } catch (Exception e) { 257 | throw new AmazonClientException("Unable to read request payload to sign request: " + e.getMessage(), e); 258 | } 259 | } 260 | 261 | protected String getCanonicalizedResourcePath(String resourcePath) { 262 | return getCanonicalizedResourcePath(resourcePath, true); 263 | } 264 | 265 | protected String getCanonicalizedResourcePath(String resourcePath, boolean urlEncode) { 266 | if (resourcePath == null || resourcePath.isEmpty()) { 267 | return "/"; 268 | } else { 269 | String value = urlEncode ? RxSdkHttpUtils.urlEncode(resourcePath, true) : resourcePath; 270 | if (value.startsWith("/")) { 271 | return value; 272 | } else { 273 | return "/".concat(value); 274 | } 275 | } 276 | } 277 | 278 | protected String getCanonicalizedEndpoint(URI endpoint) { 279 | String endpointForStringToSign = endpoint.getHost().toLowerCase(); 280 | if (RxSdkHttpUtils.isUsingNonDefaultPort(endpoint)) { 281 | endpointForStringToSign += ":" + endpoint.getPort(); 282 | } 283 | 284 | return endpointForStringToSign; 285 | } 286 | 287 | protected AWSCredentials sanitizeCredentials(AWSCredentials credentials) { 288 | String accessKeyId = null; 289 | String secretKey = null; 290 | String token = null; 291 | synchronized (credentials) { 292 | accessKeyId = credentials.getAWSAccessKeyId(); 293 | secretKey = credentials.getAWSSecretKey(); 294 | if ( credentials instanceof AWSSessionCredentials ) { 295 | token = ((AWSSessionCredentials) credentials).getSessionToken(); 296 | } 297 | } 298 | if (secretKey != null) secretKey = secretKey.trim(); 299 | if (accessKeyId != null) accessKeyId = accessKeyId.trim(); 300 | if (token != null) token = token.trim(); 301 | 302 | if (credentials instanceof AWSSessionCredentials) { 303 | return new BasicSessionCredentials(accessKeyId, secretKey, token); 304 | } 305 | 306 | return new BasicAWSCredentials(accessKeyId, secretKey); 307 | } 308 | 309 | protected String newString(byte[] bytes) { 310 | return new String(bytes, UTF8); 311 | } 312 | 313 | protected Date getSignatureDate(int offsetInSeconds) { 314 | return new Date(System.currentTimeMillis() - offsetInSeconds*1000); 315 | } 316 | 317 | @Deprecated 318 | protected int getTimeOffset(SignableRequest request) { 319 | final int globleOffset = SDKGlobalTime.getGlobalTimeOffset(); 320 | return globleOffset == 0 ? request.getTimeOffset() : globleOffset; 321 | } 322 | 323 | protected abstract void addSessionCredentials(SignableRequest request, 324 | AWSSessionCredentials credentials); 325 | } 326 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-core/src/main/java/com/amazonaws/http/AmazonRxNettyHttpClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2017 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.amazonaws.http; 17 | 18 | import java.io.ByteArrayInputStream; 19 | import java.io.InputStream; 20 | import java.net.URLEncoder; 21 | import java.util.List; 22 | import java.util.ArrayList; 23 | import java.util.Arrays; 24 | import java.util.HashMap; 25 | import java.util.LinkedHashMap; 26 | import java.util.Map; 27 | import java.util.concurrent.ConcurrentHashMap; 28 | import java.util.concurrent.TimeUnit; 29 | import java.util.concurrent.atomic.AtomicInteger; 30 | import java.util.concurrent.atomic.AtomicReference; 31 | 32 | import org.slf4j.Logger; 33 | import org.slf4j.LoggerFactory; 34 | 35 | import rx.Observable; 36 | import rx.schedulers.Schedulers; 37 | 38 | import io.netty.buffer.ByteBuf; 39 | import io.netty.handler.codec.http.HttpMethod; 40 | import io.netty.handler.codec.http.HttpResponseStatus; 41 | import io.netty.channel.ChannelOption; 42 | import io.netty.channel.ChannelPipeline; 43 | import io.netty.channel.ChannelDuplexHandler; 44 | import io.netty.channel.ChannelHandlerContext; 45 | import io.netty.handler.codec.http.HttpContentDecompressor; 46 | import iep.io.reactivex.netty.RxNetty; 47 | import iep.io.reactivex.netty.client.PooledConnectionReleasedEvent; 48 | import iep.io.reactivex.netty.protocol.http.AbstractHttpConfigurator; 49 | import iep.io.reactivex.netty.protocol.http.server.HttpServerRequest; 50 | import iep.io.reactivex.netty.protocol.http.server.HttpServerResponse; 51 | import iep.io.reactivex.netty.protocol.http.server.HttpResponseHeaders; 52 | import iep.io.reactivex.netty.protocol.http.client.ClientRequestResponseConverter; 53 | import iep.io.reactivex.netty.protocol.http.client.HttpClient; 54 | import iep.io.reactivex.netty.protocol.http.client.HttpClient.HttpClientConfig; 55 | import iep.io.reactivex.netty.protocol.http.client.HttpClientRequest; 56 | import iep.io.reactivex.netty.protocol.http.client.HttpClientResponse; 57 | import iep.io.reactivex.netty.protocol.http.client.HttpClientPipelineConfigurator; 58 | import iep.io.reactivex.netty.protocol.http.client.HttpRequestHeaders; 59 | import iep.io.reactivex.netty.pipeline.PipelineConfigurator; 60 | import iep.io.reactivex.netty.pipeline.PipelineConfiguratorComposite; 61 | import iep.io.reactivex.netty.pipeline.ssl.DefaultFactories; 62 | 63 | import org.w3c.dom.Node; 64 | 65 | import com.amazonaws.*; 66 | import com.amazonaws.auth.*; 67 | import com.amazonaws.event.*; 68 | import com.amazonaws.handlers.*; 69 | import com.amazonaws.http.*; 70 | import com.amazonaws.internal.*; 71 | import com.amazonaws.metrics.*; 72 | import com.amazonaws.regions.*; 73 | import com.amazonaws.transform.*; 74 | import com.amazonaws.util.*; 75 | import com.amazonaws.util.AWSRequestMetrics.Field; 76 | 77 | import org.joda.time.format.ISODateTimeFormat; 78 | 79 | import javax.crypto.Mac; 80 | import javax.crypto.spec.SecretKeySpec; 81 | 82 | import javax.xml.bind.DatatypeConverter; 83 | 84 | import java.security.MessageDigest; 85 | 86 | 87 | abstract public class AmazonRxNettyHttpClient extends AmazonWebServiceClient { 88 | 89 | private static final Logger LOGGER = LoggerFactory.getLogger(AmazonRxNettyHttpClient.class); 90 | 91 | private static final Map> CLIENTS = 92 | new ConcurrentHashMap>(); 93 | 94 | protected String mkToken(String... tokens) { 95 | if (tokens.length == 1) 96 | return tokens[0]; 97 | else if (Arrays.stream(tokens).anyMatch(t -> { return t != null; })) 98 | return Arrays.stream(tokens).reduce((s1, s2) -> s1 + "|" + s2).get(); 99 | else 100 | return null; 101 | } 102 | 103 | static { 104 | SignerFactory.registerSigner("AWS4SignerType", RxAWS4Signer.class); 105 | } 106 | 107 | private AWSCredentialsProvider awsCredentialsProvider; 108 | 109 | public AmazonRxNettyHttpClient() { 110 | this(new DefaultAWSCredentialsProviderChain(), new ClientConfiguration()); 111 | } 112 | 113 | public AmazonRxNettyHttpClient(AWSCredentialsProvider awsCredentialsProvider) { 114 | this(awsCredentialsProvider, new ClientConfiguration()); 115 | } 116 | 117 | public AmazonRxNettyHttpClient(ClientConfiguration clientConfiguration) { 118 | this(new DefaultAWSCredentialsProviderChain(), clientConfiguration); 119 | } 120 | 121 | public AmazonRxNettyHttpClient( 122 | AWSCredentialsProvider awsCredentialsProvider, 123 | ClientConfiguration clientConfiguration 124 | ) { 125 | super(clientConfiguration); 126 | this.awsCredentialsProvider = awsCredentialsProvider; 127 | init(); 128 | } 129 | 130 | abstract protected void init(); 131 | 132 | private Observable getBackoffStrategyDelay(Request request, int cnt, AmazonClientException error) { 133 | if (cnt == 0) return Observable.just(0L); 134 | else { 135 | long delay = clientConfiguration.getRetryPolicy().getBackoffStrategy().delayBeforeNextRetry(request.getOriginalRequest(), error, cnt); 136 | return Observable.timer(delay, TimeUnit.MILLISECONDS); 137 | } 138 | } 139 | 140 | protected Observable invokeStax( 141 | Request request, 142 | Unmarshaller unmarshaller, 143 | List> errorUnmarshallers, 144 | ExecutionContext executionContext 145 | ) { 146 | StaxRxNettyResponseHandler responseHandler = new StaxRxNettyResponseHandler(unmarshaller); 147 | XmlRxNettyErrorResponseHandler errorResponseHandler = new XmlRxNettyErrorResponseHandler(errorUnmarshallers); 148 | 149 | return invoke(request, responseHandler, errorResponseHandler, executionContext); 150 | } 151 | 152 | protected Observable invokeJson( 153 | Request request, 154 | Unmarshaller unmarshaller, 155 | List errorUnmarshallers, 156 | ExecutionContext executionContext 157 | ) { 158 | JsonRxNettyResponseHandler responseHandler = new JsonRxNettyResponseHandler(unmarshaller); 159 | JsonRxNettyErrorResponseHandler errorResponseHandler = new JsonRxNettyErrorResponseHandler(request.getServiceName(), errorUnmarshallers); 160 | 161 | return invoke(request, responseHandler, errorResponseHandler, executionContext); 162 | } 163 | 164 | protected Observable invokeJsonV2( 165 | Request request, 166 | Unmarshaller unmarshaller, 167 | List errorUnmarshallers, 168 | ExecutionContext executionContext 169 | ) { 170 | JsonRxNettyResponseHandler responseHandler = new JsonRxNettyResponseHandler(unmarshaller); 171 | JsonRxNettyErrorResponseHandlerV2 errorResponseHandler = new JsonRxNettyErrorResponseHandlerV2(request.getServiceName(), errorUnmarshallers); 172 | 173 | return invoke(request, responseHandler, errorResponseHandler, executionContext); 174 | } 175 | 176 | protected Observable invoke( 177 | Request request, 178 | RxNettyResponseHandler> responseHandler, 179 | RxNettyResponseHandler errorResponseHandler, 180 | ExecutionContext executionContext 181 | ) { 182 | return Observable.defer(() -> { 183 | final AtomicReference error = 184 | new AtomicReference(null); 185 | 186 | final AtomicInteger cnt = new AtomicInteger(0); 187 | 188 | final Map> originalParameters = 189 | new LinkedHashMap>(request.getParameters()); 190 | final Map originalHeaders = 191 | new HashMap(request.getHeaders()); 192 | // Always mark the input stream before execution. 193 | final InputStream originalContent = request.getContent(); 194 | if (originalContent != null && originalContent.markSupported()) { 195 | AmazonWebServiceRequest awsreq = request.getOriginalRequest(); 196 | final int readLimit = awsreq.getRequestClientOptions().getReadLimit(); 197 | originalContent.mark(readLimit); 198 | } 199 | 200 | return Observable.using( 201 | () -> { 202 | if (cnt.get() == 0) return Boolean.valueOf(false); 203 | return Boolean.valueOf(true); 204 | }, 205 | (isPrepared) -> { 206 | assert(cnt.get() == 0 || error.get() != null); 207 | if ( 208 | cnt.get() == 0 || 209 | ( 210 | cnt.get() < clientConfiguration.getRetryPolicy().getMaxErrorRetry() && 211 | clientConfiguration.getRetryPolicy().getRetryCondition().shouldRetry( 212 | request.getOriginalRequest(), error.get(), cnt.get() 213 | ) 214 | ) 215 | ) { 216 | return Observable.defer(() -> { 217 | request.setParameters(originalParameters); 218 | request.setHeaders(originalHeaders); 219 | request.setContent(originalContent); 220 | return prepareRequest(request, executionContext); 221 | }) 222 | .flatMap(v -> { return getBackoffStrategyDelay(request, cnt.get(), error.get()); }) 223 | .flatMap(i -> { 224 | try { 225 | return invokeImpl(request, responseHandler, errorResponseHandler, executionContext); 226 | } 227 | catch (java.io.UnsupportedEncodingException e) { 228 | return Observable.error(e); 229 | } 230 | }) 231 | .doOnNext(n -> { 232 | error.set(null); 233 | }) 234 | .onErrorResumeNext(t -> { 235 | if (t instanceof AmazonClientException) error.set((AmazonClientException) t); 236 | else error.set(new AmazonClientException(t)); 237 | return Observable.empty(); 238 | }); 239 | } 240 | else return Observable.error(error.get()); 241 | }, 242 | (isPrepared) -> { 243 | cnt.getAndIncrement(); 244 | } 245 | ) 246 | .repeat() 247 | .first(); 248 | }); 249 | } 250 | 251 | protected Observable prepareRequest( 252 | Request request, 253 | ExecutionContext executionContext 254 | ) { 255 | return Observable.defer(() -> { 256 | request.setEndpoint(endpoint); 257 | request.setTimeOffset(timeOffset); 258 | request.addHeader("User-agent", "rx-" + clientConfiguration.getUserAgent()); 259 | if (clientConfiguration.useGzip()) 260 | request.addHeader("Accept-encoding", "gzip"); 261 | AmazonWebServiceRequest originalRequest = request.getOriginalRequest(); 262 | 263 | AWSCredentials credentials = request.getOriginalRequest().getRequestCredentials(); 264 | if (credentials == null) { 265 | credentials = awsCredentialsProvider.getCredentials(); 266 | } 267 | 268 | executionContext.setCredentials(credentials); 269 | 270 | for (RequestHandler2 requestHandler2 : requestHandler2s(executionContext)) { 271 | if (requestHandler2 instanceof CredentialsRequestHandler) 272 | ((CredentialsRequestHandler) requestHandler2).setCredentials( 273 | executionContext.getCredentials() 274 | ); 275 | requestHandler2.beforeRequest(request); 276 | } 277 | 278 | ProgressListener listener = originalRequest.getGeneralProgressListener(); 279 | 280 | String serviceName = request.getServiceName().substring(6).toLowerCase(); 281 | if (serviceName.endsWith("v2")) 282 | serviceName = serviceName.substring(0, serviceName.length() - 2); 283 | if (serviceName.equals("cloudwatch")) 284 | serviceName = "monitoring"; 285 | String hostName = endpoint.getHost(); 286 | String regionName = AwsHostNameUtils.parseRegionName(hostName, serviceName); 287 | 288 | Signer signer = SignerFactory.getSigner(serviceName, regionName); 289 | signer.sign(request, credentials); 290 | return Observable.just(null); 291 | }); 292 | } 293 | 294 | private List requestHandler2s(ExecutionContext executionContext) { 295 | List requestHandler2s = executionContext.getRequestHandler2s(); 296 | return (requestHandler2s == null) ? java.util.Collections.emptyList() : requestHandler2s; 297 | } 298 | 299 | private void afterError( 300 | Request request, 301 | Response response, 302 | List requestHandler2s, 303 | AmazonClientException e 304 | ) { 305 | for (RequestHandler2 handler2 : requestHandler2s) { 306 | handler2.afterError(request, response, e); 307 | } 308 | } 309 | 310 | private void afterResponse( 311 | Request request, 312 | List requestHandler2s, 313 | Response response 314 | ) { 315 | for (RequestHandler2 handler2 : requestHandler2s) { 316 | handler2.afterResponse(request, response); 317 | } 318 | } 319 | 320 | protected Observable invokeImpl( 321 | Request request, 322 | RxNettyResponseHandler> responseHandler, 323 | RxNettyResponseHandler errorResponseHandler, 324 | ExecutionContext executionContext 325 | ) throws java.io.UnsupportedEncodingException { 326 | 327 | return Observable.defer(() -> { 328 | final List requestHandler2s = requestHandler2s(executionContext); 329 | StringBuffer sbPath = new StringBuffer(); 330 | if (request.getResourcePath() != null) sbPath.append(request.getResourcePath()); 331 | if (sbPath.length() == 0) sbPath.append("/"); 332 | 333 | String content = null; 334 | if (request.getContent() != null) 335 | content = ((StringInputStream) request.getContent()).getString(); 336 | 337 | String queryString = RxSdkHttpUtils.encodeParameters(request); 338 | if (RxSdkHttpUtils.usePayloadForQueryParameters(request)) { 339 | request.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8"); 340 | content = queryString; 341 | } 342 | else if (queryString != null) { 343 | sbPath.append("?").append(queryString); 344 | } 345 | 346 | HttpClientRequest rxRequest = HttpClientRequest.create( 347 | HttpMethod.valueOf(request.getHttpMethod().toString()), 348 | sbPath.toString() 349 | ); 350 | HttpRequestHeaders rxHeaders = rxRequest.getHeaders(); 351 | request.getHeaders().entrySet().stream().forEach(e -> { 352 | rxHeaders.set(e.getKey(), e.getValue()); 353 | }); 354 | 355 | if (content != null) rxRequest.withContent(content); 356 | 357 | return getClient(endpoint.getHost()).submit(rxRequest) 358 | .flatMap(response -> { 359 | if (response.getStatus().code() / 100 == 2) { 360 | try { 361 | return responseHandler.handle(response).map(r -> { 362 | Response awsResponse = new Response(r.getResult(), null); 363 | afterResponse(request, requestHandler2s, awsResponse); 364 | return awsResponse.getAwsResponse(); 365 | }); 366 | } 367 | catch (Exception e) { 368 | return Observable.error(e); 369 | } 370 | } 371 | else { 372 | try { 373 | return errorResponseHandler.handle(response).flatMap(e -> { 374 | e.setServiceName(request.getServiceName()); 375 | afterError(request, null, requestHandler2s, e); 376 | return Observable.error(e); 377 | }); 378 | } 379 | catch (Exception e) { 380 | return Observable.error(e); 381 | } 382 | } 383 | }); 384 | }) 385 | .onErrorResumeNext(t -> { 386 | if (t instanceof AmazonClientException) return Observable.error(t); 387 | else return Observable.error(new AmazonClientException(t)); 388 | }); 389 | } 390 | 391 | private HttpClient getClient(String host) { 392 | Protocol protocol = clientConfiguration.getProtocol(); 393 | String key = protocol + "|" + host; 394 | if (!CLIENTS.containsKey(key)) { 395 | boolean isSecure; 396 | int port; 397 | if (Protocol.HTTP.equals(protocol)) { 398 | isSecure = false; 399 | port = 80; 400 | } 401 | else if (Protocol.HTTPS.equals(protocol)) { 402 | isSecure = true; 403 | port = 443; 404 | } 405 | else { 406 | throw new IllegalStateException("unknown protocol: " + protocol); 407 | } 408 | 409 | HttpClientConfig config = new HttpClient.HttpClientConfig.Builder() 410 | .setFollowRedirect(true) 411 | .readTimeout(clientConfiguration.getSocketTimeout(), TimeUnit.MILLISECONDS) 412 | .responseSubscriptionTimeout(5000, TimeUnit.MILLISECONDS) 413 | .build(); 414 | 415 | HttpClient client = RxNetty.newHttpClientBuilder(host, port) 416 | .withName(host + "." + port) 417 | .config(config) 418 | .channelOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, clientConfiguration.getConnectionTimeout()) 419 | //.enableWireLogging(io.netty.handler.logging.LogLevel.ERROR) 420 | .withMaxConnections(clientConfiguration.getMaxConnections()) 421 | .withIdleConnectionsTimeoutMillis(60000) 422 | .withSslEngineFactory((isSecure) ? DefaultFactories.trustAll() : null) 423 | .pipelineConfigurator( 424 | new PipelineConfiguratorComposite,HttpClientRequest>( 425 | new HttpClientPipelineConfigurator(), 426 | new HttpDecompressionConfigurator() 427 | ) 428 | ) 429 | .appendPipelineConfigurator( 430 | pipeline -> pipeline.addLast(new ActiveLifeTracker(clientConfiguration.getConnectionTTL())) 431 | ) 432 | .disableAutoReleaseBuffers() 433 | .build(); 434 | CLIENTS.putIfAbsent(key, client); 435 | } 436 | return CLIENTS.get(key); 437 | } 438 | 439 | public class HttpDecompressionConfigurator implements PipelineConfigurator { 440 | @Override 441 | public void configureNewPipeline(ChannelPipeline pipeline) { 442 | pipeline.addLast("deflater", new HttpContentDecompressor()); 443 | } 444 | } 445 | 446 | private static class ActiveLifeTracker extends ChannelDuplexHandler { 447 | private long activationTime; 448 | private long ttl; 449 | 450 | public ActiveLifeTracker(long ttl) { 451 | this.ttl = ttl; 452 | } 453 | 454 | @Override 455 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 456 | activationTime = System.currentTimeMillis(); 457 | super.channelActive(ctx); 458 | } 459 | 460 | @Override 461 | public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { 462 | if (evt instanceof PooledConnectionReleasedEvent) { 463 | long timeActive = System.currentTimeMillis() - activationTime; 464 | if (ttl >= 0 && timeActive > ttl) { 465 | ctx.channel().attr(ClientRequestResponseConverter.DISCARD_CONNECTION).set(true); 466 | } 467 | } 468 | super.userEventTriggered(ctx, evt); 469 | } 470 | } 471 | } 472 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-core/src/main/java/com/amazonaws/http/JsonRxNettyErrorResponseHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2017 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.amazonaws.http; 17 | 18 | import java.io.ByteArrayOutputStream; 19 | import java.io.IOException; 20 | import java.util.List; 21 | 22 | import com.amazonaws.AmazonClientException; 23 | import com.amazonaws.AmazonServiceException; 24 | import com.amazonaws.AmazonServiceException.ErrorType; 25 | import com.amazonaws.transform.JsonErrorUnmarshaller; 26 | import com.amazonaws.util.json.JSONObject; 27 | import com.amazonaws.util.RxSchedulers; 28 | 29 | import rx.Observable; 30 | import io.netty.buffer.ByteBuf; 31 | import io.netty.util.ReferenceCountUtil; 32 | import iep.io.reactivex.netty.protocol.http.client.HttpClientResponse; 33 | 34 | public class JsonRxNettyErrorResponseHandler implements RxNettyResponseHandler { 35 | 36 | /** 37 | * Services using AWS JSON 1.1 protocol with HTTP binding send the error 38 | * type information in the response headers, instead of the content. 39 | */ 40 | private static final String X_AMZN_ERROR_TYPE = "x-amzn-ErrorType"; 41 | 42 | /** 43 | * The list of error response unmarshallers to try to apply to error 44 | * responses. 45 | */ 46 | private List unmarshallerList; 47 | private String serviceName; 48 | 49 | public JsonRxNettyErrorResponseHandler(String serviceName, List exceptionUnmarshallers) { 50 | this.serviceName = serviceName; 51 | this.unmarshallerList = exceptionUnmarshallers; 52 | } 53 | 54 | public AmazonServiceException handle(HttpResponse response) throws Exception { 55 | throw new UnsupportedOperationException("amazon client not supported"); 56 | } 57 | 58 | public Observable handle(HttpClientResponse response) throws Exception { 59 | return response.getContent().reduce( 60 | new ByteArrayOutputStream(), 61 | (out, bb) -> { 62 | try { 63 | bb.readBytes(out, bb.readableBytes()); 64 | return out; 65 | } 66 | catch (java.io.IOException e) { 67 | throw new RuntimeException(e); 68 | } 69 | finally { 70 | ReferenceCountUtil.safeRelease(bb); 71 | } 72 | } 73 | ) 74 | .observeOn(RxSchedulers.computation()) 75 | .map(out -> { 76 | JSONObject jsonErrorMessage; 77 | try { 78 | String s = new String(out.toByteArray()); 79 | if (s.length() == 0 || s.trim().length() == 0) s = "{}"; 80 | jsonErrorMessage = new JSONObject(s); 81 | } catch (Exception e) { 82 | throw new AmazonClientException("Unable to parse error response", e); 83 | } 84 | 85 | String errorTypeFromHeader = parseErrorTypeFromHeader(response); 86 | 87 | AmazonServiceException ase = runErrorUnmarshallers(response, jsonErrorMessage, errorTypeFromHeader); 88 | if (ase == null) return null; 89 | 90 | ase.setServiceName(serviceName); 91 | ase.setStatusCode(response.getStatus().code()); 92 | if (response.getStatus().code() < 500) { 93 | ase.setErrorType(ErrorType.Client); 94 | } else { 95 | ase.setErrorType(ErrorType.Service); 96 | } 97 | 98 | ase.setRequestId(response.getHeaders().get("X-Amzn-RequestId")); 99 | 100 | return ase; 101 | }); 102 | } 103 | 104 | private AmazonServiceException runErrorUnmarshallers(HttpClientResponse errorResponse, JSONObject json, String errorTypeFromHeader) { 105 | /* 106 | * We need to select which exception unmarshaller is the correct one to 107 | * use from all the possible exceptions this operation can throw. 108 | * Currently we rely on JsonErrorUnmarshaller.match(...) method which 109 | * checks for the error type parsed either from response headers or the 110 | * content. 111 | */ 112 | try { 113 | for (JsonErrorUnmarshaller unmarshaller : unmarshallerList) { 114 | if (unmarshaller.match(errorTypeFromHeader, json)) { 115 | AmazonServiceException ase = unmarshaller.unmarshall(json); 116 | ase.setStatusCode(errorResponse.getStatus().code()); 117 | return ase; 118 | } 119 | } 120 | 121 | return null; 122 | } 123 | catch (Exception e) { 124 | throw new RuntimeException(e); 125 | } 126 | } 127 | 128 | public boolean needsConnectionLeftOpen() { 129 | return false; 130 | } 131 | 132 | /** 133 | * Attempt to parse the error type from the response headers. 134 | * Returns null if such information is not available in the header. 135 | */ 136 | private String parseErrorTypeFromHeader(HttpClientResponse response) { 137 | String headerValue = response.getHeaders().get(X_AMZN_ERROR_TYPE); 138 | if (headerValue != null) { 139 | int separator = headerValue.indexOf(':'); 140 | if (separator != -1) { 141 | headerValue = headerValue.substring(0, separator); 142 | } 143 | } 144 | return headerValue; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-core/src/main/java/com/amazonaws/http/JsonRxNettyErrorResponseHandlerV2.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2017 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.amazonaws.http; 17 | 18 | import java.io.ByteArrayOutputStream; 19 | import java.io.IOException; 20 | import java.util.HashMap; 21 | import java.util.List; 22 | import java.util.Map; 23 | 24 | import com.amazonaws.AmazonClientException; 25 | import com.amazonaws.AmazonServiceException; 26 | import com.amazonaws.AmazonServiceException.ErrorType; 27 | import com.amazonaws.transform.JsonErrorUnmarshallerV2; 28 | //import com.amazonaws.util.json.JSONObject; 29 | import com.amazonaws.util.RxSchedulers; 30 | import com.amazonaws.internal.http.JsonErrorCodeParser; 31 | 32 | import com.fasterxml.jackson.core.JsonParser; 33 | import com.fasterxml.jackson.databind.JsonNode; 34 | import com.fasterxml.jackson.databind.ObjectMapper; 35 | 36 | import rx.Observable; 37 | import io.netty.buffer.ByteBuf; 38 | import io.netty.util.ReferenceCountUtil; 39 | import iep.io.reactivex.netty.protocol.http.client.HttpClientResponse; 40 | 41 | public class JsonRxNettyErrorResponseHandlerV2 implements RxNettyResponseHandler { 42 | 43 | private static final ObjectMapper MAPPER = new ObjectMapper() 44 | .configure(JsonParser.Feature.ALLOW_COMMENTS, true); 45 | 46 | /** 47 | * Services using AWS JSON 1.1 protocol with HTTP binding send the error 48 | * type information in the response headers, instead of the content. 49 | */ 50 | 51 | private final JsonErrorCodeParser errorCodeParser = JsonErrorCodeParser.DEFAULT_ERROR_CODE_PARSER; 52 | /** 53 | * The list of error response unmarshallers to try to apply to error 54 | * responses. 55 | */ 56 | private List unmarshallerList; 57 | private String serviceName; 58 | 59 | public JsonRxNettyErrorResponseHandlerV2(String serviceName, List exceptionUnmarshallers) { 60 | this.serviceName = serviceName; 61 | this.unmarshallerList = exceptionUnmarshallers; 62 | } 63 | 64 | public AmazonServiceException handle(HttpResponse response) throws Exception { 65 | throw new UnsupportedOperationException("amazon client not supported"); 66 | } 67 | 68 | public Observable handle(HttpClientResponse response) throws Exception { 69 | return response.getContent().reduce( 70 | new ByteArrayOutputStream(), 71 | (out, bb) -> { 72 | try { 73 | bb.readBytes(out, bb.readableBytes()); 74 | return out; 75 | } 76 | catch (java.io.IOException e) { 77 | throw new RuntimeException(e); 78 | } 79 | finally { 80 | ReferenceCountUtil.safeRelease(bb); 81 | } 82 | } 83 | ) 84 | .observeOn(RxSchedulers.computation()) 85 | .map(out -> { 86 | JsonContent jsonContent; 87 | try { 88 | String s = new String(out.toByteArray()); 89 | if (s.length() == 0 || s.trim().length() == 0) s = "{}"; 90 | jsonContent = new JsonContent(s); 91 | } catch (Exception e) { 92 | throw new AmazonClientException("Unable to parse error response", e); 93 | } 94 | 95 | Map responseHeaders = new HashMap(); 96 | for (String k : response.getHeaders().names()) { 97 | // TODO: comma seperated? 98 | responseHeaders.put(k, response.getHeaders().get(k)); 99 | } 100 | 101 | String errorCode = errorCodeParser.parseErrorCode(responseHeaders, jsonContent.jsonNode); 102 | AmazonServiceException ase = runErrorUnmarshallers(errorCode, jsonContent); 103 | 104 | ase.setErrorCode(errorCode); 105 | ase.setServiceName(serviceName); 106 | ase.setStatusCode(response.getStatus().code()); 107 | ase.setRawResponseContent(jsonContent.rawJsonContent); 108 | 109 | if (response.getStatus().code() < 500) { 110 | ase.setErrorType(ErrorType.Client); 111 | } else { 112 | ase.setErrorType(ErrorType.Service); 113 | } 114 | 115 | ase.setRequestId(response.getHeaders().get("X-Amzn-RequestId")); 116 | 117 | return ase; 118 | }); 119 | } 120 | 121 | private AmazonServiceException runErrorUnmarshallers(String errorCode, JsonContent jsonContent) { 122 | /* 123 | * We need to select which exception unmarshaller is the correct one to 124 | * use from all the possible exceptions this operation can throw. 125 | * Currently we rely on JsonErrorUnmarshaller.match(...) method which 126 | * checks for the error type parsed either from response headers or the 127 | * content. 128 | */ 129 | 130 | try { 131 | for (JsonErrorUnmarshallerV2 unmarshaller : unmarshallerList) { 132 | if (unmarshaller.matchErrorCode(errorCode)) { 133 | AmazonServiceException ase = unmarshaller.unmarshall(jsonContent.jsonNode); 134 | return ase; 135 | } 136 | } 137 | 138 | return null; 139 | } 140 | catch (Exception e) { 141 | throw new RuntimeException(e); 142 | } 143 | } 144 | 145 | public boolean needsConnectionLeftOpen() { 146 | return false; 147 | } 148 | 149 | private static class JsonContent { 150 | private static final ObjectMapper MAPPER = new ObjectMapper() 151 | .configure(JsonParser.Feature.ALLOW_COMMENTS, true); 152 | 153 | public final String rawJsonContent; 154 | public final JsonNode jsonNode; 155 | 156 | private JsonContent(String rawJsonContent) { 157 | this.rawJsonContent = rawJsonContent; 158 | this.jsonNode = parseJsonContent(); 159 | } 160 | 161 | private JsonNode parseJsonContent() { 162 | try { 163 | return MAPPER.readTree(rawJsonContent); 164 | } catch (Exception e) { 165 | //LOG.error("Unable to parse HTTP response content", e); 166 | return null; 167 | } 168 | } 169 | 170 | public boolean isJsonValid() { 171 | return jsonNode != null; 172 | } 173 | 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-core/src/main/java/com/amazonaws/http/JsonRxNettyResponseHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2017 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.amazonaws.http; 17 | 18 | import java.io.IOException; 19 | import java.util.Map; 20 | 21 | import org.apache.commons.logging.Log; 22 | import org.apache.commons.logging.LogFactory; 23 | import com.fasterxml.jackson.core.JsonFactory; 24 | import com.fasterxml.jackson.core.JsonParser; 25 | 26 | import com.amazonaws.AmazonWebServiceResponse; 27 | import com.amazonaws.ResponseMetadata; 28 | import com.amazonaws.internal.CRC32MismatchException; 29 | import com.amazonaws.transform.JsonUnmarshallerContext; 30 | import com.amazonaws.transform.JsonRxNettyUnmarshallerContextImpl; 31 | import com.amazonaws.transform.Unmarshaller; 32 | import com.amazonaws.transform.VoidJsonUnmarshaller; 33 | import com.amazonaws.util.CRC32ChecksumCalculatingInputStream; 34 | import com.amazonaws.util.RxSchedulers; 35 | 36 | import java.io.ByteArrayInputStream; 37 | import java.io.ByteArrayOutputStream; 38 | import rx.Observable; 39 | import rx.functions.*; 40 | import io.netty.buffer.ByteBuf; 41 | import io.netty.util.ReferenceCountUtil; 42 | import iep.io.reactivex.netty.protocol.http.client.HttpClientResponse; 43 | 44 | public class JsonRxNettyResponseHandler implements RxNettyResponseHandler> { 45 | 46 | private Unmarshaller responseUnmarshaller; 47 | 48 | private static final Log log = LogFactory.getLog("com.amazonaws.request"); 49 | 50 | private static JsonFactory jsonFactory = new JsonFactory(); 51 | 52 | public boolean needsConnectionLeftOpen = false; 53 | 54 | public JsonRxNettyResponseHandler(Unmarshaller responseUnmarshaller) { 55 | this.responseUnmarshaller = responseUnmarshaller; 56 | 57 | if (this.responseUnmarshaller == null) { 58 | this.responseUnmarshaller = new VoidJsonUnmarshaller(); 59 | } 60 | } 61 | 62 | public AmazonWebServiceResponse handle(HttpResponse response) throws Exception { 63 | throw new UnsupportedOperationException("apache http client not supported"); 64 | } 65 | 66 | public Observable> handle(HttpClientResponse response) throws Exception { 67 | 68 | return response.getContent().reduce( 69 | new ByteArrayOutputStream(), 70 | (out, bb) -> { 71 | try { 72 | bb.readBytes(out, bb.readableBytes()); 73 | return out; 74 | } 75 | catch (java.io.IOException e) { 76 | throw new RuntimeException(e); 77 | } 78 | finally { 79 | ReferenceCountUtil.safeRelease(bb); 80 | } 81 | }) 82 | .observeOn(RxSchedulers.computation()) 83 | .map(out -> { 84 | return new ByteArrayInputStream(out.toByteArray()); 85 | }) 86 | .map(in -> { 87 | String CRC32Checksum = response.getHeaders().get("x-amz-crc32"); 88 | CRC32ChecksumCalculatingInputStream crc32ChecksumInputStream = null; 89 | JsonParser jsonParser = null; 90 | try { 91 | if (!needsConnectionLeftOpen) { 92 | if (CRC32Checksum != null) { 93 | crc32ChecksumInputStream = new CRC32ChecksumCalculatingInputStream(in); 94 | jsonParser = jsonFactory.createJsonParser(crc32ChecksumInputStream); 95 | } else { 96 | jsonParser = jsonFactory.createJsonParser(in); 97 | } 98 | } 99 | 100 | AmazonWebServiceResponse awsResponse = new AmazonWebServiceResponse(); 101 | JsonUnmarshallerContext unmarshallerContext = new JsonRxNettyUnmarshallerContextImpl(jsonParser, response.getHeaders()); 102 | registerAdditionalMetadataExpressions(unmarshallerContext); 103 | 104 | T result = responseUnmarshaller.unmarshall(unmarshallerContext); 105 | 106 | if (CRC32Checksum != null) { 107 | long serverSideCRC = Long.parseLong(CRC32Checksum); 108 | long clientSideCRC = crc32ChecksumInputStream.getCRC32Checksum(); 109 | if (clientSideCRC != serverSideCRC) { 110 | throw new CRC32MismatchException("Client calculated crc32 checksum didn't match that calculated by server side [" + clientSideCRC + " != " + serverSideCRC + "]"); 111 | } 112 | } 113 | 114 | awsResponse.setResult(result); 115 | 116 | Map metadata = unmarshallerContext.getMetadata(); 117 | metadata.put(ResponseMetadata.AWS_REQUEST_ID, response.getHeaders().get("x-amzn-RequestId")); 118 | awsResponse.setResponseMetadata(new ResponseMetadata(metadata)); 119 | 120 | log.trace("Done parsing service response"); 121 | return awsResponse; 122 | } 123 | catch (java.io.IOException e) { 124 | throw new RuntimeException(e); 125 | } 126 | catch (java.lang.Exception e) { 127 | throw new RuntimeException(e); 128 | } 129 | finally { 130 | if (!needsConnectionLeftOpen) { 131 | try {jsonParser.close();} catch (Exception e) {} 132 | } 133 | } 134 | }); 135 | } 136 | 137 | protected void registerAdditionalMetadataExpressions(JsonUnmarshallerContext unmarshallerContext) {} 138 | 139 | public boolean needsConnectionLeftOpen() { 140 | return needsConnectionLeftOpen; 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-core/src/main/java/com/amazonaws/http/RxNettyResponseHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2017 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.amazonaws.http; 17 | 18 | import rx.Observable; 19 | import io.netty.buffer.ByteBuf; 20 | import iep.io.reactivex.netty.protocol.http.client.HttpClientResponse; 21 | 22 | import com.amazonaws.AmazonWebServiceResponse; 23 | 24 | interface RxNettyResponseHandler extends HttpResponseHandler { 25 | public Observable handle(HttpClientResponse response) throws Exception; 26 | } 27 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-core/src/main/java/com/amazonaws/http/StaxRxNettyResponseHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2017 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.amazonaws.http; 17 | 18 | import com.amazonaws.AmazonWebServiceResponse; 19 | import com.amazonaws.ResponseMetadata; 20 | import com.amazonaws.transform.StaxUnmarshallerContext; 21 | import com.amazonaws.transform.Unmarshaller; 22 | import com.amazonaws.transform.VoidStaxUnmarshaller; 23 | import com.amazonaws.util.RxSchedulers; 24 | import iep.io.reactivex.netty.protocol.http.client.HttpClientResponse; 25 | import io.netty.buffer.ByteBuf; 26 | import io.netty.util.ReferenceCountUtil; 27 | import org.apache.commons.logging.Log; 28 | import org.apache.commons.logging.LogFactory; 29 | import rx.Observable; 30 | import rx.functions.Func2; 31 | 32 | import javax.xml.stream.XMLEventReader; 33 | import javax.xml.stream.XMLInputFactory; 34 | import javax.xml.stream.XMLStreamException; 35 | import java.io.ByteArrayInputStream; 36 | import java.io.ByteArrayOutputStream; 37 | import java.util.HashMap; 38 | import java.util.Map; 39 | 40 | /** 41 | * Default implementation of HttpResponseHandler that handles a successful 42 | * response from an AWS service and unmarshalls the result using a StAX 43 | * unmarshaller. 44 | * 45 | * @param 46 | * Indicates the type being unmarshalled by this response handler. 47 | */ 48 | public class StaxRxNettyResponseHandler implements RxNettyResponseHandler> { 49 | 50 | /** The StAX unmarshaller to use when handling the response */ 51 | private Unmarshaller responseUnmarshaller; 52 | 53 | /** Shared logger for profiling information */ 54 | private static final Log log = LogFactory.getLog("com.amazonaws.request"); 55 | 56 | /** Shared factory for creating XML event readers */ 57 | private static final XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); 58 | 59 | 60 | /** 61 | * Constructs a new response handler that will use the specified StAX 62 | * unmarshaller to unmarshall the service response and uses the specified 63 | * response element path to find the root of the business data in the 64 | * service's response. 65 | * 66 | * @param responseUnmarshaller 67 | * The StAX unmarshaller to use on the response. 68 | */ 69 | public StaxRxNettyResponseHandler(Unmarshaller responseUnmarshaller) { 70 | this.responseUnmarshaller = responseUnmarshaller; 71 | if (this.responseUnmarshaller == null) { 72 | this.responseUnmarshaller = new VoidStaxUnmarshaller(); 73 | } 74 | } 75 | 76 | @Override 77 | public AmazonWebServiceResponse handle(HttpResponse r) { 78 | throw new UnsupportedOperationException("apache client not suppported"); 79 | } 80 | 81 | @Override 82 | public Observable> handle(HttpClientResponse response) throws Exception { 83 | return response.getContent().reduce( 84 | new ByteArrayOutputStream(), 85 | new Func2() { 86 | @Override 87 | public ByteArrayOutputStream call(ByteArrayOutputStream out, ByteBuf bb) { 88 | try { 89 | bb.readBytes(out, bb.readableBytes()); 90 | return out; 91 | } 92 | catch (java.io.IOException e) { 93 | throw new RuntimeException(e); 94 | } finally { 95 | ReferenceCountUtil.safeRelease (bb); 96 | } 97 | } 98 | } 99 | ) 100 | .observeOn(RxSchedulers.computation()) 101 | .flatMap(out -> { 102 | byte[] bytes = out.toByteArray(); 103 | if (bytes.length == 0) bytes = "".getBytes(); 104 | ByteArrayInputStream in = new ByteArrayInputStream(bytes); 105 | XMLEventReader reader = null; 106 | try { 107 | reader = xmlInputFactory.createXMLEventReader(in); 108 | } 109 | catch (XMLStreamException e) { 110 | throw new RuntimeException(e); 111 | } 112 | try { 113 | Map responseHeaders = new HashMap(); 114 | for (String k : response.getHeaders().names()) { 115 | // TODO: comma seperated? 116 | responseHeaders.put(k, response.getHeaders().get(k)); 117 | } 118 | AmazonWebServiceResponse awsResponse = new AmazonWebServiceResponse(); 119 | StaxUnmarshallerContext unmarshallerContext = new StaxUnmarshallerContext(reader, responseHeaders); 120 | unmarshallerContext.registerMetadataExpression("ResponseMetadata/RequestId", 2, ResponseMetadata.AWS_REQUEST_ID); 121 | unmarshallerContext.registerMetadataExpression("requestId", 2, ResponseMetadata.AWS_REQUEST_ID); 122 | registerAdditionalMetadataExpressions(unmarshallerContext); 123 | 124 | T result = responseUnmarshaller.unmarshall(unmarshallerContext); 125 | awsResponse.setResult(result); 126 | 127 | Map metadata = unmarshallerContext.getMetadata(); 128 | if (responseHeaders != null) { 129 | if (responseHeaders.get("x-amzn-RequestId") != null) { 130 | metadata.put(ResponseMetadata.AWS_REQUEST_ID, responseHeaders.get("x-amzn-RequestId")); 131 | } 132 | } 133 | awsResponse.setResponseMetadata(new ResponseMetadata(metadata)); 134 | 135 | return Observable.just(awsResponse); 136 | } 137 | catch (Exception e) { 138 | throw new RuntimeException(e); 139 | } 140 | finally { 141 | try { 142 | reader.close(); 143 | } catch (XMLStreamException e) { 144 | log.warn("Error closing xml parser", e); 145 | } 146 | } 147 | }); 148 | } 149 | 150 | /** 151 | * Hook for subclasses to override in order to collect additional metadata 152 | * from service responses. 153 | * 154 | * @param unmarshallerContext 155 | * The unmarshaller context used to process a service's response 156 | * data. 157 | */ 158 | protected void registerAdditionalMetadataExpressions(StaxUnmarshallerContext unmarshallerContext) {} 159 | 160 | /** 161 | * Since this response handler completely consumes all the data from the 162 | * underlying HTTP connection during the handle method, we don't need to 163 | * keep the HTTP connection open. 164 | * 165 | * @see com.amazonaws.http.HttpResponseHandler#needsConnectionLeftOpen() 166 | */ 167 | public boolean needsConnectionLeftOpen() { 168 | return false; 169 | } 170 | 171 | } 172 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-core/src/main/java/com/amazonaws/http/XmlRxNettyErrorResponseHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2017 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.amazonaws.http; 17 | 18 | import java.io.ByteArrayOutputStream; 19 | import java.io.IOException; 20 | import java.util.List; 21 | 22 | import org.apache.commons.logging.Log; 23 | import org.apache.commons.logging.LogFactory; 24 | import org.w3c.dom.Document; 25 | import org.w3c.dom.Node; 26 | 27 | import com.amazonaws.AmazonClientException; 28 | import com.amazonaws.AmazonServiceException; 29 | import com.amazonaws.transform.Unmarshaller; 30 | import com.amazonaws.util.IOUtils; 31 | import com.amazonaws.util.XpathUtils; 32 | import com.amazonaws.util.RxSchedulers; 33 | 34 | import rx.Observable; 35 | import rx.functions.*; 36 | import io.netty.buffer.ByteBuf; 37 | import io.netty.util.ReferenceCountUtil; 38 | import iep.io.reactivex.netty.protocol.http.client.HttpClientResponse; 39 | 40 | /** 41 | * Implementation of HttpResponseHandler that handles only error responses from 42 | * Amazon Web Services. A list of unmarshallers is passed into the constructor, 43 | * and while handling a response, each unmarshaller is tried, in order, until 44 | * one is found that can successfully unmarshall the error response. If no 45 | * unmarshaller is found that can unmarshall the error response, a generic 46 | * AmazonServiceException is created and populated with the AWS error response 47 | * information (error message, AWS error code, AWS request ID, etc). 48 | */ 49 | public class XmlRxNettyErrorResponseHandler 50 | implements RxNettyResponseHandler { 51 | private static final Log log = LogFactory.getLog(XmlRxNettyErrorResponseHandler.class); 52 | 53 | /** 54 | * The list of error response unmarshallers to try to apply to error 55 | * responses. 56 | */ 57 | private List> unmarshallerList; 58 | 59 | /** 60 | * Constructs a new XmlRxNettyErrorResponseHandler that will handle error 61 | * responses from Amazon services using the specified list of unmarshallers. 62 | * Each unmarshaller will be tried, in order, until one is found that can 63 | * unmarshall the error response. 64 | * 65 | * @param unmarshallerList 66 | * The list of unmarshallers to try using when handling an error 67 | * response. 68 | */ 69 | public XmlRxNettyErrorResponseHandler( 70 | List> unmarshallerList) { 71 | this.unmarshallerList = unmarshallerList; 72 | } 73 | 74 | @Override 75 | public AmazonServiceException handle(HttpResponse response) throws Exception { 76 | throw new UnsupportedOperationException("appache response not supported"); 77 | } 78 | 79 | @Override 80 | public Observable handle(HttpClientResponse response) throws Exception { 81 | // Try to read the error response 82 | return response.getContent().reduce( 83 | new ByteArrayOutputStream(), 84 | new Func2() { 85 | @Override 86 | public ByteArrayOutputStream call(ByteArrayOutputStream out, ByteBuf bb) { 87 | try { 88 | bb.readBytes(out, bb.readableBytes()); 89 | return out; 90 | } 91 | catch (java.io.IOException e) { 92 | throw newAmazonServiceException("Unable to unmarshall error response", response, e); 93 | } 94 | finally { 95 | ReferenceCountUtil.safeRelease(bb); 96 | } 97 | } 98 | } 99 | ) 100 | .observeOn(RxSchedulers.computation()) 101 | .map(new Func1() { 102 | @Override 103 | public Document call(ByteArrayOutputStream out) { 104 | String content = new String(out.toByteArray()); 105 | try { 106 | return XpathUtils.documentFrom(content); 107 | } 108 | catch (Exception e) { 109 | throw newAmazonServiceException( 110 | String.format("Unable to unmarshall error response (%s)", content), response, e 111 | ); 112 | } 113 | } 114 | }) 115 | .map(new Func1() { 116 | @Override 117 | public AmazonServiceException call(Document document) { 118 | for (Unmarshaller unmarshaller : unmarshallerList) { 119 | try { 120 | AmazonServiceException ase = unmarshaller.unmarshall(document); 121 | if (ase != null) { 122 | ase.setStatusCode(response.getStatus().code()); 123 | return ase; 124 | } 125 | } 126 | catch (Exception e) {} 127 | } 128 | throw new AmazonClientException("Unable to unmarshall error response from service"); 129 | } 130 | }) 131 | .onErrorResumeNext(new Func1>() { 132 | @Override 133 | public Observable call(Throwable t) { 134 | if (t instanceof AmazonServiceException) return Observable.just((AmazonServiceException)t); 135 | else return Observable.error(t); 136 | } 137 | }); 138 | } 139 | 140 | /** 141 | * Used to create an {@link newAmazonServiceException} when we failed to 142 | * read the error response or parsed the error response as XML. 143 | */ 144 | private AmazonServiceException newAmazonServiceException( 145 | String errmsg, 146 | HttpClientResponse httpResponse, 147 | Exception readFailure 148 | ) { 149 | AmazonServiceException exception = new AmazonServiceException(errmsg, readFailure); 150 | final int statusCode = httpResponse.getStatus().code(); 151 | exception.setErrorCode(statusCode + " " + httpResponse.getStatus().reasonPhrase()); 152 | exception.setErrorType(AmazonServiceException.ErrorType.Unknown); 153 | exception.setStatusCode(statusCode); 154 | return exception; 155 | } 156 | /** 157 | * Since this response handler completely consumes all the data from the 158 | * underlying HTTP connection during the handle method, we don't need to 159 | * keep the HTTP connection open. 160 | * 161 | * @see com.amazonaws.http.HttpResponseHandler#needsConnectionLeftOpen() 162 | */ 163 | public boolean needsConnectionLeftOpen() { 164 | return false; 165 | } 166 | 167 | } 168 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-core/src/main/java/com/amazonaws/services/NamedServiceResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2017 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.amazonaws.services; 17 | 18 | public class NamedServiceResult extends ServiceResult { 19 | public final String name; 20 | 21 | public NamedServiceResult(long startTime, String name, R result) { 22 | super(startTime, result); 23 | this.name = name; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-core/src/main/java/com/amazonaws/services/PaginatedServiceResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2017 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.amazonaws.services; 17 | 18 | public class PaginatedServiceResult extends ServiceResult { 19 | public final String token; 20 | 21 | public PaginatedServiceResult(long startTime, String token, R result) { 22 | super(startTime, result); 23 | this.token = token; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-core/src/main/java/com/amazonaws/services/ServiceResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2017 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.amazonaws.services; 17 | 18 | public class ServiceResult { 19 | public final long startTime; // currentMillis 20 | public final R result; 21 | 22 | public ServiceResult(long startTime, R result) { 23 | this.startTime = startTime; 24 | this.result = result; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-core/src/main/java/com/amazonaws/transform/JsonRxNettyUnmarshallerContextImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2017 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.amazonaws.transform; 17 | 18 | import com.fasterxml.jackson.core.JsonParser; 19 | 20 | import iep.io.reactivex.netty.protocol.http.client.HttpResponseHeaders; 21 | 22 | import com.amazonaws.http.HttpResponse; 23 | 24 | import com.amazonaws.transform.JsonUnmarshallerContextImpl; 25 | 26 | public class JsonRxNettyUnmarshallerContextImpl extends JsonUnmarshallerContextImpl { 27 | final HttpResponseHeaders httpHeaders; 28 | 29 | public JsonRxNettyUnmarshallerContextImpl(JsonParser jsonParser, HttpResponseHeaders httpHeaders) { 30 | super(jsonParser); 31 | this.httpHeaders = httpHeaders; 32 | } 33 | 34 | @Override 35 | public String getHeader(String header) { 36 | if (httpHeaders == null) return null; 37 | return httpHeaders.get(header); 38 | } 39 | 40 | @Override 41 | public HttpResponse getHttpResponse() { 42 | throw new UnsupportedOperationException("apache client not supported"); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-core/src/main/java/com/amazonaws/util/EventLoopsScheduler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2017 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.amazonaws.util; 17 | 18 | import java.util.concurrent.*; 19 | 20 | import rx.*; 21 | import rx.functions.Action0; 22 | import rx.internal.util.*; 23 | import rx.subscriptions.*; 24 | import rx.internal.schedulers.*; 25 | 26 | public class EventLoopsScheduler extends Scheduler { 27 | private static final String THREAD_NAME_PREFIX = "RxAmazonAWSThreadPool-"; 28 | private static final RxThreadFactory THREAD_FACTORY = new RxThreadFactory(THREAD_NAME_PREFIX); 29 | static final String KEY_MAX_THREADS = "rx.scheduler.max-computation-threads"; 30 | static final int MAX_THREADS; 31 | 32 | static { 33 | int maxThreads = Integer.getInteger(KEY_MAX_THREADS, 0); 34 | int ncpu = Runtime.getRuntime().availableProcessors(); 35 | int max; 36 | if (maxThreads <= 0 || maxThreads > ncpu) { 37 | max = ncpu; 38 | } else { 39 | max = maxThreads; 40 | } 41 | MAX_THREADS = max; 42 | } 43 | static final class FixedSchedulerPool { 44 | final int cores; 45 | 46 | final PoolWorker[] eventLoops; 47 | long n; 48 | 49 | FixedSchedulerPool() { 50 | this.cores = MAX_THREADS; 51 | this.eventLoops = new PoolWorker[cores]; 52 | for (int i = 0; i < cores; i++) { 53 | this.eventLoops[i] = new PoolWorker(THREAD_FACTORY); 54 | } 55 | } 56 | 57 | public PoolWorker getEventLoop() { 58 | return eventLoops[(int)(n++ % cores)]; 59 | } 60 | } 61 | 62 | final FixedSchedulerPool pool; 63 | 64 | public EventLoopsScheduler() { 65 | pool = new FixedSchedulerPool(); 66 | } 67 | 68 | @Override 69 | public Worker createWorker() { 70 | return new EventLoopWorker(pool.getEventLoop()); 71 | } 72 | 73 | public Subscription scheduleDirect(Action0 action) { 74 | PoolWorker pw = pool.getEventLoop(); 75 | return pw.scheduleActual(action, -1, TimeUnit.NANOSECONDS); 76 | } 77 | 78 | private static class EventLoopWorker extends Scheduler.Worker { 79 | private final SubscriptionList serial = new SubscriptionList(); 80 | private final CompositeSubscription timed = new CompositeSubscription(); 81 | private final SubscriptionList both = new SubscriptionList(serial, timed); 82 | private final PoolWorker poolWorker; 83 | 84 | EventLoopWorker(PoolWorker poolWorker) { 85 | this.poolWorker = poolWorker; 86 | } 87 | 88 | @Override 89 | public void unsubscribe() { 90 | both.unsubscribe(); 91 | } 92 | 93 | @Override 94 | public boolean isUnsubscribed() { 95 | return both.isUnsubscribed(); 96 | } 97 | 98 | @Override 99 | public Subscription schedule(Action0 action) { 100 | if (isUnsubscribed()) { 101 | return Subscriptions.unsubscribed(); 102 | } 103 | ScheduledAction s = poolWorker.scheduleActual(action, 0, null, serial); 104 | 105 | return s; 106 | } 107 | @Override 108 | public Subscription schedule(Action0 action, long delayTime, TimeUnit unit) { 109 | if (isUnsubscribed()) { 110 | return Subscriptions.unsubscribed(); 111 | } 112 | ScheduledAction s = poolWorker.scheduleActual(action, delayTime, unit, timed); 113 | 114 | return s; 115 | } 116 | } 117 | 118 | private static final class PoolWorker extends NewThreadWorker { 119 | PoolWorker(ThreadFactory threadFactory) { 120 | super(threadFactory); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-core/src/main/java/com/amazonaws/util/RxSchedulers.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2017 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.amazonaws.util; 17 | 18 | import rx.Scheduler; 19 | 20 | public final class RxSchedulers { 21 | 22 | private final Scheduler computationScheduler; 23 | 24 | private static final RxSchedulers INSTANCE = new RxSchedulers(); 25 | 26 | private RxSchedulers() { 27 | computationScheduler = new EventLoopsScheduler(); 28 | } 29 | 30 | public static Scheduler computation() { 31 | return INSTANCE.computationScheduler; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-core/src/main/java/com/amazonaws/util/RxSdkHttpUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2017 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.amazonaws.util; 17 | 18 | import java.io.UnsupportedEncodingException; 19 | import java.net.URI; 20 | import java.net.URLEncoder; 21 | import java.nio.charset.Charset; 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | import java.util.Map; 25 | import java.util.Map.Entry; 26 | import java.util.regex.Matcher; 27 | import java.util.regex.Pattern; 28 | 29 | import com.amazonaws.SignableRequest; 30 | import com.amazonaws.http.HttpMethodName; 31 | 32 | public class RxSdkHttpUtils { 33 | 34 | private static final String DEFAULT_ENCODING = "UTF-8"; 35 | private static final Charset DEFAULT_CHARSET = Charset.forName(DEFAULT_ENCODING); 36 | 37 | /** 38 | * Regex which matches any of the sequences that we need to fix up after 39 | * URLEncoder.encode(). 40 | */ 41 | private static final Pattern ENCODED_CHARACTERS_PATTERN; 42 | static { 43 | StringBuilder pattern = new StringBuilder(); 44 | 45 | pattern 46 | .append(Pattern.quote("+")) 47 | .append("|") 48 | .append(Pattern.quote("*")) 49 | .append("|") 50 | .append(Pattern.quote("%7E")) 51 | .append("|") 52 | .append(Pattern.quote("%2F")); 53 | 54 | ENCODED_CHARACTERS_PATTERN = Pattern.compile(pattern.toString()); 55 | } 56 | 57 | /** 58 | * Encode a string for use in the path of a URL; uses URLEncoder.encode, 59 | * (which encodes a string for use in the query portion of a URL), then 60 | * applies some postfilters to fix things up per the RFC. Can optionally 61 | * handle strings which are meant to encode a path (ie include '/'es 62 | * which should NOT be escaped). 63 | * 64 | * @param value the value to encode 65 | * @param path true if the value is intended to represent a path 66 | * @return the encoded value 67 | */ 68 | public static String urlEncode(final String value, final boolean path) { 69 | if (value == null) { 70 | return ""; 71 | } 72 | 73 | try { 74 | String encoded = URLEncoder.encode(value, DEFAULT_ENCODING); 75 | 76 | Matcher matcher = ENCODED_CHARACTERS_PATTERN.matcher(encoded); 77 | StringBuffer buffer = new StringBuffer(encoded.length()); 78 | 79 | while (matcher.find()) { 80 | String replacement = matcher.group(0); 81 | 82 | if ("+".equals(replacement)) { 83 | replacement = "%20"; 84 | } else if ("*".equals(replacement)) { 85 | replacement = "%2A"; 86 | } else if ("%7E".equals(replacement)) { 87 | replacement = "~"; 88 | } else if (path && "%2F".equals(replacement)) { 89 | replacement = "/"; 90 | } 91 | 92 | matcher.appendReplacement(buffer, replacement); 93 | } 94 | 95 | matcher.appendTail(buffer); 96 | return buffer.toString(); 97 | 98 | } catch (UnsupportedEncodingException ex) { 99 | throw new RuntimeException(ex); 100 | } 101 | } 102 | 103 | /** 104 | * Returns true if the specified URI is using a non-standard port (i.e. any 105 | * port other than 80 for HTTP URIs or any port other than 443 for HTTPS 106 | * URIs). 107 | * 108 | * @param uri 109 | * 110 | * @return True if the specified URI is using a non-standard port, otherwise 111 | * false. 112 | */ 113 | public static boolean isUsingNonDefaultPort(URI uri) { 114 | String scheme = uri.getScheme().toLowerCase(); 115 | int port = uri.getPort(); 116 | 117 | if (port <= 0) return false; 118 | if (scheme.equals("http") && port == 80) return false; 119 | if (scheme.equals("https") && port == 443) return false; 120 | 121 | return true; 122 | } 123 | 124 | public static boolean usePayloadForQueryParameters(SignableRequest request) { 125 | boolean requestIsPOST = HttpMethodName.POST.equals(request.getHttpMethod()); 126 | boolean requestHasNoPayload = (request.getContent() == null); 127 | 128 | return requestIsPOST && requestHasNoPayload; 129 | } 130 | 131 | /** 132 | * Creates an encoded query string from all the parameters in the specified 133 | * request. 134 | * 135 | * @param request 136 | * The request containing the parameters to encode. 137 | * 138 | * @return Null if no parameters were present, otherwise the encoded query 139 | * string for the parameters present in the specified request. 140 | */ 141 | public static String encodeParameters(SignableRequest request) { 142 | final Map> requestParams = request.getParameters(); 143 | 144 | if (requestParams.isEmpty()) return null; 145 | 146 | final List nameValuePairs = new ArrayList(); 147 | 148 | for (Entry> entry : requestParams.entrySet()) { 149 | String parameterName = entry.getKey(); 150 | for (String value : entry.getValue()) { 151 | nameValuePairs 152 | .add(new BasicNameValuePair(parameterName, value)); 153 | } 154 | } 155 | 156 | return RxURLEncodedUtils.format(nameValuePairs, DEFAULT_CHARSET); 157 | } 158 | 159 | /** 160 | * Append the given path to the given baseUri. 161 | * By default, all slash characters in path will not be url-encoded. 162 | */ 163 | public static String appendUri(String baseUri, String path) { 164 | return appendUri(baseUri, path, false); 165 | } 166 | 167 | /** 168 | * Append the given path to the given baseUri. 169 | * 170 | *

This method will encode the given path but not the given 171 | * baseUri.

172 | * 173 | * @param baseUri The URI to append to (required, may be relative) 174 | * @param path The path to append (may be null or empty) 175 | * @param escapeDoubleSlash Whether double-slash in the path should be escaped to "/%2F" 176 | * @return The baseUri with the (encoded) path appended 177 | */ 178 | public static String appendUri(final String baseUri, String path, final boolean escapeDoubleSlash) { 179 | String resultUri = baseUri; 180 | if (path != null && path.length() > 0) { 181 | if (path.startsWith("/")) { 182 | // trim the trailing slash in baseUri, since the path already starts with a slash 183 | if (resultUri.endsWith("/")) { 184 | resultUri = resultUri.substring(0, resultUri.length() - 1); 185 | } 186 | } else if (!resultUri.endsWith("/")) { 187 | resultUri += "/"; 188 | } 189 | String encodedPath = urlEncode(path, true); 190 | if (escapeDoubleSlash) { 191 | encodedPath = encodedPath.replace("//", "/%2F"); 192 | } 193 | resultUri += encodedPath; 194 | } else if (!resultUri.endsWith("/")) { 195 | resultUri += "/"; 196 | } 197 | 198 | return resultUri; 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-core/src/main/java/com/amazonaws/util/RxURLEncodedUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2017 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.amazonaws.util; 17 | 18 | import java.nio.ByteBuffer; 19 | import java.nio.charset.Charset; 20 | import java.util.BitSet; 21 | import java.util.List; 22 | 23 | import com.google.common.escape.Escaper; 24 | import com.google.common.net.UrlEscapers; 25 | 26 | import com.amazonaws.annotation.Immutable; 27 | 28 | // Copied and extracted from httpcomponents-client-4.3.6. 29 | /** 30 | * A collection of utilities for encoding URLs. 31 | * 32 | * @since 4.0 33 | */ 34 | @Immutable 35 | class RxURLEncodedUtils { 36 | private static final char QP_SEP_A = '&'; 37 | private static final String NAME_VALUE_SEPARATOR = "="; 38 | private static final Escaper URL_FORM_ESCAPER = UrlEscapers.urlFormParameterEscaper(); 39 | 40 | /** 41 | * Returns a String that is suitable for use as an {@code application/x-www-form-urlencoded} 42 | * list of parameters in an HTTP PUT or HTTP POST. 43 | * 44 | * @param parameters The parameters to include. 45 | * @param charset The encoding to use. 46 | * @return An {@code application/x-www-form-urlencoded} string 47 | */ 48 | public static String format( 49 | final List parameters, 50 | final String charset) { 51 | return format(parameters, QP_SEP_A, charset); 52 | } 53 | 54 | /** 55 | * Returns a String that is suitable for use as an {@code application/x-www-form-urlencoded} 56 | * list of parameters in an HTTP PUT or HTTP POST. 57 | * 58 | * @param parameters The parameters to include. 59 | * @param parameterSeparator The parameter separator, by convention, {@code '&'} or {@code ';'}. 60 | * @param charset The encoding to use. 61 | * @return An {@code application/x-www-form-urlencoded} string 62 | * 63 | * @since 4.3 64 | */ 65 | public static String format( 66 | final List parameters, 67 | final char parameterSeparator, 68 | final String charset) { 69 | final StringBuilder result = new StringBuilder(); 70 | for (final NameValuePair parameter : parameters) { 71 | final String encodedName = encodeFormFields(parameter.getName(), charset); 72 | final String encodedValue = encodeFormFields(parameter.getValue(), charset); 73 | if (result.length() > 0) { 74 | result.append(parameterSeparator); 75 | } 76 | result.append(encodedName); 77 | if (encodedValue != null) { 78 | result.append(NAME_VALUE_SEPARATOR); 79 | result.append(encodedValue); 80 | } 81 | } 82 | return result.toString(); 83 | } 84 | 85 | /** 86 | * Returns a String that is suitable for use as an {@code application/x-www-form-urlencoded} 87 | * list of parameters in an HTTP PUT or HTTP POST. 88 | * 89 | * @param parameters The parameters to include. 90 | * @param charset The encoding to use. 91 | * @return An {@code application/x-www-form-urlencoded} string 92 | * 93 | * @since 4.2 94 | */ 95 | public static String format( 96 | final Iterable parameters, 97 | final Charset charset) { 98 | return format(parameters, QP_SEP_A, charset); 99 | } 100 | 101 | /** 102 | * Returns a String that is suitable for use as an {@code application/x-www-form-urlencoded} 103 | * list of parameters in an HTTP PUT or HTTP POST. 104 | * 105 | * @param parameters The parameters to include. 106 | * @param parameterSeparator The parameter separator, by convention, {@code '&'} or {@code ';'}. 107 | * @param charset The encoding to use. 108 | * @return An {@code application/x-www-form-urlencoded} string 109 | * 110 | * @since 4.3 111 | */ 112 | public static String format( 113 | final Iterable parameters, 114 | final char parameterSeparator, 115 | final Charset charset) { 116 | final StringBuilder result = new StringBuilder(); 117 | for (final NameValuePair parameter : parameters) { 118 | final String encodedName = encodeFormFields(parameter.getName(), charset); 119 | final String encodedValue = encodeFormFields(parameter.getValue(), charset); 120 | if (result.length() > 0) { 121 | result.append(parameterSeparator); 122 | } 123 | result.append(encodedName); 124 | if (encodedValue != null) { 125 | result.append(NAME_VALUE_SEPARATOR); 126 | result.append(encodedValue); 127 | } 128 | } 129 | return result.toString(); 130 | } 131 | 132 | /** 133 | * Unreserved characters, i.e. alphanumeric, plus: {@code _ - ! . ~ ' ( ) *} 134 | *

135 | * This list is the same as the {@code unreserved} list in 136 | * RFC 2396 137 | */ 138 | private static final BitSet UNRESERVED = new BitSet(256); 139 | /** 140 | * Punctuation characters: , ; : $ & + = 141 | *

142 | * These are the additional characters allowed by userinfo. 143 | */ 144 | private static final BitSet PUNCT = new BitSet(256); 145 | /** Characters which are safe to use in userinfo, 146 | * i.e. {@link #UNRESERVED} plus {@link #PUNCT}uation */ 147 | private static final BitSet USERINFO = new BitSet(256); 148 | /** Characters which are safe to use in a path, 149 | * i.e. {@link #UNRESERVED} plus {@link #PUNCT}uation plus / @ */ 150 | private static final BitSet PATHSAFE = new BitSet(256); 151 | /** Characters which are safe to use in a query or a fragment, 152 | * i.e. {@link #RESERVED} plus {@link #UNRESERVED} */ 153 | private static final BitSet URIC = new BitSet(256); 154 | 155 | /** 156 | * Reserved characters, i.e. {@code ;/?:@&=+$,[]} 157 | *

158 | * This list is the same as the {@code reserved} list in 159 | * RFC 2396 160 | * as augmented by 161 | * RFC 2732 162 | */ 163 | private static final BitSet RESERVED = new BitSet(256); 164 | 165 | /** 166 | * Safe characters for x-www-form-urlencoded data, as per java.net.URLEncoder and browser behaviour, 167 | * i.e. alphanumeric plus {@code "-", "_", ".", "*"} 168 | */ 169 | private static final BitSet URLENCODER = new BitSet(256); 170 | 171 | static { 172 | // unreserved chars 173 | // alpha characters 174 | for (int i = 'a'; i <= 'z'; i++) { 175 | UNRESERVED.set(i); 176 | } 177 | for (int i = 'A'; i <= 'Z'; i++) { 178 | UNRESERVED.set(i); 179 | } 180 | // numeric characters 181 | for (int i = '0'; i <= '9'; i++) { 182 | UNRESERVED.set(i); 183 | } 184 | UNRESERVED.set('_'); // these are the charactes of the "mark" list 185 | UNRESERVED.set('-'); 186 | UNRESERVED.set('.'); 187 | UNRESERVED.set('*'); 188 | URLENCODER.or(UNRESERVED); // skip remaining unreserved characters 189 | UNRESERVED.set('!'); 190 | UNRESERVED.set('~'); 191 | UNRESERVED.set('\''); 192 | UNRESERVED.set('('); 193 | UNRESERVED.set(')'); 194 | // punct chars 195 | PUNCT.set(','); 196 | PUNCT.set(';'); 197 | PUNCT.set(':'); 198 | PUNCT.set('$'); 199 | PUNCT.set('&'); 200 | PUNCT.set('+'); 201 | PUNCT.set('='); 202 | // Safe for userinfo 203 | USERINFO.or(UNRESERVED); 204 | USERINFO.or(PUNCT); 205 | 206 | // URL path safe 207 | PATHSAFE.or(UNRESERVED); 208 | PATHSAFE.set('/'); // segment separator 209 | PATHSAFE.set(';'); // param separator 210 | PATHSAFE.set(':'); // rest as per list in 2396, i.e. : @ & = + $ , 211 | PATHSAFE.set('@'); 212 | PATHSAFE.set('&'); 213 | PATHSAFE.set('='); 214 | PATHSAFE.set('+'); 215 | PATHSAFE.set('$'); 216 | PATHSAFE.set(','); 217 | 218 | RESERVED.set(';'); 219 | RESERVED.set('/'); 220 | RESERVED.set('?'); 221 | RESERVED.set(':'); 222 | RESERVED.set('@'); 223 | RESERVED.set('&'); 224 | RESERVED.set('='); 225 | RESERVED.set('+'); 226 | RESERVED.set('$'); 227 | RESERVED.set(','); 228 | RESERVED.set('['); // added by RFC 2732 229 | RESERVED.set(']'); // added by RFC 2732 230 | 231 | URIC.or(RESERVED); 232 | URIC.or(UNRESERVED); 233 | } 234 | 235 | private static final int RADIX = 16; 236 | 237 | private static String urlEncode( 238 | final String content, 239 | final Charset charset, 240 | final BitSet safechars, 241 | final boolean blankAsPlus) { 242 | if (content == null) { 243 | return null; 244 | } 245 | final StringBuilder buf = new StringBuilder(); 246 | final ByteBuffer bb = charset.encode(content); 247 | while (bb.hasRemaining()) { 248 | final int b = bb.get() & 0xff; 249 | if (safechars.get(b)) { 250 | buf.append((char) b); 251 | } else if (blankAsPlus && b == ' ') { 252 | buf.append('+'); 253 | } else { 254 | buf.append("%"); 255 | final char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, RADIX)); 256 | final char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, RADIX)); 257 | buf.append(hex1); 258 | buf.append(hex2); 259 | } 260 | } 261 | return buf.toString(); 262 | } 263 | 264 | /** 265 | * Encode/escape www-url-form-encoded content. 266 | *

267 | * Uses the {@link #URLENCODER} set of characters, rather than 268 | * the {@link #UNRSERVED} set; this is for compatibilty with previous 269 | * releases, URLEncoder.encode() and most browsers. 270 | * 271 | * @param content the content to encode, will convert space to '+' 272 | * @param charset the charset to use 273 | * @return encoded string 274 | */ 275 | private static String encodeFormFields(final String content, final String charset) { 276 | if (content == null) { 277 | return null; 278 | } 279 | return encodeFormFields(content, charset != null ? Charset.forName(charset) : StringUtils.UTF8); 280 | } 281 | 282 | /** 283 | * Encode/escape www-url-form-encoded content. 284 | *

285 | * Uses the {@link #URLENCODER} set of characters, rather than 286 | * the {@link #UNRSERVED} set; this is for compatibilty with previous 287 | * releases, URLEncoder.encode() and most browsers. 288 | * 289 | * @param content the content to encode, will convert space to '+' 290 | * @param charset the charset to use 291 | * @return encoded string 292 | */ 293 | private static String encodeFormFields (final String content, final Charset charset) { 294 | if (content == null) { 295 | return null; 296 | } 297 | return URL_FORM_ESCAPER.escape(content); 298 | //return urlEncode(content, charset != null ? charset : StringUtils.UTF8, URLENCODER, true); 299 | } 300 | 301 | /** 302 | * Encode a String using the {@link #USERINFO} set of characters. 303 | *

304 | * Used by URIBuilder to encode the userinfo segment. 305 | * 306 | * @param content the string to encode, does not convert space to '+' 307 | * @param charset the charset to use 308 | * @return the encoded string 309 | */ 310 | static String encUserInfo(final String content, final Charset charset) { 311 | return urlEncode(content, charset, USERINFO, false); 312 | } 313 | 314 | /** 315 | * Encode a String using the {@link #URIC} set of characters. 316 | *

317 | * Used by URIBuilder to encode the query and fragment segments. 318 | * 319 | * @param content the string to encode, does not convert space to '+' 320 | * @param charset the charset to use 321 | * @return the encoded string 322 | */ 323 | static String encUric(final String content, final Charset charset) { 324 | return urlEncode(content, charset, URIC, false); 325 | } 326 | 327 | /** 328 | * Encode a String using the {@link #PATHSAFE} set of characters. 329 | *

330 | * Used by URIBuilder to encode path segments. 331 | * 332 | * @param content the string to encode, does not convert space to '+' 333 | * @param charset the charset to use 334 | * @return the encoded string 335 | */ 336 | static String encPath(final String content, final Charset charset) { 337 | return urlEncode(content, charset, PATHSAFE, false); 338 | } 339 | } 340 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-dynamodb/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # Root logger option 2 | log4j.rootLogger=INFO,console,file 3 | 4 | log4j.appender.console=org.apache.log4j.ConsoleAppender 5 | log4j.appender.console.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p %c{1}:%L - %m%n 7 | 8 | # Direct log messages to a log file 9 | log4j.appender.file=org.apache.log4j.RollingFileAppender 10 | log4j.appender.file.File=sbt.log 11 | log4j.appender.file.MaxFileSize=1MB 12 | log4j.appender.file.MaxBackupIndex=1 13 | log4j.appender.file.layout=org.apache.log4j.PatternLayout 14 | log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p %c{1}:%L - %m%n 15 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-dynamodb/src/test/scala/AmazonDynamoDBSuite.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2017 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.amazonaws.services.dynamodbv2 17 | 18 | import scala.collection.JavaConversions._ 19 | 20 | import org.scalatest.{ FunSuite, BeforeAndAfterAll } 21 | 22 | import rx.lang.scala.JavaConversions._ 23 | 24 | import com.amazonaws.auth.EnvironmentVariableCredentialsProvider 25 | import model._ 26 | 27 | class AmazonDynamoDBSuite extends FunSuite with BeforeAndAfterAll { 28 | 29 | val creds = new EnvironmentVariableCredentialsProvider 30 | val db = new AmazonDynamoDBRxNettyClient(creds) 31 | 32 | override def beforeAll() { 33 | } 34 | 35 | override def afterAll() { 36 | } 37 | 38 | ignore("listTables") { 39 | val r = toScalaObservable(db.listTables()).toBlocking.toList 40 | assert(r.size == 1) 41 | } 42 | 43 | ignore("describeTable - error") { 44 | val table = "does_not_exist" 45 | val req = new DescribeTableRequest().withTableName(table) 46 | intercept[ResourceNotFoundException] { 47 | val r = toScalaObservable(db.describeTable(req)).toBlocking.toList 48 | } 49 | } 50 | 51 | ignore("createTable") { 52 | val tableName = "rx-bpitman-test" 53 | val keyName = "name" 54 | val req = new CreateTableRequest() 55 | .withTableName(tableName) 56 | .withKeySchema(new KeySchemaElement(keyName, KeyType.HASH)) 57 | .withAttributeDefinitions(new AttributeDefinition(keyName, "S")) 58 | .withProvisionedThroughput( 59 | new ProvisionedThroughput() 60 | .withReadCapacityUnits(1) 61 | .withWriteCapacityUnits(1) 62 | ) 63 | val result = toScalaObservable(db.createTable(req)).toBlocking.single 64 | assert(result.result.getTableDescription.getTableName == tableName) 65 | } 66 | 67 | ignore("describeTable") { 68 | val tableName = "rx-bpitman-test" 69 | val req = new DescribeTableRequest().withTableName(tableName) 70 | val r = toScalaObservable(db.describeTable(req)).toBlocking.toList 71 | assert(r.size == 1) 72 | assert(r(0).result.getTable.getTableName == tableName) 73 | } 74 | 75 | ignore("scan") { 76 | val tableName = "rx-bpitman-test" 77 | val req = new ScanRequest().withTableName(tableName) 78 | val r = toScalaObservable(db.scan(req)).toBlocking.toList 79 | assert(r.size == 1) 80 | assert(r(0).result.getItems.size == 1) 81 | } 82 | 83 | case class TestData(k: String, s: String, i: Int, timestamp: Long) { 84 | def json = { 85 | s"""{ \"k\": \"${k}\", \"s\": \"${s}\", \"i\": \"${i}\", \"timestamp\": \"${timestamp}\" }""" 86 | } 87 | } 88 | 89 | ignore("put") { 90 | val tableName = "rx-bpitman-test" 91 | val key = "test" 92 | val data = TestData(key, "a", 1, 123456789L) 93 | val e: Option[TestData] = None 94 | val attributes = Map( 95 | "name" -> new AttributeValue(key), 96 | "data" -> new AttributeValue(data.json), 97 | "timestamp" -> new AttributeValue(data.timestamp.toString) 98 | ) 99 | val req = new PutItemRequest() 100 | .withTableName(tableName) 101 | .withItem(attributes) 102 | .withExpected(Map( "timestamp" -> { 103 | e.map(d => new AttributeValue(d.timestamp.toString)) match { 104 | case None => new ExpectedAttributeValue().withExists(false) 105 | case Some(a) => new ExpectedAttributeValue().withExists(true).withValue(a) 106 | }})) 107 | val r = toScalaObservable(db.putItem(req)).toBlocking.toList 108 | assert(r.size == 1) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-ec2/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # Root logger option 2 | log4j.rootLogger=INFO,console,file 3 | 4 | log4j.appender.console=org.apache.log4j.ConsoleAppender 5 | log4j.appender.console.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p %c{1}:%L - %m%n 7 | 8 | # Direct log messages to a log file 9 | log4j.appender.file=org.apache.log4j.RollingFileAppender 10 | log4j.appender.file.File=sbt.log 11 | log4j.appender.file.MaxFileSize=1MB 12 | log4j.appender.file.MaxBackupIndex=1 13 | log4j.appender.file.layout=org.apache.log4j.PatternLayout 14 | log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p %c{1}:%L - %m%n 15 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-ec2/src/test/scala/AmazonEC2Suite.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2017 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.amazonaws.services.ec2 17 | 18 | import org.scalatest.{ FunSuite, BeforeAndAfterAll } 19 | 20 | import rx.lang.scala.JavaConversions._ 21 | 22 | import com.amazonaws.auth.EnvironmentVariableCredentialsProvider 23 | import model._ 24 | 25 | class AmazonEC2Suite extends FunSuite with BeforeAndAfterAll { 26 | 27 | val creds = new EnvironmentVariableCredentialsProvider 28 | val ec2 = new AmazonEC2RxNettyClient(creds) 29 | 30 | override def beforeAll() { 31 | } 32 | 33 | override def afterAll() { 34 | } 35 | 36 | ignore("describeVpc") { 37 | val r = toScalaObservable(ec2.describeVpcs).toBlocking.toList 38 | assert(r.size == 1) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-route53/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # Root logger option 2 | log4j.rootLogger=INFO,console,file 3 | 4 | log4j.appender.console=org.apache.log4j.ConsoleAppender 5 | log4j.appender.console.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p %c{1}:%L - %m%n 7 | 8 | # Direct log messages to a log file 9 | log4j.appender.file=org.apache.log4j.RollingFileAppender 10 | log4j.appender.file.File=sbt.log 11 | log4j.appender.file.MaxFileSize=1MB 12 | log4j.appender.file.MaxBackupIndex=1 13 | log4j.appender.file.layout=org.apache.log4j.PatternLayout 14 | log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p %c{1}:%L - %m%n 15 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-route53/src/test/scala/AmazonCloudWatchSuite.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2017 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.amazonaws.services.route53 17 | 18 | import org.scalatest.{ FunSuite, BeforeAndAfterAll } 19 | 20 | import rx.lang.scala.JavaConversions._ 21 | 22 | import com.amazonaws.auth.EnvironmentVariableCredentialsProvider 23 | import model._ 24 | 25 | class AmazonRoute53Suite extends FunSuite with BeforeAndAfterAll { 26 | 27 | val creds = new EnvironmentVariableCredentialsProvider 28 | val client = new AmazonRoute53RxNettyClient(creds) 29 | 30 | override def beforeAll() { 31 | } 32 | 33 | override def afterAll() { 34 | } 35 | 36 | ignore("listHostedZones") { 37 | val r = toScalaObservable(client.listHostedZones).toBlocking.toList 38 | assert(r.size == 1) 39 | } 40 | 41 | ignore("listResourceRecordSets") { 42 | val r = toScalaObservable(client.listResourceRecordSets).toBlocking.toList 43 | assert(r.size == 1) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-sqs/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # Root logger option 2 | log4j.rootLogger=INFO,console,file 3 | 4 | log4j.appender.console=org.apache.log4j.ConsoleAppender 5 | log4j.appender.console.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p %c{1}:%L - %m%n 7 | 8 | # Direct log messages to a log file 9 | log4j.appender.file=org.apache.log4j.RollingFileAppender 10 | log4j.appender.file.File=sbt.log 11 | log4j.appender.file.MaxFileSize=1MB 12 | log4j.appender.file.MaxBackupIndex=1 13 | log4j.appender.file.layout=org.apache.log4j.PatternLayout 14 | log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p %c{1}:%L - %m%n 15 | -------------------------------------------------------------------------------- /rx-aws-java-sdk-sqs/src/test/scala/AmazonSQSSuite.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-2017 Netflix, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.amazonaws.services.sqs 17 | 18 | import scala.collection.JavaConversions._ 19 | 20 | import org.scalatest.{ FunSuite, BeforeAndAfterAll } 21 | 22 | import java.util.concurrent.atomic.AtomicReference 23 | import java.net.InetAddress 24 | 25 | import rx.lang.scala.JavaConversions._ 26 | 27 | import com.amazonaws.auth.EnvironmentVariableCredentialsProvider 28 | import model._ 29 | 30 | class AmazonSQSSuite extends FunSuite with BeforeAndAfterAll { 31 | 32 | val creds = new EnvironmentVariableCredentialsProvider 33 | val client = new AmazonSQSRxNettyClient(creds) 34 | 35 | val queueName = s"regression-${System.currentTimeMillis}-${InetAddress.getLocalHost.getHostName}" 36 | val queueUrl = new AtomicReference[String](null) 37 | val receiptHandle = new AtomicReference[String](null) 38 | 39 | val testMessage = "test message body" 40 | 41 | override def beforeAll() { 42 | } 43 | 44 | override def afterAll() { 45 | } 46 | 47 | ignore("describeQueues - aws") { 48 | new AmazonSQSClient(creds).listQueues 49 | } 50 | 51 | ignore("describeQueues") { 52 | val r = toScalaObservable(client.listQueues).toBlocking.toList 53 | assert(r.size == 1) 54 | } 55 | 56 | ignore(s"createQueue - ${queueName}") { 57 | val r = toScalaObservable( 58 | client.createQueue(new CreateQueueRequest().withQueueName(queueName)) 59 | ).toBlocking.toList 60 | assert(r.size == 1) 61 | queueUrl.set(r.head.result.getQueueUrl) 62 | assert(Option(queueUrl.get).isDefined) 63 | } 64 | 65 | ignore(s"sendMessage - ${queueName}") { 66 | val r = toScalaObservable( 67 | client.sendMessage( 68 | new SendMessageRequest().withQueueUrl(queueUrl.get).withMessageBody(testMessage) 69 | ) 70 | ).toBlocking.toList 71 | assert(r.size == 1) 72 | } 73 | 74 | ignore(s"receiveMessage - ${queueName}") { 75 | val r = toScalaObservable( 76 | client.receiveMessage( 77 | new ReceiveMessageRequest().withQueueUrl(queueUrl.get) 78 | ) 79 | ).toBlocking.toList 80 | assert(r.size == 1) 81 | assert(r.head.result.getMessages.size == 1) 82 | assert(r.head.result.getMessages.head.getBody == testMessage) 83 | receiptHandle.set(r.head.result.getMessages.head.getReceiptHandle) 84 | } 85 | 86 | ignore(s"deleteMessage - ${queueName}") { 87 | val r = toScalaObservable( 88 | client.deleteMessage( 89 | new DeleteMessageRequest().withQueueUrl(queueUrl.get).withReceiptHandle(receiptHandle.get) 90 | ) 91 | ).toBlocking.toList 92 | assert(r.size == 1) 93 | } 94 | 95 | ignore(s"deleteQueue - ${queueName}") { 96 | val r = toScalaObservable( 97 | client.deleteQueue(new DeleteQueueRequest().withQueueUrl(queueUrl.get)) 98 | ).toBlocking.toList 99 | assert(r.size == 1) 100 | } 101 | } 102 | --------------------------------------------------------------------------------