diff --git a/.netflixoss b/.netflixoss new file mode 100644 index 0000000000..f591e68404 --- /dev/null +++ b/.netflixoss @@ -0,0 +1,2 @@ +jdk=8 + diff --git a/.travis.yml b/.travis.yml index 03f48b5ce9..ee282a1ffb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,15 @@ language: java jdk: - - oraclejdk7 - - openjdk6 - - openjdk7 \ No newline at end of file +- oraclejdk8 +sudo: false +install: "./installViaTravis.sh" +script: "./buildViaTravis.sh" +cache: + directories: + - "$HOME/.gradle" +env: + global: + - secure: eCJZsdb6dhCyD9qnxzabonrqSn02l+vtoYX/v3DED7AZi5SnbC7iloy7borBm3yz9xcd5ajxuke+Y9eLzLx79DfE9B8Z2/xL1Yx2xSIgFBD8FgFAq62Okdi71SYwP11yGIqLNf0FWmT5UCWZ94nN0QzX7pfagWc2LQ/r+EI+XVs= + - secure: WVfJ5Cix+pIY5BKek9YeaSRKRnUFZZ7WUTDcfQ+64N+BoU0QUMoFt+VDaeigXKauDybUQ47SkVV7HdnBIwIO6wM1dVbw8UHWIa/Y346pxnCOqNsyk2eE6pqgIa2RoQgq2Jx34WBYJVtQ8eMvRF0H33jGUZefov64B5ocZVtf/yY= + - secure: Kj/AIbAO0Iao1umA0hct3aoJR5244m8R9/c9mmVPrtO/gwWin2r82kjdwT62RbQHZqGTTXfBNLUFe//QwQgkUhyJUIvLkmA6tjcJnmpWJUhpZoJMgzTxMueTebDCtsri8haFf5AUD5eAge6EIYcB0hQc4g2QTH9Thqiqa17m77U= + - secure: eKgq3A10rhjFS0oP4KtOvtc9ZcyDkAA/0NABsaMpnHW8phO+/nax3wUgMVzWyz4vzaItB56NmyenpE+3NP5PIoLBmxsOq8Cbt8mZAieWWg6oJdW5w/AMaVbvEnAoewNhxj0CCiSloBDSS+J6y79EdT5uh1vjMHkAQARJO6tVdt0= diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/build.gradle b/build.gradle index 69af4a06ae..27c59c1942 100644 --- a/build.gradle +++ b/build.gradle @@ -1,23 +1,35 @@ -ext.githubProjectName = rootProject.name - buildscript { - repositories { mavenCentral() } - apply from: file('gradle/buildscript.gradle'), to: buildscript + repositories { jcenter() } + dependencies { + classpath 'com.netflix.nebula:gradle-extra-configurations-plugin:2.2.0' + } } -allprojects { - repositories { mavenCentral() } +plugins { + id 'nebula.netflixoss' version '3.0.0' } -apply plugin: 'idea' -apply from: file('gradle/convention.gradle') -apply from: file('gradle/maven.gradle') -apply from: file('gradle/license.gradle') -apply from: file('gradle/release.gradle') +ext.githubProjectName = rootProject.name + +idea { + project { + languageLevel = '1.7' + } +} subprojects { + apply plugin: 'nebula.netflixoss' + apply plugin: 'java' + apply plugin: 'nebula.provided-base' + + repositories { + jcenter() + } group = "com.netflix.${githubProjectName}" + + sourceCompatibility = '1.7' + targetCompatibility = '1.7' sourceSets.test.java.srcDir 'src/main/java' diff --git a/buildViaTravis.sh b/buildViaTravis.sh new file mode 100755 index 0000000000..17a33a5fb9 --- /dev/null +++ b/buildViaTravis.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# This script will build the project. + +if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then + echo -e "Build Pull Request #$TRAVIS_PULL_REQUEST => Branch [$TRAVIS_BRANCH]" + ./gradlew build +elif [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_TAG" == "" ]; then + echo -e 'Build Branch with Snapshot => Branch ['$TRAVIS_BRANCH']' + ./gradlew -Prelease.travisci=true -PbintrayUser="${bintrayUser}" -PbintrayKey="${bintrayKey}" -PsonatypeUsername="${sonatypeUsername}" -PsonatypePassword="${sonatypePassword}" build snapshot +elif [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_TAG" != "" ]; then + echo -e 'Build Branch for Release => Branch ['$TRAVIS_BRANCH'] Tag ['$TRAVIS_TAG']' + case "$TRAVIS_TAG" in + *-rc\.*) + ./gradlew -Prelease.travisci=true -Prelease.useLastTag=true -PbintrayUser="${bintrayUser}" -PbintrayKey="${bintrayKey}" -PsonatypeUsername="${sonatypeUsername}" -PsonatypePassword="${sonatypePassword}" candidate + ;; + *) + ./gradlew -Prelease.travisci=true -Prelease.useLastTag=true -PbintrayUser="${bintrayUser}" -PbintrayKey="${bintrayKey}" -PsonatypeUsername="${sonatypeUsername}" -PsonatypePassword="${sonatypePassword}" final + ;; + esac +else + echo -e 'WARN: Should not be here => Branch ['$TRAVIS_BRANCH'] Tag ['$TRAVIS_TAG'] Pull Request ['$TRAVIS_PULL_REQUEST']' + ./gradlew build +fi diff --git a/codequality/HEADER b/codequality/HEADER deleted file mode 100644 index 3102e4b449..0000000000 --- a/codequality/HEADER +++ /dev/null @@ -1,13 +0,0 @@ -Copyright ${year} Netflix, Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/gradle.properties b/gradle.properties index a5779ee7b6..e69de29bb2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +0,0 @@ -version=1.0.28 diff --git a/gradle/buildViaTravis.sh b/gradle/buildViaTravis.sh new file mode 100755 index 0000000000..d98e5eb603 --- /dev/null +++ b/gradle/buildViaTravis.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# This script will build the project. + +if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then + echo -e "Build Pull Request #$TRAVIS_PULL_REQUEST => Branch [$TRAVIS_BRANCH]" + ./gradlew -Prelease.useLastTag=true build +elif [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_TAG" == "" ]; then + echo -e 'Build Branch with Snapshot => Branch ['$TRAVIS_BRANCH']' + ./gradlew -Prelease.travisci=true -PbintrayUser="${bintrayUser}" -PbintrayKey="${bintrayKey}" -PsonatypeUsername="${sonatypeUsername}" -PsonatypePassword="${sonatypePassword}" build snapshot --stacktrace +elif [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_TAG" != "" ]; then + echo -e 'Build Branch for Release => Branch ['$TRAVIS_BRANCH'] Tag ['$TRAVIS_TAG']' + ./gradlew -Prelease.travisci=true -Prelease.useLastTag=true -PbintrayUser="${bintrayUser}" -PbintrayKey="${bintrayKey}" -PsonatypeUsername="${sonatypeUsername}" -PsonatypePassword="${sonatypePassword}" final --stacktrace +else + echo -e 'WARN: Should not be here => Branch ['$TRAVIS_BRANCH'] Tag ['$TRAVIS_TAG'] Pull Request ['$TRAVIS_PULL_REQUEST']' + ./gradlew -Prelease.useLastTag=true build +fi diff --git a/gradle/buildscript.gradle b/gradle/buildscript.gradle deleted file mode 100644 index 4d6a29aabe..0000000000 --- a/gradle/buildscript.gradle +++ /dev/null @@ -1,13 +0,0 @@ -// Executed in context of buildscript -repositories { - // Repo in addition to maven central - maven { - name 'build-repo' - url 'https://raw.github.com/Netflix-Skunkworks/build-repo/master/releases/' // gradle-release/gradle-release/1.0-SNAPSHOT/gradle-release-1.0-SNAPSHOT.jar - } -} -dependencies { - classpath 'nl.javadude.gradle.plugins:license-gradle-plugin:0.6.0' - classpath 'com.mapvine:gradle-cobertura-plugin:0.1' - classpath 'gradle-release:gradle-release:1.0-SNAPSHOT' -} diff --git a/gradle/check.gradle b/gradle/check.gradle deleted file mode 100644 index 7617f17b35..0000000000 --- a/gradle/check.gradle +++ /dev/null @@ -1,25 +0,0 @@ -subprojects { - // Checkstyle - apply plugin: 'checkstyle' - tasks.withType(Checkstyle) { ignoreFailures = true } - checkstyle { - ignoreFailures = true // Waiting on GRADLE-2163 - configFile = rootProject.file('codequality/checkstyle.xml') - } - - // FindBugs - apply plugin: 'findbugs' - //tasks.withType(Findbugs) { reports.html.enabled true } - - // PMD - apply plugin: 'pmd' - //tasks.withType(Pmd) { reports.html.enabled true } - - apply plugin: 'cobertura' - cobertura { - sourceDirs = sourceSets.main.java.srcDirs - format = 'html' - includes = ['**/*.java', '**/*.groovy'] - excludes = [] - } -} diff --git a/gradle/convention.gradle b/gradle/convention.gradle deleted file mode 100644 index 8b877071d9..0000000000 --- a/gradle/convention.gradle +++ /dev/null @@ -1,83 +0,0 @@ - -// For Artifactory -rootProject.status = version.contains('-SNAPSHOT')?'snapshot':'release' - -subprojects { project -> - apply plugin: 'java' // Plugin as major conventions - - version = rootProject.version - - sourceCompatibility = 1.6 - - // GRADLE-2087 workaround, perform after java plugin - status = rootProject.status - - task sourcesJar(type: Jar, dependsOn:classes) { - from sourceSets.main.allSource - classifier 'sources' - extension 'jar' - } - - task javadocJar(type: Jar, dependsOn:javadoc) { - from javadoc.destinationDir - classifier 'javadoc' - extension 'jar' - } - - configurations.add('sources') - configurations.add('javadoc') - configurations.archives { - extendsFrom configurations.sources - extendsFrom configurations.javadoc - } - - // When outputing to an Ivy repo, we want to use the proper type field - gradle.taskGraph.whenReady { - def isNotMaven = !it.hasTask(project.uploadMavenCentral) - if (isNotMaven) { - def artifacts = project.configurations.sources.artifacts - def sourceArtifact = artifacts.iterator().next() - sourceArtifact.type = 'sources' - } - } - - artifacts { - sources(sourcesJar) { - // Weird Gradle quirk where type will be used for the extension, but only for sources - type 'jar' - } - javadoc(javadocJar) { - type 'javadoc' - } - } - - // Ensure output is on a new line - javadoc.doFirst { println "" } - - configurations { - provided { - description = 'much like compile, but indicates you expect the JDK or a container to provide it. It is only available on the compilation classpath, and is not transitive.' - transitive = true - visible = true - } - } - - project.sourceSets { - main.compileClasspath += project.configurations.provided - main.runtimeClasspath -= project.configurations.provided - test.compileClasspath += project.configurations.provided - test.runtimeClasspath += project.configurations.provided - } -} - -task aggregateJavadoc(type: Javadoc) { - description = 'Aggregate all subproject docs into a single docs directory' - source subprojects.collect {project -> project.sourceSets.main.allJava } - classpath = files(subprojects.collect {project -> project.sourceSets.main.compileClasspath}) - destinationDir = new File(projectDir, 'doc') -} - -// Generate wrapper, which is distributed as part of source to alleviate the need of installing gradle -task createWrapper(type: Wrapper) { - gradleVersion = '1.1' -} diff --git a/gradle/license.gradle b/gradle/license.gradle deleted file mode 100644 index 11a51f1137..0000000000 --- a/gradle/license.gradle +++ /dev/null @@ -1,9 +0,0 @@ -// Dependency for plugin was set in buildscript.gradle - -subprojects { -apply plugin: 'license' //nl.javadude.gradle.plugins.license.LicensePlugin -license { - header rootProject.file('codequality/HEADER') - ext.year = Calendar.getInstance().get(Calendar.YEAR) -} -} diff --git a/gradle/maven.gradle b/gradle/maven.gradle deleted file mode 100644 index a3a3d44240..0000000000 --- a/gradle/maven.gradle +++ /dev/null @@ -1,62 +0,0 @@ -// Maven side of things -subprojects { - apply plugin: 'maven' // Java plugin has to have been already applied for the conf2scope mappings to work - apply plugin: 'signing' - - signing { - required { gradle.taskGraph.hasTask(uploadMavenCentral) } - sign configurations.archives - } - - /** - * Publishing to Maven Central example provided from http://jedicoder.blogspot.com/2011/11/automated-gradle-project-deployment-to.html - */ - task uploadMavenCentral(type:Upload, dependsOn: signArchives) { - configuration = configurations.archives - doFirst { - repositories.mavenDeployer { - beforeDeployment { org.gradle.api.artifacts.maven.MavenDeployment deployment -> signing.signPom(deployment) } - - // To test deployment locally, use the following instead of oss.sonatype.org - //repository(url: "file://localhost/${rootProject.rootDir}/repo") - - repository(url: 'https://oss.sonatype.org/service/local/staging/deploy/maven2') { - authentication(userName: rootProject.sonatypeUsername, password: rootProject.sonatypePassword) - } - - // Prevent datastamp from being appending to artifacts during deployment - uniqueVersion = false - - // Closure to configure all the POM with extra info, common to all projects - pom.project { - name "${project.name}" - description "${project.name} developed by Netflix" - developers { - developer { - id 'netflixgithub' - name 'Netflix Open Source Development' - email 'talent@netflix.com' - } - } - licenses { - license { - name 'The Apache Software License, Version 2.0' - url 'http://www.apache.org/licenses/LICENSE-2.0.txt' - distribution 'repo' - } - } - url "https://github.com/Netflix/${rootProject.githubProjectName}" - scm { - connection "scm:git:git@github.com:Netflix/${rootProject.githubProjectName}.git" - url "scm:git:git@github.com:Netflix/${rootProject.githubProjectName}.git" - developerConnection "scm:git:git@github.com:Netflix/${rootProject.githubProjectName}.git" - } - issueManagement { - system 'github' - url "https://github.com/Netflix/${rootProject.githubProjectName}/issues" - } - } - } - } - } -} diff --git a/gradle/netflix-oss.gradle b/gradle/netflix-oss.gradle deleted file mode 100644 index dc9c9b8559..0000000000 --- a/gradle/netflix-oss.gradle +++ /dev/null @@ -1,69 +0,0 @@ -apply from: 'http://artifacts.netflix.com/gradle-netflix-local/artifactory.gradle' - -def replacements = [ - [ 'org.json', 'json', 'org.json:json:1.0', ['compile', 'runtime'] ], - [ 'org.powermock', 'powermock-api-mockito', 'org.powermock:powermock-mockito:1.4.12', ['provided'] ] -] - -rootProject { -// afterEvaluate { project -> - afterProject { project -> - println project - println "project.configurations: ${project.configurations}" - - [ 'compile', 'runtime', 'provided' ].each { config_name -> - println "config_name: ${config_name}" - def conf = project.configurations.findByName(config_name) - if(conf == null) return - - println "found ${conf}" - - def deps = conf.dependencies - - conf.dependencies.each { d -> - println d - } - - replacements.each { r -> - def dep = deps.find { it.group == r[0] && it.name == r[1] } - if (dep == null) return - - println "removing ${dep}" - deps.remove(dep) - - r[3].each { dep_config_name -> - println "adding ${dep_config_name} ${r[2]}" - project.dependencies.add(dep_config_name, r[2]) - } - } - } -/* - def compileConf = project.configurations.findByName('compile') - if (compileConf != null) { - def deps = compileConf.dependencies - - def jsonDep = deps.find { it.group == 'org.json' && it.name == 'json' } - if (jsonDep != null) { - print "removing ${jsonDep}" - deps.remove(jsonDep) - project.dependencies.add('compile', "org.json:json:1.0") - // project.dependencies.add('compile', "netflix:json:1.0") - } - } - - def providedConf = project.configurations.findByName('provided') - if (providedConf != null) { - def deps = providedConf.dependencies - - def powermockDep = deps.find { it.group == 'org.powermock' && it.name == 'powermock-api-mockito' } - if (powermockDep != null) { - print "removing ${powermockDep}" - deps.remove(powermockDep) - // project.dependencies.add('compile', "org.json:json:1.0") - // project.dependencies.add('compile', "netflix:json:1.0") - } - } -*/ - - } -} diff --git a/gradle/release.gradle b/gradle/release.gradle deleted file mode 100644 index cd135643bd..0000000000 --- a/gradle/release.gradle +++ /dev/null @@ -1,65 +0,0 @@ -apply plugin: 'release' - -// Ignore release plugin's task because it calls out via GradleBuild. This is a good place to put an email to send out -task release(overwrite: true, dependsOn: commitNewVersion) << { - // This is a good place to put an email to send out -} -commitNewVersion.dependsOn updateVersion -updateVersion.dependsOn createReleaseTag -createReleaseTag.dependsOn preTagCommit -def buildTasks = tasks.matching { it.name =~ /:build/ } -preTagCommit.dependsOn buildTasks -preTagCommit.dependsOn checkSnapshotDependencies -//checkSnapshotDependencies.dependsOn confirmReleaseVersion // Introduced in 1.0, forces readLine -//confirmReleaseVersion.dependsOn unSnapshotVersion -checkSnapshotDependencies.dependsOn unSnapshotVersion // Remove once above is fixed -unSnapshotVersion.dependsOn checkUpdateNeeded -checkUpdateNeeded.dependsOn checkCommitNeeded -checkCommitNeeded.dependsOn initScmPlugin - -[ - uploadIvyLocal: 'uploadLocal', - uploadArtifactory: 'artifactoryPublish', // Call out to compile against internal repository - buildWithArtifactory: 'build' // Build against internal repository -].each { key, value -> - // Call out to compile against internal repository - task "${key}"(type: GradleBuild) { - startParameter = project.gradle.startParameter.newInstance() - startParameter.addInitScript( file('gradle/netflix-oss.gradle') ) - startParameter.getExcludedTaskNames().add('check') - tasks = [ 'build', value ] - } -} -task releaseArtifactory(dependsOn: [checkSnapshotDependencies, uploadArtifactory]) - -// Ensure upload happens before taggging but after all pre-checks -releaseArtifactory.dependsOn checkSnapshotDependencies -createReleaseTag.dependsOn releaseArtifactory -gradle.taskGraph.whenReady { taskGraph -> - if ( taskGraph.hasTask(uploadArtifactory) && rootProject.status == 'release' && !taskGraph.hasTask(':release') ) { - throw new GradleException('"release" task has to be run before uploading a release to Artifactory') - } -} -subprojects.each { project -> - project.uploadMavenCentral.dependsOn rootProject.checkSnapshotDependencies - rootProject.createReleaseTag.dependsOn project.uploadMavenCentral - - gradle.taskGraph.whenReady { taskGraph -> - if ( taskGraph.hasTask(project.uploadMavenCentral) && !taskGraph.hasTask(':release') ) { - throw new GradleException('"release" task has to be run before uploading to Maven Central') - } - } -} - -// Prevent plugin from asking for a version number interactively -ext.'gradle.release.useAutomaticVersion' = "true" - -release { - // http://tellurianring.com/wiki/gradle/release - failOnCommitNeeded=true - failOnPublishNeeded=true - failOnUnversionedFiles=true - failOnUpdateNeeded=true - includeProjectNameInTag=true - requireBranch = null -} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index faa569a9a0..d43a4e55af 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9829a99a5b..d8a4dc0bd1 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Aug 14 16:28:54 PDT 2012 +#Thu Sep 10 13:23:01 PDT 2015 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=http\://services.gradle.org/distributions/gradle-1.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.6-bin.zip diff --git a/gradlew b/gradlew index e61422d06d..97fac783e1 100755 --- a/gradlew +++ b/gradlew @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash ############################################################################## ## @@ -42,11 +42,6 @@ case "`uname`" in ;; esac -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" @@ -61,9 +56,9 @@ while [ -h "$PRG" ] ; do fi done SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" +cd "`dirname \"$PRG\"`/" >&- APP_HOME="`pwd -P`" -cd "$SAVED" +cd "$SAVED" >&- CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -114,6 +109,7 @@ fi if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` diff --git a/installViaTravis.sh b/installViaTravis.sh new file mode 100755 index 0000000000..b51fe10e94 --- /dev/null +++ b/installViaTravis.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# This script will build the project. + +if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then + echo -e "Assemble Pull Request #$TRAVIS_PULL_REQUEST => Branch [$TRAVIS_BRANCH]" + ./gradlew assemble +elif [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_TAG" == "" ]; then + echo -e 'Assemble Branch with Snapshot => Branch ['$TRAVIS_BRANCH']' + ./gradlew -Prelease.travisci=true assemble +elif [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_TAG" != "" ]; then + echo -e 'Assemble Branch for Release => Branch ['$TRAVIS_BRANCH'] Tag ['$TRAVIS_TAG']' + ./gradlew -Prelease.travisci=true -Prelease.useLastTag=true assemble +else + echo -e 'WARN: Should not be here => Branch ['$TRAVIS_BRANCH'] Tag ['$TRAVIS_TAG'] Pull Request ['$TRAVIS_PULL_REQUEST']' + ./gradlew assemble +fi \ No newline at end of file diff --git a/zuul-core/build.gradle b/zuul-core/build.gradle index 16774d10f2..bea3aaa0ce 100644 --- a/zuul-core/build.gradle +++ b/zuul-core/build.gradle @@ -1,17 +1,13 @@ -apply plugin: 'java' apply plugin: 'groovy' -apply plugin: 'eclipse' -apply plugin: 'idea' dependencies { compile 'commons-io:commons-io:2.4' - compile 'org.codehaus.groovy:groovy-all:2.3.1' + compile 'org.codehaus.groovy:groovy-all:2.3.6' compile 'org.mockito:mockito-all:1.9.5' compile 'org.slf4j:slf4j-api:1.7.6' provided 'junit:junit-dep:4.10' provided 'javax.servlet:servlet-api:2.5' - groovy "org.codehaus.groovy:groovy-all:2.3.1" compile 'com.netflix.archaius:archaius-core:0.6.0' compile 'com.netflix.servo:servo-core:0.7.2' @@ -30,15 +26,7 @@ javadoc { eclipse { classpath { - plusConfigurations += configurations.provided downloadSources = true downloadJavadoc = true } } - - -idea { - module { - scopes.PROVIDED.plus += configurations.provided - } -} diff --git a/zuul-core/src/main/java/com/netflix/zuul/ZuulFilter.java b/zuul-core/src/main/java/com/netflix/zuul/ZuulFilter.java index 2d87c098a1..225d1a09bd 100644 --- a/zuul-core/src/main/java/com/netflix/zuul/ZuulFilter.java +++ b/zuul-core/src/main/java/com/netflix/zuul/ZuulFilter.java @@ -105,7 +105,7 @@ public boolean isFilterDisabled() { */ public ZuulFilterResult runFilter() { ZuulFilterResult zr = new ZuulFilterResult(); - if (!filterDisabled.get()) { + if (!isFilterDisabled()) { if (shouldFilter()) { Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName()); try { @@ -197,5 +197,49 @@ public Object run() { } + @Test + public void testIsFilterDisabled() { + class TestZuulFilter extends ZuulFilter { + + @Override + public String filterType() { + return null; + } + + @Override + public int filterOrder() { + return 0; + } + + public boolean isFilterDisabled() { + return false; + } + + public boolean shouldFilter() { + return true; + } + + public Object run() { + return null; + } + } + + TestZuulFilter tf1 = spy(new TestZuulFilter()); + TestZuulFilter tf2 = spy(new TestZuulFilter()); + + when(tf1.isFilterDisabled()).thenReturn(false); + when(tf2.isFilterDisabled()).thenReturn(true); + + try { + tf1.runFilter(); + tf2.runFilter(); + verify(tf1, times(1)).run(); + verify(tf2, times(0)).run(); + } catch (Throwable throwable) { + throwable.printStackTrace(); + } + + } + } } diff --git a/zuul-core/src/main/java/com/netflix/zuul/ZuulRunner.java b/zuul-core/src/main/java/com/netflix/zuul/ZuulRunner.java index 7b5964441c..4f01d5fcd1 100644 --- a/zuul-core/src/main/java/com/netflix/zuul/ZuulRunner.java +++ b/zuul-core/src/main/java/com/netflix/zuul/ZuulRunner.java @@ -15,17 +15,10 @@ */ package com.netflix.zuul; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.PrintWriter; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - +import com.netflix.zuul.context.RequestContext; +import com.netflix.zuul.exception.ZuulException; +import com.netflix.zuul.http.HttpServletRequestWrapper; +import com.netflix.zuul.http.HttpServletResponseWrapper; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -33,10 +26,12 @@ import org.mockito.MockitoAnnotations; import org.mockito.runners.MockitoJUnitRunner; -import com.netflix.zuul.context.RequestContext; -import com.netflix.zuul.exception.ZuulException; -import com.netflix.zuul.http.HttpServletRequestWrapper; -import com.netflix.zuul.http.HttpServletResponseWrapper; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.PrintWriter; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.*; /** @@ -48,10 +43,21 @@ */ public class ZuulRunner { + private boolean bufferRequests; + /** * Creates a new ZuulRunner instance. */ public ZuulRunner() { + this.bufferRequests = true; + } + + /** + * + * @param bufferRequests - whether to wrap the ServletRequest in HttpServletRequestWrapper and buffer the body. + */ + public ZuulRunner(boolean bufferRequests) { + this.bufferRequests = bufferRequests; } /** @@ -61,9 +67,15 @@ public ZuulRunner() { * @param servletResponse */ public void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) { - RequestContext.getCurrentContext().setRequest(new HttpServletRequestWrapper(servletRequest)); - RequestContext.getCurrentContext().setResponse(new HttpServletResponseWrapper(servletResponse)); - //RequestContext.getCurrentContext().setResponseStatusCode(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + + RequestContext ctx = RequestContext.getCurrentContext(); + if (bufferRequests) { + ctx.setRequest(new HttpServletRequestWrapper(servletRequest)); + } else { + ctx.setRequest(servletRequest); + } + + ctx.setResponse(new HttpServletResponseWrapper(servletResponse)); } /** diff --git a/zuul-core/src/main/java/com/netflix/zuul/filters/ZuulServletFilter.java b/zuul-core/src/main/java/com/netflix/zuul/filters/ZuulServletFilter.java index 2330235a1f..8e22648ffe 100644 --- a/zuul-core/src/main/java/com/netflix/zuul/filters/ZuulServletFilter.java +++ b/zuul-core/src/main/java/com/netflix/zuul/filters/ZuulServletFilter.java @@ -49,11 +49,14 @@ */ public class ZuulServletFilter implements Filter { - - private ZuulRunner zuulRunner = new ZuulRunner(); + private ZuulRunner zuulRunner; public void init(FilterConfig filterConfig) throws ServletException { + String bufferReqsStr = filterConfig.getInitParameter("buffer-requests"); + boolean bufferReqs = bufferReqsStr != null && bufferReqsStr.equals("true") ? true : false; + + zuulRunner = new ZuulRunner(bufferReqs); } public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { @@ -106,7 +109,6 @@ void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse void error(ZuulException e) { RequestContext.getCurrentContext().setThrowable(e); zuulRunner.error(); - e.printStackTrace(); } public void destroy() { diff --git a/zuul-core/src/main/java/com/netflix/zuul/http/HttpServletRequestWrapper.java b/zuul-core/src/main/java/com/netflix/zuul/http/HttpServletRequestWrapper.java index c5305c8eaa..cec3eff2da 100755 --- a/zuul-core/src/main/java/com/netflix/zuul/http/HttpServletRequestWrapper.java +++ b/zuul-core/src/main/java/com/netflix/zuul/http/HttpServletRequestWrapper.java @@ -33,6 +33,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import java.io.*; +import java.net.SocketTimeoutException; import java.net.URLDecoder; import java.security.Principal; import java.util.*; @@ -60,8 +61,10 @@ public class HttpServletRequestWrapper implements HttpServletRequest { protected static final Logger LOG = LoggerFactory.getLogger(HttpServletRequestWrapper.class); private HttpServletRequest req; - private byte[] contentData; - private HashMap parameters; + private byte[] contentData = null; + private HashMap parameters = null; + + private long bodyBufferingTimeNs = 0; public HttpServletRequestWrapper() { //a trick for Groovy @@ -98,12 +101,11 @@ public HttpServletRequest getRequest() { /** * This method is safe to use multiple times. - * Changing the returned array will not interfere with this class operation. * - * @return The cloned content data. + * @return The request body data. */ public byte[] getContentData() { - return contentData.clone(); + return contentData; } @@ -139,17 +141,25 @@ private void parseRequest() throws IOException { } } - LOG.debug("Path = " + req.getPathInfo()); - LOG.debug("Transfer-Encoding = " + String.valueOf(req.getHeader(ZuulHeaders.TRANSFER_ENCODING))); - LOG.debug("Content-Encoding = " + String.valueOf(req.getHeader(ZuulHeaders.CONTENT_ENCODING))); - - LOG.debug("Content-Length header = " + req.getContentLength()); - if (req.getContentLength() > 0) { + if (shouldBufferBody()) { // Read the request body inputstream into a byte array. ByteArrayOutputStream baos = new ByteArrayOutputStream(); - IOUtils.copy(req.getInputStream(), baos); - contentData = baos.toByteArray(); + try { + // Copy all bytes from inputstream to byte array, and record time taken. + long bufferStartTime = System.nanoTime(); + IOUtils.copy(req.getInputStream(), baos); + bodyBufferingTimeNs = System.nanoTime() - bufferStartTime; + + contentData = baos.toByteArray(); + } catch (SocketTimeoutException e) { + // This can happen if the request body is smaller than the size specified in the + // Content-Length header, and using tomcat APR connector. + LOG.error("SocketTimeoutException reading request body from inputstream. error=" + String.valueOf(e.getMessage())); + if (contentData == null) { + contentData = new byte[0]; + } + } try { LOG.debug("Length of contentData byte array = " + contentData.length); @@ -160,48 +170,46 @@ private void parseRequest() throws IOException { LOG.error("Error checking if request body gzipped!", e); } - String enc = req.getCharacterEncoding(); - - if (enc == null) - enc = "UTF-8"; - String s = new String(contentData, enc), name, value; - StringTokenizer st = new StringTokenizer(s, "&"); - int i; - - boolean decode = req.getContentType() != null && req.getContentType().equalsIgnoreCase("application/x-www-form-urlencoded"); - while (st.hasMoreTokens()) { - s = st.nextToken(); - i = s.indexOf("="); - if (i > 0 && s.length() > i + 1) { - name = s.substring(0, i); - value = s.substring(i + 1); - if (decode) { - try { - name = URLDecoder.decode(name, "UTF-8"); - } catch (Exception e) { + final boolean isPost = req.getMethod().equals("POST"); + + String contentType = req.getContentType(); + final boolean isFormBody = contentType != null && contentType.contains("application/x-www-form-urlencoded"); + + // only does magic body param parsing for POST form bodies + if (isPost && isFormBody) { + String enc = req.getCharacterEncoding(); + + if (enc == null) enc = "UTF-8"; + String s = new String(contentData, enc), name, value; + StringTokenizer st = new StringTokenizer(s, "&"); + int i; + + boolean decode = req.getContentType() != null; + while (st.hasMoreTokens()) { + s = st.nextToken(); + i = s.indexOf("="); + if (i > 0 && s.length() > i + 1) { + name = s.substring(0, i); + value = s.substring(i + 1); + if (decode) { + try { + name = URLDecoder.decode(name, "UTF-8"); + } catch (Exception e) { + } + try { + value = URLDecoder.decode(value, "UTF-8"); + } catch (Exception e) { + } } - try { - value = URLDecoder.decode(value, "UTF-8"); - } catch (Exception e) { + list = mapA.get(name); + if (list == null) { + list = new LinkedList(); + mapA.put(name, list); } + list.add(value); } - list = mapA.get(name); - if (list == null) { - list = new LinkedList(); - mapA.put(name, list); - } - list.add(value); } } - - } else if (req.getContentLength() == -1) { - final String transferEncoding = req.getHeader(ZuulHeaders.TRANSFER_ENCODING); - if (transferEncoding != null && transferEncoding.equals(ZuulHeaders.CHUNKED)) { - RequestContext.getCurrentContext().setChunkedRequestBody(); - LOG.debug("Set flag that request body is chunked."); - } - } else { - LOG.warn("Content-Length is neither greater than zero or -1. = " + req.getContentLength()); } HashMap map = new HashMap(mapA.size() * 2); @@ -214,6 +222,39 @@ private void parseRequest() throws IOException { } + private boolean shouldBufferBody() { + + if (LOG.isDebugEnabled()) { + LOG.debug("Path = " + req.getPathInfo()); + LOG.debug("Transfer-Encoding = " + String.valueOf(req.getHeader(ZuulHeaders.TRANSFER_ENCODING))); + LOG.debug("Content-Encoding = " + String.valueOf(req.getHeader(ZuulHeaders.CONTENT_ENCODING))); + LOG.debug("Content-Length header = " + req.getContentLength()); + } + + boolean should = false; + if (req.getContentLength() > 0) { + should = true; + } + else if (req.getContentLength() == -1) { + final String transferEncoding = req.getHeader(ZuulHeaders.TRANSFER_ENCODING); + if (transferEncoding != null && transferEncoding.equals(ZuulHeaders.CHUNKED)) { + RequestContext.getCurrentContext().setChunkedRequestBody(); + should = true; + } + } + + return should; + } + + /** + * Time taken to buffer the request body in nanoseconds. + * @return + */ + public long getBodyBufferingTimeNs() + { + return bodyBufferingTimeNs; + } + /** * This method is safe to call multiple times. * Calling it will not interfere with getParameterXXX() or getReader(). @@ -224,11 +265,7 @@ private void parseRequest() throws IOException { public ServletInputStream getInputStream() throws IOException { parseRequest(); - if (RequestContext.getCurrentContext().isChunkedRequestBody()) { - return req.getInputStream(); - } else { - return new ServletInputStreamWrapper(contentData); - } + return new ServletInputStreamWrapper(contentData); } /** @@ -681,6 +718,9 @@ public void before() { MockitoAnnotations.initMocks(this); RequestContext.getCurrentContext().setRequest(request); + + method("GET"); + contentType("zuul/test-content-type"); } private void body(byte[] body) throws IOException { @@ -688,6 +728,45 @@ private void body(byte[] body) throws IOException { when(request.getContentLength()).thenReturn(body.length); } + private void method(String s) { + when(request.getMethod()).thenReturn(s); + } + + private void contentType(String s) { + when(request.getContentType()).thenReturn(s); + } + + private static String readZipInputStream(InputStream input) throws IOException { + + byte[] uploadedBytes = getBytesFromInputStream(input); + input.close(); + + /* try to read it as a zip file */ + String uploadFileTxt = null; + ZipInputStream zInput = new ZipInputStream(new ByteArrayInputStream(uploadedBytes)); + ZipEntry zipEntry = zInput.getNextEntry(); + if (zipEntry != null) { + // we have a ZipEntry, so this is a zip file + while (zipEntry != null) { + byte[] fileBytes = getBytesFromInputStream(zInput); + uploadFileTxt = new String(fileBytes); + + zipEntry = zInput.getNextEntry(); + } + } + return uploadFileTxt; + } + + private static byte[] getBytesFromInputStream(InputStream input) throws IOException { + int v = 0; + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + while ((v = input.read()) != -1) { + bos.write(v); + } + bos.close(); + return bos.toByteArray(); + } + @Test public void handlesDuplicateParams() { when(request.getQueryString()).thenReturn("path=one&key1=val1&path=two"); @@ -751,41 +830,52 @@ public void handlesZipRequestBody() throws IOException { assertEquals(body, readZipInputStream(wrapper.getInputStream())); + } + @Test + public void parsesParamsFromFormBody() throws Exception { + method("POST"); + body("one=1&two=2".getBytes()); + contentType("application/x-www-form-urlencoded"); + final HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(request); + final Map params = wrapper.getParameterMap(); + assertTrue(params.containsKey("one")); + assertTrue(params.containsKey("two")); } - public String readZipInputStream(InputStream input) throws IOException { + @Test + public void ignoresParamsInBodyForNonPosts() throws Exception { + method("PUT"); + body("one=1&two=2".getBytes()); + contentType("application/x-www-form-urlencoded"); - byte[] uploadedBytes = getBytesFromInputStream(input); - input.close(); + final HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(request); + final Map params = wrapper.getParameterMap(); + assertFalse(params.containsKey("one")); + } - /* try to read it as a zip file */ - String uploadFileTxt = null; - ZipInputStream zInput = new ZipInputStream(new ByteArrayInputStream(uploadedBytes)); - ZipEntry zipEntry = zInput.getNextEntry(); - if (zipEntry != null) { - // we have a ZipEntry, so this is a zip file - while (zipEntry != null) { - byte[] fileBytes = getBytesFromInputStream(zInput); - uploadFileTxt = new String(fileBytes); + @Test + public void ignoresParamsInBodyForNonForms() throws Exception { + method("POST"); + body("one=1&two=2".getBytes()); + contentType("application/json"); - zipEntry = zInput.getNextEntry(); - } - } - return uploadFileTxt; + final HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(request); + final Map params = wrapper.getParameterMap(); + assertFalse(params.containsKey("one")); } - private byte[] getBytesFromInputStream(InputStream input) throws IOException { - int v = 0; - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - while ((v = input.read()) != -1) { - bos.write(v); - } - bos.close(); - return bos.toByteArray(); - } + @Test + public void handlesPostsWithNoContentTypeHeader() throws Exception { + method("POST"); + body("one=1&two=2".getBytes()); + contentType(null); + final HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(request); + final Map params = wrapper.getParameterMap(); + assertFalse(params.containsKey("one")); + } } diff --git a/zuul-core/src/main/java/com/netflix/zuul/http/ZuulServlet.java b/zuul-core/src/main/java/com/netflix/zuul/http/ZuulServlet.java index d11260ac00..0a97c6bd13 100644 --- a/zuul-core/src/main/java/com/netflix/zuul/http/ZuulServlet.java +++ b/zuul-core/src/main/java/com/netflix/zuul/http/ZuulServlet.java @@ -15,33 +15,27 @@ */ package com.netflix.zuul.http; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.IOException; -import java.io.PrintWriter; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - +import com.netflix.zuul.FilterProcessor; +import com.netflix.zuul.ZuulRunner; +import com.netflix.zuul.context.RequestContext; +import com.netflix.zuul.exception.ZuulException; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.runners.MockitoJUnitRunner; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import com.netflix.zuul.FilterProcessor; -import com.netflix.zuul.ZuulRunner; -import com.netflix.zuul.context.RequestContext; -import com.netflix.zuul.exception.ZuulException; +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.*; /** * Core Zuul servlet which intializes and orchestrates zuulFilter execution @@ -51,10 +45,20 @@ * Time: 10:44 AM */ public class ZuulServlet extends HttpServlet { - + private static final long serialVersionUID = -3374242278843351500L; - private ZuulRunner zuulRunner = new ZuulRunner(); - private static Logger LOG = LoggerFactory.getLogger(ZuulServlet.class); + private ZuulRunner zuulRunner; + + + @Override + public void init(ServletConfig config) throws ServletException { + super.init(config); + + String bufferReqsStr = config.getInitParameter("buffer-requests"); + boolean bufferReqs = bufferReqsStr != null && bufferReqsStr.equals("true") ? true : false; + + zuulRunner = new ZuulRunner(bufferReqs); + } @Override public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException { @@ -90,6 +94,7 @@ public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.S } catch (Throwable e) { error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName())); } finally { + RequestContext.getCurrentContext().unset(); } } @@ -138,7 +143,6 @@ void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse void error(ZuulException e) { RequestContext.getCurrentContext().setThrowable(e); zuulRunner.error(); - LOG.error(e.getMessage(), e); } @RunWith(MockitoJUnitRunner.class) @@ -188,7 +192,7 @@ public void testProcessZuulFilter() { RequestContext.testSetCurrentContext(null); } catch (Exception e) { - LOG.error(e.getMessage(), e); + e.printStackTrace(); } diff --git a/zuul-core/src/main/java/com/netflix/zuul/util/HTTPRequestUtils.java b/zuul-core/src/main/java/com/netflix/zuul/util/HTTPRequestUtils.java index 8fb2d567c1..6ec6b95924 100755 --- a/zuul-core/src/main/java/com/netflix/zuul/util/HTTPRequestUtils.java +++ b/zuul-core/src/main/java/com/netflix/zuul/util/HTTPRequestUtils.java @@ -15,8 +15,10 @@ */ package com.netflix.zuul.util; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; import java.net.URLDecoder; import java.util.ArrayList; @@ -30,7 +32,10 @@ import javax.servlet.http.HttpServletRequest; +import org.junit.Before; import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import com.netflix.zuul.context.RequestContext; @@ -163,7 +168,7 @@ public Map> getQueryParams() { while (st.hasMoreTokens()) { String s = st.nextToken(); i = s.indexOf("="); - if (i > 0 && s.length() > i + 1) { + if (i > 0 && s.length() >= i + 1) { String name = s.substring(0, i); String value = s.substring(i + 1); @@ -184,6 +189,24 @@ public Map> getQueryParams() { valueList.add(value); } + else if (i == -1) + { + String name=s; + String value=""; + try { + name = URLDecoder.decode(name, "UTF-8"); + } catch (Exception e) { + } + + List valueList = qp.get(name); + if (valueList == null) { + valueList = new LinkedList(); + qp.put(name, valueList); + } + + valueList.add(value); + + } } RequestContext.getCurrentContext().setRequestQueryParams(qp); @@ -222,6 +245,16 @@ public boolean isGzipped(String contentEncoding) { public static class UnitTest { + @Mock + private RequestContext mockContext; + @Mock + private HttpServletRequest request; + + @Before + public void before() { + MockitoAnnotations.initMocks(this); + } + @Test public void detectsGzip() { assertTrue(HTTPRequestUtils.getInstance().isGzipped("gzip")); @@ -236,6 +269,36 @@ public void detectsNonGzip() { public void detectsGzipAmongOtherEncodings() { assertTrue(HTTPRequestUtils.getInstance().isGzipped("gzip, deflate")); } + + @Test + public void testGetQueryParams() { + Map> qp; + LinkedList blankValue = new LinkedList(); + blankValue.add(""); + + RequestContext.testSetCurrentContext(mockContext); + when(mockContext.getRequestQueryParams()).thenReturn(null); + when(mockContext.getRequest()).thenReturn(request); + when(request.getQueryString()).thenReturn("wsdl"); + + qp = HTTPRequestUtils.getInstance().getQueryParams(); + assertEquals(blankValue, qp.get("wsdl")); + + when(request.getQueryString()).thenReturn("wsdl="); + + qp = HTTPRequestUtils.getInstance().getQueryParams(); + assertEquals(blankValue, qp.get("wsdl")); + + when(request.getQueryString()).thenReturn("a=123&b=234&b=345&c&d="); + + qp = HTTPRequestUtils.getInstance().getQueryParams(); + assertEquals("123", qp.get("a").get(0)); + // Not sure that order is supposed to be guaranteed here + assertEquals("234", qp.get("b").get(0)); + assertEquals("345", qp.get("b").get(1)); + assertEquals(blankValue, qp.get("c")); + assertEquals(blankValue, qp.get("d")); + } } } diff --git a/zuul-netflix-webapp/build.gradle b/zuul-netflix-webapp/build.gradle index 150f2e9ffb..1c173ebf49 100644 --- a/zuul-netflix-webapp/build.gradle +++ b/zuul-netflix-webapp/build.gradle @@ -1,7 +1,4 @@ -apply plugin: 'java' apply plugin: 'groovy' -apply plugin: 'eclipse' -apply plugin: 'idea' apply plugin: 'war' apply plugin: 'jetty' @@ -20,8 +17,8 @@ dependencies { compile 'com.sun.jersey:jersey-client:1.17.1' compile 'com.sun.jersey:jersey-servlet:1.17.1' - groovy "org.codehaus.groovy:groovy-all:2.2.2" - provided 'junit:junit-dep:4.10' + compile "org.codehaus.groovy:groovy-all:2.2.2" + providedCompile 'junit:junit-dep:4.10' } javadoc { @@ -35,20 +32,11 @@ javadoc { eclipse { classpath { - plusConfigurations += configurations.provided downloadSources = true downloadJavadoc = true } } - -idea { - module { - languageLevel = '1.7' - scopes.PROVIDED.plus += configurations.provided - } -} - war { webXml = file('src/main/webapp/WEB-INF/web.xml') webInf{ diff --git a/zuul-netflix-webapp/src/main/groovy/filters/post/sendResponse.groovy b/zuul-netflix-webapp/src/main/groovy/filters/post/sendResponse.groovy index b74a075564..dbfc594215 100644 --- a/zuul-netflix-webapp/src/main/groovy/filters/post/sendResponse.groovy +++ b/zuul-netflix-webapp/src/main/groovy/filters/post/sendResponse.groovy @@ -29,6 +29,7 @@ import org.junit.runner.RunWith import org.mockito.Mockito import org.mockito.runners.MockitoJUnitRunner +import java.nio.charset.Charset import java.util.zip.GZIPInputStream import javax.servlet.http.HttpServletResponse @@ -77,7 +78,7 @@ class sendResponse extends ZuulFilter { try { if (RequestContext.currentContext.responseBody != null) { String body = RequestContext.currentContext.responseBody - writeResponse(new ByteArrayInputStream(body.bytes), outStream) + writeResponse(new ByteArrayInputStream(body.getBytes(Charset.forName("UTF-8"))), outStream) return; } diff --git a/zuul-netflix-webapp/src/main/groovy/filters/pre/ErrorResponse.groovy b/zuul-netflix-webapp/src/main/groovy/filters/pre/ErrorResponse.groovy index 6bdba2fbfa..58beed032a 100644 --- a/zuul-netflix-webapp/src/main/groovy/filters/pre/ErrorResponse.groovy +++ b/zuul-netflix-webapp/src/main/groovy/filters/pre/ErrorResponse.groovy @@ -26,12 +26,16 @@ import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito import org.mockito.runners.MockitoJUnitRunner +import org.slf4j.Logger +import org.slf4j.LoggerFactory import javax.servlet.http.HttpServletRequest import javax.servlet.http.HttpServletResponse class ErrorResponse extends ZuulFilter { + private static final Logger LOG = LoggerFactory.getLogger(ErrorResponse.class); + @Override String filterType() { return 'error' @@ -53,6 +57,7 @@ class ErrorResponse extends ZuulFilter { RequestContext context = RequestContext.currentContext Throwable ex = context.getThrowable() try { + LOG.error(ex.getMessage(), ex); throw ex } catch (ZuulException e) { String cause = e.errorCause diff --git a/zuul-netflix-webapp/src/main/groovy/filters/route/ZuulHostRequest.groovy b/zuul-netflix-webapp/src/main/groovy/filters/route/ZuulHostRequest.groovy index e54dbe22ad..a1802432db 100644 --- a/zuul-netflix-webapp/src/main/groovy/filters/route/ZuulHostRequest.groovy +++ b/zuul-netflix-webapp/src/main/groovy/filters/route/ZuulHostRequest.groovy @@ -319,35 +319,37 @@ class ZuulHostRequest extends ZuulFilter { return true; } - def Header[] buildZuulRequestHeaders(HttpServletRequest request) { - def headers = new ArrayList() + def Header[] buildZuulRequestHeaders(HttpServletRequest request) { + Map headers = new HashMap() Enumeration headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()) { String name = (String) headerNames.nextElement(); - String value = request.getHeader(name); - if (isValidHeader(name)) headers.add(new BasicHeader(name, value)) + + final StringBuilder valBuilder = new StringBuilder(); + for (final Enumeration vals = request.getHeaders(name); vals.hasMoreElements();) { + valBuilder.append(vals.nextElement()); + valBuilder.append(',') + } + valBuilder.setLength(valBuilder.length()-1) + + if (isValidHeader(name)) { + headers.put(name.toLowerCase(),new BasicHeader(name, valBuilder.toString())) + } } Map zuulRequestHeaders = RequestContext.getCurrentContext().getZuulRequestHeaders(); zuulRequestHeaders.keySet().each { - String name = it.toLowerCase() - BasicHeader h = headers.find { BasicHeader he -> he.name == name } - if (h != null) { - headers.remove(h) - } - headers.add(new BasicHeader((String) it, (String) zuulRequestHeaders[it])) + headers.put(it.toLowerCase(),new BasicHeader((String) it, (String) zuulRequestHeaders[it])) } if (RequestContext.currentContext.responseGZipped) { - headers.add(new BasicHeader("accept-encoding", "deflate, gzip")) + headers.put("accept-encoding",new BasicHeader("accept-encoding", "deflate, gzip")) } - return headers + return headers.values().toArray() } - - String getVerb(HttpServletRequest request) { String sMethod = request.getMethod(); return sMethod.toUpperCase(); diff --git a/zuul-netflix/build.gradle b/zuul-netflix/build.gradle index d4e787c196..fd09242de5 100644 --- a/zuul-netflix/build.gradle +++ b/zuul-netflix/build.gradle @@ -1,7 +1,4 @@ -apply plugin: 'java' apply plugin: 'groovy' -apply plugin: 'eclipse' -apply plugin: 'idea' dependencies { compile project(':zuul-core') @@ -37,15 +34,7 @@ javadoc { eclipse { classpath { - plusConfigurations += configurations.provided downloadSources = true downloadJavadoc = true } } - - -idea { - module { - scopes.PROVIDED.plus += configurations.provided - } -} diff --git a/zuul-netflix/src/main/java/com/netflix/zuul/FilterId.java b/zuul-netflix/src/main/java/com/netflix/zuul/FilterId.java new file mode 100644 index 0000000000..f21a44b88d --- /dev/null +++ b/zuul-netflix/src/main/java/com/netflix/zuul/FilterId.java @@ -0,0 +1,84 @@ +package com.netflix.zuul; + +import java.util.UUID; + +import org.junit.After; +import org.junit.Test; + +import net.jcip.annotations.Immutable; +import net.jcip.annotations.ThreadSafe; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +@Immutable +@ThreadSafe +public final class FilterId { + + private String value; + + private FilterId(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } + + public static class Builder { + private String applicationName = ZuulApplicationInfo.getApplicationName(); + private String filterType; + private String filterName; + + public Builder applicationName(String applicationName) { + this.applicationName = applicationName; + return this; + } + + public Builder filterType(String filterType) { + this.filterType = filterType; + return this; + } + + public Builder filterName(String filterName) { + this.filterName = filterName; + return this; + } + + public FilterId build() { + return new FilterId(applicationName + ":" + filterName + ":" + filterType); + } + } + + + public static class UnitTest { + + String zuulAppName = ZuulApplicationInfo.getApplicationName(); + + @After + public void setZuulAppNameBack() { + ZuulApplicationInfo.setApplicationName(zuulAppName); + } + + @Test + public void filterId() { + FilterId filterId = new Builder().applicationName("app") + .filterType("com.acme.None") + .filterName("none") + .build(); + assertThat(filterId.toString(), is("app:none:com.acme.None")); + } + + @Test + public void defaultApplicationName() { + String applicationName = UUID.randomUUID().toString(); + ZuulApplicationInfo.setApplicationName(applicationName); + + FilterId filterId = new Builder().filterType("com.acme.None") + .filterName("none") + .build(); + assertThat(filterId.toString(), is(applicationName + ":none:com.acme.None")); + } + } +} diff --git a/zuul-netflix/src/main/java/com/netflix/zuul/scriptManager/FilterInfo.java b/zuul-netflix/src/main/java/com/netflix/zuul/scriptManager/FilterInfo.java index 9915afc566..889eae65b9 100644 --- a/zuul-netflix/src/main/java/com/netflix/zuul/scriptManager/FilterInfo.java +++ b/zuul-netflix/src/main/java/com/netflix/zuul/scriptManager/FilterInfo.java @@ -15,14 +15,24 @@ */ package com.netflix.zuul.scriptManager; -import net.jcip.annotations.ThreadSafe; - import java.util.Date; import java.util.concurrent.atomic.AtomicBoolean; +import com.netflix.zuul.FilterId; +import com.netflix.zuul.ZuulApplicationInfo; +import com.netflix.zuul.ZuulFilter; + +import org.junit.Test; + +import net.jcip.annotations.ThreadSafe; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + /** * Representation of a ZuulFilter for representing and storing in a database */ + @ThreadSafe public class FilterInfo implements Comparable{ @@ -34,20 +44,14 @@ public class FilterInfo implements Comparable{ private final String filter_order; private final String application_name; private int revision; - private Date creationDate; + private Date creationDate = new Date(); + /* using AtomicBoolean so we can pass it into EndpointScriptMonitor */ private final AtomicBoolean isActive = new AtomicBoolean(); private final AtomicBoolean isCanary = new AtomicBoolean(); /** - * Constructor - * @param filter_id - * @param filter_code - * @param filter_type - * @param filter_name - * @param disablePropertyName - * @param filter_order - * @param application_name + * Constructors */ public FilterInfo(String filter_id, String filter_code, String filter_type, String filter_name, String disablePropertyName, String filter_order, String application_name) { this.filter_id = filter_id; @@ -61,6 +65,32 @@ public FilterInfo(String filter_id, String filter_code, String filter_type, Stri isCanary.set(false); } + public FilterInfo(String filterCode, String filterName, ZuulFilter filter) { + this.filter_code = filterCode; + this.filter_type = filter.filterType(); + this.filter_name = filterName; + this.filter_disablePropertyName = filter.disablePropertyName(); + this.filter_order = "" + filter.filterOrder(); + this.application_name = ZuulApplicationInfo.getApplicationName(); + isActive.set(false); + isCanary.set(false); + this.filter_id = buildFilterId(); + } + + public FilterInfo(String filter_id, int revision, Date creationDate, boolean isActive, boolean isCanary, String filter_code, String filter_type, String filter_name, String disablePropertyName, String filter_order, String application_name) { + this.filter_id = filter_id; + this.revision = revision; + this.creationDate = new Date(creationDate.getTime()); + this.isActive.set(isActive); + this.isCanary.set(isCanary); + this.filter_code = filter_code; + this.filter_name = filter_name; + this.filter_type = filter_type; + this.filter_order = filter_order; + this.filter_disablePropertyName = disablePropertyName; + this.application_name = application_name; + } + /** * * @return the filter name; the class name of the filter @@ -94,7 +124,6 @@ public String getFilterType() { return filter_type; } - @Override public String toString() { return "FilterInfo{" + @@ -117,35 +146,6 @@ public String getApplication_name() { return application_name; } - /** - * - * @param filter_id - * @param revision - * @param creationDate - * @param isActive - * @param isCanary - * @param filter_code - * @param filter_type - * @param filter_name - * @param disablePropertyName - * @param filter_order - * @param application_name - */ - public FilterInfo(String filter_id, int revision, Date creationDate, boolean isActive, boolean isCanary, String filter_code, String filter_type, String filter_name, String disablePropertyName, String filter_order, String application_name) { - this.filter_id = filter_id; - this.revision = revision; - this.creationDate = creationDate; - this.isActive.set(isActive); - this.isCanary.set(isCanary); - this.filter_code = filter_code; - this.filter_name = filter_name; - this.filter_type = filter_type; - this.filter_order = filter_order; - this.filter_disablePropertyName = disablePropertyName; - this.application_name = application_name; - - } - /** * * @return the revision of this filter @@ -159,7 +159,7 @@ public int getRevision() { * @return creation date */ public Date getCreationDate() { - return creationDate; + return new Date(creationDate.getTime()); } /** @@ -179,7 +179,6 @@ public boolean isCanary() { return isCanary.get(); } - /** * * @return unique key for the filter @@ -204,7 +203,15 @@ public String getFilterOrder() { * @return key is application_name:filter_name:filter_type */ public static String buildFilterID(String application_name, String filter_type, String filter_name) { - return application_name + ":" + filter_name + ":" + filter_type; + return new FilterId.Builder().applicationName(application_name) + .filterType(filter_type) + .filterName(filter_name) + .build() + .toString(); + } + + public String buildFilterId() { + return buildFilterID(application_name, filter_type, filter_name); } @Override @@ -247,4 +254,32 @@ public int compareTo(FilterInfo filterInfo) { return filterInfo.getFilterName().compareTo(this.getFilterName()); } + public static class UnitTest { + + @Test + public void verifyFilterId() { + FilterInfo filterInfo = new FilterInfo("", "", "", "", "", "", ""); + long originalCreationTime = filterInfo.getCreationDate().getTime(); + filterInfo.getCreationDate().setTime(0); + assertThat(filterInfo.getCreationDate().getTime(), is(originalCreationTime)); + } + + @Test + public void creationDateIsCopiedInGetter() { + FilterInfo filterInfo = new FilterInfo("", "", "", "", "", "", ""); + long originalCreationTime = filterInfo.getCreationDate().getTime(); + filterInfo.getCreationDate().setTime(0); + assertThat(filterInfo.getCreationDate().getTime(), is(originalCreationTime)); + } + + @Test + public void creationDateIsCopiedInConstructor() { + Date date = new Date(); + long originalCreationTime = date.getTime(); + FilterInfo filterInfo = + new FilterInfo("", 1, date, false, false, "", "", "", "", "", ""); + date.setTime(0); + assertThat(filterInfo.getCreationDate().getTime(), is(originalCreationTime)); + } + } } diff --git a/zuul-netflix/src/main/java/com/netflix/zuul/scriptManager/FilterVerifier.java b/zuul-netflix/src/main/java/com/netflix/zuul/scriptManager/FilterVerifier.java index c776267bf2..4524c747a7 100644 --- a/zuul-netflix/src/main/java/com/netflix/zuul/scriptManager/FilterVerifier.java +++ b/zuul-netflix/src/main/java/com/netflix/zuul/scriptManager/FilterVerifier.java @@ -47,21 +47,18 @@ public static FilterVerifier getInstance() { * * @param sFilterCode * @return a FilterInfo object representing that code - * @throws org.codehaus.groovy.control.CompilationFailedException + * @throws CompilationFailedException * * @throws IllegalAccessException * @throws InstantiationException */ - public FilterInfo verifyFilter(String sFilterCode) throws org.codehaus.groovy.control.CompilationFailedException, IllegalAccessException, InstantiationException { + public FilterInfo verifyFilter(String sFilterCode) throws CompilationFailedException, IllegalAccessException, InstantiationException { Class groovyClass = compileGroovy(sFilterCode); Object instance = instanciateClass(groovyClass); checkZuulFilterInstance(instance); ZuulFilter filter = (ZuulFilter) instance; - - String filter_id = FilterInfo.buildFilterID(ZuulApplicationInfo.getApplicationName(), filter.filterType(), groovyClass.getSimpleName()); - - return new FilterInfo(filter_id, sFilterCode, filter.filterType(), groovyClass.getSimpleName(), filter.disablePropertyName(), "" + filter.filterOrder(), ZuulApplicationInfo.getApplicationName()); + return new FilterInfo(sFilterCode, groovyClass.getSimpleName(), filter); } Object instanciateClass(Class groovyClass) throws InstantiationException, IllegalAccessException { @@ -79,10 +76,10 @@ void checkZuulFilterInstance(Object zuulFilter) throws InstantiationException { * * @param sFilterCode * @return - * @throws org.codehaus.groovy.control.CompilationFailedException + * @throws CompilationFailedException * */ - public Class compileGroovy(String sFilterCode) throws org.codehaus.groovy.control.CompilationFailedException { + public Class compileGroovy(String sFilterCode) throws CompilationFailedException { GroovyClassLoader loader = new GroovyClassLoader(); return loader.parseClass(sFilterCode); } @@ -277,5 +274,3 @@ public void testVerify() { } } - - diff --git a/zuul-netflix/src/main/java/com/netflix/zuul/scriptManager/ZuulFilterDAOCassandra.java b/zuul-netflix/src/main/java/com/netflix/zuul/scriptManager/ZuulFilterDAOCassandra.java index e2130f4940..caef2de973 100644 --- a/zuul-netflix/src/main/java/com/netflix/zuul/scriptManager/ZuulFilterDAOCassandra.java +++ b/zuul-netflix/src/main/java/com/netflix/zuul/scriptManager/ZuulFilterDAOCassandra.java @@ -15,18 +15,30 @@ */ package com.netflix.zuul.scriptManager; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Observable; + import com.netflix.astyanax.AstyanaxContext; import com.netflix.astyanax.Keyspace; import com.netflix.astyanax.model.Column; import com.netflix.astyanax.model.ColumnList; import com.netflix.astyanax.model.Row; import com.netflix.astyanax.model.Rows; +import com.netflix.zuul.FilterId; import com.netflix.zuul.ZuulApplicationInfo; import com.netflix.zuul.dependency.cassandra.hystrix.HystrixCassandraGetRowsByKeys; import com.netflix.zuul.dependency.cassandra.hystrix.HystrixCassandraGetRowsByQuery; import com.netflix.zuul.dependency.cassandra.hystrix.HystrixCassandraPut; import com.netflix.zuul.event.ZuulEvent; -import net.jcip.annotations.ThreadSafe; + import org.junit.Before; import org.junit.Test; import org.mockito.InOrder; @@ -35,11 +47,22 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.*; +import net.jcip.annotations.ThreadSafe; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.anyList; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; /** * from zuul_filters @@ -318,8 +341,10 @@ public FilterInfo addFilter(String filtercode, String filter_type, String filter } public static String buildFilterID(String filter_type, String filter_name) { - return FilterInfo.buildFilterID(ZuulApplicationInfo.getApplicationName(), filter_type, filter_name); - + return new FilterId.Builder().filterType(filter_type) + .filterName(filter_name) + .build() + .toString(); } @Override diff --git a/zuul-simple-webapp/build.gradle b/zuul-simple-webapp/build.gradle index a5ba065e6c..7f21f769d5 100644 --- a/zuul-simple-webapp/build.gradle +++ b/zuul-simple-webapp/build.gradle @@ -1,20 +1,16 @@ -apply plugin: 'java' apply plugin: 'war' -apply plugin: 'eclipse' -apply plugin: 'idea' apply plugin: 'jetty' dependencies { compile project(":zuul-core") - - compile 'org.apache.httpcomponents:httpclient:4.3.2' - provided 'javax.servlet:servlet-api:2.5' + + compile 'org.apache.httpcomponents:httpclient:4.5' + providedCompile 'javax.servlet:servlet-api:2.5' } eclipse { classpath { - plusConfigurations += configurations.provided downloadSources = true downloadJavadoc = true } @@ -29,12 +25,6 @@ war { } } -idea { - module { - scopes.PROVIDED.plus += configurations.provided - } -} - jettyRun.contextPath = '/' jettyRun.doFirst { diff --git a/zuul-simple-webapp/src/main/groovy/filters/post/SendResponse.groovy b/zuul-simple-webapp/src/main/groovy/filters/post/SendResponseFilter.groovy similarity index 74% rename from zuul-simple-webapp/src/main/groovy/filters/post/SendResponse.groovy rename to zuul-simple-webapp/src/main/groovy/filters/post/SendResponseFilter.groovy index db469c9a52..c63cbf0af5 100644 --- a/zuul-simple-webapp/src/main/groovy/filters/post/SendResponse.groovy +++ b/zuul-simple-webapp/src/main/groovy/filters/post/SendResponseFilter.groovy @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package filters.post import com.netflix.config.DynamicBooleanProperty import com.netflix.config.DynamicIntProperty @@ -23,21 +24,22 @@ import com.netflix.zuul.constants.ZuulConstants import com.netflix.zuul.constants.ZuulHeaders import com.netflix.zuul.context.Debug import com.netflix.zuul.context.RequestContext -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mockito -import org.mockito.runners.MockitoJUnitRunner +import org.slf4j.Logger +import org.slf4j.LoggerFactory -import java.util.zip.GZIPInputStream import javax.servlet.http.HttpServletResponse +import java.nio.charset.Charset +import java.util.zip.GZIPInputStream +import java.util.zip.ZipException class SendResponseFilter extends ZuulFilter { + private static final Logger LOG = LoggerFactory.getLogger(SendResponseFilter.class); static DynamicBooleanProperty INCLUDE_DEBUG_HEADER = - DynamicPropertyFactory.getInstance().getBooleanProperty(ZuulConstants.ZUUL_INCLUDE_DEBUG_HEADER, false); + DynamicPropertyFactory.getInstance().getBooleanProperty(ZuulConstants.ZUUL_INCLUDE_DEBUG_HEADER, false); static DynamicIntProperty INITIAL_STREAM_BUFFER_SIZE = - DynamicPropertyFactory.getInstance().getIntProperty(ZuulConstants.ZUUL_INITIAL_STREAM_BUFFER_SIZE, 1024); + DynamicPropertyFactory.getInstance().getIntProperty(ZuulConstants.ZUUL_INITIAL_STREAM_BUFFER_SIZE, 1024); static DynamicBooleanProperty SET_CONTENT_LENGTH = DynamicPropertyFactory.getInstance().getBooleanProperty(ZuulConstants.ZUUL_SET_CONTENT_LENGTH, false); @@ -52,9 +54,9 @@ class SendResponseFilter extends ZuulFilter { } boolean shouldFilter() { - return !RequestContext.currentContext.getZuulResponseHeaders().isEmpty() || - RequestContext.currentContext.getResponseDataStream() != null || - RequestContext.currentContext.responseBody != null + return !RequestContext.getCurrentContext().getZuulResponseHeaders().isEmpty() || + RequestContext.getCurrentContext().getResponseDataStream() != null || + RequestContext.getCurrentContext().responseBody != null } Object run() { @@ -63,10 +65,11 @@ class SendResponseFilter extends ZuulFilter { } void writeResponse() { - RequestContext context = RequestContext.currentContext + RequestContext context = RequestContext.getCurrentContext() - // there is no body to send - if (context.getResponseBody() == null && context.getResponseDataStream() == null) return; + if (context.getResponseBody() == null && context.getResponseDataStream() == null) { + return + }; HttpServletResponse servletResponse = context.getResponse() servletResponse.setCharacterEncoding("UTF-8") @@ -74,9 +77,9 @@ class SendResponseFilter extends ZuulFilter { OutputStream outStream = servletResponse.getOutputStream(); InputStream is = null try { - if (RequestContext.currentContext.responseBody != null) { - String body = RequestContext.currentContext.responseBody - writeResponse(new ByteArrayInputStream(body.bytes), outStream) + if (RequestContext.getCurrentContext().responseBody != null) { + String body = RequestContext.getCurrentContext().responseBody + writeResponse(new ByteArrayInputStream(body.getBytes(Charset.forName("UTF-8"))), outStream) return; } @@ -89,19 +92,16 @@ class SendResponseFilter extends ZuulFilter { InputStream inputStream = is if (is != null) { if (context.sendZuulResponse()) { - // if origin response is gzipped, and client has not requested gzip, decompress stream - // before sending to client - // else, stream gzip directly to client if (context.getResponseGZipped() && !isGzipRequested) try { inputStream = new GZIPInputStream(is); - - } catch (java.util.zip.ZipException e) { - println("gzip expected but not received assuming unencoded response" + RequestContext.currentContext.getRequest().getRequestURL().toString()) + } catch (ZipException e) { + LOG.error("gzip expected but not received assuming unencoded response" + RequestContext.getCurrentContext().getRequest().getRequestURL().toString()) inputStream = is } - else if (context.getResponseGZipped() && isGzipRequested) + else if (context.getResponseGZipped() && isGzipRequested) { servletResponse.setHeader(ZuulHeaders.CONTENT_ENCODING, "gzip") + } writeResponse(inputStream, outStream) } } @@ -109,11 +109,9 @@ class SendResponseFilter extends ZuulFilter { } finally { try { is?.close(); - outStream.flush() outStream.close() } catch (IOException e) { - } } } @@ -122,15 +120,11 @@ class SendResponseFilter extends ZuulFilter { byte[] bytes = new byte[INITIAL_STREAM_BUFFER_SIZE.get()]; int bytesRead = -1; while ((bytesRead = zin.read(bytes)) != -1) { -// if (Debug.debugRequest() && !Debug.debugRequestHeadersOnly()) { -// Debug.addRequestDebug("OUTBOUND: < " + new String(bytes, 0, bytesRead)); -// } try { out.write(bytes, 0, bytesRead); out.flush(); } catch (IOException e) { - //ignore e.printStackTrace() } @@ -154,13 +148,6 @@ class SendResponseFilter extends ZuulFilter { debugHeader += "[[[${it}]]]"; } - /* - rd = (List) RequestContext.getCurrentContext().get("requestDebug"); - rd?.each { - debugHeader += "[[[REQUEST_DEBUG::${it}]]]"; - } - */ - if (INCLUDE_DEBUG_HEADER.get()) servletResponse.addHeader("X-Zuul-Debug-Header", debugHeader) if (Debug.debugRequest()) { @@ -184,4 +171,4 @@ class SendResponseFilter extends ZuulFilter { } } -} \ No newline at end of file +} diff --git a/zuul-simple-webapp/src/main/groovy/filters/post/Stats.groovy b/zuul-simple-webapp/src/main/groovy/filters/post/Stats.groovy index 041fe80eae..23b4f9800f 100755 --- a/zuul-simple-webapp/src/main/groovy/filters/post/Stats.groovy +++ b/zuul-simple-webapp/src/main/groovy/filters/post/Stats.groovy @@ -13,19 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package scripts.postProcess +package filters.post import com.netflix.zuul.ZuulFilter import com.netflix.zuul.context.RequestContext -import org.junit.Assert -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.runners.MockitoJUnitRunner - -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse - /** * @author Mikey Cohen * Date: 2/3/12 diff --git a/zuul-simple-webapp/src/main/groovy/filters/pre/Debug.groovy b/zuul-simple-webapp/src/main/groovy/filters/pre/DebugFilter.groovy similarity index 94% rename from zuul-simple-webapp/src/main/groovy/filters/pre/Debug.groovy rename to zuul-simple-webapp/src/main/groovy/filters/pre/DebugFilter.groovy index 72da19d5a3..14db5ba6e8 100644 --- a/zuul-simple-webapp/src/main/groovy/filters/pre/Debug.groovy +++ b/zuul-simple-webapp/src/main/groovy/filters/pre/DebugFilter.groovy @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package scripts.preProcess +package filters.pre import com.netflix.config.DynamicBooleanProperty import com.netflix.config.DynamicPropertyFactory @@ -38,9 +38,11 @@ class DebugFilter extends ZuulFilter { } boolean shouldFilter() { - if ("true".equals(RequestContext.getCurrentContext().getRequest().getParameter(debugParameter.get()))) return true; - return routingDebug.get(); + if ("true".equals(RequestContext.getCurrentContext().getRequest().getParameter(debugParameter.get()))) { + return true + } + return routingDebug.get(); } Object run() { diff --git a/zuul-simple-webapp/src/main/groovy/filters/pre/DebugRequest.groovy b/zuul-simple-webapp/src/main/groovy/filters/pre/DebugRequest.groovy index 5fd38d6d7e..b929220503 100644 --- a/zuul-simple-webapp/src/main/groovy/filters/pre/DebugRequest.groovy +++ b/zuul-simple-webapp/src/main/groovy/filters/pre/DebugRequest.groovy @@ -13,21 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package scripts.preProcess +package filters.pre import com.netflix.zuul.ZuulFilter import com.netflix.zuul.context.Debug import com.netflix.zuul.context.RequestContext -import org.junit.Assert -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Mockito -import org.mockito.runners.MockitoJUnitRunner import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse - /** * @author Mikey Cohen * Date: 3/12/12 @@ -62,7 +54,6 @@ class DebugRequest extends ZuulFilter { String name = (String) headerIt.next() String value = req.getHeader(name) Debug.addRequestDebug("REQUEST:: > " + name + ":" + value) - } final RequestContext ctx = RequestContext.getCurrentContext() diff --git a/zuul-simple-webapp/src/main/groovy/filters/pre/PreDecoration.groovy b/zuul-simple-webapp/src/main/groovy/filters/pre/PreDecorationFilter.groovy similarity index 92% rename from zuul-simple-webapp/src/main/groovy/filters/pre/PreDecoration.groovy rename to zuul-simple-webapp/src/main/groovy/filters/pre/PreDecorationFilter.groovy index 0ef694bde3..2925753cdf 100644 --- a/zuul-simple-webapp/src/main/groovy/filters/pre/PreDecoration.groovy +++ b/zuul-simple-webapp/src/main/groovy/filters/pre/PreDecorationFilter.groovy @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package filters.pre import com.netflix.zuul.ZuulFilter import com.netflix.zuul.context.RequestContext @@ -44,6 +45,9 @@ class PreDecorationFilter extends ZuulFilter { // sets origin ctx.setRouteHost(new URL("http://apache.org/")); + // set origin host header + ctx.addZuulRequestHeader("Host","apache.org"); + // sets custom header to send to the origin ctx.addOriginResponseHeader("cache-control", "max-age=3600"); } diff --git a/zuul-simple-webapp/src/main/groovy/filters/route/SimpleHostRequest.groovy b/zuul-simple-webapp/src/main/groovy/filters/route/SimpleHostRoutingFilter.groovy similarity index 60% rename from zuul-simple-webapp/src/main/groovy/filters/route/SimpleHostRequest.groovy rename to zuul-simple-webapp/src/main/groovy/filters/route/SimpleHostRoutingFilter.groovy index f6d477f0a0..2df81e630f 100644 --- a/zuul-simple-webapp/src/main/groovy/filters/route/SimpleHostRequest.groovy +++ b/zuul-simple-webapp/src/main/groovy/filters/route/SimpleHostRoutingFilter.groovy @@ -13,8 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - - +package filters.route import com.netflix.config.DynamicIntProperty import com.netflix.config.DynamicPropertyFactory @@ -23,33 +22,33 @@ import com.netflix.zuul.constants.ZuulConstants import com.netflix.zuul.context.Debug import com.netflix.zuul.context.RequestContext import com.netflix.zuul.util.HTTPRequestUtils -import org.apache.http.Header -import org.apache.http.HttpHost -import org.apache.http.HttpRequest -import org.apache.http.HttpResponse +import org.apache.http.* import org.apache.http.client.HttpClient +import org.apache.http.client.RedirectStrategy import org.apache.http.client.methods.HttpPost import org.apache.http.client.methods.HttpPut -import org.apache.http.client.params.ClientPNames -import org.apache.http.conn.ClientConnectionManager -import org.apache.http.conn.scheme.PlainSocketFactory -import org.apache.http.conn.scheme.Scheme -import org.apache.http.conn.scheme.SchemeRegistry +import org.apache.http.client.methods.HttpUriRequest +import org.apache.http.config.Registry +import org.apache.http.config.RegistryBuilder +import org.apache.http.conn.HttpClientConnectionManager +import org.apache.http.conn.socket.ConnectionSocketFactory +import org.apache.http.conn.socket.PlainConnectionSocketFactory +import org.apache.http.conn.ssl.SSLConnectionSocketFactory +import org.apache.http.conn.ssl.SSLContexts import org.apache.http.entity.InputStreamEntity -import org.apache.http.impl.client.DefaultHttpClient +import org.apache.http.impl.client.CloseableHttpClient import org.apache.http.impl.client.DefaultHttpRequestRetryHandler -import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager +import org.apache.http.impl.client.HttpClientBuilder +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager import org.apache.http.message.BasicHeader import org.apache.http.message.BasicHttpRequest -import org.apache.http.params.CoreConnectionPNames -import org.apache.http.params.HttpParams import org.apache.http.protocol.HttpContext import org.slf4j.Logger import org.slf4j.LoggerFactory +import javax.net.ssl.SSLContext import javax.servlet.http.HttpServletRequest import java.util.concurrent.atomic.AtomicReference -import java.util.zip.GZIPInputStream class SimpleHostRoutingFilter extends ZuulFilter { @@ -64,12 +63,12 @@ class SimpleHostRoutingFilter extends ZuulFilter { } private static final DynamicIntProperty SOCKET_TIMEOUT = - DynamicPropertyFactory.getInstance().getIntProperty(ZuulConstants.ZUUL_HOST_SOCKET_TIMEOUT_MILLIS, 10000) + DynamicPropertyFactory.getInstance().getIntProperty(ZuulConstants.ZUUL_HOST_SOCKET_TIMEOUT_MILLIS, 10000) private static final DynamicIntProperty CONNECTION_TIMEOUT = - DynamicPropertyFactory.getInstance().getIntProperty(ZuulConstants.ZUUL_HOST_CONNECT_TIMEOUT_MILLIS, 2000) + DynamicPropertyFactory.getInstance().getIntProperty(ZuulConstants.ZUUL_HOST_CONNECT_TIMEOUT_MILLIS, 2000) - private static final AtomicReference CLIENT = new AtomicReference(newClient()); + private static final AtomicReference CLIENT = new AtomicReference(newClient()); private static final Timer CONNECTION_MANAGER_TIMER = new Timer(true); @@ -81,9 +80,13 @@ class SimpleHostRoutingFilter extends ZuulFilter { @Override void run() { try { - final HttpClient hc = CLIENT.get(); - if (hc == null) return; - hc.getConnectionManager().closeExpiredConnections(); + final CloseableHttpClient hc = CLIENT.get(); + + if (hc == null) { + return; + } + + hc.close(); } catch (Throwable t) { LOG.error("error closing expired connections", t); } @@ -93,12 +96,15 @@ class SimpleHostRoutingFilter extends ZuulFilter { public SimpleHostRoutingFilter() {} - private static final ClientConnectionManager newConnectionManager() { - SchemeRegistry schemeRegistry = new SchemeRegistry(); - schemeRegistry.register( - new Scheme("http", 80, PlainSocketFactory.getSocketFactory())); + private static final HttpClientConnectionManager newConnectionManager() { + SSLContext sslContext = SSLContexts.createSystemDefault(); - ClientConnectionManager cm = new ThreadSafeClientConnManager(schemeRegistry); + Registry socketFactoryRegistry = RegistryBuilder.create() + .register("http", PlainConnectionSocketFactory.INSTANCE) + .register("https", new SSLConnectionSocketFactory(sslContext)) + .build(); + + HttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry); cm.setMaxTotal(Integer.parseInt(System.getProperty("zuul.max.host.connections", "200"))); cm.setDefaultMaxPerRoute(Integer.parseInt(System.getProperty("zuul.max.host.connections", "20"))); return cm; @@ -115,18 +121,18 @@ class SimpleHostRoutingFilter extends ZuulFilter { } boolean shouldFilter() { - return RequestContext.currentContext.getRouteHost() != null && RequestContext.currentContext.sendZuulResponse() + return RequestContext.getCurrentContext().getRouteHost() != null && RequestContext.getCurrentContext().sendZuulResponse() } private static final void loadClient() { - final HttpClient oldClient = CLIENT.get(); + final CloseableHttpClient oldClient = CLIENT.get(); CLIENT.set(newClient()) if (oldClient != null) { CONNECTION_MANAGER_TIMER.schedule(new TimerTask() { @Override void run() { try { - oldClient.getConnectionManager().shutdown(); + oldClient.close() } catch (Throwable t) { LOG.error("error shutting down old connection manager", t); } @@ -136,43 +142,42 @@ class SimpleHostRoutingFilter extends ZuulFilter { } - private static final HttpClient newClient() { - // I could statically cache the connection manager but we will probably want to make some of its properties - // dynamic in the near future also - HttpClient httpclient = new DefaultHttpClient(newConnectionManager()); - HttpParams httpParams = httpclient.getParams(); - httpParams.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, SOCKET_TIMEOUT.get()) - httpParams.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, CONNECTION_TIMEOUT.get()) - httpclient.setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler(0, false)) - httpParams.setParameter(ClientPNames.COOKIE_POLICY, org.apache.http.client.params.CookiePolicy.IGNORE_COOKIES); - httpclient.setRedirectStrategy(new org.apache.http.client.RedirectStrategy() { + private static final CloseableHttpClient newClient() { + HttpClientBuilder builder = HttpClientBuilder.create() + builder.setConnectionManager(newConnectionManager()) + builder.setRetryHandler(new DefaultHttpRequestRetryHandler(0, false)) + builder.setRedirectStrategy(new RedirectStrategy() { @Override - boolean isRedirected(HttpRequest httpRequest, HttpResponse httpResponse, HttpContext httpContext) { + boolean isRedirected(HttpRequest httpRequest, HttpResponse httpResponse, HttpContext httpContext) throws ProtocolException { return false } @Override - org.apache.http.client.methods.HttpUriRequest getRedirect(HttpRequest httpRequest, HttpResponse httpResponse, HttpContext httpContext) { + HttpUriRequest getRedirect(HttpRequest httpRequest, HttpResponse httpResponse, HttpContext httpContext) throws ProtocolException { return null } }) - return httpclient + + return builder.build() } Object run() { - HttpServletRequest request = RequestContext.currentContext.getRequest(); + HttpServletRequest request = RequestContext.getCurrentContext().getRequest(); Header[] headers = buildZuulRequestHeaders(request) String verb = getVerb(request); InputStream requestEntity = getRequestBody(request) - HttpClient httpclient = CLIENT.get() + CloseableHttpClient httpclient = CLIENT.get() String uri = request.getRequestURI() - if (RequestContext.currentContext.requestURI != null) { - uri = RequestContext.currentContext.requestURI + if (RequestContext.getCurrentContext().requestURI != null) { + uri = RequestContext.getCurrentContext().requestURI } try { HttpResponse response = forward(httpclient, verb, uri, request, headers, requestEntity) + Debug.addRequestDebug("ZUUL :: ${uri}") + Debug.addRequestDebug("ZUUL :: Response statusLine > ${response.getStatusLine()}") + Debug.addRequestDebug("ZUUL :: Response code > ${response.getStatusLine().statusCode}") setResponse(response) } catch (Exception e) { throw e; @@ -180,18 +185,16 @@ class SimpleHostRoutingFilter extends ZuulFilter { return null } - def InputStream debug(HttpClient httpclient, String verb, String uri, HttpServletRequest request, Header[] headers, InputStream requestEntity) { + InputStream debug(String verb, String uri, HttpServletRequest request, Header[] headers, InputStream requestEntity) { if (Debug.debugRequest()) { - - Debug.addRequestDebug("ZUUL:: host=${RequestContext.currentContext.getRouteHost()}") - + Debug.addRequestDebug("ZUUL:: host=${RequestContext.getCurrentContext().getRouteHost()}") headers.each { - Debug.addRequestDebug("ZUUL::> ${it.name} ${it.value}") + Debug.addRequestDebug("ZUUL:: Header > ${it.name} ${it.value}") } - String query = request.queryString + String query = request.queryString != null ? "?" + request.queryString : "" - Debug.addRequestDebug("ZUUL:: > ${verb} ${uri}?${query} HTTP/1.1") + Debug.addRequestDebug("ZUUL:: > ${verb} ${uri}${query} HTTP/1.1") if (requestEntity != null) { requestEntity = debugRequestEntity(requestEntity) } @@ -201,22 +204,24 @@ class SimpleHostRoutingFilter extends ZuulFilter { } InputStream debugRequestEntity(InputStream inputStream) { - if (Debug.debugRequestHeadersOnly()) return inputStream - if (inputStream == null) return null + if (Debug.debugRequestHeadersOnly()) { + return inputStream + } + + if (inputStream == null) { + return null + } + String entity = inputStream.getText() - Debug.addRequestDebug("ZUUL::> ${entity}") + Debug.addRequestDebug("ZUUL:: Entity > ${entity}") return new ByteArrayInputStream(entity.bytes) } - def HttpResponse forward(HttpClient httpclient, String verb, String uri, HttpServletRequest request, Header[] headers, InputStream requestEntity) { - - requestEntity = debug(httpclient, verb, uri, request, headers, requestEntity) - - org.apache.http.HttpHost httpHost + HttpResponse forward(CloseableHttpClient httpclient, String verb, String uri, HttpServletRequest request, Header[] headers, InputStream requestEntity) { - httpHost = getHttpHost() - - org.apache.http.HttpRequest httpRequest; + requestEntity = debug(verb, uri, request, headers, requestEntity) + HttpHost httpHost = getHttpHost() + HttpRequest httpRequest; switch (verb) { case 'POST': @@ -235,15 +240,10 @@ class SimpleHostRoutingFilter extends ZuulFilter { try { httpRequest.setHeaders(headers) - HttpResponse zuulResponse = forwardRequest(httpclient, httpHost, httpRequest) - return zuulResponse + return forwardRequest(httpclient, httpHost, httpRequest) } finally { - // When HttpClient instance is no longer needed, - // shut down the connection manager to ensure - // immediate deallocation of all system resources -// httpclient.getConnectionManager().shutdown(); + //httpclient.close(); } - } HttpResponse forwardRequest(HttpClient httpclient, HttpHost httpHost, HttpRequest httpRequest) { @@ -262,14 +262,14 @@ class SimpleHostRoutingFilter extends ZuulFilter { } String getQueryString() { - HttpServletRequest request = RequestContext.currentContext.getRequest(); + HttpServletRequest request = RequestContext.getCurrentContext().getRequest(); String query = request.getQueryString() return (query != null) ? query : ""; } HttpHost getHttpHost() { HttpHost httpHost - URL host = RequestContext.currentContext.getRouteHost() + URL host = RequestContext.getCurrentContext().getRouteHost() httpHost = new HttpHost(host.getHost(), host.getPort(), host.getProtocol()) @@ -277,73 +277,94 @@ class SimpleHostRoutingFilter extends ZuulFilter { } - def getRequestBody(HttpServletRequest request) { - Object requestEntity = null; + InputStream getRequestBody(HttpServletRequest request) { try { - requestEntity = request.getInputStream(); + return request.getInputStream(); } catch (IOException e) { - //no requestBody is ok. + LOG.warn(e.getMessage()) + return null } - return requestEntity } boolean isValidHeader(String name) { - if (name.toLowerCase().contains("content-length")) return false; - if (!RequestContext.currentContext.responseGZipped) { - if (name.toLowerCase().contains("accept-encoding")) return false; + if (name.toLowerCase().contains("content-length")) { + return false + } + + if (name.toLowerCase().equals("host")) { + return false } - return true; + + if (!RequestContext.getCurrentContext().responseGZipped) { + if (name.toLowerCase().contains("accept-encoding")) { + return false + } + } + return true } - def Header[] buildZuulRequestHeaders(HttpServletRequest request) { + Header[] buildZuulRequestHeaders(HttpServletRequest request) { - def headers = new ArrayList() + ArrayList headers = new ArrayList() Enumeration headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()) { - String name = (String) headerNames.nextElement(); + String name = ((String) headerNames.nextElement()).toLowerCase(); String value = request.getHeader(name); if (isValidHeader(name)) headers.add(new BasicHeader(name, value)) } - Map zuulRequestHeaders = RequestContext.getCurrentContext().getZuulRequestHeaders(); + Map zuulRequestHeaders = RequestContext.getCurrentContext().getZuulRequestHeaders(); - zuulRequestHeaders.keySet().each { + zuulRequestHeaders.keySet().each { String it -> String name = it.toLowerCase() BasicHeader h = headers.find { BasicHeader he -> he.name == name } if (h != null) { headers.remove(h) } - headers.add(new BasicHeader((String) it, (String) zuulRequestHeaders[it])) + headers.add(new BasicHeader(it, zuulRequestHeaders[it])) } - if (RequestContext.currentContext.responseGZipped) { + if (RequestContext.getCurrentContext().responseGZipped) { headers.add(new BasicHeader("accept-encoding", "deflate, gzip")) } + return headers } - - String getVerb(HttpServletRequest request) { - String sMethod = request.getMethod(); - return sMethod.toUpperCase(); + String getVerb(HttpServletRequest request) { + return getVerb(request.getMethod().toUpperCase()); } String getVerb(String sMethod) { - if (sMethod == null) return "GET"; + if (sMethod == null) { + return "GET" + } + sMethod = sMethod.toLowerCase(); - if (sMethod.equalsIgnoreCase("post")) return "POST" - if (sMethod.equalsIgnoreCase("put")) return "PUT" - if (sMethod.equalsIgnoreCase("delete")) return "DELETE" - if (sMethod.equalsIgnoreCase("options")) return "OPTIONS" - if (sMethod.equalsIgnoreCase("head")) return "HEAD" + + if (sMethod.equalsIgnoreCase("post")) { + return "POST" + } + if (sMethod.equalsIgnoreCase("put")) { + return "PUT" + } + if (sMethod.equalsIgnoreCase("delete")) { + return "DELETE" + } + if (sMethod.equalsIgnoreCase("options")) { + return "OPTIONS" + } + if (sMethod.equalsIgnoreCase("head")) { + return "HEAD" + } return "GET" } void setResponse(HttpResponse response) { RequestContext context = RequestContext.getCurrentContext() - RequestContext.currentContext.set("hostZuulResponse", response) + RequestContext.getCurrentContext().set("hostZuulResponse", response) RequestContext.getCurrentContext().setResponseStatusCode(response.getStatusLine().statusCode) RequestContext.getCurrentContext().responseDataStream = response?.entity?.content @@ -368,16 +389,8 @@ class SimpleHostRoutingFilter extends ZuulFilter { if (context.responseDataStream) { byte[] origBytes = context.getResponseDataStream().bytes - ByteArrayInputStream byteStream = new ByteArrayInputStream(origBytes) - InputStream inputStream = byteStream - if (RequestContext.currentContext.responseGZipped) { - inputStream = new GZIPInputStream(byteStream); - } - - context.setResponseDataStream(new ByteArrayInputStream(origBytes)) } - } else { response.getAllHeaders()?.each { Header header -> RequestContext ctx = RequestContext.getCurrentContext() @@ -406,7 +419,6 @@ class SimpleHostRoutingFilter extends ZuulFilter { return true } } - } diff --git a/zuul-simple-webapp/src/main/java/com/netflix/zuul/StartServer.java b/zuul-simple-webapp/src/main/java/com/netflix/zuul/StartServer.java index 95711b7a25..aae5aaab69 100644 --- a/zuul-simple-webapp/src/main/java/com/netflix/zuul/StartServer.java +++ b/zuul-simple-webapp/src/main/java/com/netflix/zuul/StartServer.java @@ -16,19 +16,16 @@ package com.netflix.zuul; -import java.io.File; - -import javax.servlet.ServletContextEvent; -import javax.servlet.ServletContextListener; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.filters.FilterRegistry; import com.netflix.zuul.groovy.GroovyCompiler; import com.netflix.zuul.groovy.GroovyFileFilter; import com.netflix.zuul.monitoring.MonitoringHelper; +import java.io.File; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class StartServer implements ServletContextListener {