diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index fcc0bcf..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.gitignore b/.gitignore index f8a14e9..92d24c1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Jar to be download on academy +kafka-connect-dse-1.0.0.jar + # eclipse conf file .settings .classpath @@ -19,4 +22,5 @@ dist # misc .DS_Store .java-version - +nohup.out +webui.log diff --git a/.project b/.project deleted file mode 100644 index 2b9f570..0000000 --- a/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - kafka-dse-example - - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.m2e.core.maven2Nature - - diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs deleted file mode 100644 index f897a7f..0000000 --- a/.settings/org.eclipse.m2e.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -activeProfiles= -eclipse.preferences.version=1 -resolveWorkspaceProjects=true -version=1 diff --git a/README.md b/README.md index c23a78a..a2fd72b 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,117 @@ -# Reactive Driver and Kafka -### Projects overview : -- *[kafka-dse-core](kafka-dse-core)* contains domain beans and utility classes -- [kafka-dse-producer](kafka-dse-producer) is a standalone Spring Boot application that generates random Tick Data load from AlphaVantage and then random +# DataStax Apache Kafka™ Connector and Reactive Driver + +This project is a demo illustrating *DataStax Apache Kafka™ Connector* and the new reactive driver. Stocks valuations events are generated, pushed to Kafka and sent to DataStax Enterprise. + + + + +## Overview + +### Architecture + + + +### Maven modules : + +- [kafka-dse-core](kafka-dse-core) contains domain beans and utility classes +- [kafka-dse-producer](kafka-dse-producer) is a standalone Spring Boot application that generates random Tick Data load from AlphaVantage and send Json events to kafka topics `stocks_ticks` +- [kafka-dse-consumer](kafka-dse-consumer) is a standalone Spring Boot application that read from - [kafka-dse-webui](kafka-dse-webui) is the standalone web UI. - +## Install and run -## Start the project +1. Clone this repository on your laptop, driver2 branch -#### Use docker compose to start all components +```bash +git clone -b driver2 https://github.com/clun/kafka-dse.git +``` + +2. Compile source code and modules + +``` +cd kafka-dse + +mvn clean install +``` + +3. Start `Kafka` and `DataStax` components using the following command : ```bash docker-compose up -d ``` -A `docker ps` command will list you the following containers : -- **datastax/dse-server:6.7.0** is the DataStax enterprise server (listening on 9042) -- **datastax/dse-studio:6.7.0** is the WebUI available 9091 [DataStax Studio ](http://localhost:9091) -- **wurstmeister/zookeeper:3.4.6** is the Zookeeper ensuring consistency for Kafka (listening on 2181) -- **wurstmeister/kafka:1.0.0** the kafka server (listening on 9092) - -### 2. Start the `kafka-dse-producer` component. + You have now access to : + - [KafkaHQ](http://localhost:8080/docker-kafka-server/topic), a web ui to look into Kafka topics. + - [DataStax Studio](http://localhost:9091) : edit the connection to point to `dse` host name of datastax Enterprise -Open the folder kafka-dse-producer : +4. Start the `kafka-dse-producer` component. Open the folder `kafka-dse-producer` and execute the following ``` -# Start Web Application +cd kafka-dse-producer + mvn spring-boot:run ``` -This will create the `KeySpace` `demo_kafka` if needed, the tables to work with and fill the reference table `stocks_infos` with Data coming fron the CSV +This will create the keySpace `demo_kafka` if needed, and the expected tables `stocks_infos` and `stocks_ticks`. `stocks_infos` is loaded with a list of Stocks codes to be retrieved. -This is a Camel Application with no UI, still you should access the [SpringBoot Administration Console](ttp://localhost:8088/admin#/wallboard) +To see incoming event in the topic you can use the [KafkaHQ UI](http://localhost:8080/docker-kafka-server/topic) and use the embedded tool provided by Kafka: - +``` +docker exec -i -t kafka-dse_kafka_1 kafka-console-consumer --bootstrap-server localhost:9092 --from-beginning --topic stocks_ticks +``` -### 3. Start the `kafka-dse-webui` component. +You should see something like + +``` +{"symbol":"TGT","valueDate":"2019-03-06T09:42:51.242Z","value":72.29042675857293} +{"symbol":"MPC","valueDate":"2019-03-06T09:42:51.243Z","value":59.04528781377921} +{"symbol":"CVS","valueDate":"2019-03-06T09:42:51.243Z","value":53.99304938100401} +{"symbol":"ABC","valueDate":"2019-03-06T09:42:51.243Z","value":81.10990156983966} +{"symbol":"C","valueDate":"2019-03-06T09:42:51.243Z","value":62.885685814641356} +{"symbol":"VZ","valueDate":"2019-03-06T09:42:51.243Z","value":58.235384086812275} +{"symbol":"CVX","valueDate":"2019-03-06T09:42:51.243Z","value":122.11841963153303} +{"symbol":"JPM","valueDate":"2019-03-06T09:42:51.243Z","value":107.49725481587726} +{"symbol":"F","valueDate":"2019-03-06T09:42:51.243Z","value":8.861661896519792} +{"symbol":"BRK.A","valueDate":"2019-03-06T09:42:51.243Z","value":301526.64877576346} +{"symbol":"JNJ","valueDate":"2019-03-06T09:42:51.243Z","value":142.508769918913} +{"symbol":"ADM","valueDate":"2019-03-06T09:42:51.243Z","value":42.757409759352036} +{"symbol":"UNH","valueDate":"2019-03-06T09:42:51.243Z","value":243.075302209574} +{"symbol":"PFE","valueDate":"2019-03-06T09:42:51.243Z","value":42.58821518419893} +{"symbol":"PRU","valueDate":"2019-03-06T09:42:51.243Z","value":101.21939105941746} +{"symbol":"T","valueDate":"2019-03-06T09:42:51.243Z","value":33.00476819346831} +{"symbol":"WFC","valueDate":"2019-03-06T09:42:51.243Z","value":49.8785431886429} +{"symbol":"IBM","valueDate":"2019-03-06T09:42:51.243Z","value":135.1453199656281} +{"symbol":"VLO","valueDate":"2019-03-06T09:42:51.244Z","value":80.76656130882697} +``` + +5. Start the `kafka-dse-webui` component. Open the folder kafka-dse-webui : ``` -# Start Web Application +cd ../kafka-dse-webui + mvn spring-boot:run ``` -[Access the web UI](http://localhost:8082) +You will have access to : +- [The web UI](http://localhost:8082) user interface to see charts + + +You can start `kafka-dse-consumer` component using again `spring-boot:run` and see events coming in Datastax Enterprise and the UI but this one is only for test. But you want to use the sink. + +6. Download the *DataStax Apache Kafka™ Connector* from [academy.datastax.com] and put the library in `kafka-dse-sink` folder here. Then execute the following : + +``` +docker-compose -f docker-compose-connect.yml up +``` + +Kafka-Connect is starting. You can in the log that the class `com.datastax.kafkaconnector.DseSinkConnector` is detected in the classpath. When the service is up (http interface is OK) we register the sink using a POST query. + +You can look to `kafka-connect` [sinks here](http://localhost:18083/connectors/) ou our [config here](http://localhost:18083/connectors/dse_stock_ticks) + +More informations on the [DataStax Documentation](https://docs.datastax.com/en/kafka/doc/index.html) diff --git a/demo.MD b/demo.MD new file mode 100644 index 0000000..2b26837 --- /dev/null +++ b/demo.MD @@ -0,0 +1,51 @@ + + DEMO + + curl -s \ + -X "POST" "http://localhost:18083/connectors/" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "dse_tx_flat", + "config": { + "connector.class": "com.datastax.kafkaconnector.DseSinkConnector", + "tasks.max": "1", + "topics": "stocks_ticks", + "contactPoints": "dse", + "loadBalancing.localDc": "DC1", + "port": 9042, + "maxConcurrentRequests": 500, + "maxNumberOfRecordsInBatch": 32, + "queryExecutionTimeout": 30, + "connectionPoolLocalSize": 4, + "jmx": false, + "compression": "None", + "auth.provider": "None",s + "auth.username": "", + "auth.password": "", + "auth.gssapi.keyTab": "", + "auth.gssapi.principal": "", + "auth.gssapi.service": "dse", + "ssl.provider": "None", + "ssl.hostnameValidation": true, + "ssl.keystore.password": "", + "ssl.keystore.path": "", + "ssl.openssl.keyCertChain": "", + "ssl.openssl.privateKey": "", + "ssl.truststore.password": "", + "ssl.truststore.path": "", + "ssl.cipherSuites": "", + "topic.stocks_ticks.demo_kafka.stocks_ticks.mapping": "symbol=value.symbol, value=value.value, value_date=value.valueDate", + "topic.stocks_ticks.demo_kafka.stocks_ticks.consistencyLevel": "LOCAL_ONE", + "topic.stocks_ticks.demo_kafka.stocks_ticks.ttl": -1, + "topic.stocks_ticks.demo_kafka.stocks_ticks.nullToUnset": "true", + "topic.stocks_ticks.demo_kafka.stocks_ticks.deletesEnabled": "true", + "topic.stocks_ticks.codec.locale": "en_US", + "topic.stocks_ticks.codec.timeZone": "UTC", + "topic.stocks_ticks.codec.timestamp": "CQL_TIMESTAMP", + "topic.stocks_ticks.codec.date": "ISO_LOCAL_DATE", + "topic.stocks_ticks.codec.time": "ISO_LOCAL_TIME", + "topic.stocks_ticks.codec.unit": "MILLISECONDS" + } + } + +- \ No newline at end of file diff --git a/docker-compose-connect.yml b/docker-compose-connect.yml new file mode 100644 index 0000000..618a375 --- /dev/null +++ b/docker-compose-connect.yml @@ -0,0 +1,92 @@ +version: '3' +services: + # As keyspaces and tables should already exist we need to start the applications first. + kafka-connect: + image: confluentinc/cp-kafka-connect:5.1.0 + #depends_on: + # - zookeeper + # - kafka + ports: + - 18083:18083 + environment: + CONNECT_BOOTSTRAP_SERVERS: "kafka:29092" + CONNECT_REST_PORT: 18083 + CONNECT_GROUP_ID: compose-connect-group + CONNECT_CONFIG_STORAGE_TOPIC: docker-connect-configs + CONNECT_OFFSET_STORAGE_TOPIC: docker-connect-offsets + CONNECT_STATUS_STORAGE_TOPIC: docker-connect-status + CONNECT_KEY_CONVERTER: "org.apache.kafka.connect.storage.StringConverter" + CONNECT_VALUE_CONVERTER: "org.apache.kafka.connect.json.JsonConverter" + CONNECT_VALUE_CONVERTER_SCHEMAS_ENABLE: "false" + CONNECT_INTERNAL_KEY_CONVERTER: "org.apache.kafka.connect.storage.StringConverter" + CONNECT_INTERNAL_VALUE_CONVERTER: "org.apache.kafka.connect.json.JsonConverter" + CONNECT_INTERNAL_VALUE_CONVERTER_SCHEMAS_ENABLE: "false" + CONNECT_REST_ADVERTISED_HOST_NAME: "kafka-connect" + CONNECT_LOG4J_ROOT_LOGLEVEL: "INFO" + CONNECT_LOG4J_LOGGERS: "org.apache.kafka.connect.runtime.rest=WARN,org.reflections=ERROR" + CONNECT_CONFIG_STORAGE_REPLICATION_FACTOR: "1" + CONNECT_OFFSET_STORAGE_REPLICATION_FACTOR: "1" + CONNECT_STATUS_STORAGE_REPLICATION_FACTOR: "1" + CONNECT_PLUGIN_PATH: '/usr/share/java,/etc/kafka-connect/jars,/connectors,/tmp/dse-kafka-connect/jars' + volumes: + - $PWD/kafka-dse-sink:/tmp/dse-kafka-connect/jars + command: + - bash + - -c + - | + /etc/confluent/docker/run & + echo "Waiting for Kafka Connect to start listening on $$CONNECT_REST_ADVERTISED_HOST_NAME ⏳" + while [ $$(curl -s -o /dev/null -w %{http_code} http://$$CONNECT_REST_ADVERTISED_HOST_NAME:$$CONNECT_REST_PORT/connectors) -eq 000 ] ; do + echo -e $$(date) " Kafka Connect listener HTTP state: " $$(curl -s -o /dev/null -w %{http_code} http://$$CONNECT_REST_ADVERTISED_HOST_NAME:$$CONNECT_REST_PORT/connectors) " (waiting for 200)" + sleep 5 + done + nc -vz $$CONNECT_REST_ADVERTISED_HOST_NAME $$CONNECT_REST_PORT + echo -e "\n--\n+> Creating Kafka Connect DataStax Sink" + curl -s \ + -X "POST" "http://localhost:18083/connectors/" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "dse_stock_ticks", + "config": { + "connector.class": "com.datastax.kafkaconnector.DseSinkConnector", + "tasks.max": "1", + "topics": "stocks_ticks", + "contactPoints": "dse", + "loadBalancing.localDc": "DC1", + "port": 9042, + "maxConcurrentRequests": 500, + "maxNumberOfRecordsInBatch": 32, + "queryExecutionTimeout": 30, + "connectionPoolLocalSize": 4, + "jmx": false, + "compression": "None", + "auth.provider": "None", + "auth.username": "", + "auth.password": "", + "auth.gssapi.keyTab": "", + "auth.gssapi.principal": "", + "auth.gssapi.service": "dse", + "ssl.provider": "None", + "ssl.hostnameValidation": true, + "ssl.keystore.password": "", + "ssl.keystore.path": "", + "ssl.openssl.keyCertChain": "", + "ssl.openssl.privateKey": "", + "ssl.truststore.password": "", + "ssl.truststore.path": "", + "ssl.cipherSuites": "", + "topic.stocks_ticks.demo_kafka.stocks_ticks.mapping": "symbol=value.symbol, value=value.value, value_date=value.valueDate", + "topic.stocks_ticks.demo_kafka.stocks_ticks.consistencyLevel": "LOCAL_ONE", + "topic.stocks_ticks.demo_kafka.stocks_ticks.ttl": -1, + "topic.stocks_ticks.demo_kafka.stocks_ticks.nullToUnset": "true", + "topic.stocks_ticks.demo_kafka.stocks_ticks.deletesEnabled": "true", + "topic.stocks_ticks.codec.locale": "en_US", + "topic.stocks_ticks.codec.timeZone": "UTC", + "topic.stocks_ticks.codec.timestamp": "CQL_TIMESTAMP", + "topic.stocks_ticks.codec.date": "ISO_LOCAL_DATE", + "topic.stocks_ticks.codec.time": "ISO_LOCAL_TIME", + "topic.stocks_ticks.codec.unit": "MILLISECONDS" + } + }' + sleep infinity + \ No newline at end of file diff --git a/docker-compose-kafka.yml b/docker-compose-kafka.yml deleted file mode 100644 index 74463a0..0000000 --- a/docker-compose-kafka.yml +++ /dev/null @@ -1,45 +0,0 @@ -version: '3' - -services: - - # Ensure consistency in Kafka World - zookeeper: - image: wurstmeister/zookeeper:3.4.6 - environment: - ZOOKEEPER_CLIENT_PORT: 2181 - ports: - - 2181:2181 - - # Start the Kafka Engine for Killrvideo - kafka: - image: wurstmeister/kafka:2.11-0.11.0.3 - depends_on: - - zookeeper - environment: - KAFKA_ADVERTISED_HOST_NAME: kafka - KAFKA_ADVERTISED_PORT: 9092 - KAFKA_BROKER_ID: 1 - KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 - KAFKA_CREATE_TOPICS: "stocks-ticks:1:1" - ports: - - "9092:9092" - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - kafkahq: - image: tchiotludo/kafkahq - environment: - KAFKAHQ_CONFIGURATION: | - { - kafka { - connections { - docker-kafka-server { - bootstrap.servers: "kafka:9092" - } - } - } - } - ports: - - "8080:8080" - depends_on: - - kafka \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index 2d67bc0..b5d6046 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,56 +1,110 @@ version: '3' - -services: - - # -------------------------------- - # DataStax Enterprise - # -------------------------------- - dse: +services: + + # ------------------------------------------------ + # Apache Zookeeper to ensure Kafka consistency + # ------------------------------------------------ + zookeeper: + image: confluentinc/cp-zookeeper:5.1.0 + environment: + ZOOKEEPER_CLIENT_PORT: 2181 + ZOOKEEPER_TICK_TIME: 2000 + + # ------------------------------------------------ + # Apache KAFKA Message broker + # ------------------------------------------------ + kafka: + image: confluentinc/cp-kafka:5.1.0 + depends_on: + - zookeeper + ports: + - 9092:9092 + environment: + KAFKA_BROKER_ID: 1 + KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT + KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + KAFKA_CREATE_TOPICS: "stocks_ticks:1:1" + extra_hosts: + - "moby:127.0.0.1" + + # ------------------------------------------------ + # DataStax Enterprise + # ------------------------------------------------ + dse: image: datastax/dse-server:6.7.0 command: [ -s -g -k ] ports: - - "9042:9042" - - "8983:8983" - - "8182:8182" + - "9042:9042" + - "8983:8983" + - "8182:8182" + #depends_on: + # - opscenter environment: - DS_LICENSE: accept + - DS_LICENSE=accept + - DC=DC1 + - JVM_EXTRA_OPTS=-Xmx2g -Xms2g + - NUM_TOKENS=32 cap_add: - - IPC_LOCK + - IPC_LOCK ulimits: memlock: -1 - - # One instance of DataStax Studio - studio: - image: datastax/dse-studio:6.7.0 - ports: - - "9091:9091" - depends_on: - - dse - environment: - DS_LICENSE: accept - volumes: - - "/tmp/docker/studio-notebooks:/var/lib/datastax-studio" -# Ensure consistency in Kafka World - zookeeper: - image: wurstmeister/zookeeper:3.4.6 - environment: - ZOOKEEPER_CLIENT_PORT: 2181 - ports: - - 2181:2181 - - # Start the Kafka Engine for Killrvideo - kafka: - image: wurstmeister/kafka:2.11-0.11.0.3 + # ------------------------------------------------ + # One instance of DataStax Studio + # ------------------------------------------------ + studio: + image: datastax/dse-studio:6.7.0 + ports: + - "9091:9091" + depends_on: + - dse + environment: + DS_LICENSE: accept + volumes: + - "/tmp/docker/studio-notebooks:/var/lib/datastax-studio" + + # ------------------------------------------------ + # OpsCenter 6.7.0 + # ------------------------------------------------ + #opscenter: + # image: "datastax/dse-opscenter:6.7.0" + # ports: + # - 8888:8888 + # environment: + # - DS_LICENSE=accept + + # Message broker + kafka: + image: confluentinc/cp-kafka:5.1.0 depends_on: - - zookeeper + - zookeeper + ports: + - 9092:9092 environment: - KAFKA_ADVERTISED_HOST_NAME: kafka - KAFKA_ADVERTISED_PORT: 9092 KAFKA_BROKER_ID: 1 KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 - KAFKA_CREATE_TOPICS: "stocks-ticks:1:1" + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT + KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + KAFKA_CREATE_TOPICS: "stocks_ticks:1:1" + extra_hosts: + - "moby:127.0.0.1" + + kafkahq: + image: tchiotludo/kafkahq + environment: + KAFKAHQ_CONFIGURATION: | + kafkahq: + connections: + docker-kafka-server: + properties: + bootstrap.servers: "kafka:29092" ports: - - "9092:9092" - volumes: - - /var/run/docker.sock:/var/run/docker.sock \ No newline at end of file + - "8080:8080" + depends_on: + - kafka + \ No newline at end of file diff --git a/kafka-dse-conf/connect-standalone.properties b/kafka-dse-conf/connect-standalone.properties deleted file mode 100644 index 68fd532..0000000 --- a/kafka-dse-conf/connect-standalone.properties +++ /dev/null @@ -1,41 +0,0 @@ -# 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. - -# These are defaults. This file just demonstrates how to override some settings. -bootstrap.servers=localhost:9092 - -# The converters specify the format of data in Kafka and how to translate it into Connect data. Every Connect user will -# need to configure these based on the format they want their data in when loaded from or stored into Kafka -key.converter=org.apache.kafka.connect.json.JsonConverter -value.converter=org.apache.kafka.connect.json.JsonConverter -# Converter-specific settings can be passed in by prefixing the Converter's setting with the converter we want to apply -# it to -key.converter.schemas.enable=true -value.converter.schemas.enable=true - -offset.storage.file.filename=/tmp/connect.offsets -# Flush much faster than normal, which is useful for testing/debugging -offset.flush.interval.ms=10000 - -# Set to a list of filesystem paths separated by commas (,) to enable class loading isolation for plugins -# (connectors, converters, transformations). The list should consist of top level directories that include -# any combination of: -# a) directories immediately containing jars with plugins and their dependencies -# b) uber-jars with plugins and their dependencies -# c) directories immediately containing the package directory structure of classes of plugins and their dependencies -# Note: symlinks will be followed to discover dependencies or plugins. -# Examples: -# plugin.path=/usr/local/share/java,/usr/local/share/kafka/plugins,/opt/connectors, -plugin.path=/Users/cedricklunven/apps/kafka/kafka-connect-dse-1.0.0-alpha1/kafka-connect-dse-1.0.0-alpha1.jar diff --git a/kafka-dse-consumer/.factorypath b/kafka-dse-consumer/.factorypath new file mode 100644 index 0000000..66fa196 --- /dev/null +++ b/kafka-dse-consumer/.factorypath @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kafka-dse-consumer/bin/pom.xml b/kafka-dse-consumer/bin/pom.xml new file mode 100644 index 0000000..79d0f14 --- /dev/null +++ b/kafka-dse-consumer/bin/pom.xml @@ -0,0 +1,151 @@ + + + 4.0.0 + kafka-dse-consumer + + kafka-dse-consumer + Sample Topic Consumer and save to DSE + + com.datastax + kafka-dse-example + 6.7-SNAPSHOT + + + + + com.github.signaflo + timeseries + 0.4 + + + + com.datastax + kafka-dse-core + ${project.version} + + + + com.datastax.dse + dse-java-driver-core + + + com.datastax.oss + java-driver-core + + + com.datastax.oss + java-driver-query-builder + + + com.fasterxml.jackson.dataformat + jackson-dataformat-csv + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-security + + + org.jolokia + jolokia-core + + + + org.apache.camel + camel-spring-boot-starter + + + org.apache.camel + camel-stream-starter + + + org.apache.camel + camel-kafka + + + + org.apache.kafka + connect-api + + + org.apache.kafka + connect-json + + + io.confluent + kafka-avro-serializer + + + io.netty + netty-all + + + org.patriques + alphavantage4j + 1.2 + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.springframework.boot + spring-boot-starter-test + test + + + junit + junit + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + com.datastax.demo.ProducerApplication + + + + + repackage + + + + + + + + + alphavantage + alphavantage4 + https://dl.bintray.com/patriques82/maven + + + diff --git a/kafka-dse-consumer/bin/src/main/java/com/datastax/kafkadse/consumer/ConsumerApplication.class b/kafka-dse-consumer/bin/src/main/java/com/datastax/kafkadse/consumer/ConsumerApplication.class new file mode 100644 index 0000000..5c83ded Binary files /dev/null and b/kafka-dse-consumer/bin/src/main/java/com/datastax/kafkadse/consumer/ConsumerApplication.class differ diff --git a/kafka-dse-consumer/bin/src/main/java/com/datastax/kafkadse/consumer/ConsumerRoutes.class b/kafka-dse-consumer/bin/src/main/java/com/datastax/kafkadse/consumer/ConsumerRoutes.class new file mode 100644 index 0000000..4f60d4e Binary files /dev/null and b/kafka-dse-consumer/bin/src/main/java/com/datastax/kafkadse/consumer/ConsumerRoutes.class differ diff --git a/kafka-dse-consumer/bin/src/main/java/com/datastax/kafkadse/consumer/conf/ConsumerConfiguration.class b/kafka-dse-consumer/bin/src/main/java/com/datastax/kafkadse/consumer/conf/ConsumerConfiguration.class new file mode 100644 index 0000000..6aca93d Binary files /dev/null and b/kafka-dse-consumer/bin/src/main/java/com/datastax/kafkadse/consumer/conf/ConsumerConfiguration.class differ diff --git a/kafka-dse-consumer/bin/src/main/java/com/datastax/kafkadse/consumer/route/StockTicksConsumer.class b/kafka-dse-consumer/bin/src/main/java/com/datastax/kafkadse/consumer/route/StockTicksConsumer.class new file mode 100644 index 0000000..d55317d Binary files /dev/null and b/kafka-dse-consumer/bin/src/main/java/com/datastax/kafkadse/consumer/route/StockTicksConsumer.class differ diff --git a/kafka-dse-consumer/bin/src/main/resources/application.yml b/kafka-dse-consumer/bin/src/main/resources/application.yml new file mode 100644 index 0000000..7f50df4 --- /dev/null +++ b/kafka-dse-consumer/bin/src/main/resources/application.yml @@ -0,0 +1,52 @@ +# --------------------------------------------------------------------------------- +# Spring Boot Fmk properties +# --------------------------------------------------------------------------------- +spring: + application: + name: StockData Injector for Kafka +server: + port: 8087 + +# --------------------------------------------------------------------------------- +# Apache Camel (integration fmk) +# --------------------------------------------------------------------------------- +camel: + springboot: + name: Kafka Consumer + cloud: + enabled: false + +# --------------------------------------------------------------------------------- +# Apache KAFKA client infos +# --------------------------------------------------------------------------------- +kafka: + server: 127.0.0.1:9092 + period: 2000 + group: tick-group + topics: + ticks: stocks_ticks + +# --------------------------------------------------------------------------------- +# DataStax Enterprise client infos +# --------------------------------------------------------------------------------- +dse: + contactPoints: 127.0.0.1 + port: 9042 + localdc: DC1 + keyspace: demo_kafka + username: + password: + +# --------------------------------------------------------------------------------- +# Query QUOTES from https://www.alphavantage.co/documentation/ +# --------------------------------------------------------------------------------- +alphavantage: + apiKey: 2HWDTH7BA7FRBP76 + timeout: 3000 + waitTime: 100 + pollingPeriod: + ticks: 5000 + 1min: 60000 + 1hour: 3600000 + + \ No newline at end of file diff --git a/kafka-dse-consumer/bin/src/main/resources/banner.txt b/kafka-dse-consumer/bin/src/main/resources/banner.txt new file mode 100644 index 0000000..3c82843 --- /dev/null +++ b/kafka-dse-consumer/bin/src/main/resources/banner.txt @@ -0,0 +1,12 @@ +_________ +\_ ___ \ ____ ____ ________ __ _____ ___________ +/ \ \/ / _ \ / \ / ___/ | \/ \_/ __ \_ __ \ +\ \___( <_> ) | \\___ \| | / Y Y \ ___/| | \/ + \______ /\____/|___| /____ >____/|__|_| /\___ >__| + \/ \/ \/ \/ \/ + + Provided to you by DataStax Advocates team \_0_/ + + 1. Read Topic stocks_ticks + 2. Parse Json messages and save to DataStax Enterprise + diff --git a/kafka-dse-consumer/bin/src/main/resources/logback.xml b/kafka-dse-consumer/bin/src/main/resources/logback.xml new file mode 100644 index 0000000..ad9edef --- /dev/null +++ b/kafka-dse-consumer/bin/src/main/resources/logback.xml @@ -0,0 +1,20 @@ + + + + + %d{HH:mm:ss.SSS} %magenta(%-5level) %cyan(%-45logger) : %msg%n + + + + + + + + + + + + + + + diff --git a/kafka-dse-consumer/bin/src/test/resources/config-test.properties b/kafka-dse-consumer/bin/src/test/resources/config-test.properties new file mode 100644 index 0000000..ac4cb48 --- /dev/null +++ b/kafka-dse-consumer/bin/src/test/resources/config-test.properties @@ -0,0 +1,18 @@ +alphavantage.apiKey=2HWDTH7BA7FRBP76 +alphavantage.timeout=30000 + +dse.contactPoints=127.0.0.1 +dse.port=9042 +dse.localdc=SearchGraphAnalytics +dse.username= +dse.password= +dse.keyspace=demo_kafka + +csvFile=src/main/resources/exchange-data.csv + +# Connectivity to KAFKA +kafka.server=127.0.0.1:9092 +kafka.ack=-1 +kafka.group=tick-group +kafka.topics.ticks=testTopic + diff --git a/kafka-dse-consumer/pom.xml b/kafka-dse-consumer/pom.xml new file mode 100644 index 0000000..79d0f14 --- /dev/null +++ b/kafka-dse-consumer/pom.xml @@ -0,0 +1,151 @@ + + + 4.0.0 + kafka-dse-consumer + + kafka-dse-consumer + Sample Topic Consumer and save to DSE + + com.datastax + kafka-dse-example + 6.7-SNAPSHOT + + + + + com.github.signaflo + timeseries + 0.4 + + + + com.datastax + kafka-dse-core + ${project.version} + + + + com.datastax.dse + dse-java-driver-core + + + com.datastax.oss + java-driver-core + + + com.datastax.oss + java-driver-query-builder + + + com.fasterxml.jackson.dataformat + jackson-dataformat-csv + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-security + + + org.jolokia + jolokia-core + + + + org.apache.camel + camel-spring-boot-starter + + + org.apache.camel + camel-stream-starter + + + org.apache.camel + camel-kafka + + + + org.apache.kafka + connect-api + + + org.apache.kafka + connect-json + + + io.confluent + kafka-avro-serializer + + + io.netty + netty-all + + + org.patriques + alphavantage4j + 1.2 + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.springframework.boot + spring-boot-starter-test + test + + + junit + junit + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + com.datastax.demo.ProducerApplication + + + + + repackage + + + + + + + + + alphavantage + alphavantage4 + https://dl.bintray.com/patriques82/maven + + + diff --git a/kafka-dse-consumer/src/main/java/com/datastax/kafkadse/consumer/ConsumerApplication.java b/kafka-dse-consumer/src/main/java/com/datastax/kafkadse/consumer/ConsumerApplication.java new file mode 100755 index 0000000..1dc7c56 --- /dev/null +++ b/kafka-dse-consumer/src/main/java/com/datastax/kafkadse/consumer/ConsumerApplication.java @@ -0,0 +1,17 @@ +package com.datastax.kafkadse.consumer; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +/** Main class for CannysEngine. */ +@SpringBootApplication +@EnableAutoConfiguration +@ComponentScan(basePackages = {"com.datastax.kafkadse.consumer", "com.datastax.kafkadse.core"}) +public class ConsumerApplication { + + public static void main(String[] args) { + SpringApplication.run(ConsumerApplication.class, args); + } +} diff --git a/kafka-dse-consumer/src/main/java/com/datastax/kafkadse/consumer/ConsumerRoutes.java b/kafka-dse-consumer/src/main/java/com/datastax/kafkadse/consumer/ConsumerRoutes.java new file mode 100644 index 0000000..3fd98f9 --- /dev/null +++ b/kafka-dse-consumer/src/main/java/com/datastax/kafkadse/consumer/ConsumerRoutes.java @@ -0,0 +1,21 @@ +package com.datastax.kafkadse.consumer; + +import com.datastax.kafkadse.consumer.route.StockTicksConsumer; +import org.apache.camel.builder.RouteBuilder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class ConsumerRoutes extends RouteBuilder { + + @Autowired private StockTicksConsumer stockTickConsumerAvro; + + /** {@inheritDoc} */ + @Override + public void configure() { + from("timer:ticksConsumer?fixedRate=true&period={{alphavantage.pollingPeriod.ticks}}") + .routeId("ticks_Kafka2Dse") + .process(stockTickConsumerAvro) + .end(); + } +} diff --git a/kafka-dse-consumer/src/main/java/com/datastax/kafkadse/consumer/conf/ConsumerConfiguration.java b/kafka-dse-consumer/src/main/java/com/datastax/kafkadse/consumer/conf/ConsumerConfiguration.java new file mode 100644 index 0000000..7b38074 --- /dev/null +++ b/kafka-dse-consumer/src/main/java/com/datastax/kafkadse/consumer/conf/ConsumerConfiguration.java @@ -0,0 +1,46 @@ +package com.datastax.kafkadse.consumer.conf; + +import static org.apache.kafka.clients.consumer.ConsumerConfig.GROUP_ID_CONFIG; +import static org.apache.kafka.clients.consumer.ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG; +import static org.apache.kafka.clients.consumer.ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG; +import static org.apache.kafka.clients.producer.ProducerConfig.BOOTSTRAP_SERVERS_CONFIG; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import java.util.Properties; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.apache.kafka.connect.json.JsonDeserializer; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ConsumerConfiguration { + + @Value("${kafka.server}") + private String kafkaServer; + + @Value("${kafka.group}") + private String consumerGroup; + + @Bean("producer.mapper") + public ObjectMapper jacksonMapper() { + ObjectMapper jacksonMapper = new ObjectMapper(); + jacksonMapper.registerModule(new JavaTimeModule()); + jacksonMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); + return jacksonMapper; + } + + @Bean("consumer.json") + public KafkaConsumer jsonConsumer() { + Properties props = new Properties(); + props.put(BOOTSTRAP_SERVERS_CONFIG, kafkaServer); + props.put(GROUP_ID_CONFIG, consumerGroup); + props.put(KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); + props.put(VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class.getName()); + return new KafkaConsumer<>(props); + } +} diff --git a/kafka-dse-producer/src/main/java/com/datastax/demo/route/StockTicksConsumer.java b/kafka-dse-consumer/src/main/java/com/datastax/kafkadse/consumer/route/StockTicksConsumer.java similarity index 76% rename from kafka-dse-producer/src/main/java/com/datastax/demo/route/StockTicksConsumer.java rename to kafka-dse-consumer/src/main/java/com/datastax/kafkadse/consumer/route/StockTicksConsumer.java index e2d86eb..12474cd 100644 --- a/kafka-dse-producer/src/main/java/com/datastax/demo/route/StockTicksConsumer.java +++ b/kafka-dse-consumer/src/main/java/com/datastax/kafkadse/consumer/route/StockTicksConsumer.java @@ -1,7 +1,7 @@ -package com.datastax.demo.route; +package com.datastax.kafkadse.consumer.route; -import com.datastax.demo.dao.DseDao; -import com.datastax.demo.domain.StockTick; +import com.datastax.kafkadse.core.dao.DseDao; +import com.datastax.kafkadse.core.domain.StockTick; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -27,9 +27,6 @@ public class StockTicksConsumer implements Processor { /** Internal logger. */ private static final Logger LOGGER = LoggerFactory.getLogger(StockTicksConsumer.class); - /** Json Jackson parser. */ - private static final ObjectMapper JACKSON_MAPPER = new ObjectMapper(); - @Autowired @Qualifier("consumer.json") private KafkaConsumer kafkaConsumer; @@ -37,10 +34,20 @@ public class StockTicksConsumer implements Processor { @Value("${kafka.topics.ticks}") private String topicTicks; - @Autowired private DseDao dseDao; + // @Autowired + // private DseDao dseDao; + + /** Json Jackson parser. */ + @Autowired + @Qualifier("producer.mapper") + private ObjectMapper jacksonMapper; + + @Autowired protected DseDao dseDao; @PostConstruct public void init() { + dseDao.createOrUpdateSchema(); + LOGGER.info("Start consuming events from topic '{}' ..", topicTicks); kafkaConsumer.subscribe(Collections.singletonList(topicTicks)); } @@ -51,21 +58,22 @@ public void process(Exchange exchange) throws Exception { .map(this::mapAsStockData) .filter(Optional::isPresent) .map(Optional::get) - .forEach(dseDao::saveTicker); + .forEach(dseDao::saveTickerAsync); } /** * Skip invalid messages. * - * @param msg - * @return + * @param msg the received message. + * @return the stock tick object, or empty if the object could not be created. */ public Optional mapAsStockData(ConsumerRecord msg) { Optional result = Optional.empty(); try { - StockTick tick = JACKSON_MAPPER.treeToValue(msg.value(), StockTick.class); + StockTick tick = jacksonMapper.treeToValue(msg.value(), StockTick.class); result = Optional.of(tick); } catch (JsonProcessingException e) { + e.printStackTrace(); LOGGER.warn("Message " + msg.value().asText() + " cannot be processed"); } return result; diff --git a/kafka-dse-consumer/src/main/resources/application.yml b/kafka-dse-consumer/src/main/resources/application.yml new file mode 100644 index 0000000..7f50df4 --- /dev/null +++ b/kafka-dse-consumer/src/main/resources/application.yml @@ -0,0 +1,52 @@ +# --------------------------------------------------------------------------------- +# Spring Boot Fmk properties +# --------------------------------------------------------------------------------- +spring: + application: + name: StockData Injector for Kafka +server: + port: 8087 + +# --------------------------------------------------------------------------------- +# Apache Camel (integration fmk) +# --------------------------------------------------------------------------------- +camel: + springboot: + name: Kafka Consumer + cloud: + enabled: false + +# --------------------------------------------------------------------------------- +# Apache KAFKA client infos +# --------------------------------------------------------------------------------- +kafka: + server: 127.0.0.1:9092 + period: 2000 + group: tick-group + topics: + ticks: stocks_ticks + +# --------------------------------------------------------------------------------- +# DataStax Enterprise client infos +# --------------------------------------------------------------------------------- +dse: + contactPoints: 127.0.0.1 + port: 9042 + localdc: DC1 + keyspace: demo_kafka + username: + password: + +# --------------------------------------------------------------------------------- +# Query QUOTES from https://www.alphavantage.co/documentation/ +# --------------------------------------------------------------------------------- +alphavantage: + apiKey: 2HWDTH7BA7FRBP76 + timeout: 3000 + waitTime: 100 + pollingPeriod: + ticks: 5000 + 1min: 60000 + 1hour: 3600000 + + \ No newline at end of file diff --git a/kafka-dse-consumer/src/main/resources/banner.txt b/kafka-dse-consumer/src/main/resources/banner.txt new file mode 100644 index 0000000..3c82843 --- /dev/null +++ b/kafka-dse-consumer/src/main/resources/banner.txt @@ -0,0 +1,12 @@ +_________ +\_ ___ \ ____ ____ ________ __ _____ ___________ +/ \ \/ / _ \ / \ / ___/ | \/ \_/ __ \_ __ \ +\ \___( <_> ) | \\___ \| | / Y Y \ ___/| | \/ + \______ /\____/|___| /____ >____/|__|_| /\___ >__| + \/ \/ \/ \/ \/ + + Provided to you by DataStax Advocates team \_0_/ + + 1. Read Topic stocks_ticks + 2. Parse Json messages and save to DataStax Enterprise + diff --git a/kafka-dse-consumer/src/main/resources/logback.xml b/kafka-dse-consumer/src/main/resources/logback.xml new file mode 100644 index 0000000..ad9edef --- /dev/null +++ b/kafka-dse-consumer/src/main/resources/logback.xml @@ -0,0 +1,20 @@ + + + + + %d{HH:mm:ss.SSS} %magenta(%-5level) %cyan(%-45logger) : %msg%n + + + + + + + + + + + + + + + diff --git a/kafka-dse-consumer/src/test/resources/config-test.properties b/kafka-dse-consumer/src/test/resources/config-test.properties new file mode 100644 index 0000000..ac4cb48 --- /dev/null +++ b/kafka-dse-consumer/src/test/resources/config-test.properties @@ -0,0 +1,18 @@ +alphavantage.apiKey=2HWDTH7BA7FRBP76 +alphavantage.timeout=30000 + +dse.contactPoints=127.0.0.1 +dse.port=9042 +dse.localdc=SearchGraphAnalytics +dse.username= +dse.password= +dse.keyspace=demo_kafka + +csvFile=src/main/resources/exchange-data.csv + +# Connectivity to KAFKA +kafka.server=127.0.0.1:9092 +kafka.ack=-1 +kafka.group=tick-group +kafka.topics.ticks=testTopic + diff --git a/kafka-dse-core/.DS_Store b/kafka-dse-core/.DS_Store deleted file mode 100644 index e9057af..0000000 Binary files a/kafka-dse-core/.DS_Store and /dev/null differ diff --git a/kafka-dse-core/.classpath b/kafka-dse-core/.classpath deleted file mode 100644 index 39abf1c..0000000 --- a/kafka-dse-core/.classpath +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/kafka-dse-core/.factorypath b/kafka-dse-core/.factorypath index 1ef31bb..0f47de8 100644 --- a/kafka-dse-core/.factorypath +++ b/kafka-dse-core/.factorypath @@ -1,29 +1,12 @@ - - - - - - - - - + + - - - - - - - - - - - - - - + + + + @@ -35,23 +18,36 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kafka-dse-core/.project b/kafka-dse-core/.project deleted file mode 100644 index 58b7ee4..0000000 --- a/kafka-dse-core/.project +++ /dev/null @@ -1,23 +0,0 @@ - - - kafka-dse-core - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.m2e.core.maven2Nature - - diff --git a/kafka-dse-core/.settings/org.eclipse.jdt.apt.core.prefs b/kafka-dse-core/.settings/org.eclipse.jdt.apt.core.prefs deleted file mode 100644 index dfa4f3a..0000000 --- a/kafka-dse-core/.settings/org.eclipse.jdt.apt.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.apt.aptEnabled=true -org.eclipse.jdt.apt.genSrcDir=target/generated-sources/annotations -org.eclipse.jdt.apt.genTestSrcDir=target/generated-test-sources/test-annotations diff --git a/kafka-dse-core/.settings/org.eclipse.jdt.core.prefs b/kafka-dse-core/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 21227d9..0000000 --- a/kafka-dse-core/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,7 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 -org.eclipse.jdt.core.compiler.compliance=1.8 -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.processAnnotations=enabled -org.eclipse.jdt.core.compiler.release=disabled -org.eclipse.jdt.core.compiler.source=1.8 diff --git a/kafka-dse-core/.settings/org.eclipse.m2e.core.prefs b/kafka-dse-core/.settings/org.eclipse.m2e.core.prefs deleted file mode 100644 index f897a7f..0000000 --- a/kafka-dse-core/.settings/org.eclipse.m2e.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -activeProfiles= -eclipse.preferences.version=1 -resolveWorkspaceProjects=true -version=1 diff --git a/kafka-dse-core/bin/pom.xml b/kafka-dse-core/bin/pom.xml new file mode 100644 index 0000000..d2acf9e --- /dev/null +++ b/kafka-dse-core/bin/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + kafka-dse-core + + kafka-dse-core + DAO, BEAN + + com.datastax + kafka-dse-example + 6.7-SNAPSHOT + + + + + com.datastax.dse + dse-java-driver-core + + + com.datastax.oss + java-driver-core + + + com.datastax.oss + java-driver-query-builder + + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + ch.qos.logback + logback-classic + + + ch.qos.logback + logback-core + + + org.springframework + spring-context + + + diff --git a/kafka-dse-core/bin/src/main/java/com/datastax/kafkadse/core/conf/DseConfiguration.class b/kafka-dse-core/bin/src/main/java/com/datastax/kafkadse/core/conf/DseConfiguration.class new file mode 100644 index 0000000..6076e96 Binary files /dev/null and b/kafka-dse-core/bin/src/main/java/com/datastax/kafkadse/core/conf/DseConfiguration.class differ diff --git a/kafka-dse-core/bin/src/main/java/com/datastax/kafkadse/core/conf/DseConstants.class b/kafka-dse-core/bin/src/main/java/com/datastax/kafkadse/core/conf/DseConstants.class new file mode 100644 index 0000000..6d7cb7b Binary files /dev/null and b/kafka-dse-core/bin/src/main/java/com/datastax/kafkadse/core/conf/DseConstants.class differ diff --git a/kafka-dse-core/bin/src/main/java/com/datastax/kafkadse/core/dao/DseDao.class b/kafka-dse-core/bin/src/main/java/com/datastax/kafkadse/core/dao/DseDao.class new file mode 100644 index 0000000..83f50cf Binary files /dev/null and b/kafka-dse-core/bin/src/main/java/com/datastax/kafkadse/core/dao/DseDao.class differ diff --git a/kafka-dse-core/bin/src/main/java/com/datastax/kafkadse/core/domain/Stock.class b/kafka-dse-core/bin/src/main/java/com/datastax/kafkadse/core/domain/Stock.class new file mode 100644 index 0000000..0ee65f3 Binary files /dev/null and b/kafka-dse-core/bin/src/main/java/com/datastax/kafkadse/core/domain/Stock.class differ diff --git a/kafka-dse-core/bin/src/main/java/com/datastax/kafkadse/core/domain/StockInfo.class b/kafka-dse-core/bin/src/main/java/com/datastax/kafkadse/core/domain/StockInfo.class new file mode 100644 index 0000000..fe0f4de Binary files /dev/null and b/kafka-dse-core/bin/src/main/java/com/datastax/kafkadse/core/domain/StockInfo.class differ diff --git a/kafka-dse-core/bin/src/main/java/com/datastax/kafkadse/core/domain/StockTick.class b/kafka-dse-core/bin/src/main/java/com/datastax/kafkadse/core/domain/StockTick.class new file mode 100644 index 0000000..3bb80db Binary files /dev/null and b/kafka-dse-core/bin/src/main/java/com/datastax/kafkadse/core/domain/StockTick.class differ diff --git a/kafka-dse-core/bin/src/main/java/com/datastax/kafkadse/core/domain/package-info.class b/kafka-dse-core/bin/src/main/java/com/datastax/kafkadse/core/domain/package-info.class new file mode 100644 index 0000000..261d7a4 Binary files /dev/null and b/kafka-dse-core/bin/src/main/java/com/datastax/kafkadse/core/domain/package-info.class differ diff --git a/kafka-dse-core/bin/src/main/resources/cql/create_schema.cql b/kafka-dse-core/bin/src/main/resources/cql/create_schema.cql new file mode 100644 index 0000000..a035b94 --- /dev/null +++ b/kafka-dse-core/bin/src/main/resources/cql/create_schema.cql @@ -0,0 +1,11 @@ +create keyspace demo_sdc WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1}; + +use demo_sdc; + +CREATE TABLE stocks_ticks ( + symbol text, + value_date timestamp, + value double, + PRIMARY KEY (symbol, value_date) +) WITH CLUSTERING ORDER BY (value_date DESC); + diff --git a/kafka-dse-core/bin/src/main/resources/cql/drop_schema.cql b/kafka-dse-core/bin/src/main/resources/cql/drop_schema.cql new file mode 100644 index 0000000..ddace09 --- /dev/null +++ b/kafka-dse-core/bin/src/main/resources/cql/drop_schema.cql @@ -0,0 +1,9 @@ +use demo_kafka; + +truncate ticks +drop table ticks; + +truncate alphavantage_1min; +drop table alphavantage_1min; + +drop keyspace demo_kafka; diff --git a/kafka-dse-core/pom.xml b/kafka-dse-core/pom.xml index 0e97e3d..d2acf9e 100644 --- a/kafka-dse-core/pom.xml +++ b/kafka-dse-core/pom.xml @@ -16,16 +16,12 @@ dse-java-driver-core - com.datastax.dse - dse-java-driver-mapping + com.datastax.oss + java-driver-core - com.datastax.dse - dse-java-driver-extras - - - com.datastax.dse - dse-java-driver-graph + com.datastax.oss + java-driver-query-builder diff --git a/kafka-dse-core/src/.DS_Store b/kafka-dse-core/src/.DS_Store deleted file mode 100644 index 615983b..0000000 Binary files a/kafka-dse-core/src/.DS_Store and /dev/null differ diff --git a/kafka-dse-core/src/main/.DS_Store b/kafka-dse-core/src/main/.DS_Store deleted file mode 100644 index 34f4aa4..0000000 Binary files a/kafka-dse-core/src/main/.DS_Store and /dev/null differ diff --git a/kafka-dse-core/src/main/java/.DS_Store b/kafka-dse-core/src/main/java/.DS_Store deleted file mode 100644 index 55731e8..0000000 Binary files a/kafka-dse-core/src/main/java/.DS_Store and /dev/null differ diff --git a/kafka-dse-core/src/main/java/com/.DS_Store b/kafka-dse-core/src/main/java/com/.DS_Store deleted file mode 100644 index 6ea1c8b..0000000 Binary files a/kafka-dse-core/src/main/java/com/.DS_Store and /dev/null differ diff --git a/kafka-dse-core/src/main/java/com/datastax/.DS_Store b/kafka-dse-core/src/main/java/com/datastax/.DS_Store deleted file mode 100644 index b1d8102..0000000 Binary files a/kafka-dse-core/src/main/java/com/datastax/.DS_Store and /dev/null differ diff --git a/kafka-dse-core/src/main/java/com/datastax/demo/.DS_Store b/kafka-dse-core/src/main/java/com/datastax/demo/.DS_Store deleted file mode 100644 index 06d6ab3..0000000 Binary files a/kafka-dse-core/src/main/java/com/datastax/demo/.DS_Store and /dev/null differ diff --git a/kafka-dse-core/src/main/java/com/datastax/demo/conf/DseConfiguration.java b/kafka-dse-core/src/main/java/com/datastax/demo/conf/DseConfiguration.java deleted file mode 100644 index c30ce4a..0000000 --- a/kafka-dse-core/src/main/java/com/datastax/demo/conf/DseConfiguration.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.datastax.demo.conf; - -import com.datastax.demo.domain.LongToTimeStampCodec; -import com.datastax.driver.core.AuthProvider; -import com.datastax.driver.core.CodecRegistry; -import com.datastax.driver.core.ConsistencyLevel; -import com.datastax.driver.core.QueryOptions; -import com.datastax.driver.core.exceptions.InvalidQueryException; -import com.datastax.driver.core.schemabuilder.SchemaBuilder; -import com.datastax.driver.dse.DseCluster.Builder; -import com.datastax.driver.dse.DseSession; -import com.datastax.driver.dse.auth.DsePlainTextAuthProvider; -import com.datastax.driver.mapping.DefaultPropertyMapper; -import com.datastax.driver.mapping.MappingConfiguration; -import com.datastax.driver.mapping.MappingManager; -import com.datastax.driver.mapping.PropertyMapper; -import com.datastax.driver.mapping.PropertyTransienceStrategy; -import com.google.common.collect.ImmutableMap; -import java.util.List; -import java.util.Optional; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** Connectivity to DSE (cassandra, graph, search). */ -@Configuration -public class DseConfiguration { - - /** Internal logger. */ - private static final Logger LOGGER = LoggerFactory.getLogger(DseConfiguration.class); - - @Value("#{'${dse.contactPoints}'.split(',')}") - public List contactPoints; - - @Value("${dse.port: 9042}") - public int port; - - @Value("${dse.keyspace: system}") - public String keyspace; - - @Value("${dse.username}") - public Optional dseUsername; - - @Value("${dse.password}") - public Optional dsePassword; - - @Value("${dse.localdc : dc1}") - public String localDc; - - @Bean - public DseSession dseSession() { - long top = System.currentTimeMillis(); - LOGGER.info("Initializing connection to DSE Cluster"); - - Builder clusterConfig = new Builder(); - LOGGER.info(" + Contact Points : {}", contactPoints); - contactPoints.stream().forEach(clusterConfig::addContactPoint); - LOGGER.info(" + Listening Port : {}", port); - clusterConfig.withPort(port); - - if (dseUsername.isPresent() && dsePassword.isPresent() && dseUsername.get().length() > 0) { - AuthProvider cassandraAuthProvider = - new DsePlainTextAuthProvider(dseUsername.get(), dsePassword.get()); - clusterConfig.withAuthProvider(cassandraAuthProvider); - LOGGER.info(" + With username : {}", dseUsername.get()); - } - - // OPTIONS - clusterConfig.withQueryOptions(new QueryOptions().setConsistencyLevel(ConsistencyLevel.QUORUM)); - - // Long <-> Timestamp - clusterConfig.withCodecRegistry(new CodecRegistry().register(new LongToTimeStampCodec())); - - try { - // First Connect without Keyspace (to create if needed) - DseSession tmpSession = null; - try { - tmpSession = clusterConfig.build().connect(); - tmpSession.execute( - SchemaBuilder.createKeyspace(keyspace) - .ifNotExists() - .with() - .replication(ImmutableMap.of("class", "SimpleStrategy", "replication_factor", 1))); - LOGGER.info(" + Creating keyspace '{}' (if needed)", keyspace); - } finally { - if (tmpSession != null) { - tmpSession.close(); - } - } - - // Real Connection now - DseSession dseSession = clusterConfig.build().connect(keyspace); - LOGGER.info( - " + Connection established to DSE Cluster \\_0_/ in {} millis.", - System.currentTimeMillis() - top); - return dseSession; - } catch (InvalidQueryException iqe) { - LOGGER.error( - "\n-----------------------------------------\n\n" - + "Keyspace '{}' seems does not exist. \nPlease update 'application.yml' with correct keyspace name or create one with:\n\n" - + " create keyspace {} WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1}; \n\nI will create the " - + "tables I need after that.\n-----------------------------------------", - keyspace, - keyspace); - throw new IllegalStateException("", iqe); - } - } - - /** - * Use to create mapper and perform ORM on top of Cassandra tables. - * - * @param session current dse session. - * @return mapper - */ - @Bean - public MappingManager mappingManager(DseSession session) { - // Do not map all fields, only the annotated ones with @Column or @Fields - PropertyMapper propertyMapper = - new DefaultPropertyMapper() - .setPropertyTransienceStrategy(PropertyTransienceStrategy.OPT_IN); - // Build configuration from mapping - MappingConfiguration configuration = - MappingConfiguration.builder().withPropertyMapper(propertyMapper).build(); - // Sample Manager with advance configuration - return new MappingManager(session, configuration); - } -} diff --git a/kafka-dse-core/src/main/java/com/datastax/demo/conf/DseConstants.java b/kafka-dse-core/src/main/java/com/datastax/demo/conf/DseConstants.java deleted file mode 100644 index 013c94d..0000000 --- a/kafka-dse-core/src/main/java/com/datastax/demo/conf/DseConstants.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.datastax.demo.conf; - -/** - * Constants in DSE-DB Tables. - * - * @author DataStax Evangelist Team - */ -public interface DseConstants { - - /** Table Names in Keyspace (Columns are defined in Beans). */ - String STOCKS_MINUTE = "stocks_by_min"; - - String STOCKS_HOUR = "stocks_by_hour"; - String STOCKS_DAY = "stocks_by_day"; - - String STOCKS_TICKS = "stocks_ticks"; - String STOCKS_INFOS = "stocks_infos"; - - String TICKER_COL_EXCHANGE = "exchange"; - String TICKER_COL_INDUSTRY = "industry"; - String TICKER_COL_NAME = "name"; - String TICKER_COL_SYMBOL = "symbol"; -} diff --git a/kafka-dse-core/src/main/java/com/datastax/demo/domain/LongToTimeStampCodec.java b/kafka-dse-core/src/main/java/com/datastax/demo/domain/LongToTimeStampCodec.java deleted file mode 100644 index 3452cdb..0000000 --- a/kafka-dse-core/src/main/java/com/datastax/demo/domain/LongToTimeStampCodec.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.datastax.demo.domain; - -import com.datastax.driver.core.TypeCodec; -import com.datastax.driver.extras.codecs.MappingCodec; -import java.util.Date; - -/** - * Column expect a blob, attribute is a String, we need a codec here for conversion. - * - *

In CQL you would be able to use textAsBlob(). - * - * @author DataStax evangelist team. - */ -public class LongToTimeStampCodec extends MappingCodec { - - /** Default charset will be UTF8. */ - public LongToTimeStampCodec() { - super(TypeCodec.timestamp(), Long.class); - } - - /** {@inheritDoc} */ - @Override - protected Long deserialize(Date value) { - return value.getTime(); - } - - /** {@inheritDoc} */ - @Override - protected Date serialize(Long value) { - return new Date(value); - } -} diff --git a/kafka-dse-core/src/main/java/com/datastax/demo/domain/Stock1Hour.java b/kafka-dse-core/src/main/java/com/datastax/demo/domain/Stock1Hour.java deleted file mode 100644 index 19f2763..0000000 --- a/kafka-dse-core/src/main/java/com/datastax/demo/domain/Stock1Hour.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.datastax.demo.domain; - -import com.datastax.demo.conf.DseConstants; -import com.datastax.driver.mapping.annotations.Table; - -/** Bean to save into table for minutes aggregation. */ -@Table(name = DseConstants.STOCKS_HOUR) -public class Stock1Hour extends Stock { - - /** Specialization for a dedicated table. */ - private static final long serialVersionUID = 6789940996895471880L; - - /** Specialization. */ - public Stock1Hour(Stock parent) { - super(parent); - } -} diff --git a/kafka-dse-core/src/main/java/com/datastax/demo/domain/Stock1Min.java b/kafka-dse-core/src/main/java/com/datastax/demo/domain/Stock1Min.java deleted file mode 100644 index e03d5b8..0000000 --- a/kafka-dse-core/src/main/java/com/datastax/demo/domain/Stock1Min.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.datastax.demo.domain; - -import com.datastax.demo.conf.DseConstants; -import com.datastax.driver.mapping.annotations.Table; - -/** Bean to save into table for minutes aggregation. */ -@Table(name = DseConstants.STOCKS_MINUTE) -public class Stock1Min extends Stock { - - /** Specialization for a dedicated table. */ - private static final long serialVersionUID = 6789940996895471880L; - - /** Specialization. */ - public Stock1Min(Stock parent) { - super(parent); - } -} diff --git a/kafka-dse-core/src/main/java/com/datastax/demo/utils/FileUtils.java b/kafka-dse-core/src/main/java/com/datastax/demo/utils/FileUtils.java deleted file mode 100644 index 70a2c71..0000000 --- a/kafka-dse-core/src/main/java/com/datastax/demo/utils/FileUtils.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.datastax.demo.utils; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.List; -import java.util.Scanner; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** utility class to parse files. */ -public class FileUtils { - - /** Hide default. */ - private FileUtils() {} - - /** Load file content as a String. */ - public static String readFileIntoString(String filename) { - try (Scanner s = new Scanner(new File(filename))) { - return s.useDelimiter("\\Z").next(); - } catch (FileNotFoundException e) { - throw new IllegalArgumentException("Cannot find the file", e); - } - } - - /** Load each line as a row. */ - public static List readFileIntoList(String filename) { - try (Stream lines = Files.lines(Paths.get(filename))) { - return lines.collect(Collectors.toList()); - } catch (IOException e1) { - throw new IllegalArgumentException("Cannot read file", e1); - } - } -} diff --git a/kafka-dse-core/src/main/java/com/datastax/kafkadse/core/conf/DseConfiguration.java b/kafka-dse-core/src/main/java/com/datastax/kafkadse/core/conf/DseConfiguration.java new file mode 100644 index 0000000..fbd080c --- /dev/null +++ b/kafka-dse-core/src/main/java/com/datastax/kafkadse/core/conf/DseConfiguration.java @@ -0,0 +1,100 @@ +package com.datastax.kafkadse.core.conf; + +import com.datastax.dse.driver.api.core.DseSession; +import com.datastax.dse.driver.api.core.DseSessionBuilder; +import com.datastax.dse.driver.internal.core.auth.DsePlainTextAuthProvider; +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.config.DefaultDriverOption; +import com.datastax.oss.driver.api.core.cql.SimpleStatement; +import com.datastax.oss.driver.api.querybuilder.SchemaBuilder; +import com.datastax.oss.driver.internal.core.config.typesafe.DefaultDriverConfigLoader; +import com.datastax.oss.driver.internal.core.config.typesafe.DefaultDriverConfigLoaderBuilder; +import java.net.InetSocketAddress; +import java.time.Duration; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.StopWatch; +import org.springframework.util.StringUtils; + +/** Connectivity to DSE (cassandra, graph, search). */ +@Configuration +public class DseConfiguration { + + /** Internal logger. */ + private static final Logger LOGGER = LoggerFactory.getLogger(DseConfiguration.class); + + @Value("#{'${dse.contactPoints}'.split(',')}") + private List contactPoints; + + @Value("${dse.port: 9042}") + private int port; + + @Value( + "#{T(com.datastax.oss.driver.api.core.CqlIdentifier).fromInternal('${dse.keyspace: demo_kafka}')}") + public CqlIdentifier keyspace; + + @Value("${dse.username}") + private String dseUsername; + + @Value("${dse.password}") + private String dsePassword; + + @Value("${dse.localdc: dc1}") + private String localDc; + + @Bean + public DseSession dseSession() { + + LOGGER.info("Initializing connection to DSE Cluster"); + LOGGER.info("Contact Points : {}", contactPoints); + LOGGER.info("Listening Port : {}", port); + LOGGER.info("Local DC : {}", localDc); + LOGGER.info("Keyspace : {}", keyspace); + + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + + DseSessionBuilder sessionBuilder = new DseSessionBuilder().withLocalDatacenter(localDc); + + contactPoints + .stream() + .map(cp -> InetSocketAddress.createUnresolved(cp, port)) + .forEach(sessionBuilder::addContactPoint); + + DefaultDriverConfigLoaderBuilder configLoaderBuilder = + DefaultDriverConfigLoader.builder() + .withString(DefaultDriverOption.REQUEST_CONSISTENCY, "QUORUM") + .withDuration(DefaultDriverOption.REQUEST_TIMEOUT, Duration.ofSeconds(30)); + + if (!StringUtils.isEmpty(dseUsername) && !StringUtils.isEmpty(dsePassword)) { + LOGGER.info("Username : {}", dseUsername); + configLoaderBuilder + .withString( + DefaultDriverOption.AUTH_PROVIDER_CLASS, DsePlainTextAuthProvider.class.getName()) + .withString(DefaultDriverOption.AUTH_PROVIDER_USER_NAME, dseUsername) + .withString(DefaultDriverOption.AUTH_PROVIDER_PASSWORD, dsePassword); + } + + sessionBuilder.withConfigLoader(configLoaderBuilder.build()); + + // First Connect without Keyspace (to create it if needed) + try (DseSession tempSession = sessionBuilder.build()) { + LOGGER.info("Creating keyspace {} (if needed)", keyspace); + SimpleStatement createKeyspace = + SchemaBuilder.createKeyspace(keyspace).ifNotExists().withSimpleStrategy(1).build(); + tempSession.execute(createKeyspace); + } + + // Now create the actual session + DseSession dseSession = sessionBuilder.withKeyspace(keyspace).build(); + stopWatch.stop(); + LOGGER.info( + "Connection established to DSE Cluster \\_0_/ in {} seconds.", + stopWatch.getTotalTimeSeconds()); + return dseSession; + } +} diff --git a/kafka-dse-core/src/main/java/com/datastax/kafkadse/core/conf/DseConstants.java b/kafka-dse-core/src/main/java/com/datastax/kafkadse/core/conf/DseConstants.java new file mode 100644 index 0000000..0dab8ac --- /dev/null +++ b/kafka-dse-core/src/main/java/com/datastax/kafkadse/core/conf/DseConstants.java @@ -0,0 +1,32 @@ +package com.datastax.kafkadse.core.conf; + +import com.datastax.oss.driver.api.core.CqlIdentifier; + +/** + * Constants in DSE-DB Tables. + * + * @author DataStax Evangelist Team + */ +public interface DseConstants { + + // Table names + + CqlIdentifier STOCKS_MINUTE = CqlIdentifier.fromCql("stocks_by_min"); + CqlIdentifier STOCKS_HOUR = CqlIdentifier.fromCql("stocks_by_hour"); + CqlIdentifier STOCKS_TICKS = CqlIdentifier.fromCql("stocks_ticks"); + CqlIdentifier STOCKS_INFOS = CqlIdentifier.fromCql("stocks_infos"); + + // Column names + + CqlIdentifier EXCHANGE = CqlIdentifier.fromCql("exchange"); + CqlIdentifier NAME = CqlIdentifier.fromCql("name"); + CqlIdentifier INDUSTRY = CqlIdentifier.fromCql("industry"); + CqlIdentifier SYMBOL = CqlIdentifier.fromCql("symbol"); + CqlIdentifier VALUE_DATE = CqlIdentifier.fromCql("value_date"); + CqlIdentifier VALUE = CqlIdentifier.fromCql("value"); + CqlIdentifier OPEN = CqlIdentifier.fromCql("open"); + CqlIdentifier CLOSE = CqlIdentifier.fromCql("close"); + CqlIdentifier HIGH = CqlIdentifier.fromCql("high"); + CqlIdentifier LOW = CqlIdentifier.fromCql("low"); + CqlIdentifier VOLUME = CqlIdentifier.fromCql("volume"); +} diff --git a/kafka-dse-core/src/main/java/com/datastax/kafkadse/core/dao/DseDao.java b/kafka-dse-core/src/main/java/com/datastax/kafkadse/core/dao/DseDao.java new file mode 100644 index 0000000..6b6cecb --- /dev/null +++ b/kafka-dse-core/src/main/java/com/datastax/kafkadse/core/dao/DseDao.java @@ -0,0 +1,130 @@ +package com.datastax.kafkadse.core.dao; + +import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.bindMarker; +import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.insertInto; +import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.literal; +import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.selectFrom; +import static com.datastax.oss.driver.api.querybuilder.SchemaBuilder.createTable; +import static com.datastax.oss.driver.api.querybuilder.relation.Relation.column; + +import com.datastax.dse.driver.api.core.DseSession; +import com.datastax.kafkadse.core.conf.DseConstants; +import com.datastax.kafkadse.core.domain.StockInfo; +import com.datastax.kafkadse.core.domain.StockTick; +import com.datastax.oss.driver.api.core.CqlIdentifier; +import com.datastax.oss.driver.api.core.cql.PreparedStatement; +import com.datastax.oss.driver.api.core.metadata.schema.ClusteringOrder; +import com.datastax.oss.driver.api.core.type.DataTypes; +import java.util.Set; +import java.util.concurrent.CompletionStage; +import java.util.stream.Collectors; +import javax.annotation.PostConstruct; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; + +@Repository +public class DseDao implements DseConstants { + + private static final Logger LOGGER = LoggerFactory.getLogger(DseDao.class); + + @Autowired private DseSession dseSession; + + private CqlIdentifier keyspace; + + private PreparedStatement insertIntoStockInfos; + private PreparedStatement insertIntoStockTicks; + + @PostConstruct + public void createOrUpdateSchema() { + keyspace = dseSession.getKeyspace().orElseThrow(IllegalStateException::new); + createTableStockInfosIfNotExists(); + createTableStockTicksIfNotExists(); + prepareStatements(); + LOGGER.info("Connection established to DSE and schema successfully created or updated."); + } + + /** Metadata table (Home page for webUI) */ + private void createTableStockInfosIfNotExists() { + dseSession.execute( + createTable(STOCKS_INFOS) + .ifNotExists() + .withPartitionKey(EXCHANGE, DataTypes.TEXT) + .withClusteringColumn(NAME, DataTypes.TEXT) + .withColumn(INDUSTRY, DataTypes.TEXT) + .withColumn(SYMBOL, DataTypes.TEXT) + .withClusteringOrder(NAME, ClusteringOrder.ASC) + .build()); + LOGGER.info(" + Table {} created in keyspace {} (if needed)", STOCKS_INFOS, keyspace); + } + + /** Random ticks where seed is last AlphaVantage */ + private void createTableStockTicksIfNotExists() { + dseSession.execute( + createTable(STOCKS_TICKS) + .ifNotExists() + .withPartitionKey(SYMBOL, DataTypes.TEXT) + .withClusteringColumn(VALUE_DATE, DataTypes.TIMESTAMP) + .withColumn(VALUE, DataTypes.DOUBLE) + .withClusteringOrder(VALUE_DATE, ClusteringOrder.DESC) + .build()); + LOGGER.info(" + Table {} created in keyspace {} (if needed)", STOCKS_TICKS, keyspace); + } + + private void prepareStatements() { + insertIntoStockInfos = + dseSession.prepare( + insertInto(STOCKS_INFOS) + .value(EXCHANGE, bindMarker(EXCHANGE)) + .value(NAME, bindMarker(NAME)) + .value(INDUSTRY, bindMarker(INDUSTRY)) + .value(SYMBOL, bindMarker(SYMBOL)) + .build()); + insertIntoStockTicks = + dseSession.prepare( + insertInto(STOCKS_TICKS) + .value(SYMBOL, bindMarker(SYMBOL)) + .value(VALUE_DATE, bindMarker(VALUE_DATE)) + .value(VALUE, bindMarker(VALUE)) + .build()); + } + + public CompletionStage saveTickerAsync(StockTick tick) { + return dseSession + .executeAsync( + insertIntoStockTicks + .boundStatementBuilder() + .setString(SYMBOL, tick.getSymbol()) + .setInstant(VALUE_DATE, tick.getValueDate()) + .setDouble(VALUE, tick.getValue()) + .build()) + .thenApply(rs -> tick); + } + + public CompletionStage saveStockInfoAsync(StockInfo info) { + return dseSession + .executeAsync( + insertIntoStockInfos + .boundStatementBuilder() + .setString(EXCHANGE, info.getExchange()) + .setString(NAME, info.getName()) + .setString(INDUSTRY, info.getIndustry()) + .setString(SYMBOL, info.getSymbol()) + .build()) + .thenApply(rs -> info); + } + + public Set getSymbolsNYSE() { + return dseSession + .execute( + selectFrom(STOCKS_INFOS) + .column(SYMBOL) + .where(column(EXCHANGE).isEqualTo(literal("NYSE"))) + .build()) + .all() + .stream() + .map(row -> row.getString(SYMBOL)) + .collect(Collectors.toSet()); + } +} diff --git a/kafka-dse-core/src/main/java/com/datastax/demo/domain/Stock.java b/kafka-dse-core/src/main/java/com/datastax/kafkadse/core/domain/Stock.java similarity index 52% rename from kafka-dse-core/src/main/java/com/datastax/demo/domain/Stock.java rename to kafka-dse-core/src/main/java/com/datastax/kafkadse/core/domain/Stock.java index fa2937b..78a0510 100644 --- a/kafka-dse-core/src/main/java/com/datastax/demo/domain/Stock.java +++ b/kafka-dse-core/src/main/java/com/datastax/kafkadse/core/domain/Stock.java @@ -1,11 +1,10 @@ -package com.datastax.demo.domain; +package com.datastax.kafkadse.core.domain; -import com.datastax.driver.mapping.annotations.ClusteringColumn; -import com.datastax.driver.mapping.annotations.Column; -import com.datastax.driver.mapping.annotations.PartitionKey; -import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import java.io.Serializable; -import java.util.Date; +import java.time.Instant; +import java.util.Objects; /** POJO Representing stock from Alpha Vantage. */ public class Stock implements Serializable { @@ -14,41 +13,53 @@ public class Stock implements Serializable { private static final long serialVersionUID = -5240591446495279713L; /** Stock symbol. */ - @PartitionKey private String symbol; + private String symbol; /** timestamp. */ - @ClusteringColumn - @Column(name = "value_date") - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") - private Date valueDate; + private Instant valueDate; - /** value at begining of period. */ - @Column private double open; + /** value at beginning of period. */ + private double open; /** value at end of period. */ - @Column private double close; + private double close; /** low value. */ - @Column private double low; + private double low; /** high value. */ - @Column private double high; - - /** number exchanged. */ - @Column private long volume; - - /** Default constructor (unmarshalling) */ - public Stock() {} + private double high; + + /** volume exchanged. */ + private long volume; + + @JsonCreator + public Stock( + @JsonProperty("symbol") String symbol, + @JsonProperty("valueDate") Instant valueDate, + @JsonProperty("open") double open, + @JsonProperty("close") double close, + @JsonProperty("low") double low, + @JsonProperty("high") double high, + @JsonProperty("volume") long volume) { + this.symbol = symbol; + this.valueDate = valueDate; + this.open = open; + this.close = close; + this.low = low; + this.high = high; + this.volume = volume; + } /** Copy constructor (specialization) */ - public Stock(Stock parent) { - this.valueDate = parent.getValueDate(); - this.high = parent.getHigh(); - this.low = parent.getLow(); - this.open = parent.getOpen(); - this.close = parent.getClose(); - this.volume = parent.getVolume(); - this.symbol = parent.getSymbol(); + public Stock(Stock toCopy) { + this.symbol = toCopy.getSymbol(); + this.valueDate = toCopy.getValueDate(); + this.open = toCopy.getOpen(); + this.close = toCopy.getClose(); + this.high = toCopy.getHigh(); + this.low = toCopy.getLow(); + this.volume = toCopy.getVolume(); } /** @@ -56,7 +67,7 @@ public Stock(Stock parent) { * * @return current value of 'valueDate' */ - public Date getValueDate() { + public Instant getValueDate() { return valueDate; } @@ -65,7 +76,7 @@ public Date getValueDate() { * * @param valueDate new value for 'valueDate ' */ - public void setValueDate(Date valueDate) { + public void setValueDate(Instant valueDate) { this.valueDate = valueDate; } @@ -176,4 +187,48 @@ public String getSymbol() { public void setSymbol(String symbol) { this.symbol = symbol; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Stock stock = (Stock) o; + return Double.compare(stock.open, open) == 0 + && Double.compare(stock.close, close) == 0 + && Double.compare(stock.low, low) == 0 + && Double.compare(stock.high, high) == 0 + && volume == stock.volume + && symbol.equals(stock.symbol) + && valueDate.equals(stock.valueDate); + } + + @Override + public int hashCode() { + return Objects.hash(symbol, valueDate, open, close, low, high, volume); + } + + @Override + public String toString() { + return "Stock{" + + "symbol='" + + symbol + + '\'' + + ", valueDate=" + + valueDate + + ", open=" + + open + + ", close=" + + close + + ", low=" + + low + + ", high=" + + high + + ", volume=" + + volume + + '}'; + } } diff --git a/kafka-dse-core/src/main/java/com/datastax/demo/domain/StockInfo.java b/kafka-dse-core/src/main/java/com/datastax/kafkadse/core/domain/StockInfo.java similarity index 53% rename from kafka-dse-core/src/main/java/com/datastax/demo/domain/StockInfo.java rename to kafka-dse-core/src/main/java/com/datastax/kafkadse/core/domain/StockInfo.java index c747e9f..32465a4 100644 --- a/kafka-dse-core/src/main/java/com/datastax/demo/domain/StockInfo.java +++ b/kafka-dse-core/src/main/java/com/datastax/kafkadse/core/domain/StockInfo.java @@ -1,34 +1,39 @@ -package com.datastax.demo.domain; +package com.datastax.kafkadse.core.domain; -import static com.datastax.demo.conf.DseConstants.STOCKS_INFOS; - -import com.datastax.driver.mapping.annotations.ClusteringColumn; -import com.datastax.driver.mapping.annotations.Column; -import com.datastax.driver.mapping.annotations.PartitionKey; -import com.datastax.driver.mapping.annotations.Table; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import java.io.Serializable; +import java.util.Objects; /** Value for Ticks. */ -@Table(name = STOCKS_INFOS) public class StockInfo implements Serializable { /** serial. */ private static final long serialVersionUID = 5806346188526710465L; /** value. */ - @PartitionKey private String exchange; + private String exchange; /** Value Date. */ - @ClusteringColumn private String name; + private String name; /** code. */ - @Column private String symbol; + private String symbol; /** value. */ - @Column private String industry; - - /** Default Constructor */ - public StockInfo() {} + private String industry; + + @JsonCreator + public StockInfo( + @JsonProperty("exchange") String exchange, + @JsonProperty("name") String name, + @JsonProperty("symbol") String symbol, + @JsonProperty("industry") String industry) { + this.exchange = exchange; + this.name = name; + this.symbol = symbol; + this.industry = industry; + } /** * Getter accessor for attribute 'symbol'. @@ -101,4 +106,42 @@ public String getExchange() { public void setExchange(String exchange) { this.exchange = exchange; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + StockInfo stockInfo = (StockInfo) o; + return exchange.equals(stockInfo.exchange) + && name.equals(stockInfo.name) + && symbol.equals(stockInfo.symbol) + && industry.equals(stockInfo.industry); + } + + @Override + public int hashCode() { + return Objects.hash(exchange, name, symbol, industry); + } + + @Override + public String toString() { + return "StockInfo{" + + "exchange='" + + exchange + + '\'' + + ", name='" + + name + + '\'' + + ", symbol='" + + symbol + + '\'' + + ", industry='" + + industry + + '\'' + + '}'; + } } diff --git a/kafka-dse-core/src/main/java/com/datastax/demo/domain/StockTick.java b/kafka-dse-core/src/main/java/com/datastax/kafkadse/core/domain/StockTick.java similarity index 51% rename from kafka-dse-core/src/main/java/com/datastax/demo/domain/StockTick.java rename to kafka-dse-core/src/main/java/com/datastax/kafkadse/core/domain/StockTick.java index 150653a..d2d6664 100644 --- a/kafka-dse-core/src/main/java/com/datastax/demo/domain/StockTick.java +++ b/kafka-dse-core/src/main/java/com/datastax/kafkadse/core/domain/StockTick.java @@ -1,40 +1,33 @@ -package com.datastax.demo.domain; +package com.datastax.kafkadse.core.domain; -import static com.datastax.demo.conf.DseConstants.STOCKS_TICKS; - -import com.datastax.driver.mapping.annotations.ClusteringColumn; -import com.datastax.driver.mapping.annotations.Column; -import com.datastax.driver.mapping.annotations.PartitionKey; -import com.datastax.driver.mapping.annotations.Table; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import java.io.Serializable; +import java.time.Instant; +import java.util.Objects; /** Value for Ticks. */ -@Table(name = STOCKS_TICKS) public class StockTick implements Serializable { /** serial. */ private static final long serialVersionUID = 5806346188526710465L; /** code. */ - @PartitionKey private String symbol; + private String symbol; /** Value Date. */ - @ClusteringColumn private long valueDate; + private Instant valueDate; /** value. */ - @Column private double value; - - /** Default Constructor */ - public StockTick() {} - - /** Constructor with parameters. */ - public StockTick(String tickSymbol, double value) { - this(tickSymbol, value, System.currentTimeMillis()); - } + private double value; /** Constructor with parameters. */ - public StockTick(String tickSymbol, double value, long valueDate) { - this.symbol = tickSymbol; + @JsonCreator + public StockTick( + @JsonProperty("symbol") String symbol, + @JsonProperty("valueDate") Instant valueDate, + @JsonProperty("value") double value) { + this.symbol = symbol; this.value = value; this.valueDate = valueDate; } @@ -62,7 +55,7 @@ public void setValue(double value) { * * @return current value of 'valueDate' */ - public long getValueDate() { + public Instant getValueDate() { return valueDate; } @@ -71,7 +64,7 @@ public long getValueDate() { * * @param valueDate new value for 'valueDate ' */ - public void setValueDate(long valueDate) { + public void setValueDate(Instant valueDate) { this.valueDate = valueDate; } @@ -92,4 +85,36 @@ public String getSymbol() { public void setSymbol(String symbol) { this.symbol = symbol; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + StockTick stockTick = (StockTick) o; + return Double.compare(stockTick.value, value) == 0 + && symbol.equals(stockTick.symbol) + && valueDate.equals(stockTick.valueDate); + } + + @Override + public int hashCode() { + return Objects.hash(symbol, valueDate, value); + } + + @Override + public String toString() { + return "StockTick{" + + "symbol='" + + symbol + + '\'' + + ", valueDate=" + + valueDate + + ", value=" + + value + + '}'; + } } diff --git a/kafka-dse-core/src/main/java/com/datastax/kafkadse/core/domain/package-info.java b/kafka-dse-core/src/main/java/com/datastax/kafkadse/core/domain/package-info.java new file mode 100644 index 0000000..8bd644b --- /dev/null +++ b/kafka-dse-core/src/main/java/com/datastax/kafkadse/core/domain/package-info.java @@ -0,0 +1,6 @@ +@NonNullApi +@NonNullFields +package com.datastax.kafkadse.core.domain; + +import org.springframework.lang.NonNullApi; +import org.springframework.lang.NonNullFields; diff --git a/kafka-dse-core/src/main/resources/cql/create_schema.cql b/kafka-dse-core/src/main/resources/cql/create_schema.cql index 6512cd5..a035b94 100644 --- a/kafka-dse-core/src/main/resources/cql/create_schema.cql +++ b/kafka-dse-core/src/main/resources/cql/create_schema.cql @@ -4,8 +4,8 @@ use demo_sdc; CREATE TABLE stocks_ticks ( symbol text, - valueDate timestamp, + value_date timestamp, value double, - PRIMARY KEY (symbol, valueDate) -) WITH CLUSTERING ORDER BY (valueDate DESC); + PRIMARY KEY (symbol, value_date) +) WITH CLUSTERING ORDER BY (value_date DESC); diff --git a/kafka-dse-producer/.DS_Store b/kafka-dse-producer/.DS_Store deleted file mode 100644 index 9a874b5..0000000 Binary files a/kafka-dse-producer/.DS_Store and /dev/null differ diff --git a/kafka-dse-producer/.classpath b/kafka-dse-producer/.classpath deleted file mode 100644 index 0ca1374..0000000 --- a/kafka-dse-producer/.classpath +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/kafka-dse-producer/.factorypath b/kafka-dse-producer/.factorypath index 978be56..66fa196 100644 --- a/kafka-dse-producer/.factorypath +++ b/kafka-dse-producer/.factorypath @@ -16,16 +16,49 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + @@ -33,47 +66,13 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + @@ -83,10 +82,8 @@ - - @@ -102,34 +99,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -155,17 +125,15 @@ - - - + @@ -173,19 +141,6 @@ - - - - - - - - - - - - - diff --git a/kafka-dse-producer/.project b/kafka-dse-producer/.project deleted file mode 100644 index 4923f3c..0000000 --- a/kafka-dse-producer/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - kafka-dse-producer - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - org.springframework.ide.eclipse.boot.validation.springbootbuilder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.m2e.core.maven2Nature - - diff --git a/kafka-dse-producer/.settings/org.eclipse.jdt.apt.core.prefs b/kafka-dse-producer/.settings/org.eclipse.jdt.apt.core.prefs deleted file mode 100644 index dfa4f3a..0000000 --- a/kafka-dse-producer/.settings/org.eclipse.jdt.apt.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.apt.aptEnabled=true -org.eclipse.jdt.apt.genSrcDir=target/generated-sources/annotations -org.eclipse.jdt.apt.genTestSrcDir=target/generated-test-sources/test-annotations diff --git a/kafka-dse-producer/.settings/org.eclipse.jdt.core.prefs b/kafka-dse-producer/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 21227d9..0000000 --- a/kafka-dse-producer/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,7 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 -org.eclipse.jdt.core.compiler.compliance=1.8 -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.processAnnotations=enabled -org.eclipse.jdt.core.compiler.release=disabled -org.eclipse.jdt.core.compiler.source=1.8 diff --git a/kafka-dse-producer/.settings/org.eclipse.m2e.core.prefs b/kafka-dse-producer/.settings/org.eclipse.m2e.core.prefs deleted file mode 100644 index f897a7f..0000000 --- a/kafka-dse-producer/.settings/org.eclipse.m2e.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -activeProfiles= -eclipse.preferences.version=1 -resolveWorkspaceProjects=true -version=1 diff --git a/kafka-dse-producer/.settings/org.springframework.ide.eclipse.prefs b/kafka-dse-producer/.settings/org.springframework.ide.eclipse.prefs deleted file mode 100644 index a12794d..0000000 --- a/kafka-dse-producer/.settings/org.springframework.ide.eclipse.prefs +++ /dev/null @@ -1,2 +0,0 @@ -boot.validation.initialized=true -eclipse.preferences.version=1 diff --git a/kafka-dse-producer/bin/pom.xml b/kafka-dse-producer/bin/pom.xml new file mode 100644 index 0000000..9bceed3 --- /dev/null +++ b/kafka-dse-producer/bin/pom.xml @@ -0,0 +1,163 @@ + + + 4.0.0 + kafka-dse-producer + + kafka-dse-producer + DAO, BEAN + + com.datastax + kafka-dse-example + 6.7-SNAPSHOT + + + + + com.github.signaflo + timeseries + 0.4 + + + + com.datastax + kafka-dse-core + ${project.version} + + + + com.datastax.dse + dse-java-driver-core + + + com.datastax.oss + java-driver-core + + + com.datastax.oss + java-driver-query-builder + + + com.fasterxml.jackson.dataformat + jackson-dataformat-csv + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.boot + spring-boot-starter-security + + + org.jolokia + jolokia-core + + + + org.apache.camel + camel-spring-boot-starter + + + org.apache.camel + camel-stream-starter + + + org.apache.camel + camel-kafka + + + + org.apache.kafka + connect-api + + + org.apache.kafka + connect-json + + + io.confluent + kafka-avro-serializer + + + io.netty + netty-all + + + org.patriques + alphavantage4j + 1.2 + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.springframework.boot + spring-boot-starter-test + test + + + junit + junit + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + com.datastax.kafkadse.producer.ProducerApplication + + + + + repackage + + + + + + + + + alphavantage + alphavantage4 + https://dl.bintray.com/patriques82/maven + + + diff --git a/kafka-dse-producer/bin/src/main/java/com/datastax/kafkadse/producer/ProducerApplication.class b/kafka-dse-producer/bin/src/main/java/com/datastax/kafkadse/producer/ProducerApplication.class new file mode 100644 index 0000000..1fbcc98 Binary files /dev/null and b/kafka-dse-producer/bin/src/main/java/com/datastax/kafkadse/producer/ProducerApplication.class differ diff --git a/kafka-dse-producer/bin/src/main/java/com/datastax/kafkadse/producer/ProducerRoutes.class b/kafka-dse-producer/bin/src/main/java/com/datastax/kafkadse/producer/ProducerRoutes.class new file mode 100644 index 0000000..7a1eeb0 Binary files /dev/null and b/kafka-dse-producer/bin/src/main/java/com/datastax/kafkadse/producer/ProducerRoutes.class differ diff --git a/kafka-dse-producer/bin/src/main/java/com/datastax/kafkadse/producer/conf/ProducerConfiguration.class b/kafka-dse-producer/bin/src/main/java/com/datastax/kafkadse/producer/conf/ProducerConfiguration.class new file mode 100644 index 0000000..48fa193 Binary files /dev/null and b/kafka-dse-producer/bin/src/main/java/com/datastax/kafkadse/producer/conf/ProducerConfiguration.class differ diff --git a/kafka-dse-producer/bin/src/main/java/com/datastax/kafkadse/producer/conf/SecurityConf.class b/kafka-dse-producer/bin/src/main/java/com/datastax/kafkadse/producer/conf/SecurityConf.class new file mode 100644 index 0000000..1e8d49e Binary files /dev/null and b/kafka-dse-producer/bin/src/main/java/com/datastax/kafkadse/producer/conf/SecurityConf.class differ diff --git a/kafka-dse-producer/bin/src/main/java/com/datastax/kafkadse/producer/dao/AlphaVantageDao.class b/kafka-dse-producer/bin/src/main/java/com/datastax/kafkadse/producer/dao/AlphaVantageDao.class new file mode 100644 index 0000000..bc621b3 Binary files /dev/null and b/kafka-dse-producer/bin/src/main/java/com/datastax/kafkadse/producer/dao/AlphaVantageDao.class differ diff --git a/kafka-dse-producer/bin/src/main/java/com/datastax/kafkadse/producer/dao/CsvDao.class b/kafka-dse-producer/bin/src/main/java/com/datastax/kafkadse/producer/dao/CsvDao.class new file mode 100644 index 0000000..ff87251 Binary files /dev/null and b/kafka-dse-producer/bin/src/main/java/com/datastax/kafkadse/producer/dao/CsvDao.class differ diff --git a/kafka-dse-producer/bin/src/main/java/com/datastax/kafkadse/producer/route/StockTicksProducer.class b/kafka-dse-producer/bin/src/main/java/com/datastax/kafkadse/producer/route/StockTicksProducer.class new file mode 100644 index 0000000..e9a3559 Binary files /dev/null and b/kafka-dse-producer/bin/src/main/java/com/datastax/kafkadse/producer/route/StockTicksProducer.class differ diff --git a/kafka-dse-producer/bin/src/main/resources/application.yml b/kafka-dse-producer/bin/src/main/resources/application.yml new file mode 100644 index 0000000..e9c52ce --- /dev/null +++ b/kafka-dse-producer/bin/src/main/resources/application.yml @@ -0,0 +1,53 @@ +# --------------------------------------------------------------------------------- +# Spring Boot Fmk properties +# --------------------------------------------------------------------------------- +spring: + application: + name: StockData Injector for Kafka +server: + port: 8088 + +# --------------------------------------------------------------------------------- +# Apache Camel (integration fmk) +# --------------------------------------------------------------------------------- +camel: + springboot: + name: Kafka Producer + cloud: + enabled: false + +# --------------------------------------------------------------------------------- +# Apache KAFKA client infos +# --------------------------------------------------------------------------------- +kafka: + server: 127.0.0.1:9092 + period: 2000 + ack: -1 + topics: + ticks: stocks_ticks + +# --------------------------------------------------------------------------------- +# DataStax Enterprise client infos +# --------------------------------------------------------------------------------- +dse: + contactPoints: 127.0.0.1 + port: 9042 + localdc: DC1 + keyspace: demo_kafka + username: + password: + +# File with the Ticks metadata (Codes...) +csvStocksMetadata: src/main/resources/stocks-metadata.csv + +# --------------------------------------------------------------------------------- +# Query QUOTES from https://www.alphavantage.co/documentation/ +# --------------------------------------------------------------------------------- +alphavantage: + apiKey: 2HWDTH7BA7FRBP76 + timeout: 3000 + waitTime: 100 + pollingPeriod: + ticks: 5000 + 1min: 60000 + 1hour: 3600000 \ No newline at end of file diff --git a/kafka-dse-producer/bin/src/main/resources/banner.txt b/kafka-dse-producer/bin/src/main/resources/banner.txt new file mode 100644 index 0000000..09bc827 --- /dev/null +++ b/kafka-dse-producer/bin/src/main/resources/banner.txt @@ -0,0 +1,12 @@ +__________ .___ +\______ \_______ ____ __| _/_ __ ____ ___________ + | ___/\_ __ \/ _ \ / __ | | \_/ ___\/ __ \_ __ \ + | | | | \( <_> ) /_/ | | /\ \__\ ___/| | \/ + |____| |__| \____/\____ |____/ \___ >___ >__| + \/ \/ \/ + + Provided to you by DataStax Advocates team \_0_/ + + 1. Load a Stocks inventory from CSV File + 2. Init Stocks values from AlphaVantage API calls + 3. Generate random values and send to Kafka topic 'stocks_ticks' diff --git a/kafka-dse-producer/bin/src/main/resources/logback.xml b/kafka-dse-producer/bin/src/main/resources/logback.xml new file mode 100644 index 0000000..a6df44c --- /dev/null +++ b/kafka-dse-producer/bin/src/main/resources/logback.xml @@ -0,0 +1,20 @@ + + + + + %d{HH:mm:ss.SSS} %magenta(%-5level) %cyan(%-45logger) : %msg%n + + + + + + + + + + + + + + + diff --git a/kafka-dse-producer/bin/src/main/resources/stocks-metadata.csv b/kafka-dse-producer/bin/src/main/resources/stocks-metadata.csv new file mode 100644 index 0000000..8d41130 --- /dev/null +++ b/kafka-dse-producer/bin/src/main/resources/stocks-metadata.csv @@ -0,0 +1,61 @@ +name,symbol,exchange,industry +WALMART,WMT,NYSE,RETAIL +BERKSHIRE HATHAWAY,BRK.A,NYSE,FINANCE +APPLE,APPL,NASDAQ,TECH +EXXON MOBIL,XOM,NYSE,ENERGY +MCKESSON,MCK,NYSE,HEALTH +UNITEDHEALTH GROUP,UNH,NYSE,HEALTH +CVS HEALTH,CVS,NYSE,RETAIL PHARMA +GENERAL MOTORS,GM,NYSE,AUTO +AT&T,T,NYSE,TELECOM +FORD MOTOR,F,NYSE,AUTO +AMERISOURCEBERGEN,ABC,NYSE,PHARMA +AMAZON.COM,AMZN,NASDAQ,TECH +GENERAL ELECTRIC,GE,NYSE,CONGLOMERATE +VERIZON COMMUNICATIONS,VZ,NYSE,TELECOM +CARDINAL HEALTH,CAH,NYSE,PHARMA +COSTCO,COST,NASDAQ,RETAIL +WALGREENS BOOTS ALLIANCE,WBA,NASDAQ,RETAIL PHARMA +KROGER,KR,NYSE,GROCERY +CHEVRON,CVX,NYSE,ENERGY +FANNIE MAE,FNMA,OTCMKTS,FINANCE +J.P. MORGAN CHASE,JPM,NYSE,FINANCE +EXPRESS SCRIPTS HOLDING,ESRX,NASDAQ,HEALTH +HOME DEPOT,HD,NYSE,RETAIL +BOEING,BA,NYSE,AIR +WELLS FARGO,WFC,NYSE,FINANCE +BANK OF AMERICA CORP.,BAC,NYSE,FINANCE +ALPHABET,GOOGL,NASDAQ,TECH +MICROSOFT,MSFT,NASDAQ,TECH +ANTHEM,ANTM,NYSE,HEALTH +CITIGROUP,C,NYSE,FINANCE +COMCAST,CMCSA,NASDAQ,TELECOM +IBM,IBM,NYSE,TECH +STATE FARM INSURANCE COS.,STFGX,NASDAQ,INSURANCE +PHILLIPS 66,PSX,NYSE,ENERGY +JOHNSON & JOHNSON,JNJ,NYSE,PHARMA +PROCTER & GAMBLE,PG,NYSE,RETAIL +VALERO ENERGY,VLO,NYSE,ENERGY +TARGET,TGT,NYSE,RETAIL +FREDDIE MAC,FMCC,OTCMKTS,FINANCE +LOWE'S,LOW,NYSE,RETAIL +DELL TECHNOLOGIES,DVMT,NYSE,TECH +METLIFE,MET,NYSE,FINANCE +AETNA,AET,NYSE,HEALTH +PEPSICO,PEP,NASDAQ,FOOD BEVERAGE +ARCHER DANIELS MIDLAND,ADM,NYSE,FOOD BEVERAGE +UPS,UPS,NYSE,COURIER +INTEL,INTC,NASDAQ,TECH +PRUDENTIAL FINANCIAL,PRU,NYSE,FINANCE +ALBERTSONS COS.,ABS,NYSE,GROCERY +UNITED TECHNOLOGIES,UTX,NYSE,AIR +MARATHON PETROLEUM,MPC,NYSE,ENERGY +DISNEY,DIS,NYSE,ENTERTAINMENT +HUMANA,HUM,NYSE,HEALTH +PFIZER,PFE,NYSE,PHARMA +AIG,AIG,NYSE,INSURANCE +LOCKHEED MARTIN,LMT,NYSE,AIR +SYSCO,SYY,NYSE,RETAIL +FEDEX,FDX,NYSE,COURIER +HEWLETT PACKARD ENTERPRISE,HPE,NYSE,TECH +CISCO SYSTEMS,CSCO,NASDAQ,TECH \ No newline at end of file diff --git a/kafka-dse-producer/bin/src/test/java/com/datastax/demo/test/LoadAlphaVantageDataJob.class b/kafka-dse-producer/bin/src/test/java/com/datastax/demo/test/LoadAlphaVantageDataJob.class new file mode 100644 index 0000000..85df435 Binary files /dev/null and b/kafka-dse-producer/bin/src/test/java/com/datastax/demo/test/LoadAlphaVantageDataJob.class differ diff --git a/kafka-dse-producer/bin/src/test/java/com/datastax/demo/test/TestSendMessage.class b/kafka-dse-producer/bin/src/test/java/com/datastax/demo/test/TestSendMessage.class new file mode 100644 index 0000000..ca2271f Binary files /dev/null and b/kafka-dse-producer/bin/src/test/java/com/datastax/demo/test/TestSendMessage.class differ diff --git a/kafka-dse-producer/bin/src/test/java/com/datastax/demo/test/TimeSeriesMachineLearning.class b/kafka-dse-producer/bin/src/test/java/com/datastax/demo/test/TimeSeriesMachineLearning.class new file mode 100644 index 0000000..95bd0a9 Binary files /dev/null and b/kafka-dse-producer/bin/src/test/java/com/datastax/demo/test/TimeSeriesMachineLearning.class differ diff --git a/kafka-dse-producer/bin/src/test/resources/config-test.properties b/kafka-dse-producer/bin/src/test/resources/config-test.properties new file mode 100644 index 0000000..ac4cb48 --- /dev/null +++ b/kafka-dse-producer/bin/src/test/resources/config-test.properties @@ -0,0 +1,18 @@ +alphavantage.apiKey=2HWDTH7BA7FRBP76 +alphavantage.timeout=30000 + +dse.contactPoints=127.0.0.1 +dse.port=9042 +dse.localdc=SearchGraphAnalytics +dse.username= +dse.password= +dse.keyspace=demo_kafka + +csvFile=src/main/resources/exchange-data.csv + +# Connectivity to KAFKA +kafka.server=127.0.0.1:9092 +kafka.ack=-1 +kafka.group=tick-group +kafka.topics.ticks=testTopic + diff --git a/kafka-dse-producer/pom.xml b/kafka-dse-producer/pom.xml index 598a84e..9bceed3 100644 --- a/kafka-dse-producer/pom.xml +++ b/kafka-dse-producer/pom.xml @@ -16,16 +16,33 @@ timeseries 0.4 - + com.datastax kafka-dse-core ${project.version} + + + com.datastax.dse + dse-java-driver-core + + + com.datastax.oss + java-driver-core + + + com.datastax.oss + java-driver-query-builder + com.fasterxml.jackson.dataformat jackson-dataformat-csv + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + org.springframework.boot @@ -35,6 +52,7 @@ org.springframework.boot spring-boot-starter-actuator + org.springframework.boot spring-boot-starter-security @@ -88,31 +107,31 @@ alphavantage4j 1.2 - - org.junit.platform - junit-platform-launcher - - - org.junit.platform - junit-platform-runner - - - org.junit.platform - junit-platform-console-standalone + org.junit.jupiter + junit-jupiter-api + test org.junit.jupiter junit-jupiter-engine + test org.junit.jupiter junit-jupiter-params + test org.springframework.boot spring-boot-starter-test test + + + junit + junit + + @@ -122,7 +141,7 @@ spring-boot-maven-plugin ${spring-boot.version} - com.datastax.demo.ProducerApplication + com.datastax.kafkadse.producer.ProducerApplication diff --git a/kafka-dse-producer/src/.DS_Store b/kafka-dse-producer/src/.DS_Store deleted file mode 100644 index 9604c91..0000000 Binary files a/kafka-dse-producer/src/.DS_Store and /dev/null differ diff --git a/kafka-dse-producer/src/main/.DS_Store b/kafka-dse-producer/src/main/.DS_Store deleted file mode 100644 index 5ddc54c..0000000 Binary files a/kafka-dse-producer/src/main/.DS_Store and /dev/null differ diff --git a/kafka-dse-producer/src/main/java/.DS_Store b/kafka-dse-producer/src/main/java/.DS_Store deleted file mode 100644 index 35a54bf..0000000 Binary files a/kafka-dse-producer/src/main/java/.DS_Store and /dev/null differ diff --git a/kafka-dse-producer/src/main/java/com/.DS_Store b/kafka-dse-producer/src/main/java/com/.DS_Store deleted file mode 100644 index 804ace9..0000000 Binary files a/kafka-dse-producer/src/main/java/com/.DS_Store and /dev/null differ diff --git a/kafka-dse-producer/src/main/java/com/datastax/.DS_Store b/kafka-dse-producer/src/main/java/com/datastax/.DS_Store deleted file mode 100644 index 8067953..0000000 Binary files a/kafka-dse-producer/src/main/java/com/datastax/.DS_Store and /dev/null differ diff --git a/kafka-dse-producer/src/main/java/com/datastax/demo/.DS_Store b/kafka-dse-producer/src/main/java/com/datastax/demo/.DS_Store deleted file mode 100644 index fca0e5d..0000000 Binary files a/kafka-dse-producer/src/main/java/com/datastax/demo/.DS_Store and /dev/null differ diff --git a/kafka-dse-producer/src/main/java/com/datastax/demo/ProducerRoutes.java b/kafka-dse-producer/src/main/java/com/datastax/demo/ProducerRoutes.java deleted file mode 100644 index 3156ee5..0000000 --- a/kafka-dse-producer/src/main/java/com/datastax/demo/ProducerRoutes.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.datastax.demo; - -import com.datastax.demo.route.StockTicksConsumer; -import com.datastax.demo.route.StockTicksProducer; -import org.apache.camel.builder.RouteBuilder; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -/** Apache Camel trigger processors with routes. */ -@Component -public class ProducerRoutes extends RouteBuilder { - - // Producers - - @Autowired private StockTicksProducer stockTickProducerAvro; - - @Autowired private StockTicksConsumer stockTickConsumerAvro; - - /** {@inheritDoc} */ - @Override - public void configure() throws Exception { - - // ALPHAVANYAGE REST API ==> KAFKA (ticks, 1Min, 1hour) - from("timer:ticks?fixedRate=true&period={{alphavantage.pollingPeriod.ticks}}") - .routeId("ticks_Alpha2Kafka") - .process(stockTickProducerAvro) - .end(); - - // ALPHAVANYAGE KAFKA => DSE (TMP) - from("timer:ticksConsumer?" + "fixedRate=true" + "&period={{alphavantage.pollingPeriod.ticks}}") - .routeId("ticks_Kafka2Dse") - .process(stockTickConsumerAvro) - .end(); - } -} diff --git a/kafka-dse-producer/src/main/java/com/datastax/demo/dao/DseDao.java b/kafka-dse-producer/src/main/java/com/datastax/demo/dao/DseDao.java deleted file mode 100644 index 17c39c7..0000000 --- a/kafka-dse-producer/src/main/java/com/datastax/demo/dao/DseDao.java +++ /dev/null @@ -1,155 +0,0 @@ -package com.datastax.demo.dao; - -import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; -import static com.datastax.driver.core.querybuilder.QueryBuilder.select; - -import com.datastax.demo.conf.DseConstants; -import com.datastax.demo.domain.Stock; -import com.datastax.demo.domain.Stock1Hour; -import com.datastax.demo.domain.Stock1Min; -import com.datastax.demo.domain.StockInfo; -import com.datastax.demo.domain.StockTick; -import com.datastax.driver.core.DataType; -import com.datastax.driver.core.schemabuilder.SchemaBuilder; -import com.datastax.driver.core.schemabuilder.SchemaBuilder.Direction; -import com.datastax.driver.dse.DseSession; -import com.datastax.driver.mapping.Mapper; -import com.datastax.driver.mapping.MappingManager; -import java.util.Set; -import java.util.stream.Collectors; -import javax.annotation.PostConstruct; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Repository; - -@Repository -public class DseDao implements DseConstants { - - /** Internal logger. */ - private static final Logger LOGGER = LoggerFactory.getLogger(DseDao.class); - - /** Hold Connectivity to DSE. */ - @Autowired DseSession dseSession; - - /** Hold Connectivity to DSE. */ - @Autowired CsvDao csvDao; - - /** Hold Driver Mapper to implement ORM with Cassandra. */ - @Autowired MappingManager mappingManager; - - /** Mapper. */ - Mapper stockTicksMapper; - - /** Mapper. */ - Mapper stockInfoMapper; - - /** Mapper. */ - Mapper stock1minMapper; - - /** Mapper. */ - Mapper stock1hourMapper; - - @PostConstruct - public void createTableifNotExist() { - // Metadata (Home page for webUI) - dseSession.execute( - SchemaBuilder.createTable(STOCKS_INFOS) - .ifNotExists() - .addPartitionKey("exchange", DataType.text()) - .addClusteringColumn("name", DataType.text()) - .addColumn("industry", DataType.text()) - .addColumn("symbol", DataType.text()) - .withOptions() - .clusteringOrder("name", Direction.ASC) - .buildInternal()); - LOGGER.info( - " + Table '{}' created in keyspace '{}' (if needed)", - STOCKS_INFOS, - dseSession.getLoggedKeyspace()); - - // Random ticks where seed is last AlphaVantage - dseSession.execute( - SchemaBuilder.createTable(STOCKS_TICKS) - .ifNotExists() - .addPartitionKey("symbol", DataType.text()) - .addClusteringColumn("valueDate", DataType.timestamp()) - .addColumn("value", DataType.cdouble()) - .withOptions() - .clusteringOrder("valueDate", Direction.DESC) - .buildInternal()); - LOGGER.info( - " + Table '{}' created in keyspace '{}' (if needed)", - STOCKS_TICKS, - dseSession.getLoggedKeyspace()); - - // Create tables for histograms - createTableStocksIntervalIfNotExist(STOCKS_MINUTE); - LOGGER.info( - " + Table '{}' created in keyspace '{}' (if needed)", - STOCKS_MINUTE, - dseSession.getLoggedKeyspace()); - - createTableStocksIntervalIfNotExist(STOCKS_HOUR); - LOGGER.info( - " + Table '{}' created in keyspace '{}' (if needed)", - STOCKS_HOUR, - dseSession.getLoggedKeyspace()); - - // Init Mappers - stockTicksMapper = mappingManager.mapper(StockTick.class); - stockInfoMapper = mappingManager.mapper(StockInfo.class); - - // Load CSV and fill table 'stocks_infos' - csvDao.readStockInfosFromCsv().forEach(this::saveStockInfo); - LOGGER.info(" + Table '{}' filled with symbols found in CSV.", STOCKS_INFOS); - LOGGER.info( - "Connection successfully established to DSE and schema has been created.", STOCKS_INFOS); - } - - /** Creation of tables stocks_by* */ - private void createTableStocksIntervalIfNotExist(String tableName) { - dseSession.execute( - SchemaBuilder.createTable(tableName) - .ifNotExists() - .addPartitionKey("symbol", DataType.text()) - .addClusteringColumn("value_date", DataType.timestamp()) - .addColumn("open", DataType.cdouble()) - .addColumn("close", DataType.cdouble()) - .addColumn("high", DataType.cdouble()) - .addColumn("low", DataType.cdouble()) - .addColumn("volume", DataType.bigint()) - .withOptions() - .clusteringOrder("value_date", Direction.DESC) - .buildInternal()); - } - - public void saveTicker(StockTick tick) { - dseSession.executeAsync(stockTicksMapper.saveQuery(tick)); - } - - public void saveStock1Min(Stock quote) { - dseSession.executeAsync(stock1minMapper.saveQuery(new Stock1Min(quote))); - } - - public void saveStock1Hour(Stock quote) { - dseSession.executeAsync(stock1hourMapper.saveQuery(new Stock1Hour(quote))); - } - - public void saveStock1Day(Stock quote) { - dseSession.executeAsync(stock1hourMapper.saveQuery(new Stock1Hour(quote))); - } - - public void saveStockInfo(StockInfo ti) { - dseSession.executeAsync(stockInfoMapper.saveQuery(ti)); - } - - public Set getSymbolsNYSE() { - return dseSession - .execute(select("symbol").from(STOCKS_INFOS).where(eq("exchange", "NYSE"))) - .all() - .stream() - .map(row -> row.getString("symbol")) - .collect(Collectors.toSet()); - } -} diff --git a/kafka-dse-producer/src/main/java/com/datastax/demo/dao/KafkaDao.java b/kafka-dse-producer/src/main/java/com/datastax/demo/dao/KafkaDao.java deleted file mode 100644 index 22a2f7e..0000000 --- a/kafka-dse-producer/src/main/java/com/datastax/demo/dao/KafkaDao.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.datastax.demo.dao; - -import com.fasterxml.jackson.databind.JsonNode; -import org.apache.kafka.clients.producer.KafkaProducer; -import org.apache.kafka.clients.producer.ProducerRecord; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Repository; - -@Repository -public class KafkaDao { - - @Autowired - @Qualifier("producer.json") - protected KafkaProducer jsonProducer; - - /** - * JSON PRODUCER. - * - * @param jsonMsg - */ - public void sendJsonMessage(ProducerRecord jsonMsg) { - System.out.println(jsonMsg.topic()); - jsonProducer.send(jsonMsg); - } -} diff --git a/kafka-dse-producer/src/main/java/com/datastax/demo/route/ErrorHandlerProcessor.java b/kafka-dse-producer/src/main/java/com/datastax/demo/route/ErrorHandlerProcessor.java deleted file mode 100644 index 85cc9cf..0000000 --- a/kafka-dse-producer/src/main/java/com/datastax/demo/route/ErrorHandlerProcessor.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.datastax.demo.route; - -import org.apache.camel.Exchange; -import org.apache.camel.Processor; -import org.apache.camel.builder.RouteBuilder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - -/** Custom Behaviour to handle Error. */ -@Component -public class ErrorHandlerProcessor extends RouteBuilder implements Processor { - - /** logger. */ - private static Logger logger = LoggerFactory.getLogger(ErrorHandlerProcessor.class); - - /** {@inheritDoc} */ - public void configure() throws Exception { - errorHandler(deadLetterChannel("seda:errors")); - from("seda:errors").bean(this); - } - - /** - * @param exchange current camel exchange - * @throws Exception - */ - public void process(Exchange exchange) throws Exception { - Exception cause = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class); - if (cause != null) { - logger.error("A technical error has occurred: ", cause); - } - logger.error("ExchangeiD" + exchange.getExchangeId()); - logger.error("Incoming" + exchange.getFromRouteId()); - } -} diff --git a/kafka-dse-producer/src/main/java/com/datastax/demo/ProducerApplication.java b/kafka-dse-producer/src/main/java/com/datastax/kafkadse/producer/ProducerApplication.java similarity index 64% rename from kafka-dse-producer/src/main/java/com/datastax/demo/ProducerApplication.java rename to kafka-dse-producer/src/main/java/com/datastax/kafkadse/producer/ProducerApplication.java index d95c19d..5db399e 100755 --- a/kafka-dse-producer/src/main/java/com/datastax/demo/ProducerApplication.java +++ b/kafka-dse-producer/src/main/java/com/datastax/kafkadse/producer/ProducerApplication.java @@ -1,19 +1,15 @@ -package com.datastax.demo; +package com.datastax.kafkadse.producer; -import de.codecentric.boot.admin.server.config.EnableAdminServer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; -/** Main class for CannysEngine. */ @SpringBootApplication -@ComponentScan(basePackages = "com.datastax.demo") +@ComponentScan(basePackages = {"com.datastax.kafkadse.producer", "com.datastax.kafkadse.core"}) @EnableAutoConfiguration -@EnableAdminServer public class ProducerApplication { - /** A main method to start this application. */ public static void main(String[] args) { SpringApplication.run(ProducerApplication.class, args); } diff --git a/kafka-dse-producer/src/main/java/com/datastax/kafkadse/producer/ProducerRoutes.java b/kafka-dse-producer/src/main/java/com/datastax/kafkadse/producer/ProducerRoutes.java new file mode 100644 index 0000000..af798d7 --- /dev/null +++ b/kafka-dse-producer/src/main/java/com/datastax/kafkadse/producer/ProducerRoutes.java @@ -0,0 +1,20 @@ +package com.datastax.kafkadse.producer; + +import com.datastax.kafkadse.producer.route.StockTicksProducer; +import org.apache.camel.builder.RouteBuilder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class ProducerRoutes extends RouteBuilder { + + @Autowired private StockTicksProducer stockTicksProducer; + + @Override + public void configure() { + from("timer:ticks?fixedRate=true&period={{alphavantage.pollingPeriod.ticks}}") + .routeId("ticks_Alpha2Kafka") + .process(stockTicksProducer) + .end(); + } +} diff --git a/kafka-dse-producer/src/main/java/com/datastax/demo/conf/ProducerConfiguration.java b/kafka-dse-producer/src/main/java/com/datastax/kafkadse/producer/conf/ProducerConfiguration.java similarity index 56% rename from kafka-dse-producer/src/main/java/com/datastax/demo/conf/ProducerConfiguration.java rename to kafka-dse-producer/src/main/java/com/datastax/kafkadse/producer/conf/ProducerConfiguration.java index ef99ce7..f4a75dc 100644 --- a/kafka-dse-producer/src/main/java/com/datastax/demo/conf/ProducerConfiguration.java +++ b/kafka-dse-producer/src/main/java/com/datastax/kafkadse/producer/conf/ProducerConfiguration.java @@ -1,20 +1,17 @@ -package com.datastax.demo.conf; +package com.datastax.kafkadse.producer.conf; -import static org.apache.kafka.clients.consumer.ConsumerConfig.GROUP_ID_CONFIG; -import static org.apache.kafka.clients.consumer.ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG; -import static org.apache.kafka.clients.consumer.ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG; import static org.apache.kafka.clients.producer.ProducerConfig.ACKS_CONFIG; import static org.apache.kafka.clients.producer.ProducerConfig.BOOTSTRAP_SERVERS_CONFIG; import static org.apache.kafka.clients.producer.ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG; import static org.apache.kafka.clients.producer.ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import java.util.Properties; -import org.apache.kafka.clients.consumer.KafkaConsumer; import org.apache.kafka.clients.producer.KafkaProducer; -import org.apache.kafka.common.serialization.StringDeserializer; import org.apache.kafka.common.serialization.StringSerializer; -import org.apache.kafka.connect.json.JsonDeserializer; import org.apache.kafka.connect.json.JsonSerializer; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -29,8 +26,13 @@ public class ProducerConfiguration { @Value("${kafka.ack}") private String producerAck; - @Value("${kafka.group}") - private String consumerGroup; + @Bean("producer.mapper") + public ObjectMapper jacksonMapper() { + ObjectMapper jacksonMapper = new ObjectMapper(); + jacksonMapper.registerModule(new JavaTimeModule()); + jacksonMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); + return jacksonMapper; + } @Bean("producer.json") public KafkaProducer jsonProducer() { @@ -39,16 +41,6 @@ public KafkaProducer jsonProducer() { props.put(KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); props.put(VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class.getName()); props.put(ACKS_CONFIG, producerAck); - return new KafkaProducer(props); - } - - @Bean("consumer.json") - public KafkaConsumer jsonConsumer() { - Properties props = new Properties(); - props.put(BOOTSTRAP_SERVERS_CONFIG, kafkaServer); - props.put(GROUP_ID_CONFIG, consumerGroup); - props.put(KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); - props.put(VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class.getName()); - return new KafkaConsumer(props); + return new KafkaProducer<>(props); } } diff --git a/kafka-dse-producer/src/main/java/com/datastax/demo/conf/SecurityConf.java b/kafka-dse-producer/src/main/java/com/datastax/kafkadse/producer/conf/SecurityConf.java similarity index 81% rename from kafka-dse-producer/src/main/java/com/datastax/demo/conf/SecurityConf.java rename to kafka-dse-producer/src/main/java/com/datastax/kafkadse/producer/conf/SecurityConf.java index 6cf51ef..1a7c4d4 100644 --- a/kafka-dse-producer/src/main/java/com/datastax/demo/conf/SecurityConf.java +++ b/kafka-dse-producer/src/main/java/com/datastax/kafkadse/producer/conf/SecurityConf.java @@ -1,11 +1,10 @@ -package com.datastax.demo.conf; +package com.datastax.kafkadse.producer.conf; -import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; /** SpringBoot Admin Console. */ -@Configuration +// @Configuration public class SecurityConf extends WebSecurityConfigurerAdapter { /** {@inheritDoc} */ diff --git a/kafka-dse-producer/src/main/java/com/datastax/demo/dao/AlphaVantageDao.java b/kafka-dse-producer/src/main/java/com/datastax/kafkadse/producer/dao/AlphaVantageDao.java similarity index 64% rename from kafka-dse-producer/src/main/java/com/datastax/demo/dao/AlphaVantageDao.java rename to kafka-dse-producer/src/main/java/com/datastax/kafkadse/producer/dao/AlphaVantageDao.java index 376f6f8..cbdc7f4 100644 --- a/kafka-dse-producer/src/main/java/com/datastax/demo/dao/AlphaVantageDao.java +++ b/kafka-dse-producer/src/main/java/com/datastax/kafkadse/producer/dao/AlphaVantageDao.java @@ -1,9 +1,8 @@ -package com.datastax.demo.dao; +package com.datastax.kafkadse.producer.dao; -import com.datastax.demo.domain.Stock; -import com.datastax.demo.domain.StockTick; +import com.datastax.kafkadse.core.domain.Stock; +import com.datastax.kafkadse.core.domain.StockTick; import java.time.ZoneId; -import java.util.Date; import java.util.Set; import java.util.stream.Stream; import javax.annotation.PostConstruct; @@ -12,7 +11,6 @@ import org.patriques.TimeSeries; import org.patriques.input.timeseries.Interval; import org.patriques.input.timeseries.OutputSize; -import org.patriques.output.quote.BatchStockQuotesResponse; import org.patriques.output.quote.data.StockQuote; import org.patriques.output.timeseries.data.StockData; import org.slf4j.Logger; @@ -46,13 +44,14 @@ public void initAlphaVantageConnector() { } public Stream getCurrentStockTicks(Set symbols) { - try { - BatchStockQuotesResponse response = clientStockApi.quote(symbols.toArray(new String[] {})); - return response.getStockQuotes().stream().map(this::mapStockQuoteAsStockTick); - } catch (RuntimeException re) { - LOGGER.error("Cannot get data."); + if (symbols == null || symbols.isEmpty()) { + throw new IllegalStateException("Symbol list is emptys"); } - return Stream.empty(); + return clientStockApi + .quote(symbols.toArray(new String[] {})) + .getStockQuotes() + .stream() + .map(AlphaVantageDao::mapStockQuoteAsStockTick); } public Stream getLastXStocks1Min(String symbol, int nbValue) { @@ -71,25 +70,28 @@ private Stream getStocks(Interval interval, String symbol, int nbValue) { .getStockData() .stream() .limit(nbValue) - .map(this::mapStockDataAsStockTick); + .map(item -> mapStockDataAsStock(symbol, item)); } catch (RuntimeException re) { + LOGGER.error("Cannot get stocks.", re); } return Stream.empty(); } - private StockTick mapStockQuoteAsStockTick(StockQuote q) { - // long real = q.getTimestamp().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); - return new StockTick(q.getSymbol(), q.getPrice(), System.currentTimeMillis()); + public static StockTick mapStockQuoteAsStockTick(StockQuote quote) { + return new StockTick( + quote.getSymbol(), + quote.getTimestamp().atZone(ZoneId.systemDefault()).toInstant(), + quote.getPrice()); } - private Stock mapStockDataAsStockTick(StockData item) { - Stock tick = new Stock(); - tick.setClose(item.getClose()); - tick.setOpen(item.getOpen()); - tick.setLow(item.getLow()); - tick.setHigh(item.getHigh()); - tick.setVolume(item.getVolume()); - tick.setValueDate(Date.from(item.getDateTime().atZone(ZoneId.systemDefault()).toInstant())); - return tick; + public static Stock mapStockDataAsStock(String symbol, StockData item) { + return new Stock( + symbol, + item.getDateTime().atZone(ZoneId.systemDefault()).toInstant(), + item.getOpen(), + item.getClose(), + item.getLow(), + item.getHigh(), + item.getVolume()); } } diff --git a/kafka-dse-producer/src/main/java/com/datastax/demo/dao/CsvDao.java b/kafka-dse-producer/src/main/java/com/datastax/kafkadse/producer/dao/CsvDao.java similarity index 84% rename from kafka-dse-producer/src/main/java/com/datastax/demo/dao/CsvDao.java rename to kafka-dse-producer/src/main/java/com/datastax/kafkadse/producer/dao/CsvDao.java index 2e770a2..245ed61 100644 --- a/kafka-dse-producer/src/main/java/com/datastax/demo/dao/CsvDao.java +++ b/kafka-dse-producer/src/main/java/com/datastax/kafkadse/producer/dao/CsvDao.java @@ -1,9 +1,9 @@ -package com.datastax.demo.dao; +package com.datastax.kafkadse.producer.dao; import static java.util.Spliterators.spliteratorUnknownSize; import static java.util.stream.StreamSupport.stream; -import com.datastax.demo.domain.StockInfo; +import com.datastax.kafkadse.core.domain.StockInfo; import com.fasterxml.jackson.dataformat.csv.CsvMapper; import com.fasterxml.jackson.dataformat.csv.CsvSchema; import java.io.File; @@ -17,7 +17,7 @@ public class CsvDao { @Value("${csvStocksMetadata}") - protected String csvFileName; + private String csvFileName; /** Init table stocks_infos (used in home page of webUI). */ public Stream readStockInfosFromCsv() { @@ -34,7 +34,7 @@ public Stream readStockInfosFromCsv() { Spliterator.ORDERED), false); } catch (IOException e) { - throw new IllegalArgumentException("Cannot filled table"); + throw new IllegalArgumentException("Cannot read file: " + csvFileName); } } } diff --git a/kafka-dse-producer/src/main/java/com/datastax/demo/route/StockTicksProducer.java b/kafka-dse-producer/src/main/java/com/datastax/kafkadse/producer/route/StockTicksProducer.java similarity index 58% rename from kafka-dse-producer/src/main/java/com/datastax/demo/route/StockTicksProducer.java rename to kafka-dse-producer/src/main/java/com/datastax/kafkadse/producer/route/StockTicksProducer.java index 1f17b90..f7bc689 100644 --- a/kafka-dse-producer/src/main/java/com/datastax/demo/route/StockTicksProducer.java +++ b/kafka-dse-producer/src/main/java/com/datastax/kafkadse/producer/route/StockTicksProducer.java @@ -1,11 +1,13 @@ -package com.datastax.demo.route; +package com.datastax.kafkadse.producer.route; -import com.datastax.demo.dao.AlphaVantageDao; -import com.datastax.demo.dao.DseDao; -import com.datastax.demo.dao.KafkaDao; -import com.datastax.demo.domain.StockTick; +import com.datastax.kafkadse.core.conf.DseConstants; +import com.datastax.kafkadse.core.dao.DseDao; +import com.datastax.kafkadse.core.domain.StockTick; +import com.datastax.kafkadse.producer.dao.AlphaVantageDao; +import com.datastax.kafkadse.producer.dao.CsvDao; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import java.time.Instant; import java.util.Map; import java.util.Set; import java.util.function.Function; @@ -13,28 +15,34 @@ import javax.annotation.PostConstruct; import org.apache.camel.Exchange; import org.apache.camel.Processor; +import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.ProducerRecord; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -/** Read Quotes values in RT and push to KAFKA. */ @Component("stockTicks.producer") -public class StockTicksProducer implements Processor { - - /** Json Jackson parser. */ - protected static final ObjectMapper JACKSON_MAPPER = new ObjectMapper(); +public class StockTicksProducer implements Processor, DseConstants { /** Internal logger. */ - protected final Logger LOGGER = LoggerFactory.getLogger(getClass().getName()); + private final Logger LOGGER = LoggerFactory.getLogger(getClass().getName()); @Autowired protected DseDao dseDao; + @Autowired protected CsvDao csvDao; + @Autowired protected AlphaVantageDao alphaVantageDao; - @Autowired protected KafkaDao kafkaDao; + @Autowired + @Qualifier("producer.mapper") + private ObjectMapper jacksonMapper; + + @Autowired + @Qualifier("producer.json") + private KafkaProducer jsonProducer; @Value("${alphavantage.waitTime: 100 }") protected int apiWaitTime; @@ -42,22 +50,25 @@ public class StockTicksProducer implements Processor { @Value("${kafka.topics.ticks}") private String topicTicks; - /** Symbols in CSV FILE. */ - protected Set symbols; - - /** Askl to get prices. */ - protected Map initialStockPrices = null; + /** Ask to get prices. */ + private Map initialStockPrices = null; /** Initialize connection to API AlphaVantage */ @PostConstruct public void init() { - symbols = dseDao.getSymbolsNYSE(); + // Loading Data into reference table (CSV->DSE) + csvDao.readStockInfosFromCsv().forEach(dseDao::saveStockInfoAsync); + LOGGER.info(" + Table {} filled with symbols found in CSV.", STOCKS_INFOS); + + // Initialize with Dse Data + Set symbols = dseDao.getSymbolsNYSE(); LOGGER.info("Symbols list retrieved from DSE. ({} items)", symbols.size()); initialStockPrices = alphaVantageDao .getCurrentStockTicks(symbols) .collect(Collectors.toMap(StockTick::getSymbol, Function.identity())); + LOGGER.info("Stocks initial prices retrieved from alphaVantage REST API."); } @@ -72,14 +83,14 @@ public void process(Exchange exchange) throws Exception { // Map to Avro Message .map(this::mapAsProducerRecord) // Send to Kafka - .forEach(kafkaDao::sendJsonMessage); + .forEach(jsonProducer::send); } - public ProducerRecord mapAsProducerRecord(StockTick sTick) { + private ProducerRecord mapAsProducerRecord(StockTick sTick) { sTick.setValue(createRandomValue(sTick.getValue())); - sTick.setValueDate(System.currentTimeMillis()); - JsonNode jsonValue = JACKSON_MAPPER.valueToTree(sTick); - return new ProducerRecord(topicTicks, sTick.getSymbol(), jsonValue); + sTick.setValueDate(Instant.now()); + JsonNode jsonValue = jacksonMapper.valueToTree(sTick); + return new ProducerRecord<>(topicTicks, sTick.getSymbol(), jsonValue); } /** Randomly making the stock evolving with random */ diff --git a/kafka-dse-producer/src/main/resources/application.yml b/kafka-dse-producer/src/main/resources/application.yml index 5d2074e..e9c52ce 100644 --- a/kafka-dse-producer/src/main/resources/application.yml +++ b/kafka-dse-producer/src/main/resources/application.yml @@ -4,24 +4,8 @@ spring: application: name: StockData Injector for Kafka - messages: - basename: i18n/messages - boot: - admin: - context-path: /admin - client: - url: - - "http://localhost:8088/admin" server: port: 8088 -management: - endpoints: - web: - exposure: - include: "*" - endpoint: - health: - show-details: ALWAYS # --------------------------------------------------------------------------------- # Apache Camel (integration fmk) @@ -38,10 +22,9 @@ camel: kafka: server: 127.0.0.1:9092 period: 2000 - group: tick-group ack: -1 topics: - ticks: stocks-ticks + ticks: stocks_ticks # --------------------------------------------------------------------------------- # DataStax Enterprise client infos @@ -49,7 +32,7 @@ kafka: dse: contactPoints: 127.0.0.1 port: 9042 - localdc: dc1 + localdc: DC1 keyspace: demo_kafka username: password: diff --git a/kafka-dse-producer/src/main/resources/banner.txt b/kafka-dse-producer/src/main/resources/banner.txt index b1a4478..09bc827 100644 --- a/kafka-dse-producer/src/main/resources/banner.txt +++ b/kafka-dse-producer/src/main/resources/banner.txt @@ -4,6 +4,9 @@ __________ .___ | | | | \( <_> ) /_/ | | /\ \__\ ___/| | \/ |____| |__| \____/\____ |____/ \___ >___ >__| \/ \/ \/ - Read Stocks from AlphaVantage and then, generate random values simulating evolutions + + Provided to you by DataStax Advocates team \_0_/ -Open your browser for monitoring : http://localhost:8088/admin#/wallboard + 1. Load a Stocks inventory from CSV File + 2. Init Stocks values from AlphaVantage API calls + 3. Generate random values and send to Kafka topic 'stocks_ticks' diff --git a/kafka-dse-producer/src/main/resources/logback.xml b/kafka-dse-producer/src/main/resources/logback.xml index c1d9e35..a6df44c 100644 --- a/kafka-dse-producer/src/main/resources/logback.xml +++ b/kafka-dse-producer/src/main/resources/logback.xml @@ -5,10 +5,13 @@ %d{HH:mm:ss.SSS} %magenta(%-5level) %cyan(%-45logger) : %msg%n - + - + + + + diff --git a/kafka-dse-producer/src/test/.DS_Store b/kafka-dse-producer/src/test/.DS_Store deleted file mode 100644 index 55c1fcb..0000000 Binary files a/kafka-dse-producer/src/test/.DS_Store and /dev/null differ diff --git a/kafka-dse-producer/src/test/java/.DS_Store b/kafka-dse-producer/src/test/java/.DS_Store deleted file mode 100644 index 35a54bf..0000000 Binary files a/kafka-dse-producer/src/test/java/.DS_Store and /dev/null differ diff --git a/kafka-dse-producer/src/test/java/com/.DS_Store b/kafka-dse-producer/src/test/java/com/.DS_Store deleted file mode 100644 index 804ace9..0000000 Binary files a/kafka-dse-producer/src/test/java/com/.DS_Store and /dev/null differ diff --git a/kafka-dse-producer/src/test/java/com/datastax/.DS_Store b/kafka-dse-producer/src/test/java/com/datastax/.DS_Store deleted file mode 100644 index 8067953..0000000 Binary files a/kafka-dse-producer/src/test/java/com/datastax/.DS_Store and /dev/null differ diff --git a/kafka-dse-producer/src/test/java/com/datastax/demo/.DS_Store b/kafka-dse-producer/src/test/java/com/datastax/demo/.DS_Store deleted file mode 100644 index ca5d0e6..0000000 Binary files a/kafka-dse-producer/src/test/java/com/datastax/demo/.DS_Store and /dev/null differ diff --git a/kafka-dse-producer/src/test/java/com/datastax/demo/test/LoadAlphaVantageDataJob.java b/kafka-dse-producer/src/test/java/com/datastax/demo/test/LoadAlphaVantageDataJob.java deleted file mode 100644 index b645755..0000000 --- a/kafka-dse-producer/src/test/java/com/datastax/demo/test/LoadAlphaVantageDataJob.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.datastax.demo.test; - -import com.datastax.demo.conf.DseConfiguration; -import com.datastax.demo.dao.DseDao; -import com.datastax.demo.domain.Stock; -import java.time.ZoneId; -import java.util.Date; -import org.junit.Ignore; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.platform.runner.JUnitPlatform; -import org.junit.runner.RunWith; -import org.patriques.AlphaVantageConnector; -import org.patriques.BatchStockQuotes; -import org.patriques.TimeSeries; -import org.patriques.input.timeseries.Interval; -import org.patriques.input.timeseries.OutputSize; -import org.patriques.output.quote.BatchStockQuotesResponse; -import org.patriques.output.quote.data.StockQuote; -import org.patriques.output.timeseries.data.StockData; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -@RunWith(JUnitPlatform.class) -@ExtendWith(SpringExtension.class) -@TestPropertySource(locations = "/config-test.properties") -@ContextConfiguration(classes = {DseConfiguration.class, DseDao.class}) -@Ignore -public class LoadAlphaVantageDataJob { - - @Value("${alphavantage.apiKey}") - private String apiKey; - - @Value("${alphavantage.timeout}") - private int apiTimeout; - - @Autowired private DseDao dseDao; - - private TimeSeries stockTimeSeries; - - @Test - public void runStocks() { - AlphaVantageConnector apiConnector = new AlphaVantageConnector("2HWDTH7BA7FRBP76", apiTimeout); - BatchStockQuotes bsq = new BatchStockQuotes(apiConnector); - BatchStockQuotesResponse res = bsq.quote(dseDao.getSymbolsNYSE().toArray(new String[] {})); - for (StockQuote sq : res.getStockQuotes()) { - System.out.println(sq.getSymbol() + "-" + sq.getTimestamp() + "-" + sq.getPrice()); - } - } - - @Test - @DisplayName("Test CSV Parson") - public void load1MinData() throws Exception { - AlphaVantageConnector apiConnector = new AlphaVantageConnector("2HWDTH7BA7FRBP76", apiTimeout); - stockTimeSeries = new TimeSeries(apiConnector); - for (String symbol : dseDao.getSymbolsNYSE()) { - System.out.println("Grabbing ... " + symbol); - Thread.sleep(1000); - try { - stockTimeSeries - .intraDay(symbol, Interval.ONE_MIN, OutputSize.FULL) - .getStockData() - .stream() - .map(item -> mapToTick(symbol, item)) - .forEach(dseDao::saveStock1Min); - - /*stockTimeSeries.intraDay(symbol, Interval.SIXTY_MIN, OutputSize.FULL) - .getStockData().stream() - .map(item -> mapToTick(symbol, item)) - .forEach(dseDao::saveStock1Hour);*/ - } catch (RuntimeException error) { - System.out.println("Error FOR " + symbol + " " + error.getMessage()); - } - } - } - - private Stock mapToTick(String symbol, StockData item) { - Stock tick = new Stock(); - tick.setSymbol(symbol); - tick.setClose(item.getClose()); - tick.setOpen(item.getOpen()); - tick.setLow(item.getLow()); - tick.setHigh(item.getHigh()); - tick.setVolume(item.getVolume()); - tick.setValueDate(Date.from(item.getDateTime().atZone(ZoneId.systemDefault()).toInstant())); - return tick; - } -} diff --git a/kafka-dse-producer/src/test/java/com/datastax/demo/test/TestSendMessage.java b/kafka-dse-producer/src/test/java/com/datastax/demo/test/TestSendMessage.java index afb09e4..316be51 100644 --- a/kafka-dse-producer/src/test/java/com/datastax/demo/test/TestSendMessage.java +++ b/kafka-dse-producer/src/test/java/com/datastax/demo/test/TestSendMessage.java @@ -4,9 +4,8 @@ import static org.apache.kafka.clients.producer.ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG; import static org.apache.kafka.clients.producer.ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG; -import com.datastax.demo.conf.ProducerConfiguration; -import com.datastax.demo.dao.KafkaDao; -import com.datastax.demo.domain.StockTick; +import com.datastax.kafkadse.core.domain.StockTick; +import com.datastax.kafkadse.producer.conf.ProducerConfiguration; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -18,11 +17,9 @@ import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.ProducerRecord; import org.apache.kafka.common.serialization.StringSerializer; -import org.junit.Ignore; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.platform.runner.JUnitPlatform; -import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; @@ -31,29 +28,26 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; /** - * LOCAL: kafka-topics --zookeeper localhost:2181 --list kafka-console-consumer --topic stocks-ticks - * --bootstrap-server localhost:9092 kafka-topics --zookeeper localhost:2181 --delete --topic - * stocks-ticks - * - *

O List available Topics : /opt/kafka/bin/kafka-topics.sh --zookeeper zookeeper:2181 --list - * - *

Consumer topic stock-ticks : /opt/kafka/bin/kafka-console-consumer.sh —-topic testTopic - * --zookeeper zookeeper:2181 - * - *

Create messages in Kafka : /opt/kafka/bin/kafka-console-producer.sh --broker-list - * localhost:9092 --topic testTopic + * List available Topics : + *

  • kafka-topics --zookeeper zookeeper:2181 --list
    + * To Delete a Topic: kafka-topics --zookeeper localhost:2181 --delete --topic
    + * To Write message in topic from console + *
  • kafka-console-producer --broker-list localhost:9092 --topic stocks-ticks + *
  • {"symbol":"HPE","valueDate":1550662132131,"value":16.395170844470908}
    + * To look messages in Console : + *
  • docker exec -i -t kafka-dse_kafka_1 kafka-console-consumer --bootstrap-server localhost:9092 + * --from-beginning --topic stocks-ticks * * @author cedricklunven */ -@RunWith(JUnitPlatform.class) @ExtendWith(SpringExtension.class) @TestPropertySource(locations = "/config-test.properties") -@ContextConfiguration(classes = {KafkaDao.class, ProducerConfiguration.class}) -@Ignore +@ContextConfiguration(classes = {ProducerConfiguration.class}) +@Disabled public class TestSendMessage { /** Json Jackson parser. */ - protected static final ObjectMapper JACKSON_MAPPER = new ObjectMapper(); + private static final ObjectMapper JACKSON_MAPPER = new ObjectMapper(); @Value("${kafka.topics.ticks}") private String topicTicks; @@ -67,11 +61,7 @@ public class TestSendMessage { private KafkaConsumer kafkaConsumer; @Test - public void sendMessage() throws InterruptedException { - StockTick sampleTick = new StockTick(); - sampleTick.setSymbol("MST"); - sampleTick.setValue(10.0); - sampleTick.setValueDate(System.currentTimeMillis()); + void sendMessage() { Properties props = new Properties(); props.put(BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); @@ -79,24 +69,27 @@ public void sendMessage() throws InterruptedException { props.put(VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); KafkaProducer p = new KafkaProducer<>(props); p.send( - new ProducerRecord( + new ProducerRecord<>( "stocks-ticks", "VLO", - "{\"symbol\":\"VLO\",\"valueDate\":1550244068123,\"value\":85.66046453803746}")); + "{\"symbol\":\"VLO\",\"CqlIdentifier\":1550244068123,\"value\":85.66046453803746}")); p.close(); - /* Send + /* + // Send + StockTick sampleTick = new StockTick("MST", Instant.now(), 10.0); JsonNode jsonValue = JACKSON_MAPPER.valueToTree(sampleTick); for (int i=0;i<7;i++) { jsonProducer.send(new ProducerRecord(topicTicks, sampleTick.getSymbol(), jsonValue)); System.out.println("Message sent to " + topicTicks); } - jsonProducer.close();*/ + jsonProducer.close(); + */ } @Test - public void receiveMessage() throws InterruptedException { + void receiveMessage() { // Subscription kafkaConsumer.subscribe(Collections.singletonList("stocks-ticks")); System.out.println("Subscription Started to " + topicTicks); @@ -106,7 +99,7 @@ public void receiveMessage() throws InterruptedException { kafkaConsumer.close(); } - public StockTick mapAsStockData(ConsumerRecord msg) { + StockTick mapAsStockData(ConsumerRecord msg) { try { return JACKSON_MAPPER.treeToValue(msg.value(), StockTick.class); } catch (JsonProcessingException e) { diff --git a/kafka-dse-producer/src/test/java/com/datastax/demo/test/TimeSeriesMachineLearning.java b/kafka-dse-producer/src/test/java/com/datastax/demo/test/TimeSeriesMachineLearning.java index 8565a1e..ff989ef 100644 --- a/kafka-dse-producer/src/test/java/com/datastax/demo/test/TimeSeriesMachineLearning.java +++ b/kafka-dse-producer/src/test/java/com/datastax/demo/test/TimeSeriesMachineLearning.java @@ -2,8 +2,8 @@ import static com.github.signaflo.data.visualization.Plots.plot; -import com.datastax.demo.conf.DseConfiguration; -import com.datastax.demo.dao.DseDao; +import com.datastax.kafkadse.core.conf.DseConfiguration; +import com.datastax.kafkadse.core.dao.DseDao; import com.github.signaflo.timeseries.TestData; import com.github.signaflo.timeseries.TimeSeries; import com.github.signaflo.timeseries.model.arima.Arima; @@ -11,20 +11,17 @@ import java.io.IOException; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.platform.runner.JUnitPlatform; -import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; -@RunWith(JUnitPlatform.class) @ExtendWith(SpringExtension.class) @TestPropertySource(locations = "/config-test.properties") @ContextConfiguration(classes = {DseConfiguration.class, DseDao.class}) -public class TimeSeriesMachineLearning { +class TimeSeriesMachineLearning { @Test - public void testForecastTimeSeries() throws IOException { + void testForecastTimeSeries() throws IOException { // Create a timeSeries from Data in DSE // OffsetDateTime startingDate = OffsetDateTime.of(LocalDateTime.of(2018, 11, 19, 0, 0), // ZoneOffset.ofHours(0)); diff --git a/kafka-dse-sink/README.MD b/kafka-dse-sink/README.MD new file mode 100644 index 0000000..d955a8a --- /dev/null +++ b/kafka-dse-sink/README.MD @@ -0,0 +1,7 @@ +Installation : + + - Please go to [DataStax Academy](https://academy.datastax.com) + - Download the *DataStax Apache Kafka™ Connector* on download page bloc **Connectors** + - Unzip the file `kafka-connect-dse-1.0.0.jar` in this folder + + \ No newline at end of file diff --git a/kafka-dse-sink/kafka-connect-dse-1.0.0/LICENSE.txt b/kafka-dse-sink/kafka-connect-dse-1.0.0/LICENSE.txt new file mode 100644 index 0000000..90c0ce5 --- /dev/null +++ b/kafka-dse-sink/kafka-connect-dse-1.0.0/LICENSE.txt @@ -0,0 +1,128 @@ +DataStax Apache Kafka (R) Connector License Terms + +Last Updated: December 5, 2018 + +PLEASE READ THIS DATASTAX APACHE KAFKA CONNECTOR TERMS (“AGREEMENT”) CAREFULLY. BY +CLICKING A BOX INDICATING ACCEPTANCE, DOWNLOADING OR USING THE SOFTWARE +DOWNLOADED (“Licensed Software”), YOU ACKNOWLEDGE THAT YOU HAVE READ THE +TERMS AND AGREE TO THEM. IF YOU ARE AGREEING TO THESE TERMS ON BEHALF OF A +COMPANY OR OTHER LEGAL ENTITY, YOU REPRESENT THAT YOU HAVE THE LEGAL AUTHORITY +TO BIND THE LEGAL ENTITY TO THESE TERMS. IF YOU DO NOT HAVE SUCH AUTHORITY, OR +IF YOU DO NOT WISH TO BE BOUND BY THE TERMS, THEN YOU MUST NOT USE THE LICENSED +SOFTWARE. + +1. Software License + +Subject to the terms and conditions of this Agreement, DataStax grants you a +non-exclusive, non-transferable, non-sublicensable, limited, royalty-free +license to use the unmodified Licensed Software, without the right to +re-distribute to any third party. The Licensed Software may be used for writing +data from any distribution of Apache Kafka (R) to any DataStax product for which +you have an active, paid subscription. + +2. Restrictions + +Licensed Software is confidential and copyrighted. Except as expressly permitted +under this Agreement, you agree not to assign, distribute, transfer, lease, +disassemble, decompile, reverse engineer, modify, or create derivative works of +the Licensed Software, in whole or in part, or permit or authorize a third party +to do so. + +3. Ownership + +The Licensed Software is licensed, not sold. DataStax retains title, ownership, +and all associated intellectual property of Licensed Software. You will not +delete or in any manner alter the copyright, trademark, and other proprietary +rights notices or markings appearing on the Licensed Software as delivered to +you. If the Licensed Software is being used by or on behalf of the U.S. +Government, then the U.S. Government’s rights in them will be only those +specified in this Agreement, consistent with FAR 12.212 and DFARS 227.7202-1 +through 227.7202-4, as applicable. + +4. Term + +This Agreement is effective until terminated. DataStax reserves the right, in +its sole discretion, to terminate this Agreement upon 30-days’ written notice. +You may terminate this Agreement at any time by destroying all copies of +Licensed Software. This Agreement will terminate immediately without notice from +DataStax if you fail to comply with any provision of this Agreement. Either +party may terminate this Agreement immediately should any Licensed Software +become, or in either party’s opinion be likely to become, the subject of a +claim of infringement of any intellectual property right. Upon termination, you +must destroy all copies of Licensed Software. + +5. Feedback + +If you provide any suggestions, feedback, or improvements to the Licensed +Software, DataStax will have the right to use and have others use such +suggestions, feedback, and improvements for any purpose. You hereby irrevocably +assign to DataStax all rights, title and interest in such suggestions, feedback, +or improvements. + +6. Disclaimer of Warranty + +Unless required by applicable law or agreed to in writing, DataStax provides +Licensed Software on an “as-is” basis. EXCEPT AS EXPRESSLY PROVIDED IN THIS +AGREEMENT, DATASTAX PROVIDES NO OTHER WARRANTIES REGARDING THE LICENSED +SOFTWARE, AND TO THE FULLEST EXTENT PERMITTED BY LAW DISCLAIMS ALL OTHER +WARRANTIES, TERMS AND CONDITIONS, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO ANY IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, QUALITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT, AND ANY WARRANTIES, TERMS AND +CONDITIONS ARISING OUT OF COURSE OF DEALING OR USAGE OF TRADE. NO ADVICE OR +INFORMATION, WHETHER ORAL OR WRITTEN, OBTAINED FROM DATASTAX OR ELSEWHERE WILL +CREATE ANY WARRANTY, TERM OR CONDITION UNLESS EXPRESSLY STATED IN THIS +AGREEMENT. + +7. Limitation of Liability + +To the fullest extent permitted by applicable law, in no event shall DataStax be +liable for the cost of procurement of substitute goods or technology, loss of +profits, or for any special, consequential, incidental, punitive or indirect +damages on any theory of liability, whether in contract, tort, strict liability +or otherwise, even if advised of the possibility of such damages. To the fullest +extent permitted by applicable law, in no event shall the total liability of +DataStax to you under this Agreement exceed one hundred US dollars ($100.00). + +8. Miscellaneous + +8.1. Assignment. You may not assign this Agreement by operation of law or +otherwise. DataStax may assign this Agreement upon written notice. + +8.2. Entire Agreement. This Agreement constitutes the complete Agreement +between the parties and supersedes all prior or contemporaneous agreements or +representations, written or oral, concerning the subject matter of this +Agreement. No modification of this Agreement will be binding, unless in writing +and signed by an authorized representative of each party. + +8.3. Export Controls. By using the Licensed Software, you agree to comply with +all import, export, and re-export restrictions and regulations of the United +States and other countries. + +8.4. Inspection. During the term of this Agreement and for one year thereafter, +DataStax or its designated agent may inspect your facilities and records to +verify your compliance with the Agreement. Any such inspection will take place +only during your normal business hours and upon no less than 15 days prior +written notice. + +8.5. Governing Law. This Agreement is to be construed in accordance with the +laws of the State of California and controlling U.S. federal laws, without +regard to the choice of law rules of any jurisdiction. + +8.6. Severability and Waiver. If any provision of this Agreement (or any +portion hereof) is held to be unenforceable, this Agreement will remain in +effect with the provision omitted, unless omission would frustrate the intent of +the parties. The waiver by either party of any default or breach of this +Agreement will not constitute a waiver of any other or subsequent default or +breach. + +8.7. Third Party Code. Additional copyright notices and license terms that may +be applicable to portions of the Licensed Software are set forth, if any, in a +THIRD-PARTY.txt file. + +9. Questions + +If you have questions, please contact us using the information +at www.datastax.com/contactus. + +Apache Kafka (R) is a registered trademark of The Apache Software Foundation + diff --git a/kafka-dse-sink/kafka-connect-dse-1.0.0/README.md b/kafka-dse-sink/kafka-connect-dse-1.0.0/README.md new file mode 100644 index 0000000..658f52a --- /dev/null +++ b/kafka-dse-sink/kafka-connect-dse-1.0.0/README.md @@ -0,0 +1,5 @@ +DataStax Apache® Kafka® Connector + +The DataStax Apache® Kafka® Connector enables users to ingest topics from Apache® Kafka® to tables in DataStax Enterprise. Visit the documentation for all connector information. + +https://docs.datastax.com/en/kafka/doc \ No newline at end of file diff --git a/kafka-dse-sink/kafka-connect-dse-1.0.0/THIRD-PARTY.txt b/kafka-dse-sink/kafka-connect-dse-1.0.0/THIRD-PARTY.txt new file mode 100644 index 0000000..c961df7 --- /dev/null +++ b/kafka-dse-sink/kafka-connect-dse-1.0.0/THIRD-PARTY.txt @@ -0,0 +1,80 @@ + +Lists of 78 third-party dependencies. + (Eclipse Public License, Version 1.0) (GNU Lesser General Public License) Logback Classic Module (ch.qos.logback:logback-classic:1.2.3 - http://logback.qos.ch/logback-classic) + (Eclipse Public License, Version 1.0) (GNU Lesser General Public License) Logback Core Module (ch.qos.logback:logback-core:1.2.3 - http://logback.qos.ch/logback-core) + (Apache License, Version 2.0) HPPC Collections (com.carrotsearch:hppc:0.7.1 - http://labs.carrotsearch.com/hppc.html/hppc) + (DataStax DSE Bulk Utility License) DataStax Bulk Loader - Commons (com.datastax.dse:dsbulk-commons:2.0.0-kafka-SNAPSHOT - https://github.com/riptano/dsbulk/dsbulk-commons) + (DataStax DSE Driver License) DataStax Enterprise Java Driver - Core (com.datastax.dse:dse-java-driver-core:2.0.0-alpha1 - http://docs.datastax.com/en/developer/java-driver-dse/dse-java-driver-core) + (DataStax DSE Driver License) Extension of the Apache Cassandra® native protocol for DataStax Enterprise (com.datastax.dse:dse-native-protocol:1.2.0 - http://docs.datastax.com/en/developer/java-driver-dse) + (DataStax DSE Bulk Utility License) DataStax Apache Kafka Connector - Sink (com.datastax.dse:kafka-connect-dse-sink:1.0.0 - https://github.com/riptano/kafka-sink/kafka-connect-dse-sink) + (Apache License, Version 2.0) DataStax Java driver for Apache Cassandra(R) - core (com.datastax.oss:java-driver-core:4.0.0-beta2 - https://github.com/datastax/java-driver/java-driver-core) + (Apache License, Version 2.0) Shaded Guava artifact for use in the DataStax Java driver for Apache Cassandra® (com.datastax.oss:java-driver-shaded-guava:25.1-jre - https://github.com/datastax/java-driver-shaded-guava) + (Apache License, Version 2.0) An implementation of the Apache Cassandra® native protocol (com.datastax.oss:native-protocol:1.4.4 - https://github.com/datastax/native-protocol) + (Apache License, Version 2.0) Esri Geometry API for Java (com.esri.geometry:esri-geometry-api:1.2.1 - https://github.com/Esri/geometry-api-java) + (Apache License, Version 2.0) Jackson-annotations (com.fasterxml.jackson.core:jackson-annotations:2.9.0 - http://github.com/FasterXML/jackson) + (Apache License, Version 2.0) Jackson-core (com.fasterxml.jackson.core:jackson-core:2.9.5 - https://github.com/FasterXML/jackson-core) + (Apache License, Version 2.0) jackson-databind (com.fasterxml.jackson.core:jackson-databind:2.9.5 - http://github.com/FasterXML/jackson) + (Apache License, Version 2.0) Caffeine cache (com.github.ben-manes.caffeine:caffeine:2.6.2 - https://github.com/ben-manes/caffeine) + (Apache License, Version 2.0) jffi (com.github.jnr:jffi:1.2.16 - http://github.com/jnr/jffi) + (Apache License, Version 2.0) jffi (com.github.jnr:jffi:1.2.17 - http://github.com/jnr/jffi) + (Apache License, Version 2.0) jnr-constants (com.github.jnr:jnr-constants:0.9.9 - http://github.com/jnr/jnr-constants) + (Apache License, Version 2.0) jnr-ffi (com.github.jnr:jnr-ffi:2.1.8 - http://github.com/jnr/jnr-ffi) + (Eclipse Public License, Version 1.0) (GNU General Public License Version 2) (GNU Lesser General Public License Version 2.1) jnr-posix (com.github.jnr:jnr-posix:3.0.46 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix) + (MIT License) jnr-x86asm (com.github.jnr:jnr-x86asm:1.0.2 - http://github.com/jnr/jnr-x86asm) + (Apache License, Version 2.0) JCIP Annotations under Apache License (com.github.stephenc.jcip:jcip-annotations:1.0-1 - http://stephenc.github.com/jcip-annotations) + (Apache License, Version 2.0) Guava: Google Core Libraries for Java (com.google.guava:guava:25.0-jre - https://github.com/google/guava/guava) + (BSD License) jcabi-log (com.jcabi:jcabi-log:0.14 - http://www.jcabi.com/jcabi-log) + (BSD License) jcabi-manifests (com.jcabi:jcabi-manifests:1.1 - http://www.jcabi.com/jcabi-manifests) + (Apache License, Version 2.0) JavaPoet (com.squareup:javapoet:1.8.0 - http://github.com/square/javapoet/) + (Apache License, Version 2.0) config (com.typesafe:config:1.3.3 - https://github.com/lightbend/config) + (Apache License, Version 2.0) Apache Commons Collections (commons-collections:commons-collections:3.2.2 - http://commons.apache.org/collections/) + (Apache License, Version 2.0) Apache Commons Configuration (commons-configuration:commons-configuration:1.10 - http://commons.apache.org/configuration/) + (Apache License, Version 2.0) Commons Lang (commons-lang:commons-lang:2.6 - http://commons.apache.org/lang/) + (Apache License, Version 2.0) Metrics Core (io.dropwizard.metrics:metrics-core:4.0.2 - http://metrics.dropwizard.io/metrics-core) + (Apache License, Version 2.0) Metrics Integration with JMX (io.dropwizard.metrics:metrics-jmx:4.0.2 - http://metrics.dropwizard.io/metrics-jmx) + (Apache License, Version 2.0) Netty/Buffer (io.netty:netty-buffer:4.1.27.Final - http://netty.io/netty-buffer/) + (Apache License, Version 2.0) Netty/Codec (io.netty:netty-codec:4.1.27.Final - http://netty.io/netty-codec/) + (Apache License, Version 2.0) Netty/Common (io.netty:netty-common:4.1.27.Final - http://netty.io/netty-common/) + (Apache License, Version 2.0) Netty/Handler (io.netty:netty-handler:4.1.27.Final - http://netty.io/netty-handler/) + (Apache License, Version 2.0) Netty/Resolver (io.netty:netty-resolver:4.1.27.Final - http://netty.io/netty-resolver/) + (Apache License, Version 2.0) Netty/TomcatNative [BoringSSL - Static] (io.netty:netty-tcnative-boringssl-static:2.0.15.Final - https://github.com/netty/netty-tcnative/netty-tcnative-boringssl-static/) + (Apache License, Version 2.0) Netty/Transport (io.netty:netty-transport:4.1.27.Final - http://netty.io/netty-transport/) + (Apache License, Version 2.0) Byte Buddy (without dependencies) (net.bytebuddy:byte-buddy:1.8.5 - http://bytebuddy.net/byte-buddy) + (Apache License, Version 2.0) Byte Buddy Java agent (net.bytebuddy:byte-buddy-agent:1.8.5 - http://bytebuddy.net/byte-buddy-agent) + (Apache License, Version 2.0) LZ4 and xxHash (net.jpountz.lz4:lz4:1.3.0 - https://github.com/jpountz/lz4-java) + (Apache License, Version 2.0) exp4j (net.objecthunter:exp4j:0.4.8 - http://www.objecthunter.net/exp4j) + (BSD License) ANTLR 4 Runtime (org.antlr:antlr4-runtime:4.7.1 - http://www.antlr.org/antlr4-runtime) + (Apache License, Version 2.0) Apache Commons Lang (org.apache.commons:commons-lang3:3.3.1 - http://commons.apache.org/proper/commons-lang/) + (Apache License, Version 2.0) Apache Kafka (org.apache.kafka:connect-api:1.1.0 - http://kafka.apache.org) + (Apache License, Version 2.0) Apache Kafka (org.apache.kafka:kafka-clients:1.1.0 - http://kafka.apache.org) + (Apache License, Version 2.0) Apache TinkerPop :: Gremlin Core (org.apache.tinkerpop:gremlin-core:3.3.3 - http://tinkerpop.apache.org/gremlin-core/) + (Apache License, Version 2.0) Apache TinkerPop :: Gremlin Shaded (org.apache.tinkerpop:gremlin-shaded:3.3.3 - http://tinkerpop.apache.org/gremlin-shaded/) + (Apache License, Version 2.0) Apache TinkerPop :: TinkerGraph Gremlin (org.apache.tinkerpop:tinkergraph-gremlin:3.3.3 - http://tinkerpop.apache.org/tinkergraph-gremlin/) + (The Apache License, Version 2.0) org.apiguardian:apiguardian-api (org.apiguardian:apiguardian-api:1.0.0 - https://github.com/apiguardian-team/apiguardian) + (Apache License, Version 2.0) AssertJ fluent assertions (org.assertj:assertj-core:3.10.0 - http://assertj.org/assertj-core) + (GNU General Public License, version 2 (GPL2), with the classpath exception) (MIT License) Checker Qual (org.checkerframework:checker-compat-qual:2.0.0 - http://checkerframework.org) + (Apache License, Version 2.0) Jackson (org.codehaus.jackson:jackson-core-asl:1.9.12 - http://jackson.codehaus.org) + (MIT License) Animal Sniffer Annotations (org.codehaus.mojo:animal-sniffer-annotations:1.14 - http://mojo.codehaus.org/animal-sniffer/animal-sniffer-annotations) + (BSD License) (Creative Commons CC0) HdrHistogram (org.hdrhistogram:HdrHistogram:2.1.10 - http://hdrhistogram.github.io/HdrHistogram/) + (Apache License, Version 2.0) javatuples (org.javatuples:javatuples:1.2 - http://www.javatuples.org) + (Apache License, Version 2.0) IntelliJ IDEA Annotations (org.jetbrains:annotations:15.0 - http://www.jetbrains.org) + (provided without support or warranty) JSON (JavaScript Object Notation) (org.json:json:20090211 - http://www.json.org/java/index.html) + (Eclipse Public License v2.0) JUnit Jupiter API (org.junit.jupiter:junit-jupiter-api:5.3.0-M1 - http://junit.org/junit5/) + (Eclipse Public License v2.0) JUnit Jupiter Engine (org.junit.jupiter:junit-jupiter-engine:5.3.0-M1 - http://junit.org/junit5/) + (Eclipse Public License v2.0) JUnit Jupiter Params (org.junit.jupiter:junit-jupiter-params:5.3.0-M1 - http://junit.org/junit5/) + (Eclipse Public License v2.0) JUnit Platform Commons (org.junit.platform:junit-platform-commons:1.3.0-M1 - http://junit.org/junit5/) + (Eclipse Public License v2.0) JUnit Platform Engine API (org.junit.platform:junit-platform-engine:1.3.0-M1 - http://junit.org/junit5/) + (Apache License, Version 2.0) LZ4 and xxHash (org.lz4:lz4-java:1.4 - https://github.com/lz4/lz4-java) + (MIT License) mockito-core (org.mockito:mockito-core:2.18.3 - https://github.com/mockito/mockito) + (Apache License, Version 2.0) Objenesis (org.objenesis:objenesis:2.6 - http://objenesis.org) + (The Apache License, Version 2.0) org.opentest4j:opentest4j (org.opentest4j:opentest4j:1.1.0 - https://github.com/ota4j-team/opentest4j) + (BSD License) ASM Core (org.ow2.asm:asm:5.0.3 - http://asm.objectweb.org/asm/) + (BSD License) ASM Analysis (org.ow2.asm:asm-analysis:5.0.3 - http://asm.objectweb.org/asm-analysis/) + (BSD License) ASM Commons (org.ow2.asm:asm-commons:5.0.3 - http://asm.objectweb.org/asm-commons/) + (BSD License) ASM Tree (org.ow2.asm:asm-tree:5.0.3 - http://asm.objectweb.org/asm-tree/) + (BSD License) ASM Util (org.ow2.asm:asm-util:5.0.3 - http://asm.objectweb.org/asm-util/) + (MIT License) JCL 1.1.1 implemented over SLF4J (org.slf4j:jcl-over-slf4j:1.7.21 - http://www.slf4j.org) + (MIT License) JUL to SLF4J bridge (org.slf4j:jul-to-slf4j:1.7.25 - http://www.slf4j.org) + (MIT License) SLF4J API Module (org.slf4j:slf4j-api:1.7.25 - http://www.slf4j.org) + (Apache License, Version 2.0) snappy-java (org.xerial.snappy:snappy-java:1.1.7.2 - https://github.com/xerial/snappy-java) + (Apache License Version 2.0) SnakeYAML (org.yaml:snakeyaml:1.15 - http://www.snakeyaml.org) diff --git a/kafka-dse-sink/kafka-connect-dse-1.0.0/conf/dse-sink-distributed.json.sample b/kafka-dse-sink/kafka-connect-dse-1.0.0/conf/dse-sink-distributed.json.sample new file mode 100644 index 0000000..93bf49a --- /dev/null +++ b/kafka-dse-sink/kafka-connect-dse-1.0.0/conf/dse-sink-distributed.json.sample @@ -0,0 +1,43 @@ +{ + "name": "dse-sink", + "config": { + "connector.class": "com.datastax.kafkaconnector.DseSinkConnector", + "tasks.max": "1", + "topics": "my_topic", + "contactPoints": "", + "loadBalancing.localDc": "", + "port": 9042, + "maxConcurrentRequests": 500, + "maxNumberOfRecordsInBatch": 32, + "queryExecutionTimeout": 30, + "connectionPoolLocalSize": 4, + "jmx": true, + "compression": "None", + "auth.provider": "None", + "auth.username": "", + "auth.password": "", + "auth.gssapi.keyTab": "", + "auth.gssapi.principal": "", + "auth.gssapi.service": "dse", + "ssl.provider": "None", + "ssl.hostnameValidation": true, + "ssl.keystore.password": "", + "ssl.keystore.path": "", + "ssl.openssl.keyCertChain": "", + "ssl.openssl.privateKey": "", + "ssl.truststore.password": "", + "ssl.truststore.path": "", + "ssl.cipherSuites": "", + "topic.my_topic.my_ks.my_table.mapping": "col1=key.f1, col2=value.f1", + "topic.my_topic.my_ks.my_table.consistencyLevel": "LOCAL_ONE", + "topic.my_topic.my_ks.my_table.ttl": -1, + "topic.my_topic.my_ks.my_table.nullToUnset": "true", + "topic.my_topic.my_ks.my_table.deletesEnabled": "true", + "topic.my_topic.codec.locale": "en_US", + "topic.my_topic.codec.timeZone": "UTC", + "topic.my_topic.codec.timestamp": "CQL_TIMESTAMP", + "topic.my_topic.codec.date": "ISO_LOCAL_DATE", + "topic.my_topic.codec.time": "ISO_LOCAL_TIME", + "topic.my_topic.codec.unit": "MILLISECONDS" + } +} diff --git a/kafka-dse-conf/dse-sink.properties b/kafka-dse-sink/kafka-connect-dse-1.0.0/conf/dse-sink-standalone.properties.sample similarity index 50% rename from kafka-dse-conf/dse-sink.properties rename to kafka-dse-sink/kafka-connect-dse-1.0.0/conf/dse-sink-standalone.properties.sample index d5e181a..1f2f2b5 100644 --- a/kafka-dse-conf/dse-sink.properties +++ b/kafka-dse-sink/kafka-connect-dse-1.0.0/conf/dse-sink-standalone.properties.sample @@ -3,38 +3,116 @@ connector.class=com.datastax.kafkaconnector.DseSinkConnector tasks.max=1 # Topics to subscribe to, comma-delimited. -topics=tick-stream +topics=my_topic # Comma-separated list of DSE cluster contact points. Used by the DataStax Driver to discover # DSE cluster topology. Defaults to empty, meaning localhost. -contactPoints=localhost +#contactPoints=[] # DSE data center name to which the DataStax Driver connects. Required if contactPoints # is specified. -loadBalancing.localDc=dc1 +#loadBalancing.localDc= # Native transport port to connect to on DSE nodes; defaults to 9042. -port=9042 +#port=9042 # Maximum number of requests to send to DSE at a single time. Defaults to 500. #maxConcurrentRequests=500 +# Maximum number of records that could be send in one batch request to DSE +#maxNumberOfRecordsInBatch=32 + +# Number of connections that driver maintains within a connection pool to each node in local dc +#connectionPoolLocalSize=4 + +# CQL statement execution timeout, in seconds. Defaults to 30 seconds. +#queryExecutionTimeout=30 + +# Whether or not to enable stats reporting through JMX. Defaults to true. +#jmx=true + +# Compression algorithm to use when issuing requests to DSE. Valid values are +# None, Snappy, LZ4. Defaults to None. +#compression=None + +### Authentication Setings ### + +# Authentication provider to use, if any. Valid choices: None, DSE, GSSAPI. +# Defaults to None. +#auth.provider=None + +# Username for DSE provider authentication +#auth.username= + +# Password for DSE provider authentication +#auth.password= + +# Kerberos keytab file +#auth.gssapi.keyTab= + +# Kerberos principal +#auth.gssapi.principal= + +# SASL service name to use for GSSAPI provider authentication. +# Defautls to dse. +#auth.gssapi.service=dse + +### SSL Settings ### + +# SSL provider to use, if any. Valid choices: None, JDK, OpenSSL. +# Defaults to None. +#ssl.provider=None + +# Comma-separated list of cipher suites to enable. +#ssl.cipherSuites= + +# Whether or not to validate DSE node hostnames when using SSL. +# Defaults to true. +#ssl.hostnameValidation=true + +# Keystore password. +#ssl.keystore.password= + +# Path to the keystore file. +#ssl.keystore.path= + +# Truststore password. +#ssl.truststore.password= + +# Path to the truststore file. +#ssl.truststore.path= + +# Path to the SSL certificate file, when using OpenSSL. +#ssl.openssl.keyCertChain= + +# Path to the private key file, when using OpenSSL. +#ssl.openssl.privateKey= + ### Settings for topic my_topic ### -# Mapping for String -topic.tick-stream.keyspace=datastax_tickdata_demo -topic.tick-stream.table=tick_data -topic.tick-stream.mapping=symbol=key, value=value -topic.tick-stream.consistencyLevel=LOCAL_ONE +#### Settings for table my_ks.my_table in topic my_topic #### + +# Kafka record field to DSE table column mapping spec, in the form 'col1=value.f1, col2=key.f1'. +topic.my_topic.my_ks.my_table.mapping=col1=key.f1, col2=value.f1 + +# Query consistency level; defaults to LOCAL_ONE. +#topic.my_topic.my_ks.my_table.consistencyLevel=LOCAL_ONE # Time-to-live. Set to the number of seconds before the data is automatically deleted in DSE. # Defaults to -1 (disabled). -#topic.my_topic.ttl=-1 +#topic.my_topic.my_ks.my_table.ttl=-1 # Whether to treat nulls in Kafka as UNSET in DSE. DataStax recommends using the default to # avoid creating unnecessary tombstones. Defaults to true. -#topic.my_topic.nullToUnset=true +#topic.my_topic.my_ks.my_table.nullToUnset=true + +# When enabled, treat records that after mapping would result in only non-null values for +# primary key columns as deletes, rather than inserting/updating nulls for all regular columns. +# Note that for this behavior to trigger, the mapping specification must map all columns in the +# table. Defaults to true. +#topic.my_topic.my_ks.my_table.deletesEnabled=true +#### Record decoding settings in topic my_topic #### # Locale to use for locale-sensitive conversions. Defaults to en_US. #topic.my_topic.codec.locale=en_US diff --git a/kafka-dse-webui/.DS_Store b/kafka-dse-webui/.DS_Store deleted file mode 100644 index 5801cb5..0000000 Binary files a/kafka-dse-webui/.DS_Store and /dev/null differ diff --git a/kafka-dse-webui/.classpath b/kafka-dse-webui/.classpath deleted file mode 100644 index 0ca1374..0000000 --- a/kafka-dse-webui/.classpath +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/kafka-dse-webui/.factorypath b/kafka-dse-webui/.factorypath index 6fbf177..27c6052 100644 --- a/kafka-dse-webui/.factorypath +++ b/kafka-dse-webui/.factorypath @@ -1,13 +1,25 @@ - - - - - + + + + + + + + + + + + + + + + + @@ -15,34 +27,14 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + @@ -62,25 +54,16 @@ - - - + + - - - - - - - - @@ -98,6 +81,7 @@ + @@ -106,25 +90,17 @@ + + + + + - - - - - - - - - - - - - diff --git a/kafka-dse-webui/.project b/kafka-dse-webui/.project deleted file mode 100644 index 880a692..0000000 --- a/kafka-dse-webui/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - kafka-dse-webui - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - org.springframework.ide.eclipse.boot.validation.springbootbuilder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.m2e.core.maven2Nature - - diff --git a/kafka-dse-webui/.settings/org.eclipse.jdt.apt.core.prefs b/kafka-dse-webui/.settings/org.eclipse.jdt.apt.core.prefs deleted file mode 100644 index dfa4f3a..0000000 --- a/kafka-dse-webui/.settings/org.eclipse.jdt.apt.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.apt.aptEnabled=true -org.eclipse.jdt.apt.genSrcDir=target/generated-sources/annotations -org.eclipse.jdt.apt.genTestSrcDir=target/generated-test-sources/test-annotations diff --git a/kafka-dse-webui/.settings/org.eclipse.jdt.core.prefs b/kafka-dse-webui/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 21227d9..0000000 --- a/kafka-dse-webui/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,7 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 -org.eclipse.jdt.core.compiler.compliance=1.8 -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.processAnnotations=enabled -org.eclipse.jdt.core.compiler.release=disabled -org.eclipse.jdt.core.compiler.source=1.8 diff --git a/kafka-dse-webui/.settings/org.eclipse.m2e.core.prefs b/kafka-dse-webui/.settings/org.eclipse.m2e.core.prefs deleted file mode 100644 index f897a7f..0000000 --- a/kafka-dse-webui/.settings/org.eclipse.m2e.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -activeProfiles= -eclipse.preferences.version=1 -resolveWorkspaceProjects=true -version=1 diff --git a/kafka-dse-webui/.settings/org.springframework.ide.eclipse.prefs b/kafka-dse-webui/.settings/org.springframework.ide.eclipse.prefs deleted file mode 100644 index a12794d..0000000 --- a/kafka-dse-webui/.settings/org.springframework.ide.eclipse.prefs +++ /dev/null @@ -1,2 +0,0 @@ -boot.validation.initialized=true -eclipse.preferences.version=1 diff --git a/kafka-dse-webui/bin/pom.xml b/kafka-dse-webui/bin/pom.xml new file mode 100644 index 0000000..48433a5 --- /dev/null +++ b/kafka-dse-webui/bin/pom.xml @@ -0,0 +1,113 @@ + + + 4.0.0 + kafka-dse-webui + + kafka-dse-webui + DAO, BEAN + + com.datastax + kafka-dse-example + 6.7-SNAPSHOT + + + + + com.datastax + kafka-dse-core + ${project.version} + + + + com.datastax.dse + dse-java-driver-core + + + com.datastax.oss + java-driver-core + + + com.datastax.oss + java-driver-query-builder + + + + org.springframework.boot + spring-boot-starter-webflux + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-devtools + true + + + + org.webjars + bootstrap + 3.3.7 + runtime + + + org.webjars + highcharts + 5.0.8 + runtime + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.springframework.boot + spring-boot-starter-test + test + + + junit + junit + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + diff --git a/kafka-dse-webui/bin/src/main/java/com/datastax/kafkadse/web/WebUiApplication.class b/kafka-dse-webui/bin/src/main/java/com/datastax/kafkadse/web/WebUiApplication.class new file mode 100644 index 0000000..28794ba Binary files /dev/null and b/kafka-dse-webui/bin/src/main/java/com/datastax/kafkadse/web/WebUiApplication.class differ diff --git a/kafka-dse-webui/bin/src/main/java/com/datastax/kafkadse/web/conf/WebUiConfiguration.class b/kafka-dse-webui/bin/src/main/java/com/datastax/kafkadse/web/conf/WebUiConfiguration.class new file mode 100644 index 0000000..56ca1be Binary files /dev/null and b/kafka-dse-webui/bin/src/main/java/com/datastax/kafkadse/web/conf/WebUiConfiguration.class differ diff --git a/kafka-dse-webui/bin/src/main/java/com/datastax/kafkadse/web/controller/ChartController.class b/kafka-dse-webui/bin/src/main/java/com/datastax/kafkadse/web/controller/ChartController.class new file mode 100644 index 0000000..ed9bfb1 Binary files /dev/null and b/kafka-dse-webui/bin/src/main/java/com/datastax/kafkadse/web/controller/ChartController.class differ diff --git a/kafka-dse-webui/bin/src/main/java/com/datastax/kafkadse/web/controller/HomeController.class b/kafka-dse-webui/bin/src/main/java/com/datastax/kafkadse/web/controller/HomeController.class new file mode 100644 index 0000000..1876879 Binary files /dev/null and b/kafka-dse-webui/bin/src/main/java/com/datastax/kafkadse/web/controller/HomeController.class differ diff --git a/kafka-dse-webui/bin/src/main/java/com/datastax/kafkadse/web/controller/MultiChartController.class b/kafka-dse-webui/bin/src/main/java/com/datastax/kafkadse/web/controller/MultiChartController.class new file mode 100644 index 0000000..e3e9c64 Binary files /dev/null and b/kafka-dse-webui/bin/src/main/java/com/datastax/kafkadse/web/controller/MultiChartController.class differ diff --git a/kafka-dse-webui/bin/src/main/java/com/datastax/kafkadse/web/controller/TickerController.class b/kafka-dse-webui/bin/src/main/java/com/datastax/kafkadse/web/controller/TickerController.class new file mode 100644 index 0000000..884a654 Binary files /dev/null and b/kafka-dse-webui/bin/src/main/java/com/datastax/kafkadse/web/controller/TickerController.class differ diff --git a/kafka-dse-webui/bin/src/main/java/com/datastax/kafkadse/web/dao/DseWebUiDao.class b/kafka-dse-webui/bin/src/main/java/com/datastax/kafkadse/web/dao/DseWebUiDao.class new file mode 100644 index 0000000..3a19f91 Binary files /dev/null and b/kafka-dse-webui/bin/src/main/java/com/datastax/kafkadse/web/dao/DseWebUiDao.class differ diff --git a/kafka-dse-webui/bin/src/main/resources/application.yml b/kafka-dse-webui/bin/src/main/resources/application.yml new file mode 100644 index 0000000..5aff557 --- /dev/null +++ b/kafka-dse-webui/bin/src/main/resources/application.yml @@ -0,0 +1,22 @@ +# ---------------------------------------------------------- +# Spring Boot extra Config +# ---------------------------------------------------------- +spring: + application: + name: WebUI for reactive streams + messages: + basename: messages +server: + port: 8082 + +# ---------------------------------------------------------- +# Connectivity to DataStax Enterprise +# ---------------------------------------------------------- +dse: + contactPoints: 127.0.0.1 + port: 9042 + localdc: DC1 + username: + password: + keyspace: demo_kafka + \ No newline at end of file diff --git a/kafka-dse-webui/bin/src/main/resources/banner.txt b/kafka-dse-webui/bin/src/main/resources/banner.txt new file mode 100644 index 0000000..b682fc1 --- /dev/null +++ b/kafka-dse-webui/bin/src/main/resources/banner.txt @@ -0,0 +1,8 @@ + __ __ _ _____ +/ / /\ \ \___| |__ /\ /\ \_ \ +\ \/ \/ / _ \ '_ \/ / \ \ / /\/ + \ /\ / __/ |_) \ \_/ /\/ /_ + \/ \/ \___|_.__/ \___/\____/ + + Read Tick in DSE in realtime as Reactive + \ No newline at end of file diff --git a/kafka-dse-webui/bin/src/main/resources/logback.xml b/kafka-dse-webui/bin/src/main/resources/logback.xml new file mode 100644 index 0000000..e969ad4 --- /dev/null +++ b/kafka-dse-webui/bin/src/main/resources/logback.xml @@ -0,0 +1,17 @@ + + + + + %d{HH:mm:ss.SSS} %magenta(%-5level) %cyan(%-45logger) : %msg%n + + + + + + + + + + + + diff --git a/kafka-dse-webui/bin/src/main/resources/messages.properties b/kafka-dse-webui/bin/src/main/resources/messages.properties new file mode 100644 index 0000000..79dca5c --- /dev/null +++ b/kafka-dse-webui/bin/src/main/resources/messages.properties @@ -0,0 +1,21 @@ +# ----------------- # +# NAVBARS # +# ----------------- # +navbar.title=Reactive WebApp Demo + +# Navbar of home page +navbar.main.home=Home + +# 500 Page +error.table.key=Session Key +error.table.value=Value +500.title=An Error occured +500.subtitle=Please go back to home +500.back=Get back to home + +# 404 Page +404.title=Page not Found +404.subtitle=This page has not been found +404.back=Get back to home + + diff --git a/kafka-dse-webui/bin/src/main/resources/static/css/bootstrap-responsive.min.css b/kafka-dse-webui/bin/src/main/resources/static/css/bootstrap-responsive.min.css new file mode 100644 index 0000000..60a47c9 --- /dev/null +++ b/kafka-dse-webui/bin/src/main/resources/static/css/bootstrap-responsive.min.css @@ -0,0 +1,12 @@ +.clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";} +.clearfix:after{clear:both;} +.hide-text{overflow:hidden;text-indent:100%;white-space:nowrap;} +.input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;} +.hidden{display:none;visibility:hidden;} +.visible-phone{display:none;} +.visible-tablet{display:none;} +.visible-desktop{display:block;} +.hidden-phone{display:block;} +.hidden-tablet{display:block;} +.hidden-desktop{display:none;} +@media (max-width:767px){.visible-phone{display:block;} .hidden-phone{display:none;} .hidden-desktop{display:block;} .visible-desktop{display:none;}}@media (min-width:768px) and (max-width:979px){.visible-tablet{display:block;} .hidden-tablet{display:none;} .hidden-desktop{display:block;} .visible-desktop{display:none;}}@media (max-width:480px){.nav-collapse{-webkit-transform:translate3d(0, 0, 0);} .page-header h1 small{display:block;line-height:18px;} input[type="checkbox"],input[type="radio"]{border:1px solid #ccc;} .form-horizontal .control-group>label{float:none;width:auto;padding-top:0;text-align:left;} .form-horizontal .controls{margin-left:0;} .form-horizontal .control-list{padding-top:0;} .form-horizontal .form-actions{padding-left:10px;padding-right:10px;} .modal{position:absolute;top:10px;left:10px;right:10px;width:auto;margin:0;}.modal.fade.in{top:auto;} .modal-header .close{padding:10px;margin:-10px;} .carousel-caption{position:static;}}@media (max-width:767px){body{padding-left:20px;padding-right:20px;} .navbar-fixed-top{margin-left:-20px;margin-right:-20px;} .container{width:auto;} .row-fluid{width:100%;} .row{margin-left:0;} .row>[class*="span"],.row-fluid>[class*="span"]{float:none;display:block;width:auto;margin:0;} .thumbnails [class*="span"]{width:auto;} input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;} .input-prepend input[class*="span"],.input-append input[class*="span"]{width:auto;}}@media (min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:20px;} .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px;} .span12{width:724px;} .span11{width:662px;} .span10{width:600px;} .span9{width:538px;} .span8{width:476px;} .span7{width:414px;} .span6{width:352px;} .span5{width:290px;} .span4{width:228px;} .span3{width:166px;} .span2{width:104px;} .span1{width:42px;} .offset12{margin-left:764px;} .offset11{margin-left:702px;} .offset10{margin-left:640px;} .offset9{margin-left:578px;} .offset8{margin-left:516px;} .offset7{margin-left:454px;} .offset6{margin-left:392px;} .offset5{margin-left:330px;} .offset4{margin-left:268px;} .offset3{margin-left:206px;} .offset2{margin-left:144px;} .offset1{margin-left:82px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid>[class*="span"]{float:left;margin-left:2.762430939%;} .row-fluid>[class*="span"]:first-child{margin-left:0;} .row-fluid > .span12{width:99.999999993%;} .row-fluid > .span11{width:91.436464082%;} .row-fluid > .span10{width:82.87292817100001%;} .row-fluid > .span9{width:74.30939226%;} .row-fluid > .span8{width:65.74585634900001%;} .row-fluid > .span7{width:57.182320438000005%;} .row-fluid > .span6{width:48.618784527%;} .row-fluid > .span5{width:40.055248616%;} .row-fluid > .span4{width:31.491712705%;} .row-fluid > .span3{width:22.928176794%;} .row-fluid > .span2{width:14.364640883%;} .row-fluid > .span1{width:5.801104972%;} input,textarea,.uneditable-input{margin-left:0;} input.span12, textarea.span12, .uneditable-input.span12{width:714px;} input.span11, textarea.span11, .uneditable-input.span11{width:652px;} input.span10, textarea.span10, .uneditable-input.span10{width:590px;} input.span9, textarea.span9, .uneditable-input.span9{width:528px;} input.span8, textarea.span8, .uneditable-input.span8{width:466px;} input.span7, textarea.span7, .uneditable-input.span7{width:404px;} input.span6, textarea.span6, .uneditable-input.span6{width:342px;} input.span5, textarea.span5, .uneditable-input.span5{width:280px;} input.span4, textarea.span4, .uneditable-input.span4{width:218px;} input.span3, textarea.span3, .uneditable-input.span3{width:156px;} input.span2, textarea.span2, .uneditable-input.span2{width:94px;} input.span1, textarea.span1, .uneditable-input.span1{width:32px;}}@media (max-width:979px){body{padding-top:0;} .navbar-fixed-top{position:static;margin-bottom:18px;} .navbar-fixed-top .navbar-inner{padding:5px;} .navbar .container{width:auto;padding:0;} .navbar .brand{padding-left:10px;padding-right:10px;margin:0 0 0 -5px;} .navbar .nav-collapse{clear:left;} .navbar .nav{float:none;margin:0 0 9px;} .navbar .nav>li{float:none;} .navbar .nav>li>a{margin-bottom:2px;} .navbar .nav>.divider-vertical{display:none;} .navbar .nav .nav-header{color:#999999;text-shadow:none;} .navbar .nav>li>a,.navbar .dropdown-menu a{padding:6px 15px;font-weight:bold;color:#999999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} .navbar .dropdown-menu li+li a{margin-bottom:2px;} .navbar .nav>li>a:hover,.navbar .dropdown-menu a:hover{background-color:#222222;} .navbar .dropdown-menu{position:static;top:auto;left:auto;float:none;display:block;max-width:none;margin:0 15px;padding:0;background-color:transparent;border:none;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} .navbar .dropdown-menu:before,.navbar .dropdown-menu:after{display:none;} .navbar .dropdown-menu .divider{display:none;} .navbar-form,.navbar-search{float:none;padding:9px 15px;margin:9px 0;border-top:1px solid #222222;border-bottom:1px solid #222222;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);} .navbar .nav.pull-right{float:none;margin-left:0;} .navbar-static .navbar-inner{padding-left:10px;padding-right:10px;} .btn-navbar{display:block;} .nav-collapse{overflow:hidden;height:0;}}@media (min-width:980px){.nav-collapse.collapse{height:auto !important;overflow:visible !important;}}@media (min-width:1200px){.row{margin-left:-30px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:30px;} .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px;} .span12{width:1170px;} .span11{width:1070px;} .span10{width:970px;} .span9{width:870px;} .span8{width:770px;} .span7{width:670px;} .span6{width:570px;} .span5{width:470px;} .span4{width:370px;} .span3{width:270px;} .span2{width:170px;} .span1{width:70px;} .offset12{margin-left:1230px;} .offset11{margin-left:1130px;} .offset10{margin-left:1030px;} .offset9{margin-left:930px;} .offset8{margin-left:830px;} .offset7{margin-left:730px;} .offset6{margin-left:630px;} .offset5{margin-left:530px;} .offset4{margin-left:430px;} .offset3{margin-left:330px;} .offset2{margin-left:230px;} .offset1{margin-left:130px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid>[class*="span"]{float:left;margin-left:2.564102564%;} .row-fluid>[class*="span"]:first-child{margin-left:0;} .row-fluid > .span12{width:100%;} .row-fluid > .span11{width:91.45299145300001%;} .row-fluid > .span10{width:82.905982906%;} .row-fluid > .span9{width:74.358974359%;} .row-fluid > .span8{width:65.81196581200001%;} .row-fluid > .span7{width:57.264957265%;} .row-fluid > .span6{width:48.717948718%;} .row-fluid > .span5{width:40.170940171000005%;} .row-fluid > .span4{width:31.623931624%;} .row-fluid > .span3{width:23.076923077%;} .row-fluid > .span2{width:14.529914530000001%;} .row-fluid > .span1{width:5.982905983%;} input,textarea,.uneditable-input{margin-left:0;} input.span12, textarea.span12, .uneditable-input.span12{width:1160px;} input.span11, textarea.span11, .uneditable-input.span11{width:1060px;} input.span10, textarea.span10, .uneditable-input.span10{width:960px;} input.span9, textarea.span9, .uneditable-input.span9{width:860px;} input.span8, textarea.span8, .uneditable-input.span8{width:760px;} input.span7, textarea.span7, .uneditable-input.span7{width:660px;} input.span6, textarea.span6, .uneditable-input.span6{width:560px;} input.span5, textarea.span5, .uneditable-input.span5{width:460px;} input.span4, textarea.span4, .uneditable-input.span4{width:360px;} input.span3, textarea.span3, .uneditable-input.span3{width:260px;} input.span2, textarea.span2, .uneditable-input.span2{width:160px;} input.span1, textarea.span1, .uneditable-input.span1{width:60px;} .thumbnails{margin-left:-30px;} .thumbnails>li{margin-left:30px;}} diff --git a/kafka-dse-webui/bin/src/main/resources/static/css/bootstrap.min.css b/kafka-dse-webui/bin/src/main/resources/static/css/bootstrap.min.css new file mode 100644 index 0000000..d76398e --- /dev/null +++ b/kafka-dse-webui/bin/src/main/resources/static/css/bootstrap.min.css @@ -0,0 +1,3655 @@ +article, aside, details, figcaption, figure, footer, header, hgroup, nav, section { + display: block; +} +audio, canvas, video { + display: inline-block; +*display:inline; +*zoom:1; +} +audio:not([controls]) { + display: none; +} +html { + font-size: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +a:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +a:hover, a:active { + outline: 0; +} +sub, sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} +sup { + top: -0.5em; +} +sub { + bottom: -0.25em; +} +img { + height: auto; + border: 0; + -ms-interpolation-mode: bicubic; + vertical-align: middle; +} +button, input, select, textarea { + margin: 0; + font-size: 100%; + vertical-align: middle; +} +button, input { +*overflow:visible; + line-height: normal; +} +button::-moz-focus-inner, input::-moz-focus-inner { +padding:0; +border:0; +} +button, input[type="button"], input[type="reset"], input[type="submit"] { + cursor: pointer; + -webkit-appearance: button; +} +input[type="search"] { + -webkit-appearance: textfield; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} +input[type="search"]::-webkit-search-decoration, input[type="search"]::-webkit-search-cancel-button { +-webkit-appearance:none; +} +textarea { + overflow: auto; + vertical-align: top; +} +.clearfix { +*zoom:1; +} +.clearfix:before, .clearfix:after { + display: table; + content: ""; +} +.clearfix:after { + clear: both; +} +.hide-text { + overflow: hidden; + text-indent: 100%; + white-space: nowrap; +} +.input-block-level { + display: block; + width: 100%; + min-height: 28px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; +} +body { + margin: 0; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 13px; + line-height: 18px; + color: #333333; + background-color: #ffffff; +} +a { + color: #19bc9c; + text-decoration: none; +} +a:hover { + color: #545454; + text-decoration: underline; +} +.row { + margin-left: -20px; +*zoom:1; +} +.row:before, .row:after { + display: table; + content: ""; +} +.row:after { + clear: both; +} +[class*="span"] { +float:left; +margin-left:20px; +} +.container, .navbar-fixed-top .container, .navbar-fixed-bottom .container { + width: 940px; +} +.span12 { + width: 940px; +} +.span11 { + width: 860px; +} +.span10 { + width: 780px; +} +.span9 { + width: 700px; +} +.span8 { + width: 620px; +} +.span7 { + width: 540px; +} +.span6 { + width: 460px; +} +.span5 { + width: 380px; +} +.span4 { + width: 300px; +} +.span3 { + width: 220px; +} +.span2 { + width: 140px; +} +.span1 { + width: 60px; +} +.offset12 { + margin-left: 980px; +} +.offset11 { + margin-left: 900px; +} +.offset10 { + margin-left: 820px; +} +.offset9 { + margin-left: 740px; +} +.offset8 { + margin-left: 660px; +} +.offset7 { + margin-left: 580px; +} +.offset6 { + margin-left: 500px; +} +.offset5 { + margin-left: 420px; +} +.offset4 { + margin-left: 340px; +} +.offset3 { + margin-left: 260px; +} +.offset2 { + margin-left: 180px; +} +.offset1 { + margin-left: 100px; +} +.row-fluid { + width: 100%; +*zoom:1; +} +.row-fluid:before, .row-fluid:after { + display: table; + content: ""; +} +.row-fluid:after { + clear: both; +} +.row-fluid>[class*="span"] { +float:left; +margin-left:2.127659574%; +} +.row-fluid>[class*="span"]:first-child { + margin-left: 0; +} +.row-fluid > .span12 { + width: 99.99999998999999%; +} +.row-fluid > .span11 { + width: 91.489361693%; +} +.row-fluid > .span10 { + width: 82.97872339599999%; +} +.row-fluid > .span9 { + width: 74.468085099%; +} +.row-fluid > .span8 { + width: 65.95744680199999%; +} +.row-fluid > .span7 { + width: 57.446808505%; +} +.row-fluid > .span6 { + width: 48.93617020799999%; +} +.row-fluid > .span5 { + width: 40.425531911%; +} +.row-fluid > .span4 { + width: 31.914893614%; +} +.row-fluid > .span3 { + width: 23.404255317%; +} +.row-fluid > .span2 { + width: 14.89361702%; +} +.row-fluid > .span1 { + width: 6.382978723%; +} +.container { + margin-left: auto; + margin-right: auto; +*zoom:1; +} +.container:before, .container:after { + display: table; + content: ""; +} +.container:after { + clear: both; +} +.container-fluid { + padding-left: 20px; + padding-right: 20px; +*zoom:1; +} +.container-fluid:before, .container-fluid:after { + display: table; + content: ""; +} +.container-fluid:after { + clear: both; +} +p { + margin: 0 0 9px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 13px; + line-height: 18px; +} +p small { + font-size: 11px; + color: #999999; +} +.lead { + margin-bottom: 18px; + font-size: 20px; + font-weight: 200; + line-height: 27px; +} +h1, h2, h3, h4, h5, h6 { + margin: 0; + font-family: inherit; + font-weight: bold; + color: inherit; + text-rendering: optimizelegibility; +} +h1 small, h2 small, h3 small, h4 small, h5 small, h6 small { + font-weight: normal; + color: #999999; +} +h1 { + font-size: 30px; + line-height: 36px; +} +h1 small { + font-size: 18px; +} +h2 { + font-size: 24px; + line-height: 36px; +} +h2 small { + font-size: 18px; +} +h3 { + line-height: 27px; + font-size: 18px; +} +h3 small { + font-size: 14px; +} +h4, h5, h6 { + line-height: 18px; +} +h4 { + font-size: 14px; +} +h4 small { + font-size: 12px; +} +h5 { + font-size: 12px; +} +h6 { + font-size: 11px; + color: #999999; + text-transform: uppercase; +} +.page-header { + padding-bottom: 17px; + margin: 18px 0; + border-bottom: 1px solid #eeeeee; +} +.page-header h1 { + line-height: 1; +} +ul, ol { + padding: 0; + margin: 0 0 9px 25px; +} +ul ul, ul ol, ol ol, ol ul { + margin-bottom: 0; +} +ul { + list-style: disc; +} +ol { + list-style: decimal; +} +li { + line-height: 18px; +} +ul.unstyled, ol.unstyled { + margin-left: 0; + list-style: none; +} +dl { + margin-bottom: 18px; +} +dt, dd { + line-height: 18px; +} +dt { + font-weight: bold; + line-height: 17px; +} +dd { + margin-left: 9px; +} +.dl-horizontal dt { + float: left; + clear: left; + width: 120px; + text-align: right; +} +.dl-horizontal dd { + margin-left: 130px; +} +hr { + margin: 18px 0; + border: 0; + border-top: 1px solid #eeeeee; + border-bottom: 1px solid #ffffff; +} +strong { + font-weight: bold; +} +em { + font-style: italic; +} +.muted { + color: #999999; +} +abbr[title] { + border-bottom: 1px dotted #ddd; + cursor: help; +} +abbr.initialism { + font-size: 90%; + text-transform: uppercase; +} +blockquote { + padding: 0 0 0 15px; + margin: 0 0 18px; + border-left: 5px solid #eeeeee; +} +blockquote p { + margin-bottom: 0; + font-size: 16px; + font-weight: 300; + line-height: 22.5px; +} +blockquote small { + display: block; + line-height: 18px; + color: #999999; +} +blockquote small:before { + content: '\2014 \00A0'; +} +blockquote.pull-right { + float: right; + padding-left: 0; + padding-right: 15px; + border-left: 0; + border-right: 5px solid #eeeeee; +} +blockquote.pull-right p, blockquote.pull-right small { + text-align: right; +} +q:before, q:after, blockquote:before, blockquote:after { + content: ""; +} +address { + display: block; + margin-bottom: 18px; + line-height: 18px; + font-style: normal; +} +small { + font-size: 100%; +} +cite { + font-style: normal; +} +code, pre { + padding: 0 3px 2px; + font-family: Menlo, Monaco, "Courier New", monospace; + font-size: 12px; + color: #333333; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +code { + padding: 2px 4px; + color: #d14; + background-color: #f7f7f9; + border: 1px solid #e1e1e8; +} +pre { + display: block; + padding: 8.5px; + margin: 0 0 9px; + font-size: 12.025px; + line-height: 18px; + background-color: #f5f5f5; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.15); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + white-space: pre; + white-space: pre-wrap; + word-break: break-all; + word-wrap: break-word; +} +pre.prettyprint { + margin-bottom: 18px; +} +pre code { + padding: 0; + color: inherit; + background-color: transparent; + border: 0; +} +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} +form { + margin: 0 0 18px; +} +fieldset { + padding: 0; + margin: 0; + border: 0; +} +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 27px; + font-size: 19.5px; + line-height: 36px; + color: #333333; + border: 0; + border-bottom: 1px solid #eee; +} +legend small { + font-size: 13.5px; + color: #999999; +} +label, input, button, select, textarea { + font-size: 13px; + font-weight: normal; + line-height: 18px; +} +input, button, select, textarea { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; +} +label { + display: block; + margin-bottom: 5px; + color: #333333; +} +input, textarea, select, .uneditable-input { + display: inline-block; + width: 210px; + height: 18px; + padding: 4px; + margin-bottom: 9px; + font-size: 13px; + line-height: 18px; + color: #555555; + border: 1px solid #cccccc; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.uneditable-textarea { + width: auto; + height: auto; +} +label input, label textarea, label select { + display: block; +} +input[type="image"], input[type="checkbox"], input[type="radio"] { + width: auto; + height: auto; + padding: 0; + margin: 3px 0; +*margin-top:0; + line-height: normal; + cursor: pointer; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; + border: 0 \9; +} +input[type="image"] { + border: 0; +} +input[type="file"] { + width: auto; + padding: initial; + line-height: initial; + border: initial; + background-color: #ffffff; + background-color: initial; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} +input[type="button"], input[type="reset"], input[type="submit"] { + width: auto; + height: auto; +} +select, input[type="file"] { + height: 28px; +*margin-top:4px; + line-height: 28px; +} +input[type="file"] { + line-height: 18px \9; +} +select { + width: 220px; + background-color: #ffffff; +} +select[multiple], select[size] { + height: auto; +} +input[type="image"] { + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} +textarea { + height: auto; +} +input[type="hidden"] { + display: none; +} +.radio, .checkbox { + padding-left: 18px; +} +.radio input[type="radio"], .checkbox input[type="checkbox"] { + float: left; + margin-left: -18px; +} +.controls>.radio:first-child, .controls>.checkbox:first-child { + padding-top: 5px; +} +.radio.inline, .checkbox.inline { + display: inline-block; + padding-top: 5px; + margin-bottom: 0; + vertical-align: middle; +} +.radio.inline+.radio.inline, .checkbox.inline+.checkbox.inline { + margin-left: 10px; +} +input, textarea { + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border linear 0.2s, box-shadow linear 0.2s; + -moz-transition: border linear 0.2s, box-shadow linear 0.2s; + -ms-transition: border linear 0.2s, box-shadow linear 0.2s; + -o-transition: border linear 0.2s, box-shadow linear 0.2s; + transition: border linear 0.2s, box-shadow linear 0.2s; +} +input:focus, textarea:focus { + border-color: rgba(82, 168, 236, 0.8); + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); + outline: 0; + outline: thin dotted \9; +} +input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus, select:focus { + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.input-mini { + width: 60px; +} +.input-small { + width: 90px; +} +.input-medium { + width: 150px; +} +.input-large { + width: 210px; +} +.input-xlarge { + width: 270px; +} +.input-xxlarge { + width: 530px; +} +input[class*="span"], select[class*="span"], textarea[class*="span"], .uneditable-input { + float: none; + margin-left: 0; +} +input, textarea, .uneditable-input { + margin-left: 0; +} +input.span12, textarea.span12, .uneditable-input.span12 { + width: 930px; +} +input.span11, textarea.span11, .uneditable-input.span11 { + width: 850px; +} +input.span10, textarea.span10, .uneditable-input.span10 { + width: 770px; +} +input.span9, textarea.span9, .uneditable-input.span9 { + width: 690px; +} +input.span8, textarea.span8, .uneditable-input.span8 { + width: 610px; +} +input.span7, textarea.span7, .uneditable-input.span7 { + width: 530px; +} +input.span6, textarea.span6, .uneditable-input.span6 { + width: 450px; +} +input.span5, textarea.span5, .uneditable-input.span5 { + width: 370px; +} +input.span4, textarea.span4, .uneditable-input.span4 { + width: 290px; +} +input.span3, textarea.span3, .uneditable-input.span3 { + width: 210px; +} +input.span2, textarea.span2, .uneditable-input.span2 { + width: 130px; +} +input.span1, textarea.span1, .uneditable-input.span1 { + width: 50px; +} +input[disabled], select[disabled], textarea[disabled], input[readonly], select[readonly], textarea[readonly] { + background-color: #eeeeee; + border-color: #ddd; + cursor: not-allowed; +} +.control-group.warning>label, .control-group.warning .help-block, .control-group.warning .help-inline { + color: #c09853; +} +.control-group.warning input, .control-group.warning select, .control-group.warning textarea { + color: #c09853; + border-color: #c09853; +} +.control-group.warning input:focus, .control-group.warning select:focus, .control-group.warning textarea:focus { + border-color: #a47e3c; + -webkit-box-shadow: 0 0 6px #dbc59e; + -moz-box-shadow: 0 0 6px #dbc59e; + box-shadow: 0 0 6px #dbc59e; +} +.control-group.warning .input-prepend .add-on, .control-group.warning .input-append .add-on { + color: #c09853; + background-color: #fcf8e3; + border-color: #c09853; +} +.control-group.error>label, .control-group.error .help-block, .control-group.error .help-inline { + color: #b94a48; +} +.control-group.error input, .control-group.error select, .control-group.error textarea { + color: #b94a48; + border-color: #b94a48; +} +.control-group.error input:focus, .control-group.error select:focus, .control-group.error textarea:focus { + border-color: #953b39; + -webkit-box-shadow: 0 0 6px #d59392; + -moz-box-shadow: 0 0 6px #d59392; + box-shadow: 0 0 6px #d59392; +} +.control-group.error .input-prepend .add-on, .control-group.error .input-append .add-on { + color: #b94a48; + background-color: #f2dede; + border-color: #b94a48; +} +.control-group.success>label, .control-group.success .help-block, .control-group.success .help-inline { + color: #468847; +} +.control-group.success input, .control-group.success select, .control-group.success textarea { + color: #468847; + border-color: #468847; +} +.control-group.success input:focus, .control-group.success select:focus, .control-group.success textarea:focus { + border-color: #356635; + -webkit-box-shadow: 0 0 6px #7aba7b; + -moz-box-shadow: 0 0 6px #7aba7b; + box-shadow: 0 0 6px #7aba7b; +} +.control-group.success .input-prepend .add-on, .control-group.success .input-append .add-on { + color: #468847; + background-color: #dff0d8; + border-color: #468847; +} +input:focus:required:invalid, textarea:focus:required:invalid, select:focus:required:invalid { +color:#b94a48; +border-color:#ee5f5b; +} +input:focus:required:invalid:focus, textarea:focus:required:invalid:focus, select:focus:required:invalid:focus { +border-color:#e9322d; +-webkit-box-shadow:0 0 6px #f8b9b7; +-moz-box-shadow:0 0 6px #f8b9b7; +box-shadow:0 0 6px #f8b9b7; +} +.form-actions { + padding: 17px 20px 18px; + margin-top: 18px; + margin-bottom: 18px; + background-color: #eeeeee; + border-top: 1px solid #ddd; +*zoom:1; +} +.form-actions:before, .form-actions:after { + display: table; + content: ""; +} +.form-actions:after { + clear: both; +} +.uneditable-input { + display: block; + background-color: #ffffff; + border-color: #eee; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + cursor: not-allowed; +} +:-moz-placeholder { +color:#999999; +} +::-webkit-input-placeholder { +color:#999999; +} +.help-block, .help-inline { + color: #555555; +} +.help-block { + display: block; + margin-bottom: 9px; +} +.help-inline { + display: inline-block; +*display:inline; +*zoom:1; + vertical-align: middle; + padding-left: 5px; +} +.input-prepend, .input-append { + margin-bottom: 5px; +} +.input-prepend input, .input-append input, .input-prepend select, .input-append select, .input-prepend .uneditable-input, .input-append .uneditable-input { +*margin-left:0; + -webkit-border-radius: 0 3px 3px 0; + -moz-border-radius: 0 3px 3px 0; + border-radius: 0 3px 3px 0; +} +.input-prepend input:focus, .input-append input:focus, .input-prepend select:focus, .input-append select:focus, .input-prepend .uneditable-input:focus, .input-append .uneditable-input:focus { + position: relative; + z-index: 2; +} +.input-prepend .uneditable-input, .input-append .uneditable-input { + border-left-color: #ccc; +} +.input-prepend .add-on, .input-append .add-on { + display: inline-block; + width: auto; + min-width: 16px; + height: 18px; + padding: 4px 5px; + font-weight: normal; + line-height: 18px; + text-align: center; + text-shadow: 0 1px 0 #ffffff; + vertical-align: middle; + background-color: #eeeeee; + border: 1px solid #ccc; +} +.input-prepend .add-on, .input-append .add-on, .input-prepend .btn, .input-append .btn { + -webkit-border-radius: 3px 0 0 3px; + -moz-border-radius: 3px 0 0 3px; + border-radius: 3px 0 0 3px; +} +.input-prepend .active, .input-append .active { + background-color: #a9dba9; + border-color: #46a546; +} +.input-prepend .add-on, .input-prepend .btn { + margin-right: -1px; +} +.input-append input, .input-append select .uneditable-input { + -webkit-border-radius: 3px 0 0 3px; + -moz-border-radius: 3px 0 0 3px; + border-radius: 3px 0 0 3px; +} +.input-append .uneditable-input { + border-left-color: #eee; + border-right-color: #ccc; +} +.input-append .add-on, .input-append .btn { + margin-left: -4px; + -webkit-border-radius: 0 3px 3px 0; + -moz-border-radius: 0 3px 3px 0; + border-radius: 0 3px 3px 0; +} +.input-prepend.input-append input, .input-prepend.input-append select, .input-prepend.input-append .uneditable-input { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.input-prepend.input-append .add-on:first-child, .input-prepend.input-append .btn:first-child { + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + -ms-border-radius: 4px 0 0 4px; + -o-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; + margin-right: -1px; +} +.input-prepend.input-append .add-on:last-child, .input-prepend.input-append .btn:last-child { + margin-left: -4px; + -webkit-border-radius: 0 3px 3px 0; + -moz-border-radius: 0 3px 3px 0; + border-radius: 0 3px 3px 0; +} +.search-query { + padding-left: 14px; + padding-right: 14px; + margin-bottom: 0; + -webkit-border-radius: 14px; + -moz-border-radius: 14px; + border-radius: 14px; +} +.form-search input, .form-inline input, .form-horizontal input, .form-search textarea, .form-inline textarea, .form-horizontal textarea, .form-search select, .form-inline select, .form-horizontal select, .form-search .help-inline, .form-inline .help-inline, .form-horizontal .help-inline, .form-search .uneditable-input, .form-inline .uneditable-input, .form-horizontal .uneditable-input, .form-search .input-prepend, .form-inline .input-prepend, .form-horizontal .input-prepend, .form-search .input-append, .form-inline .input-append, .form-horizontal .input-append { + display: inline-block; + margin-bottom: 0; +} +.form-search .hide, .form-inline .hide, .form-horizontal .hide { + display: none; +} +.form-search label, .form-inline label { + display: inline-block; +} +.form-search .input-append, .form-inline .input-append, .form-search .input-prepend, .form-inline .input-prepend { + margin-bottom: 0; +} +.form-search .radio, .form-search .checkbox, .form-inline .radio, .form-inline .checkbox { + padding-left: 0; + margin-bottom: 0; + vertical-align: middle; +} +.form-search .radio input[type="radio"], .form-search .checkbox input[type="checkbox"], .form-inline .radio input[type="radio"], .form-inline .checkbox input[type="checkbox"] { + float: left; + margin-left: 0; + margin-right: 3px; +} +.control-group { + margin-bottom: 9px; +} +legend+.control-group { + margin-top: 18px; + -webkit-margin-top-collapse: separate; +} +.form-horizontal .control-group { + margin-bottom: 18px; +*zoom:1; +} +.form-horizontal .control-group:before, .form-horizontal .control-group:after { + display: table; + content: ""; +} +.form-horizontal .control-group:after { + clear: both; +} +.form-horizontal .control-label { + float: left; + width: 140px; + padding-top: 5px; + text-align: right; +} +.form-horizontal .controls { + margin-left: 160px; +*display:inline-block; +*margin-left:0; +*padding-left:20px; +} +.form-horizontal .help-block { + margin-top: 9px; + margin-bottom: 0; +} +.form-horizontal .form-actions { + padding-left: 160px; +} +table { + max-width: 100%; + border-collapse: collapse; + border-spacing: 0; + background-color: transparent; +} +.table { + width: 100%; + margin-bottom: 18px; +} +.table th, .table td { + padding: 8px; + line-height: 18px; + text-align: left; + vertical-align: top; + border-top: 1px solid #dddddd; +} +.table th { + font-weight: bold; +} +.table thead th { + vertical-align: bottom; +} +.table colgroup+thead tr:first-child th, .table colgroup+thead tr:first-child td, .table thead:first-child tr:first-child th, .table thead:first-child tr:first-child td { + border-top: 0; +} +.table tbody+tbody { + border-top: 2px solid #dddddd; +} +.table-condensed th, .table-condensed td { + padding: 4px 5px; +} +.table-bordered { + border: 1px solid #dddddd; + border-left: 0; + border-collapse: separate; +*border-collapse:collapsed; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.table-bordered th, .table-bordered td { + border-left: 1px solid #dddddd; +} +.table-bordered thead:first-child tr:first-child th, .table-bordered tbody:first-child tr:first-child th, .table-bordered tbody:first-child tr:first-child td { + border-top: 0; +} +.table-bordered thead:first-child tr:first-child th:first-child, .table-bordered tbody:first-child tr:first-child td:first-child { + -webkit-border-radius: 4px 0 0 0; + -moz-border-radius: 4px 0 0 0; + border-radius: 4px 0 0 0; +} +.table-bordered thead:first-child tr:first-child th:last-child, .table-bordered tbody:first-child tr:first-child td:last-child { + -webkit-border-radius: 0 4px 0 0; + -moz-border-radius: 0 4px 0 0; + border-radius: 0 4px 0 0; +} +.table-bordered thead:last-child tr:last-child th:first-child, .table-bordered tbody:last-child tr:last-child td:first-child { + -webkit-border-radius: 0 0 0 4px; + -moz-border-radius: 0 0 0 4px; + border-radius: 0 0 0 4px; +} +.table-bordered thead:last-child tr:last-child th:last-child, .table-bordered tbody:last-child tr:last-child td:last-child { + -webkit-border-radius: 0 0 4px 0; + -moz-border-radius: 0 0 4px 0; + border-radius: 0 0 4px 0; +} +.table-striped tbody tr:nth-child(odd) td, .table-striped tbody tr:nth-child(odd) th { + background-color: #f9f9f9; +} +.table tbody tr:hover td, .table tbody tr:hover th { + background-color: #f5f5f5; +} +table .span1 { + float: none; + width: 44px; + margin-left: 0; +} +table .span2 { + float: none; + width: 124px; + margin-left: 0; +} +table .span3 { + float: none; + width: 204px; + margin-left: 0; +} +table .span4 { + float: none; + width: 284px; + margin-left: 0; +} +table .span5 { + float: none; + width: 364px; + margin-left: 0; +} +table .span6 { + float: none; + width: 444px; + margin-left: 0; +} +table .span7 { + float: none; + width: 524px; + margin-left: 0; +} +table .span8 { + float: none; + width: 604px; + margin-left: 0; +} +table .span9 { + float: none; + width: 684px; + margin-left: 0; +} +table .span10 { + float: none; + width: 764px; + margin-left: 0; +} +table .span11 { + float: none; + width: 844px; + margin-left: 0; +} +table .span12 { + float: none; + width: 924px; + margin-left: 0; +} +table .span13 { + float: none; + width: 1004px; + margin-left: 0; +} +table .span14 { + float: none; + width: 1084px; + margin-left: 0; +} +table .span15 { + float: none; + width: 1164px; + margin-left: 0; +} +table .span16 { + float: none; + width: 1244px; + margin-left: 0; +} +table .span17 { + float: none; + width: 1324px; + margin-left: 0; +} +table .span18 { + float: none; + width: 1404px; + margin-left: 0; +} +table .span19 { + float: none; + width: 1484px; + margin-left: 0; +} +table .span20 { + float: none; + width: 1564px; + margin-left: 0; +} +table .span21 { + float: none; + width: 1644px; + margin-left: 0; +} +table .span22 { + float: none; + width: 1724px; + margin-left: 0; +} +table .span23 { + float: none; + width: 1804px; + margin-left: 0; +} +table .span24 { + float: none; + width: 1884px; + margin-left: 0; +} +[class^="icon-"], [class*=" icon-"] { +display:inline-block; +width:14px; +height:14px; +line-height:14px; +vertical-align:text-top; +background-image:url("../img/glyphicons-halflings.png"); +background-position:14px 14px; +background-repeat:no-repeat; +*margin-right:.3em; +} +[class^="icon-"]:last-child, [class*=" icon-"]:last-child { +*margin-left:0; +} +.icon-white { + background-image: url("../img/glyphiconsHalflingsWhite.png"); +} +.icon-glass { + background-position: 0 0; +} +.icon-music { + background-position: -24px 0; +} +.icon-search { + background-position: -48px 0; +} +.icon-envelope { + background-position: -72px 0; +} +.icon-heart { + background-position: -96px 0; +} +.icon-star { + background-position: -120px 0; +} +.icon-star-empty { + background-position: -144px 0; +} +.icon-user { + background-position: -168px 0; +} +.icon-film { + background-position: -192px 0; +} +.icon-th-large { + background-position: -216px 0; +} +.icon-th { + background-position: -240px 0; +} +.icon-th-list { + background-position: -264px 0; +} +.icon-ok { + background-position: -288px 0; +} +.icon-remove { + background-position: -312px 0; +} +.icon-zoom-in { + background-position: -336px 0; +} +.icon-zoom-out { + background-position: -360px 0; +} +.icon-off { + background-position: -384px 0; +} +.icon-signal { + background-position: -408px 0; +} +.icon-cog { + background-position: -432px 0; +} +.icon-trash { + background-position: -456px 0; +} +.icon-home { + background-position: 0 -24px; +} +.icon-file { + background-position: -24px -24px; +} +.icon-time { + background-position: -48px -24px; +} +.icon-road { + background-position: -72px -24px; +} +.icon-download-alt { + background-position: -96px -24px; +} +.icon-download { + background-position: -120px -24px; +} +.icon-upload { + background-position: -144px -24px; +} +.icon-inbox { + background-position: -168px -24px; +} +.icon-play-circle { + background-position: -192px -24px; +} +.icon-repeat { + background-position: -216px -24px; +} +.icon-refresh { + background-position: -240px -24px; +} +.icon-list-alt { + background-position: -264px -24px; +} +.icon-lock { + background-position: -287px -24px; +} +.icon-flag { + background-position: -312px -24px; +} +.icon-headphones { + background-position: -336px -24px; +} +.icon-volume-off { + background-position: -360px -24px; +} +.icon-volume-down { + background-position: -384px -24px; +} +.icon-volume-up { + background-position: -408px -24px; +} +.icon-qrcode { + background-position: -432px -24px; +} +.icon-barcode { + background-position: -456px -24px; +} +.icon-tag { + background-position: 0 -48px; +} +.icon-tags { + background-position: -25px -48px; +} +.icon-book { + background-position: -48px -48px; +} +.icon-bookmark { + background-position: -72px -48px; +} +.icon-print { + background-position: -96px -48px; +} +.icon-camera { + background-position: -120px -48px; +} +.icon-font { + background-position: -144px -48px; +} +.icon-bold { + background-position: -167px -48px; +} +.icon-italic { + background-position: -192px -48px; +} +.icon-text-height { + background-position: -216px -48px; +} +.icon-text-width { + background-position: -240px -48px; +} +.icon-align-left { + background-position: -264px -48px; +} +.icon-align-center { + background-position: -288px -48px; +} +.icon-align-right { + background-position: -312px -48px; +} +.icon-align-justify { + background-position: -336px -48px; +} +.icon-list { + background-position: -360px -48px; +} +.icon-indent-left { + background-position: -384px -48px; +} +.icon-indent-right { + background-position: -408px -48px; +} +.icon-facetime-video { + background-position: -432px -48px; +} +.icon-picture { + background-position: -456px -48px; +} +.icon-pencil { + background-position: 0 -72px; +} +.icon-map-marker { + background-position: -24px -72px; +} +.icon-adjust { + background-position: -48px -72px; +} +.icon-tint { + background-position: -72px -72px; +} +.icon-edit { + background-position: -96px -72px; +} +.icon-share { + background-position: -120px -72px; +} +.icon-check { + background-position: -144px -72px; +} +.icon-move { + background-position: -168px -72px; +} +.icon-step-backward { + background-position: -192px -72px; +} +.icon-fast-backward { + background-position: -216px -72px; +} +.icon-backward { + background-position: -240px -72px; +} +.icon-play { + background-position: -264px -72px; +} +.icon-pause { + background-position: -288px -72px; +} +.icon-stop { + background-position: -312px -72px; +} +.icon-forward { + background-position: -336px -72px; +} +.icon-fast-forward { + background-position: -360px -72px; +} +.icon-step-forward { + background-position: -384px -72px; +} +.icon-eject { + background-position: -408px -72px; +} +.icon-chevron-left { + background-position: -432px -72px; +} +.icon-chevron-right { + background-position: -456px -72px; +} +.icon-plus-sign { + background-position: 0 -96px; +} +.icon-minus-sign { + background-position: -24px -96px; +} +.icon-remove-sign { + background-position: -48px -96px; +} +.icon-ok-sign { + background-position: -72px -96px; +} +.icon-question-sign { + background-position: -96px -96px; +} +.icon-info-sign { + background-position: -120px -96px; +} +.icon-screenshot { + background-position: -144px -96px; +} +.icon-remove-circle { + background-position: -168px -96px; +} +.icon-ok-circle { + background-position: -192px -96px; +} +.icon-ban-circle { + background-position: -216px -96px; +} +.icon-arrow-left { + background-position: -240px -96px; +} +.icon-arrow-right { + background-position: -264px -96px; +} +.icon-arrow-up { + background-position: -289px -96px; +} +.icon-arrow-down { + background-position: -312px -96px; +} +.icon-share-alt { + background-position: -336px -96px; +} +.icon-resize-full { + background-position: -360px -96px; +} +.icon-resize-small { + background-position: -384px -96px; +} +.icon-plus { + background-position: -408px -96px; +} +.icon-minus { + background-position: -433px -96px; +} +.icon-asterisk { + background-position: -456px -96px; +} +.icon-exclamation-sign { + background-position: 0 -120px; +} +.icon-gift { + background-position: -24px -120px; +} +.icon-leaf { + background-position: -48px -120px; +} +.icon-fire { + background-position: -72px -120px; +} +.icon-eye-open { + background-position: -96px -120px; +} +.icon-eye-close { + background-position: -120px -120px; +} +.icon-warning-sign { + background-position: -144px -120px; +} +.icon-plane { + background-position: -168px -120px; +} +.icon-calendar { + background-position: -192px -120px; +} +.icon-random { + background-position: -216px -120px; +} +.icon-comment { + background-position: -240px -120px; +} +.icon-magnet { + background-position: -264px -120px; +} +.icon-chevron-up { + background-position: -288px -120px; +} +.icon-chevron-down { + background-position: -313px -119px; +} +.icon-retweet { + background-position: -336px -120px; +} +.icon-shopping-cart { + background-position: -360px -120px; +} +.icon-folder-close { + background-position: -384px -120px; +} +.icon-folder-open { + background-position: -408px -120px; +} +.icon-resize-vertical { + background-position: -432px -119px; +} +.icon-resize-horizontal { + background-position: -456px -118px; +} +.dropdown { + position: relative; +} +.dropdown-toggle { +*margin-bottom:-3px; +} +.dropdown-toggle:active, .open .dropdown-toggle { + outline: 0; +} +.caret { + display: inline-block; + width: 0; + height: 0; + vertical-align: top; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 4px solid #000000; + opacity: 0.3; + filter: alpha(opacity=30); + content: ""; +} +.dropdown .caret { + margin-top: 8px; + margin-left: 2px; +} +.dropdown:hover .caret, .open.dropdown .caret { + opacity: 1; + filter: alpha(opacity=100); +} +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + float: left; + display: none; + min-width: 160px; + padding: 4px 0; + margin: 0; + list-style: none; + background-color: #ffffff; + border-color: #ccc; + border-color: rgba(0, 0, 0, 0.2); + border-style: solid; + border-width: 1px; + -webkit-border-radius: 0 0 5px 5px; + -moz-border-radius: 0 0 5px 5px; + border-radius: 0 0 5px 5px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; +*border-right-width:2px; +*border-bottom-width:2px; +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 8px 1px; + overflow: hidden; + background-color: #e5e5e5; + border-bottom: 1px solid #ffffff; +*width:100%; +*margin:-5px 0 5px; +} +.dropdown-menu a { + display: block; + padding: 3px 15px; + clear: both; + font-weight: normal; + line-height: 18px; + color: #333333; + white-space: nowrap; +} +.dropdown-menu li>a:hover, .dropdown-menu .active>a, .dropdown-menu .active>a:hover { + color: #ffffff; + text-decoration: none; + background-color: #0088cc; +} +.dropdown.open { +*z-index:1000; +} +.dropdown.open .dropdown-toggle { + color: #ffffff; + background: #ccc; + background: rgba(0, 0, 0, 0.3); +} +.dropdown.open .dropdown-menu { + display: block; +} +.pull-right .dropdown-menu { + left: auto; + right: 0; +} +.dropup .caret, .navbar-fixed-bottom .dropdown .caret { + border-top: 0; + border-bottom: 4px solid #000000; + content: "\2191"; +} +.dropup .dropdown-menu, .navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 1px; +} +.typeahead { + margin-top: 2px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #eee; + border: 1px solid rgba(0, 0, 0, 0.05); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); +} +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, 0.15); +} +.well-large { + padding: 24px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.well-small { + padding: 9px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.fade { + -webkit-transition: opacity 0.15s linear; + -moz-transition: opacity 0.15s linear; + -ms-transition: opacity 0.15s linear; + -o-transition: opacity 0.15s linear; + transition: opacity 0.15s linear; + opacity: 0; +} +.fade.in { + opacity: 1; +} +.collapse { + -webkit-transition: height 0.35s ease; + -moz-transition: height 0.35s ease; + -ms-transition: height 0.35s ease; + -o-transition: height 0.35s ease; + transition: height 0.35s ease; + position: relative; + overflow: hidden; + height: 0; +} +.collapse.in { + height: auto; +} +.close { + float: right; + font-size: 20px; + font-weight: bold; + line-height: 18px; + color: #000000; + text-shadow: 0 1px 0 #ffffff; + opacity: 0.2; + filter: alpha(opacity=20); +} +.close:hover { + color: #000000; + text-decoration: none; + opacity: 0.4; + filter: alpha(opacity=40); + cursor: pointer; +} + + +/**/ + +.btn { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -ms-border-radius: 4px; + -o-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: inset rgba(255, 255, 255, 0.1) 0 1px 0, rgba(0, 0, 0, 0.1) 0 1px 2px; + -moz-box-shadow: inset rgba(255, 255, 255, 0.1) 0 1px 0, rgba(0, 0, 0, 0.1) 0 1px 2px; + box-shadow: inset rgba(255, 255, 255, 0.1) 0 1px 0, rgba(0, 0, 0, 0.1) 0 1px 2px; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, rgba(255, 255, 255, 0)), color-stop(100%, rgba(128, 128, 128, 0.1))); + background-image: -webkit-linear-gradient(rgba(255, 255, 255, 0), rgba(128, 128, 128, 0.1)); + background-image: -moz-linear-gradient(rgba(255, 255, 255, 0), rgba(128, 128, 128, 0.1)); + background-image: -o-linear-gradient(rgba(255, 255, 255, 0), rgba(128, 128, 128, 0.1)); + background-image: linear-gradient(rgba(255, 255, 255, 0), rgba(128, 128, 128, 0.1)); + background-color: white; + border-color: whitesmoke whitesmoke #cfcfcf; + border-color: rgba(0, 0, 0, 0.05) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.15); + color: #333333; + *background-color: whitesmoke; + font-size: 14px; + line-height: 22px; + -webkit-transition: background-color 0.2s; + -moz-transition: background-color 0.2s; + -o-transition: background-color 0.2s; + transition: background-color 0.2s; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.2); + border-style: solid; + border-width: 1px; + cursor: pointer; + display: inline-block; + margin-bottom: 0; + padding: 2px 12px; + text-align: center; + vertical-align: middle; + *border: 0; + *display: inline; + *zoom: 1; + *margin-left: .3em; +} +.btn:hover, .btn:active, .btn.active, .btn.disabled, .btn[disabled] { + background-color: whitesmoke; + color: #333333; + *background-color: #e8e8e8; +} +.btn:active, .btn.active { + background-color: #dbdbdb \9; +} +.btn:first-child { +*margin-left:0; +} +.btn:hover { + color: #333333; + text-decoration: none; + background-color: #e6e6e6; + background-position: 0 -15px; + -webkit-transition: background-position 0.1s linear; + -moz-transition: background-position 0.1s linear; + -ms-transition: background-position 0.1s linear; + -o-transition: background-position 0.1s linear; + transition: background-position 0.1s linear; +} +.btn:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.btn.active, .btn:active { + -webkit-box-shadow: inset 0 2px 3px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 2px 3px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 2px 3px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + outline: 0; +} +.btn.disabled, .btn[disabled] { + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=65); + opacity: 0.65; + background-image: none; + cursor: default; +} +.btn-large { + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + -ms-border-radius: 6px; + -o-border-radius: 6px; + border-radius: 6px; + font-size: 16px; + line-height: 24px; + line-height: 22px; + padding: 6px 14px; +} +.btn-large [class^="icon-"] { + margin-top: 1px; +} +.btn-small { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + font-size: 12px; + line-height: 24px; + line-height: 22px; + padding: 0 9px; +} +.btn-small [class^="icon-"] { + margin-top: -1px; +} +.btn-mini { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; + font-size: 11px; + line-height: 24px; + line-height: 22px; + padding: 0 6px; +} +.btn-primary, .btn-primary:hover, .btn-warning, .btn-warning:hover, .btn-danger, .btn-danger:hover, .btn-success, .btn-success:hover, .btn-info, .btn-info:hover, .btn-inverse, .btn-inverse:hover { + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + color: #ffffff; +} +.btn-primary.active, .btn-warning.active, .btn-danger.active, .btn-success.active, .btn-info.active, .btn-inverse.active { + color: rgba(255, 255, 255, 0.75); +} +.btn-primary { + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, rgba(33, 169, 236, 0)), color-stop(100%, rgba(1, 9, 13, 0.1))); + background-image: -webkit-linear-gradient(rgba(33, 169, 236, 0), rgba(1, 9, 13, 0.1)); + background-image: -moz-linear-gradient(rgba(33, 169, 236, 0), rgba(1, 9, 13, 0.1)); + background-image: -o-linear-gradient(rgba(33, 169, 236, 0), rgba(1, 9, 13, 0.1)); + background-image: linear-gradient(rgba(33, 169, 236, 0), rgba(1, 9, 13, 0.1)); + background-color: #21a9ec; + border-color: #1399dc #1399dc #0d6895; + border-color: rgba(0, 0, 0, 0.05) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.15); + color: white; + text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.15); + *background-color: #1399dc; +} +.btn-primary:hover, .btn-primary:active, .btn-primary.active, .btn-primary.disabled, .btn-primary[disabled] { + background-color: #1399dc; + color: white; + *background-color: #1189c4; +} +.btn-primary:active, .btn-primary.active { + background-color: #0f79ad \9; +} +.btn-warning { + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, rgba(245, 167, 50, 0)), color-stop(100%, rgba(38, 24, 2, 0.1))); + background-image: -webkit-linear-gradient(rgba(245, 167, 50, 0), rgba(38, 24, 2, 0.1)); + background-image: -moz-linear-gradient(rgba(245, 167, 50, 0), rgba(38, 24, 2, 0.1)); + background-image: -o-linear-gradient(rgba(245, 167, 50, 0), rgba(38, 24, 2, 0.1)); + background-image: linear-gradient(rgba(245, 167, 50, 0), rgba(38, 24, 2, 0.1)); + background-color: #f5a732; + border-color: #f49a15 #f49a15 #b36f09; + border-color: rgba(0, 0, 0, 0.05) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.15); + color: white; + text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.15); + *background-color: #f49a15; +} +.btn-warning:hover, .btn-warning:active, .btn-warning.active, .btn-warning.disabled, .btn-warning[disabled] { + background-color: #f49a15; + color: white; + *background-color: #e48d0b; +} +.btn-warning:active, .btn-warning.active { + background-color: #cb7e0a \9; +} +.btn-danger { + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, rgba(219, 51, 37, 0)), color-stop(100%, rgba(1, 0, 0, 0.1))); + background-image: -webkit-linear-gradient(rgba(219, 51, 37, 0), rgba(1, 0, 0, 0.1)); + background-image: -moz-linear-gradient(rgba(219, 51, 37, 0), rgba(1, 0, 0, 0.1)); + background-image: -o-linear-gradient(rgba(219, 51, 37, 0), rgba(1, 0, 0, 0.1)); + background-image: linear-gradient(rgba(219, 51, 37, 0), rgba(1, 0, 0, 0.1)); + background-color: #db3325; + border-color: #bd2b1f #bd2b1f #7b1c14; + border-color: rgba(0, 0, 0, 0.05) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.15); + color: white; + text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.15); + *background-color: #bd2b1f; +} +.btn-danger:hover, .btn-danger:active, .btn-danger.active, .btn-danger.disabled, .btn-danger[disabled] { + background-color: #bd2b1f; + color: white; + *background-color: #a7261c; +} +.btn-danger:active, .btn-danger.active { + background-color: #912118 \9; +} +.btn-success { + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, rgba(126, 178, 22, 0)), color-stop(100%, rgba(0, 0, 0, 0.1))); + background-image: -webkit-linear-gradient(rgba(126, 178, 22, 0), rgba(0, 0, 0, 0.1)); + background-image: -moz-linear-gradient(rgba(126, 178, 22, 0), rgba(0, 0, 0, 0.1)); + background-image: -o-linear-gradient(rgba(126, 178, 22, 0), rgba(0, 0, 0, 0.1)); + background-image: linear-gradient(rgba(126, 178, 22, 0), rgba(0, 0, 0, 0.1)); + background-color: #7eb216; + border-color: #6e9b13 #6e9b13 #3e570b; + border-color: rgba(0, 0, 0, 0.05) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.15); + color: white; + text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.15); + *background-color: #6e9b13; +} +.btn-success:hover, .btn-success:active, .btn-success.active, .btn-success.disabled, .btn-success[disabled] { + background-color: #6e9b13; + color: white; + *background-color: #5e8510; +} +.btn-success:active, .btn-success.active { + background-color: #4e6e0e \9; +} +.btn-info { + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, rgba(78, 178, 213, 0)), color-stop(100%, rgba(7, 23, 29, 0.1))); + background-image: -webkit-linear-gradient(rgba(78, 178, 213, 0), rgba(7, 23, 29, 0.1)); + background-image: -moz-linear-gradient(rgba(78, 178, 213, 0), rgba(7, 23, 29, 0.1)); + background-image: -o-linear-gradient(rgba(78, 178, 213, 0), rgba(7, 23, 29, 0.1)); + background-image: linear-gradient(rgba(78, 178, 213, 0), rgba(7, 23, 29, 0.1)); + background-color: #4eb2d5; + border-color: #35a7cf #35a7cf #237795; + border-color: rgba(0, 0, 0, 0.05) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.15); + color: white; + text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.15); + *background-color: #35a7cf; +} +.btn-info:hover, .btn-info:active, .btn-info.active, .btn-info.disabled, .btn-info[disabled] { + background-color: #35a7cf; + color: white; + *background-color: #2d98be; +} +.btn-info:active, .btn-info.active { + background-color: #2888a9 \9; +} +.btn-inverse { + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, rgba(85, 85, 85, 0)), color-stop(100%, rgba(0, 0, 0, 0.1))); + background-image: -webkit-linear-gradient(rgba(85, 85, 85, 0), rgba(0, 0, 0, 0.1)); + background-image: -moz-linear-gradient(rgba(85, 85, 85, 0), rgba(0, 0, 0, 0.1)); + background-image: -o-linear-gradient(rgba(85, 85, 85, 0), rgba(0, 0, 0, 0.1)); + background-image: linear-gradient(rgba(85, 85, 85, 0), rgba(0, 0, 0, 0.1)); + background-color: #555555; + border-color: #464646 #464646 #1f1f1f; + border-color: rgba(0, 0, 0, 0.05) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.15); + color: white; + text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.15); + *background-color: #464646; +} +.btn-inverse:hover, .btn-inverse:active, .btn-inverse.active, .btn-inverse.disabled, .btn-inverse[disabled] { + background-color: #464646; + color: white; + *background-color: #393939; +} +.btn-inverse:active, .btn-inverse.active { + background-color: #2c2c2c \9; +} +button.btn, input[type="submit"].btn { +*padding-top:2px; +*padding-bottom:2px; +} +button.btn::-moz-focus-inner, input[type="submit"].btn::-moz-focus-inner { +padding:0; +border:0; +} +button.btn.btn-large, input[type="submit"].btn.btn-large { +*padding-top:7px; +*padding-bottom:7px; +} +button.btn.btn-small, input[type="submit"].btn.btn-small { +*padding-top:3px; +*padding-bottom:3px; +} +button.btn.btn-mini, input[type="submit"].btn.btn-mini { +*padding-top:1px; +*padding-bottom:1px; +} +.btn-group { + position: relative; + + *zoom:1; + *margin-left:.3em; +} +.btn-group:before, .btn-group:after { + display: table; + content: ""; +} +.btn-group:after { + clear: both; +} +.btn-group:first-child { +*margin-left:0; +} +.btn-group+.btn-group { + margin-left: 5px; +} +.btn-toolbar { + margin-top: 9px; + margin-bottom: 9px; +} +.btn-toolbar .btn-group { + display: inline-block; +*display:inline; +*zoom:1; +} +.btn-group .btn { + position: relative; + float: left; + margin-left: -1px; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.btn-group .btn:first-child { + margin-left: 0; + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; + border-top-left-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + border-bottom-left-radius: 4px; +} +.btn-group .btn:last-child, .btn-group .dropdown-toggle { + -webkit-border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; + border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + border-bottom-right-radius: 4px; +} +.btn-group .btn.large:first-child { + margin-left: 0; + -webkit-border-top-left-radius: 6px; + -moz-border-radius-topleft: 6px; + border-top-left-radius: 6px; + -webkit-border-bottom-left-radius: 6px; + -moz-border-radius-bottomleft: 6px; + border-bottom-left-radius: 6px; +} +.btn-group .btn.large:last-child, .btn-group .large.dropdown-toggle { + -webkit-border-top-right-radius: 6px; + -moz-border-radius-topright: 6px; + border-top-right-radius: 6px; + -webkit-border-bottom-right-radius: 6px; + -moz-border-radius-bottomright: 6px; + border-bottom-right-radius: 6px; +} +.btn-group .btn:hover, .btn-group .btn:focus, .btn-group .btn:active, .btn-group .btn.active { + z-index: 2; +} +.btn-group .dropdown-toggle:active, .btn-group.open .dropdown-toggle { + outline: 0; +} +.btn-group .dropdown-toggle { + padding-left: 8px; + padding-right: 8px; + -webkit-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); +*padding-top:3px; +*padding-bottom:3px; +} +.btn-group .btn-mini.dropdown-toggle { + padding-left: 5px; + padding-right: 5px; +*padding-top:1px; +*padding-bottom:1px; +} +.btn-group .btn-small.dropdown-toggle { +*padding-top:4px; +*padding-bottom:4px; +} +.btn-group .btn-large.dropdown-toggle { + padding-left: 12px; + padding-right: 12px; +} +.btn-group.open { +*z-index:1000; +} +.btn-group.open .dropdown-menu { + display: block; + margin-top: 1px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} +.btn-group.open .dropdown-toggle { + background-image: none; + -webkit-box-shadow: inset 0 1px 6px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 6px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 6px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); +} + + +/**/ + + +.btn .caret { + margin-left: 0; + margin-top: 10px; + *margin-top: 5px; +} +.btn:hover .caret, .open.btn-group .caret { + opacity: 1; + filter: alpha(opacity=100); +} +.btn-mini .caret { + margin-top: 5px; +} +.btn-small .caret { + margin-top: 6px; +} +.btn-large .caret { + margin-top: 6px; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 5px solid #000000; +} +.btn-primary .caret, .btn-warning .caret, .btn-danger .caret, .btn-info .caret, .btn-success .caret, .btn-inverse .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; + opacity: 0.75; + filter: alpha(opacity=75); +} +.alert { + padding: 8px 35px 8px 14px; + margin-bottom: 18px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + background-color: #fcf8e3; + border: 1px solid #fbeed5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + color: #c09853; +} +.alert-heading { + color: inherit; +} +.alert .close { + position: relative; + top: -2px; + right: -21px; + line-height: 18px; +} +.alert-success { + background-color: #dff0d8; + border-color: #d6e9c6; + color: #468847; +} +.alert-danger, .alert-error { + background-color: #f2dede; + border-color: #eed3d7; + color: #b94a48; +} +.alert-info { + background-color: #d9edf7; + border-color: #bce8f1; + color: #3a87ad; +} +.alert-block { + padding-top: 14px; + padding-bottom: 14px; +} +.alert-block>p, .alert-block>ul { + margin-bottom: 0; +} +.alert-block p+p { + margin-top: 5px; +} +.nav { + margin-left: 0; + margin-bottom: 18px; + list-style: none; +} +.nav>li>a { + display: block; +} +.nav>li>a:hover { + text-decoration: none; + background-color: #eeeeee; +} +.nav .nav-header { + display: block; + padding: 3px 15px; + font-size: 11px; + font-weight: bold; + line-height: 18px; + color: #999999; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + text-transform: uppercase; +} +.nav li+.nav-header { + margin-top: 9px; +} +.nav-list { + padding-left: 15px; + padding-right: 15px; + margin-bottom: 0; +} +.nav-list>li>a, .nav-list .nav-header { + margin-left: -15px; + margin-right: -15px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); +} +.nav-list>li>a { + padding: 3px 15px; +} +.nav-list>.active>a, .nav-list>.active>a:hover { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); + background-color: #0088cc; +} +.nav-list [class^="icon-"] { + margin-right: 2px; +} +.nav-list .divider { + height: 1px; + margin: 8px 1px; + overflow: hidden; + background-color: #e5e5e5; + border-bottom: 1px solid #ffffff; +*width:100%; +*margin:-5px 0 5px; +} +.nav-tabs, .nav-pills { +*zoom:1; +} +.nav-tabs:before, .nav-pills:before, .nav-tabs:after, .nav-pills:after { + display: table; + content: ""; +} +.nav-tabs:after, .nav-pills:after { + clear: both; +} +.nav-tabs>li, .nav-pills>li { + float: left; +} +.nav-tabs>li>a, .nav-pills>li>a { + padding-right: 12px; + padding-left: 12px; + margin-right: 2px; + line-height: 14px; +} +.nav-tabs { + border-bottom: 1px solid #ddd; +} +.nav-tabs>li { + margin-bottom: -1px; +} +.nav-tabs>li>a { + padding-top: 8px; + padding-bottom: 8px; + line-height: 18px; + border: 1px solid transparent; + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} +.nav-tabs>li>a:hover { + border-color: #eeeeee #eeeeee #dddddd; +} +.nav-tabs>.active>a, .nav-tabs>.active>a:hover { + color: #555555; + background-color: #ffffff; + border: 1px solid #ddd; + border-bottom-color: transparent; + cursor: default; +} +.nav-pills>li>a { + padding-top: 8px; + padding-bottom: 8px; + margin-top: 2px; + margin-bottom: 2px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} +.nav-pills>.active>a, .nav-pills>.active>a:hover { + color: #ffffff; + background-color: #0088cc; +} +.nav-stacked>li { + float: none; +} +.nav-stacked>li>a { + margin-right: 0; +} +.nav-tabs.nav-stacked { + border-bottom: 0; +} +.nav-tabs.nav-stacked>li>a { + border: 1px solid #ddd; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.nav-tabs.nav-stacked>li:first-child>a { + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} +.nav-tabs.nav-stacked>li:last-child>a { + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} +.nav-tabs.nav-stacked>li>a:hover { + border-color: #ddd; + z-index: 2; +} +.nav-pills.nav-stacked>li>a { + margin-bottom: 3px; +} +.nav-pills.nav-stacked>li:last-child>a { + margin-bottom: 1px; +} +.nav-tabs .dropdown-menu, .nav-pills .dropdown-menu { + margin-top: 1px; + border-width: 1px; +} +.nav-pills .dropdown-menu { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.nav-tabs .dropdown-toggle .caret, .nav-pills .dropdown-toggle .caret { + border-top-color: #0088cc; + border-bottom-color: #0088cc; + margin-top: 6px; +} +.nav-tabs .dropdown-toggle:hover .caret, .nav-pills .dropdown-toggle:hover .caret { + border-top-color: #005580; + border-bottom-color: #005580; +} +.nav-tabs .active .dropdown-toggle .caret, .nav-pills .active .dropdown-toggle .caret { + border-top-color: #333333; + border-bottom-color: #333333; +} +.nav>.dropdown.active>a:hover { + color: #000000; + cursor: pointer; +} +.nav-tabs .open .dropdown-toggle, .nav-pills .open .dropdown-toggle, .nav>.open.active>a:hover { + color: #ffffff; + background-color: #999999; + border-color: #999999; +} +.nav .open .caret, .nav .open.active .caret, .nav .open a:hover .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; + opacity: 1; + filter: alpha(opacity=100); +} +.tabs-stacked .open>a:hover { + border-color: #999999; +} +.tabbable { +*zoom:1; +} +.tabbable:before, .tabbable:after { + display: table; + content: ""; +} +.tabbable:after { + clear: both; +} +.tab-content { + display: table; + width: 100%; +} +.tabs-below .nav-tabs, .tabs-right .nav-tabs, .tabs-left .nav-tabs { + border-bottom: 0; +} +.tab-content>.tab-pane, .pill-content>.pill-pane { + display: none; +} +.tab-content>.active, .pill-content>.active { + display: block; +} +.tabs-below .nav-tabs { + border-top: 1px solid #ddd; +} +.tabs-below .nav-tabs>li { + margin-top: -1px; + margin-bottom: 0; +} +.tabs-below .nav-tabs>li>a { + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} +.tabs-below .nav-tabs>li>a:hover { + border-bottom-color: transparent; + border-top-color: #ddd; +} +.tabs-below .nav-tabs .active>a, .tabs-below .nav-tabs .active>a:hover { + border-color: transparent #ddd #ddd #ddd; +} +.tabs-left .nav-tabs>li, .tabs-right .nav-tabs>li { + float: none; +} +.tabs-left .nav-tabs>li>a, .tabs-right .nav-tabs>li>a { + min-width: 74px; + margin-right: 0; + margin-bottom: 3px; +} +.tabs-left .nav-tabs { + float: left; + margin-right: 19px; + border-right: 1px solid #ddd; +} +.tabs-left .nav-tabs>li>a { + margin-right: -1px; + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} +.tabs-left .nav-tabs>li>a:hover { + border-color: #eeeeee #dddddd #eeeeee #eeeeee; +} +.tabs-left .nav-tabs .active>a, .tabs-left .nav-tabs .active>a:hover { + border-color: #ddd transparent #ddd #ddd; +*border-right-color:#ffffff; +} +.tabs-right .nav-tabs { + float: right; + margin-left: 19px; + border-left: 1px solid #ddd; +} +.tabs-right .nav-tabs>li>a { + margin-left: -1px; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} +.tabs-right .nav-tabs>li>a:hover { + border-color: #eeeeee #eeeeee #eeeeee #dddddd; +} +.tabs-right .nav-tabs .active>a, .tabs-right .nav-tabs .active>a:hover { + border-color: #ddd #ddd #ddd transparent; +*border-left-color:#ffffff; +} +.navbar { +*position:relative; +*z-index:2; + overflow: visible; + margin-bottom: 18px; +} +.navbar-inner { + padding-left: 20px; + padding-right: 20px; + background-color: #2c2c2c; + background-image: -moz-linear-gradient(top, #333333, #222222); + background-image: -ms-linear-gradient(top, #333333, #222222); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222)); + background-image: -webkit-linear-gradient(top, #333333, #222222); + background-image: -o-linear-gradient(top, #333333, #222222); + background-image: linear-gradient(top, #333333, #222222); + background-repeat: repeat-x; +filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); +} +.navbar .container { + width: auto; +} +.btn-navbar { + display: none; + float: right; + padding: 7px 10px; + margin-left: 5px; + margin-right: 5px; + background-color: #2c2c2c; + background-image: -moz-linear-gradient(top, #333333, #222222); + background-image: -ms-linear-gradient(top, #333333, #222222); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222)); + background-image: -webkit-linear-gradient(top, #333333, #222222); + background-image: -o-linear-gradient(top, #333333, #222222); + background-image: linear-gradient(top, #333333, #222222); + background-repeat: repeat-x; +filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0); + border-color: #222222 #222222 #000000; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); +filter:progid:dximagetransform.microsoft.gradient(enabled=false); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); +} +.btn-navbar:hover, .btn-navbar:active, .btn-navbar.active, .btn-navbar.disabled, .btn-navbar[disabled] { + background-color: #222222; +} +.btn-navbar:active, .btn-navbar.active { + background-color: #080808 \9; +} +.btn-navbar .icon-bar { + display: block; + width: 18px; + height: 2px; + background-color: #f5f5f5; + -webkit-border-radius: 1px; + -moz-border-radius: 1px; + border-radius: 1px; + -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); +} +.btn-navbar .icon-bar+.icon-bar { + margin-top: 3px; +} +.nav-collapse.collapse { + height: auto; +} +.navbar { + color: #999999; +} +.navbar .brand:hover { + text-decoration: none; +} +.navbar .brand { + float: left; + display: block; + padding: 8px 20px 12px; + margin-left: -20px; + font-size: 20px; + font-weight: 200; + line-height: 1; + color: #ffffff; +} +.navbar .navbar-text { + margin-bottom: 0; + line-height: 40px; +} +.navbar .btn, .navbar .btn-group { + margin-top: 5px; +} +.navbar .btn-group .btn { + margin-top: 0; +} +.navbar-form { + margin-bottom: 0; +*zoom:1; +} +.navbar-form:before, .navbar-form:after { + display: table; + content: ""; +} +.navbar-form:after { + clear: both; +} +.navbar-form input, .navbar-form select, .navbar-form .radio, .navbar-form .checkbox { + margin-top: 5px; +} +.navbar-form input, .navbar-form select { + display: inline-block; + margin-bottom: 0; +} +.navbar-form input[type="image"], .navbar-form input[type="checkbox"], .navbar-form input[type="radio"] { + margin-top: 3px; +} +.navbar-form .input-append, .navbar-form .input-prepend { + margin-top: 6px; + white-space: nowrap; +} +.navbar-form .input-append input, .navbar-form .input-prepend input { + margin-top: 0; +} +.navbar-search { + position: relative; + float: left; + margin-top: 6px; + margin-bottom: 0; +} +.navbar-search .search-query { + padding: 4px 9px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 13px; + font-weight: normal; + line-height: 1; + color: #ffffff; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0px rgba(255, 255, 255, 0.15); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0px rgba(255, 255, 255, 0.15); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0px rgba(255, 255, 255, 0.15); + -webkit-transition: none; + -moz-transition: none; + -ms-transition: none; + -o-transition: none; + transition: none; +} +.navbar-search .search-query:-moz-placeholder { +color:#cccccc; +} +.navbar-search .search-query::-webkit-input-placeholder { +color:#cccccc; +} +.navbar-search .search-query:focus, .navbar-search .search-query.focused { + padding: 5px 10px; + color: #333333; + text-shadow: 0 1px 0 #ffffff; + background-color: #ffffff; + border: 0; + -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + outline: 0; +} +.navbar-fixed-top, .navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; + margin-bottom: 0; +} +.navbar-fixed-top .navbar-inner, .navbar-fixed-bottom .navbar-inner { + padding-left: 0; + padding-right: 0; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.navbar-fixed-top .container, .navbar-fixed-bottom .container { + width: 940px; +} +.navbar-fixed-top { + top: 0; +} +.navbar-fixed-bottom { + bottom: 0; +} +.navbar .nav { + position: relative; + left: 0; + display: block; + float: left; + margin: 0 10px 0 0; +} +.navbar .nav.pull-right { + float: right; +} +.navbar .nav>li { + display: block; + float: left; +} +.navbar .nav>li>a { + float: none; + padding: 10px 10px 11px; + line-height: 19px; + color: #999999; + text-decoration: none; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.navbar .nav>li>a:hover { + background-color: transparent; + color: #ffffff; + text-decoration: none; +} +.navbar .nav .active>a, .navbar .nav .active>a:hover { + color: #ffffff; + text-decoration: none; + background-color: #222222; +} +.navbar .divider-vertical { + height: 40px; + width: 1px; + margin: 0 9px; + overflow: hidden; + background-color: #222222; + border-right: 1px solid #333333; +} +.navbar .nav.pull-right { + margin-left: 10px; + margin-right: 0; +} +.navbar .dropdown-menu { + margin-top: 1px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.navbar .dropdown-menu:before { + content: ''; + display: inline-block; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-bottom-color: rgba(0, 0, 0, 0.2); + position: absolute; + top: -7px; + left: 9px; +} +.navbar .dropdown-menu:after { + content: ''; + display: inline-block; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid #ffffff; + position: absolute; + top: -6px; + left: 10px; +} +.navbar-fixed-bottom .dropdown-menu:before { + border-top: 7px solid #ccc; + border-top-color: rgba(0, 0, 0, 0.2); + border-bottom: 0; + bottom: -7px; + top: auto; +} +.navbar-fixed-bottom .dropdown-menu:after { + border-top: 6px solid #ffffff; + border-bottom: 0; + bottom: -6px; + top: auto; +} +.navbar .nav .dropdown-toggle .caret, .navbar .nav .open.dropdown .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} +.navbar .nav .active .caret { + opacity: 1; + filter: alpha(opacity=100); +} +.navbar .nav .open>.dropdown-toggle, .navbar .nav .active>.dropdown-toggle, .navbar .nav .open.active>.dropdown-toggle { + background-color: transparent; +} +.navbar .nav .active>.dropdown-toggle:hover { + color: #ffffff; +} +.navbar .nav.pull-right .dropdown-menu, .navbar .nav .dropdown-menu.pull-right { + left: auto; + right: 0; +} +.navbar .nav.pull-right .dropdown-menu:before, .navbar .nav .dropdown-menu.pull-right:before { + left: auto; + right: 12px; +} +.navbar .nav.pull-right .dropdown-menu:after, .navbar .nav .dropdown-menu.pull-right:after { + left: auto; + right: 13px; +} +.breadcrumb { + padding: 7px 14px; + margin: 0 0 18px; + list-style: none; + background-color: #fbfbfb; + background-image: -moz-linear-gradient(top, #ffffff, #f5f5f5); + background-image: -ms-linear-gradient(top, #ffffff, #f5f5f5); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f5f5f5)); + background-image: -webkit-linear-gradient(top, #ffffff, #f5f5f5); + background-image: -o-linear-gradient(top, #ffffff, #f5f5f5); + background-image: linear-gradient(top, #ffffff, #f5f5f5); + background-repeat: repeat-x; +filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0); + border: 1px solid #ddd; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + -webkit-box-shadow: inset 0 1px 0 #ffffff; + -moz-box-shadow: inset 0 1px 0 #ffffff; + box-shadow: inset 0 1px 0 #ffffff; +} +.breadcrumb li { + display: inline-block; +*display:inline; +*zoom:1; + text-shadow: 0 1px 0 #ffffff; +} +.breadcrumb .divider { + padding: 0 5px; + color: #999999; +} +.breadcrumb .active a { + color: #333333; +} +.pagination { + height: 36px; + margin: 18px 0; +} +.pagination ul { + display: inline-block; +*display:inline; +*zoom:1; + margin-left: 0; + margin-bottom: 0; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); +} +.pagination li { + display: inline; +} +.pagination a { + float: left; + padding: 0 14px; + line-height: 34px; + text-decoration: none; + border: 1px solid #ddd; + border-left-width: 0; +} +.pagination a:hover, .pagination .active a { + background-color: #f5f5f5; +} +.pagination .active a { + color: #999999; + cursor: default; +} +.pagination .disabled span, .pagination .disabled a, .pagination .disabled a:hover { + color: #999999; + background-color: transparent; + cursor: default; +} +.pagination li:first-child a { + border-left-width: 1px; + -webkit-border-radius: 3px 0 0 3px; + -moz-border-radius: 3px 0 0 3px; + border-radius: 3px 0 0 3px; +} +.pagination li:last-child a { + -webkit-border-radius: 0 3px 3px 0; + -moz-border-radius: 0 3px 3px 0; + border-radius: 0 3px 3px 0; +} +.pagination-centered { + text-align: center; +} +.pagination-right { + text-align: right; +} +.pager { + margin-left: 0; + margin-bottom: 18px; + list-style: none; + text-align: center; +*zoom:1; +} +.pager:before, .pager:after { + display: table; + content: ""; +} +.pager:after { + clear: both; +} +.pager li { + display: inline; +} +.pager a { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} +.pager a:hover { + text-decoration: none; + background-color: #f5f5f5; +} +.pager .next a { + float: right; +} +.pager .previous a { + float: left; +} +.pager .disabled a, .pager .disabled a:hover { + color: #999999; + background-color: #fff; + cursor: default; +} + +.modal-open .dropdown-menu { + z-index: 2050; +} +.modal-open .dropdown.open { +*z-index:2050; +} +.modal-open .popover { + z-index: 2060; +} +.modal-open .tooltip { + z-index: 2070; +} +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #687684; +} +.modal-backdrop.fade { + opacity: 0; +} +.modal-backdrop, .modal-backdrop.fade.in { + opacity: 0.8; + filter: alpha(opacity=80); +} +.modal { + position: fixed; + top: 50%; + left: 50%; + z-index: 1050; + overflow: auto; + width: 560px; + margin: -250px 0 0 -280px; + border-radius: 1px; + background-clip: padding-box; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + border:0px; +} +.modal.fade { + -webkit-transition: opacity .3s linear, top .3s ease-out; + -moz-transition: opacity .3s linear, top .3s ease-out; + -ms-transition: opacity .3s linear, top .3s ease-out; + -o-transition: opacity .3s linear, top .3s ease-out; + transition: opacity .3s linear, top .3s ease-out; + top: -25%; +} +.modal.fade.in { + top: 50%; +} +.modal-header { + padding: 9px 15px; + border-bottom: 1px solid #eee; + background-color: #0890D0; + background: #0890D0; + color:yellow; +} +.modal-header .close { + margin-top: -5px; + color :white; + background : #0890D0; + background-color :#0890D0; +} +.modal-body { + overflow-y : auto; + max-height : 400px; + padding : 15px; + color : #687684; + background-image: url("../img/bodybg.png"); +} +.modal-form { + margin-bottom: 0; +} + +.modal-footer { + color:#0890D0; + padding: 14px 15px 15px; + margin-bottom: 0; + text-align: right; + background-image: url("../img/subnavbg.png"); + border-top: 1px solid #ddd; + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; + -webkit-box-shadow: inset 0 1px 0 #ffffff; + -moz-box-shadow: inset 0 1px 0 #ffffff; + box-shadow: inset 0 1px 0 #ffffff; +*zoom:1; +} +.modal-footer:before, .modal-footer:after { + display: table; + content: ""; +} +.modal-footer:after { + clear: both; +} +.modal-footer .btn+.btn { + margin-left: 5px; + margin-bottom: 0; +} +.modal-footer .btn-group .btn+.btn { + margin-left: -1px; +} +.tooltip { + position: absolute; + z-index: 1020; + display: block; + visibility: visible; + padding: 5px; + font-size: 11px; + opacity: 0; + filter: alpha(opacity=0); +} +.tooltip.in { + opacity: 0.8; + filter: alpha(opacity=80); +} +.tooltip.top { + margin-top: -2px; +} +.tooltip.right { + margin-left: 2px; +} +.tooltip.bottom { + margin-top: 2px; +} +.tooltip.left { + margin-left: -2px; +} +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 5px solid #000000; +} +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-left: 5px solid #000000; +} +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 5px solid #000000; +} +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-right: 5px solid #000000; +} +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #ffffff; + text-align: center; + text-decoration: none; + background-color: #000000; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; +} +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1010; + display: none; + padding: 5px; +} +.popover.top { + margin-top: -5px; +} +.popover.right { + margin-left: 5px; +} +.popover.bottom { + margin-top: 5px; +} +.popover.left { + margin-left: -5px; +} +.popover.top .arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 5px solid #000000; +} +.popover.right .arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-right: 5px solid #000000; +} +.popover.bottom .arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 5px solid #000000; +} +.popover.left .arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-left: 5px solid #000000; +} +.popover .arrow { + position: absolute; + width: 0; + height: 0; +} +.popover-inner { + padding: 3px; + width: 280px; + overflow: hidden; + background: #000000; + background: rgba(0, 0, 0, 0.8); + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); +} +.popover-title { + padding: 9px 15px; + line-height: 1; + background-color: #f5f5f5; + border-bottom: 1px solid #eee; + -webkit-border-radius: 3px 3px 0 0; + -moz-border-radius: 3px 3px 0 0; + border-radius: 3px 3px 0 0; +} +.popover-content { + padding: 14px; + background-color: #ffffff; + -webkit-border-radius: 0 0 3px 3px; + -moz-border-radius: 0 0 3px 3px; + border-radius: 0 0 3px 3px; + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + background-clip: padding-box; +} +.popover-content p, .popover-content ul, .popover-content ol { + margin-bottom: 0; +} +.thumbnails { + margin-left: -20px; + list-style: none; +*zoom:1; +} +.thumbnails:before, .thumbnails:after { + display: table; + content: ""; +} +.thumbnails:after { + clear: both; +} +.thumbnails>li { + float: left; + margin: 0 0 18px 20px; +} +.thumbnail { + display: block; + padding: 4px; + line-height: 1; + border: 1px solid #ddd; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); +} +a.thumbnail:hover { + border-color: #0088cc; + -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); + -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); + box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); +} +.thumbnail>img { + display: block; + max-width: 100%; + margin-left: auto; + margin-right: auto; +} +.thumbnail .caption { + padding: 9px; +} +.label { + padding: 1px 4px 2px; + font-size: 10.998px; + font-weight: bold; + line-height: 13px; + color: #ffffff; + vertical-align: middle; + white-space: nowrap; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #999999; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.label:hover { + color: #ffffff; + text-decoration: none; +} +.label-important { + background-color: #b94a48; +} +.label-important:hover { + background-color: #953b39; +} +.label-warning { + background-color: #f89406; +} +.label-warning:hover { + background-color: #c67605; +} +.label-success { + background-color: #468847; +} +.label-success:hover { + background-color: #356635; +} +.label-info { + background-color: #3a87ad; +} +.label-info:hover { + background-color: #2d6987; +} +.label-inverse { + background-color: #333333; +} +.label-inverse:hover { + background-color: #1a1a1a; +} +.badge { + padding: 1px 9px 2px; + font-size: 12.025px; + font-weight: bold; + white-space: nowrap; + color: #ffffff; + background-color: #999999; + -webkit-border-radius: 9px; + -moz-border-radius: 9px; + border-radius: 9px; +} +.badge:hover { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} +.badge-error { + background-color: #b94a48; +} +.badge-error:hover { + background-color: #953b39; +} +.badge-warning { + background-color: #f89406; +} +.badge-warning:hover { + background-color: #c67605; +} +.badge-success { + background-color: #468847; +} +.badge-success:hover { + background-color: #356635; +} +.badge-info { + background-color: #3a87ad; +} +.badge-info:hover { + background-color: #2d6987; +} +.badge-inverse { + background-color: #333333; +} +.badge-inverse:hover { + background-color: #1a1a1a; +} +@-webkit-keyframes progress-bar-stripes { +from { +background-position:0 0; +} +to { + background-position: 40px 0; +} +} +@-moz-keyframes progress-bar-stripes { +from { +background-position:0 0; +} +to { + background-position: 40px 0; +} +} +@-ms-keyframes progress-bar-stripes { +from { +background-position:0 0; +} +to { + background-position: 40px 0; +} +} +@keyframes progress-bar-stripes { +from { +background-position:0 0; +} +to { + background-position: 40px 0; +} +} +.progress { + overflow: hidden; + height: 18px; + margin-bottom: 18px; + background-color: #f7f7f7; + background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -ms-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9)); + background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: linear-gradient(top, #f5f5f5, #f9f9f9); + background-repeat: repeat-x; +filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f5f5f5', endColorstr='#f9f9f9', GradientType=0); + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.progress .bar { + width: 0%; + height: 18px; + color: #ffffff; + font-size: 12px; + text-align: center; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #0e90d2; + background-image: -moz-linear-gradient(top, #149bdf, #0480be); + background-image: -ms-linear-gradient(top, #149bdf, #0480be); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be)); + background-image: -webkit-linear-gradient(top, #149bdf, #0480be); + background-image: -o-linear-gradient(top, #149bdf, #0480be); + background-image: linear-gradient(top, #149bdf, #0480be); + background-repeat: repeat-x; +filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#149bdf', endColorstr='#0480be', GradientType=0); + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; + -webkit-transition: width 0.6s ease; + -moz-transition: width 0.6s ease; + -ms-transition: width 0.6s ease; + -o-transition: width 0.6s ease; + transition: width 0.6s ease; +} +.progress-striped .bar { + background-color: #149bdf; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + -moz-background-size: 40px 40px; + -o-background-size: 40px 40px; + background-size: 40px 40px; +} +.progress.active .bar { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -moz-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} +.progress-danger .bar { + background-color: #dd514c; + background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -o-linear-gradient(top, #ee5f5b, #c43c35); + background-image: linear-gradient(top, #ee5f5b, #c43c35); + background-repeat: repeat-x; +filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0); +} +.progress-danger.progress-striped .bar { + background-color: #ee5f5b; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-success .bar { + background-color: #5eb95e; + background-image: -moz-linear-gradient(top, #62c462, #57a957); + background-image: -ms-linear-gradient(top, #62c462, #57a957); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957)); + background-image: -webkit-linear-gradient(top, #62c462, #57a957); + background-image: -o-linear-gradient(top, #62c462, #57a957); + background-image: linear-gradient(top, #62c462, #57a957); + background-repeat: repeat-x; +filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0); +} +.progress-success.progress-striped .bar { + background-color: #62c462; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-info .bar { + background-color: #4bb1cf; + background-image: -moz-linear-gradient(top, #5bc0de, #339bb9); + background-image: -ms-linear-gradient(top, #5bc0de, #339bb9); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9)); + background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9); + background-image: -o-linear-gradient(top, #5bc0de, #339bb9); + background-image: linear-gradient(top, #5bc0de, #339bb9); + background-repeat: repeat-x; +filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0); +} +.progress-info.progress-striped .bar { + background-color: #5bc0de; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-warning .bar { + background-color: #faa732; + background-image: -moz-linear-gradient(top, #fbb450, #f89406); + background-image: -ms-linear-gradient(top, #fbb450, #f89406); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); + background-image: -webkit-linear-gradient(top, #fbb450, #f89406); + background-image: -o-linear-gradient(top, #fbb450, #f89406); + background-image: linear-gradient(top, #fbb450, #f89406); + background-repeat: repeat-x; +filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0); +} +.progress-warning.progress-striped .bar { + background-color: #fbb450; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.accordion { + margin-bottom: 18px; +} +.accordion-group { + margin-bottom: 2px; + border: 1px solid #e5e5e5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.accordion-heading { + border-bottom: 0; +} +.accordion-heading .accordion-toggle { + display: block; + padding: 8px 15px; +} +.accordion-inner { + padding: 9px 15px; + border-top: 1px solid #e5e5e5; +} +.carousel { + position: relative; + margin-bottom: 18px; + line-height: 1; +} +.carousel-inner { + overflow: hidden; + width: 100%; + position: relative; +} +.carousel .item { + display: none; + position: relative; + -webkit-transition: 0.6s ease-in-out left; + -moz-transition: 0.6s ease-in-out left; + -ms-transition: 0.6s ease-in-out left; + -o-transition: 0.6s ease-in-out left; + transition: 0.6s ease-in-out left; +} +.carousel .item>img { + display: block; + line-height: 1; +} +.carousel .active, .carousel .next, .carousel .prev { + display: block; +} +.carousel .active { + left: 0; +} +.carousel .next, .carousel .prev { + position: absolute; + top: 0; + width: 100%; +} +.carousel .next { + left: 100%; +} +.carousel .prev { + left: -100%; +} +.carousel .next.left, .carousel .prev.right { + left: 0; +} +.carousel .active.left { + left: -100%; +} +.carousel .active.right { + left: 100%; +} +.carousel-control { + position: absolute; + top: 40%; + left: 15px; + width: 40px; + height: 40px; + margin-top: -20px; + font-size: 60px; + font-weight: 100; + line-height: 30px; + color: #ffffff; + text-align: center; + background: #222222; + border: 3px solid #ffffff; + -webkit-border-radius: 23px; + -moz-border-radius: 23px; + border-radius: 23px; + opacity: 0.5; + filter: alpha(opacity=50); +} +.carousel-control.right { + left: auto; + right: 15px; +} +.carousel-control:hover { + color: #ffffff; + text-decoration: none; + opacity: 0.9; + filter: alpha(opacity=90); +} +.carousel-caption { + position: absolute; + left: 0; + right: 0; + bottom: 0; + padding: 10px 15px 5px; + background: #333333; + background: rgba(0, 0, 0, 0.75); +} +.carousel-caption h4, .carousel-caption p { + color: #ffffff; +} +.hero-unit { + padding: 60px; + margin-bottom: 30px; + background-color: #eeeeee; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.hero-unit h1 { + margin-bottom: 0; + font-size: 60px; + line-height: 1; + color: inherit; + letter-spacing: -1px; +} +.hero-unit p { + font-size: 18px; + font-weight: 200; + line-height: 27px; + color: inherit; +} +.pull-right { + float: right; +} +.pull-left { + float: left; +} +.hide { + display: none; +} +.show { + display: block; +} +.invisible { + visibility: hidden; +} diff --git a/kafka-dse-webui/bin/src/main/resources/static/css/dashboard.css b/kafka-dse-webui/bin/src/main/resources/static/css/dashboard.css new file mode 100644 index 0000000..579bb39 --- /dev/null +++ b/kafka-dse-webui/bin/src/main/resources/static/css/dashboard.css @@ -0,0 +1,341 @@ +/*------------------------------------------------------------------ +[1. Big icons and direct links] +*/ +.shortcuts { + text-align: center; +} +.shortcuts .shortcut { + width: 22.50%; + display: inline-block; + padding: 12px 0; + margin: 0 .9% 1em; + vertical-align: top; + text-decoration: none; + background: #f9f6f1; + border-radius: 5px; +} +.shortcuts .shortcut .shortcut-icon { + margin-top: .25em; + margin-bottom: .25em; + font-size: 32px; + color: #545454; +} +.shortcuts .shortcut:hover { + background: #00ba8b; +} +.shortcuts .shortcut:hover span{ + color: #fff; +} +.shortcuts .shortcut:hover .shortcut-icon { + color: #fff; +} +.shortcuts .shortcut-label { + display: block; + font-weight: 400; + color: #545454; +} + +/*------------------------------------------------------------------ +[2. Stats / .stats] +*/ + +.stats { + width: 100%; + display: table; + padding: 0 0 0 10px; + margin-top: .5em; + margin-bottom: 1.9em; +} + +.stats .stat { + display : table-cell; + width : 40%; + vertical-align : top; + font-size : 14px; + font-weight : bold; + color : #00ab8b; +} + +.stat-value { + display: block; + margin-bottom: .55em; + font-size: 30px; + font-weight: bold; + letter-spacing: -2px; + color: #444; +} + +.stat-time { + text-align: center; + padding-top: 1.5em; +} + +.stat-time .stat-value { + color: #19bc9c; + font-size: 40px; +} + +.stats #donut-chart { + height: 100px; + margin-left: -20px; +} + + + + + +/*------------------------------------------------------------------ +[3. News Item / .news-items] +*/ + +.news-items { + margin: 1em 0 0; +} + +.news-items li { + display: table; + padding: 0 2em 0 1.5em; + padding-bottom: 1em; + margin-bottom: 1em; + border-bottom: 1px dotted #CCC; +} + +.news-items li:last-child { padding-bottom: 0; border: none; } + +.news-item-date { + display: table-cell; +} + +.news-item-detail { + display: table-cell; +} + +.news-item-title { + font-size: 13px; + font-weight: 600; +} + +.news-item-date { + width: 75px; + vertical-align: middle; + text-align: center; + +} + +.news-item-day { + display: block; + margin-bottom: .25em; + + font-size: 24px; + color: #888; +} + +.news-item-preview { + margin-bottom: 0; + + color: #777; +} + +.news-item-month { + display: block; + padding-right: 1px; + + font-size: 12px; + font-weight: 600; + color: #888; +} + + + +/*------------------------------------------------------------------ +[4. Action Table / .action-table] +*/ + +.action-table .btn-small { + padding: 4px 5px 5px; + + font-size: 10px; +} + +.action-table .td-actions { + width: 80px; + + text-align: center; +} + + .action-table .td-actions .btn { + margin-right: .5em; + } + + .action-table .td-actions .btn:last-child { + margin-rigth: 0; + } + + + +#big_stats +{ + width: 100%; + display: table; + margin-top: 1.5em; +} + +.big-stats-container .widget-content { + border:0; +} + +#big_stats .stat { + width: 25%; + height: 70px; + text-align: center; + display: table-cell; + padding: 0; + position: relative; + vertical-align: top; + border-right: 1px solid #CCC; + border-left: 1px solid #FFF; +} +#big_stats i { + font-size:30px; display:block; line-height: 40px; color:#b2afaa; +} +#big_stats .stat:hover i {color:#19bc9c;} + +#big_stats .stat:first-child { + border-left: none; +} + +#big_stats .stat:last-child { + border-right: none; +} + +h6.bigstats{ + margin : 20px; + padding-bottom : 10px; + margin-bottom : 0; +} + +#big_stats .stat h4 +{ + font-size: 11px; + font-weight: bold; + color: #777; + margin-bottom: 1.5em; +} + +#big_stats .stat .value +{ + font-size: 45px; + font-weight: bold; + color: #545454; + line-height: 1em; +} + + + +@media all and (max-width: 950px) and (min-width: 1px) { + + #big_stats { + display: block; + margin-bottom: -40px; + } + + #big_stats .stat { + width: 49%; + display: block; + margin-bottom: 3em; + float: left; + } + + #big_stats .stat:nth-child(2) { + border-right: none; + } + + #big_stats .stat:nth-child(3) { + border-left: none; + } + +} + +@media (max-width: 767px) { + #big_stats .stat .value { + font-size: 40px; + } +} + + + + +@media (max-width: 979px) { + + .shortcuts .shortcut { + width: 31%; + } +} + + +@media (max-width: 480px) { + + .stats .stat { + + margin-bottom: 3em; + } + + .stats .stat .stat-value { + margin-bottom: .15em; + font-size: 20px; + } + + .stats { + float: left; + display: block; + margin-bottom: 0; + } + + #chart-stats { + margin: 2em 0 1em; + } + + .shortcuts .shortcut { + width: 48%; + } +} + +.homebean-info { + font-size:12px; + text-align:right; + color:#687684; + background-color:#f9f6f1; + padding:0; + padding-right:8px; + height:20px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + width:370px; +} + + +.ff4jstores { + text-align: center; +} + +.ff4jstores .ff4jstore { + width : 22.50%; + display: inline-block; + padding: 12px 0; + margin : 0 .9% 1em; + vertical-align: top; + text-align:center; + height:130px; + margin-bottom:0px; +} + +.circle { + display:block;width:120px;height:120px;border-radius:60px;font-size:12px;color:#687684; +text-align:center;text-decoration:none;background:#f9f6f1} + +.circle:hover { + color:white; + text-decoration:none; + background-color:#00ba8b; +} diff --git a/kafka-dse-webui/bin/src/main/resources/static/css/font-awesome-3.2.1.css b/kafka-dse-webui/bin/src/main/resources/static/css/font-awesome-3.2.1.css new file mode 100644 index 0000000..7ede182 --- /dev/null +++ b/kafka-dse-webui/bin/src/main/resources/static/css/font-awesome-3.2.1.css @@ -0,0 +1,1479 @@ +/*! + * Font Awesome 3.2.1 + * the iconic font designed for Bootstrap + * ------------------------------------------------------------------------------ + * The full suite of pictographic icons, examples, and documentation can be + * found at http://fontawesome.io. Stay up to date on Twitter at + * http://twitter.com/fontawesome. + * + * License + * ------------------------------------------------------------------------------ + * - The Font Awesome font is licensed under SIL OFL 1.1 - + * http://scripts.sil.org/OFL + * - Font Awesome CSS, LESS, and SASS files are licensed under MIT License - + * http://opensource.org/licenses/mit-license.html + * - Font Awesome documentation licensed under CC BY 3.0 - + * http://creativecommons.org/licenses/by/3.0/ + * - Attribution is no longer required in Font Awesome 3.0, but much appreciated: + * "Font Awesome by Dave Gandy - http://fontawesome.io" + * + * Author - Dave Gandy + * ------------------------------------------------------------------------------ + * Email: dave@fontawesome.io + * Twitter: http://twitter.com/davegandy + * Work: Lead Product Designer @ Kyruus - http://kyruus.com + */ +/* FONT PATH + * -------------------------- */ +@font-face { + font-family: 'FontAwesome'; + src: url('../font/fontawesome-webfont.eot?v=3.2.1'); + src: url('../font/fontawesome-webfont.eot?#iefix&v=3.2.1') format('embedded-opentype'), url('../font/fontawesome-webfont.woff?v=3.2.1') format('woff'), url('../font/fontawesome-webfont.ttf?v=3.2.1') format('truetype'), url('../font/fontawesome-webfont.svg#fontawesomeregular?v=3.2.1') format('svg'); + font-weight: normal; + font-style: normal; +} +/* FONT AWESOME CORE + * -------------------------- */ +[class^="icon-"], +[class*=" icon-"] { + font-family: FontAwesome; + font-weight: normal; + font-style: normal; + text-decoration: inherit; + -webkit-font-smoothing: antialiased; + *margin-right: .3em; +} +[class^="icon-"]:before, +[class*=" icon-"]:before { + text-decoration: inherit; + display: inline-block; + speak: none; +} +/* makes the font 33% larger relative to the icon container */ +.icon-large:before { + vertical-align: -10%; + font-size: 1.3333333333333333em; +} +/* makes sure icons active on rollover in links */ +a [class^="icon-"], +a [class*=" icon-"] { + display: inline; +} +/* increased font size for icon-large */ +[class^="icon-"].icon-fixed-width, +[class*=" icon-"].icon-fixed-width { + display: inline-block; + width: 1.1428571428571428em; + text-align: right; + padding-right: 0.2857142857142857em; +} +[class^="icon-"].icon-fixed-width.icon-large, +[class*=" icon-"].icon-fixed-width.icon-large { + width: 1.4285714285714286em; +} +.icons-ul { + margin-left: 2.142857142857143em; + list-style-type: none; +} +.icons-ul > li { + position: relative; +} +.icons-ul .icon-li { + position: absolute; + left: -2.142857142857143em; + width: 2.142857142857143em; + text-align: center; + line-height: inherit; +} +[class^="icon-"].hide, +[class*=" icon-"].hide { + display: none; +} +.icon-muted { + color: #eeeeee; +} +.icon-light { + color: #ffffff; +} +.icon-dark { + color: #333333; +} +.icon-border { + border: solid 1px #eeeeee; + padding: .2em .25em .15em; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.icon-2x { + font-size: 2em; +} +.icon-2x.icon-border { + border-width: 2px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.icon-3x { + font-size: 3em; +} +.icon-3x.icon-border { + border-width: 3px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} +.icon-4x { + font-size: 4em; +} +.icon-4x.icon-border { + border-width: 4px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.icon-5x { + font-size: 5em; +} +.icon-5x.icon-border { + border-width: 5px; + -webkit-border-radius: 7px; + -moz-border-radius: 7px; + border-radius: 7px; +} +.pull-right { + float: right; +} +.pull-left { + float: left; +} +[class^="icon-"].pull-left, +[class*=" icon-"].pull-left { + margin-right: .3em; +} +[class^="icon-"].pull-right, +[class*=" icon-"].pull-right { + margin-left: .3em; +} +/* BOOTSTRAP SPECIFIC CLASSES + * -------------------------- */ +/* Bootstrap 2.0 sprites.less reset */ +[class^="icon-"], +[class*=" icon-"] { + display: inline; + width: auto; + height: auto; + line-height: normal; + vertical-align: baseline; + background-image: none; + background-position: 0% 0%; + background-repeat: repeat; + margin-top: 0; +} +/* more sprites.less reset */ +.icon-white, +.nav-pills > .active > a > [class^="icon-"], +.nav-pills > .active > a > [class*=" icon-"], +.nav-list > .active > a > [class^="icon-"], +.nav-list > .active > a > [class*=" icon-"], +.navbar-inverse .nav > .active > a > [class^="icon-"], +.navbar-inverse .nav > .active > a > [class*=" icon-"], +.dropdown-menu > li > a:hover > [class^="icon-"], +.dropdown-menu > li > a:hover > [class*=" icon-"], +.dropdown-menu > .active > a > [class^="icon-"], +.dropdown-menu > .active > a > [class*=" icon-"], +.dropdown-submenu:hover > a > [class^="icon-"], +.dropdown-submenu:hover > a > [class*=" icon-"] { + background-image: none; +} +/* keeps Bootstrap styles with and without icons the same */ +.btn [class^="icon-"].icon-large, +.nav [class^="icon-"].icon-large, +.btn [class*=" icon-"].icon-large, +.nav [class*=" icon-"].icon-large { + line-height: .9em; +} +.btn [class^="icon-"].icon-spin, +.nav [class^="icon-"].icon-spin, +.btn [class*=" icon-"].icon-spin, +.nav [class*=" icon-"].icon-spin { + display: inline-block; +} +.nav-tabs [class^="icon-"], +.nav-pills [class^="icon-"], +.nav-tabs [class*=" icon-"], +.nav-pills [class*=" icon-"], +.nav-tabs [class^="icon-"].icon-large, +.nav-pills [class^="icon-"].icon-large, +.nav-tabs [class*=" icon-"].icon-large, +.nav-pills [class*=" icon-"].icon-large { + line-height: .9em; +} +.btn [class^="icon-"].pull-left.icon-2x, +.btn [class*=" icon-"].pull-left.icon-2x, +.btn [class^="icon-"].pull-right.icon-2x, +.btn [class*=" icon-"].pull-right.icon-2x { + margin-top: .18em; +} +.btn [class^="icon-"].icon-spin.icon-large, +.btn [class*=" icon-"].icon-spin.icon-large { + line-height: .8em; +} +.btn.btn-small [class^="icon-"].pull-left.icon-2x, +.btn.btn-small [class*=" icon-"].pull-left.icon-2x, +.btn.btn-small [class^="icon-"].pull-right.icon-2x, +.btn.btn-small [class*=" icon-"].pull-right.icon-2x { + margin-top: .25em; +} +.btn.btn-large [class^="icon-"], +.btn.btn-large [class*=" icon-"] { + margin-top: 0; +} +.btn.btn-large [class^="icon-"].pull-left.icon-2x, +.btn.btn-large [class*=" icon-"].pull-left.icon-2x, +.btn.btn-large [class^="icon-"].pull-right.icon-2x, +.btn.btn-large [class*=" icon-"].pull-right.icon-2x { + margin-top: .05em; +} +.btn.btn-large [class^="icon-"].pull-left.icon-2x, +.btn.btn-large [class*=" icon-"].pull-left.icon-2x { + margin-right: .2em; +} +.btn.btn-large [class^="icon-"].pull-right.icon-2x, +.btn.btn-large [class*=" icon-"].pull-right.icon-2x { + margin-left: .2em; +} +/* Fixes alignment in nav lists */ +.nav-list [class^="icon-"], +.nav-list [class*=" icon-"] { + line-height: inherit; +} +/* EXTRAS + * -------------------------- */ +/* Stacked and layered icon */ +.icon-stack { + position: relative; + display: inline-block; + width: 2em; + height: 2em; + line-height: 2em; + vertical-align: -35%; +} +.icon-stack [class^="icon-"], +.icon-stack [class*=" icon-"] { + display: block; + text-align: center; + position: absolute; + width: 100%; + height: 100%; + font-size: 1em; + line-height: inherit; + *line-height: 2em; +} +.icon-stack .icon-stack-base { + font-size: 2em; + *line-height: 1em; +} +/* Animated rotating icon */ +.icon-spin { + display: inline-block; + -moz-animation: spin 2s infinite linear; + -o-animation: spin 2s infinite linear; + -webkit-animation: spin 2s infinite linear; + animation: spin 2s infinite linear; +} +/* Prevent stack and spinners from being taken inline when inside a link */ +a .icon-stack, +a .icon-spin { + display: inline-block; + text-decoration: none; +} +@-moz-keyframes spin { + 0% { + -moz-transform: rotate(0deg); + } + 100% { + -moz-transform: rotate(359deg); + } +} +@-webkit-keyframes spin { + 0% { + -webkit-transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + } +} +@-o-keyframes spin { + 0% { + -o-transform: rotate(0deg); + } + 100% { + -o-transform: rotate(359deg); + } +} +@-ms-keyframes spin { + 0% { + -ms-transform: rotate(0deg); + } + 100% { + -ms-transform: rotate(359deg); + } +} +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(359deg); + } +} +/* Icon rotations and mirroring */ +.icon-rotate-90:before { + -webkit-transform: rotate(90deg); + -moz-transform: rotate(90deg); + -ms-transform: rotate(90deg); + -o-transform: rotate(90deg); + transform: rotate(90deg); + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); +} +.icon-rotate-180:before { + -webkit-transform: rotate(180deg); + -moz-transform: rotate(180deg); + -ms-transform: rotate(180deg); + -o-transform: rotate(180deg); + transform: rotate(180deg); + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); +} +.icon-rotate-270:before { + -webkit-transform: rotate(270deg); + -moz-transform: rotate(270deg); + -ms-transform: rotate(270deg); + -o-transform: rotate(270deg); + transform: rotate(270deg); + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); +} +.icon-flip-horizontal:before { + -webkit-transform: scale(-1, 1); + -moz-transform: scale(-1, 1); + -ms-transform: scale(-1, 1); + -o-transform: scale(-1, 1); + transform: scale(-1, 1); +} +.icon-flip-vertical:before { + -webkit-transform: scale(1, -1); + -moz-transform: scale(1, -1); + -ms-transform: scale(1, -1); + -o-transform: scale(1, -1); + transform: scale(1, -1); +} +/* ensure rotation occurs inside anchor tags */ +a .icon-rotate-90:before, +a .icon-rotate-180:before, +a .icon-rotate-270:before, +a .icon-flip-horizontal:before, +a .icon-flip-vertical:before { + display: inline-block; +} +/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen + readers do not read off random characters that represent icons */ +.icon-glass:before { + content: "\f000"; +} +.icon-music:before { + content: "\f001"; +} +.icon-search:before { + content: "\f002"; +} +.icon-envelope-alt:before { + content: "\f003"; +} +.icon-heart:before { + content: "\f004"; +} +.icon-star:before { + content: "\f005"; +} +.icon-star-empty:before { + content: "\f006"; +} +.icon-user:before { + content: "\f007"; +} +.icon-film:before { + content: "\f008"; +} +.icon-th-large:before { + content: "\f009"; +} +.icon-th:before { + content: "\f00a"; +} +.icon-th-list:before { + content: "\f00b"; +} +.icon-ok:before { + content: "\f00c"; +} +.icon-remove:before { + content: "\f00d"; +} +.icon-zoom-in:before { + content: "\f00e"; +} +.icon-zoom-out:before { + content: "\f010"; +} +.icon-power-off:before, +.icon-off:before { + content: "\f011"; +} +.icon-signal:before { + content: "\f012"; +} +.icon-gear:before, +.icon-cog:before { + content: "\f013"; +} +.icon-trash:before { + content: "\f014"; +} +.icon-home:before { + content: "\f015"; +} +.icon-file-alt:before { + content: "\f016"; +} +.icon-time:before { + content: "\f017"; +} +.icon-road:before { + content: "\f018"; +} +.icon-download-alt:before { + content: "\f019"; +} +.icon-download:before { + content: "\f01a"; +} +.icon-upload:before { + content: "\f01b"; +} +.icon-inbox:before { + content: "\f01c"; +} +.icon-play-circle:before { + content: "\f01d"; +} +.icon-rotate-right:before, +.icon-repeat:before { + content: "\f01e"; +} +.icon-refresh:before { + content: "\f021"; +} +.icon-list-alt:before { + content: "\f022"; +} +.icon-lock:before { + content: "\f023"; +} +.icon-flag:before { + content: "\f024"; +} +.icon-headphones:before { + content: "\f025"; +} +.icon-volume-off:before { + content: "\f026"; +} +.icon-volume-down:before { + content: "\f027"; +} +.icon-volume-up:before { + content: "\f028"; +} +.icon-qrcode:before { + content: "\f029"; +} +.icon-barcode:before { + content: "\f02a"; +} +.icon-tag:before { + content: "\f02b"; +} +.icon-tags:before { + content: "\f02c"; +} +.icon-book:before { + content: "\f02d"; +} +.icon-bookmark:before { + content: "\f02e"; +} +.icon-print:before { + content: "\f02f"; +} +.icon-camera:before { + content: "\f030"; +} +.icon-font:before { + content: "\f031"; +} +.icon-bold:before { + content: "\f032"; +} +.icon-italic:before { + content: "\f033"; +} +.icon-text-height:before { + content: "\f034"; +} +.icon-text-width:before { + content: "\f035"; +} +.icon-align-left:before { + content: "\f036"; +} +.icon-align-center:before { + content: "\f037"; +} +.icon-align-right:before { + content: "\f038"; +} +.icon-align-justify:before { + content: "\f039"; +} +.icon-list:before { + content: "\f03a"; +} +.icon-indent-left:before { + content: "\f03b"; +} +.icon-indent-right:before { + content: "\f03c"; +} +.icon-facetime-video:before { + content: "\f03d"; +} +.icon-picture:before { + content: "\f03e"; +} +.icon-pencil:before { + content: "\f040"; +} +.icon-map-marker:before { + content: "\f041"; +} +.icon-adjust:before { + content: "\f042"; +} +.icon-tint:before { + content: "\f043"; +} +.icon-edit:before { + content: "\f044"; +} +.icon-share:before { + content: "\f045"; +} +.icon-check:before { + content: "\f046"; +} +.icon-move:before { + content: "\f047"; +} +.icon-step-backward:before { + content: "\f048"; +} +.icon-fast-backward:before { + content: "\f049"; +} +.icon-backward:before { + content: "\f04a"; +} +.icon-play:before { + content: "\f04b"; +} +.icon-pause:before { + content: "\f04c"; +} +.icon-stop:before { + content: "\f04d"; +} +.icon-forward:before { + content: "\f04e"; +} +.icon-fast-forward:before { + content: "\f050"; +} +.icon-step-forward:before { + content: "\f051"; +} +.icon-eject:before { + content: "\f052"; +} +.icon-chevron-left:before { + content: "\f053"; +} +.icon-chevron-right:before { + content: "\f054"; +} +.icon-plus-sign:before { + content: "\f055"; +} +.icon-minus-sign:before { + content: "\f056"; +} +.icon-remove-sign:before { + content: "\f057"; +} +.icon-ok-sign:before { + content: "\f058"; +} +.icon-question-sign:before { + content: "\f059"; +} +.icon-info-sign:before { + content: "\f05a"; +} +.icon-screenshot:before { + content: "\f05b"; +} +.icon-remove-circle:before { + content: "\f05c"; +} +.icon-ok-circle:before { + content: "\f05d"; +} +.icon-ban-circle:before { + content: "\f05e"; +} +.icon-arrow-left:before { + content: "\f060"; +} +.icon-arrow-right:before { + content: "\f061"; +} +.icon-arrow-up:before { + content: "\f062"; +} +.icon-arrow-down:before { + content: "\f063"; +} +.icon-mail-forward:before, +.icon-share-alt:before { + content: "\f064"; +} +.icon-resize-full:before { + content: "\f065"; +} +.icon-resize-small:before { + content: "\f066"; +} +.icon-plus:before { + content: "\f067"; +} +.icon-minus:before { + content: "\f068"; +} +.icon-asterisk:before { + content: "\f069"; +} +.icon-exclamation-sign:before { + content: "\f06a"; +} +.icon-gift:before { + content: "\f06b"; +} +.icon-leaf:before { + content: "\f06c"; +} +.icon-fire:before { + content: "\f06d"; +} +.icon-eye-open:before { + content: "\f06e"; +} +.icon-eye-close:before { + content: "\f070"; +} +.icon-warning-sign:before { + content: "\f071"; +} +.icon-plane:before { + content: "\f072"; +} +.icon-calendar:before { + content: "\f073"; +} +.icon-random:before { + content: "\f074"; +} +.icon-comment:before { + content: "\f075"; +} +.icon-magnet:before { + content: "\f076"; +} +.icon-chevron-up:before { + content: "\f077"; +} +.icon-chevron-down:before { + content: "\f078"; +} +.icon-retweet:before { + content: "\f079"; +} +.icon-shopping-cart:before { + content: "\f07a"; +} +.icon-folder-close:before { + content: "\f07b"; +} +.icon-folder-open:before { + content: "\f07c"; +} +.icon-resize-vertical:before { + content: "\f07d"; +} +.icon-resize-horizontal:before { + content: "\f07e"; +} +.icon-bar-chart:before { + content: "\f080"; +} +.icon-twitter-sign:before { + content: "\f081"; +} +.icon-facebook-sign:before { + content: "\f082"; +} +.icon-camera-retro:before { + content: "\f083"; +} +.icon-key:before { + content: "\f084"; +} +.icon-gears:before, +.icon-cogs:before { + content: "\f085"; +} +.icon-comments:before { + content: "\f086"; +} +.icon-thumbs-up-alt:before { + content: "\f087"; +} +.icon-thumbs-down-alt:before { + content: "\f088"; +} +.icon-star-half:before { + content: "\f089"; +} +.icon-heart-empty:before { + content: "\f08a"; +} +.icon-signout:before { + content: "\f08b"; +} +.icon-linkedin-sign:before { + content: "\f08c"; +} +.icon-pushpin:before { + content: "\f08d"; +} +.icon-external-link:before { + content: "\f08e"; +} +.icon-signin:before { + content: "\f090"; +} +.icon-trophy:before { + content: "\f091"; +} +.icon-github-sign:before { + content: "\f092"; +} +.icon-upload-alt:before { + content: "\f093"; +} +.icon-lemon:before { + content: "\f094"; +} +.icon-phone:before { + content: "\f095"; +} +.icon-unchecked:before, +.icon-check-empty:before { + content: "\f096"; +} +.icon-bookmark-empty:before { + content: "\f097"; +} +.icon-phone-sign:before { + content: "\f098"; +} +.icon-twitter:before { + content: "\f099"; +} +.icon-facebook:before { + content: "\f09a"; +} +.icon-github:before { + content: "\f09b"; +} +.icon-unlock:before { + content: "\f09c"; +} +.icon-credit-card:before { + content: "\f09d"; +} +.icon-rss:before { + content: "\f09e"; +} +.icon-hdd:before { + content: "\f0a0"; +} +.icon-bullhorn:before { + content: "\f0a1"; +} +.icon-bell:before { + content: "\f0a2"; +} +.icon-certificate:before { + content: "\f0a3"; +} +.icon-hand-right:before { + content: "\f0a4"; +} +.icon-hand-left:before { + content: "\f0a5"; +} +.icon-hand-up:before { + content: "\f0a6"; +} +.icon-hand-down:before { + content: "\f0a7"; +} +.icon-circle-arrow-left:before { + content: "\f0a8"; +} +.icon-circle-arrow-right:before { + content: "\f0a9"; +} +.icon-circle-arrow-up:before { + content: "\f0aa"; +} +.icon-circle-arrow-down:before { + content: "\f0ab"; +} +.icon-globe:before { + content: "\f0ac"; +} +.icon-wrench:before { + content: "\f0ad"; +} +.icon-tasks:before { + content: "\f0ae"; +} +.icon-filter:before { + content: "\f0b0"; +} +.icon-briefcase:before { + content: "\f0b1"; +} +.icon-fullscreen:before { + content: "\f0b2"; +} +.icon-group:before { + content: "\f0c0"; +} +.icon-link:before { + content: "\f0c1"; +} +.icon-cloud:before { + content: "\f0c2"; +} +.icon-beaker:before { + content: "\f0c3"; +} +.icon-cut:before { + content: "\f0c4"; +} +.icon-copy:before { + content: "\f0c5"; +} +.icon-paperclip:before, +.icon-paper-clip:before { + content: "\f0c6"; +} +.icon-save:before { + content: "\f0c7"; +} +.icon-sign-blank:before { + content: "\f0c8"; +} +.icon-reorder:before { + content: "\f0c9"; +} +.icon-list-ul:before { + content: "\f0ca"; +} +.icon-list-ol:before { + content: "\f0cb"; +} +.icon-strikethrough:before { + content: "\f0cc"; +} +.icon-underline:before { + content: "\f0cd"; +} +.icon-table:before { + content: "\f0ce"; +} +.icon-magic:before { + content: "\f0d0"; +} +.icon-truck:before { + content: "\f0d1"; +} +.icon-pinterest:before { + content: "\f0d2"; +} +.icon-pinterest-sign:before { + content: "\f0d3"; +} +.icon-google-plus-sign:before { + content: "\f0d4"; +} +.icon-google-plus:before { + content: "\f0d5"; +} +.icon-money:before { + content: "\f0d6"; +} +.icon-caret-down:before { + content: "\f0d7"; +} +.icon-caret-up:before { + content: "\f0d8"; +} +.icon-caret-left:before { + content: "\f0d9"; +} +.icon-caret-right:before { + content: "\f0da"; +} +.icon-columns:before { + content: "\f0db"; +} +.icon-sort:before { + content: "\f0dc"; +} +.icon-sort-down:before { + content: "\f0dd"; +} +.icon-sort-up:before { + content: "\f0de"; +} +.icon-envelope:before { + content: "\f0e0"; +} +.icon-linkedin:before { + content: "\f0e1"; +} +.icon-rotate-left:before, +.icon-undo:before { + content: "\f0e2"; +} +.icon-legal:before { + content: "\f0e3"; +} +.icon-dashboard:before { + content: "\f0e4"; +} +.icon-comment-alt:before { + content: "\f0e5"; +} +.icon-comments-alt:before { + content: "\f0e6"; +} +.icon-bolt:before { + content: "\f0e7"; +} +.icon-sitemap:before { + content: "\f0e8"; +} +.icon-umbrella:before { + content: "\f0e9"; +} +.icon-paste:before { + content: "\f0ea"; +} +.icon-lightbulb:before { + content: "\f0eb"; +} +.icon-exchange:before { + content: "\f0ec"; +} +.icon-cloud-download:before { + content: "\f0ed"; +} +.icon-cloud-upload:before { + content: "\f0ee"; +} +.icon-user-md:before { + content: "\f0f0"; +} +.icon-stethoscope:before { + content: "\f0f1"; +} +.icon-suitcase:before { + content: "\f0f2"; +} +.icon-bell-alt:before { + content: "\f0f3"; +} +.icon-coffee:before { + content: "\f0f4"; +} +.icon-food:before { + content: "\f0f5"; +} +.icon-file-text-alt:before { + content: "\f0f6"; +} +.icon-building:before { + content: "\f0f7"; +} +.icon-hospital:before { + content: "\f0f8"; +} +.icon-ambulance:before { + content: "\f0f9"; +} +.icon-medkit:before { + content: "\f0fa"; +} +.icon-fighter-jet:before { + content: "\f0fb"; +} +.icon-beer:before { + content: "\f0fc"; +} +.icon-h-sign:before { + content: "\f0fd"; +} +.icon-plus-sign-alt:before { + content: "\f0fe"; +} +.icon-double-angle-left:before { + content: "\f100"; +} +.icon-double-angle-right:before { + content: "\f101"; +} +.icon-double-angle-up:before { + content: "\f102"; +} +.icon-double-angle-down:before { + content: "\f103"; +} +.icon-angle-left:before { + content: "\f104"; +} +.icon-angle-right:before { + content: "\f105"; +} +.icon-angle-up:before { + content: "\f106"; +} +.icon-angle-down:before { + content: "\f107"; +} +.icon-desktop:before { + content: "\f108"; +} +.icon-laptop:before { + content: "\f109"; +} +.icon-tablet:before { + content: "\f10a"; +} +.icon-mobile-phone:before { + content: "\f10b"; +} +.icon-circle-blank:before { + content: "\f10c"; +} +.icon-quote-left:before { + content: "\f10d"; +} +.icon-quote-right:before { + content: "\f10e"; +} +.icon-spinner:before { + content: "\f110"; +} +.icon-circle:before { + content: "\f111"; +} +.icon-mail-reply:before, +.icon-reply:before { + content: "\f112"; +} +.icon-github-alt:before { + content: "\f113"; +} +.icon-folder-close-alt:before { + content: "\f114"; +} +.icon-folder-open-alt:before { + content: "\f115"; +} +.icon-expand-alt:before { + content: "\f116"; +} +.icon-collapse-alt:before { + content: "\f117"; +} +.icon-smile:before { + content: "\f118"; +} +.icon-frown:before { + content: "\f119"; +} +.icon-meh:before { + content: "\f11a"; +} +.icon-gamepad:before { + content: "\f11b"; +} +.icon-keyboard:before { + content: "\f11c"; +} +.icon-flag-alt:before { + content: "\f11d"; +} +.icon-flag-checkered:before { + content: "\f11e"; +} +.icon-terminal:before { + content: "\f120"; +} +.icon-code:before { + content: "\f121"; +} +.icon-reply-all:before { + content: "\f122"; +} +.icon-mail-reply-all:before { + content: "\f122"; +} +.icon-star-half-full:before, +.icon-star-half-empty:before { + content: "\f123"; +} +.icon-location-arrow:before { + content: "\f124"; +} +.icon-crop:before { + content: "\f125"; +} +.icon-code-fork:before { + content: "\f126"; +} +.icon-unlink:before { + content: "\f127"; +} +.icon-question:before { + content: "\f128"; +} +.icon-info:before { + content: "\f129"; +} +.icon-exclamation:before { + content: "\f12a"; +} +.icon-superscript:before { + content: "\f12b"; +} +.icon-subscript:before { + content: "\f12c"; +} +.icon-eraser:before { + content: "\f12d"; +} +.icon-puzzle-piece:before { + content: "\f12e"; +} +.icon-microphone:before { + content: "\f130"; +} +.icon-microphone-off:before { + content: "\f131"; +} +.icon-shield:before { + content: "\f132"; +} +.icon-calendar-empty:before { + content: "\f133"; +} +.icon-fire-extinguisher:before { + content: "\f134"; +} +.icon-rocket:before { + content: "\f135"; +} +.icon-maxcdn:before { + content: "\f136"; +} +.icon-chevron-sign-left:before { + content: "\f137"; +} +.icon-chevron-sign-right:before { + content: "\f138"; +} +.icon-chevron-sign-up:before { + content: "\f139"; +} +.icon-chevron-sign-down:before { + content: "\f13a"; +} +.icon-html5:before { + content: "\f13b"; +} +.icon-css3:before { + content: "\f13c"; +} +.icon-anchor:before { + content: "\f13d"; +} +.icon-unlock-alt:before { + content: "\f13e"; +} +.icon-bullseye:before { + content: "\f140"; +} +.icon-ellipsis-horizontal:before { + content: "\f141"; +} +.icon-ellipsis-vertical:before { + content: "\f142"; +} +.icon-rss-sign:before { + content: "\f143"; +} +.icon-play-sign:before { + content: "\f144"; +} +.icon-ticket:before { + content: "\f145"; +} +.icon-minus-sign-alt:before { + content: "\f146"; +} +.icon-check-minus:before { + content: "\f147"; +} +.icon-level-up:before { + content: "\f148"; +} +.icon-level-down:before { + content: "\f149"; +} +.icon-check-sign:before { + content: "\f14a"; +} +.icon-edit-sign:before { + content: "\f14b"; +} +.icon-external-link-sign:before { + content: "\f14c"; +} +.icon-share-sign:before { + content: "\f14d"; +} +.icon-compass:before { + content: "\f14e"; +} +.icon-collapse:before { + content: "\f150"; +} +.icon-collapse-top:before { + content: "\f151"; +} +.icon-expand:before { + content: "\f152"; +} +.icon-euro:before, +.icon-eur:before { + content: "\f153"; +} +.icon-gbp:before { + content: "\f154"; +} +.icon-dollar:before, +.icon-usd:before { + content: "\f155"; +} +.icon-rupee:before, +.icon-inr:before { + content: "\f156"; +} +.icon-yen:before, +.icon-jpy:before { + content: "\f157"; +} +.icon-renminbi:before, +.icon-cny:before { + content: "\f158"; +} +.icon-won:before, +.icon-krw:before { + content: "\f159"; +} +.icon-bitcoin:before, +.icon-btc:before { + content: "\f15a"; +} +.icon-file:before { + content: "\f15b"; +} +.icon-file-text:before { + content: "\f15c"; +} +.icon-sort-by-alphabet:before { + content: "\f15d"; +} +.icon-sort-by-alphabet-alt:before { + content: "\f15e"; +} +.icon-sort-by-attributes:before { + content: "\f160"; +} +.icon-sort-by-attributes-alt:before { + content: "\f161"; +} +.icon-sort-by-order:before { + content: "\f162"; +} +.icon-sort-by-order-alt:before { + content: "\f163"; +} +.icon-thumbs-up:before { + content: "\f164"; +} +.icon-thumbs-down:before { + content: "\f165"; +} +.icon-youtube-sign:before { + content: "\f166"; +} +.icon-youtube:before { + content: "\f167"; +} +.icon-xing:before { + content: "\f168"; +} +.icon-xing-sign:before { + content: "\f169"; +} +.icon-youtube-play:before { + content: "\f16a"; +} +.icon-dropbox:before { + content: "\f16b"; +} +.icon-stackexchange:before { + content: "\f16c"; +} +.icon-instagram:before { + content: "\f16d"; +} +.icon-flickr:before { + content: "\f16e"; +} +.icon-adn:before { + content: "\f170"; +} +.icon-bitbucket:before { + content: "\f171"; +} +.icon-bitbucket-sign:before { + content: "\f172"; +} +.icon-tumblr:before { + content: "\f173"; +} +.icon-tumblr-sign:before { + content: "\f174"; +} +.icon-long-arrow-down:before { + content: "\f175"; +} +.icon-long-arrow-up:before { + content: "\f176"; +} +.icon-long-arrow-left:before { + content: "\f177"; +} +.icon-long-arrow-right:before { + content: "\f178"; +} +.icon-apple:before { + content: "\f179"; +} +.icon-windows:before { + content: "\f17a"; +} +.icon-android:before { + content: "\f17b"; +} +.icon-linux:before { + content: "\f17c"; +} +.icon-dribbble:before { + content: "\f17d"; +} +.icon-skype:before { + content: "\f17e"; +} +.icon-foursquare:before { + content: "\f180"; +} +.icon-trello:before { + content: "\f181"; +} +.icon-female:before { + content: "\f182"; +} +.icon-male:before { + content: "\f183"; +} +.icon-gittip:before { + content: "\f184"; +} +.icon-sun:before { + content: "\f185"; +} +.icon-moon:before { + content: "\f186"; +} +.icon-archive:before { + content: "\f187"; +} +.icon-bug:before { + content: "\f188"; +} +.icon-vk:before { + content: "\f189"; +} +.icon-weibo:before { + content: "\f18a"; +} +.icon-renren:before { + content: "\f18b"; +} diff --git a/kafka-dse-webui/bin/src/main/resources/static/css/jquery.dataTables.min.css b/kafka-dse-webui/bin/src/main/resources/static/css/jquery.dataTables.min.css new file mode 100644 index 0000000..781de6b --- /dev/null +++ b/kafka-dse-webui/bin/src/main/resources/static/css/jquery.dataTables.min.css @@ -0,0 +1 @@ +table.dataTable{width:100%;margin:0 auto;clear:both;border-collapse:separate;border-spacing:0}table.dataTable thead th,table.dataTable tfoot th{font-weight:bold}table.dataTable thead th,table.dataTable thead td{padding:10px 18px;border-bottom:1px solid #111}table.dataTable thead th:active,table.dataTable thead td:active{outline:none}table.dataTable tfoot th,table.dataTable tfoot td{padding:10px 18px 6px 18px;border-top:1px solid #111}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_desc{cursor:pointer;*cursor:hand}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_desc,table.dataTable thead .sorting_asc_disabled,table.dataTable thead .sorting_desc_disabled{background-repeat:no-repeat;background-position:center right}table.dataTable thead .sorting{background-image:url("../images/sort_both.png")}table.dataTable thead .sorting_asc{background-image:url("../images/sort_asc.png")}table.dataTable thead .sorting_desc{background-image:url("../images/sort_desc.png")}table.dataTable thead .sorting_asc_disabled{background-image:url("../images/sort_asc_disabled.png")}table.dataTable thead .sorting_desc_disabled{background-image:url("../images/sort_desc_disabled.png")}table.dataTable tbody tr{background-color:#ffffff}table.dataTable tbody tr.selected{background-color:#B0BED9}table.dataTable tbody th,table.dataTable tbody td{padding:8px 10px}table.dataTable.row-border tbody th,table.dataTable.row-border tbody td,table.dataTable.display tbody th,table.dataTable.display tbody td{border-top:1px solid #ddd}table.dataTable.row-border tbody tr:first-child th,table.dataTable.row-border tbody tr:first-child td,table.dataTable.display tbody tr:first-child th,table.dataTable.display tbody tr:first-child td{border-top:none}table.dataTable.cell-border tbody th,table.dataTable.cell-border tbody td{border-top:1px solid #ddd;border-right:1px solid #ddd}table.dataTable.cell-border tbody tr th:first-child,table.dataTable.cell-border tbody tr td:first-child{border-left:1px solid #ddd}table.dataTable.cell-border tbody tr:first-child th,table.dataTable.cell-border tbody tr:first-child td{border-top:none}table.dataTable.stripe tbody tr.odd,table.dataTable.display tbody tr.odd{background-color:#f9f9f9}table.dataTable.stripe tbody tr.odd.selected,table.dataTable.display tbody tr.odd.selected{background-color:#acbad4}table.dataTable.hover tbody tr:hover,table.dataTable.display tbody tr:hover{background-color:#f6f6f6}table.dataTable.hover tbody tr:hover.selected,table.dataTable.display tbody tr:hover.selected{background-color:#aab7d1}table.dataTable.order-column tbody tr>.sorting_1,table.dataTable.order-column tbody tr>.sorting_2,table.dataTable.order-column tbody tr>.sorting_3,table.dataTable.display tbody tr>.sorting_1,table.dataTable.display tbody tr>.sorting_2,table.dataTable.display tbody tr>.sorting_3{background-color:#fafafa}table.dataTable.order-column tbody tr.selected>.sorting_1,table.dataTable.order-column tbody tr.selected>.sorting_2,table.dataTable.order-column tbody tr.selected>.sorting_3,table.dataTable.display tbody tr.selected>.sorting_1,table.dataTable.display tbody tr.selected>.sorting_2,table.dataTable.display tbody tr.selected>.sorting_3{background-color:#acbad5}table.dataTable.display tbody tr.odd>.sorting_1,table.dataTable.order-column.stripe tbody tr.odd>.sorting_1{background-color:#f1f1f1}table.dataTable.display tbody tr.odd>.sorting_2,table.dataTable.order-column.stripe tbody tr.odd>.sorting_2{background-color:#f3f3f3}table.dataTable.display tbody tr.odd>.sorting_3,table.dataTable.order-column.stripe tbody tr.odd>.sorting_3{background-color:whitesmoke}table.dataTable.display tbody tr.odd.selected>.sorting_1,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_1{background-color:#a6b4cd}table.dataTable.display tbody tr.odd.selected>.sorting_2,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_2{background-color:#a8b5cf}table.dataTable.display tbody tr.odd.selected>.sorting_3,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_3{background-color:#a9b7d1}table.dataTable.display tbody tr.even>.sorting_1,table.dataTable.order-column.stripe tbody tr.even>.sorting_1{background-color:#fafafa}table.dataTable.display tbody tr.even>.sorting_2,table.dataTable.order-column.stripe tbody tr.even>.sorting_2{background-color:#fcfcfc}table.dataTable.display tbody tr.even>.sorting_3,table.dataTable.order-column.stripe tbody tr.even>.sorting_3{background-color:#fefefe}table.dataTable.display tbody tr.even.selected>.sorting_1,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_1{background-color:#acbad5}table.dataTable.display tbody tr.even.selected>.sorting_2,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_2{background-color:#aebcd6}table.dataTable.display tbody tr.even.selected>.sorting_3,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_3{background-color:#afbdd8}table.dataTable.display tbody tr:hover>.sorting_1,table.dataTable.order-column.hover tbody tr:hover>.sorting_1{background-color:#eaeaea}table.dataTable.display tbody tr:hover>.sorting_2,table.dataTable.order-column.hover tbody tr:hover>.sorting_2{background-color:#ececec}table.dataTable.display tbody tr:hover>.sorting_3,table.dataTable.order-column.hover tbody tr:hover>.sorting_3{background-color:#efefef}table.dataTable.display tbody tr:hover.selected>.sorting_1,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_1{background-color:#a2aec7}table.dataTable.display tbody tr:hover.selected>.sorting_2,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_2{background-color:#a3b0c9}table.dataTable.display tbody tr:hover.selected>.sorting_3,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_3{background-color:#a5b2cb}table.dataTable.no-footer{border-bottom:1px solid #111}table.dataTable.nowrap th,table.dataTable.nowrap td{white-space:nowrap}table.dataTable.compact thead th,table.dataTable.compact thead td{padding:4px 17px 4px 4px}table.dataTable.compact tfoot th,table.dataTable.compact tfoot td{padding:4px}table.dataTable.compact tbody th,table.dataTable.compact tbody td{padding:4px}table.dataTable th.dt-left,table.dataTable td.dt-left{text-align:left}table.dataTable th.dt-center,table.dataTable td.dt-center,table.dataTable td.dataTables_empty{text-align:center}table.dataTable th.dt-right,table.dataTable td.dt-right{text-align:right}table.dataTable th.dt-justify,table.dataTable td.dt-justify{text-align:justify}table.dataTable th.dt-nowrap,table.dataTable td.dt-nowrap{white-space:nowrap}table.dataTable thead th.dt-head-left,table.dataTable thead td.dt-head-left,table.dataTable tfoot th.dt-head-left,table.dataTable tfoot td.dt-head-left{text-align:left}table.dataTable thead th.dt-head-center,table.dataTable thead td.dt-head-center,table.dataTable tfoot th.dt-head-center,table.dataTable tfoot td.dt-head-center{text-align:center}table.dataTable thead th.dt-head-right,table.dataTable thead td.dt-head-right,table.dataTable tfoot th.dt-head-right,table.dataTable tfoot td.dt-head-right{text-align:right}table.dataTable thead th.dt-head-justify,table.dataTable thead td.dt-head-justify,table.dataTable tfoot th.dt-head-justify,table.dataTable tfoot td.dt-head-justify{text-align:justify}table.dataTable thead th.dt-head-nowrap,table.dataTable thead td.dt-head-nowrap,table.dataTable tfoot th.dt-head-nowrap,table.dataTable tfoot td.dt-head-nowrap{white-space:nowrap}table.dataTable tbody th.dt-body-left,table.dataTable tbody td.dt-body-left{text-align:left}table.dataTable tbody th.dt-body-center,table.dataTable tbody td.dt-body-center{text-align:center}table.dataTable tbody th.dt-body-right,table.dataTable tbody td.dt-body-right{text-align:right}table.dataTable tbody th.dt-body-justify,table.dataTable tbody td.dt-body-justify{text-align:justify}table.dataTable tbody th.dt-body-nowrap,table.dataTable tbody td.dt-body-nowrap{white-space:nowrap}table.dataTable,table.dataTable th,table.dataTable td{-webkit-box-sizing:content-box;box-sizing:content-box}.dataTables_wrapper{position:relative;clear:both;*zoom:1;zoom:1}.dataTables_wrapper .dataTables_length{float:left}.dataTables_wrapper .dataTables_filter{float:right;text-align:right}.dataTables_wrapper .dataTables_filter input{margin-left:0.5em}.dataTables_wrapper .dataTables_info{clear:both;float:left;padding-top:0.755em}.dataTables_wrapper .dataTables_paginate{float:right;text-align:right;padding-top:0.25em}.dataTables_wrapper .dataTables_paginate .paginate_button{box-sizing:border-box;display:inline-block;min-width:1.5em;padding:0.5em 1em;margin-left:2px;text-align:center;text-decoration:none !important;cursor:pointer;*cursor:hand;color:#333 !important;border:1px solid transparent;border-radius:2px}.dataTables_wrapper .dataTables_paginate .paginate_button.current,.dataTables_wrapper .dataTables_paginate .paginate_button.current:hover{color:#333 !important;border:1px solid #979797;background-color:white;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #fff), color-stop(100%, #dcdcdc));background:-webkit-linear-gradient(top, #fff 0%, #dcdcdc 100%);background:-moz-linear-gradient(top, #fff 0%, #dcdcdc 100%);background:-ms-linear-gradient(top, #fff 0%, #dcdcdc 100%);background:-o-linear-gradient(top, #fff 0%, #dcdcdc 100%);background:linear-gradient(to bottom, #fff 0%, #dcdcdc 100%)}.dataTables_wrapper .dataTables_paginate .paginate_button.disabled,.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover,.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active{cursor:default;color:#666 !important;border:1px solid transparent;background:transparent;box-shadow:none}.dataTables_wrapper .dataTables_paginate .paginate_button:hover{color:white !important;border:1px solid #111;background-color:#585858;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #585858), color-stop(100%, #111));background:-webkit-linear-gradient(top, #585858 0%, #111 100%);background:-moz-linear-gradient(top, #585858 0%, #111 100%);background:-ms-linear-gradient(top, #585858 0%, #111 100%);background:-o-linear-gradient(top, #585858 0%, #111 100%);background:linear-gradient(to bottom, #585858 0%, #111 100%)}.dataTables_wrapper .dataTables_paginate .paginate_button:active{outline:none;background-color:#2b2b2b;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #2b2b2b), color-stop(100%, #0c0c0c));background:-webkit-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:-moz-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:-ms-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:-o-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:linear-gradient(to bottom, #2b2b2b 0%, #0c0c0c 100%);box-shadow:inset 0 0 3px #111}.dataTables_wrapper .dataTables_paginate .ellipsis{padding:0 1em}.dataTables_wrapper .dataTables_processing{position:absolute;top:50%;left:50%;width:100%;height:40px;margin-left:-50%;margin-top:-25px;padding-top:20px;text-align:center;font-size:1.2em;background-color:white;background:-webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255,255,255,0)), color-stop(25%, rgba(255,255,255,0.9)), color-stop(75%, rgba(255,255,255,0.9)), color-stop(100%, rgba(255,255,255,0)));background:-webkit-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:-moz-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:-ms-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:-o-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:linear-gradient(to right, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%)}.dataTables_wrapper .dataTables_length,.dataTables_wrapper .dataTables_filter,.dataTables_wrapper .dataTables_info,.dataTables_wrapper .dataTables_processing,.dataTables_wrapper .dataTables_paginate{color:#333}.dataTables_wrapper .dataTables_scroll{clear:both}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody{*margin-top:-1px;-webkit-overflow-scrolling:touch}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th,.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td{vertical-align:middle}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th>div.dataTables_sizing,.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td>div.dataTables_sizing{height:0;overflow:hidden;margin:0 !important;padding:0 !important}.dataTables_wrapper.no-footer .dataTables_scrollBody{border-bottom:1px solid #111}.dataTables_wrapper.no-footer div.dataTables_scrollHead table,.dataTables_wrapper.no-footer div.dataTables_scrollBody table{border-bottom:none}.dataTables_wrapper:after{visibility:hidden;display:block;content:"";clear:both;height:0}@media screen and (max-width: 767px){.dataTables_wrapper .dataTables_info,.dataTables_wrapper .dataTables_paginate{float:none;text-align:center}.dataTables_wrapper .dataTables_paginate{margin-top:0.5em}}@media screen and (max-width: 640px){.dataTables_wrapper .dataTables_length,.dataTables_wrapper .dataTables_filter{float:none;text-align:center}.dataTables_wrapper .dataTables_filter{margin-top:0.5em}} diff --git a/kafka-dse-webui/bin/src/main/resources/static/css/style.css b/kafka-dse-webui/bin/src/main/resources/static/css/style.css new file mode 100644 index 0000000..ade010f --- /dev/null +++ b/kafka-dse-webui/bin/src/main/resources/static/css/style.css @@ -0,0 +1,1395 @@ +body { + background-image: url("../img/bodybg.png"); + font: 13px/1.7em 'Open Sans'; +} + +p { + font: 13px/1.7em 'Open Sans'; +} + +input, +button, +select, +textarea { + font-family: 'Open Sans'; +} + +.dropdown .dropdown-menu { + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.btn-icon-only { + padding-right: 3px; + padding-left: 3px; +} + +.table td { + vertical-align: middle; +} + +.table-bordered th { + background-color: #687684; + font-size: 10px; + color: #fff; +} + +.table tbody td { + color:#888888; + font-family: Monaco, Menlo, Consolas, "Courier New", monospace; + font-size:.9em; +} + +.table th, +.table td { + padding: 8px; + line-height: 20px; + text-align: left; + vertical-align: top; + border-top: 1px solid #dddddd; +} + + +/*------------------------------------------------------------------ +[2. Navbar / .navbar] +*/ + +.navbar .container { + position: relative; +} + +.navbar-inner { + padding: 0; + margin-top:-5px; + background-color: #0890D0; + background:#0890D0; + border-radius: 0; +} + +.navbar-fixed-top { + position: static; + height:55px +} + +.navbar .nav a { + font-size: 11px; +} +.navbar .nav>li>a { color:#fff !important;} +.navbar .brand { + font-weight: 500; + position: relative; + top: 2px; +} + + +/*------------------------------------------------------------------ +[3. Subnavbar / .subnavbar] +*/ + +.subnavbar { + margin-bottom: 2.5em; +} + +.subnavbar-inner { + background-image: url("../img/subnavbg.png"); + border-bottom: 1px solid #d6d6d6; + position:absolute; + top:60px; + left:0; + width:100%; +} + +.wrapper { + position:absolute; + top:130px; +} + +.subnavbar .container > ul { + display: inline-block; + height: 51px; + padding: 0; + margin: 0; +} + +.subnavbar .container > ul > li { + float: left; + min-width: 90px; + height: 55px; + padding: 0; + margin: 0; + margin-bottom:5px; + text-align: center; + list-style: none; + border-left: 1px solid #d9d9d9; +} + +.subnavbar .container > ul > li > a { + display: block; + + height: 100%; + padding: 0 15px; + + font-size: 12px; + font-weight: bold; + color: #687684; +} + +.subnavbar .container > ul > li > a:hover { + color: #0890D0; + text-decoration: none; +} + +.subnavbar .container > ul > li > a > i { + display: inline-block; + width: 24px; + height: 24px; + margin-top: 11px; + font-size: 20px; +} + +.subnavbar .container > ul > li > a > span { + display: block; +} + +.subnavbar .container > ul > li.active > a { + border-bottom:3px solid #DA4453; + color: #33495F; +} + +.subnavbar .container > ul > li.active2 > a { + color: #0890D0; + border-bottom:3px solid #0890D0; +} +.subnavbar .container > ul > li> a { +} + +.subnavbar .container > ul > li> a:hover { + border-bottom:2px solid #0890D0; +} + +.subnavbar .container > ul > li.active2 > a:hover { + border-bottom:2px solid #0890D0; +} + +.subnavbar .dropdown .dropdown-menu a { + font-size: 12px; +} + +.subnavbar .dropdown .dropdown-menu { + text-align: left; + + -webkit-border-top-left-radius: 0; + -webkit-border-top-right-radius: 0; + -moz-border-radius-topleft: 0; + -moz-border-radius-topright: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; + } + +.subnavbar .dropdown-menu::before { + content: ''; + display: inline-block; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid #CCC; + border-bottom-color: rgba(0, 0, 0, 0.2); + position: absolute; + top: -7px; + left: 9px; +} + +.subnavbar .dropdown-menu::after { + content: ''; + display: inline-block; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid white; + position: absolute; + top: -6px; + left: 10px; +} + + +.subnavbar .caret { + margin-top: 4px; + border-top-color: white; + border-bottom-color: white; +} + +.subnavbar .dropdown.open .caret { + display: none; +} + +.main { + padding-bottom: 2em; + border-bottom: 1px solid #000; +} + +.extra { + border-top: 1px solid #696969; + border-bottom: 1px solid #000; +} + +.extra-inner { + padding: 5px 0; + font-size: 11px; + color: #BBB; + background: #444; +} + +.extra-inner ul { + padding: 0; + + margin: 0; +} + +.extra-inner li { + margin-bottom: .6em; + list-style: none; +} + +.extra-inner a { + color: #999; +} + +.extra-inner a:hover { + color: #0890D0; + text-decoration:none; +} + +.extra a { + color: #999; +} + +.extra a:hover { + color: #FFF; + text-decoration: none; +} + + +.extra h4 { + margin-bottom: 1em; + font-weight: 400; +} + +.extra-inner h4 { + margin-bottom: 1em; + font-weight: 400; + color:white; +} + +.extra ul { + padding: 0; + margin: 0; +} + +.extra li { + margin-bottom: .6em; + list-style: none; +} + + +/*------------------------------------------------------------------ +[6. Footer/ .footer] +*/ + +.footer { + margin-top: 0; + border-top: 1px solid #292929; +} + +.footer-inner { + padding: 5px 0; + font-size: 12px; + background: #111; + color: #ddd; +} + +.footer a { + color: #ddd; +} + +.footer a:hover { + color: #FFF; + text-decoration: none; +} + + +/*------------------------------------------------------------------ +[6. Widget / .widget] +*/ + +.widget { + + position: relative; + clear: both; + + width: auto; + + margin-bottom: 2em; + + overflow: hidden; +} + +.widget-header { + + position: relative; + + height: 40px; + line-height: 40px; + + background: #f9f6f1; + background:-moz-linear-gradient(top, #f9f6f1 0%, #f2efea 100%); /* FF3.6+ */ + background:-webkit-gradient(linear, left top, left bottom, color-stop(0%,#f9f6f1), color-stop(100%,#f2efea)); /* Chrome,Safari4+ */ + background:-webkit-linear-gradient(top, #f9f6f1 0%,#f2efea 100%); /* Chrome10+,Safari5.1+ */ + background:-o-linear-gradient(top, #f9f6f1 0%,#f2efea 100%); /* Opera11.10+ */ + background:-ms-linear-gradient(top, #f9f6f1 0%,#f2efea 100%); /* IE10+ */ + background:linear-gradient(top, #f9f6f1 0%,#f2efea 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f9f6f1', endColorstr='#f2efea'); + -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#f9f6f1', endColorstr='#f2efea')"; + + color:#33495F; + border: 1px solid #d6d6d6; + + + -webkit-background-clip: padding-box; +} + + .widget-header h3 { + + position: relative; + top: 2px; + left: 10px; + + display: inline-block; + margin-right: 3em; + + font-size: 14px; + font-weight: 800; + color: #525252; + line-height: 18px; + + text-shadow: 1px 1px 2px rgba(255,255,255,.5); + } + + .widget-header [class^="icon-"], .widget-header [class*=" icon-"] { + + display: inline-block; + margin-left: 13px; + margin-right: -2px; + + font-size: 16px; + color: #555; + vertical-align: middle; + } + + +.widget-content { + padding: 20px 15px 15px; + + background: #FFF; + + + border: 1px solid #D5D5D5; + + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; +} + +.widget-header+.widget-content { + border-top: none; + + -webkit-border-top-left-radius: 0; + -webkit-border-top-right-radius: 0; + -moz-border-radius-topleft: 0; + -moz-border-radius-topright: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.widget-nopad .widget-content { + padding: 0; +} + +/* Widget Content Clearfix */ +.widget-content:before, +.widget-content:after { + content:""; + display:table; +} + +.widget-content:after { + clear:both; +} + +/* For IE 6/7 (trigger hasLayout) */ +.widget-content { + zoom:1; +} + +/* Widget Table */ + +.widget-table .widget-content { + padding: 0; +} + +.widget-table .table { + margin-bottom: 0; + + border: none; +} + +.widget-table .table tr td:first-child { + border-left: none; +} + +.widget-table .table tr th:first-child { + border-left: none; +} + + +/* Widget Plain */ + +.widget-plain { + + background: transparent; + + border: none; +} + +.widget-plain .widget-content { + padding: 0; + + background: transparent; + + border: none; +} + + +/* Widget Box */ + +.widget-box { + +} + +.widget-box .widget-content { + background: #E3E3E3; + background: #FFF; +} + + + + +/*------------------------------------------------------------------ +[7. Error / .error-container] +*/ + +.error-container { + margin-top: 4em; + margin-bottom: 4em; + text-align: center; +} + +.error-container h1 { + margin-bottom: .5em; + + font-size: 120px; + line-height: 1em; +} + +.error-container h2 { + margin-bottom: .75em; + font-size: 28px; +} + +.error-container .error-details { + margin-bottom: 1.5em; + + font-size: 16px; +} + +.error-container .error-actions a { + margin: 0 .5em; +} + + + +/* Message layout */ + + +ul.messages_layout { + position: relative; + margin: 0; + padding: 0 +} +ul.messages_layout li { + float: left; + list-style: none; + position: relative +} +ul.messages_layout li.left { + padding-left: 75px +} +ul.messages_layout li.right { + padding-right: 75px +} +ul.messages_layout li.right .avatar { + right: 0; + left: auto +} +ul.messages_layout li.right .message_wrap .arrow { + right: -12px; + left: auto; + background-position: 0 -213px; + height: 15px; + width: 12px +} +ul.messages_layout li.by_myself .message_wrap { + border: 1px solid #b3cdf8 +} +ul.messages_layout li.by_myself .message_wrap .info a.name { + color: #4a8cf7 +} +ul.messages_layout li a.avatar { + position: absolute; + left: 0; + top: 0 +} +ul.messages_layout li a.avatar img { + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px +} +ul.messages_layout li .message_wrap { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + position: relative; + border: 1px solid #e9e9e9; + padding: 10px; + border: 1px solid #cbcbcb; + margin-bottom: 20px; + float: left; + background: #fefefe; + -webkit-box-shadow: rgba(0,0,0,0.1) 0 1px 0; + -moz-box-shadow: rgba(0,0,0,0.1) 0 1px 0; + box-shadow: rgba(0,0,0,0.1) 0 1px 0; +} +ul.messages_layout li .message_wrap .arrow { + background-position: 0 -228px; + height: 15px; + width: 12px; + height: 15px; + width: 12px; + position: absolute; + left: -12px; + top: 13px +} +ul.messages_layout li .message_wrap .info { + float: left; + width: 100%; + border-bottom: 1px solid #fff; + line-height: 23px +} +ul.messages_layout li .message_wrap .info .name { + float: left; + font-weight: bold; + color: #483734 +} +ul.messages_layout li .message_wrap .info .time { + float: left; + font-size: 11px; + margin-left: 6px +} +ul.messages_layout li .message_wrap .text { + float: left; + width: 100%; + border-top: 1px solid #cfcfcf; + padding-top: 5px +} + +ul.messages_layout .dropdown-menu li{ width:100%; font-size:11px;} + + +/* Full Calendar */ + +.fc { + direction: ltr; + text-align: left; + position: relative +} +.fc table { + border-collapse: collapse; + border-spacing: 0 +} +html .fc, .fc table { + font-size: 1em +} +.fc td, .fc th { + padding: 0; + vertical-align: top +} +.fc-header td { + white-space: nowrap; + background: none +} +.fc-header-left { + width: 100%; + text-align: left; + position: absolute; + left: 0; + top: 6px +} +.fc-header-left .fc-button { + margin: 0; + position: relative +} +.fc-header-left .fc-button-prev, .fc-header-left .fc-button-next { + float: left; + border: none; + padding: 14px 10px; + opacity: 0.5 +} +.fc-header-left .fc-button-prev .fc-button-inner, .fc-header-left .fc-button-next .fc-button-inner { + border: none +} +.fc-header-left .fc-button-prev .fc-button-inner .fc-button-content, .fc-header-left .fc-button-next .fc-button-inner .fc-button-content { + display: none +} +.fc-header-left .fc-button-prev.fc-state-hover, .fc-header-left .fc-button-next.fc-state-hover { + opacity: 1 +} +.fc-header-left .fc-button-prev.fc-state-down, .fc-header-left .fc-button-next.fc-state-down { + background: none !important; + margin-top: -1px +} +.fc-header-left .fc-button-prev .fc-button-inner { + background-position: 0 -351px; + height: 16px; + width: 11px +} +.fc-header-left .fc-button-next { + float: right +} +.fc-header-left .fc-button-next .fc-button-inner { + background-position: 0 -367px; + height: 16px; + width: 11px +} +.fc-header-center { + text-align: center +} +.fc-header-right { + text-align: right; + position: absolute; + top: -34px; + right: 10px +} +.fc-header-title { + display: inline-block; + vertical-align: top +} +.fc-header-title h2 { + margin-top: 0; + white-space: nowrap; + font-size: 1.1rem; + color: #6C737F; + line-height: 55px; +} +.fc .fc-header-space { + padding-left: 10px +} +.fc-header .fc-button { + margin-bottom: 1em; + vertical-align: top +} +.fc-header .fc-button { + margin-right: -1px +} +.fc-header .fc-corner-right { + margin-right: 1px +} +.fc-header .ui-corner-right { + margin-right: 0 +} +.fc-header .fc-state-hover, .fc-header .ui-state-hover { + z-index: 2 +} +.fc-header .fc-state-down { + z-index: 3 +} +.fc-header .fc-state-active, .fc-header .ui-state-active { + z-index: 4 +} +.fc-content { + clear: both; + background: #f9f9f9 +} +.fc-view { + width: 100%; + overflow: hidden +} +.fc-view thead { + background:#e9ecf1; + line-height: 35px +} +.fc-widget-header, .fc-widget-content { + border: 1px solid #ccc +} +.fc-state-highlight { + background: #F4F3E6 +} +.fc-cell-overlay { + background: #9cf; + opacity: .2; + filter: alpha(opacity=20) +} +.fc-button { + position: relative; + display: inline-block; + cursor: pointer +} +.fc-button-today{margin-top: 8px !important;} +.fc-state-default { + border-style: solid; + border-width: 1px 0 +} +.fc-button-inner { + position: relative; + float: left; + overflow: hidden +} +.fc-state-default .fc-button-inner { + border-style: solid; + border-width: 0 1px +} +.fc-button-content { + position: relative; + float: leftx; + height: 1.9em; + line-height: 1.9em; + padding: 0 .6em; + white-space: nowrap +} +.fc-button-content .fc-icon-wrap { + position: relative; + float: left; + top: 50% +} +.fc-button-content .ui-icon { + position: relative; + float: left; + margin-top: -50%; +*margin-top:0; +*top:-50% +} +.fc-state-default .fc-button-effect { + position: absolute; + top: 50%; + left: 0 +} +.fc-state-default .fc-button-effect span { + position: absolute; + top: -100px; + left: 0; + width: 500px; + height: 100px; + border-width: 100px 0 0 1px; + border-style: solid; + border-color: #fff; + background: #444; + opacity: .09; + filter: alpha(opacity=9) +} +.fc-state-default, .fc-state-default .fc-button-inner { + border-style: solid; + border-color: #ccc #bbb #aaa; + color: #000 +} +.fc-state-hover, .fc-state-hover .fc-button-inner { + border-color: #999 +} +.fc-state-down { + border-color: #555; + background: #777 +} +.fc-state-active, .fc-state-active .fc-button-inner { + border-color: #555; + background: #777; + color: #fff +} +.fc-state-disabled, .fc-state-disabled .fc-button-inner { + color: #999; + border-color: #ddd +} +.fc-state-disabled { + cursor: default +} +.fc-state-disabled .fc-button-effect { + display: none +} +.fc-event { + border-style: solid; + border-width: 0; + font-size: .85em; + cursor: default +} +a.fc-event, .fc-event-draggable { + cursor: pointer +} +a.fc-event { + text-decoration: none +} +.fc-rtl .fc-event { + text-align: right +} +.fc-event-skin { + border-color: #3f85f5; + background-color: #5e96ea; + color: #fff +} +.fc-event-inner { + position: relative; + width: 100%; + height: 100%; + border-style: solid; + border-width: 0; + overflow: hidden +} +.fc-event-time, .fc-event-title { + padding: 0 1px +} +.fc .ui-resizable-handle { + display: block; + position: absolute; + z-index: 99999; + overflow: hidden; + font-size: 300%; + line-height: 50% +} +.fc-event-hori { + border-width: 1px 0; + margin-bottom: 1px +} +.fc-event-hori .ui-resizable-e { + top: 0 !important; + right: -3px !important; + width: 7px !important; + height: 100% !important; + cursor: e-resize +} +.fc-event-hori .ui-resizable-w { + top: 0 !important; + left: -3px !important; + width: 7px !important; + height: 100% !important; + cursor: w-resize +} +.fc-event-hori .ui-resizable-handle { + _padding-bottom: 14px +} +.fc-corner-left { + margin-left: 1px +} +.fc-corner-left .fc-button-inner, .fc-corner-left .fc-event-inner { + margin-left: -1px +} +.fc-corner-right { + margin-right: 1px +} +.fc-corner-right .fc-button-inner, .fc-corner-right .fc-event-inner { + margin-right: -1px +} +.fc-corner-top { + margin-top: 1px +} +.fc-corner-top .fc-event-inner { + margin-top: -1px +} +.fc-corner-bottom { + margin-bottom: 1px +} +.fc-corner-bottom .fc-event-inner { + margin-bottom: -1px +} +.fc-corner-left .fc-event-inner { + border-left-width: 1px +} +.fc-corner-right .fc-event-inner { + border-right-width: 1px +} +.fc-corner-top .fc-event-inner { + border-top-width: 1px +} +.fc-corner-bottom .fc-event-inner { + border-bottom-width: 1px +} +table.fc-border-separate { + border-collapse: separate +} +.fc-border-separate th, .fc-border-separate td { + border-width: 1px 0 0 1px +} +.fc-border-separate th.fc-last, .fc-border-separate td.fc-last { + border-right-width: 1px +} +.fc-border-separate tr.fc-last th, .fc-border-separate tr.fc-last td { + border-bottom-width: 0; +} +.fc-first { + border-left-width: 0 !important +} +.fc-last { + border-right-width: 0 !important +} +.fc-grid th { + text-align: center +} +.fc-grid .fc-day-number { + float: right; + padding: 0 2px +} +.fc-grid .fc-other-month .fc-day-number { + opacity: 0.3; + filter: alpha(opacity=30) +} +.fc-grid .fc-day-content { + clear: both; + padding: 2px 2px 1px +} +.fc-grid .fc-event-time { + font-weight: bold +} +.fc-rtl .fc-grid .fc-day-number { + float: left +} +.fc-rtl .fc-grid .fc-event-time { + float: right +} +.fc-agenda table { + border-collapse: separate +} +.fc-agenda-days th { + text-align: center +} +.fc-agenda .fc-agenda-axis { + width: 60px !important; + padding: 0 4px; + vertical-align: middle; + text-align: right; + white-space: nowrap; + font-weight: normal +} +.fc-agenda .fc-day-content { + padding: 2px 2px 1px +} +.fc-agenda-days .fc-agenda-axis { + border-right-width: 1px +} +.fc-agenda-days .fc-col0 { + border-left-width: 0 +} +.fc-agenda-allday th { + border-width: 0 1px +} +.fc-agenda-allday .fc-day-content { + min-height: 34px; + _height: 34px +} +.fc-agenda-divider-inner { + height: 2px; + overflow: hidden +} +.fc-widget-header .fc-agenda-divider-inner { + background: #eee +} +.fc-agenda-slots th { + border-width: 1px 1px 0 +} +.fc-agenda-slots td { + border-width: 1px 0 0; + background: none +} +.fc-agenda-slots td div { + height: 20px +} +.fc-agenda-slots tr.fc-slot0 th, .fc-agenda-slots tr.fc-slot0 td { + border-top-width: 0 +} +.fc-agenda-slots tr.fc-minor th, .fc-agenda-slots tr.fc-minor td { + border-top-style: dotted +} +.fc-agenda-slots tr.fc-minor th.ui-widget-header { +*border-top-style:solid +} +.fc-event-vert { + border-width: 0 1px +} +.fc-event-vert .fc-event-head, .fc-event-vert .fc-event-content { + position: relative; + z-index: 2; + width: 100%; + overflow: hidden +} +.fc-event-vert .fc-event-time { + white-space: nowrap; + font-size: 10px +} +.fc-event-vert .fc-event-bg { + position: absolute; + z-index: 1; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: #fff; + opacity: .3; + filter: alpha(opacity=30) +} +.fc .ui-draggable-dragging .fc-event-bg, .fc-select-helper .fc-event-bg { + display: none\9 +} +.fc-event-vert .ui-resizable-s { + bottom: 0 !important; + width: 100% !important; + height: 8px !important; + overflow: hidden !important; + line-height: 8px !important; + font-size: 11px !important; + font-family: monospace; + text-align: center; + cursor: s-resize +} +.fc-agenda .ui-resizable-resizing { + _overflow: hidden +} + +/*------------------------------------------------------------------ +[8. Miscellaneous] +*/ + +.chart-holder { + width: 100%; + height: 250px; +} + +.dropdown-menu li>a:hover, .dropdown-menu .active>a, .dropdown-menu .active>a:hover { background:#00ba8b;} + +.accordion-heading { background:#e5e5e5; } +.accordion-heading a { color:#545454; text-decoration:none; font-weight:bold; } + +.btn-facebook-alt i { +color: #23386a; +} +.btn-twitter-alt i { +color: #0098d0; +} +.btn-google-alt i { +color: #b6362d; +} +.btn-linkedin-alt i { +color: #0073b2; +} +.btn-pinterest-alt i { +color: #ab171e; +} +.btn-github-alt i { +color: #333; +} + +.all-icons li { list-style:none;} + +.ML0 { margin-left:0} +.MR0 { margin-right:0;} + + + +/*------------------------------------------------------------------ +[1. Max Width: 480px] +*/ + +@media (max-width: 480px) { + + .error-container h1 { + font-size: 72px; + } + +} + + +@media (max-width: 767px) { + + #main { + padding: 0 10px; + margin-right: -20px; + margin-left: -20px; + } + + .subnavbar { + margin-left: -20px; + margin-right: -20px; + } + + .subnavbar-inner { + height: auto; + } + + .subnavbar .container > ul { + width: 100%; + height: auto; + border: none; + left:0px + } + + .subnavbar .container > ul > li { + width: 33%; + height: 70px; + margin-bottom: 0; + border: none; + } + + .subnavbar .container > ul > li.active > a { + font-size: 11px; + background: transparent; + } + + .subnavbar .container > ul > li > a > i { + display: inline-block; + margin-bottom: 0; + font-size: 20px; + } + + .subnavbar-open-right .dropdown-menu { + left: auto; + right: 0; + } + + .subnavbar-open-right .dropdown-menu:before { + left: auto; + right: 12px; + } + .subnavbar-open-right .dropdown-menu:after { + left: auto; + right: 13px; + } + + .extra { + margin-right: -20px; + margin-left: -20px; + } + + .extra .container { + padding: 0 20px; + } + + .footer { + margin-right: -20px; + margin-left: -20px; + } + + .footer .container { + padding: 0 20px; + } + + .footer .footer-terms { + text-align: left; + } + + .footer .footer-terms a { + margin-left: 0; + margin-right: 1em; + } + +} + + + + + +/*------------------------------------------------------------------ +[3. Max Width: 979px] +*/ + +@media (max-width: 979px) { + + .navbar-fixed-top { + position: static; + margin-bottom: 0; + } + +} + + + + + + +/*------------------------------------------------------------------ +[2. Max Width: 1200px] +*/ + +@media (min-width: 1200px) { + .navbar .search-query { + width: 200px; + } + +} + +.btn-green { + background-color: hsl(156, 77%, 27%) !important; + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#1bd48a", endColorstr="#0f794f"); + background-image: -khtml-gradient(linear, left top, left bottom, from(#1bd48a), to(#0f794f)); + background-image: -moz-linear-gradient(top, #1bd48a, #0f794f); + background-image: -ms-linear-gradient(top, #1bd48a, #0f794f); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #1bd48a), color-stop(100%, #0f794f)); + background-image: -webkit-linear-gradient(top, #1bd48a, #0f794f); + background-image: -o-linear-gradient(top, #1bd48a, #0f794f); + background-image: linear-gradient(#1bd48a, #0f794f); + border-color: #0f794f #0f794f hsl(156, 77%, 22%); + color: #fff !important; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.33); + -webkit-font-smoothing: antialiased; +} + +.ff4j-tooltip { + display: inline; + position: relative; +} +.ff4j-tooltip:hover:after{ + background: #333; + background: rgba(0,0,0,.8); + border-radius: 5px; + bottom: 26px; + color: #fff; + content: attr(tooltip); + left: 20%; + padding: 5px 15px; + position: absolute; + z-index: 98; + width: 220px; +} +.ff4j-tooltip:hover:before { + border: solid; + border-color: #333 transparent; + border-width: 6px 6px 0 6px; + bottom: 20px; + content: ""; + left: 50%; + position: absolute; + z-index: 99; +} + +// SWITCH +.container > .switch {display: block;margin: 12px auto;} +.switch {position: relative;display: inline-block;vertical-align:top;width: 56px;height:20px;padding: 3px;background-color: white;border-radius: 18px;box-shadow: inset 0 -1px white, inset 0 1px 1px rgba(0, 0, 0, 0.05);cursor: pointer;background-image: -webkit-linear-gradient(top, #eeeeee, white 25px);background-image: -moz-linear-gradient(top, #eeeeee, white 25px);background-image: -o-linear-gradient(top, #eeeeee, white 25px);background-image: linear-gradient(to bottom, #eeeeee, white 25px);} +.switch-input { + position: absolute; + top: 0; + left: 0; + opacity: 0; +} + +.switch-label { + position: relative; + display: block; + height: inherit; + font-size: 10px; + text-transform: uppercase; + background: #eceeef; + border-radius: inherit; + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.12), inset 0 0 2px rgba(0, 0, 0, 0.15); + -webkit-transition: 0.15s ease-out; + -moz-transition: 0.15s ease-out; + -o-transition: 0.15s ease-out; + transition: 0.15s ease-out; + -webkit-transition-property: opacity background; + -moz-transition-property: opacity background; + -o-transition-property: opacity background; + transition-property: opacity background; +} +.switch-label:before, .switch-label:after { + position: absolute; + top: 50%; + margin-top: -.5em; + line-height: 1; + -webkit-transition: inherit; + -moz-transition: inherit; + -o-transition: inherit; + transition: inherit; +} +.switch-label:before { + content: attr(data-off); + right: 11px; + color: #aaa; + text-shadow: 0 1px rgba(255, 255, 255, 0.5); +} +.switch-label:after { + content: attr(data-on); + left: 11px; + color: white; + text-shadow: 0 1px rgba(0, 0, 0, 0.2); + opacity: 0; +} +.switch-input:checked ~ .switch-label { + background: #47a8d8; + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15), inset 0 0 3px rgba(0, 0, 0, 0.2); +} +.switch-input:checked ~ .switch-label:before { + opacity: 0; +} +.switch-input:checked ~ .switch-label:after { + opacity: 1; +} + +.switch-handle { + position: absolute; + top: 4px; + left: 4px; + width: 18px; + height: 18px; + background: white; + border-radius: 10px; + box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.2); + background-image: -webkit-linear-gradient(top, white 40%, #f0f0f0); + background-image: -moz-linear-gradient(top, white 40%, #f0f0f0); + background-image: -o-linear-gradient(top, white 40%, #f0f0f0); + background-image: linear-gradient(to bottom, white 40%, #f0f0f0); + -webkit-transition: left 0.15s ease-out; + -moz-transition: left 0.15s ease-out; + -o-transition: left 0.15s ease-out; + transition: left 0.15s ease-out; +} +.switch-handle:before { + content: ''; + position: absolute; + top: 50%; + left: 50%; + margin: -6px 0 0 -6px; + width: 12px; + height: 12px; + background: #888888; + border-radius: 6px; + box-shadow: inset 0 1px rgba(0, 0, 0, 0.02); + background-image: -webkit-linear-gradient(top, #eeeeee, white); + background-image: -moz-linear-gradient(top, #eeeeee, white); + background-image: -o-linear-gradient(top, #eeeeee, white); + background-image: linear-gradient(to bottom, #eeeeee, white); +} +.switch-input:checked ~ .switch-handle { + left: 40px; + box-shadow: -1px 1px 5px rgba(0, 0, 0, 0.2); +} + +.switch-green > .switch-input:checked ~ .switch-label { + background: #0890D0; +} + +.modal { overflow : visible; } +.modal-body { overflow-y : visible; overflow-y: scroll;} + +.maincurveStyle { + margin-right:250px; +} diff --git a/kafka-dse-webui/bin/src/main/resources/static/font/FontAwesome.otf b/kafka-dse-webui/bin/src/main/resources/static/font/FontAwesome.otf new file mode 100644 index 0000000..7012545 Binary files /dev/null and b/kafka-dse-webui/bin/src/main/resources/static/font/FontAwesome.otf differ diff --git a/kafka-dse-webui/bin/src/main/resources/static/font/fontawesome-webfont.eot b/kafka-dse-webui/bin/src/main/resources/static/font/fontawesome-webfont.eot new file mode 100644 index 0000000..0662cb9 Binary files /dev/null and b/kafka-dse-webui/bin/src/main/resources/static/font/fontawesome-webfont.eot differ diff --git a/kafka-dse-webui/bin/src/main/resources/static/font/fontawesome-webfont.svg b/kafka-dse-webui/bin/src/main/resources/static/font/fontawesome-webfont.svg new file mode 100644 index 0000000..2edb4ec --- /dev/null +++ b/kafka-dse-webui/bin/src/main/resources/static/font/fontawesome-webfont.svg @@ -0,0 +1,399 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/kafka-dse-webui/bin/src/main/resources/static/font/fontawesome-webfont.svgz b/kafka-dse-webui/bin/src/main/resources/static/font/fontawesome-webfont.svgz new file mode 100644 index 0000000..2a73cd7 Binary files /dev/null and b/kafka-dse-webui/bin/src/main/resources/static/font/fontawesome-webfont.svgz differ diff --git a/kafka-dse-webui/bin/src/main/resources/static/font/fontawesome-webfont.ttf b/kafka-dse-webui/bin/src/main/resources/static/font/fontawesome-webfont.ttf new file mode 100644 index 0000000..d365924 Binary files /dev/null and b/kafka-dse-webui/bin/src/main/resources/static/font/fontawesome-webfont.ttf differ diff --git a/kafka-dse-webui/bin/src/main/resources/static/font/fontawesome-webfont.woff b/kafka-dse-webui/bin/src/main/resources/static/font/fontawesome-webfont.woff new file mode 100644 index 0000000..b9bd17e Binary files /dev/null and b/kafka-dse-webui/bin/src/main/resources/static/font/fontawesome-webfont.woff differ diff --git a/kafka-dse-webui/bin/src/main/resources/static/font/fontawesome-webfontd41d.eot b/kafka-dse-webui/bin/src/main/resources/static/font/fontawesome-webfontd41d.eot new file mode 100644 index 0000000..3f669a7 Binary files /dev/null and b/kafka-dse-webui/bin/src/main/resources/static/font/fontawesome-webfontd41d.eot differ diff --git a/kafka-dse-webui/bin/src/main/resources/static/img/bodybg.png b/kafka-dse-webui/bin/src/main/resources/static/img/bodybg.png new file mode 100644 index 0000000..e96f5db Binary files /dev/null and b/kafka-dse-webui/bin/src/main/resources/static/img/bodybg.png differ diff --git a/kafka-dse-webui/bin/src/main/resources/static/img/glyphicons-halflings-white.png b/kafka-dse-webui/bin/src/main/resources/static/img/glyphicons-halflings-white.png new file mode 100644 index 0000000..3bf6484 Binary files /dev/null and b/kafka-dse-webui/bin/src/main/resources/static/img/glyphicons-halflings-white.png differ diff --git a/kafka-dse-webui/bin/src/main/resources/static/img/glyphicons-halflings.png b/kafka-dse-webui/bin/src/main/resources/static/img/glyphicons-halflings.png new file mode 100644 index 0000000..a996999 Binary files /dev/null and b/kafka-dse-webui/bin/src/main/resources/static/img/glyphicons-halflings.png differ diff --git a/kafka-dse-webui/bin/src/main/resources/static/img/logo-header.png b/kafka-dse-webui/bin/src/main/resources/static/img/logo-header.png new file mode 100644 index 0000000..30e3c50 Binary files /dev/null and b/kafka-dse-webui/bin/src/main/resources/static/img/logo-header.png differ diff --git a/kafka-dse-webui/bin/src/main/resources/static/img/logo_solr.png b/kafka-dse-webui/bin/src/main/resources/static/img/logo_solr.png new file mode 100644 index 0000000..05f1abd Binary files /dev/null and b/kafka-dse-webui/bin/src/main/resources/static/img/logo_solr.png differ diff --git a/kafka-dse-webui/bin/src/main/resources/static/img/logo_spark.png b/kafka-dse-webui/bin/src/main/resources/static/img/logo_spark.png new file mode 100644 index 0000000..f313b5d Binary files /dev/null and b/kafka-dse-webui/bin/src/main/resources/static/img/logo_spark.png differ diff --git a/kafka-dse-webui/bin/src/main/resources/static/img/pixel.gif b/kafka-dse-webui/bin/src/main/resources/static/img/pixel.gif new file mode 100644 index 0000000..1d11fa9 Binary files /dev/null and b/kafka-dse-webui/bin/src/main/resources/static/img/pixel.gif differ diff --git a/kafka-dse-webui/bin/src/main/resources/static/img/subnavbg.png b/kafka-dse-webui/bin/src/main/resources/static/img/subnavbg.png new file mode 100644 index 0000000..2f4febc Binary files /dev/null and b/kafka-dse-webui/bin/src/main/resources/static/img/subnavbg.png differ diff --git a/kafka-dse-webui/webui.log b/kafka-dse-webui/bin/src/main/resources/static/js/base.js similarity index 100% rename from kafka-dse-webui/webui.log rename to kafka-dse-webui/bin/src/main/resources/static/js/base.js diff --git a/kafka-dse-webui/bin/src/main/resources/static/js/bootstrap.js b/kafka-dse-webui/bin/src/main/resources/static/js/bootstrap.js new file mode 100644 index 0000000..1bd53fe --- /dev/null +++ b/kafka-dse-webui/bin/src/main/resources/static/js/bootstrap.js @@ -0,0 +1,575 @@ + +// button +!function ($) { + + "use strict"; // jshint ;_; + + var dismiss = '[data-dismiss="alert"]' + , Alert = function (el) { + $(el).on('click', dismiss, this.close) + } + + Alert.prototype.close = function (e) { + var $this = $(this) + , selector = $this.attr('data-target') + , $parent + + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 + } + + $parent = $(selector) + + e && e.preventDefault() + + $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent()) + + $parent.trigger(e = $.Event('close')) + + if (e.isDefaultPrevented()) return + + $parent.removeClass('in') + + function removeElement() { + $parent + .trigger('closed') + .remove() + } + + $.support.transition && $parent.hasClass('fade') ? + $parent.on($.support.transition.end, removeElement) : + removeElement() + } + + var old = $.fn.alert + + $.fn.alert = function (option) { + return this.each(function () { + var $this = $(this) + , data = $this.data('alert') + if (!data) $this.data('alert', (data = new Alert(this))) + if (typeof option == 'string') data[option].call($this) + }) + } + + $.fn.alert.Constructor = Alert + + $.fn.alert.noConflict = function () { + $.fn.alert = old + return this + } + + $(document).on('click.alert.data-api', dismiss, Alert.prototype.close) + +}(window.jQuery); + + +// button +!function ($) { + "use strict"; // jshint ;_; + + + /* BUTTON PUBLIC CLASS DEFINITION + * ============================== */ + + var Button = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, $.fn.button.defaults, options) + } + + Button.prototype.setState = function (state) { + var d = 'disabled' + , $el = this.$element + , data = $el.data() + , val = $el.is('input') ? 'val' : 'html' + + state = state + 'Text' + data.resetText || $el.data('resetText', $el[val]()) + + $el[val](data[state] || this.options[state]) + + // push to event loop to allow forms to submit + setTimeout(function () { + state == 'loadingText' ? + $el.addClass(d).attr(d, d) : + $el.removeClass(d).removeAttr(d) + }, 0) + } + + Button.prototype.toggle = function () { + var $parent = this.$element.closest('[data-toggle="buttons-radio"]') + + $parent && $parent + .find('.active') + .removeClass('active') + + this.$element.toggleClass('active') + } + + + /* BUTTON PLUGIN DEFINITION + * ======================== */ + + var old = $.fn.button + + $.fn.button = function (option) { + return this.each(function () { + var $this = $(this) + , data = $this.data('button') + , options = typeof option == 'object' && option + if (!data) $this.data('button', (data = new Button(this, options))) + if (option == 'toggle') data.toggle() + else if (option) data.setState(option) + }) + } + + $.fn.button.defaults = { + loadingText: 'loading...' + } + + $.fn.button.Constructor = Button + + + /* BUTTON NO CONFLICT + * ================== */ + + $.fn.button.noConflict = function () { + $.fn.button = old + return this + } + + + /* BUTTON DATA-API + * =============== */ + + $(document).on('click.button.data-api', '[data-toggle^=button]', function (e) { + var $btn = $(e.target) + if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') + $btn.button('toggle') + }) + +}(window.jQuery); + +// dropdown +!function ($) { + + "use strict"; // jshint ;_; + + + /* DROPDOWN CLASS DEFINITION + * ========================= */ + + var toggle = '[data-toggle=dropdown]' + , Dropdown = function (element) { + var $el = $(element).on('click.dropdown.data-api', this.toggle) + $('html').on('click.dropdown.data-api', function () { + $el.parent().removeClass('open') + }) + } + + Dropdown.prototype = { + + constructor: Dropdown + + , toggle: function (e) { + var $this = $(this) + , $parent + , isActive + + if ($this.is('.disabled, :disabled')) return + + $parent = getParent($this) + + isActive = $parent.hasClass('open') + + clearMenus() + + if (!isActive) { + $parent.toggleClass('open') + } + + $this.focus() + + return false + } + + , keydown: function (e) { + var $this + , $items + , $active + , $parent + , isActive + , index + + if (!/(38|40|27)/.test(e.keyCode)) return + + $this = $(this) + + e.preventDefault() + e.stopPropagation() + + if ($this.is('.disabled, :disabled')) return + + $parent = getParent($this) + + isActive = $parent.hasClass('open') + + if (!isActive || (isActive && e.keyCode == 27)) { + if (e.which == 27) $parent.find(toggle).focus() + return $this.click() + } + + $items = $('[role=menu] li:not(.divider):visible a', $parent) + + if (!$items.length) return + + index = $items.index($items.filter(':focus')) + + if (e.keyCode == 38 && index > 0) index-- // up + if (e.keyCode == 40 && index < $items.length - 1) index++ // down + if (!~index) index = 0 + + $items + .eq(index) + .focus() + } + + } + + function clearMenus() { + $(toggle).each(function () { + getParent($(this)).removeClass('open') + }) + } + + function getParent($this) { + var selector = $this.attr('data-target') + , $parent + + if (!selector) { + selector = $this.attr('href') + selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 + } + + $parent = selector && $(selector) + + if (!$parent || !$parent.length) $parent = $this.parent() + + return $parent + } + + + /* DROPDOWN PLUGIN DEFINITION + * ========================== */ + + var old = $.fn.dropdown + + $.fn.dropdown = function (option) { + return this.each(function () { + var $this = $(this) + , data = $this.data('dropdown') + if (!data) $this.data('dropdown', (data = new Dropdown(this))) + if (typeof option == 'string') data[option].call($this) + }) + } + + $.fn.dropdown.Constructor = Dropdown + + + /* DROPDOWN NO CONFLICT + * ==================== */ + + $.fn.dropdown.noConflict = function () { + $.fn.dropdown = old + return this + } + + + /* APPLY TO STANDARD DROPDOWN ELEMENTS + * =================================== */ + + $(document) + .on('click.dropdown.data-api', clearMenus) + .on('click.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() }) + .on('click.dropdown-menu', function (e) { e.stopPropagation() }) + .on('click.dropdown.data-api' , toggle, Dropdown.prototype.toggle) + .on('keydown.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown) + + }(window.jQuery); + + // modal + + !function ($) { + + "use strict"; // jshint ;_; + + + /* MODAL CLASS DEFINITION + * ====================== */ + + var Modal = function (element, options) { + this.options = options + this.$element = $(element) + .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this)) + this.options.remote && this.$element.find('.modal-body').load(this.options.remote) + } + + Modal.prototype = { + + constructor: Modal + + , toggle: function () { + return this[!this.isShown ? 'show' : 'hide']() + } + + , show: function () { + var that = this + , e = $.Event('show') + + this.$element.trigger(e) + + if (this.isShown || e.isDefaultPrevented()) return + + this.isShown = true + + this.escape() + + this.backdrop(function () { + var transition = $.support.transition && that.$element.hasClass('fade') + + if (!that.$element.parent().length) { + that.$element.appendTo(document.body) //don't move modals dom position + } + + that.$element.show() + + if (transition) { + that.$element[0].offsetWidth // force reflow + } + + that.$element + .addClass('in') + .attr('aria-hidden', false) + + that.enforceFocus() + + transition ? + that.$element.one($.support.transition.end, function () { that.$element.focus().trigger('shown') }) : + that.$element.focus().trigger('shown') + + }) + } + + , hide: function (e) { + e && e.preventDefault() + + var that = this + + e = $.Event('hide') + + this.$element.trigger(e) + + if (!this.isShown || e.isDefaultPrevented()) return + + this.isShown = false + + this.escape() + + $(document).off('focusin.modal') + + this.$element + .removeClass('in') + .attr('aria-hidden', true) + + $.support.transition && this.$element.hasClass('fade') ? + this.hideWithTransition() : + this.hideModal() + } + + , enforceFocus: function () { + var that = this + $(document).on('focusin.modal', function (e) { + if (that.$element[0] !== e.target && !that.$element.has(e.target).length) { + that.$element.focus() + } + }) + } + + , escape: function () { + var that = this + if (this.isShown && this.options.keyboard) { + this.$element.on('keyup.dismiss.modal', function ( e ) { + e.which == 27 && that.hide() + }) + } else if (!this.isShown) { + this.$element.off('keyup.dismiss.modal') + } + } + + , hideWithTransition: function () { + var that = this + , timeout = setTimeout(function () { + that.$element.off($.support.transition.end) + that.hideModal() + }, 500) + + this.$element.one($.support.transition.end, function () { + clearTimeout(timeout) + that.hideModal() + }) + } + + , hideModal: function () { + var that = this + this.$element.hide() + this.backdrop(function () { + that.removeBackdrop() + that.$element.trigger('hidden') + }) + } + + , removeBackdrop: function () { + this.$backdrop && this.$backdrop.remove() + this.$backdrop = null + } + + , backdrop: function (callback) { + var that = this + , animate = this.$element.hasClass('fade') ? 'fade' : '' + + if (this.isShown && this.options.backdrop) { + var doAnimate = $.support.transition && animate + + this.$backdrop = $('