Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id AC850200CA7 for ; Wed, 14 Jun 2017 14:40:57 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id AABB6160BE8; Wed, 14 Jun 2017 12:40:57 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 52A98160BDB for ; Wed, 14 Jun 2017 14:40:56 +0200 (CEST) Received: (qmail 75740 invoked by uid 500); 14 Jun 2017 12:40:55 -0000 Mailing-List: contact commits-help@ambari.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: ambari-dev@ambari.apache.org Delivered-To: mailing list commits@ambari.apache.org Received: (qmail 75730 invoked by uid 99); 14 Jun 2017 12:40:53 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 14 Jun 2017 12:40:53 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id D10A2DFE8F; Wed, 14 Jun 2017 12:40:53 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: adoroszlai@apache.org To: commits@ambari.apache.org Message-Id: <54d49270ce584e66b0d4c7418dc3b077@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: ambari git commit: AMBARI-21207. Extend Swagger Maven plugin to handle nested APIs (Balazs Bence Sari via adoroszlai) Date: Wed, 14 Jun 2017 12:40:53 +0000 (UTC) archived-at: Wed, 14 Jun 2017 12:40:57 -0000 Repository: ambari Updated Branches: refs/heads/trunk 24e2cacb8 -> 03812cba4 AMBARI-21207. Extend Swagger Maven plugin to handle nested APIs (Balazs Bence Sari via adoroszlai) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/03812cba Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/03812cba Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/03812cba Branch: refs/heads/trunk Commit: 03812cba4fff24093cf10035667d1a99d71e9a80 Parents: 24e2cac Author: Balazs Bence Sari Authored: Wed Jun 14 14:38:10 2017 +0200 Committer: Attila Doroszlai Committed: Wed Jun 14 14:38:24 2017 +0200 ---------------------------------------------------------------------- ambari-project/pom.xml | 47 +++- ambari-server/pom.xml | 28 +-- .../server/api/services/ClusterService.java | 2 +- .../server/api/services/ServiceService.java | 3 +- utility/pom.xml | 20 ++ .../ambari/swagger/AmbariSwaggerReader.java | 222 +++++++++++++++++++ .../ambari/swagger/AmbariSwaggerReaderTest.java | 182 +++++++++++++++ utility/src/test/resources/log4j.properties | 19 ++ 8 files changed, 499 insertions(+), 24 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/03812cba/ambari-project/pom.xml ---------------------------------------------------------------------- diff --git a/ambari-project/pom.xml b/ambari-project/pom.xml index 9bb2e26..b06bd18 100644 --- a/ambari-project/pom.xml +++ b/ambari-project/pom.xml @@ -32,6 +32,8 @@ 1.6.3 8.1.19.v20160209 6.19 + 1.5.10 + 1.7.20 4 false @@ -210,12 +212,22 @@ org.slf4j slf4j-api - 1.7.20 + ${slf4j.version} org.slf4j slf4j-log4j12 - 1.7.20 + ${slf4j.version} + + + org.slf4j + jul-to-slf4j + ${slf4j.version} + + + org.slf4j + jcl-over-slf4j + ${slf4j.version} org.eclipse.persistence @@ -312,7 +324,6 @@ ant-launcher 1.7.1 - commons-logging commons-logging @@ -486,6 +497,36 @@ checkstyle ${checkstyle.version} + + org.easymock + easymock + 3.4 + + + io.swagger + swagger-annotations + ${swagger.version} + + + io.swagger + swagger-core + ${swagger.version} + + + io.swagger + swagger-jaxrs + ${swagger.version} + + + io.swagger + swagger-models + ${swagger.version} + + + com.github.kongchen + swagger-maven-plugin + 3.1.4 + http://git-wip-us.apache.org/repos/asf/ambari/blob/03812cba/ambari-server/pom.xml ---------------------------------------------------------------------- diff --git a/ambari-server/pom.xml b/ambari-server/pom.xml index a1cd239..ac78595 100644 --- a/ambari-server/pom.xml +++ b/ambari-server/pom.xml @@ -48,7 +48,6 @@ src/main/resources false 2.7.2 - 1.5.10 src/main/package ALL none @@ -413,10 +412,10 @@ com.github.kongchen swagger-maven-plugin - 3.1.4 + org.apache.ambari.swagger.AmbariSwaggerReader false org.apache.ambari.server.api.services http,https @@ -1291,12 +1290,18 @@ org.slf4j slf4j-api - 1.7.20 org.slf4j slf4j-log4j12 - 1.7.20 + + + org.slf4j + jul-to-slf4j + + + org.slf4j + jcl-over-slf4j log4j @@ -1426,17 +1431,14 @@ io.swagger swagger-annotations - ${swagger.version} io.swagger swagger-core - ${swagger.version} io.swagger swagger-jaxrs - ${swagger.version} @@ -1448,7 +1450,6 @@ io.swagger swagger-models - ${swagger.version} org.codehaus.jackson @@ -1477,7 +1478,6 @@ org.easymock easymock - 3.4 test @@ -1651,16 +1651,6 @@ compile - org.slf4j - jul-to-slf4j - 1.7.20 - - - org.slf4j - jcl-over-slf4j - 1.7.20 - - io.dropwizard.metrics metrics-core 3.1.0 http://git-wip-us.apache.org/repos/asf/ambari/blob/03812cba/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java index f61fb2a..44d50731 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java @@ -505,7 +505,7 @@ public class ClusterService extends BaseService { * @return the services service */ @Path("{clusterName}/services") - public ServiceService getServiceHandler(@Context javax.ws.rs.core.Request request, @PathParam("clusterName") String clusterName) { + public ServiceService getServiceHandler(@Context javax.ws.rs.core.Request request, @ApiParam @PathParam("clusterName") String clusterName) { return new ServiceService(clusterName); } http://git-wip-us.apache.org/repos/asf/ambari/blob/03812cba/ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceService.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceService.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceService.java index a28c4aa..6ab2704 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceService.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceService.java @@ -51,7 +51,7 @@ import io.swagger.annotations.ApiResponses; /** * Service responsible for services resource requests. */ -@Api(value = "Services", description = "Endpoint for service specific operations") +@Api(value = "Cluster Services", description = "Endpoint for service specific operations") public class ServiceService extends BaseService { private static final String SERVICE_REQUEST_TYPE = "org.apache.ambari.server.controller.ServiceRequestSwagger"; private static final String ARTIFACT_REQUEST_TYPE = "org.apache.ambari.server.controller.ClusterServiceArtifactRequest"; @@ -112,6 +112,7 @@ public class ServiceService extends BaseService { * @return service collection resource representation */ @GET + @Path("") // This is needed if class level path is not present otherwise no Swagger docs will be generated for this method @Produces(MediaType.TEXT_PLAIN) @ApiOperation(value = "Get all services", nickname = "ServiceService#getServices", http://git-wip-us.apache.org/repos/asf/ambari/blob/03812cba/utility/pom.xml ---------------------------------------------------------------------- diff --git a/utility/pom.xml b/utility/pom.xml index 918080e..ac91474 100644 --- a/utility/pom.xml +++ b/utility/pom.xml @@ -33,6 +33,21 @@ + com.github.kongchen + swagger-maven-plugin + provided + + + org.slf4j + slf4j-log4j12 + provided + + + log4j + log4j + provided + + junit junit compile @@ -53,6 +68,11 @@ guava 19.0 + + org.easymock + easymock + test + http://git-wip-us.apache.org/repos/asf/ambari/blob/03812cba/utility/src/main/java/org/apache/ambari/swagger/AmbariSwaggerReader.java ---------------------------------------------------------------------- diff --git a/utility/src/main/java/org/apache/ambari/swagger/AmbariSwaggerReader.java b/utility/src/main/java/org/apache/ambari/swagger/AmbariSwaggerReader.java new file mode 100644 index 0000000..e258fc4 --- /dev/null +++ b/utility/src/main/java/org/apache/ambari/swagger/AmbariSwaggerReader.java @@ -0,0 +1,222 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ambari.swagger; + +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.ws.rs.Path; + +import org.apache.maven.plugin.logging.Log; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.annotation.AnnotationUtils; + +import com.github.kongchen.swagger.docgen.reader.JaxrsReader; +import com.google.common.base.Function; +import com.google.common.base.Predicates; +import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import io.swagger.annotations.Api; +import io.swagger.models.Operation; +import io.swagger.models.Swagger; +import io.swagger.models.Tag; +import io.swagger.models.parameters.Parameter; +import io.swagger.models.parameters.PathParameter; + +/** + * Customized {@link com.github.kongchen.swagger.docgen.reader.ClassSwaggerReader} implementation to + * treat nested API's. + */ +public class AmbariSwaggerReader extends JaxrsReader { + + /** + * Logger instance. + */ + protected final static Logger logger = LoggerFactory.getLogger(AmbariSwaggerReader.class); + + public AmbariSwaggerReader(Swagger swagger, Log LOG) { + super(swagger, LOG); + } + + private final Map, NestedApiRecord> nestedAPIs = Maps.newHashMap(); + + @Override + public Swagger getSwagger() { + if (null == this.swagger) { + this.swagger = new Swagger(); + } + return this.swagger; + } + + /** + * Original method is overwritten so that to gather information about top level api - nested api relations + */ + @Override + public Swagger read(Set> classes) { + // scan for and register nested API classes + logger.debug("Looking for nested API's"); + for (Class cls: classes) { + logger.debug("Examining API {}", cls.getSimpleName()); + for (Method method: cls.getMethods()) { + Path methodPath = AnnotationUtils.findAnnotation(method, Path.class); + if (null != methodPath) { + Class returnType = method.getReturnType(); + Api nestedApi = AnnotationUtils.findAnnotation(returnType, Api.class); + Path nestedApiPath = AnnotationUtils.findAnnotation(returnType, Path.class); + logger.debug("Examinig API method {}#{}, path={}, returnType={}", cls.getSimpleName(), method.getName(), + nestedApiPath != null ? nestedApiPath.value() : null, returnType.getSimpleName()); + if (null != nestedApi) { + if (null != nestedApiPath) { + logger.info("This class exists both as top level and nested API: {}, treating it as top level API", + returnType.getName()); + } + else { + Path apiPath = AnnotationUtils.findAnnotation(cls, Path.class); + String apiPathValue; + if (null == apiPath) { + logger.warn("Parent api {} also seems to be a nested API. The current version does not support " + + "multi-level nesting."); + apiPathValue = ""; + } + else { + apiPathValue = apiPath.value(); + } + NestedApiRecord nar = new NestedApiRecord(returnType, cls, apiPathValue, method, methodPath.value()); + if (nestedAPIs.containsKey(returnType)) { + logger.warn("{} is a nested API of multiple top level API's. Ignoring top level API {}", returnType, cls); + } + else { + logger.info("Registering nested API: {}", returnType); + nestedAPIs.put(returnType, nar); + } + } + } + } + } + } + logger.info("Found {} nested API's", nestedAPIs.size()); + // With all information gathered, call superclass implementation + return super.read(classes); + } + + /** + * Original method is overwritten to handle nested api's properly + */ + @Override + protected Swagger read(Class cls, String parentPath, + String parentMethod, + boolean readHidden, + String[] parentConsumes, + String[] parentProduces, + Map parentTags, + List parentParameters) { + NestedApiRecord nestedApiRecord = nestedAPIs.get(cls); + if (null != nestedApiRecord) { + logger.info("Processing nested API: {}", nestedApiRecord); + // Get the path parameters of the parent API method. All methods of the nested API class should include these + // parameters. + Operation operation = parseMethod(nestedApiRecord.parentMethod); + List pathParameters = ImmutableList.copyOf( + Collections2.filter(operation.getParameters(), Predicates.instanceOf(PathParameter.class))); + logger.info("Will copy path params from parent method: {}", + Lists.transform(pathParameters, new ParameterToName())); + return super.read(cls, + joinPaths(nestedApiRecord.parentApiPath, nestedApiRecord.parentMethodPath, parentPath), + parentMethod, readHidden, + parentConsumes, parentProduces, parentTags, pathParameters); + } + else { + logger.info("Processing top level API: {}", cls.getSimpleName()); + return super.read(cls, parentPath, parentMethod, readHidden, parentConsumes, parentProduces, parentTags, parentParameters); + } + } + + /** + * Joins path elements properly with slashes avoiding duplicate slashes. + * + * @param firstPath the first path element + * @param paths optionally other path elements + * @return the joined path + */ + static String joinPaths(String firstPath, String... paths) { + StringBuilder joined = new StringBuilder(firstPath); + for(String path: paths) { + if (path.isEmpty()) { /* NOP */ } + else if (joined.length() == 0) { + joined.append(path); + } + else if (joined.charAt(joined.length() - 1) == '/') { + if (path.startsWith("/")) { + joined.append(path.substring(1, path.length())); + } + else { + joined.append(path); + } + } + else { + if (path.startsWith("/")) { + joined.append(path); + } + else { + joined.append('/').append(path); + } + + } + } + return joined.toString(); + } +} + +class ParameterToName implements Function { + public String apply(Parameter input) { + return input.getName(); + } +} + +class NestedApiRecord { + final Class nestedApi; + final Class parentApi; + final String parentApiPath; + final Method parentMethod; + final String parentMethodPath; + + public NestedApiRecord(Class nestedApi, Class parentApi, String parentApiPath, Method parentMethod, String parentMethodPath) { + this.nestedApi = nestedApi; + this.parentApi = parentApi; + this.parentApiPath = parentApiPath; + this.parentMethod = parentMethod; + this.parentMethodPath = parentMethodPath; + } + + @Override + public String toString() { + return "NestedApiRecord {" + + "nestedApi=" + nestedApi + + ", parentApi=" + parentApi + + ", parentApiPath='" + parentApiPath + '\'' + + ", parentMethod=" + parentMethod + + ", parentMethodPath='" + parentMethodPath + '\'' + + '}'; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/03812cba/utility/src/test/java/org/apache/ambari/swagger/AmbariSwaggerReaderTest.java ---------------------------------------------------------------------- diff --git a/utility/src/test/java/org/apache/ambari/swagger/AmbariSwaggerReaderTest.java b/utility/src/test/java/org/apache/ambari/swagger/AmbariSwaggerReaderTest.java new file mode 100644 index 0000000..a102152 --- /dev/null +++ b/utility/src/test/java/org/apache/ambari/swagger/AmbariSwaggerReaderTest.java @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ambari.swagger; + +import static org.easymock.EasyMock.createMock; +import static org.junit.Assert.assertEquals ; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; + +import org.apache.commons.collections.set.ListOrderedSet; +import org.apache.maven.plugin.logging.Log; +import org.junit.Test; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.models.Response; +import io.swagger.models.Swagger; +import io.swagger.models.parameters.Parameter; +import io.swagger.models.parameters.PathParameter; + +public class AmbariSwaggerReaderTest { + + + /** + * Test the {@link AmbariSwaggerReader#joinPaths(String, String...)} method + */ + @Test + public void testJoinPaths() { + assertEquals("/toplevel/nested/{param}/list", + AmbariSwaggerReader.joinPaths("", "/", "/", "", "toplevel", "/nested/", "/{param}", "list")); + assertEquals("/toplevel/nested/{param}/list", + AmbariSwaggerReader.joinPaths("/", "toplevel", "", "/nested/", "/", "/{param}", "list", "")); + } + + /** + * Test the basic case: one top level API and one nested API, each with one operation + */ + @Test + public void swaggerBasicCase() { + AmbariSwaggerReader asr = new AmbariSwaggerReader(null, createMock(Log.class)); + Swagger swagger = asr.read(ImmutableSet.of(TopLevelAPI.class, NestedAPI.class)); + assertEquals(ImmutableSet.of("/toplevel/top", "/toplevel/{param}/nested/list"), + swagger.getPaths().keySet()); + assertPathParamsExist(swagger, "/toplevel/{param}/nested/list", "param"); + } + + /** + * Test conflicting nested API's (the same API's are returned from different top level API's). + * In this case the nested API should be associated to the first processed top level API. + */ + @Test + public void swaggerConflictingNestedApis() { + AmbariSwaggerReader asr = new AmbariSwaggerReader(null, createMock(Log.class)); + ListOrderedSet classes = ListOrderedSet.decorate( + Lists.newArrayList(TopLevelAPI.class, AnotherTopLevelAPI.class, NestedAPI.class)); + Swagger swagger = asr.read(classes); + assertEquals( + ImmutableSet.of("/toplevel/top", "/toplevel/{param}/nested/list", "/toplevel2/anotherTop"), + swagger.getPaths().keySet()); + assertPathParamsExist(swagger, "/toplevel/{param}/nested/list", "param"); + } + + /** + * If an API is both top level (the class has a @Path annotation) and nested (class is a return type of an + * API operation) then it should be treated as top level. + */ + @Test + public void swaggerApiThatIsBothTopLevelAndNestedIsCountedAsTopLevel() { + AmbariSwaggerReader asr = new AmbariSwaggerReader(null, createMock(Log.class)); + Swagger swagger = asr.read(ImmutableSet.of(YetAnotherTopLevelAPI.class, NestedAndTopLevelAPI.class)); + assertEquals(ImmutableSet.of("/toplevel3/yetAnotherTop", "/canBeReachedFromTopToo/list"), + swagger.getPaths().keySet()); + } + + + /** + * Verify that the top level API's path parameters are transferred to the nested API. + */ + private static void assertPathParamsExist(Swagger swagger, String path, String... expectedPathParams) { + List parameters = swagger.getPath(path).getGet().getParameters(); + assertNotNull("No path parameters for path: " + path, parameters); + Set pathParamNames = new HashSet<>(); + for (Parameter param: parameters) { + if (param instanceof PathParameter) { + pathParamNames.add(param.getName()); + } + } + Set missingPathParams = Sets.difference(ImmutableSet.copyOf(expectedPathParams), pathParamNames); + assertTrue("Expected path params for [" + path + "] are missing: " + missingPathParams, missingPathParams.isEmpty()); + } + +} + +@Path("/toplevel") +@Api(value = "Top Level", description = "A top level API") +abstract class TopLevelAPI { + + @GET + @Path("/top") + @ApiOperation(value = "list") + public abstract Response getList(); + + @Path("{param}/nested") + public abstract NestedAPI getNested(@ApiParam @PathParam(value = "param") String param); +} + +@Path("/toplevel2") +@Api(value = "Top Level 2", description = "Another top level API") +abstract class AnotherTopLevelAPI { + + @GET + @Path("/anotherTop") + @ApiOperation(value = "list") + public abstract Response getList(); + + @Path("{param}/anotherNested") + public abstract NestedAPI getSecondNested(@ApiParam @PathParam(value = "param") String param); + +} + +@Path("/toplevel3") +@Api(value = "Top Level 3", description = "Yet another top level API") +abstract class YetAnotherTopLevelAPI { + + @GET + @Path("/yetAnotherTop") + @ApiOperation(value = "list") + public abstract Response getList(); + + @Path("{param}/nested") + public abstract NestedAPI getFirstNested(@ApiParam @PathParam(value = "param") String param); + +} + +@Api(value = "Nested", description = "A nested API") +abstract class NestedAPI { + + @GET + @Path("/list") + @ApiOperation(value = "list") + public abstract Response getList(); + +} + +@Path("/canBeReachedFromTopToo") +@Api(value = "Nested and Top Level", description = "An API that is both nested and top level") +abstract class NestedAndTopLevelAPI { + + @GET + @Path("/list") + @ApiOperation(value = "list") + public abstract Response getList(); + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/03812cba/utility/src/test/resources/log4j.properties ---------------------------------------------------------------------- diff --git a/utility/src/test/resources/log4j.properties b/utility/src/test/resources/log4j.properties new file mode 100644 index 0000000..c088bb7 --- /dev/null +++ b/utility/src/test/resources/log4j.properties @@ -0,0 +1,19 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# log4j configuration used during build and unit tests + +log4j.rootLogger=INFO,stdout +log4j.threshold=ALL +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2} (%F:%M(%L)) - %m%n