diff --git a/wrappers/s2i/java/wrapper/.gitignore b/wrappers/s2i/java/wrapper/.gitignore
deleted file mode 100644
index b83d22266a..0000000000
--- a/wrappers/s2i/java/wrapper/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/target/
diff --git a/wrappers/s2i/java/wrapper/Makefile b/wrappers/s2i/java/wrapper/Makefile
deleted file mode 100644
index b7c19da8e3..0000000000
--- a/wrappers/s2i/java/wrapper/Makefile
+++ /dev/null
@@ -1,12 +0,0 @@
-IMAGE_NAME=seldon-java-wrapper
-VERSION_FILE=target/version.txt
-
-build_jar: update_proto
- mvn clean package -B
-
-update_proto:
- @cp -v ../../../../proto/prediction.proto src/main/proto/
-
-clean:
- rm -rf src/main/proto/*
- mvn clean
diff --git a/wrappers/s2i/java/wrapper/pom.xml b/wrappers/s2i/java/wrapper/pom.xml
deleted file mode 100644
index 157e92f74a..0000000000
--- a/wrappers/s2i/java/wrapper/pom.xml
+++ /dev/null
@@ -1,288 +0,0 @@
-
- 4.0.0
- io.seldon.wrapper
- seldon-core-wrapper
- jar
- 0.1.1
- Seldon Core Java Wrapper
- http://maven.apache.org
- Wrapper for seldon-core Java prediction models. Allows easy creation of a Spring Boot app with Tomcat and gRPC servers for handling the microservice APIs for seldon-core.
-
- Seldon
- https://www.seldon.io/
-
-
-
- Apache License, Version 2.0
- http://www.apache.org/licenses/LICENSE-2.0
-
-
-
-
- cc
- Clive Cox
- cc at seldon.io
- Seldon
- http://www.seldon.io
-
- Project lead
-
-
-
-
- scm:git:git://github.com/SeldonIO/seldon-core.git
- https://github.com/SeldonIO/seldon-core/tree/master
- scm:git:git@github.com:SeldonIO/seldon-core.git
-
-
-
- org.springframework.boot
- spring-boot-starter-parent
- 1.5.1.RELEASE
-
-
-
- 1.8
- UTF-8
- UTF-8
- 1.0.0
- 1.0.0-rc.1
- io.seldon.wrapper.App
-
-
-
- ossrh
- https://oss.sonatype.org/content/repositories/snapshots
-
-
- ossrh
- https://oss.sonatype.org/service/local/staging/deploy/maven2/
-
-
-
- ${project.artifactId}-${project.version}
-
-
- kr.motd.maven
- os-maven-plugin
- 1.4.1.Final
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
- 1.4.1.RELEASE
-
-
-
- repackage
-
-
- exec
-
-
-
-
-
- maven-compiler-plugin
- 3.5.1
-
-
- 1.8
- UTF-8
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
- org.xolstice.maven.plugins
- protobuf-maven-plugin
- 0.5.0
-
- com.google.protobuf:protoc:3.1.0:exe:${os.detected.classifier}
- grpc-java
- io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}
- false
- true
-
- k8s.io/**/*.proto
- **/v1.proto
-
-
-
-
-
- compile
- compile-custom
-
-
-
-
-
- org.apache.maven.plugins
- maven-source-plugin
- 2.2.1
-
-
- attach-sources
-
- jar-no-fork
-
-
-
-
-
- org.apache.maven.plugins
- maven-javadoc-plugin
- 2.9.1
-
-
- attach-javadocs
-
- jar
-
-
-
-
-
- org.sonatype.plugins
- nexus-staging-maven-plugin
- 1.6.7
- true
-
- ossrh
- https://oss.sonatype.org/
- true
-
-
-
- org.apache.maven.plugins
- maven-gpg-plugin
- 1.5
-
- Seldon Dev Team
-
-
-
- sign-artifacts
- verify
-
- sign
-
-
-
-
-
-
-
-
-
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
-
- log4j
- log4j
- 1.2.17
-
-
- com.fasterxml.jackson.core
- jackson-core
- 2.8.6
-
-
- com.fasterxml.jackson.core
- jackson-databind
- 2.8.6
-
-
- commons-lang
- commons-lang
- 2.6
-
-
-
- org.apache.commons
- commons-lang3
- 3.5
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.apache.httpcomponents
- httpclient
- 4.3.4
-
-
-
-
- io.grpc
- grpc-netty
- ${grpc.version}
-
-
- io.grpc
- grpc-stub
- ${grpc.version}
-
-
- io.grpc
- grpc-protobuf
- ${grpc.version}
-
-
-
- com.google.protobuf
- protobuf-java
- 3.2.0
-
-
- com.google.protobuf
- protobuf-java-util
- 3.2.0rc2
-
-
- com.google.guava
- guava
- 22.0
-
-
-
- org.springframework.boot
- spring-boot-starter-actuator
-
-
-
- ai.h2o
- h2o-genmodel
- 3.18.0.5
-
-
- org.nd4j
- nd4j-native-platform
- 0.9.1
-
-
-
- org.jpmml
- pmml-evaluator
- 1.4.1
-
-
- org.jpmml
- pmml-evaluator-extension
- 1.4.1
-
-
-
-
-
diff --git a/wrappers/s2i/java/wrapper/src/main/config/application.properties b/wrappers/s2i/java/wrapper/src/main/config/application.properties
deleted file mode 100644
index 4a35865320..0000000000
--- a/wrappers/s2i/java/wrapper/src/main/config/application.properties
+++ /dev/null
@@ -1,7 +0,0 @@
-server.port = 9000
-
-seldon.api.model.enabled=true
-seldon.api.route.enabled=false
-
-spring.jmx.enabled = false
-
diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/App.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/App.java
deleted file mode 100644
index 239d613eb7..0000000000
--- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/App.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package io.seldon.wrapper;
-
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
-import org.springframework.context.annotation.Import;
-import org.springframework.scheduling.annotation.EnableAsync;
-
-import io.seldon.wrapper.config.AppConfig;
-
-@SpringBootApplication
-@EnableAsync
-@Import({ AppConfig.class })
-public class App {
- public static void main(String[] args) throws Exception {
- SpringApplication.run(App.class, args);
- }
-
-
-}
diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/CombinerRestController.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/CombinerRestController.java
deleted file mode 100644
index e1ad4db692..0000000000
--- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/CombinerRestController.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package io.seldon.wrapper.api;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
-
-import com.google.protobuf.InvalidProtocolBufferException;
-
-import io.seldon.protos.PredictionProtos.SeldonMessage;
-import io.seldon.protos.PredictionProtos.SeldonMessageList;
-import io.seldon.wrapper.exception.APIException;
-import io.seldon.wrapper.exception.APIException.ApiExceptionType;
-import io.seldon.wrapper.pb.ProtoBufUtils;
-
-@RestController
-@ConditionalOnExpression("${seldon.api.combiner.enabled:false}")
-public class CombinerRestController {
-
- private static Logger logger = LoggerFactory.getLogger(RouterRestController.class.getName());
-
- @Autowired
- SeldonPredictionService predictionService;
-
- @RequestMapping(value = "/aggregate", method = {RequestMethod.GET, RequestMethod.POST}, produces = "application/json; charset=utf-8")
- public ResponseEntity route( @RequestParam("json") String json)
- {
- SeldonMessageList request;
- try
- {
- SeldonMessageList.Builder builder = SeldonMessageList.newBuilder();
- ProtoBufUtils.updateMessageBuilderFromJson(builder, json );
- request = builder.build();
- }
- catch (InvalidProtocolBufferException e)
- {
- logger.error("Bad request",e);
- throw new APIException(ApiExceptionType.WRAPPER_INVALID_MESSAGE,json);
- }
-
- try
- {
- SeldonMessage response = predictionService.aggregate(request);
- String res = ProtoBufUtils.toJson(response);
- return new ResponseEntity(res,HttpStatus.OK);
- }
- catch (InvalidProtocolBufferException e) {
- throw new APIException(ApiExceptionType.WRAPPER_INVALID_MESSAGE,"");
- }
- }
-}
-
\ No newline at end of file
diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/ModelRestController.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/ModelRestController.java
deleted file mode 100644
index 7ef1b857e8..0000000000
--- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/ModelRestController.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package io.seldon.wrapper.api;
-
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
-
-import com.google.protobuf.InvalidProtocolBufferException;
-
-import io.seldon.protos.PredictionProtos.SeldonMessage;
-import io.seldon.wrapper.exception.APIException;
-import io.seldon.wrapper.exception.APIException.ApiExceptionType;
-import io.seldon.wrapper.pb.ProtoBufUtils;
-
-@RestController
-@ConditionalOnExpression("${seldon.api.model.enabled:false}")
-public class ModelRestController {
- private static Logger logger = LoggerFactory.getLogger(ModelRestController.class.getName());
-
-
- @Autowired
- SeldonPredictionService predictionService;
-
- @RequestMapping(value = "/predict", method = {RequestMethod.GET, RequestMethod.POST}, produces = "application/json; charset=utf-8")
- public ResponseEntity predictions( @RequestParam("json") String json)
- {
- SeldonMessage request;
- try
- {
- SeldonMessage.Builder builder = SeldonMessage.newBuilder();
- ProtoBufUtils.updateMessageBuilderFromJson(builder, json );
- request = builder.build();
- }
- catch (InvalidProtocolBufferException e)
- {
- logger.error("Bad request",e);
- throw new APIException(ApiExceptionType.WRAPPER_INVALID_MESSAGE,json);
- }
-
- try
- {
- SeldonMessage response = predictionService.predict(request);
- String res = ProtoBufUtils.toJson(response);
- return new ResponseEntity(res,HttpStatus.OK);
- }
- catch (InvalidProtocolBufferException e) {
- throw new APIException(ApiExceptionType.WRAPPER_INVALID_MESSAGE,"");
- }
-
-
- }
-}
diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/RouterRestController.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/RouterRestController.java
deleted file mode 100644
index bc0a74a0fb..0000000000
--- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/RouterRestController.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package io.seldon.wrapper.api;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
-
-import com.google.protobuf.InvalidProtocolBufferException;
-
-import io.seldon.protos.PredictionProtos.Feedback;
-import io.seldon.protos.PredictionProtos.SeldonMessage;
-import io.seldon.wrapper.exception.APIException;
-import io.seldon.wrapper.exception.APIException.ApiExceptionType;
-import io.seldon.wrapper.pb.ProtoBufUtils;
-
-@RestController
-@ConditionalOnExpression("${seldon.api.router.enabled:false}")
-public class RouterRestController {
-
- private static Logger logger = LoggerFactory.getLogger(RouterRestController.class.getName());
-
- @Autowired
- SeldonPredictionService predictionService;
-
- @RequestMapping(value = "/route", method = {RequestMethod.GET, RequestMethod.POST}, produces = "application/json; charset=utf-8")
- public ResponseEntity route( @RequestParam("json") String json)
- {
- SeldonMessage request;
- try
- {
- SeldonMessage.Builder builder = SeldonMessage.newBuilder();
- ProtoBufUtils.updateMessageBuilderFromJson(builder, json );
- request = builder.build();
- }
- catch (InvalidProtocolBufferException e)
- {
- logger.error("Bad request",e);
- throw new APIException(ApiExceptionType.WRAPPER_INVALID_MESSAGE,json);
- }
-
- try
- {
- SeldonMessage response = predictionService.route(request);
- String res = ProtoBufUtils.toJson(response);
- return new ResponseEntity(res,HttpStatus.OK);
- }
- catch (InvalidProtocolBufferException e) {
- throw new APIException(ApiExceptionType.WRAPPER_INVALID_MESSAGE,"");
- }
- }
-
- @RequestMapping(value = "/send-feedback", method = {RequestMethod.GET, RequestMethod.POST}, produces = "application/json; charset=utf-8")
- public ResponseEntity sendFeedback( @RequestParam("json") String json)
- {
- Feedback request;
- try
- {
- Feedback.Builder builder = Feedback.newBuilder();
- ProtoBufUtils.updateMessageBuilderFromJson(builder, json );
- request = builder.build();
- }
- catch (InvalidProtocolBufferException e)
- {
- logger.error("Bad request",e);
- throw new APIException(ApiExceptionType.WRAPPER_INVALID_MESSAGE,json);
- }
-
- try
- {
- SeldonMessage response = predictionService.sendFeedback(request);
- String res = ProtoBufUtils.toJson(response);
- return new ResponseEntity(res,HttpStatus.OK);
- }
- catch (InvalidProtocolBufferException e) {
- throw new APIException(ApiExceptionType.WRAPPER_INVALID_MESSAGE,"");
- }
- }
-
-}
\ No newline at end of file
diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/SeldonPredictionService.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/SeldonPredictionService.java
deleted file mode 100644
index 70f575689d..0000000000
--- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/SeldonPredictionService.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package io.seldon.wrapper.api;
-
-import io.seldon.protos.PredictionProtos.Feedback;
-import io.seldon.protos.PredictionProtos.SeldonMessage;
-import io.seldon.protos.PredictionProtos.SeldonMessageList;
-
-public interface SeldonPredictionService {
- default public SeldonMessage predict(SeldonMessage request) {
- return null;
- }
- default public SeldonMessage route(SeldonMessage request) {
- return null;
- }
- default public SeldonMessage sendFeedback(Feedback request) {
- return null;
- }
- default public SeldonMessage transformInput(SeldonMessage request) {
- return null;
- }
- default public SeldonMessage transformOutput(SeldonMessage request) {
- return null;
- }
- default public SeldonMessage aggregate(SeldonMessageList request) {
- return null;
- }
-}
diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/TransformerRestController.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/TransformerRestController.java
deleted file mode 100644
index 474ec8c706..0000000000
--- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/api/TransformerRestController.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package io.seldon.wrapper.api;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
-
-import com.google.protobuf.InvalidProtocolBufferException;
-
-import io.seldon.protos.PredictionProtos.SeldonMessage;
-import io.seldon.wrapper.exception.APIException;
-import io.seldon.wrapper.exception.APIException.ApiExceptionType;
-import io.seldon.wrapper.pb.ProtoBufUtils;
-
-@RestController
-@ConditionalOnExpression("${seldon.api.transformer.enabled:false}")
-public class TransformerRestController {
-
- private static Logger logger = LoggerFactory.getLogger(TransformerRestController.class.getName());
-
- @Autowired
- SeldonPredictionService predictionService;
-
- @RequestMapping(value = "/transform-input", method = {RequestMethod.GET, RequestMethod.POST}, produces = "application/json; charset=utf-8")
- public ResponseEntity transformInput( @RequestParam("json") String json)
- {
- SeldonMessage request;
- try
- {
- SeldonMessage.Builder builder = SeldonMessage.newBuilder();
- ProtoBufUtils.updateMessageBuilderFromJson(builder, json );
- request = builder.build();
- }
- catch (InvalidProtocolBufferException e)
- {
- logger.error("Bad request",e);
- throw new APIException(ApiExceptionType.WRAPPER_INVALID_MESSAGE,json);
- }
-
- try
- {
- SeldonMessage response = predictionService.transformInput(request);
- String res = ProtoBufUtils.toJson(response);
- return new ResponseEntity(res,HttpStatus.OK);
- }
- catch (InvalidProtocolBufferException e) {
- throw new APIException(ApiExceptionType.WRAPPER_INVALID_MESSAGE,"");
- }
- }
-
- @RequestMapping(value = "/transform-output", method = {RequestMethod.GET, RequestMethod.POST}, produces = "application/json; charset=utf-8")
- public ResponseEntity transformOutput( @RequestParam("json") String json)
- {
- SeldonMessage request;
- try
- {
- SeldonMessage.Builder builder = SeldonMessage.newBuilder();
- ProtoBufUtils.updateMessageBuilderFromJson(builder, json );
- request = builder.build();
- }
- catch (InvalidProtocolBufferException e)
- {
- logger.error("Bad request",e);
- throw new APIException(ApiExceptionType.WRAPPER_INVALID_MESSAGE,json);
- }
-
- try
- {
- SeldonMessage response = predictionService.transformOutput(request);
- String res = ProtoBufUtils.toJson(response);
- return new ResponseEntity(res,HttpStatus.OK);
- }
- catch (InvalidProtocolBufferException e) {
- throw new APIException(ApiExceptionType.WRAPPER_INVALID_MESSAGE,"");
- }
- }
-}
-
\ No newline at end of file
diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/config/AppConfig.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/config/AppConfig.java
deleted file mode 100644
index d55b9e53ce..0000000000
--- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/config/AppConfig.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package io.seldon.wrapper.config;
-
-import java.util.concurrent.Executor;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.catalina.connector.Connector;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
-import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
-import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer;
-import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
-import org.springframework.context.ApplicationListener;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Scope;
-import org.springframework.context.event.ContextClosedEvent;
-
-
-/**
- * Spring REST config
- * @author clive
- *
- */
-public class AppConfig {
-
- @Bean
- @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
- public EmbeddedServletContainerCustomizer containerCustomizer() {
- return new CustomizationBean();
- }
-
- @Bean
- public GracefulShutdown gracefulShutdown() {
- return new GracefulShutdown();
- }
-
- @Bean
- public EmbeddedServletContainerCustomizer tomcatCustomizer() {
- return new EmbeddedServletContainerCustomizer() {
-
- @Override
- public void customize(ConfigurableEmbeddedServletContainer container) {
- if (container instanceof TomcatEmbeddedServletContainerFactory) {
- ((TomcatEmbeddedServletContainerFactory) container)
- .addConnectorCustomizers(gracefulShutdown());
- }
-
- }
- };
- }
-
- /**
- * Ensure a graceful shutdown of Tomcat to allow requests in process to complete.
- * @author clive
- *
- */
- private static class GracefulShutdown implements TomcatConnectorCustomizer,
- ApplicationListener {
-
- private static final Logger log = LoggerFactory.getLogger(GracefulShutdown.class);
-
- private volatile Connector connector;
-
- @Override
- public void customize(Connector connector) {
- this.connector = connector;
- }
-
- @Override
- public void onApplicationEvent(ContextClosedEvent event) {
- log.info("Starting graceful shutdown of Tomcat");
- if (this.connector != null)
- {
- this.connector.pause();
- Executor executor = this.connector.getProtocolHandler().getExecutor();
- if (executor instanceof ThreadPoolExecutor) {
- try {
- ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
- threadPoolExecutor.shutdown();
- if (!threadPoolExecutor.awaitTermination(20, TimeUnit.SECONDS)) {
- log.warn("Tomcat thread pool did not shut down gracefully within "
- + "20 seconds. Proceeding with forceful shutdown");
- }
- else
- {
- log.info("Thread pool has closed");
- }
- }
- catch (InterruptedException ex) {
- Thread.currentThread().interrupt();
- }
- }
- }
- }
-
- }
-}
-
diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/config/CustomizationBean.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/config/CustomizationBean.java
deleted file mode 100644
index bb9839d6ca..0000000000
--- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/config/CustomizationBean.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*******************************************************************************
- * Copyright 2017 Seldon Technologies Ltd (http://www.seldon.io/)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *******************************************************************************/
-package io.seldon.wrapper.config;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
-import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
-
-/**
- * Customization of the Tomcat embedded servlet engne
- * @author clive
- *
- */
-public class CustomizationBean implements EmbeddedServletContainerCustomizer {
-
- private final static Logger logger = LoggerFactory.getLogger(CustomizationBean.class);
-
- @Value("${server.port}")
- private Integer defaultServerPort;
-
- @Override
- public void customize(ConfigurableEmbeddedServletContainer container) {
- logger.info("Customizing EmbeddedServlet");
-
- Integer serverPort;
- serverPort = defaultServerPort;
-
- logger.info("setting serverPort[{}]", serverPort);
- container.setPort(serverPort);
- }
-
-}
diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/exception/APIException.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/exception/APIException.java
deleted file mode 100644
index 33603cb49b..0000000000
--- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/exception/APIException.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Seldon -- open source prediction engine
- * =======================================
- *
- * Copyright 2011-2017 Seldon Technologies Ltd and Rummble Ltd (http://www.seldon.io/)
- *
- * ********************************************************************************************
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * ********************************************************************************************
- */
-
-package io.seldon.wrapper.exception;
-
-/**
- * API Exceptions
- * @author clive
- *
- */
-public class APIException extends RuntimeException {
-
- public enum ApiExceptionType {
-
- WRAPPER_INVALID_MESSAGE(201,"Invalid prediction message",500);
-
- int id;
- String message;
- int httpCode;
-
- ApiExceptionType(int id,String message,int httpCode) {
- this.id = id;
- this.message = message;
- this.httpCode = httpCode;
- }
-
- public int getId() {
- return id;
- }
-
- public String getMessage() {
- return message;
- }
-
- public int getHttpCode() {
- return httpCode;
- }
-
-
- };
-
- ApiExceptionType apiExceptionType;
- String info;
-
- public APIException(ApiExceptionType apiExceptionType,String info) {
- super();
- this.apiExceptionType = apiExceptionType;
- this.info = info;
- }
-
- public ApiExceptionType getApiExceptionType() {
- return apiExceptionType;
- }
-
- public void setApiExceptionType(ApiExceptionType apiExceptionType) {
- this.apiExceptionType = apiExceptionType;
- }
-
- public String getInfo() {
- return info;
- }
-
- public void setInfo(String info) {
- this.info = info;
- }
-
-}
diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/CombinerService.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/CombinerService.java
deleted file mode 100644
index 90d74c735c..0000000000
--- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/CombinerService.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package io.seldon.wrapper.grpc;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import io.seldon.protos.CombinerGrpc;
-
-public class CombinerService extends CombinerGrpc.CombinerImplBase {
-
- protected static Logger logger = LoggerFactory.getLogger(CombinerService.class.getName());
-
- private SeldonGrpcServer server;
-
- public CombinerService(SeldonGrpcServer server) {
- super();
- this.server = server;
- }
-
- @Override
- public void aggregate(io.seldon.protos.PredictionProtos.SeldonMessageList request,
- io.grpc.stub.StreamObserver responseObserver) {
- logger.debug("Received aggregate request");
- responseObserver.onNext(server.getPredictionService().aggregate(request));
- responseObserver.onCompleted();
- }
-}
\ No newline at end of file
diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/GenericService.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/GenericService.java
deleted file mode 100644
index b4047324a7..0000000000
--- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/GenericService.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package io.seldon.wrapper.grpc;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import io.seldon.protos.GenericGrpc;
-
-public class GenericService extends GenericGrpc.GenericImplBase {
-
- protected static Logger logger = LoggerFactory.getLogger(GenericService.class.getName());
-
- private SeldonGrpcServer server;
-
- public GenericService(SeldonGrpcServer server) {
- super();
- this.server = server;
- }
-
- @Override
- public void transformInput(io.seldon.protos.PredictionProtos.SeldonMessage request,
- io.grpc.stub.StreamObserver responseObserver) {
- logger.debug("Received transformInput request");
- responseObserver.onNext(server.getPredictionService().transformInput(request));
- responseObserver.onCompleted();
- }
-
- @Override
- public void route(io.seldon.protos.PredictionProtos.SeldonMessage request,
- io.grpc.stub.StreamObserver responseObserver) {
- logger.debug("Received route request");
- responseObserver.onNext(server.getPredictionService().route(request));
- responseObserver.onCompleted();
- }
-
- @Override
- public void sendFeedback(io.seldon.protos.PredictionProtos.Feedback request,
- io.grpc.stub.StreamObserver responseObserver) {
- logger.debug("Received sendFeedback request");
- responseObserver.onNext(server.getPredictionService().sendFeedback(request));
- responseObserver.onCompleted();
- }
-
-
-
- @Override
- public void transformOutput(io.seldon.protos.PredictionProtos.SeldonMessage request,
- io.grpc.stub.StreamObserver responseObserver) {
- logger.debug("Received transformOutput request");
- responseObserver.onNext(server.getPredictionService().transformOutput(request));
- responseObserver.onCompleted();
- }
-
- @Override
- public void aggregate(io.seldon.protos.PredictionProtos.SeldonMessageList request,
- io.grpc.stub.StreamObserver responseObserver) {
- logger.debug("Received aggregate request");
- responseObserver.onNext(server.getPredictionService().aggregate(request));
- responseObserver.onCompleted();
- }
-}
diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/ModelService.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/ModelService.java
deleted file mode 100644
index 25f558bef2..0000000000
--- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/ModelService.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*******************************************************************************
- * Copyright 2017 Seldon Technologies Ltd (http://www.seldon.io/)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *******************************************************************************/
-package io.seldon.wrapper.grpc;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import io.seldon.protos.ModelGrpc;
-
-/**
- * Passes gRPC requests on to the engine.
- * @author clive
- *
- */
-public class ModelService extends ModelGrpc.ModelImplBase {
-
- protected static Logger logger = LoggerFactory.getLogger(ModelService.class.getName());
-
- private SeldonGrpcServer server;
-
- public ModelService(SeldonGrpcServer server) {
- super();
- this.server = server;
- }
-
- @Override
- public void predict(io.seldon.protos.PredictionProtos.SeldonMessage request,
- io.grpc.stub.StreamObserver responseObserver) {
- logger.debug("Received predict request");
-
- responseObserver.onNext(server.getPredictionService().predict(request));
- responseObserver.onCompleted();
- }
-
-}
diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/OutputTransformerService.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/OutputTransformerService.java
deleted file mode 100644
index 6ccedcc99c..0000000000
--- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/OutputTransformerService.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package io.seldon.wrapper.grpc;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import io.seldon.protos.OutputTransformerGrpc;
-
-public class OutputTransformerService extends OutputTransformerGrpc.OutputTransformerImplBase {
-
- protected static Logger logger = LoggerFactory.getLogger(OutputTransformerService.class.getName());
-
- private SeldonGrpcServer server;
-
- public OutputTransformerService(SeldonGrpcServer server) {
- super();
- this.server = server;
- }
-
- @Override
- public void transformOutput(io.seldon.protos.PredictionProtos.SeldonMessage request,
- io.grpc.stub.StreamObserver responseObserver) {
- logger.debug("Received transformOutput request");
- responseObserver.onNext(server.getPredictionService().transformOutput(request));
- responseObserver.onCompleted();
- }
-}
\ No newline at end of file
diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/RouterService.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/RouterService.java
deleted file mode 100644
index 6589451723..0000000000
--- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/RouterService.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package io.seldon.wrapper.grpc;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import io.seldon.protos.RouterGrpc;
-
-public class RouterService extends RouterGrpc.RouterImplBase {
-
- protected static Logger logger = LoggerFactory.getLogger(RouterService.class.getName());
-
- private SeldonGrpcServer server;
-
- public RouterService(SeldonGrpcServer server) {
- super();
- this.server = server;
- }
-
- @Override
- public void route(io.seldon.protos.PredictionProtos.SeldonMessage request,
- io.grpc.stub.StreamObserver responseObserver) {
- logger.debug("Received route request");
- responseObserver.onNext(server.getPredictionService().route(request));
- responseObserver.onCompleted();
- }
-
- @Override
- public void sendFeedback(io.seldon.protos.PredictionProtos.Feedback request,
- io.grpc.stub.StreamObserver responseObserver) {
- logger.debug("Received sendFeedback request");
- responseObserver.onNext(server.getPredictionService().sendFeedback(request));
- responseObserver.onCompleted();
- }
-
-
-
-}
-
diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/SeldonGrpcServer.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/SeldonGrpcServer.java
deleted file mode 100644
index 24e08640eb..0000000000
--- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/SeldonGrpcServer.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*******************************************************************************
- * Copyright 2017 Seldon Technologies Ltd (http://www.seldon.io/)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *******************************************************************************/
-package io.seldon.wrapper.grpc;
-
-import java.io.IOException;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.scheduling.annotation.Async;
-import org.springframework.stereotype.Component;
-
-import io.grpc.Server;
-import io.grpc.ServerBuilder;
-import io.seldon.wrapper.api.SeldonPredictionService;
-
-@Component
-public class SeldonGrpcServer {
- protected static Logger logger = LoggerFactory.getLogger(SeldonGrpcServer.class.getName());
-
- public static final int SERVER_PORT = 5001;
-
- private final int port;
- private final Server server;
-
- private final SeldonPredictionService predictionService;
-
- @Autowired
- public SeldonGrpcServer(SeldonPredictionService predictionService,@Value("${grpc.port}") Integer grpcPort)
- {
- logger.info("grpc port {}",grpcPort);
-
- port = grpcPort;
-
- this.predictionService = predictionService;
- server = ServerBuilder
- .forPort(port)
- .addService(new ModelService(this))
- .addService(new RouterService(this))
- .addService(new TransformerService(this))
- .addService(new OutputTransformerService(this))
- .addService(new CombinerService(this))
- .addService(new GenericService(this))
- .build();
- }
-
-
-
- public SeldonPredictionService getPredictionService() {
- return predictionService;
- }
-
- @Async
- public void runServer() throws InterruptedException, IOException
- {
- logger.info("Starting grpc server");
- start();
- blockUntilShutdown();
- }
-
- /**
- * Start serving requests.
- */
- public void start() throws IOException {
- server.start();
- logger.info("Server started, listening on " + port);
- Runtime.getRuntime().addShutdownHook(new Thread() {
- @Override
- public void run() {
- // Use stderr here since the logger may has been reset by its JVM shutdown hook.
- System.err.println("*** shutting down gRPC server since JVM is shutting down");
- SeldonGrpcServer.this.stop();
- System.err.println("*** server shut down");
- }
- });
- }
-
- /** Stop serving requests and shutdown resources. */
- public void stop() {
- if (server != null) {
- server.shutdown();
- }
- }
-
- /**
- * Await termination on the main thread since the grpc library uses daemon threads.
- */
- private void blockUntilShutdown() throws InterruptedException {
- if (server != null) {
- server.awaitTermination();
- }
- }
-
-
-}
diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/SeldonGrpcServerInitializer.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/SeldonGrpcServerInitializer.java
deleted file mode 100644
index 0555cbdef0..0000000000
--- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/SeldonGrpcServerInitializer.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*******************************************************************************
- * Copyright 2017 Seldon Technologies Ltd (http://www.seldon.io/)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *******************************************************************************/
-package io.seldon.wrapper.grpc;
-
-import java.io.IOException;
-
-import javax.annotation.PostConstruct;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-@Component
-public class SeldonGrpcServerInitializer {
-
- protected static Logger logger = LoggerFactory.getLogger(SeldonGrpcServerInitializer.class.getName());
-
- @Autowired
- SeldonGrpcServer server;
-
- @PostConstruct
- public void initialise() {
- try
- {
- server.runServer();
- } catch (InterruptedException e) {
- logger.error("Failed to start grc server",e);
- } catch (IOException e) {
- logger.error("Failed to start grc server",e);
- }
- }
-
-}
diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/TransformerService.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/TransformerService.java
deleted file mode 100644
index d9c60a05cc..0000000000
--- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/grpc/TransformerService.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package io.seldon.wrapper.grpc;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import io.seldon.protos.TransformerGrpc;
-
-public class TransformerService extends TransformerGrpc.TransformerImplBase {
-
- protected static Logger logger = LoggerFactory.getLogger(TransformerService.class.getName());
-
- private SeldonGrpcServer server;
-
- public TransformerService(SeldonGrpcServer server) {
- super();
- this.server = server;
- }
-
- @Override
- public void transformInput(io.seldon.protos.PredictionProtos.SeldonMessage request,
- io.grpc.stub.StreamObserver responseObserver) {
- logger.debug("Received transformInput request");
- responseObserver.onNext(server.getPredictionService().transformInput(request));
- responseObserver.onCompleted();
- }
-}
diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/pb/ProtoBufUtils.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/pb/ProtoBufUtils.java
deleted file mode 100644
index 7794ec8bb2..0000000000
--- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/pb/ProtoBufUtils.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*******************************************************************************
- * Copyright 2017 Seldon Technologies Ltd (http://www.seldon.io/)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *******************************************************************************/
-package io.seldon.wrapper.pb;
-
-import com.google.protobuf.InvalidProtocolBufferException;
-import com.google.protobuf.Message;
-import com.google.protobuf.util.JsonFormat;
-import com.google.protobuf.util.JsonFormat.Printer;
-
-public class ProtoBufUtils {
-
- private ProtoBufUtils() {
- }
-
- /**
- * Serialize a protobuf message to JSON, indicating if whitespace needs to be stripped.
- *
- * @param message
- * The protobuf message to serialize.
- * @param omittingInsignificantWhitespace
- * True if needs to be stripped of whitespace.
- * @return json string
- * @throws InvalidProtocolBufferException
- */
- public static String toJson(Message message, boolean omittingInsignificantWhitespace) throws InvalidProtocolBufferException {
- String json = null;
- // json = JsonFormat.printer().includingDefaultValueFields().preservingProtoFieldNames().print(message);
- // json =
- // JsonFormat.printer().includingDefaultValueFields().preservingProtoFieldNames().omittingInsignificantWhitespace().print(message);
-
- Printer jsonPrinter = JsonFormat.printer().includingDefaultValueFields().preservingProtoFieldNames();
- if (omittingInsignificantWhitespace) {
- jsonPrinter = jsonPrinter.omittingInsignificantWhitespace();
- }
- return jsonPrinter.print(message);
- }
-
- /**
- * Serialize a protobuf message to JSON, allowing the inclusion of whitespace.
- *
- * @param message
- * The protobuf message to serialize.
- * @return json string
- * @throws InvalidProtocolBufferException
- */
- public static String toJson(Message message) throws InvalidProtocolBufferException {
- boolean omittingInsignificantWhitespace = false;
- return toJson(message, omittingInsignificantWhitespace);
- }
-
- public static void updateMessageBuilderFromJson(T messageBuilder, String json) throws InvalidProtocolBufferException {
- JsonFormat.parser().ignoringUnknownFields()
- .merge(json, messageBuilder);
- }
-
-}
diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/utils/DL4JUtils.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/utils/DL4JUtils.java
deleted file mode 100644
index db6ae5f4e2..0000000000
--- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/utils/DL4JUtils.java
+++ /dev/null
@@ -1,115 +0,0 @@
-package io.seldon.wrapper.utils;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.nd4j.linalg.api.ndarray.INDArray;
-import org.nd4j.linalg.factory.Nd4j;
-
-import com.google.protobuf.ListValue;
-import com.google.protobuf.Value;
-
-import io.seldon.protos.PredictionProtos.DefaultData;
-import io.seldon.protos.PredictionProtos.Tensor;
-import io.seldon.protos.PredictionProtos.DefaultData.DataOneofCase;
-
-/**
- * Utilities for working with Deep Learning4J Models
- * @author clive
- *
- */
-public class DL4JUtils {
-
- /**
- * Convert seldon protobuf DefaultData to nd4j Array
- * @param data Seldon protobuf message
- * @return nd4j Array
- */
- public static INDArray getINDArray(DefaultData data) {
-
- if (data.getDataOneofCase() == DataOneofCase.TENSOR) {
-
- List valuesList = data.getTensor().getValuesList();
- List shapeList = data.getTensor().getShapeList();
-
- double[] values = new double[valuesList.size()];
- int[] shape = new int[shapeList.size()];
- for (int i = 0; i < values.length; i++) {
- values[i] = valuesList.get(i);
- }
- for (int i = 0; i < shape.length; i++) {
- shape[i] = shapeList.get(i);
- }
-
- INDArray newArr = Nd4j.create(values, shape, 'c');
-
- return newArr;
- } else if (data.getDataOneofCase() == DataOneofCase.NDARRAY) {
- ListValue list = data.getNdarray();
- int bLength = list.getValuesCount();
- int vLength = list.getValues(0).getListValue().getValuesCount();
-
- double[] values = new double[bLength * vLength];
- int[] shape = { bLength, vLength };
-
- for (int i = 0; i < bLength; ++i) {
- for (int j = 0; j < vLength; j++) {
- values[i * bLength + j] = list.getValues(i).getListValue().getValues(j).getNumberValue();
- }
- }
-
- INDArray newArr = Nd4j.create(values, shape, 'c');
-
- return newArr;
- }
- return null;
- }
-
- /**
- * Convert a nd4j array into a seldon protobuf DefaultData following same type as oldData
- * @param oldData original data
- * @param newData nd4j array
- * @return seldon DefaultData protobuf message
- */
- public static DefaultData updateData(DefaultData oldData, INDArray newData) {
- DefaultData.Builder dataBuilder = DefaultData.newBuilder();
-
- dataBuilder.addAllNames(oldData.getNamesList());
-
- // int index=0;
- // for (Iterator i = oldData.getFeaturesList().iterator();
- // i.hasNext();){
- // dataBuilder.setFeatures(index, i.next());
- // index++;
- // }
-
- if (oldData == null || oldData.getDataOneofCase() == DataOneofCase.TENSOR) {
- Tensor.Builder tBuilder = Tensor.newBuilder();
- List shapeList = Arrays.stream(newData.shape()).boxed().collect(Collectors.toList());
- tBuilder.addAllShape(shapeList);
-
- for (int i = 0; i < shapeList.get(0); ++i) {
- for (int j = 0; j < shapeList.get(1); ++j) {
- tBuilder.addValues(newData.getDouble(i, j));
- }
- }
- dataBuilder.setTensor(tBuilder);
- return dataBuilder.build();
- } else if (oldData.getDataOneofCase() == DataOneofCase.NDARRAY) {
- ListValue.Builder b1 = ListValue.newBuilder();
- for (int i = 0; i < newData.shape()[0]; ++i) {
- ListValue.Builder b2 = ListValue.newBuilder();
- for (int j = 0; j < newData.shape()[1]; j++) {
- b2.addValues(Value.newBuilder().setNumberValue(newData.getDouble(i, j)));
- }
- b1.addValues(Value.newBuilder().setListValue(b2.build()));
- }
- dataBuilder.setNdarray(b1.build());
- return dataBuilder.build();
- }
- return null;
-
- }
-
-}
diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/utils/H2OUtils.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/utils/H2OUtils.java
deleted file mode 100644
index 9c8a956d3f..0000000000
--- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/utils/H2OUtils.java
+++ /dev/null
@@ -1,183 +0,0 @@
-package io.seldon.wrapper.utils;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.nd4j.linalg.dataset.api.iterator.CachingDataSetIterator;
-
-import com.google.protobuf.ListValue;
-import com.google.protobuf.Value;
-
-import hex.genmodel.easy.RowData;
-import hex.genmodel.easy.prediction.AbstractPrediction;
-import hex.genmodel.easy.prediction.BinomialModelPrediction;
-import hex.genmodel.easy.prediction.ClusteringModelPrediction;
-import hex.genmodel.easy.prediction.DimReductionModelPrediction;
-import hex.genmodel.easy.prediction.MultinomialModelPrediction;
-import hex.genmodel.easy.prediction.OrdinalModelPrediction;
-import hex.genmodel.easy.prediction.RegressionModelPrediction;
-import io.seldon.protos.PredictionProtos.DefaultData;
-import io.seldon.protos.PredictionProtos.Tensor;
-import io.seldon.protos.PredictionProtos.DefaultData.DataOneofCase;
-
-/**
- * Utilities for working with H2O models
- *
- * @author clive
- *
- */
-public class H2OUtils {
-
- /**
- * Convert a Seldon Default Data into H2O RowData
- * @param data Seldon protobuf data
- * @return List of H2O RowData
- */
- public static List convertSeldonMessage(DefaultData data) {
- List out = new ArrayList<>();
- if (data.getDataOneofCase() == DataOneofCase.TENSOR) {
-
- List valuesList = data.getTensor().getValuesList();
- List shapeList = data.getTensor().getShapeList();
-
- if (shapeList.size() > 2) {
- return null;
- }
-
- int cols = 0;
- if (shapeList.size() == 1)
- cols = shapeList.get(0);
- else
- cols = shapeList.get(1);
- RowData row = new RowData();
- for (int i = 0; i < valuesList.size(); i++) {
- if (i > 0 && i % cols == 0) {
- out.add(row);
- row = new RowData();
- }
- String name = data.getNamesCount() > 0 ? data.getNames(i % cols) : "" + (i % cols);
- row.put(name, valuesList.get(i));
- }
- out.add(row);
-
- return out;
- } else if (data.getDataOneofCase() == DataOneofCase.NDARRAY) {
- ListValue list = data.getNdarray();
- int rows = list.getValuesCount();
- int cols = list.getValues(0).getListValue().getValuesCount();
-
- for (int i = 0; i < rows; ++i) {
- RowData row = new RowData();
- for (int j = 0; j < cols; j++) {
- String name = data.getNamesCount() > 0 ? data.getNames(j % cols) : "" + (j % cols);
- Object value;
- Value listValue = list.getValues(i).getListValue().getValues(j);
- switch(listValue.getKindCase()) {
- case NUMBER_VALUE:
- value = listValue.getNumberValue();
- break;
- case STRING_VALUE:
- value = listValue.getStringValue();
- break;
- case BOOL_VALUE:
- //Get value as String
- value = listValue.getStringValue();
- break;
- case NULL_VALUE:
- // Treat Nulls as 0
- value = 0.0;
- break;
- case LIST_VALUE:
- throw new UnsupportedOperationException("Only 2-D arrays unsupported for H2O conversion");
- case STRUCT_VALUE:
- throw new UnsupportedOperationException("Struct in NDArray unsupported for H2O conversion");
- default:
- throw new UnsupportedOperationException("Unknown kind in NDArray");
- }
- row.put(name, value);
- }
- out.add(row);
- }
- return out;
- } else
- return null;
- }
-
- /**
- * Convert a prediction result from H2O to Seldon protobuf DefaultData with same type as input
- * @param predictions The H2O predictions
- * @param input The original input
- * @return A seldon DefaultData protobuf message
- */
- public static DefaultData convertH2OPrediction(List predictions, DefaultData input) {
- if (input == null || input.getDataOneofCase() == DataOneofCase.TENSOR) {
- int rows = predictions.size();
- Tensor.Builder tBuilder = Tensor.newBuilder();
- for (AbstractPrediction p : predictions) {
- if (p instanceof BinomialModelPrediction) {
- BinomialModelPrediction bp = (BinomialModelPrediction) p;
- for (int i = 0; i < bp.classProbabilities.length; i++)
- tBuilder.addValues(bp.classProbabilities[i]);
- } else if (p instanceof MultinomialModelPrediction) {
- MultinomialModelPrediction mp = (MultinomialModelPrediction) p;
- for (int i = 0; i < mp.classProbabilities.length; i++)
- tBuilder.addValues(mp.classProbabilities[i]);
- } else if (p instanceof OrdinalModelPrediction) {
- OrdinalModelPrediction op = (OrdinalModelPrediction) p;
- for (int i = 0; i < op.classProbabilities.length; i++)
- tBuilder.addValues(op.classProbabilities[i]);
- } else if (p instanceof ClusteringModelPrediction) {
- ClusteringModelPrediction cp = (ClusteringModelPrediction) p;
- for (int i = 0; i < cp.distances.length; i++)
- tBuilder.addValues(cp.distances[i]);
- } else if (p instanceof RegressionModelPrediction) {
- RegressionModelPrediction r = (RegressionModelPrediction) p;
- tBuilder.addValues(r.value);
- } else if (p instanceof DimReductionModelPrediction) {
- DimReductionModelPrediction cp = (DimReductionModelPrediction) p;
- for (int i = 0; i < cp.dimensions.length; i++)
- tBuilder.addValues(cp.dimensions[i]);
- } else
- return null;
- }
- DefaultData.Builder dataBuilder = DefaultData.newBuilder();
- dataBuilder.setTensor(tBuilder);
- return dataBuilder.build();
- } else if (input.getDataOneofCase() == DataOneofCase.NDARRAY) {
- ListValue.Builder rows = ListValue.newBuilder();
- for (AbstractPrediction p : predictions) {
- ListValue.Builder row = ListValue.newBuilder();
- if (p instanceof BinomialModelPrediction) {
- BinomialModelPrediction bp = (BinomialModelPrediction) p;
- for (int i = 0; i < bp.classProbabilities.length; i++)
- row.addValues(Value.newBuilder().setNumberValue(bp.classProbabilities[i]));
- } else if (p instanceof MultinomialModelPrediction) {
- MultinomialModelPrediction mp = (MultinomialModelPrediction) p;
- for (int i = 0; i < mp.classProbabilities.length; i++)
- row.addValues(Value.newBuilder().setNumberValue(mp.classProbabilities[i]));
- } else if (p instanceof OrdinalModelPrediction) {
- OrdinalModelPrediction op = (OrdinalModelPrediction) p;
- for (int i = 0; i < op.classProbabilities.length; i++)
- row.addValues(Value.newBuilder().setNumberValue(op.classProbabilities[i]));
- } else if (p instanceof ClusteringModelPrediction) {
- ClusteringModelPrediction cp = (ClusteringModelPrediction) p;
- for (int i = 0; i < cp.distances.length; i++)
- row.addValues(Value.newBuilder().setNumberValue(cp.distances[i]));
- } else if (p instanceof RegressionModelPrediction) {
- RegressionModelPrediction r = (RegressionModelPrediction) p;
- row.addValues(Value.newBuilder().setNumberValue(r.value));
- } else if (p instanceof DimReductionModelPrediction) {
- DimReductionModelPrediction cp = (DimReductionModelPrediction) p;
- for (int i = 0; i < cp.dimensions.length; i++)
- row.addValues(Value.newBuilder().setNumberValue(cp.dimensions[i]));
- } else
- return null;
- rows.addValues(Value.newBuilder().setListValue(row.build()));
- }
- DefaultData.Builder dataBuilder = DefaultData.newBuilder();
- dataBuilder.setNdarray(rows.build());
- return dataBuilder.build();
- } else
- return null;
- }
-}
diff --git a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/utils/PMMLUtils.java b/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/utils/PMMLUtils.java
deleted file mode 100644
index 266b9ebcd0..0000000000
--- a/wrappers/s2i/java/wrapper/src/main/java/io/seldon/wrapper/utils/PMMLUtils.java
+++ /dev/null
@@ -1,193 +0,0 @@
-package io.seldon.wrapper.utils;
-
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.dmg.pmml.FieldName;
-import org.dmg.pmml.MiningFunction;
-import org.dmg.pmml.PMML;
-import org.jpmml.evaluator.Evaluator;
-import org.jpmml.evaluator.FieldValue;
-import org.jpmml.evaluator.InputField;
-import org.jpmml.evaluator.ModelEvaluatorFactory;
-import org.jpmml.evaluator.ProbabilityDistribution;
-import org.jpmml.evaluator.ReportingValueFactoryFactory;
-import org.jpmml.evaluator.TargetField;
-import org.jpmml.evaluator.ValueFactoryFactory;
-
-import com.google.protobuf.ListValue;
-import com.google.protobuf.Value;
-
-import hex.genmodel.easy.RowData;
-import io.seldon.protos.PredictionProtos.DefaultData;
-import io.seldon.protos.PredictionProtos.DefaultData.DataOneofCase;
-import io.seldon.protos.PredictionProtos.Tensor;
-import io.seldon.wrapper.exception.APIException;
-import io.seldon.wrapper.exception.APIException.ApiExceptionType;
-
-public class PMMLUtils {
-
-
- public static abstract class ValuesBuilder {
- public abstract void addValues(double v);
- }
-
- public static class TensorBuilder extends ValuesBuilder {
- Tensor.Builder b;
-
- public TensorBuilder(Tensor.Builder b) {
- super();
- this.b = b;
- }
-
- @Override
- public void addValues(double v) {
- b.addValues(v);
- }
- }
-
- public static class ListBuilder extends ValuesBuilder {
- ListValue.Builder b;
-
- public ListBuilder(ListValue.Builder b) {
- super();
- this.b = b;
- }
-
- @Override
- public void addValues(double v) {
- b.addValues(Value.newBuilder().setNumberValue(v));
- }
- }
-
- private Map getInputFieldMap(Evaluator evaluator)
- {
- Map fieldMap = new HashMap<>();
- List inputFields = evaluator.getInputFields();
- for(InputField inputField : inputFields){
- FieldName inputFieldName = inputField.getName();
- fieldMap.put(inputFieldName.getValue(), inputField);
- }
- return fieldMap;
- }
-
-
- private void evaluateForTensor(Evaluator evaluator,Map arguments,ValuesBuilder tBuilder)
- {
- Map result = evaluator.evaluate(arguments);
- List targetFields = evaluator.getTargetFields();
- MiningFunction miningFunction = evaluator.getMiningFunction();
- TargetField targetField = targetFields.get(0);
- switch(miningFunction){
- case CLASSIFICATION:
- FieldName targetFieldName = targetField.getName();
- Object targetFieldValue = result.get(targetFieldName);
- if (targetFieldValue instanceof ProbabilityDistribution)
- {
- ProbabilityDistribution prob = (ProbabilityDistribution) targetFieldValue;
- Set categories = prob.getCategories();
- for (String k : categories)
- {
- Double v = prob.getValue(k);
- tBuilder.addValues(v);
- }
- arguments.clear();
- return;
- }
- else
- throw new IllegalArgumentException("Expected probability distribution");
- case REGRESSION:
- default:
- throw new IllegalArgumentException("Expected a classification model, got " + miningFunction);
- }
- }
-
- public DefaultData evaluate(PMML model,DefaultData data) {
-
- if (data.getNamesCount() == 0)
- {
- throw new APIException(APIException.ApiExceptionType.WRAPPER_INVALID_MESSAGE, "Data for PMML models must contain names for the fields in the prediction message");
- }
-
- ModelEvaluatorFactory modelEvaluatorFactory = ModelEvaluatorFactory.newInstance();
- ValueFactoryFactory valueFactoryFactory = ReportingValueFactoryFactory.newInstance();
- modelEvaluatorFactory.setValueFactoryFactory(valueFactoryFactory);
- Evaluator evaluator = (Evaluator)modelEvaluatorFactory.newModelEvaluator(model);
- Map arguments = new LinkedHashMap<>();
- Map fieldMap = getInputFieldMap(evaluator);
-
- if (data.getDataOneofCase() == DataOneofCase.TENSOR) {
- Tensor.Builder tBuilder = Tensor.newBuilder();
- TensorBuilder tb = new TensorBuilder(tBuilder);
- List valuesList = data.getTensor().getValuesList();
- List shapeList = data.getTensor().getShapeList();
-
- if (shapeList.size() > 2) {
- return null;
- }
-
- int cols = 0;
- if (shapeList.size() == 1)
- cols = shapeList.get(0);
- else
- cols = shapeList.get(1);
-
- for (int i = 0; i < valuesList.size(); i++) {
- if (i > 0 && i % cols == 0) {
- evaluateForTensor(evaluator, arguments, tb);
- }
- String name = data.getNames(i % cols);
- InputField field = fieldMap.get(name);
- if (field != null)
- {
- Object rawValue = valuesList.get(i);
- FieldValue inputFieldValue = field.prepare(rawValue);
- arguments.put(field.getName(), inputFieldValue);
- }
- }
- evaluateForTensor(evaluator, arguments, tb);
- if (shapeList.size() == 1)
- tBuilder.addShape(tBuilder.getValuesCount());
- else
- {
- int outCols = tBuilder.getValuesCount() / shapeList.get(0);
- tBuilder.addShape(tBuilder.getValuesCount() / outCols).addShape(outCols);
- }
- DefaultData.Builder dataBuilder = DefaultData.newBuilder();
- dataBuilder.setTensor(tBuilder);
- return dataBuilder.build();
- }else if (data.getDataOneofCase() == DataOneofCase.NDARRAY) {
- ListValue list = data.getNdarray();
- int rows = list.getValuesCount();
- int cols = list.getValues(0).getListValue().getValuesCount();
-
- ListValue.Builder rowsBuilder = ListValue.newBuilder();
- for (int i = 0; i < rows; ++i) {
-
- ListValue.Builder row = ListValue.newBuilder();
- ListBuilder lb = new ListBuilder(row);
- for (int j = 0; j < cols; j++) {
- String name = data.getNames(j % cols);
- InputField field = fieldMap.get(name);
- if (field != null)
- {
- Double rawValue = list.getValues(i).getListValue().getValues(j).getNumberValue();
- FieldValue inputFieldValue = field.prepare(rawValue);
- arguments.put(field.getName(), inputFieldValue);
- }
- }
- evaluateForTensor(evaluator, arguments, lb);
- rowsBuilder.addValues(Value.newBuilder().setListValue(row.build()));
- }
- DefaultData.Builder dataBuilder = DefaultData.newBuilder();
- dataBuilder.setNdarray(rowsBuilder.build());
- return dataBuilder.build();
- }
- else
- throw new UnsupportedOperationException("Only Tensor or NDArray is supported");
- }
-
-}
diff --git a/wrappers/s2i/java/wrapper/src/main/proto/.keep b/wrappers/s2i/java/wrapper/src/main/proto/.keep
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/wrappers/s2i/java/wrapper/src/main/resources/application.properties b/wrappers/s2i/java/wrapper/src/main/resources/application.properties
deleted file mode 100644
index 650ed26ff9..0000000000
--- a/wrappers/s2i/java/wrapper/src/main/resources/application.properties
+++ /dev/null
@@ -1,8 +0,0 @@
-server.port = 9000
-grpc.port = 9001
-
-seldon.api.model.enabled=true
-seldon.api.route.enabled=false
-
-spring.jmx.enabled = false
-
diff --git a/wrappers/s2i/java/wrapper/src/test/java/io/seldon/wrapper/utils/TestPMMMLUtils.java b/wrappers/s2i/java/wrapper/src/test/java/io/seldon/wrapper/utils/TestPMMMLUtils.java
deleted file mode 100644
index 9c46cafa1b..0000000000
--- a/wrappers/s2i/java/wrapper/src/test/java/io/seldon/wrapper/utils/TestPMMMLUtils.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package io.seldon.wrapper.utils;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Random;
-
-import javax.xml.bind.JAXBException;
-
-import org.dmg.pmml.PMML;
-import org.jpmml.model.PMMLUtil;
-import org.junit.Test;
-import org.xml.sax.SAXException;
-
-import com.google.protobuf.ListValue;
-import com.google.protobuf.Value;
-
-import io.seldon.protos.PredictionProtos.DefaultData;
-import io.seldon.protos.PredictionProtos.Tensor;
-
-public class TestPMMMLUtils {
-
- @Test
- public void testTensorMnist() throws SAXException, JAXBException
- {
- PMML model = PMMLUtil.unmarshal(getClass().getClassLoader().getResourceAsStream(
- "mnist.pmml"));
-
- Tensor.Builder tBuilder = Tensor.newBuilder();
- Random r = new Random();
- List names = new ArrayList<>();
- int numRows = 2;
- for(int rows = 0;rows < numRows;rows++)
- for(int i=0;i<784;i++)
- {
- tBuilder.addValues(r.nextInt() % 255);
- names.add("_c"+i);
- }
- if (numRows == 1)
- tBuilder.addShape(784);
- else
- tBuilder.addShape(numRows).addShape(784);
- DefaultData.Builder dataBuilder = DefaultData.newBuilder();
- dataBuilder.setTensor(tBuilder).addAllNames(names);
-
- PMMLUtils util = new PMMLUtils();
- DefaultData resp = util.evaluate(model, dataBuilder.build());
- System.out.println(resp);
- }
- @Test
-
- public void testNDArrayMnist() throws SAXException, JAXBException
- {
- PMML model = PMMLUtil.unmarshal(getClass().getClassLoader().getResourceAsStream(
- "mnist.pmml"));
-
- ListValue.Builder rowsBuilder = ListValue.newBuilder();
- Random r = new Random();
- List names = new ArrayList<>();
- int numRows = 2;
- for(int rows = 0;rows < numRows;rows++)
- {
- ListValue.Builder row = ListValue.newBuilder();
- for(int i=0;i<784;i++)
- {
-
- row.addValues(Value.newBuilder().setNumberValue(r.nextInt() % 255));
- names.add("_c"+i);
- }
- rowsBuilder.addValues(Value.newBuilder().setListValue(row.build()));
- }
- DefaultData.Builder dataBuilder = DefaultData.newBuilder();
- dataBuilder.setNdarray(rowsBuilder).addAllNames(names);
-
- PMMLUtils util = new PMMLUtils();
- DefaultData resp = util.evaluate(model, dataBuilder.build());
- System.out.println(resp);
- }
-
-}
diff --git a/wrappers/s2i/java/wrapper/src/test/resources/mnist.pmml b/wrappers/s2i/java/wrapper/src/test/resources/mnist.pmml
deleted file mode 100644
index 352a2d287b..0000000000
--- a/wrappers/s2i/java/wrapper/src/test/resources/mnist.pmml
+++ /dev/null
@@ -1,8710 +0,0 @@
-
-
-
-
- 2018-05-19T15:57:10Z
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-